1 /**
2 * This file is part of the HTML widget for KDE.
3 *
4 * Copyright (C) 1999-2003 Lars Knoll (knoll@kde.org)
5 * (C) 2003 Apple Computer, Inc.
6 * (C) 2005 Allan Sandfeld Jensen (kde@carewolf.com)
7 * (C) 2007-2009 Germain Garand (germain@ebooksfrance.org)
8 *
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Library General Public
11 * License as published by the Free Software Foundation; either
12 * version 2 of the License, or (at your option) any later version.
13 *
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Library General Public License for more details.
18 *
19 * You should have received a copy of the GNU Library General Public License
20 * along with this library; see the file COPYING.LIB. If not, write to
21 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
22 * Boston, MA 02110-1301, USA.
23 */
24
25 #include "rendering/render_canvas.h"
26 #include "rendering/render_layer.h"
27 #include "rendering/render_replaced.h"
28 #include "xml/dom_docimpl.h"
29
30 #include "khtmlview.h"
31 #include "khtml_part.h"
32 #include "khtml_debug.h"
33 #include <QScrollBar>
34
35 using namespace khtml;
36
37 //#define BOX_DEBUG
38 //#define SPEED_DEBUG
39 #ifdef SPEED_DEBUG
40 #include <QTime>
41 #endif
42
RenderCanvas(DOM::NodeImpl * node,KHTMLView * view)43 RenderCanvas::RenderCanvas(DOM::NodeImpl *node, KHTMLView *view)
44 : RenderBlock(node)
45 {
46 // init RenderObject attributes
47 setInline(false);
48 setIsAnonymous(false);
49
50 m_view = view;
51 // try to contrain the width to the views width
52
53 m_minWidth = 0;
54 m_height = 0;
55
56 m_width = m_minWidth;
57 m_maxWidth = m_minWidth;
58
59 m_rootWidth = m_rootHeight = 0;
60 m_viewportWidth = m_viewportHeight = 0;
61 m_cachedDocWidth = m_cachedDocHeight = -1;
62
63 setPositioned(true); // to 0,0 :)
64
65 m_staticMode = false;
66 m_pagedMode = false;
67 m_printImages = true;
68
69 m_pageTop = 0;
70 m_pageBottom = 0;
71
72 m_page = nullptr;
73
74 m_maximalOutlineSize = 0;
75
76 m_selectionStart = nullptr;
77 m_selectionEnd = nullptr;
78 m_selectionStartPos = -1;
79 m_selectionEndPos = -1;
80
81 m_needsWidgetMasks = false;
82
83 m_isPerformingLayout = false;
84
85 // Create a new root layer for our layer hierarchy.
86 m_layer = new(node->document()->renderArena()) RenderLayer(this);
87 }
88
~RenderCanvas()89 RenderCanvas::~RenderCanvas()
90 {
91 delete m_page;
92 }
93
setStyle(RenderStyle * style)94 void RenderCanvas::setStyle(RenderStyle *style)
95 {
96 /*
97 if (m_pagedMode)
98 style->setOverflow(OHIDDEN); */
99 RenderBlock::setStyle(style);
100 }
101
calcHeight()102 void RenderCanvas::calcHeight()
103 {
104 if (m_pagedMode || !m_view) {
105 m_height = m_rootHeight;
106 } else {
107 m_height = m_view->visibleHeight();
108 }
109 }
110
calcWidth()111 void RenderCanvas::calcWidth()
112 {
113 // the width gets set by KHTMLView::print when printing to a printer.
114 if (m_pagedMode || !m_view) {
115 m_width = m_rootWidth;
116 return;
117 }
118
119 m_width = m_view ? m_view->frameWidth() : m_minWidth;
120
121 if (style()->marginLeft().isFixed()) {
122 m_marginLeft = style()->marginLeft().value();
123 } else {
124 m_marginLeft = 0;
125 }
126
127 if (style()->marginRight().isFixed()) {
128 m_marginRight = style()->marginRight().value();
129 } else {
130 m_marginRight = 0;
131 }
132 }
133
calcMinMaxWidth()134 void RenderCanvas::calcMinMaxWidth()
135 {
136 KHTMLAssert(!minMaxKnown());
137
138 RenderBlock::calcMinMaxWidth();
139
140 m_maxWidth = m_minWidth;
141
142 setMinMaxKnown();
143 }
144
layout()145 void RenderCanvas::layout()
146 {
147 m_isPerformingLayout = true;
148
149 if (m_pagedMode) {
150 m_minWidth = m_width;
151 // m_maxWidth = m_width;
152 }
153
154 m_needsFullRepaint = markedForRepaint() || !view() || view()->needsFullRepaint() || m_pagedMode;
155
156 setChildNeedsLayout(true);
157 setMinMaxKnown(false);
158 for (RenderObject *c = firstChild(); c; c = c->nextSibling()) {
159 c->setChildNeedsLayout(true);
160 }
161
162 int oldWidth = m_width;
163 int oldHeight = m_height;
164
165 m_cachedDocWidth = m_cachedDocHeight = -1;
166
167 if (m_pagedMode || !m_view) {
168 m_width = m_rootWidth;
169 m_height = m_rootHeight;
170 } else {
171 m_viewportWidth = m_width = m_view->visibleWidth();
172 m_viewportHeight = m_height = m_view->visibleHeight();
173 }
174
175 #ifdef SPEED_DEBUG
176 QTime qt;
177 qt.start();
178 #endif
179
180 if (recalcMinMax()) {
181 recalcMinMaxWidths();
182 }
183
184 #ifdef SPEED_DEBUG
185 qCDebug(KHTML_LOG) << "RenderCanvas::calcMinMax time used=" << qt.elapsed();
186 qt.start();
187 #endif
188
189 bool relayoutChildren = (oldWidth != m_width) || (oldHeight != m_height);
190
191 RenderBlock::layoutBlock(relayoutChildren);
192
193 #ifdef SPEED_DEBUG
194 qCDebug(KHTML_LOG) << "RenderCanvas::layout time used=" << qt.elapsed();
195 qt.start();
196 #endif
197
198 updateDocumentSize();
199
200 layer()->updateLayerPositions(layer(), needsFullRepaint(), true);
201
202 if (!m_pagedMode && m_needsWidgetMasks) {
203 layer()->updateWidgetMasks(layer());
204 }
205
206 scheduleDeferredRepaints();
207 setNeedsLayout(false);
208
209 m_isPerformingLayout = false;
210 #ifdef SPEED_DEBUG
211 qCDebug(KHTML_LOG) << "RenderCanvas::end time used=" << qt.elapsed();
212 #endif
213 }
214
setNeedsWidgetMasks(bool b)215 void RenderCanvas::setNeedsWidgetMasks(bool b)
216 {
217 if (b == m_needsWidgetMasks) {
218 return;
219 }
220 m_needsWidgetMasks = b;
221 KHTMLWidget *k = dynamic_cast<KHTMLWidget *>(m_view);
222 // ### should be reversible
223 if (k && b && k->m_kwp->isRedirected()) {
224 k->m_kwp->setIsRedirected(!b);
225 if (k->m_kwp->renderWidget()) {
226 k->m_kwp->renderWidget()->setNeedsLayout(true);
227 }
228 }
229 }
230
updateDocumentSize()231 void RenderCanvas::updateDocumentSize()
232 {
233 // update our cached document size
234 int hDocH = m_cachedDocHeight = docHeight();
235 int hDocW = m_cachedDocWidth = docWidth();
236
237 int zLevel = m_view ? m_view->zoomLevel() : 100;
238 hDocW = hDocW * zLevel / 100;
239 hDocH = hDocH * zLevel / 100;
240
241 if (!m_pagedMode && m_view) {
242
243 // we need to adjust the document's size to make sure we don't enter
244 // an endless cycle of scrollbars being added, then removed at the next layout.
245
246 bool vss = m_view->verticalScrollBar()->isVisible();
247 bool hss = m_view->horizontalScrollBar()->isVisible();
248
249 // calculate the extent of scrollbars
250 int vsPixSize = m_view->verticalScrollBar()->sizeHint().width();
251 int hsPixSize = m_view->horizontalScrollBar()->sizeHint().height();
252
253 // this variable holds the size the viewport will have after the inner content is resized to
254 // the new document dimensions
255 QSize viewport = m_view->maximumViewportSize();
256
257 // of course, if the scrollbar policy isn't auto, there's no point adjusting any value..
258 int overrideH = m_view->verticalScrollBarPolicy() == Qt::ScrollBarAsNeeded ? 0 : hDocH;
259 int overrideW = m_view->verticalScrollBarPolicy() == Qt::ScrollBarAsNeeded ? 0 : hDocW;
260
261 if (!overrideW && hDocW > viewport.width()) {
262 viewport.setHeight(viewport.height() - hsPixSize);
263 }
264 if (!overrideH && hDocH > viewport.height()) {
265 viewport.setWidth(viewport.width() - vsPixSize);
266 }
267
268 // if we are about to show a scrollbar, and the document is sized to the viewport w or h,
269 // then reserve the scrollbar space so that it doesn't trigger the _other_ scrollbar
270
271 if (!vss && m_width - vsPixSize == viewport.width() &&
272 hDocW <= m_width) {
273 hDocW = qMin(hDocW, viewport.width());
274 }
275
276 if (!hss && m_height - hsPixSize == viewport.height() &&
277 hDocH <= m_height) {
278 hDocH = qMin(hDocH, viewport.height());
279 }
280
281 // likewise, if a scrollbar is shown, and we have a cunning plan to turn it off,
282 // think again if we are falling downright in the hysteresis zone
283
284 if (vss && viewport.width() > hDocW && hDocW > m_view->visibleWidth()) {
285 hDocW = viewport.width() + 1;
286 }
287
288 if (hss && viewport.height() > hDocH && hDocH > m_view->visibleHeight()) {
289 hDocH = viewport.height() + 1;
290 }
291
292 m_view->resizeContents((overrideW ? overrideW : hDocW), (overrideH ? overrideH : hDocH));
293
294 }
295 layer()->resize(qMax(m_cachedDocWidth, int(m_width)), qMax(m_cachedDocHeight, m_height));
296 }
297
updateDocSizeAfterLayerTranslation(RenderObject * o,bool posXOffset,bool posYOffset)298 void RenderCanvas::updateDocSizeAfterLayerTranslation(RenderObject *o, bool posXOffset, bool posYOffset)
299 {
300 if (needsLayout()) {
301 return;
302 }
303 int rightmost, lowest;
304 o->absolutePosition(rightmost, lowest);
305 if (posXOffset) {
306 rightmost += o->rightmostPosition(false, true);
307 setCachedDocWidth(qMax(docWidth(), rightmost));
308 } else {
309 setCachedDocWidth(-1);
310 }
311 if (posYOffset) {
312 lowest += o->lowestPosition(false, true);
313 setCachedDocHeight(qMax(docHeight(), lowest));
314 } else {
315 setCachedDocHeight(-1);
316 }
317 // qCDebug(KHTML_LOG) << " posXOffset: " << posXOffset << " posYOffset " << posYOffset << " m_cachedDocWidth " << m_cachedDocWidth << " m_cachedDocHeight " << m_cachedDocHeight;
318 updateDocumentSize();
319 }
320
staticRegion() const321 QRegion RenderCanvas::staticRegion() const
322 {
323 QRegion ret = QRegion();
324
325 // position:fixed objects
326 if (m_positionedObjects) {
327 RenderObject *obj;
328 QListIterator<RenderObject *> it(*m_positionedObjects);
329 while (it.hasNext()) {
330 obj = it.next();
331 if (obj->style()->position() == PFIXED && obj->layer()) {
332 ret += obj->layer()->paintedRegion(layer());
333 assert(m_fixedPosition.contains(obj));
334 }
335 }
336 }
337
338 // background-attachment:fixed images
339 QSetIterator<RenderObject *> i(m_fixedBackground);
340 while (i.hasNext()) {
341 RenderObject *ro = i.next();
342 if (ro && ro->isBox()) {
343 int d1, d2, d3, d4;
344 const BackgroundLayer *bgLayer = ro->style()->backgroundLayers();
345 while (bgLayer) {
346 CachedImage *bg = bgLayer->backgroundAttachment() == BGAFIXED ? bgLayer->backgroundImage() : nullptr;
347 if (bg && bg->isComplete() && !bg->isTransparent() && !bg->isErrorImage()) {
348 int xpos, ypos;
349 absolutePosition(xpos, ypos);
350 ret += static_cast<RenderBox *>(ro)->getFixedBackgroundImageRect(bgLayer, d1, d2, d3, d4)
351 .intersected(QRect(xpos, ypos, ro->width(), ro->height()));
352 }
353 bgLayer = bgLayer->next();
354 }
355 }
356 }
357 return ret;
358 }
359
needsFullRepaint() const360 bool RenderCanvas::needsFullRepaint() const
361 {
362 return m_needsFullRepaint || m_pagedMode;
363 }
364
repaintViewRectangle(int x,int y,int w,int h,bool asap)365 void RenderCanvas::repaintViewRectangle(int x, int y, int w, int h, bool asap)
366 {
367 KHTMLAssert(view());
368 view()->scheduleRepaint(x, y, w, h, asap);
369 }
370
absolutePosition(int & xPos,int & yPos,bool f) const371 bool RenderCanvas::absolutePosition(int &xPos, int &yPos, bool f) const
372 {
373 if (f && m_pagedMode) {
374 xPos = 0;
375 yPos = m_pageTop;
376 } else if (f && m_view) {
377 xPos = m_view->contentsX();
378 yPos = m_view->contentsY();
379 } else {
380 xPos = yPos = 0;
381 }
382 return true;
383 }
384
paint(PaintInfo & paintInfo,int _tx,int _ty)385 void RenderCanvas::paint(PaintInfo &paintInfo, int _tx, int _ty)
386 {
387 #ifdef DEBUG_LAYOUT
388 qCDebug(KHTML_LOG) << renderName() << this << " ::paintObject() w/h = (" << width() << "/" << height() << ")";
389 #endif
390
391 // 1. paint background, borders etc
392 if (paintInfo.phase == PaintActionElementBackground) {
393 paintBoxDecorations(paintInfo, _tx, _ty);
394 return;
395 }
396
397 // 2. paint contents
398 for (RenderObject *child = firstChild(); child; child = child->nextSibling())
399 if (!child->layer() && !child->isFloating()) {
400 child->paint(paintInfo, _tx, _ty);
401 }
402
403 // 3. paint floats.
404 if (paintInfo.phase == PaintActionFloat) {
405 paintFloats(paintInfo, _tx, _ty);
406 }
407
408 #ifdef BOX_DEBUG
409 if (m_view) {
410 _tx += m_view->contentsX();
411 _ty += m_view->contentsY();
412 }
413
414 outlineBox(paintInfo.p, _tx, _ty);
415 #endif
416
417 }
418
paintBoxDecorations(PaintInfo & paintInfo,int,int)419 void RenderCanvas::paintBoxDecorations(PaintInfo &paintInfo, int /*_tx*/, int /*_ty*/)
420 {
421 if ((firstChild() && firstChild()->style()->visibility() == VISIBLE) || !view()) {
422 return;
423 }
424
425 paintInfo.p->fillRect(paintInfo.r, view()->palette().color(QPalette::Active, QPalette::Base));
426 }
427
repaintRectangle(int x,int y,int w,int h,Priority p,bool f)428 void RenderCanvas::repaintRectangle(int x, int y, int w, int h, Priority p, bool f)
429 {
430 if (m_staticMode) {
431 return;
432 }
433 // qCDebug(KHTML_LOG) << "updating views contents (" << x << "/" << y << ") (" << w << "/" << h << ")";
434
435 if (f && m_pagedMode) {
436 y += m_pageTop;
437 } else if (f && m_view) {
438 x += m_view->contentsX();
439 y += m_view->contentsY();
440 }
441
442 QRect vr = viewRect();
443 QRect ur(x, y, w, h);
444
445 if (m_view && ur.intersects(vr)) {
446
447 if (p == RealtimePriority) {
448 m_view->updateContents(ur);
449 } else if (p == HighPriority) {
450 m_view->scheduleRepaint(x, y, w, h, true /*asap*/);
451 } else {
452 m_view->scheduleRepaint(x, y, w, h);
453 }
454 }
455 }
456
deferredRepaint(RenderObject * o)457 void RenderCanvas::deferredRepaint(RenderObject *o)
458 {
459 m_dirtyChildren.append(o);
460 }
461
scheduleDeferredRepaints()462 void RenderCanvas::scheduleDeferredRepaints()
463 {
464 if (!needsFullRepaint()) {
465 QList<RenderObject *>::const_iterator it;
466 for (it = m_dirtyChildren.constBegin(); it != m_dirtyChildren.constEnd(); ++it) {
467 (*it)->repaint();
468 }
469 }
470 //qCDebug(KHTML_LOG) << "scheduled deferred repaints: " << m_dirtyChildren.count() << " needed full repaint: " << needsFullRepaint();
471 m_dirtyChildren.clear();
472 }
473
repaint(Priority p)474 void RenderCanvas::repaint(Priority p)
475 {
476 if (m_view && !m_staticMode) {
477 if (p == RealtimePriority) {
478 //m_view->resizeContents(docWidth(), docHeight());
479 m_view->unscheduleRepaint();
480 if (needsLayout()) {
481 m_view->scheduleRelayout();
482 return;
483 }
484 // ### same as in repaintRectangle
485 m_view->updateContents(m_view->contentsX(), m_view->contentsY(),
486 m_view->visibleWidth(), m_view->visibleHeight());
487 } else if (p == HighPriority)
488 m_view->scheduleRepaint(m_view->contentsX(), m_view->contentsY(),
489 m_view->visibleWidth(), m_view->visibleHeight(), true /*asap*/);
490 else
491 m_view->scheduleRepaint(m_view->contentsX(), m_view->contentsY(),
492 m_view->visibleWidth(), m_view->visibleHeight());
493 }
494 }
495
enclosingPositionedRect(RenderObject * n)496 static QRect enclosingPositionedRect(RenderObject *n)
497 {
498 RenderObject *enclosingParent = n->containingBlock();
499 QRect rect(0, 0, 0, 0);
500 if (enclosingParent) {
501 int ox, oy;
502 enclosingParent->absolutePosition(ox, oy);
503 if (!enclosingParent->hasOverflowClip()) {
504 ox += enclosingParent->overflowLeft();
505 oy += enclosingParent->overflowTop();
506 }
507 rect.setX(ox);
508 rect.setY(oy);
509 rect.setWidth(enclosingParent->effectiveWidth());
510 rect.setHeight(enclosingParent->effectiveHeight());
511 }
512 return rect;
513 }
514
addStaticObject(RenderObject * o,bool pos)515 void RenderCanvas::addStaticObject(RenderObject *o, bool pos)
516 {
517 QSet<RenderObject *> &set = pos ? m_fixedPosition : m_fixedBackground;
518 if (!o || !o->isBox() || set.contains(o)) {
519 return;
520 }
521 set.insert(o);
522 if (view()) {
523 view()->addStaticObject(pos);
524 }
525 }
526
removeStaticObject(RenderObject * o,bool pos)527 void RenderCanvas::removeStaticObject(RenderObject *o, bool pos)
528 {
529 QSet<RenderObject *> &set = pos ? m_fixedPosition : m_fixedBackground;
530 if (!o || !o->isBox() || !set.contains(o)) {
531 return;
532 }
533 set.remove(o);
534 if (view()) {
535 view()->removeStaticObject(pos);
536 }
537 }
538
selectionRect() const539 QRect RenderCanvas::selectionRect() const
540 {
541 RenderObject *r = m_selectionStart;
542 if (!r) {
543 return QRect();
544 }
545
546 QRect selectionRect = enclosingPositionedRect(r);
547
548 while (r && r != m_selectionEnd) {
549 RenderObject *n;
550 if (!(n = r->firstChild())) {
551 if (!(n = r->nextSibling())) {
552 n = r->parent();
553 while (n && !n->nextSibling()) {
554 n = n->parent();
555 }
556 if (n) {
557 n = n->nextSibling();
558 }
559 }
560 }
561 r = n;
562 if (r) {
563 selectionRect = selectionRect.united(enclosingPositionedRect(r));
564 }
565 }
566
567 return selectionRect;
568 }
569
setSelection(RenderObject * s,int sp,RenderObject * e,int ep)570 void RenderCanvas::setSelection(RenderObject *s, int sp, RenderObject *e, int ep)
571 {
572 // Check we got valid renderobjects. www.msnbc.com and clicking
573 // around, to find the case where this happened.
574 if (!s || !e) {
575 qCWarning(KHTML_LOG) << "RenderCanvas::setSelection() called with start=" << s << " end=" << e;
576 return;
577 }
578 // qCDebug(KHTML_LOG) << "RenderCanvas::setSelection(" << s << "," << sp << "," << e << "," << ep << ")";
579
580 bool changedSelectionBorder = (s != m_selectionStart || e != m_selectionEnd);
581
582 // Cut out early if the selection hasn't changed.
583 if (!changedSelectionBorder && m_selectionStartPos == sp && m_selectionEndPos == ep) {
584 return;
585 }
586
587 // Record the old selected objects. Will be used later
588 // to delta against the selected objects.
589
590 RenderObject *oldStart = m_selectionStart;
591 int oldStartPos = m_selectionStartPos;
592 RenderObject *oldEnd = m_selectionEnd;
593 int oldEndPos = m_selectionEndPos;
594 QList<RenderObject *> oldSelectedInside;
595 QList<RenderObject *> newSelectedInside;
596 RenderObject *os = oldStart;
597
598 while (os && os != oldEnd) {
599 RenderObject *no;
600 if (!(no = os->firstChild())) {
601 if (!(no = os->nextSibling())) {
602 no = os->parent();
603 while (no && !no->nextSibling()) {
604 no = no->parent();
605 }
606 if (no) {
607 no = no->nextSibling();
608 }
609 }
610 }
611 if (os->selectionState() == SelectionInside && !oldSelectedInside.contains(os)) {
612 oldSelectedInside.append(os);
613 }
614
615 os = no;
616 }
617 if (changedSelectionBorder) {
618 clearSelection(false);
619 }
620
621 while (s->firstChild()) {
622 s = s->firstChild();
623 }
624 while (e->lastChild()) {
625 e = e->lastChild();
626 }
627
628 #if 0
629 bool changedSelectionBorder = (s != m_selectionStart || e != m_selectionEnd);
630
631 if (!changedSelectionBorder && m_selectionStartPos == sp && m_selectionEndPos = ep) {
632 return;
633 }
634 #endif
635
636 // set selection start
637 if (m_selectionStart) {
638 m_selectionStart->setIsSelectionBorder(false);
639 }
640 m_selectionStart = s;
641 if (m_selectionStart) {
642 m_selectionStart->setIsSelectionBorder(true);
643 }
644 m_selectionStartPos = sp;
645
646 // set selection end
647 if (m_selectionEnd) {
648 m_selectionEnd->setIsSelectionBorder(false);
649 }
650 m_selectionEnd = e;
651 if (m_selectionEnd) {
652 m_selectionEnd->setIsSelectionBorder(true);
653 }
654 m_selectionEndPos = ep;
655
656 #if 0
657 // qCDebug(KHTML_LOG) << "old selection (" << oldStart << "," << oldStartPos << "," << oldEnd << "," << oldEndPos << ")";
658 // qCDebug(KHTML_LOG) << "new selection (" << s << "," << sp << "," << e << "," << ep << ")";
659 #endif
660
661 // update selection status of all objects between m_selectionStart and m_selectionEnd
662 RenderObject *o = s;
663
664 while (o && o != e) {
665 o->setSelectionState(SelectionInside);
666 // qCDebug(KHTML_LOG) << "setting selected " << o << ", " << o->isText();
667 RenderObject *no;
668 if (!(no = o->firstChild()))
669 if (!(no = o->nextSibling())) {
670 no = o->parent();
671 while (no && !no->nextSibling()) {
672 no = no->parent();
673 }
674 if (no) {
675 no = no->nextSibling();
676 }
677 }
678 if (o->selectionState() == SelectionInside && !newSelectedInside.contains(o)) {
679 newSelectedInside.append(o);
680 }
681
682 o = no;
683 }
684 s->setSelectionState(SelectionStart);
685 e->setSelectionState(SelectionEnd);
686 if (s == e) {
687 s->setSelectionState(SelectionBoth);
688 }
689
690 if (!m_view) {
691 return;
692 }
693
694 int i;
695 i = newSelectedInside.indexOf(s);
696 if (i != -1) {
697 newSelectedInside.removeAt(i);
698 }
699 i = newSelectedInside.indexOf(e);
700 if (i != -1) {
701 newSelectedInside.removeAt(i);
702 }
703
704 QRect updateRect;
705
706 // Don't use repaint() because it will cause all rects to
707 // be united (see khtmlview::scheduleRepaint()). Instead
708 // just draw damage rects for objects that have a change
709 // in selection state.
710 // ### for Qt, updateContents will unite them, too. This has to be
711 // circumvented somehow (LS)
712
713 // Are any of the old fully selected objects not in the new selection?
714 // If so we have to draw them.
715 // Could be faster by building list of non-intersecting rectangles rather
716 // than unioning rectangles.
717 QListIterator<RenderObject *> oldIterator(oldSelectedInside);
718 bool firstRect = true;
719 while (oldIterator.hasNext()) {
720 o = oldIterator.next();
721 if (!newSelectedInside.contains(o)) {
722 if (firstRect) {
723 updateRect = enclosingPositionedRect(o);
724 firstRect = false;
725 } else {
726 updateRect = updateRect.united(enclosingPositionedRect(o));
727 }
728 }
729 }
730 if (!firstRect) {
731 m_view->updateContents(updateRect);
732 }
733
734 // Are any of the new fully selected objects not in the previous selection?
735 // If so we have to draw them.
736 // Could be faster by building list of non-intersecting rectangles rather
737 // than unioning rectangles.
738 QListIterator<RenderObject *> newIterator(newSelectedInside);
739 firstRect = true;
740 while (newIterator.hasNext()) {
741 o = newIterator.next();
742 if (!oldSelectedInside.contains(o)) {
743 if (firstRect) {
744 updateRect = enclosingPositionedRect(o);
745 firstRect = false;
746 } else {
747 updateRect = updateRect.united(enclosingPositionedRect(o));
748 }
749 }
750 }
751 if (!firstRect) {
752 m_view->updateContents(updateRect);
753 }
754
755 // Is the new starting object different, or did the position in the starting
756 // element change? If so we have to draw it.
757 if (oldStart != m_selectionStart ||
758 (oldStart == oldEnd && (oldStartPos != m_selectionStartPos || oldEndPos != m_selectionEndPos)) ||
759 (oldStart == m_selectionStart && oldStartPos != m_selectionStartPos)) {
760 m_view->updateContents(enclosingPositionedRect(m_selectionStart));
761 }
762
763 // Draw the old selection start object if it's different than the new selection
764 // start object.
765 if (oldStart && oldStart != m_selectionStart) {
766 m_view->updateContents(enclosingPositionedRect(oldStart));
767 }
768
769 // Does the selection span objects and is the new end object different, or did the position
770 // in the end element change? If so we have to draw it.
771 if (/*(oldStart != oldEnd || !oldEnd) &&*/
772 (oldEnd != m_selectionEnd ||
773 (oldEnd == m_selectionEnd && oldEndPos != m_selectionEndPos))) {
774 m_view->updateContents(enclosingPositionedRect(m_selectionEnd));
775 }
776
777 // Draw the old selection end object if it's different than the new selection
778 // end object.
779 if (oldEnd && oldEnd != m_selectionEnd) {
780 m_view->updateContents(enclosingPositionedRect(oldEnd));
781 }
782 }
783
clearSelection(bool doRepaint)784 void RenderCanvas::clearSelection(bool doRepaint)
785 {
786 // update selection status of all objects between m_selectionStart and m_selectionEnd
787 RenderObject *o = m_selectionStart;
788 while (o && o != m_selectionEnd) {
789 if (o->selectionState() != SelectionNone)
790 if (doRepaint) {
791 o->repaint();
792 }
793 o->setSelectionState(SelectionNone);
794 o->repaint();
795 RenderObject *no;
796 if (!(no = o->firstChild()))
797 if (!(no = o->nextSibling())) {
798 no = o->parent();
799 while (no && !no->nextSibling()) {
800 no = no->parent();
801 }
802 if (no) {
803 no = no->nextSibling();
804 }
805 }
806 o = no;
807 }
808 if (m_selectionEnd) {
809 m_selectionEnd->setSelectionState(SelectionNone);
810 if (doRepaint) {
811 m_selectionEnd->repaint();
812 }
813 }
814
815 // set selection start & end to 0
816 if (m_selectionStart) {
817 m_selectionStart->setIsSelectionBorder(false);
818 }
819 m_selectionStart = nullptr;
820 m_selectionStartPos = -1;
821
822 if (m_selectionEnd) {
823 m_selectionEnd->setIsSelectionBorder(false);
824 }
825 m_selectionEnd = nullptr;
826 m_selectionEndPos = -1;
827 }
828
selectionStartEnd(int & spos,int & epos)829 void RenderCanvas::selectionStartEnd(int &spos, int &epos)
830 {
831 spos = m_selectionStartPos;
832 epos = m_selectionEndPos;
833 }
834
viewRect() const835 QRect RenderCanvas::viewRect() const
836 {
837 if (m_pagedMode)
838 if (m_pageTop == m_pageBottom) {
839 // qCDebug(KHTML_LOG) << "viewRect: " << QRect(0, m_pageTop, m_width, m_height);
840 return QRect(0, m_pageTop, m_width, m_height);
841 } else {
842 // qCDebug(KHTML_LOG) << "viewRect: " << QRect(0, m_pageTop, m_width, m_pageBottom - m_pageTop);
843 return QRect(0, m_pageTop, m_width, m_pageBottom - m_pageTop);
844 }
845 else if (m_view) {
846 const int z = m_view->zoomLevel() ? m_view->zoomLevel() : 100;
847 return QRect(m_view->contentsX() * 100 / z, m_view->contentsY() * 100 / z,
848 m_view->visibleWidth(), m_view->visibleHeight());
849 } else {
850 return QRect(0, 0, m_rootWidth, m_rootHeight);
851 }
852 }
853
docHeight() const854 int RenderCanvas::docHeight() const
855 {
856 if (m_cachedDocHeight != -1) {
857 return m_cachedDocHeight;
858 }
859
860 int h;
861 if (m_pagedMode || !m_view) {
862 h = m_height;
863 } else {
864 h = 0;
865 }
866
867 RenderObject *fc = firstChild();
868 if (fc) {
869 int dh = fc->overflowHeight() + fc->marginTop() + fc->marginBottom();
870 int lowestPos = fc->lowestPosition(false);
871 // qCDebug(KHTML_LOG) << "h " << h << " lowestPos " << lowestPos << " dh " << dh << " fc->rh " << fc->effectiveHeight() << " fc->height() " << fc->height();
872 if (lowestPos > dh) {
873 dh = lowestPos;
874 }
875 lowestPos = lowestAbsolutePosition();
876 if (lowestPos > dh) {
877 dh = lowestPos;
878 }
879 if (dh > h) {
880 h = dh;
881 }
882 }
883
884 RenderLayer *layer = m_layer;
885 h = qMax(h, layer->yPos() + layer->height());
886 // qCDebug(KHTML_LOG) << "h " << h << " layer(" << layer->renderer()->renderName() << "@" << layer->renderer() << ")->height " << layer->height() << " lp " << (layer->yPos() + layer->height()) << " height() " << layer->renderer()->height() << " rh " << layer->renderer()->effectiveHeight();
887 return h;
888 }
889
docWidth() const890 int RenderCanvas::docWidth() const
891 {
892 if (m_cachedDocWidth != -1) {
893 return m_cachedDocWidth;
894 }
895
896 int w;
897 if (m_pagedMode || !m_view) {
898 w = m_width;
899 } else {
900 w = 0;
901 }
902
903 RenderObject *fc = firstChild();
904 if (fc) {
905 // ow: like effectiveWidth() but without the negative
906 const int ow = fc->hasOverflowClip() ? fc->width() : fc->overflowWidth();
907 int dw = ow + fc->marginLeft() + fc->marginRight();
908 int rightmostPos = fc->rightmostPosition(false);
909 // qCDebug(KHTML_LOG) << "w " << w << " rightmostPos " << rightmostPos << " dw " << dw << " fc->rw " << fc->effectiveWidth() << " fc->width() " << fc->width();
910 if (rightmostPos > dw) {
911 dw = rightmostPos;
912 }
913 rightmostPos = rightmostAbsolutePosition();
914 if (rightmostPos > dw) {
915 dw = rightmostPos;
916 }
917 if (dw > w) {
918 w = dw;
919 }
920 }
921
922 RenderLayer *layer = m_layer;
923 w = qMax(w, layer->xPos() + layer->width());
924 // qCDebug(KHTML_LOG) << "w " << w << " layer(" << layer->renderer()->renderName() << ")->width " << layer->width() << " rm " << (layer->xPos() + layer->width()) << " width() " << layer->renderer()->width() << " rw " << layer->renderer()->effectiveWidth();
925 return w;
926 }
927
page()928 RenderPage *RenderCanvas::page()
929 {
930 if (!m_page) {
931 m_page = new RenderPage(this);
932 }
933 return m_page;
934 }
935
updateInvalidatedFonts()936 void RenderCanvas::updateInvalidatedFonts()
937 {
938 for (RenderObject *o = firstChild(); o; o = o->nextRenderer()) {
939 if (o->style()->htmlFont().isInvalidated()) {
940 o->setNeedsLayoutAndMinMaxRecalc();
941 // qCDebug(KHTML_LOG) << "updating object using invalid font" << o->style()->htmlFont().getFontDef().family << o->information();
942 }
943 }
944 }
945