00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022 #include "khtml_caret_p.h"
00023
00024 namespace khtml {
00025
00026 static InlineFlowBox *findFlowBox(DOM::NodeImpl *node, long offset,
00027 RenderArena *arena, RenderFlow *&cb, InlineBox **ibox = 0);
00028 static RenderObject *nextLeafRenderObject(RenderObject *r);
00029
00034 static inline RenderObject *nextSuitableLeafRenderObject(RenderObject *r)
00035 {
00036 do {
00037 r = nextLeafRenderObject(r);
00038 } while (r && r->isTableCol());
00039 return r;
00040 }
00041
00043 static void ensureLeafNode(NodeImpl *&node)
00044 {
00045 if (node && node->hasChildNodes()) node = node->nextLeafNode();
00046 }
00047
00056 static RenderObject* findRenderer(NodeImpl *&node)
00057 {
00058 if (!node) return 0;
00059 RenderObject *r = node->renderer();
00060 while (!r) {
00061 node = node->nextLeafNode();
00062 if (!node) break;
00063 r = node->renderer();
00064 }
00065 if (r && r->isTableCol()) r = nextSuitableLeafRenderObject(r);
00066 return r;
00067 }
00068
00070 static void sanitizeCaretState(NodeImpl *&caretNode, long &offset)
00071 {
00072 ensureLeafNode(caretNode);
00073
00074
00075
00076
00077
00078 NodeImpl *tmpNode = caretNode;
00079 if (findRenderer(tmpNode)) caretNode = tmpNode;
00080 if (!caretNode) return;
00081
00082 long max = caretNode->maxOffset();
00083 long min = caretNode->minOffset();
00084 if (offset < min) offset = min;
00085 else if (offset > max) offset = max;
00086 }
00087
00089 static RenderObject *prevLeafRenderObject(RenderObject *r)
00090 {
00091 RenderObject *n = r->objectAbove();
00092 while (n && n == r->parent()) {
00093 if (n->previousSibling()) return n->objectAbove();
00094 r = n;
00095 n = r->parent();
00096 }
00097 return n;
00098 }
00099
00101 static RenderObject *nextLeafRenderObject(RenderObject *r)
00102 {
00103 RenderObject *n = r->objectBelow();
00104 r = n;
00105 while (n) r = n, n = n->firstChild();
00106 return r;
00107 }
00108
00113 static RenderObject *prevSuitableLeafRenderObject(RenderObject *r)
00114 {
00115 do {
00116 r = prevLeafRenderObject(r);
00117 } while (r && r->isTableCol());
00118 return r;
00119 }
00120
00123 static inline InlineBox *seekLeafInlineBox(InlineBox *box)
00124 {
00125 while (box && box->isInlineFlowBox()) {
00126
00127 box = static_cast<InlineFlowBox *>(box)->firstChild();
00128
00129
00130
00131
00132 }
00133 return box;
00134 }
00135
00138 static inline InlineBox *seekLeafInlineBoxFromEnd(InlineBox *box)
00139 {
00140 while (box && box->isInlineFlowBox()) {
00141 box = static_cast<InlineFlowBox *>(box)->lastChild();
00142 }
00143 #if DEBUG_CARETMODE > 0
00144 kdDebug(6200) << "seekLeafFromEnd: box " << box << (box && box->isInlineTextBox() ? QString(" contains \"%1\"").arg(QConstString(static_cast<RenderText *>(box->object())->str->s+box->minOffset(), box->maxOffset() - box->minOffset()).string()) : QString::null) << endl;
00145 #endif
00146 return box;
00147 }
00148
00149
00150
00151 InlineBox *LineIterator::currentBox;
00152
00153 InlineBoxIterator::InlineBoxIterator(RenderArena *arena, InlineFlowBox *flowBox, bool fromEnd)
00154 : arena(arena)
00155 {
00156 box = fromEnd ? seekLeafInlineBoxFromEnd(flowBox) : seekLeafInlineBox(flowBox);
00157 }
00158
00159 InlineBoxIterator::InlineBoxIterator(LineIterator &lit, bool fromEnd,
00160 InlineBox *initBox)
00161 : arena(lit.lines->arena)
00162 {
00163 if (initBox) box = initBox;
00164 else box = fromEnd ? seekLeafInlineBoxFromEnd(*lit) : seekLeafInlineBox(*lit);
00165 }
00166
00167
00168 InlineBoxIterator& InlineBoxIterator::operator ++()
00169 {
00170 InlineBox *newBox = box->nextOnLine();
00171
00172 if (newBox)
00173 box = seekLeafInlineBox(newBox);
00174 else {
00175 InlineFlowBox *flowBox = box->parent();
00176 box = 0;
00177 while (flowBox) {
00178 InlineBox *newBox2 = flowBox->nextOnLine();
00179 if (newBox2) {
00180 box = seekLeafInlineBox(newBox2);
00181 break;
00182 }
00183
00184 flowBox = flowBox->parent();
00185 }
00186 }
00187
00188 return *this;
00189 }
00190
00194 InlineBoxIterator& InlineBoxIterator::operator --()
00195 {
00196 InlineBox *newBox = box->prevOnLine();
00197
00198 if (newBox)
00199 box = seekLeafInlineBoxFromEnd(newBox);
00200 else {
00201 InlineFlowBox *flowBox = box->parent();
00202 box = 0;
00203 while (flowBox) {
00204 InlineBox *newBox2 = flowBox->prevOnLine();
00205 if (newBox2) {
00206 box = seekLeafInlineBoxFromEnd(newBox2);
00207 break;
00208 }
00209
00210 flowBox = flowBox->parent();
00211 }
00212 }
00213
00214 return *this;
00215 }
00216
00233 static InlineFlowBox* generateDummyFlowBox(RenderArena *arena, RenderFlow *cb,
00234 RenderObject *childNodeHint = 0)
00235 {
00236 InlineFlowBox *flowBox = new(arena) InlineFlowBox(cb);
00237 int width = cb->width();
00238
00239
00240
00241
00242
00243 int height = cb->style()->fontMetrics().height();
00244 flowBox->setWidth(0);
00245 flowBox->setHeight(height);
00246
00247
00248 InlineBox *child = new(arena) InlineBox(childNodeHint ? childNodeHint : cb);
00249
00250 switch (cb->style()->textAlign()) {
00251 case LEFT:
00252 case TAAUTO:
00253 case JUSTIFY:
00254 child->setXPos(0);
00255 break;
00256 case CENTER:
00257 case KONQ_CENTER:
00258 child->setXPos(width / 2);
00259 break;
00260 case RIGHT:
00261 child->setXPos(width);
00262 break;
00263 }
00264 child->setYPos(0);
00265 child->setWidth(1);
00266 child->setHeight(height);
00267
00268 flowBox->setXPos(child->xPos());
00269 flowBox->setYPos(child->yPos());
00270 flowBox->addToLine(child);
00271
00272 return flowBox;
00273 }
00274
00280 static RenderFlow* generateDummyBlock(RenderArena *, RenderObject *cb)
00281 {
00282
00283 RenderFlow *result = RenderFlow::createFlow(cb->element(), cb->style(), cb->renderArena());
00284 result->setParent(cb->parent());
00285 result->setPreviousSibling(cb->previousSibling());
00286 result->setNextSibling(cb->nextSibling());
00287
00288 result->setOverhangingContents(cb->overhangingContents());
00289 result->setPositioned(cb->isPositioned());
00290 result->setRelPositioned(cb->isRelPositioned());
00291 result->setFloating(cb->isFloating());
00292 result->setInline(cb->isInline());
00293 result->setMouseInside(cb->mouseInside());
00294
00295 result->setPos(cb->xPos(), cb->yPos());
00296 result->setWidth(cb->width());
00297 result->setHeight(cb->height());
00298
00299 return result;
00300 }
00301
00317 static InlineFlowBox* findFlowBox(DOM::NodeImpl *node, long offset,
00318 RenderArena *arena, RenderFlow *&cb, InlineBox **ibox)
00319 {
00320 RenderObject *r = findRenderer(node);
00321 if (!r) { cb = 0; return 0; }
00322 #if DEBUG_CARETMODE > 0
00323 kdDebug(6200) << "=================== findFlowBox" << endl;
00324 kdDebug(6200) << "node " << node << " r " << r->renderName() << "[" << r << "].node " << r->element()->nodeName().string() << "[" << r->element() << "]" << " offset: " << offset << endl;
00325 #endif
00326
00327
00328
00329
00330 if (r->isRenderBlock() && !static_cast<RenderBlock *>(r)->firstLineBox()) {
00331 cb = static_cast<RenderBlock *>(r);
00332 #if DEBUG_CARETMODE > 0
00333 kdDebug(6200) << "=================== end findFlowBox (dummy)" << endl;
00334 #endif
00335 InlineFlowBox *fb = generateDummyFlowBox(arena, cb);
00336 if (ibox) *ibox = fb;
00337 return fb;
00338 }
00339
00340
00341
00342
00343
00344
00345
00346 if (r->isText()) do {
00347 RenderText *t = static_cast<RenderText *>(r);
00348 int dummy;
00349 InlineBox *b = t->findInlineTextBox(offset, dummy, true);
00350
00351
00352
00353 if (!b) {
00354 if (t->m_lines.count() > 0)
00355 b = t->m_lines[t->m_lines.count() - 1];
00356 else
00357 break;
00358 }
00359 Q_ASSERT(b);
00360 if (ibox) *ibox = b;
00361 while (b->parent()) {
00362 b = b->parent();
00363 }
00364
00365 Q_ASSERT(b->isRootInlineBox());
00366 cb = static_cast<RenderFlow *>(b->object());
00367 Q_ASSERT(cb->isRenderBlock());
00368 #if DEBUG_CARETMODE > 0
00369 kdDebug(6200) << "=================== end findFlowBox (renderText)" << endl;
00370 #endif
00371 return static_cast<InlineFlowBox *>(b);
00372 } while(false);
00373
00374 cb = r->containingBlock();
00375 if ( !cb ) return 0L;
00376
00377 if (!cb->isRenderBlock()) {
00378 cb = generateDummyBlock(arena, cb);
00379 #if DEBUG_CARETMODE > 0
00380 kdDebug(6200) << "dummy block created: " << cb << endl;
00381 #endif
00382 }
00383
00384 InlineFlowBox *flowBox = cb->firstLineBox();
00385
00386
00387
00388 if (!flowBox) {
00389 flowBox = generateDummyFlowBox(arena, cb, r);
00390 if (ibox) *ibox = flowBox->firstChild();
00391 #if DEBUG_CARETMODE > 0
00392 kdDebug(6200) << "=================== end findFlowBox (2)" << endl;
00393 #endif
00394 return flowBox;
00395 }
00396
00397
00398
00399
00400 for (; flowBox; flowBox = static_cast<InlineFlowBox *>(flowBox->nextLineBox())) {
00401 #if DEBUG_CARETMODE > 0
00402 kdDebug(6200) << "[scan line]" << endl;
00403 #endif
00404
00405
00406 InlineBox *box;
00407 InlineBoxIterator it(arena, flowBox);
00408 for (; (box = *it) != 0; ++it) {
00409 RenderObject *br = box->object();
00410 if (!br) continue;
00411
00412 #if DEBUG_CARETMODE > 0
00413 kdDebug(6200) << "box->obj " << br->renderName() << "[" << br << "]" << " minOffset: " << box->minOffset() << " maxOffset: " << box->maxOffset() << endl;
00414 #endif
00415 if (br == r && offset >= box->minOffset() && offset <= box->maxOffset())
00416 break;
00417 }
00418 if (box) {
00419 if (ibox) *ibox = box;
00420 break;
00421 }
00422
00423 }
00424
00425
00426
00427
00428 if (!flowBox) flowBox = findFlowBox(node->nextLeafNode(), 0, arena, cb, ibox);
00429
00430 #if DEBUG_CARETMODE > 0
00431 kdDebug(6200) << "=================== end findFlowBox" << endl;
00432 #endif
00433 return flowBox;
00434 }
00435
00442 static inline RenderTable *findTableUpTo(RenderObject *r, RenderFlow *cb)
00443 {
00444 while (r && r != cb && !r->isTable()) r = r->parent();
00445 return r && r->isTable() ? static_cast<RenderTable *>(r) : 0;
00446 }
00447
00450 static inline bool isDescendant(RenderObject *r, RenderObject *cb)
00451 {
00452 while (r && r != cb) r = r->parent();
00453 return r;
00454 }
00455
00466 static bool containsEditableElement(KHTMLPart *part, RenderFlow *cb,
00467 RenderTable *&table, bool fromEnd = false)
00468 {
00469 RenderObject *r = cb;
00470 if (fromEnd)
00471 while (r->lastChild()) r = r->lastChild();
00472 else
00473 while (r->firstChild()) r = r->firstChild();
00474
00475 RenderTable *tempTable = 0;
00476 table = 0;
00477 bool withinCb;
00478 do {
00479 tempTable = findTableUpTo(r, cb);
00480 withinCb = isDescendant(r, cb);
00481
00482 #if DEBUG_CARETMODE > 1
00483 kdDebug(6201) << "r " << (r ? r->renderName() : QString::null) << "@" << r << endl;
#endif
if (r && withinCb && r->element() && !r->isTableCol()
&& (part->isCaretMode() || part->isEditable()
|| r->style()->userInput() == UI_ENABLED)) {
table = tempTable;
return true;
}/*end if*/
r = fromEnd ? prevSuitableLeafRenderObject(r) : nextSuitableLeafRenderObject(r);
} while (r && withinCb);
return false;
}
00496 static bool containsEditableChildElement(KHTMLPart *part, RenderFlow *cb,
RenderTable *&table, bool fromEnd, RenderObject *start)
{
RenderObject *r = start;
if (fromEnd)
while (r->firstChild()) r = r->firstChild();
else
while (r->lastChild()) r = r->lastChild();
if (!r) return false;
RenderTable *tempTable = 0;
table = 0;
bool withinCb = false;
do {
r = fromEnd ? prevSuitableLeafRenderObject(r) : nextSuitableLeafRenderObject(r);
if (!r) break;
withinCb = isDescendant(r, cb) && r != cb;
tempTable = findTableUpTo(r, cb);
#if DEBUG_CARETMODE > 1
kdDebug(6201) << "r " << (r ? r->renderName() : QString::null) << "@" << r << endl;
#endif
if (r && withinCb && r->element() && !r->isTableCol()
&& (part->isCaretMode() || part->isEditable()
|| r->style()->userInput() == UI_ENABLED)) {
table = tempTable;
return true;
}/*end if*/
} while (withinCb);
return false;
}
// == class LinearDocument implementation
LinearDocument::LinearDocument(KHTMLPart *part, NodeImpl *node, long offset)
: arena(0), node(node), offset(offset), m_part(part)
{
if (node == 0) return;
sanitizeCaretState(this->node, this->offset);
arena = new RenderArena(512);
initPreBeginIterator();
initEndIterator();
//m_part = node->getDocument()->view()->part();
}
LinearDocument::~LinearDocument()
{
delete arena;
}
int LinearDocument::count() const
{
// FIXME: not implemented
return 1;
}
LinearDocument::Iterator LinearDocument::current()
{
return LineIterator(this, node, offset);
}
LinearDocument::Iterator LinearDocument::begin()
{
DocumentImpl *doc = node ? node->getDocument() : 0;
if (!doc) return end();
NodeImpl *firstLeaf = doc->nextLeafNode();
if (!firstLeaf) return end(); // must be empty document (is this possible?)
return LineIterator(this, firstLeaf, firstLeaf->minOffset());
}
LinearDocument::Iterator LinearDocument::preEnd()
{
DocumentImpl *doc = node ? node->getDocument() : 0;
if (!doc) return preBegin();
NodeImpl *lastLeaf = doc;
while (lastLeaf->lastChild()) lastLeaf = lastLeaf->lastChild();
if (!lastLeaf) return preBegin(); // must be empty document (is this possible?)
return LineIterator(this, lastLeaf, lastLeaf->maxOffset());
}
void LinearDocument::initPreBeginIterator()
{
_preBegin = LineIterator(this, 0, 0);
}
void LinearDocument::initEndIterator()
{
_end = LineIterator(this, 0, 1);
}
// == class LineIterator implementation
LineIterator::LineIterator(LinearDocument *l, DOM::NodeImpl *node, long offset)
: lines(l)
{
// kdDebug(6200) << "LineIterator: node " << node << " offset " << offset << endl;
00497 flowBox = findFlowBox(node, offset, lines->arena, cb, ¤tBox);
00498 if (!flowBox) {
00499 #if DEBUG_CARETMODE > 0
00500 kdDebug(6200) << "LineIterator: findFlowBox failed" << endl;
00501 #endif
00502 cb = 0;
00503 }
00504 }
00505
00506 void LineIterator::nextBlock()
00507 {
00508 RenderObject *r = cb;
00509 RenderObject *n = r->lastChild();
00510 while (n) r = n, n = r->lastChild();
00511 r = nextSuitableLeafRenderObject(r);
00512 #if DEBUG_CARETMODE > 0
00513 kdDebug(6200) << "++: r " << r << "[" << (r?r->renderName():QString::null) << "]" << endl;
00514 #endif
00515 if (!r) {
00516 cb = 0;
00517 return;
00518 }
00519
00520
00521
00522 if (r->isRenderBlock()) {
00523 cb = static_cast<RenderBlock *>(r);
00524 #if DEBUG_CARETMODE > 0
00525 kdDebug(6200) << "r->isFlow is cb. continuation @" << cb->continuation() << endl;
00526 #endif
00527
00528
00529
00530
00531 RenderFlow *flow = static_cast<RenderFlow *>(cb->element()
00532 ? cb->element()->renderer() : 0);
00533 if (cb->continuation() || flow && flow->isRenderBlock() && flow != cb
00534 && flow->continuation()) {
00535 nextBlock();
00536 return;
00537 }
00538 } else {
00539 cb = static_cast<RenderFlow *>(r->containingBlock());
00540 if (!cb->isRenderBlock()) {
00541 #if DEBUG_CARETMODE > 0
00542 kdDebug(6200) << "dummy cb created " << cb << endl;
00543 #endif
00544 cb = generateDummyBlock(lines->arena, r);
00545 }
00546 }
00547 flowBox = cb->firstLineBox();
00548 #if DEBUG_CARETMODE > 0
00549 kdDebug(6200) << "++: flowBox " << flowBox << endl;
00550 #endif
00551
00552 if (!flowBox) flowBox = generateDummyFlowBox(lines->arena, cb, r);
00553 #if DEBUG_CARETMODE > 0
00554 if (!cb->firstLineBox()) kdDebug(6200) << "++: dummy flowBox " << flowBox << endl;
00555 #endif
00556 }
00557
00558 inline LineIterator &LineIterator::operator ++()
00559 {
00560 flowBox = static_cast<InlineFlowBox *>(flowBox->nextLineBox());
00561
00562
00563
00564 if (!flowBox) nextBlock();
00565
00566 return *this;
00567 }
00568
00569 inline LineIterator LineIterator::operator ++(int)
00570 {
00571 LineIterator it(*this);
00572 operator ++();
00573 return it;
00574 }
00575
00576 void LineIterator::prevBlock()
00577 {
00578 RenderObject *r = cb;
00579 RenderObject *n = r->firstChild();
00580 while (n) r = n, n = r->firstChild();
00581 r = prevSuitableLeafRenderObject(r);
00582 if (!r) {
00583 cb = 0;
00584 return;
00585 }
00586
00587
00588
00589 if (r->isRenderBlock()) {
00590 cb = static_cast<RenderFlow *>(r);
00591 #if DEBUG_CARETMODE > 0
00592 kdDebug(6200) << "r->isFlow is cb. continuation @" << cb->continuation() << endl;
00593 #endif
00594
00595
00596
00597
00598 RenderFlow *flow = static_cast<RenderFlow *>(cb->element()
00599 ? cb->element()->renderer() : 0);
00600 if (cb->continuation() || flow && flow->isRenderBlock() && flow != cb
00601 && flow->continuation()) {
00602 prevBlock();
00603 return;
00604 }
00605 } else {
00606 cb = static_cast<RenderFlow *>(r->containingBlock());
00607 if (!cb->isRenderBlock()) {
00608 #if DEBUG_CARETMODE > 0
00609 kdDebug(6200) << "dummy cb created " << cb << endl;
00610 #endif
00611 cb = generateDummyBlock(lines->arena, r);
00612 }
00613 }
00614 flowBox = cb->lastLineBox();
00615
00616 if (!flowBox) flowBox = generateDummyFlowBox(lines->arena, cb, r);
00617 }
00618
00619 inline LineIterator &LineIterator::operator --()
00620 {
00621 flowBox = static_cast<InlineFlowBox *>(flowBox->prevLineBox());
00622
00623
00624
00625 if (!flowBox) prevBlock();
00626
00627 return *this;
00628 }
00629
00630 inline LineIterator LineIterator::operator --(int)
00631 {
00632 LineIterator it(*this);
00633 operator --();
00634 return it;
00635 }
00636
00637 #if 0 // not implemented because it's not needed
00638 LineIterator LineIterator::operator +(int ) const
00639 {
00640
00641 return LineIterator();
00642 }
00643
00644 LineIterator LineIterator::operator -(int ) const
00645 {
00646
00647 return LineIterator();
00648 }
00649 #endif
00650
00651 LineIterator &LineIterator::operator +=(int summand)
00652 {
00653 if (summand > 0)
00654 while (summand-- && *this != lines->end()) ++*this;
00655 else if (summand < 0)
00656 operator -=(-summand);
00657 return *this;
00658 }
00659
00660 LineIterator &LineIterator::operator -=(int summand)
00661 {
00662 if (summand > 0)
00663 while (summand-- && *this != lines->preBegin()) --*this;
00664 else if (summand < 0)
00665 operator +=(-summand);
00666 return *this;
00667 }
00668
00669
00670
00671 void EditableCharacterIterator::initFirstChar()
00672 {
00673 InlineBox *b = *ebit;
00674 if (b) {
00675 if (_offset == b->maxOffset())
00676 peekNext();
00677 else if (b->isInlineTextBox())
00678 _char = static_cast<RenderText *>(b->object())->str->s[_offset].unicode();
00679 else
00680 _char = -1;
00681 }
00682 }
00683
00684 EditableCharacterIterator &EditableCharacterIterator::operator ++()
00685 {
00686 _offset++;
00687
00688 InlineBox *b = *ebit;
00689 RenderObject *r = b->object();
00690
00691
00692
00693 long maxofs = r->isBR() || r->isRenderBlock() ? b->minOffset() : b->maxOffset();
00694 #if DEBUG_CARETMODE > 0
00695 kdDebug(6200) << "b->maxOffset() " << b->maxOffset() << " b->minOffset() " << b->minOffset() << endl;
00696 #endif
00697 if (_offset == maxofs) {
00698 #if DEBUG_CARETMODE > 2
00699 kdDebug(6200) << "_offset == maxofs: " << _offset << " == " << maxofs << endl;
00700 #endif
00701
00702 peekNext();
00703 } else if (_offset > maxofs) {
00704 #if DEBUG_CARETMODE > 2
00705 kdDebug(6200) << "_offset > maxofs: " << _offset << " > " << maxofs << endl;
00706 #endif
00707 if (true) {
00708 if (*ebit)
00709 ++ebit;
00710 if (!*ebit) {
00711 ++_it;
00712 #if DEBUG_CARETMODE > 3
00713 kdDebug(6200) << "++_it" << endl;
00714 #endif
00715 if (_it != ld->end()) {
00716 ebit = _it;
00717 b = *ebit;
00718 #if DEBUG_CARETMODE > 3
00719 kdDebug(6200) << "b " << b << " isText " << b->isInlineTextBox() << endl;
00720 #endif
00721 _node = b->object()->element();
00722 #if DEBUG_CARETMODE > 3
00723 kdDebug(6200) << "_node " << _node << ":" << _node->nodeName().string() << endl;
00724 #endif
00725 _offset = b->minOffset();
00726 #if DEBUG_CARETMODE > 3
00727 kdDebug(6200) << "_offset " << _offset << endl;
00728 #endif
00729 } else {
00730 _node = 0;
00731 b = 0;
00732 }
00733 goto readchar;
00734 }
00735 }
00736 bool adjacent = ebit.isAdjacent();
00737
00738 if (adjacent && !(*ebit)->isInlineTextBox()) {
00739 EditableInlineBoxIterator copy = ebit;
00740 ++ebit;
00741 if (*ebit && (*ebit)->isInlineTextBox()) adjacent = false;
00742 else ebit = copy;
00743 }
00744 _node = (*ebit)->object()->element();
00745 _offset = (*ebit)->minOffset() + adjacent;
00746
00747 b = *ebit;
00748 goto readchar;
00749 } else {
00750 readchar:
00751
00752 if (b && b->isInlineTextBox() && _offset < b->maxOffset())
00753 _char = static_cast<RenderText *>(b->object())->str->s[_offset].unicode();
00754 else
00755 _char = -1;
00756 }
00757 #if DEBUG_CARETMODE > 2
00758 kdDebug(6200) << "_offset: " << _offset << " char '" << (char)_char << "'" << endl;
00759 #endif
00760
00761 #if DEBUG_CARETMODE > 0
00762 if (*ebit) {
00763 InlineBox *box = *ebit;
00764 kdDebug(6200) << "echit++(1): box " << box << (box && box->isInlineTextBox() ? QString(" contains \"%1\"").arg(QConstString(static_cast<RenderText *>(box->object())->str->s+box->minOffset(), box->maxOffset() - box->minOffset()).string()) : QString::null) << " node " << (_node ? _node->nodeName().string() : QString("<nil>")) << endl;
00765 }
00766 #endif
00767 return *this;
00768 }
00769
00770 EditableCharacterIterator &EditableCharacterIterator::operator --()
00771 {
00772 _offset--;
00773
00774
00775 InlineBox *b = *ebit;
00776 InlineBox *_peekPrev = 0;
00777 InlineBox *_peekNext = 0;
00778 long minofs = b ? b->minOffset() : _offset + 1;
00779 #if DEBUG_CARETMODE > 0
00780 kdDebug(6200) << "b->maxOffset() " << b->maxOffset() << " b->minOffset() " << b->minOffset() << endl;
00781 #endif
00782 if (_offset == minofs) {
00783 #if DEBUG_CARETMODE > 2
00784 kdDebug(6200) << "_offset == minofs: " << _offset << " == " << minofs << endl;
00785 #endif
00786 _peekNext = b;
00787
00788 if (b && b->isInlineTextBox())
00789 _char = static_cast<RenderText *>(b->object())->text()[_offset].unicode();
00790 else
00791 _char = -1;
00792
00793
00794 bool do_prev = false;
00795 {
00796 EditableInlineBoxIterator copy = ebit;
00797 --ebit;
00798 _peekPrev = *ebit;
00799
00800 if (ebit.isAdjacent() && *ebit && (*ebit)->isInlineTextBox())
00801
00802 do_prev = true;
00803 else
00804 ebit = copy;
00805 }
00806 if (do_prev) goto prev;
00807 } else if (_offset < minofs) {
00808 prev:
00809 #if DEBUG_CARETMODE > 2
00810 kdDebug(6200) << "_offset < minofs: " << _offset << " < " << minofs << endl;
00811 #endif
00812 if (!_peekPrev) {
00813 _peekNext = *ebit;
00814 if (*ebit)
00815 --ebit;
00816 if (!*ebit) {
00817 --_it;
00818 #if DEBUG_CARETMODE > 3
00819 kdDebug(6200) << "--_it" << endl;
00820 #endif
00821 if (_it != ld->preBegin()) {
00822
00823 ebit = EditableInlineBoxIterator(_it, true);
00824 RenderObject *r = (*ebit)->object();
00825 #if DEBUG_CARETMODE > 3
00826 kdDebug(6200) << "b " << *ebit << " isText " << (*ebit)->isInlineTextBox() << endl;
00827 #endif
00828 _node = r->element();
00829 _offset = r->isBR() ? (*ebit)->minOffset() : (*ebit)->maxOffset();
00830 _char = -1;
00831 #if DEBUG_CARETMODE > 0
00832 {InlineBox *box = *ebit; kdDebug(6200) << "echit--(2): box " << box << (box && box->isInlineTextBox() ? QString(" contains \"%1\"").arg(QConstString(static_cast<RenderText *>(box->object())->str->s+box->minOffset(), box->maxOffset() - box->minOffset()).string()) : QString::null) << endl;}
00833 #endif
00834 } else
00835 _node = 0;
00836 return *this;
00837 }
00838 }
00839
00840 bool adjacent = ebit.isAdjacent();
00841
00842 #if DEBUG_CARETMODE > 0
00843 kdDebug(6200) << "adjacent " << adjacent << " _peekNext " << _peekNext << " _peekNext->isInlineTextBox: " << (_peekNext ? _peekNext->isInlineTextBox() : false) << " !((*ebit)->isInlineTextBox): " << (*ebit ? !(*ebit)->isInlineTextBox() : true) << endl;
00844 #endif
00845 if (adjacent && _peekNext && _peekNext->isInlineTextBox()
00846 && !(*ebit)->isInlineTextBox()) {
00847 EditableInlineBoxIterator copy = ebit;
00848 --ebit;
00849 if (!*ebit)
00850 ebit = copy;
00851 }
00852 #if DEBUG_CARETMODE > 0
00853 kdDebug(6200) << "(*ebit)->obj " << (*ebit)->object()->renderName() << "[" << (*ebit)->object() << "]" << " minOffset: " << (*ebit)->minOffset() << " maxOffset: " << (*ebit)->maxOffset() << endl;
00854 #endif
00855 _node = (*ebit)->object()->element();
00856 #if DEBUG_CARETMODE > 3
00857 kdDebug(6200) << "_node " << _node << ":" << _node->nodeName().string() << endl;
00858 #endif
00859 _offset = (*ebit)->maxOffset();
00860 #if DEBUG_CARETMODE > 3
00861 kdDebug(6200) << "_offset " << _offset << endl;
00862 #endif
00863 _peekPrev = 0;
00864 } else {
00865 #if DEBUG_CARETMODE > 0
00866 kdDebug(6200) << "_offset: " << _offset << " _peekNext: " << _peekNext << endl;
00867 #endif
00868
00869 if (_peekNext && _offset >= b->maxOffset() && _peekNext->isInlineTextBox())
00870 _char = static_cast<RenderText *>(_peekNext->object())->text()[_peekNext->minOffset()].unicode();
00871 else if (b && _offset < b->maxOffset() && b->isInlineTextBox())
00872 _char = static_cast<RenderText *>(b->object())->text()[_offset].unicode();
00873 else
00874 _char = -1;
00875 }
00876
00877 #if DEBUG_CARETMODE > 0
00878 if (*ebit) {
00879 InlineBox *box = *ebit;
00880 kdDebug(6200) << "echit--(1): box " << box << (box && box->isInlineTextBox() ? QString(" contains \"%1\"").arg(QConstString(static_cast<RenderText *>(box->object())->str->s+box->minOffset(), box->maxOffset() - box->minOffset()).string()) : QString::null) << endl;
00881 }
00882 #endif
00883 return *this;
00884 }
00885
00886
00887
00888 TableRowIterator::TableRowIterator(RenderTable *table, bool fromEnd,
00889 RenderTableSection::RowStruct *row)
00890 : sec(table, fromEnd)
00891 {
00892
00893 if (*sec) {
00894 if (fromEnd) index = (*sec)->grid.size() - 1;
00895 else index = 0;
00896 }
00897
00898
00899 if (row && *sec) {
00900 while (operator *() != row)
00901 if (fromEnd) operator --(); else operator ++();
00902 }
00903 }
00904
00905 TableRowIterator &TableRowIterator::operator ++()
00906 {
00907 index++;
00908
00909 if (index >= (int)(*sec)->grid.size()) {
00910 ++sec;
00911
00912 if (*sec) index = 0;
00913 }
00914 return *this;
00915 }
00916
00917 TableRowIterator &TableRowIterator::operator --()
00918 {
00919 index--;
00920
00921 if (index < 0) {
00922 --sec;
00923
00924 if (*sec) index = (*sec)->grid.size() - 1;
00925 }
00926 return *this;
00927 }
00928
00929
00930
00931
00932 static RenderTableCell *findNearestTableCellInRow(KHTMLPart *part, int x,
00933 RenderTableSection::RowStruct *row, bool fromEnd);
00934
00948 static inline RenderTableCell *findNearestTableCell(KHTMLPart *part, int x,
00949 TableRowIterator &it, bool fromEnd)
00950 {
00951 RenderTableCell *result = 0;
00952
00953 while (*it) {
00954 result = findNearestTableCellInRow(part, x, *it, fromEnd);
00955 if (result) break;
00956
00957 if (fromEnd) --it; else ++it;
00958 }
00959
00960 return result;
00961 }
00962
00976 static RenderTableCell *findNearestTableCellInRow(KHTMLPart *part, int x,
00977 RenderTableSection::RowStruct *row, bool fromEnd)
00978 {
00979
00980 int n = (int)row->row->size();
00981 int i;
00982 for (i = 0; i < n; i++) {
00983 RenderTableCell *cell = row->row->at(i);
00984 if (!cell || (int)cell == -1) continue;
00985
00986 int absx, absy;
00987 cell->absolutePosition(absx, absy, false);
00988 #if DEBUG_CARETMODE > 1
00989 kdDebug(6201) << "i/n " << i << "/" << n << " absx " << absx << " absy " << absy << endl;
00990 #endif
00991
00992
00993
00994 #if DEBUG_CARETMODE > 1
00995 kdDebug(6201) << "x " << x << " < " << (absx + cell->width()) << "?" << endl;
00996 #endif
00997 if (x < absx + cell->width()) break;
00998 }
00999 if (i >= n) i = n - 1;
01000
01001
01002
01003 for (int cnt = 0; cnt < 2*n; cnt++) {
01004 int index = i - ((cnt >> 1) + 1)*(cnt & 1) + (cnt >> 1)*!(cnt & 1);
01005 if (index < 0 || index >= n) continue;
01006
01007 RenderTableCell *cell = row->row->at(index);
01008 if (!cell || (int)cell == -1) continue;
01009
01010 #if DEBUG_CARETMODE > 1
01011 kdDebug(6201) << "index " << index << " cell " << cell << endl;
01012 #endif
01013 RenderTable *nestedTable;
01014 if (containsEditableElement(part, cell, nestedTable, fromEnd)) {
01015
01016 if (nestedTable) {
01017 TableRowIterator it(nestedTable, fromEnd);
01018 while (*it) {
01019 cell = findNearestTableCell(part, x, it, fromEnd);
01020 if (cell) break;
01021 if (fromEnd) --it; else ++it;
01022 }
01023 }
01024
01025 return cell;
01026 }
01027 }
01028 return 0;
01029 }
01030
01037 static RenderObject *commonAncestorTableSectionOrCell(RenderObject *r1,
01038 RenderObject *r2)
01039 {
01040 if (!r1 || !r2) return 0;
01041 RenderTableSection *sec = 0;
01042 int start_depth=0, end_depth=0;
01043
01044 RenderObject *n = r1;
01045 while (n->parent()) {
01046 n = n->parent();
01047 start_depth++;
01048 }
01049 n = r2;
01050 while( n->parent()) {
01051 n = n->parent();
01052 end_depth++;
01053 }
01054
01055 while (end_depth > start_depth) {
01056 r2 = r2->parent();
01057 end_depth--;
01058 }
01059 while (start_depth > end_depth) {
01060 r1 = r1->parent();
01061
01062 start_depth--;
01063 }
01064
01065 while (r1 != r2){
01066 r1 = r1->parent();
01067 if (r1->isTableSection()) sec = static_cast<RenderTableSection *>(r1);
01068 r2 = r2->parent();
01069 }
01070
01071
01072
01073 while (r1 && !r1->isTableCell() && !r1->isTableSection() && !r1->isTable())
01074 r1 = r1->parent();
01075
01076 return r1 && r1->isTable() ? sec : r1;
01077 }
01078
01086 static int findRowInSection(RenderTableSection *section, RenderTableCell *cell,
01087 RenderTableSection::RowStruct *&row, RenderTableCell *&directCell)
01088 {
01089
01090 RenderObject *r = cell;
01091 while (r != section) {
01092 if (r->isTableCell()) directCell = static_cast<RenderTableCell *>(r);
01093 r = r->parent();
01094 }
01095
01096
01097
01098
01099 int n = section->numRows();
01100 for (int i = 0; i < n; i++) {
01101 row = §ion->grid[i];
01102
01103
01104 int m = row->row->size();
01105 for (int j = 0; j < m; j++) {
01106 RenderTableCell *c = row->row->at(j);
01107 if (c == directCell) return i;
01108 }
01109
01110 }
01111 Q_ASSERT(false);
01112 return -1;
01113 }
01114
01120 static inline RenderTable *findFirstDescendantTable(RenderObject *leaf, RenderFlow *block)
01121 {
01122 RenderTable *result = 0;
01123 while (leaf && leaf != block) {
01124 if (leaf->isTable()) result = static_cast<RenderTable *>(leaf);
01125 leaf = leaf->parent();
01126 }
01127 return result;
01128 }
01129
01133 static inline RenderTableCell *containingTableCell(RenderObject *r)
01134 {
01135 while (r && !r->isTableCell()) r = r->parent();
01136 return static_cast<RenderTableCell *>(r);
01137 }
01138
01139 inline void ErgonomicEditableLineIterator::calcAndStoreNewLine(
01140 RenderFlow *newBlock, bool toBegin)
01141 {
01142
01143
01144 cb = newBlock;
01145 if (toBegin) prevBlock(); else nextBlock();
01146
01147 if (!cb) {
01148 flowBox = 0;
01149 return;
01150 }
01151
01152 if (!isEditable(*this)) {
01153 if (toBegin) EditableLineIterator::operator --();
01154 else EditableLineIterator::operator ++();
01155 }
01156 }
01157
01158 void ErgonomicEditableLineIterator::determineTopologicalElement(
01159 RenderTableCell *oldCell, RenderObject *newObject, bool toBegin)
01160 {
01161
01162
01163
01164
01165
01166 TableRowIterator it;
01167
01168 RenderObject *commonAncestor = commonAncestorTableSectionOrCell(oldCell, newObject);
01169 #if DEBUG_CARETMODE > 1
01170 kdDebug(6201) << " ancestor " << commonAncestor << endl;
01171 #endif
01172
01173
01174 if (!commonAncestor || commonAncestor->isTableCell()) {
01175
01176 RenderTableCell *cell = static_cast<RenderTableCell *>(commonAncestor);
01177 RenderTable *table = findFirstDescendantTable(newObject, cell);
01178
01179 #if DEBUG_CARETMODE > 0
01180 kdDebug(6201) << "table cell: " << cell << endl;
01181 #endif
01182
01183
01184
01185 if (!table) return;
01186
01187 it = TableRowIterator(table, toBegin);
01188
01189 } else if (commonAncestor->isTableSection()) {
01190
01191 RenderTableSection *section = static_cast<RenderTableSection *>(commonAncestor);
01192 RenderTableSection::RowStruct *row;
01193 int idx = findRowInSection(section, oldCell, row, oldCell);
01194 #if DEBUG_CARETMODE > 1
01195 kdDebug(6201) << "table section: row idx " << idx << endl;
01196 #endif
01197
01198 it = TableRowIterator(section, idx);
01199
01200
01201 int rowspan = oldCell->rowSpan();
01202 while (*it && rowspan--) {
01203 if (toBegin) --it; else ++it;
01204 }
01205
01206 } else {
01207 kdError(6201) << "Neither common cell nor section! " << commonAncestor->renderName() << endl;
01208
01209 }
01210
01211 RenderTableCell *cell = findNearestTableCell(lines->m_part, xCoor, it, toBegin);
01212 #if DEBUG_CARETMODE > 1
01213 kdDebug(6201) << "findNearestTableCell result: " << cell << endl;
01214 #endif
01215
01216 RenderFlow *newBlock = cell;
01217 if (!cell) {
01218 Q_ASSERT(commonAncestor->isTableSection());
01219 RenderTableSection *section = static_cast<RenderTableSection *>(commonAncestor);
01220 cell = containingTableCell(section);
01221 #if DEBUG_CARETMODE > 1
01222 kdDebug(6201) << "containing cell: " << cell << endl;
01223 #endif
01224
01225 RenderTable *nestedTable;
01226 bool editableChild = cell && containsEditableChildElement(lines->m_part,
01227 cell, nestedTable, toBegin, section->table());
01228
01229 if (cell && !editableChild) {
01230 #if DEBUG_CARETMODE > 1
01231 kdDebug(6201) << "========= recursive invocation outer =========" << endl;
01232 #endif
01233 determineTopologicalElement(cell, cell->section(), toBegin);
01234 #if DEBUG_CARETMODE > 1
01235 kdDebug(6201) << "========= end recursive invocation outer =========" << endl;
01236 #endif
01237 return;
01238
01239 } else if (cell && nestedTable) {
01240 #if DEBUG_CARETMODE > 1
01241 kdDebug(6201) << "========= recursive invocation inner =========" << endl;
01242 #endif
01243 determineTopologicalElement(cell, nestedTable, toBegin);
01244 #if DEBUG_CARETMODE > 1
01245 kdDebug(6201) << "========= end recursive invocation inner =========" << endl;
01246 #endif
01247 return;
01248
01249 } else {
01250 #if DEBUG_CARETMODE > 1
01251 kdDebug(6201) << "newBlock is table: " << section->table() << endl;
01252 #endif
01253 newBlock = section->table();
01254
01255 }
01256 } else {
01257
01258 RenderObject *r = cell;
01259 if (toBegin) {
01260 while (r->lastChild()) r = r->lastChild();
01261 r = nextSuitableLeafRenderObject(r);
01262 } else
01263 r = prevSuitableLeafRenderObject(r);
01264 newBlock = static_cast<RenderFlow *>(!r || r->isRenderBlock() ? r : r->containingBlock());
01265 }
01266
01267 calcAndStoreNewLine(newBlock, toBegin);
01268 }
01269
01270 ErgonomicEditableLineIterator &ErgonomicEditableLineIterator::operator ++()
01271 {
01272 RenderTableCell *oldCell = containingTableCell(cb);
01273
01274 EditableLineIterator::operator ++();
01275 if (*this == lines->end() || *this == lines->preBegin()) return *this;
01276
01277 RenderTableCell *newCell = containingTableCell(cb);
01278
01279 if (!newCell || newCell == oldCell) return *this;
01280
01281 determineTopologicalElement(oldCell, newCell, false);
01282
01283 return *this;
01284 }
01285
01286 ErgonomicEditableLineIterator &ErgonomicEditableLineIterator::operator --()
01287 {
01288 RenderTableCell *oldCell = containingTableCell(cb);
01289
01290 EditableLineIterator::operator --();
01291 if (*this == lines->end() || *this == lines->preBegin()) return *this;
01292
01293 RenderTableCell *newCell = containingTableCell(cb);
01294
01295 if (!newCell || newCell == oldCell) return *this;
01296
01297 determineTopologicalElement(oldCell, newCell, true);
01298
01299 return *this;
01300 }
01301
01302
01303
01313 static InlineBox *nearestInlineBox(LineIterator &it, CaretViewContext *cv,
01314 int &x, int &absx, int &absy)
01315 {
01316 InlineFlowBox *fbox = *it;
01317
01318
01319 RenderObject *cb = fbox->object();
01320
01321 if (cb) cb->absolutePosition(absx, absy);
01322 else absx = absy = 0;
01323
01324
01325
01326
01327 x = cv->origX - absx;
01328 InlineBox *caretBox = 0;
01329
01330 int xPos;
01331 int oldXPos = -1;
01332 EditableInlineBoxIterator fbit = it;
01333 #if DEBUG_CARETMODE > 0
01334 kdDebug(6200) << "*fbit = " << *fbit << endl;
01335 #endif
01336
01337
01338 for (InlineBox *b; *fbit != 0; ++fbit) {
01339 b = *fbit;
01340
01341
01342 #if DEBUG_CARETMODE > 0
01343 if (b->isInlineFlowBox()) kdDebug(6200) << "b is inline flow box" << endl;
01344
01345 #endif
01346
01347 xPos = b->xPos();
01348
01349
01350 if (x < xPos) {
01351
01352 if (oldXPos < 0 || x - (oldXPos + caretBox->width()) > xPos - x) {
01353 caretBox = b;
01354
01355 }
01356 break;
01357 }
01358
01359 caretBox = b;
01360
01361
01362
01363 if (x >= xPos && x < xPos + caretBox->width())
01364 break;
01365 oldXPos = xPos;
01366
01367
01368
01369
01370 if (b == fbox) break;
01371 }
01372
01373 return caretBox;
01374 }
01375
01381 static void moveItToNextWord(EditableCharacterIterator &it)
01382 {
01383 #if DEBUG_CARETMODE > 0
01384 kdDebug(6200) << "%%%%%%%%%%%%%%%%%%%%% moveItToNextWord" << endl;
01385 #endif
01386 EditableCharacterIterator copy;
01387 while (it.node() && !(*it).isSpace() && !(*it).isPunct()) {
01388 #if DEBUG_CARETMODE > 2
01389 kdDebug(6200) << "reading1 '" << (*it).latin1() << "'" << endl;
01390 #endif
01391 copy = it;
01392 ++it;
01393 }
01394
01395 if (!it.node()) {
01396 it = copy;
01397 return;
01398 }
01399
01400 while (it.node() && ((*it).isSpace() || (*it).isPunct())) {
01401 #if DEBUG_CARETMODE > 2
01402 kdDebug(6200) << "reading2 '" << (*it).latin1() << "'" << endl;
01403 #endif
01404 copy = it;
01405 ++it;
01406 }
01407
01408 if (!it.node()) it = copy;
01409 }
01410
01416 static void moveItToPrevWord(EditableCharacterIterator &it)
01417 {
01418 if (!it.node()) return;
01419
01420 #if DEBUG_CARETMODE > 0
01421 kdDebug(6200) << "%%%%%%%%%%%%%%%%%%%%% moveItToPrevWord" << endl;
01422 #endif
01423 EditableCharacterIterator copy;
01424
01425
01426 do {
01427 copy = it;
01428 --it;
01429 #if DEBUG_CARETMODE > 2
01430 if (it.node()) kdDebug(6200) << "reading1 '" << (*it).latin1() << "'" << endl;
01431 #endif
01432 } while (it.node() && ((*it).isSpace() || (*it).isPunct()));
01433
01434 if (!it.node()) {
01435 it = copy;
01436 return;
01437 }
01438
01439 do {
01440 copy = it;
01441 --it;
01442 #if DEBUG_CARETMODE > 0
01443 if (it.node()) kdDebug(6200) << "reading2 '" << (*it).latin1() << "'" << endl;
01444 #endif
01445 } while (it.node() && !(*it).isSpace() && !(*it).isPunct());
01446
01447 it = copy;
01448 }
01449
01450
01458 static void moveIteratorByPage(LinearDocument &ld,
01459 ErgonomicEditableLineIterator &it, int mindist, bool next)
01460 {
01461 if (it == ld.end() || it == ld.preBegin()) return;
01462
01463 ErgonomicEditableLineIterator copy = it;
01464 #if DEBUG_CARETMODE > 0
01465 kdDebug(6200) << " mindist: " << mindist << endl;
01466 #endif
01467
01468 InlineFlowBox *flowBox = *copy;
01469 int absx = 0, absy = 0;
01470
01471 RenderFlow *lastcb = static_cast<RenderFlow *>(flowBox->object());
01472 Q_ASSERT(lastcb->isRenderBlock());
01473 lastcb->absolutePosition(absx, absy, false);
01474
01475
01476
01477 int lastfby = flowBox->firstChild()->yPos();
01478 int lastheight = 0;
01479 do {
01480 if (next) ++copy; else --copy;
01481 if (copy == ld.end() || copy == ld.preBegin()) break;
01482
01483
01484 flowBox = static_cast<InlineFlowBox *>(*copy);
01485 Q_ASSERT(flowBox->isInlineFlowBox());
01486
01487 RenderFlow *cb = static_cast<RenderFlow *>(flowBox->object());
01488 Q_ASSERT(cb->isRenderBlock());
01489
01490 int diff = 0;
01491
01492
01493 int fby = flowBox->firstChild()->yPos();
01494 if (cb != lastcb) {
01495 if (next) {
01496 diff = absy + lastfby + lastheight;
01497 cb->absolutePosition(absx, absy, false);
01498 diff = absy - diff + fby;
01499 lastfby = 0;
01500 } else {
01501 diff = absy;
01502 cb->absolutePosition(absx, absy, false);
01503 diff -= absy + fby + lastheight;
01504 lastfby = fby - lastheight;
01505 }
01506 #if DEBUG_CARETMODE > 2
01507 kdDebug(6200) << "absdiff " << diff << endl;
01508 #endif
01509 } else {
01510 diff = QABS(fby - lastfby);
01511 }
01512 #if DEBUG_CARETMODE > 2
01513 kdDebug(6200) << "flowBox->firstChild->yPos: " << fby << " diff " << diff << endl;
01514 #endif
01515
01516 mindist -= diff;
01517
01518 lastheight = QABS(fby - lastfby);
01519 lastfby = fby;
01520 lastcb = cb;
01521 it = copy;
01522 #if DEBUG_CARETMODE > 0
01523 kdDebug(6200) << " mindist: " << mindist << endl;
01524 #endif
01525
01526
01527
01528
01529 } while (mindist - lastheight > 0);
01530 }
01531
01532
01533 }
01534