00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024 #include "q3richtext_p.h"
00025
00026 #ifndef QT_NO_RICHTEXT
00027
00028 #include "qbitmap.h"
00029 #include "qapplication.h"
00030 #include "q3cleanuphandler.h"
00031 #include "qcursor.h"
00032 #include "qdatastream.h"
00033 #include "q3dragobject.h"
00034 #include "qdrawutil.h"
00035 #include "qfile.h"
00036 #include "qfileinfo.h"
00037 #include "qfont.h"
00038 #include "qimage.h"
00039 #include "qmap.h"
00040 #include "qmime.h"
00041 #include "q3paintdevicemetrics.h"
00042 #include "qpainter.h"
00043 #include "qstringlist.h"
00044 #include "qstyle.h"
00045 #include "qstyleoption.h"
00046 #include "q3stylesheet.h"
00047 #include "qtextstream.h"
00048 #include <private/qtextengine_p.h>
00049 #include <private/qunicodetables_p.h>
00050
00051 #include <stdlib.h>
00052
00053 #if defined(Q_WS_X11)
00054 #include "qx11info_x11.h"
00055 #endif
00056
00057 static Q3TextCursor* richTextExportStart = 0;
00058 static Q3TextCursor* richTextExportEnd = 0;
00059
00060 class Q3TextFormatCollection;
00061
00062 const int border_tolerance = 2;
00063
00064 #ifdef Q_WS_WIN
00065 #include "qt_windows.h"
00066 #endif
00067
00068 static inline bool is_printer(QPainter *p)
00069 {
00070 if (!p || !p->device())
00071 return false;
00072 return p->device()->devType() == QInternal::Printer;
00073 }
00074
00075 static inline int scale(int value, QPainter *painter)
00076 {
00077 if (is_printer(painter)) {
00078 Q3PaintDeviceMetrics metrics(painter->device());
00079 #if defined(Q_WS_X11)
00080 value = value * metrics.logicalDpiY() /
00081 QX11Info::appDpiY(painter->device()->x11Screen());
00082 #elif defined (Q_WS_WIN)
00083 HDC hdc = GetDC(0);
00084 int gdc = GetDeviceCaps(hdc, LOGPIXELSY);
00085 if (gdc)
00086 value = value * metrics.logicalDpiY() / gdc;
00087 ReleaseDC(0, hdc);
00088 #elif defined (Q_WS_MAC)
00089 value = value * metrics.logicalDpiY() / 75;
00090 #elif defined (Q_WS_QWS)
00091 value = value * metrics.logicalDpiY() / 75;
00092 #endif
00093 }
00094 return value;
00095 }
00096
00097
00098 static inline bool isBreakable(Q3TextString *string, int pos)
00099 {
00100 if (string->at(pos).nobreak)
00101 return false;
00102 return (pos < string->length()-1 && string->at(pos+1).softBreak);
00103 }
00104
00105
00106
00107 void Q3TextCommandHistory::addCommand(Q3TextCommand *cmd)
00108 {
00109 if (current < history.count() - 1) {
00110 QList<Q3TextCommand *> commands;
00111
00112 for (int i = 0; i <= current; ++i)
00113 commands.insert(i, history.takeFirst());
00114
00115 commands.append(cmd);
00116 while (!history.isEmpty())
00117 delete history.takeFirst();
00118 history = commands;
00119 } else {
00120 history.append(cmd);
00121 }
00122
00123 if (history.count() > steps)
00124 delete history.takeFirst();
00125 else
00126 ++current;
00127 }
00128
00129 Q3TextCursor *Q3TextCommandHistory::undo(Q3TextCursor *c)
00130 {
00131 if (current > -1) {
00132 Q3TextCursor *c2 = history.at(current)->unexecute(c);
00133 --current;
00134 return c2;
00135 }
00136 return 0;
00137 }
00138
00139 Q3TextCursor *Q3TextCommandHistory::redo(Q3TextCursor *c)
00140 {
00141 if (current > -1) {
00142 if (current < history.count() - 1) {
00143 ++current;
00144 return history.at(current)->execute(c);
00145 }
00146 } else {
00147 if (history.count() > 0) {
00148 ++current;
00149 return history.at(current)->execute(c);
00150 }
00151 }
00152 return 0;
00153 }
00154
00155 bool Q3TextCommandHistory::isUndoAvailable()
00156 {
00157 return current > -1;
00158 }
00159
00160 bool Q3TextCommandHistory::isRedoAvailable()
00161 {
00162 return current > -1 && current < history.count() - 1 || current == -1 && history.count() > 0;
00163 }
00164
00165
00166
00167 Q3TextDeleteCommand::Q3TextDeleteCommand(Q3TextDocument *dc, int i, int idx, const QVector<Q3TextStringChar> &str,
00168 const QByteArray& oldStyleInfo)
00169 : Q3TextCommand(dc), id(i), index(idx), parag(0), text(str), styleInformation(oldStyleInfo)
00170 {
00171 for (int j = 0; j < (int)text.size(); ++j) {
00172 if (text[j].format())
00173 text[j].format()->addRef();
00174 }
00175 }
00176
00177 Q3TextDeleteCommand::Q3TextDeleteCommand(Q3TextParagraph *p, int idx, const QVector<Q3TextStringChar> &str)
00178 : Q3TextCommand(0), id(-1), index(idx), parag(p), text(str)
00179 {
00180 for (int i = 0; i < (int)text.size(); ++i) {
00181 if (text[i].format())
00182 text[i].format()->addRef();
00183 }
00184 }
00185
00186 Q3TextDeleteCommand::~Q3TextDeleteCommand()
00187 {
00188 for (int i = 0; i < (int)text.size(); ++i) {
00189 if (text[i].format())
00190 text[i].format()->removeRef();
00191 }
00192 text.resize(0);
00193 }
00194
00195 Q3TextCursor *Q3TextDeleteCommand::execute(Q3TextCursor *c)
00196 {
00197 Q3TextParagraph *s = doc ? doc->paragAt(id) : parag;
00198 if (!s) {
00199 qWarning("can't locate parag at %d, last parag: %d", id, doc->lastParagraph()->paragId());
00200 return 0;
00201 }
00202
00203 cursor.setParagraph(s);
00204 cursor.setIndex(index);
00205 int len = text.size();
00206 if (c)
00207 *c = cursor;
00208 if (doc) {
00209 doc->setSelectionStart(Q3TextDocument::Temp, cursor);
00210 for (int i = 0; i < len; ++i)
00211 cursor.gotoNextLetter();
00212 doc->setSelectionEnd(Q3TextDocument::Temp, cursor);
00213 doc->removeSelectedText(Q3TextDocument::Temp, &cursor);
00214 if (c)
00215 *c = cursor;
00216 } else {
00217 s->remove(index, len);
00218 }
00219
00220 return c;
00221 }
00222
00223 Q3TextCursor *Q3TextDeleteCommand::unexecute(Q3TextCursor *c)
00224 {
00225 Q3TextParagraph *s = doc ? doc->paragAt(id) : parag;
00226 if (!s) {
00227 qWarning("can't locate parag at %d, last parag: %d", id, doc->lastParagraph()->paragId());
00228 return 0;
00229 }
00230
00231 cursor.setParagraph(s);
00232 cursor.setIndex(index);
00233 QString str = Q3TextString::toString(text);
00234 cursor.insert(str, true, &text);
00235 if (c)
00236 *c = cursor;
00237 cursor.setParagraph(s);
00238 cursor.setIndex(index);
00239
00240 #ifndef QT_NO_DATASTREAM
00241 if (!styleInformation.isEmpty()) {
00242 QDataStream styleStream(&styleInformation, IO_ReadOnly);
00243 int num;
00244 styleStream >> num;
00245 Q3TextParagraph *p = s;
00246 while (num-- && p) {
00247 p->readStyleInformation(styleStream);
00248 p = p->next();
00249 }
00250 }
00251 #endif
00252 s = cursor.paragraph();
00253 while (s) {
00254 s->format();
00255 s->setChanged(true);
00256 if (s == c->paragraph())
00257 break;
00258 s = s->next();
00259 }
00260
00261 return &cursor;
00262 }
00263
00264 Q3TextFormatCommand::Q3TextFormatCommand(Q3TextDocument *dc, int sid, int sidx, int eid, int eidx,
00265 const QVector<Q3TextStringChar> &old, Q3TextFormat *f, int fl)
00266 : Q3TextCommand(dc), startId(sid), startIndex(sidx), endId(eid), endIndex(eidx), format(f), oldFormats(old), flags(fl)
00267 {
00268 format = dc->formatCollection()->format(f);
00269 for (int j = 0; j < (int)oldFormats.size(); ++j) {
00270 if (oldFormats[j].format())
00271 oldFormats[j].format()->addRef();
00272 }
00273 }
00274
00275 Q3TextFormatCommand::~Q3TextFormatCommand()
00276 {
00277 format->removeRef();
00278 for (int j = 0; j < (int)oldFormats.size(); ++j) {
00279 if (oldFormats[j].format())
00280 oldFormats[j].format()->removeRef();
00281 }
00282 }
00283
00284 Q3TextCursor *Q3TextFormatCommand::execute(Q3TextCursor *c)
00285 {
00286 Q3TextParagraph *sp = doc->paragAt(startId);
00287 Q3TextParagraph *ep = doc->paragAt(endId);
00288 if (!sp || !ep)
00289 return c;
00290
00291 Q3TextCursor start(doc);
00292 start.setParagraph(sp);
00293 start.setIndex(startIndex);
00294 Q3TextCursor end(doc);
00295 end.setParagraph(ep);
00296 end.setIndex(endIndex);
00297
00298 doc->setSelectionStart(Q3TextDocument::Temp, start);
00299 doc->setSelectionEnd(Q3TextDocument::Temp, end);
00300 doc->setFormat(Q3TextDocument::Temp, format, flags);
00301 doc->removeSelection(Q3TextDocument::Temp);
00302 if (endIndex == ep->length())
00303 end.gotoLeft();
00304 *c = end;
00305 return c;
00306 }
00307
00308 Q3TextCursor *Q3TextFormatCommand::unexecute(Q3TextCursor *c)
00309 {
00310 Q3TextParagraph *sp = doc->paragAt(startId);
00311 Q3TextParagraph *ep = doc->paragAt(endId);
00312 if (!sp || !ep)
00313 return 0;
00314
00315 int idx = startIndex;
00316 int fIndex = 0;
00317 while ( fIndex < int(oldFormats.size()) ) {
00318 if (oldFormats.at(fIndex).c == '\n') {
00319 if (idx > 0) {
00320 if (idx < sp->length() && fIndex > 0)
00321 sp->setFormat(idx, 1, oldFormats.at(fIndex - 1).format());
00322 if (sp == ep)
00323 break;
00324 sp = sp->next();
00325 idx = 0;
00326 }
00327 fIndex++;
00328 }
00329 if (oldFormats.at(fIndex).format())
00330 sp->setFormat(idx, 1, oldFormats.at(fIndex).format());
00331 idx++;
00332 fIndex++;
00333 if (fIndex >= (int)oldFormats.size())
00334 break;
00335 if (idx >= sp->length()) {
00336 if (sp == ep)
00337 break;
00338 sp = sp->next();
00339 idx = 0;
00340 }
00341 }
00342
00343 Q3TextCursor end(doc);
00344 end.setParagraph(ep);
00345 end.setIndex(endIndex);
00346 if (endIndex == ep->length())
00347 end.gotoLeft();
00348 *c = end;
00349 return c;
00350 }
00351
00352 Q3TextStyleCommand::Q3TextStyleCommand(Q3TextDocument *dc, int fParag, int lParag, const QByteArray& beforeChange)
00353 : Q3TextCommand(dc), firstParag(fParag), lastParag(lParag), before(beforeChange)
00354 {
00355 after = readStyleInformation( dc, fParag, lParag);
00356 }
00357
00358
00359 QByteArray Q3TextStyleCommand::readStyleInformation( Q3TextDocument* doc, int fParag, int lParag)
00360 {
00361 QByteArray style;
00362 #ifndef QT_NO_DATASTREAM
00363 Q3TextParagraph *p = doc->paragAt(fParag);
00364 if (!p)
00365 return style;
00366 QDataStream styleStream(&style, IO_WriteOnly);
00367 int num = lParag - fParag + 1;
00368 styleStream << num;
00369 while (num -- && p) {
00370 p->writeStyleInformation(styleStream);
00371 p = p->next();
00372 }
00373 #endif
00374 return style;
00375 }
00376
00377 void Q3TextStyleCommand::writeStyleInformation( Q3TextDocument* doc, int fParag, const QByteArray& style)
00378 {
00379 #ifndef QT_NO_DATASTREAM
00380 Q3TextParagraph *p = doc->paragAt(fParag);
00381 if (!p)
00382 return;
00383 QByteArray copy = style;
00384 QDataStream styleStream(©, IO_ReadOnly);
00385 int num;
00386 styleStream >> num;
00387 while (num-- && p) {
00388 p->readStyleInformation(styleStream);
00389 p = p->next();
00390 }
00391 #endif
00392 }
00393
00394 Q3TextCursor *Q3TextStyleCommand::execute(Q3TextCursor *c)
00395 {
00396 writeStyleInformation(doc, firstParag, after);
00397 return c;
00398 }
00399
00400 Q3TextCursor *Q3TextStyleCommand::unexecute(Q3TextCursor *c)
00401 {
00402 writeStyleInformation(doc, firstParag, before);
00403 return c;
00404 }
00405
00406
00407
00408 Q3TextCursor::Q3TextCursor(Q3TextDocument *dc)
00409 : idx(0), tmpX(-1), ox(0), oy(0),
00410 valid(true)
00411 {
00412 para = dc ? dc->firstParagraph() : 0;
00413 }
00414
00415 Q3TextCursor::Q3TextCursor(const Q3TextCursor &c)
00416 {
00417 ox = c.ox;
00418 oy = c.oy;
00419 idx = c.idx;
00420 para = c.para;
00421 tmpX = c.tmpX;
00422 indices = c.indices;
00423 paras = c.paras;
00424 xOffsets = c.xOffsets;
00425 yOffsets = c.yOffsets;
00426 valid = c.valid;
00427 }
00428
00429 Q3TextCursor::~Q3TextCursor()
00430 {
00431 }
00432
00433 Q3TextCursor &Q3TextCursor::operator=(const Q3TextCursor &c)
00434 {
00435 ox = c.ox;
00436 oy = c.oy;
00437 idx = c.idx;
00438 para = c.para;
00439 tmpX = c.tmpX;
00440 indices = c.indices;
00441 paras = c.paras;
00442 xOffsets = c.xOffsets;
00443 yOffsets = c.yOffsets;
00444 valid = c.valid;
00445
00446 return *this;
00447 }
00448
00449 bool Q3TextCursor::operator==(const Q3TextCursor &c) const
00450 {
00451 return para == c.para && idx == c.idx;
00452 }
00453
00454 int Q3TextCursor::totalOffsetX() const
00455 {
00456 int xoff = ox;
00457 for (QStack<int>::ConstIterator xit = xOffsets.begin(); xit != xOffsets.end(); ++xit)
00458 xoff += *xit;
00459 return xoff;
00460 }
00461
00462 int Q3TextCursor::totalOffsetY() const
00463 {
00464 int yoff = oy;
00465 for (QStack<int>::ConstIterator yit = yOffsets.begin(); yit != yOffsets.end(); ++yit)
00466 yoff += *yit;
00467 return yoff;
00468 }
00469
00470 #ifndef QT_NO_TEXTCUSTOMITEM
00471 void Q3TextCursor::gotoIntoNested(const QPoint &globalPos)
00472 {
00473 if (!para)
00474 return;
00475 Q_ASSERT(para->at(idx)->isCustom());
00476 push();
00477 ox = 0;
00478 int bl, y;
00479 para->lineHeightOfChar(idx, &bl, &y);
00480 oy = y + para->rect().y();
00481 ox = para->at(idx)->x;
00482 Q3TextDocument* doc = document();
00483 para->at(idx)->customItem()->enterAt(this, doc, para, idx, ox, oy, globalPos-QPoint(ox,oy));
00484 }
00485 #endif
00486
00487 void Q3TextCursor::invalidateNested()
00488 {
00489 if (nestedDepth()) {
00490 QStack<Q3TextParagraph*>::Iterator it = paras.begin();
00491 QStack<int>::Iterator it2 = indices.begin();
00492 for (; it != paras.end(); ++it, ++it2) {
00493 if (*it == para)
00494 continue;
00495 (*it)->invalidate(0);
00496 #ifndef QT_NO_TEXTCUSTOMITEM
00497 if ((*it)->at(*it2)->isCustom())
00498 (*it)->at(*it2)->customItem()->invalidate();
00499 #endif
00500 }
00501 }
00502 }
00503
00504 void Q3TextCursor::insert(const QString &str, bool checkNewLine, QVector<Q3TextStringChar> *formatting)
00505 {
00506 tmpX = -1;
00507 bool justInsert = true;
00508 QString s(str);
00509 #if defined(Q_WS_WIN)
00510 if (checkNewLine) {
00511 int i = 0;
00512 while ((i = s.indexOf('\r', i)) != -1)
00513 s.remove(i ,1);
00514 }
00515 #endif
00516 if (checkNewLine)
00517 justInsert = s.indexOf('\n') == -1;
00518 if (justInsert) {
00519 para->insert(idx, s.unicode(), s.length());
00520 if (formatting) {
00521 for (int i = 0; i < (int)s.length(); ++i) {
00522 if (formatting->at(i).format()) {
00523 formatting->at(i).format()->addRef();
00524 para->string()->setFormat(idx + i, formatting->at(i).format(), true);
00525 }
00526 }
00527 }
00528 idx += s.length();
00529 } else {
00530 int start = -1;
00531 int end;
00532 int y = para->rect().y() + para->rect().height();
00533 int lastIndex = 0;
00534 do {
00535 end = s.indexOf('\n', start + 1);
00536 if (end == -1)
00537 end = s.length();
00538 int len = (start == -1 ? end : end - start - 1);
00539 if (len > 0)
00540 para->insert(idx, s.unicode() + start + 1, len);
00541 else
00542 para->invalidate(0);
00543 if (formatting) {
00544 for (int i = 0; i < len; ++i) {
00545 if (formatting->at(i + lastIndex).format()) {
00546 formatting->at(i + lastIndex).format()->addRef();
00547 para->string()->setFormat(i + idx, formatting->at(i + lastIndex).format(), true);
00548 }
00549 }
00550 lastIndex += len;
00551 }
00552 start = end;
00553 idx += len;
00554 if (s[end] == '\n') {
00555 splitAndInsertEmptyParagraph(false, true);
00556 para->setEndState(-1);
00557 para->prev()->format(-1, false);
00558 lastIndex++;
00559 }
00560
00561 } while (end < (int)s.length());
00562
00563 para->format(-1, false);
00564 int dy = para->rect().y() + para->rect().height() - y;
00565 Q3TextParagraph *p = para;
00566 p->setParagId(p->prev() ? p->prev()->paragId() + 1 : 0);
00567 p = p->next();
00568 while (p) {
00569 p->setParagId(p->prev()->paragId() + 1);
00570 p->move(dy);
00571 p->invalidate(0);
00572 p->setEndState(-1);
00573 p = p->next();
00574 }
00575 }
00576
00577 int h = para->rect().height();
00578 para->format(-1, true);
00579 if (h != para->rect().height())
00580 invalidateNested();
00581 else if (para->document() && para->document()->parent())
00582 para->document()->nextDoubleBuffered = true;
00583
00584 fixCursorPosition();
00585 }
00586
00587 void Q3TextCursor::gotoLeft()
00588 {
00589 if (para->string()->isRightToLeft())
00590 gotoNextLetter();
00591 else
00592 gotoPreviousLetter();
00593 }
00594
00595 void Q3TextCursor::gotoPreviousLetter()
00596 {
00597 tmpX = -1;
00598
00599 if (idx > 0) {
00600 idx = para->string()->previousCursorPosition(idx);
00601 #ifndef QT_NO_TEXTCUSTOMITEM
00602 const Q3TextStringChar *tsc = para->at(idx);
00603 if (tsc && tsc->isCustom() && tsc->customItem()->isNested())
00604 processNesting(EnterEnd);
00605 #endif
00606 } else if (para->prev()) {
00607 para = para->prev();
00608 while (!para->isVisible() && para->prev())
00609 para = para->prev();
00610 idx = para->length() - 1;
00611 } else if (nestedDepth()) {
00612 pop();
00613 processNesting(Prev);
00614 if (idx == -1) {
00615 pop();
00616 if (idx > 0) {
00617 idx = para->string()->previousCursorPosition(idx);
00618 #ifndef QT_NO_TEXTCUSTOMITEM
00619 const Q3TextStringChar *tsc = para->at(idx);
00620 if (tsc && tsc->isCustom() && tsc->customItem()->isNested())
00621 processNesting(EnterEnd);
00622 #endif
00623 } else if (para->prev()) {
00624 para = para->prev();
00625 idx = para->length() - 1;
00626 }
00627 }
00628 }
00629 }
00630
00631 void Q3TextCursor::push()
00632 {
00633 indices.push(idx);
00634 paras.push(para);
00635 xOffsets.push(ox);
00636 yOffsets.push(oy);
00637 }
00638
00639 void Q3TextCursor::pop()
00640 {
00641 if (indices.isEmpty())
00642 return;
00643 idx = indices.pop();
00644 para = paras.pop();
00645 ox = xOffsets.pop();
00646 oy = yOffsets.pop();
00647 }
00648
00649 void Q3TextCursor::restoreState()
00650 {
00651 while (!indices.isEmpty())
00652 pop();
00653 }
00654
00655 bool Q3TextCursor::place(const QPoint &p, Q3TextParagraph *s, bool link)
00656 {
00657 QPoint pos(p);
00658 QRect r;
00659 Q3TextParagraph *str = s;
00660 if (pos.y() < s->rect().y()) {
00661 pos.setY(s->rect().y());
00662 #ifdef Q_WS_MAC
00663 pos.setX(s->rect().x());
00664 #endif
00665 }
00666 while (s) {
00667 r = s->rect();
00668 r.setWidth(document() ? document()->width() : QWIDGETSIZE_MAX);
00669 if (s->isVisible())
00670 str = s;
00671 if (pos.y() >= r.y() && pos.y() <= r.y() + r.height())
00672 break;
00673 if (!s->next()) {
00674 #ifdef Q_WS_MAC
00675 pos.setX(s->rect().x() + s->rect().width());
00676 #endif
00677 break;
00678 }
00679 s = s->next();
00680 }
00681
00682 if (!s || !str)
00683 return false;
00684
00685 s = str;
00686
00687 setParagraph(s);
00688 int y = s->rect().y();
00689 int lines = s->lines();
00690 Q3TextStringChar *chr = 0;
00691 int index = 0;
00692 int i = 0;
00693 int cy = 0;
00694 int ch = 0;
00695 for (; i < lines; ++i) {
00696 chr = s->lineStartOfLine(i, &index);
00697 cy = s->lineY(i);
00698 ch = s->lineHeight(i);
00699 if (!chr)
00700 return false;
00701 if (pos.y() <= y + cy + ch)
00702 break;
00703 }
00704 int nextLine;
00705 if (i < lines - 1)
00706 s->lineStartOfLine(i+1, &nextLine);
00707 else
00708 nextLine = s->length();
00709 i = index;
00710 int x = s->rect().x();
00711 if (pos.x() < x)
00712 pos.setX(x + 1);
00713 int cw;
00714 int curpos = s->length()-1;
00715 int dist = 10000000;
00716 bool inCustom = false;
00717 while (i < nextLine) {
00718 chr = s->at(i);
00719 int cpos = x + chr->x;
00720 cw = s->string()->width(i);
00721 #ifndef QT_NO_TEXTCUSTOMITEM
00722 if (chr->isCustom() && chr->customItem()->isNested()) {
00723 if (pos.x() >= cpos && pos.x() <= cpos + cw &&
00724 pos.y() >= y + cy && pos.y() <= y + cy + chr->height()) {
00725 inCustom = true;
00726 curpos = i;
00727 break;
00728 }
00729 } else
00730 #endif
00731 {
00732 if(chr->rightToLeft)
00733 cpos += cw;
00734 int diff = cpos - pos.x();
00735 bool dm = diff < 0 ? !chr->rightToLeft : chr->rightToLeft;
00736 if ((QABS(diff) < dist || (dist == diff && dm == true)) && para->string()->validCursorPosition(i)) {
00737 dist = QABS(diff);
00738 if (!link || pos.x() >= x + chr->x)
00739 curpos = i;
00740 }
00741 }
00742 i++;
00743 }
00744 setIndex(curpos);
00745
00746 #ifndef QT_NO_TEXTCUSTOMITEM
00747 if (inCustom && para->document() && para->at(curpos)->isCustom() && para->at(curpos)->customItem()->isNested()) {
00748 Q3TextDocument *oldDoc = para->document();
00749 gotoIntoNested(pos);
00750 if (oldDoc == para->document())
00751 return true;
00752 QPoint p(pos.x() - offsetX(), pos.y() - offsetY());
00753 if (!place(p, document()->firstParagraph(), link))
00754 pop();
00755 }
00756 #endif
00757 return true;
00758 }
00759
00760 bool Q3TextCursor::processNesting(Operation op)
00761 {
00762 if (!para->document())
00763 return false;
00764 Q3TextDocument* doc = para->document();
00765 push();
00766 ox = para->at(idx)->x;
00767 int bl, y;
00768 para->lineHeightOfChar(idx, &bl, &y);
00769 oy = y + para->rect().y();
00770 bool ok = false;
00771
00772 #ifndef QT_NO_TEXTCUSTOMITEM
00773 switch (op) {
00774 case EnterBegin:
00775 ok = para->at(idx)->customItem()->enter(this, doc, para, idx, ox, oy);
00776 break;
00777 case EnterEnd:
00778 ok = para->at(idx)->customItem()->enter(this, doc, para, idx, ox, oy, true);
00779 break;
00780 case Next:
00781 ok = para->at(idx)->customItem()->next(this, doc, para, idx, ox, oy);
00782 break;
00783 case Prev:
00784 ok = para->at(idx)->customItem()->prev(this, doc, para, idx, ox, oy);
00785 break;
00786 case Down:
00787 ok = para->at(idx)->customItem()->down(this, doc, para, idx, ox, oy);
00788 break;
00789 case Up:
00790 ok = para->at(idx)->customItem()->up(this, doc, para, idx, ox, oy);
00791 break;
00792 }
00793 if (!ok)
00794 #endif
00795 pop();
00796 return ok;
00797 }
00798
00799 void Q3TextCursor::gotoRight()
00800 {
00801 if (para->string()->isRightToLeft())
00802 gotoPreviousLetter();
00803 else
00804 gotoNextLetter();
00805 }
00806
00807 void Q3TextCursor::gotoNextLetter()
00808 {
00809 tmpX = -1;
00810
00811 #ifndef QT_NO_TEXTCUSTOMITEM
00812 const Q3TextStringChar *tsc = para->at(idx);
00813 if (tsc && tsc->isCustom() && tsc->customItem()->isNested()) {
00814 if (processNesting(EnterBegin))
00815 return;
00816 }
00817 #endif
00818
00819 if (idx < para->length() - 1) {
00820 idx = para->string()->nextCursorPosition(idx);
00821 } else if (para->next()) {
00822 para = para->next();
00823 while (!para->isVisible() && para->next())
00824 para = para->next();
00825 idx = 0;
00826 } else if (nestedDepth()) {
00827 pop();
00828 processNesting(Next);
00829 if (idx == -1) {
00830 pop();
00831 if (idx < para->length() - 1) {
00832 idx = para->string()->nextCursorPosition(idx);
00833 } else if (para->next()) {
00834 para = para->next();
00835 idx = 0;
00836 }
00837 }
00838 }
00839 }
00840
00841 void Q3TextCursor::gotoUp()
00842 {
00843 int indexOfLineStart;
00844 int line;
00845 Q3TextStringChar *c = para->lineStartOfChar(idx, &indexOfLineStart, &line);
00846 if (!c)
00847 return;
00848
00849 if (tmpX < 0)
00850 tmpX = x();
00851
00852 if (indexOfLineStart == 0) {
00853 if (!para->prev()) {
00854 if (!nestedDepth())
00855 return;
00856 pop();
00857 processNesting(Up);
00858 if (idx == -1) {
00859 pop();
00860 if (!para->prev())
00861 return;
00862 idx = tmpX = 0;
00863 } else {
00864 tmpX = -1;
00865 return;
00866 }
00867 }
00868 Q3TextParagraph *p = para->prev();
00869 while (p && !p->isVisible())
00870 p = p->prev();
00871 if (p)
00872 para = p;
00873 int lastLine = para->lines() - 1;
00874 if (!para->lineStartOfLine(lastLine, &indexOfLineStart))
00875 return;
00876 idx = indexOfLineStart;
00877 while (idx < para->length()-1 && para->at(idx)->x < tmpX)
00878 ++idx;
00879 if (idx > indexOfLineStart &&
00880 para->at(idx)->x - tmpX > tmpX - para->at(idx-1)->x)
00881 --idx;
00882 } else {
00883 --line;
00884 int oldIndexOfLineStart = indexOfLineStart;
00885 if (!para->lineStartOfLine(line, &indexOfLineStart))
00886 return;
00887 idx = indexOfLineStart;
00888 while (idx < oldIndexOfLineStart-1 && para->at(idx)->x < tmpX)
00889 ++idx;
00890 if (idx > indexOfLineStart &&
00891 para->at(idx)->x - tmpX > tmpX - para->at(idx-1)->x)
00892 --idx;
00893 }
00894 fixCursorPosition();
00895 }
00896
00897 void Q3TextCursor::gotoDown()
00898 {
00899 int indexOfLineStart;
00900 int line;
00901 Q3TextStringChar *c = para->lineStartOfChar(idx, &indexOfLineStart, &line);
00902 if (!c)
00903 return;
00904
00905 if (tmpX < 0)
00906 tmpX = x();
00907
00908 if (line == para->lines() - 1) {
00909 if (!para->next()) {
00910 if (!nestedDepth())
00911 return;
00912 pop();
00913 processNesting(Down);
00914 if (idx == -1) {
00915 pop();
00916 if (!para->next())
00917 return;
00918 idx = tmpX = 0;
00919 } else {
00920 tmpX = -1;
00921 return;
00922 }
00923 }
00924 Q3TextParagraph *s = para->next();
00925 while (s && !s->isVisible())
00926 s = s->next();
00927 if (s)
00928 para = s;
00929 if (!para->lineStartOfLine(0, &indexOfLineStart))
00930 return;
00931 int end;
00932 if (para->lines() == 1)
00933 end = para->length();
00934 else
00935 para->lineStartOfLine(1, &end);
00936
00937 idx = indexOfLineStart;
00938 while (idx < end-1 && para->at(idx)->x < tmpX)
00939 ++idx;
00940 if (idx > indexOfLineStart &&
00941 para->at(idx)->x - tmpX > tmpX - para->at(idx-1)->x)
00942 --idx;
00943 } else {
00944 ++line;
00945 int end;
00946 if (line == para->lines() - 1)
00947 end = para->length();
00948 else
00949 para->lineStartOfLine(line + 1, &end);
00950 if (!para->lineStartOfLine(line, &indexOfLineStart))
00951 return;
00952 idx = indexOfLineStart;
00953 while (idx < end-1 && para->at(idx)->x < tmpX)
00954 ++idx;
00955 if (idx > indexOfLineStart &&
00956 para->at(idx)->x - tmpX > tmpX - para->at(idx-1)->x)
00957 --idx;
00958 }
00959 fixCursorPosition();
00960 }
00961
00962 void Q3TextCursor::gotoLineEnd()
00963 {
00964 tmpX = -1;
00965 int indexOfLineStart;
00966 int line;
00967 Q3TextStringChar *c = para->lineStartOfChar(idx, &indexOfLineStart, &line);
00968 if (!c)
00969 return;
00970
00971 if (line == para->lines() - 1) {
00972 idx = para->length() - 1;
00973 } else {
00974 c = para->lineStartOfLine(++line, &indexOfLineStart);
00975 indexOfLineStart--;
00976 idx = indexOfLineStart;
00977 }
00978 }
00979
00980 void Q3TextCursor::gotoLineStart()
00981 {
00982 tmpX = -1;
00983 int indexOfLineStart;
00984 int line;
00985 Q3TextStringChar *c = para->lineStartOfChar(idx, &indexOfLineStart, &line);
00986 if (!c)
00987 return;
00988
00989 idx = indexOfLineStart;
00990 }
00991
00992 void Q3TextCursor::gotoHome()
00993 {
00994 if (topParagraph()->document())
00995 gotoPosition(topParagraph()->document()->firstParagraph());
00996 else
00997 gotoLineStart();
00998 }
00999
01000 void Q3TextCursor::gotoEnd()
01001 {
01002 if (topParagraph()->document() && topParagraph()->document()->lastParagraph()->isValid())
01003 gotoPosition(topParagraph()->document()->lastParagraph(),
01004 topParagraph()->document()->lastParagraph()->length() - 1);
01005 else
01006 gotoLineEnd();
01007 }
01008
01009 void Q3TextCursor::gotoPageUp(int visibleHeight)
01010 {
01011 int targetY = globalY() - visibleHeight;
01012 Q3TextParagraph* old; int index;
01013 do {
01014 old = para; index = idx;
01015 gotoUp();
01016 } while ((old != para || index != idx) && globalY() > targetY);
01017 }
01018
01019 void Q3TextCursor::gotoPageDown(int visibleHeight)
01020 {
01021 int targetY = globalY() + visibleHeight;
01022 Q3TextParagraph* old; int index;
01023 do {
01024 old = para; index = idx;
01025 gotoDown();
01026 } while ((old != para || index != idx) && globalY() < targetY);
01027 }
01028
01029 void Q3TextCursor::gotoWordRight()
01030 {
01031 if (para->string()->isRightToLeft())
01032 gotoPreviousWord();
01033 else
01034 gotoNextWord();
01035 }
01036
01037 void Q3TextCursor::gotoWordLeft()
01038 {
01039 if (para->string()->isRightToLeft())
01040 gotoNextWord();
01041 else
01042 gotoPreviousWord();
01043 }
01044
01045 static bool is_seperator(const QChar &c, bool onlySpace)
01046 {
01047 if (onlySpace)
01048 return c.isSpace();
01049 return c.isSpace() ||
01050 c == '\t' ||
01051 c == '.' ||
01052 c == ',' ||
01053 c == ':' ||
01054 c == ';' ||
01055 c == '-' ||
01056 c == '<' ||
01057 c == '>' ||
01058 c == '[' ||
01059 c == ']' ||
01060 c == '(' ||
01061 c == ')' ||
01062 c == '{' ||
01063 c == '}';
01064 }
01065
01066 void Q3TextCursor::gotoPreviousWord(bool onlySpace)
01067 {
01068 gotoPreviousLetter();
01069 tmpX = -1;
01070 Q3TextString *s = para->string();
01071 bool allowSame = false;
01072 if (idx == ((int)s->length()-1))
01073 return;
01074 for (int i = idx; i >= 0; --i) {
01075 if (is_seperator(s->at(i).c, onlySpace)) {
01076 if (!allowSame)
01077 continue;
01078 idx = i + 1;
01079 return;
01080 }
01081 if (!allowSame && !is_seperator(s->at(i).c, onlySpace))
01082 allowSame = true;
01083 }
01084 idx = 0;
01085 }
01086
01087 void Q3TextCursor::gotoNextWord(bool onlySpace)
01088 {
01089 tmpX = -1;
01090 Q3TextString *s = para->string();
01091 bool allowSame = false;
01092 for (int i = idx; i < (int)s->length(); ++i) {
01093 if (!is_seperator(s->at(i).c, onlySpace)) {
01094 if (!allowSame)
01095 continue;
01096 idx = i;
01097 return;
01098 }
01099 if (!allowSame && is_seperator(s->at(i).c, onlySpace))
01100 allowSame = true;
01101
01102 }
01103
01104 if (idx < ((int)s->length()-1)) {
01105 gotoLineEnd();
01106 } else if (para->next()) {
01107 Q3TextParagraph *p = para->next();
01108 while (p && !p->isVisible())
01109 p = p->next();
01110 if (s) {
01111 para = p;
01112 idx = 0;
01113 }
01114 } else {
01115 gotoLineEnd();
01116 }
01117 }
01118
01119 bool Q3TextCursor::atParagStart()
01120 {
01121 return idx == 0;
01122 }
01123
01124 bool Q3TextCursor::atParagEnd()
01125 {
01126 return idx == para->length() - 1;
01127 }
01128
01129 void Q3TextCursor::splitAndInsertEmptyParagraph(bool ind, bool updateIds)
01130 {
01131 if (!para->document())
01132 return;
01133 tmpX = -1;
01134 Q3TextFormat *f = 0;
01135 if (para->document()->useFormatCollection()) {
01136 f = para->at(idx)->format();
01137 if (idx == para->length() - 1 && idx > 0)
01138 f = para->at(idx - 1)->format();
01139 if (f->isMisspelled()) {
01140 f->removeRef();
01141 f = para->document()->formatCollection()->format(f->font(), f->color());
01142 }
01143 }
01144
01145 if (atParagEnd()) {
01146 Q3TextParagraph *n = para->next();
01147 Q3TextParagraph *s = para->document()->createParagraph(para->document(), para, n, updateIds);
01148 if (f)
01149 s->setFormat(0, 1, f, true);
01150 s->copyParagData(para);
01151 if (ind) {
01152 int oi, ni;
01153 s->indent(&oi, &ni);
01154 para = s;
01155 idx = ni;
01156 } else {
01157 para = s;
01158 idx = 0;
01159 }
01160 } else if (atParagStart()) {
01161 Q3TextParagraph *p = para->prev();
01162 Q3TextParagraph *s = para->document()->createParagraph(para->document(), p, para, updateIds);
01163 if (f)
01164 s->setFormat(0, 1, f, true);
01165 s->copyParagData(para);
01166 if (ind) {
01167 s->indent();
01168 s->format();
01169 indent();
01170 para->format();
01171 }
01172 } else {
01173 QString str = para->string()->toString().mid(idx, 0xFFFFFF);
01174 Q3TextParagraph *n = para->next();
01175 Q3TextParagraph *s = para->document()->createParagraph(para->document(), para, n, updateIds);
01176 s->copyParagData(para);
01177 s->remove(0, 1);
01178 s->append(str, true);
01179 for (int i = 0; i < str.length(); ++i) {
01180 Q3TextStringChar* tsc = para->at(idx + i);
01181 s->setFormat(i, 1, tsc->format(), true);
01182 #ifndef QT_NO_TEXTCUSTOMITEM
01183 if (tsc->isCustom()) {
01184 Q3TextCustomItem * item = tsc->customItem();
01185 s->at(i)->setCustomItem(item);
01186 tsc->loseCustomItem();
01187 }
01188 #endif
01189 if (tsc->isAnchor())
01190 s->at(i)->setAnchor(tsc->anchorName(),
01191 tsc->anchorHref());
01192 }
01193 para->truncate(idx);
01194 if (ind) {
01195 int oi, ni;
01196 s->indent(&oi, &ni);
01197 para = s;
01198 idx = ni;
01199 } else {
01200 para = s;
01201 idx = 0;
01202 }
01203 }
01204
01205 invalidateNested();
01206 }
01207
01208 bool Q3TextCursor::remove()
01209 {
01210 tmpX = -1;
01211 if (!atParagEnd()) {
01212 int next = para->string()->nextCursorPosition(idx);
01213 para->remove(idx, next-idx);
01214 int h = para->rect().height();
01215 para->format(-1, true);
01216 if (h != para->rect().height())
01217 invalidateNested();
01218 else if (para->document() && para->document()->parent())
01219 para->document()->nextDoubleBuffered = true;
01220 return false;
01221 } else if (para->next()) {
01222 para->join(para->next());
01223 invalidateNested();
01224 return true;
01225 }
01226 return false;
01227 }
01228
01229
01230 bool Q3TextCursor::removePreviousChar()
01231 {
01232 tmpX = -1;
01233 if (!atParagStart()) {
01234 para->remove(idx-1, 1);
01235 int h = para->rect().height();
01236 idx--;
01237
01238 fixCursorPosition();
01239 para->format(-1, true);
01240 if (h != para->rect().height())
01241 invalidateNested();
01242 else if (para->document() && para->document()->parent())
01243 para->document()->nextDoubleBuffered = true;
01244 return false;
01245 } else if (para->prev()) {
01246 para = para->prev();
01247 para->join(para->next());
01248 invalidateNested();
01249 return true;
01250 }
01251 return false;
01252 }
01253
01254 void Q3TextCursor::indent()
01255 {
01256 int oi = 0, ni = 0;
01257 para->indent(&oi, &ni);
01258 if (oi == ni)
01259 return;
01260
01261 if (idx >= oi)
01262 idx += ni - oi;
01263 else
01264 idx = ni;
01265 }
01266
01267 void Q3TextCursor::fixCursorPosition()
01268 {
01269
01270 if (para->string()->validCursorPosition(idx))
01271 return;
01272
01273 int lineIdx;
01274 Q3TextStringChar *start = para->lineStartOfChar(idx, &lineIdx, 0);
01275 int x = para->string()->at(idx).x;
01276 int diff = QABS(start->x - x);
01277 int best = lineIdx;
01278
01279 Q3TextStringChar *c = start;
01280 ++c;
01281
01282 Q3TextStringChar *end = ¶->string()->at(para->length()-1);
01283 while (c <= end && !c->lineStart) {
01284 int xp = c->x;
01285 if (c->rightToLeft)
01286 xp += para->string()->width(lineIdx + (c-start));
01287 int ndiff = QABS(xp - x);
01288 if (ndiff < diff && para->string()->validCursorPosition(lineIdx + (c-start))) {
01289 diff = ndiff;
01290 best = lineIdx + (c-start);
01291 }
01292 ++c;
01293 }
01294 idx = best;
01295 }
01296
01297
01298
01299
01300 Q3TextDocument::Q3TextDocument(Q3TextDocument *p)
01301 : par(p), parentPar(0)
01302 #ifndef QT_NO_TEXTCUSTOMITEM
01303 , tc(0)
01304 #endif
01305 , tArray(0), tStopWidth(0)
01306 {
01307 fCollection = par ? par->fCollection : new Q3TextFormatCollection;
01308 init();
01309 }
01310
01311 void Q3TextDocument::init()
01312 {
01313 oTextValid = true;
01314 mightHaveCustomItems = false;
01315 if (par)
01316 par->insertChild(this);
01317 pProcessor = 0;
01318 useFC = true;
01319 pFormatter = 0;
01320 indenter = 0;
01321 fParag = 0;
01322 txtFormat = Qt::AutoText;
01323 preferRichText = false;
01324 pages = false;
01325 focusIndicator.parag = 0;
01326 minw = 0;
01327 wused = 0;
01328 minwParag = curParag = 0;
01329 align = Qt::AlignAuto;
01330 nSelections = 1;
01331
01332 setStyleSheet(Q3StyleSheet::defaultSheet());
01333 #ifndef QT_NO_MIME
01334 factory_ = Q3MimeSourceFactory::defaultFactory();
01335 #endif
01336 contxt.clear();
01337
01338 underlLinks = par ? par->underlLinks : true;
01339 backBrush = 0;
01340 buf_pixmap = 0;
01341 nextDoubleBuffered = false;
01342
01343 if (par)
01344 withoutDoubleBuffer = par->withoutDoubleBuffer;
01345 else
01346 withoutDoubleBuffer = false;
01347
01348 lParag = fParag = createParagraph(this, 0, 0);
01349
01350 cx = 0;
01351 cy = 2;
01352 if (par)
01353 cx = cy = 0;
01354 cw = 600;
01355 vw = 0;
01356 flow_ = new Q3TextFlow;
01357 flow_->setWidth(cw);
01358
01359 leftmargin = rightmargin = 4;
01360 scaleFontsFactor = 1;
01361
01362 commandHistory = new Q3TextCommandHistory(100);
01363 tStopWidth = formatCollection()->defaultFormat()->width('x') * 8;
01364 }
01365
01366 Q3TextDocument::~Q3TextDocument()
01367 {
01368 delete commandHistory;
01369 if (par)
01370 par->removeChild(this);
01371 clear();
01372 delete flow_;
01373 if (!par) {
01374 delete pFormatter;
01375 delete fCollection;
01376 }
01377 delete pProcessor;
01378 delete buf_pixmap;
01379 delete indenter;
01380 delete backBrush;
01381 delete [] tArray;
01382 }
01383
01384 void Q3TextDocument::clear(bool createEmptyParag)
01385 {
01386 while (fParag) {
01387 Q3TextParagraph *p = fParag->next();
01388 delete fParag;
01389 fParag = p;
01390 }
01391 if (flow_)
01392 flow_->clear();
01393 fParag = lParag = 0;
01394 if (createEmptyParag)
01395 fParag = lParag = createParagraph(this);
01396 selections.clear();
01397 oText.clear();
01398 oTextValid = false;
01399 }
01400
01401 int Q3TextDocument::widthUsed() const
01402 {
01403 return wused + 2*border_tolerance;
01404 }
01405
01406 int Q3TextDocument::height() const
01407 {
01408 int h = 0;
01409 if (lParag)
01410 h = lParag->rect().top() + lParag->rect().height() + 1;
01411 int fh = flow_->boundingRect().bottom();
01412 return qMax(h, fh);
01413 }
01414
01415
01416
01417 Q3TextParagraph *Q3TextDocument::createParagraph(Q3TextDocument *dc, Q3TextParagraph *pr, Q3TextParagraph *nx, bool updateIds)
01418 {
01419 return new Q3TextParagraph(dc, pr, nx, updateIds);
01420 }
01421
01422 bool Q3TextDocument::setMinimumWidth(int needed, int used, Q3TextParagraph *p)
01423 {
01424 if (needed == -1) {
01425 minw = 0;
01426 wused = 0;
01427 p = 0;
01428 }
01429 if (p == minwParag) {
01430 if (minw > needed) {
01431 Q3TextParagraph *tp = fParag;
01432 while (tp) {
01433 if (tp != p && tp->minwidth > needed) {
01434 needed = tp->minwidth;
01435 minwParag = tp;
01436 }
01437 tp = tp->n;
01438 }
01439 }
01440 minw = needed;
01441 emit minimumWidthChanged(minw);
01442 } else if (needed > minw) {
01443 minw = needed;
01444 minwParag = p;
01445 emit minimumWidthChanged(minw);
01446 }
01447 wused = qMax(wused, used);
01448 wused = qMax(wused, minw);
01449 cw = qMax(minw, cw);
01450 return true;
01451 }
01452
01453 void Q3TextDocument::setPlainText(const QString &text)
01454 {
01455 preferRichText = false;
01456 clear();
01457 oTextValid = true;
01458 oText = text;
01459
01460 int lastNl = 0;
01461 int nl = text.indexOf('\n');
01462 if (nl == -1) {
01463 lParag = createParagraph(this, lParag, 0);
01464 if (!fParag)
01465 fParag = lParag;
01466 QString s = text;
01467 if (!s.isEmpty()) {
01468 if (s[(int)s.length() - 1] == '\r')
01469 s.remove(s.length() - 1, 1);
01470 lParag->append(s);
01471 }
01472 } else {
01473 for (;;) {
01474 lParag = createParagraph(this, lParag, 0);
01475 if (!fParag)
01476 fParag = lParag;
01477 int l = nl - lastNl;
01478 if (l > 0) {
01479 if (text.unicode()[nl-1] == '\r')
01480 l--;
01481 QString cs = QString::fromRawData(text.unicode()+lastNl, l);
01482 lParag->append(cs);
01483 }
01484 if (nl == (int)text.length())
01485 break;
01486 lastNl = nl + 1;
01487 nl = text.indexOf('\n', nl + 1);
01488 if (nl == -1)
01489 nl = text.length();
01490 }
01491 }
01492 if (!lParag)
01493 lParag = fParag = createParagraph(this, 0, 0);
01494 }
01495
01496 struct Q3TextDocumentTag {
01497 Q3TextDocumentTag(){}
01498 Q3TextDocumentTag(const QString&n, const Q3StyleSheetItem* s, const Q3TextFormat& f)
01499 :name(n),style(s), format(f), alignment(Qt::AlignAuto), direction(QChar::DirON),liststyle(Q3StyleSheetItem::ListDisc) {
01500 wsm = Q3StyleSheetItem::WhiteSpaceNormal;
01501 }
01502 QString name;
01503 const Q3StyleSheetItem* style;
01504 QString anchorHref;
01505 Q3StyleSheetItem::WhiteSpaceMode wsm;
01506 Q3TextFormat format;
01507 signed int alignment : 16;
01508 signed int direction : 5;
01509 Q3StyleSheetItem::ListStyle liststyle;
01510
01511 Q3TextDocumentTag( const Q3TextDocumentTag& t) {
01512 name = t.name;
01513 style = t.style;
01514 anchorHref = t.anchorHref;
01515 wsm = t.wsm;
01516 format = t.format;
01517 alignment = t.alignment;
01518 direction = t.direction;
01519 liststyle = t.liststyle;
01520 }
01521 Q3TextDocumentTag& operator=(const Q3TextDocumentTag& t) {
01522 name = t.name;
01523 style = t.style;
01524 anchorHref = t.anchorHref;
01525 wsm = t.wsm;
01526 format = t.format;
01527 alignment = t.alignment;
01528 direction = t.direction;
01529 liststyle = t.liststyle;
01530 return *this;
01531 }
01532
01533 Q_DUMMY_COMPARISON_OPERATOR(Q3TextDocumentTag)
01534 };
01535
01536
01537 #define NEWPAR \
01538 do{ \
01539 if (!hasNewPar) { \
01540 if (!textEditMode && curpar && curpar->length()>1 \
01541 && curpar->at(curpar->length()-2)->c == QChar::LineSeparator) \
01542 curpar->remove(curpar->length()-2, 1); \
01543 curpar = createParagraph(this, curpar, curpar->next()); \
01544 styles.append(vec); \
01545 vec = 0; \
01546 } \
01547 hasNewPar = true; \
01548 curpar->rtext = true; \
01549 curpar->align = curtag.alignment; \
01550 curpar->lstyle = curtag.liststyle; \
01551 curpar->litem = (curtag.style->displayMode() == Q3StyleSheetItem::DisplayListItem); \
01552 curpar->str->setDirection((QChar::Direction)curtag.direction); \
01553 space = true; \
01554 tabExpansionColumn = 0; \
01555 delete vec; \
01556 vec = new QVector<Q3StyleSheetItem *>(); \
01557 for (QStack<Q3TextDocumentTag>::Iterator it = tags.begin(); it != tags.end(); ++it) \
01558 vec->append(const_cast<Q3StyleSheetItem *>((*it).style)); \
01559 vec->append(const_cast<Q3StyleSheetItem *>(curtag.style)); \
01560 } while(false);
01561
01562
01563 void Q3TextDocument::setRichText(const QString &text, const QString &context, const Q3TextFormat *initialFormat)
01564 {
01565 preferRichText = true;
01566 if (!context.isEmpty())
01567 setContext(context);
01568 clear();
01569 fParag = lParag = createParagraph(this);
01570 oTextValid = true;
01571 oText = text;
01572 setRichTextInternal(text, 0, initialFormat);
01573 fParag->rtext = true;
01574 }
01575
01576 void Q3TextDocument::setRichTextInternal(const QString &text, Q3TextCursor* cursor, const Q3TextFormat *initialFormat)
01577 {
01578 Q3TextParagraph* curpar = lParag;
01579 int pos = 0;
01580 QStack<Q3TextDocumentTag> tags;
01581 if (!initialFormat)
01582 initialFormat = formatCollection()->defaultFormat();
01583 Q3TextDocumentTag initag("", sheet_->item(""), *initialFormat);
01584 if (bodyText.isValid())
01585 initag.format.setColor(bodyText);
01586 Q3TextDocumentTag curtag = initag;
01587 bool space = true;
01588 bool canMergeLi = false;
01589
01590 bool textEditMode = false;
01591 int tabExpansionColumn = 0;
01592
01593 const QChar* doc = text.unicode();
01594 int length = text.length();
01595 bool hasNewPar = curpar->length() <= 1;
01596 QString anchorName;
01597
01598
01599 Q3TextParagraph* stylesPar = curpar;
01600 QVector<Q3StyleSheetItem *>* vec = 0;
01601 QList< QVector<Q3StyleSheetItem *> *> styles;
01602
01603 if (cursor) {
01604 cursor->splitAndInsertEmptyParagraph();
01605 Q3TextCursor tmp = *cursor;
01606 tmp.gotoPreviousLetter();
01607 stylesPar = curpar = tmp.paragraph();
01608 hasNewPar = true;
01609 textEditMode = true;
01610 } else {
01611 NEWPAR;
01612 }
01613
01614
01615 curpar->rtext = false;
01616
01617 QString wellKnownTags = "br hr wsp table qt body meta title";
01618
01619 while (pos < length) {
01620 if (hasPrefix(doc, length, pos, '<')){
01621 if (!hasPrefix(doc, length, pos+1, QChar('/'))) {
01622
01623 QMap<QString, QString> attr;
01624 QMap<QString, QString>::Iterator it, end = attr.end();
01625 bool emptyTag = false;
01626 QString tagname = parseOpenTag(doc, length, pos, attr, emptyTag);
01627 if (tagname.isEmpty())
01628 continue;
01629
01630 const Q3StyleSheetItem* nstyle = sheet_->item(tagname);
01631
01632 if (nstyle) {
01633
01634 while (!nstyle->allowedInContext(curtag.style)) {
01635 QString msg;
01636 msg.sprintf("QText Warning: Document not valid ('%s' not allowed in '%s' #%d)",
01637 tagname.ascii(), curtag.style->name().ascii(), pos);
01638 sheet_->error(msg);
01639 if (tags.isEmpty())
01640 break;
01641 curtag = tags.pop();
01642 }
01643
01644
01645
01646
01647
01648 if(nstyle->displayMode() == Q3StyleSheetItem::DisplayListItem) {
01649 canMergeLi = true;
01650 } else if (nstyle->displayMode() == Q3StyleSheetItem::DisplayBlock) {
01651 while (curtag.style->name() == "p") {
01652 if (tags.isEmpty())
01653 break;
01654 curtag = tags.pop();
01655 }
01656
01657 if (curtag.style->displayMode() == Q3StyleSheetItem::DisplayListItem) {
01658
01659 if (nstyle->name() == "ul" || nstyle->name() == "ol")
01660 hasNewPar = false;
01661 if (!hasNewPar) {
01662
01663
01664 while (curtag.style->displayMode() == Q3StyleSheetItem::DisplayListItem) {
01665 if (tags.isEmpty())
01666 break;
01667 curtag = tags.pop();
01668 }
01669 } else if (canMergeLi) {
01670
01671
01672 nstyle = curtag.style;
01673 }
01674 canMergeLi = false;
01675 }
01676 }
01677 }
01678
01679 #ifndef QT_NO_TEXTCUSTOMITEM
01680 Q3TextCustomItem* custom = 0;
01681 #else
01682 bool custom = false;
01683 #endif
01684
01685
01686 if (wellKnownTags.contains(tagname)) {
01687 if (tagname == "br") {
01688 emptyTag = space = true;
01689 int index = qMax(curpar->length(),1) - 1;
01690 Q3TextFormat format = curtag.format.makeTextFormat(nstyle, attr, scaleFontsFactor);
01691 curpar->append(QString(QChar(QChar::LineSeparator)));
01692 curpar->setFormat(index, 1, &format);
01693 hasNewPar = false;
01694 } else if (tagname == "hr") {
01695 emptyTag = space = true;
01696 #ifndef QT_NO_TEXTCUSTOMITEM
01697 custom = tag(sheet_, tagname, attr, contxt, *factory_ , emptyTag, this);
01698 #endif
01699 } else if (tagname == "table") {
01700 emptyTag = space = true;
01701 #ifndef QT_NO_TEXTCUSTOMITEM
01702 Q3TextFormat format = curtag.format.makeTextFormat( nstyle, attr, scaleFontsFactor);
01703 curpar->setAlignment(curtag.alignment);
01704 custom = parseTable(attr, format, doc, length, pos, curpar);
01705 #endif
01706 } else if (tagname == "qt" || tagname == "body") {
01707 it = attr.find("bgcolor");
01708 if (it != end) {
01709 QBrush *b = new QBrush(QColor(*it));
01710 setPaper(b);
01711 }
01712 it = attr.find("background");
01713 if (it != end) {
01714 #ifndef QT_NO_MIME
01715 QImage img;
01716 QString bg = *it;
01717 const QMimeSource* m = factory_->data(bg, contxt);
01718 if (!m) {
01719 qCritical("QRichText: no mimesource for %s",
01720 QFile::encodeName(bg).data());
01721 } else {
01722 if (!Q3ImageDrag::decode(m, img)) {
01723 qCritical("Q3TextImage: cannot decode %s",
01724 QFile::encodeName(bg).data());
01725 }
01726 }
01727 if (!img.isNull()) {
01728 QBrush *b = new QBrush(QColor(), QPixmap(img));
01729 setPaper(b);
01730 }
01731 #endif
01732 }
01733 it = attr.find("text");
01734 if (it != end) {
01735 QColor c(*it);
01736 initag.format.setColor(c);
01737 curtag.format.setColor(c);
01738 bodyText = c;
01739 }
01740 it = attr.find("link");
01741 if (it != end)
01742 linkColor = QColor(*it);
01743 it = attr.find("title");
01744 if (it != end)
01745 attribs.insert("title", *it);
01746
01747 if (textEditMode) {
01748 it = attr.find("style");
01749 if (it != end) {
01750 QString a = *it;
01751 int count = a.count(';') + 1;
01752 for (int s = 0; s < count; s++) {
01753 QString style = a.section(';', s, s);
01754 if (style.startsWith("font-size:") && style.endsWith("pt")) {
01755 scaleFontsFactor = double(formatCollection()->defaultFormat()->fn.pointSize()) /
01756 style.mid(10, style.length() - 12).toInt();
01757 }
01758 }
01759 }
01760 nstyle = 0;
01761 }
01762
01763 } else if (tagname == "meta") {
01764 if (attr["name"] == "qrichtext" && attr["content"] == "1")
01765 textEditMode = true;
01766 } else if (tagname == "title") {
01767 QString title;
01768 while (pos < length) {
01769 if (hasPrefix(doc, length, pos, QChar('<')) && hasPrefix(doc, length, pos+1, QChar('/')) &&
01770 parseCloseTag(doc, length, pos) == "title")
01771 break;
01772 title += doc[pos];
01773 ++pos;
01774 }
01775 attribs.insert("title", title);
01776 }
01777 }
01778
01779 #ifndef QT_NO_TEXTCUSTOMITEM
01780 if (!custom)
01781 custom = tag(sheet_, tagname, attr, contxt, *factory_ , emptyTag, this);
01782 #endif
01783 if (!nstyle && !custom)
01784 continue;
01785
01786 if (custom) {
01787 #ifndef QT_NO_TEXTCUSTOMITEM
01788 int index = qMax(curpar->length(),1) - 1;
01789 Q3TextFormat format = curtag.format.makeTextFormat(nstyle, attr, scaleFontsFactor);
01790 curpar->append(QString(QChar('*')));
01791 Q3TextFormat* f = formatCollection()->format(&format);
01792 curpar->setFormat(index, 1, f);
01793 curpar->at(index)->setCustomItem(custom);
01794 if (!curtag.anchorHref.isEmpty())
01795 curpar->at(index)->setAnchor(QString(), curtag.anchorHref);
01796 if (!anchorName.isEmpty() ) {
01797 curpar->at(index)->setAnchor(anchorName, curpar->at(index)->anchorHref());
01798 anchorName.clear();
01799 }
01800 registerCustomItem(custom, curpar);
01801 hasNewPar = false;
01802 #endif
01803 } else if (!emptyTag) {
01804
01805
01806 if (curtag.style->name() != tagname || nstyle->selfNesting()) {
01807 tags.push(curtag);
01808 } else {
01809 if (!tags.isEmpty())
01810 curtag = tags.top();
01811 else
01812 curtag = initag;
01813 }
01814
01815 curtag.name = tagname;
01816 curtag.style = nstyle;
01817 curtag.name = tagname;
01818 curtag.style = nstyle;
01819 if (nstyle->whiteSpaceMode() != Q3StyleSheetItem::WhiteSpaceModeUndefined)
01820 curtag.wsm = nstyle->whiteSpaceMode();
01821
01822
01823 if (curtag.wsm == Q3StyleSheetItem::WhiteSpacePre &&
01824 nstyle->displayMode() == Q3StyleSheetItem::DisplayBlock)
01825 eat(doc, length, pos, '\n');
01826
01827
01828
01829 if (!textEditMode &&
01830 (curtag.wsm == Q3StyleSheetItem::WhiteSpaceNormal
01831 || curtag.wsm == Q3StyleSheetItem::WhiteSpaceNoWrap)
01832 && (space || nstyle->displayMode() != Q3StyleSheetItem::DisplayInline))
01833 eatSpace(doc, length, pos);
01834
01835 curtag.format = curtag.format.makeTextFormat(nstyle, attr, scaleFontsFactor);
01836 if (nstyle->isAnchor()) {
01837 if (!anchorName.isEmpty())
01838 anchorName += "#" + attr["name"];
01839 else
01840 anchorName = attr["name"];
01841 curtag.anchorHref = attr["href"];
01842 }
01843
01844 if (nstyle->alignment() != Q3StyleSheetItem::Undefined)
01845 curtag.alignment = nstyle->alignment();
01846
01847 if (nstyle->listStyle() != Q3StyleSheetItem::ListStyleUndefined)
01848 curtag.liststyle = nstyle->listStyle();
01849
01850 if (nstyle->displayMode() == Q3StyleSheetItem::DisplayBlock
01851 || nstyle->displayMode() == Q3StyleSheetItem::DisplayListItem) {
01852
01853 if (nstyle->name() == "ol" || nstyle->name() == "ul" || nstyle->name() == "li") {
01854 QString type = attr["type"];
01855 if (!type.isEmpty()) {
01856 if (type == "1") {
01857 curtag.liststyle = Q3StyleSheetItem::ListDecimal;
01858 } else if (type == "a") {
01859 curtag.liststyle = Q3StyleSheetItem::ListLowerAlpha;
01860 } else if (type == "A") {
01861 curtag.liststyle = Q3StyleSheetItem::ListUpperAlpha;
01862 } else {
01863 type = type.toLower();
01864 if (type == "square")
01865 curtag.liststyle = Q3StyleSheetItem::ListSquare;
01866 else if (type == "disc")
01867 curtag.liststyle = Q3StyleSheetItem::ListDisc;
01868 else if (type == "circle")
01869 curtag.liststyle = Q3StyleSheetItem::ListCircle;
01870 }
01871 }
01872 }
01873
01874
01875
01876
01877
01878
01879
01880
01881
01882
01883 if (nstyle->name() == "ul")
01884 curtag.style = sheet_->item("ol");
01885
01886 it = attr.find("align");
01887 if (it != end) {
01888 QString align = (*it).toLower();
01889 if (align == "center")
01890 curtag.alignment = Qt::AlignCenter;
01891 else if (align == "right")
01892 curtag.alignment = Qt::AlignRight;
01893 else if (align == "justify")
01894 curtag.alignment = Qt::AlignJustify;
01895 }
01896 it = attr.find("dir");
01897 if (it != end) {
01898 QString dir = (*it).toLower();
01899 if (dir == "rtl")
01900 curtag.direction = QChar::DirR;
01901 else if (dir == "ltr")
01902 curtag.direction = QChar::DirL;
01903 }
01904
01905 NEWPAR;
01906
01907 if (curtag.style && curtag.style->displayMode() == Q3StyleSheetItem::DisplayListItem) {
01908 it = attr.find("value");
01909 if (it != end)
01910 curpar->setListValue((*it).toInt());
01911 }
01912
01913 it = attr.find("style");
01914 if (it != end) {
01915 QString a = *it;
01916 bool ok = true;
01917 int count = a.count(';')+1;
01918 for (int s = 0; ok && s < count; s++) {
01919 QString style = a.section(';', s, s);
01920 if (style.startsWith("margin-top:") && style.endsWith("px"))
01921 curpar->utm = 1+style.mid(11, style.length() - 13).toInt(&ok);
01922 else if (style.startsWith("margin-bottom:") && style.endsWith("px"))
01923 curpar->ubm = 1+style.mid(14, style.length() - 16).toInt(&ok);
01924 else if (style.startsWith("margin-left:") && style.endsWith("px"))
01925 curpar->ulm = 1+style.mid(12, style.length() - 14).toInt(&ok);
01926 else if (style.startsWith("margin-right:") && style.endsWith("px"))
01927 curpar->urm = 1+style.mid(13, style.length() - 15).toInt(&ok);
01928 else if (style.startsWith("text-indent:") && style.endsWith("px"))
01929 curpar->uflm = 1+style.mid(12, style.length() - 14).toInt(&ok);
01930 }
01931 if (!ok)
01932 curpar->utm = curpar->ubm = curpar->urm = curpar->ulm = 0;
01933 }
01934 }
01935 }
01936 } else {
01937 QString tagname = parseCloseTag(doc, length, pos);
01938 if (tagname.isEmpty())
01939 continue;
01940 if (!sheet_->item(tagname))
01941 continue;
01942 if (tagname == "li")
01943 continue;
01944
01945
01946 bool needNewPar = curtag.style->displayMode() == Q3StyleSheetItem::DisplayBlock
01947 || curtag.style->displayMode() == Q3StyleSheetItem::DisplayListItem;
01948
01949
01950
01951 while (curtag.name != tagname) {
01952 QString msg;
01953 msg.sprintf("QText Warning: Document not valid ('%s' not closed before '%s' #%d)",
01954 curtag.name.ascii(), tagname.ascii(), pos);
01955 sheet_->error(msg);
01956 if (tags.isEmpty())
01957 break;
01958 curtag = tags.pop();
01959 }
01960
01961
01962
01963 if (!tags.isEmpty())
01964 curtag = tags.pop();
01965 else
01966 curtag = initag;
01967
01968 if (needNewPar) {
01969 if (textEditMode && (tagname == "p" || tagname == "div"))
01970 hasNewPar = false;
01971 NEWPAR;
01972 }
01973 }
01974 } else {
01975
01976 QString s;
01977 QChar c;
01978 while (pos < length && !hasPrefix(doc, length, pos, QChar('<'))){
01979 if (textEditMode) {
01980
01981 c = parseChar(doc, length, pos, Q3StyleSheetItem::WhiteSpacePre);
01982 if (c == QChar::LineSeparator)
01983 break;
01984 } else {
01985 int l = pos;
01986 c = parseChar(doc, length, pos, curtag.wsm);
01987
01988
01989
01990 if (curtag.wsm == Q3StyleSheetItem::WhiteSpacePre) {
01991 if (c == '\t') {
01992 c = ' ';
01993 while((++tabExpansionColumn)%8)
01994 s += c;
01995 }
01996 if (c == QChar::LineSeparator)
01997 tabExpansionColumn = 0;
01998 else
01999 tabExpansionColumn++;
02000
02001 }
02002 if (c == ' ' || c == QChar::LineSeparator) {
02003
02004
02005
02006
02007
02008
02009 if (curtag.wsm == Q3StyleSheetItem::WhiteSpaceNormal && s.length() > 4096) do {
02010 if (doc[l] == '\n') {
02011 hasNewPar = false;
02012 NEWPAR;
02013 hasNewPar = false;
02014 c = '\n';
02015 break;
02016 }
02017 } while (++l < pos);
02018 }
02019 }
02020
02021 if (c == '\n')
02022 break;
02023
02024 bool c_isSpace = c.isSpace() && c.unicode() != 0x00a0U && !textEditMode;
02025
02026 if (curtag.wsm == Q3StyleSheetItem::WhiteSpaceNormal && c_isSpace && space)
02027 continue;
02028 if (c == '\r')
02029 continue;
02030 space = c_isSpace;
02031 s += c;
02032 }
02033 if (!s.isEmpty() && curtag.style->displayMode() != Q3StyleSheetItem::DisplayNone) {
02034 hasNewPar = false;
02035 int index = qMax(curpar->length(),1) - 1;
02036 curpar->append(s);
02037 if (curtag.wsm != Q3StyleSheetItem::WhiteSpaceNormal) {
02038 Q3TextString *str = curpar->string();
02039 for (int i = index; i < index + s.length(); ++i)
02040 str->at(i).nobreak = true;
02041 }
02042
02043 Q3TextFormat* f = formatCollection()->format(&curtag.format);
02044 curpar->setFormat(index, s.length(), f, false);
02045 f->ref += s.length() -1;
02046 if (!curtag.anchorHref.isEmpty()) {
02047 for (int i = 0; i < int(s.length()); i++)
02048 curpar->at(index + i)->setAnchor(QString(), curtag.anchorHref);
02049 }
02050 if (!anchorName.isEmpty() ) {
02051 for (int i = 0; i < int(s.length()); i++)
02052 curpar->at(index + i)->setAnchor(anchorName, curpar->at(index + i)->anchorHref());
02053 anchorName.clear();
02054 }
02055 }
02056 }
02057 }
02058
02059 if (hasNewPar && curpar != fParag && !cursor && stylesPar != curpar) {
02060
02061 curpar = curpar->p;
02062 delete curpar->n;
02063 }
02064
02065 if (!anchorName.isEmpty() ) {
02066 curpar->at(curpar->length() - 1)->setAnchor(anchorName, curpar->at(curpar->length() - 1)->anchorHref());
02067 anchorName.clear();
02068 }
02069
02070 setRichTextMarginsInternal(styles, stylesPar);
02071
02072 if (cursor) {
02073 cursor->gotoPreviousLetter();
02074 cursor->remove();
02075 }
02076 while (!styles.isEmpty())
02077 delete styles.takeFirst();
02078 delete vec;
02079 }
02080
02081 void Q3TextDocument::setRichTextMarginsInternal(QList< QVector<Q3StyleSheetItem *> *>& styles, Q3TextParagraph* stylesPar)
02082 {
02083
02084
02085 QVector<Q3StyleSheetItem *>* prevStyle = 0;
02086 int stylesIndex = 0;
02087 QVector<Q3StyleSheetItem *>* curStyle = styles.size() ? styles.first() : 0;
02088 QVector<Q3StyleSheetItem *>* nextStyle =
02089 (++stylesIndex) < styles.size() ? styles.at(stylesIndex) : 0;
02090 while (stylesPar) {
02091 if (!curStyle) {
02092 stylesPar = stylesPar->next();
02093 prevStyle = curStyle;
02094 curStyle = nextStyle;
02095 nextStyle = (++stylesIndex) < styles.size() ? styles.at(stylesIndex) : 0;
02096 continue;
02097 }
02098
02099 int i, mar;
02100 Q3StyleSheetItem* mainStyle = curStyle->size() ? (*curStyle)[curStyle->size()-1] : 0;
02101 if (mainStyle && mainStyle->displayMode() == Q3StyleSheetItem::DisplayListItem)
02102 stylesPar->setListItem(true);
02103 int numLists = 0;
02104 for (i = 0; i < (int)curStyle->size(); ++i) {
02105 if ((*curStyle)[i]->displayMode() == Q3StyleSheetItem::DisplayBlock
02106 && (*curStyle)[i]->listStyle() != Q3StyleSheetItem::ListStyleUndefined)
02107 numLists++;
02108 }
02109 stylesPar->ldepth = numLists;
02110 if (stylesPar->next() && nextStyle) {
02111
02112 numLists = 0;
02113 for (i = 0; i < (int)nextStyle->size(); ++i) {
02114 if ((*nextStyle)[i]->displayMode() == Q3StyleSheetItem::DisplayBlock
02115 && (*nextStyle)[i]->listStyle() != Q3StyleSheetItem::ListStyleUndefined)
02116 numLists++;
02117 }
02118 stylesPar->next()->ldepth = numLists;
02119 }
02120
02121
02122 Q3StyleSheetItem* item = mainStyle;
02123 int m;
02124 if (stylesPar->utm > 0) {
02125 m = stylesPar->utm-1;
02126 stylesPar->utm = 0;
02127 } else {
02128 m = qMax(0, item->margin(Q3StyleSheetItem::MarginTop));
02129 if (stylesPar->ldepth)
02130 if (item->displayMode() == Q3StyleSheetItem::DisplayListItem)
02131 m /= stylesPar->ldepth * stylesPar->ldepth;
02132 else
02133 m = 0;
02134 }
02135 for (i = (int)curStyle->size() - 2 ; i >= 0; --i) {
02136 item = (*curStyle)[i];
02137 if (prevStyle && i < (int) prevStyle->size() &&
02138 ( item->displayMode() == Q3StyleSheetItem::DisplayBlock &&
02139 (*prevStyle)[i] == item))
02140 break;
02141
02142 if (item->listStyle() != Q3StyleSheetItem::ListStyleUndefined &&
02143 (( i> 0 && (*curStyle)[i-1] == item) || (*curStyle)[i+1] == item))
02144 continue;
02145 mar = qMax(0, item->margin(Q3StyleSheetItem::MarginTop));
02146 m = qMax(m, mar);
02147 }
02148 stylesPar->utm = m - stylesPar->topMargin();
02149
02150
02151 item = mainStyle;
02152 if (stylesPar->ubm > 0) {
02153 m = stylesPar->ubm-1;
02154 stylesPar->ubm = 0;
02155 } else {
02156 m = qMax(0, item->margin(Q3StyleSheetItem::MarginBottom));
02157 if (stylesPar->ldepth)
02158 if (item->displayMode() == Q3StyleSheetItem::DisplayListItem)
02159 m /= stylesPar->ldepth * stylesPar->ldepth;
02160 else
02161 m = 0;
02162 }
02163 for (i = (int)curStyle->size() - 2 ; i >= 0; --i) {
02164 item = (*curStyle)[i];
02165 if (nextStyle && i < (int) nextStyle->size() &&
02166 ( item->displayMode() == Q3StyleSheetItem::DisplayBlock &&
02167 (*nextStyle)[i] == item))
02168 break;
02169
02170 if (item->listStyle() != Q3StyleSheetItem::ListStyleUndefined &&
02171 (( i> 0 && (*curStyle)[i-1] == item) || (*curStyle)[i+1] == item))
02172 continue;
02173 mar = qMax(0, item->margin(Q3StyleSheetItem::MarginBottom));
02174 m = qMax(m, mar);
02175 }
02176 stylesPar->ubm = m - stylesPar->bottomMargin();
02177
02178
02179 item = mainStyle;
02180 if (stylesPar->ulm > 0) {
02181 m = stylesPar->ulm-1;
02182 stylesPar->ulm = 0;
02183 } else {
02184 m = qMax(0, item->margin(Q3StyleSheetItem::MarginLeft));
02185 }
02186 for (i = (int)curStyle->size() - 2 ; i >= 0; --i) {
02187 item = (*curStyle)[i];
02188 m += qMax(0, item->margin(Q3StyleSheetItem::MarginLeft));
02189 }
02190 stylesPar->ulm = m - stylesPar->leftMargin();
02191
02192
02193 item = mainStyle;
02194 if (stylesPar->urm > 0) {
02195 m = stylesPar->urm-1;
02196 stylesPar->urm = 0;
02197 } else {
02198 m = qMax(0, item->margin(Q3StyleSheetItem::MarginRight));
02199 }
02200 for (i = (int)curStyle->size() - 2 ; i >= 0; --i) {
02201 item = (*curStyle)[i];
02202 m += qMax(0, item->margin(Q3StyleSheetItem::MarginRight));
02203 }
02204 stylesPar->urm = m - stylesPar->rightMargin();
02205
02206
02207 item = mainStyle;
02208 if (stylesPar->uflm > 0) {
02209 m = stylesPar->uflm-1;
02210 stylesPar->uflm = 0;
02211 } else {
02212 m = qMax(0, item->margin(Q3StyleSheetItem::MarginFirstLine));
02213 }
02214 for (i = (int)curStyle->size() - 2 ; i >= 0; --i) {
02215 item = (*curStyle)[i];
02216 mar = qMax(0, item->margin(Q3StyleSheetItem::MarginFirstLine));
02217 m = qMax(m, mar);
02218 }
02219 stylesPar->uflm =m - stylesPar->firstLineMargin();
02220
02221
02222 item = mainStyle;
02223 for (i = (int)curStyle->size() - 1 ; i >= 0; --i) {
02224 item = (*curStyle)[i];
02225 if (item->lineSpacing() != Q3StyleSheetItem::Undefined) {
02226 stylesPar->ulinespacing = item->lineSpacing();
02227 if (formatCollection() &&
02228 stylesPar->ulinespacing < formatCollection()->defaultFormat()->height())
02229 stylesPar->ulinespacing += formatCollection()->defaultFormat()->height();
02230 break;
02231 }
02232 }
02233
02234 stylesPar = stylesPar->next();
02235 prevStyle = curStyle;
02236 curStyle = nextStyle;
02237 nextStyle = (++stylesIndex) < styles.size() ? styles.at(stylesIndex) : 0;
02238 }
02239 }
02240
02241 void Q3TextDocument::setText(const QString &text, const QString &context)
02242 {
02243 focusIndicator.parag = 0;
02244 selections.clear();
02245 if (txtFormat == Qt::AutoText && Q3StyleSheet::mightBeRichText(text) ||
02246 txtFormat == Qt::RichText)
02247 setRichText(text, context);
02248 else
02249 setPlainText(text);
02250 }
02251
02252 QString Q3TextDocument::plainText() const
02253 {
02254 QString buffer;
02255 QString s;
02256 Q3TextParagraph *p = fParag;
02257 while (p) {
02258 if (!p->mightHaveCustomItems) {
02259 const Q3TextString *ts = p->string();
02260 s = ts->toString();
02261 } else {
02262 for (int i = 0; i < p->length() - 1; ++i) {
02263 #ifndef QT_NO_TEXTCUSTOMITEM
02264 if (p->at(i)->isCustom()) {
02265 if (p->at(i)->customItem()->isNested()) {
02266 s += "\n";
02267 Q3TextTable *t = (Q3TextTable*)p->at(i)->customItem();
02268 QList<Q3TextTableCell *> cells = t->tableCells();
02269 for (int idx = 0; idx < cells.size(); ++idx) {
02270 Q3TextTableCell *c = cells.at(idx);
02271 s += c->richText()->plainText() + "\n";
02272 }
02273 s += "\n";
02274 }
02275 } else
02276 #endif
02277 {
02278 s += p->at(i)->c;
02279 }
02280 }
02281 }
02282 s.remove(s.length() - 1, 1);
02283 if (p->next())
02284 s += "\n";
02285 buffer += s;
02286 p = p->next();
02287 }
02288 return buffer;
02289 }
02290
02291 static QString align_to_string(int a)
02292 {
02293 if (a & Qt::AlignRight)
02294 return " align=\"right\"";
02295 if (a & Qt::AlignHCenter)
02296 return " align=\"center\"";
02297 if (a & Qt::AlignJustify)
02298 return " align=\"justify\"";
02299 return QString();
02300 }
02301
02302 static QString direction_to_string(int dir)
02303 {
02304 if (dir != QChar::DirON)
02305 return (dir == QChar::DirL? " dir=\"ltr\"" : " dir=\"rtl\"");
02306 return QString();
02307 }
02308
02309 static QString list_value_to_string(int v)
02310 {
02311 if (v != -1)
02312 return " listvalue=\"" + QString::number(v) + "\"";
02313 return QString();
02314 }
02315
02316 static QString list_style_to_string(int v)
02317 {
02318 switch(v) {
02319 case Q3StyleSheetItem::ListDecimal: return "\"1\"";
02320 case Q3StyleSheetItem::ListLowerAlpha: return "\"a\"";
02321 case Q3StyleSheetItem::ListUpperAlpha: return "\"A\"";
02322 case Q3StyleSheetItem::ListDisc: return "\"disc\"";
02323 case Q3StyleSheetItem::ListSquare: return "\"square\"";
02324 case Q3StyleSheetItem::ListCircle: return "\"circle\"";
02325 default:
02326 return QString();
02327 }
02328 }
02329
02330 static inline bool list_is_ordered(int v)
02331 {
02332 return v == Q3StyleSheetItem::ListDecimal ||
02333 v == Q3StyleSheetItem::ListLowerAlpha ||
02334 v == Q3StyleSheetItem::ListUpperAlpha;
02335 }
02336
02337
02338 static QString margin_to_string(Q3StyleSheetItem* style, int t, int b, int l, int r, int fl)
02339 {
02340 QString s;
02341 if (l > 0)
02342 s += QString(s.size() ? ";" : "") + "margin-left:" + QString::number(l+qMax(0,style->margin(Q3StyleSheetItem::MarginLeft))) + "px";
02343 if (r > 0)
02344 s += QString(s.size() ? ";" : "") + "margin-right:" + QString::number(r+qMax(0,style->margin(Q3StyleSheetItem::MarginRight))) + "px";
02345 if (t > 0)
02346 s += QString(s.size() ? ";" : "") + "margin-top:" + QString::number(t+qMax(0,style->margin(Q3StyleSheetItem::MarginTop))) + "px";
02347 if (b > 0)
02348 s += QString(s.size() ? ";" : "") + "margin-bottom:" + QString::number(b+qMax(0,style->margin(Q3StyleSheetItem::MarginBottom))) + "px";
02349 if (fl > 0)
02350 s += QString(s.size() ? ";" : "") + "text-indent:" + QString::number(fl+qMax(0,style->margin(Q3StyleSheetItem::MarginFirstLine))) + "px";
02351 if (s.size())
02352 return " style=\"" + s + "\"";
02353 return QString();
02354 }
02355
02356 QString Q3TextDocument::richText() const
02357 {
02358 QString s = "";
02359 if (!par) {
02360 s += "<html><head><meta name=\"qrichtext\" content=\"1\" /></head><body style=\"font-size:" ;
02361 s += QString::number(formatCollection()->defaultFormat()->font().pointSize());
02362 s += "pt;font-family:";
02363 s += formatCollection()->defaultFormat()->font().family();
02364 s +="\">";
02365 }
02366 Q3TextParagraph* p = fParag;
02367
02368 Q3StyleSheetItem* item_p = styleSheet()->item("p");
02369 Q3StyleSheetItem* item_div = styleSheet()->item("div");
02370 Q3StyleSheetItem* item_ul = styleSheet()->item("ul");
02371 Q3StyleSheetItem* item_ol = styleSheet()->item("ol");
02372 Q3StyleSheetItem* item_li = styleSheet()->item("li");
02373 if (!item_p || !item_div || !item_ul || !item_ol || !item_li) {
02374 qWarning("QTextEdit: cannot export HTML due to insufficient stylesheet (lack of p, div, ul, ol, or li)");
02375 return QString();
02376 }
02377 int pastListDepth = 0;
02378 int listDepth = 0;
02379 #if 0
02380 int futureListDepth = 0;
02381 #endif
02382 QVector<int> listStyles(10);
02383
02384 while (p) {
02385 listDepth = p->listDepth();
02386 if (listDepth < pastListDepth) {
02387 for (int i = pastListDepth; i > listDepth; i--)
02388 s += list_is_ordered(listStyles[i]) ? "</ol>" : "</ul>";
02389 s += '\n';
02390 } else if (listDepth > pastListDepth) {
02391 s += '\n';
02392 listStyles.resize(qMax((int)listStyles.size(), listDepth+1));
02393 QString list_type;
02394 listStyles[listDepth] = p->listStyle();
02395 if (!list_is_ordered(p->listStyle()) || item_ol->listStyle() != p->listStyle())
02396 list_type = " type=" + list_style_to_string(p->listStyle());
02397 for (int i = pastListDepth; i < listDepth; i++) {
02398 s += list_is_ordered(p->listStyle()) ? "<ol" : "<ul" ;
02399 s += list_type + ">";
02400 }
02401 } else {
02402 s += '\n';
02403 }
02404
02405 QString ps = p->richText();
02406
02407 #if 0
02408
02409 futureListDepth = 0;
02410 if (listDepth > 0 && p->next())
02411 futureListDepth = p->next()->listDepth();
02412 #endif
02413
02414 if (richTextExportStart && richTextExportStart->paragraph() ==p &&
02415 richTextExportStart->index() == 0)
02416 s += "<!--StartFragment-->";
02417
02418 if (p->isListItem()) {
02419 s += "<li";
02420 if (p->listStyle() != listStyles[listDepth])
02421 s += " type=" + list_style_to_string(p->listStyle());
02422 s +=align_to_string(p->alignment());
02423 s += margin_to_string(item_li, p->utm, p->ubm, p->ulm, p->urm, p->uflm);
02424 s += list_value_to_string(p->listValue());
02425 s += direction_to_string(p->direction());
02426 s +=">";
02427 s += ps;
02428 s += "</li>";
02429 } else if (p->listDepth()) {
02430 s += "<div";
02431 s += align_to_string(p->alignment());
02432 s += margin_to_string(item_div, p->utm, p->ubm, p->ulm, p->urm, p->uflm);
02433 s +=direction_to_string(p->direction());
02434 s += ">";
02435 s += ps;
02436 s += "</div>";
02437 } else {
02438
02439 s += "<p";
02440 s += align_to_string(p->alignment());
02441 s += margin_to_string(item_p, p->utm, p->ubm, p->ulm, p->urm, p->uflm);
02442 s +=direction_to_string(p->direction());
02443 s += ">";
02444 s += ps;
02445 s += "</p>";
02446 }
02447 pastListDepth = listDepth;
02448 p = p->next();
02449 }
02450 while (listDepth > 0) {
02451 s += list_is_ordered(listStyles[listDepth]) ? "</ol>" : "</ul>";
02452 listDepth--;
02453 }
02454
02455 if (!par)
02456 s += "\n</body></html>\n";
02457
02458 return s;
02459 }
02460
02461 QString Q3TextDocument::text() const
02462 {
02463 if (txtFormat == Qt::AutoText && preferRichText || txtFormat == Qt::RichText)
02464 return richText();
02465 return plainText();
02466 }
02467
02468 QString Q3TextDocument::text(int parag) const
02469 {
02470 Q3TextParagraph *p = paragAt(parag);
02471 if (!p)
02472 return QString();
02473
02474 if (txtFormat == Qt::AutoText && preferRichText || txtFormat == Qt::RichText)
02475 return p->richText();
02476 else
02477 return p->string()->toString();
02478 }
02479
02480 void Q3TextDocument::invalidate()
02481 {
02482 Q3TextParagraph *s = fParag;
02483 while (s) {
02484 s->invalidate(0);
02485 s = s->next();
02486 }
02487 }
02488
02489 void Q3TextDocument::selectionStart(int id, int ¶gId, int &index)
02490 {
02491 QMap<int, Q3TextDocumentSelection>::Iterator it = selections.find(id);
02492 if (it == selections.end())
02493 return;
02494 Q3TextDocumentSelection &sel = *it;
02495 paragId = !sel.swapped ? sel.startCursor.paragraph()->paragId() : sel.endCursor.paragraph()->paragId();
02496 index = !sel.swapped ? sel.startCursor.index() : sel.endCursor.index();
02497 }
02498
02499 Q3TextCursor Q3TextDocument::selectionStartCursor(int id)
02500 {
02501 QMap<int, Q3TextDocumentSelection>::Iterator it = selections.find(id);
02502 if (it == selections.end())
02503 return Q3TextCursor(this);
02504 Q3TextDocumentSelection &sel = *it;
02505 if (sel.swapped)
02506 return sel.endCursor;
02507 return sel.startCursor;
02508 }
02509
02510 Q3TextCursor Q3TextDocument::selectionEndCursor(int id)
02511 {
02512 QMap<int, Q3TextDocumentSelection>::Iterator it = selections.find(id);
02513 if (it == selections.end())
02514 return Q3TextCursor(this);
02515 Q3TextDocumentSelection &sel = *it;
02516 if (!sel.swapped)
02517 return sel.endCursor;
02518 return sel.startCursor;
02519 }
02520
02521 void Q3TextDocument::selectionEnd(int id, int ¶gId, int &index)
02522 {
02523 QMap<int, Q3TextDocumentSelection>::Iterator it = selections.find(id);
02524 if (it == selections.end())
02525 return;
02526 Q3TextDocumentSelection &sel = *it;
02527 paragId = sel.swapped ? sel.startCursor.paragraph()->paragId() : sel.endCursor.paragraph()->paragId();
02528 index = sel.swapped ? sel.startCursor.index() : sel.endCursor.index();
02529 }
02530
02531 void Q3TextDocument::addSelection(int id)
02532 {
02533 nSelections = qMax(nSelections, id + 1);
02534 }
02535
02536 static void setSelectionEndHelper(int id, Q3TextDocumentSelection &sel, Q3TextCursor &start, Q3TextCursor &end)
02537 {
02538 Q3TextCursor c1 = start;
02539 Q3TextCursor c2 = end;
02540 if (sel.swapped) {
02541 c1 = end;
02542 c2 = start;
02543 }
02544
02545 c1.paragraph()->removeSelection(id);
02546 c2.paragraph()->removeSelection(id);
02547 if (c1.paragraph() != c2.paragraph()) {
02548 c1.paragraph()->setSelection(id, c1.index(), c1.paragraph()->length() - 1);
02549 c2.paragraph()->setSelection(id, 0, c2.index());
02550 } else {
02551 c1.paragraph()->setSelection(id, qMin(c1.index(), c2.index()), qMax(c1.index(), c2.index()));
02552 }
02553
02554 sel.startCursor = start;
02555 sel.endCursor = end;
02556 if (sel.startCursor.paragraph() == sel.endCursor.paragraph())
02557 sel.swapped = sel.startCursor.index() > sel.endCursor.index();
02558 }
02559
02560 bool Q3TextDocument::setSelectionEnd(int id, const Q3TextCursor &cursor)
02561 {
02562 QMap<int, Q3TextDocumentSelection>::Iterator it = selections.find(id);
02563 if (it == selections.end())
02564 return false;
02565 Q3TextDocumentSelection &sel = *it;
02566
02567 Q3TextCursor start = sel.startCursor;
02568 Q3TextCursor end = cursor;
02569
02570 if (start == end) {
02571 removeSelection(id);
02572 setSelectionStart(id, cursor);
02573 return true;
02574 }
02575
02576 if (sel.endCursor.paragraph() == end.paragraph()) {
02577 setSelectionEndHelper(id, sel, start, end);
02578 return true;
02579 }
02580
02581 bool inSelection = false;
02582 Q3TextCursor c(this);
02583 Q3TextCursor tmp = sel.startCursor;
02584 if (sel.swapped)
02585 tmp = sel.endCursor;
02586 tmp.restoreState();
02587 Q3TextCursor tmp2 = cursor;
02588 tmp2.restoreState();
02589 c.setParagraph(tmp.paragraph()->paragId() < tmp2.paragraph()->paragId() ? tmp.paragraph() : tmp2.paragraph());
02590 bool hadStart = false;
02591 bool hadEnd = false;
02592 bool hadStartParag = false;
02593 bool hadEndParag = false;
02594 bool hadOldStart = false;
02595 bool hadOldEnd = false;
02596 bool leftSelection = false;
02597 sel.swapped = false;
02598 for (;;) {
02599 if (c == start)
02600 hadStart = true;
02601 if (c == end)
02602 hadEnd = true;
02603 if (c.paragraph() == start.paragraph())
02604 hadStartParag = true;
02605 if (c.paragraph() == end.paragraph())
0260