File indexing completed on 2023-05-30 09:09:58
0001 /* This file is part of the KDE project 0002 * 0003 * Copyright (C) 1998, 1999 Torben Weis <weis@kde.org> 0004 * 1999 Lars Knoll <knoll@kde.org> 0005 * 1999 Antti Koivisto <koivisto@kde.org> 0006 * 2000-2004 Dirk Mueller <mueller@kde.org> 0007 * 2003 Leo Savernik <l.savernik@aon.at> 0008 * 2003-2008 Apple Computer, Inc. 0009 * 2008 Allan Sandfeld Jensen <kde@carewolf.com> 0010 * 2006-2008 Germain Garand <germain@ebooksfrance.org> 0011 * 0012 * This library is free software; you can redistribute it and/or 0013 * modify it under the terms of the GNU Library General Public 0014 * License as published by the Free Software Foundation; either 0015 * version 2 of the License, or (at your option) any later version. 0016 * 0017 * This library is distributed in the hope that it will be useful, 0018 * but WITHOUT ANY WARRANTY; without even the implied warranty of 0019 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 0020 * Library General Public License for more details. 0021 * 0022 * You should have received a copy of the GNU Library General Public License 0023 * along with this library; see the file COPYING.LIB. If not, write to 0024 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 0025 * Boston, MA 02110-1301, USA. 0026 */ 0027 0028 #include "khtmlview.h" 0029 0030 #include "khtml_part.h" 0031 #include "khtml_events.h" 0032 #include <config-khtml.h> 0033 #if HAVE_X11 0034 #include <qx11info_x11.h> 0035 #endif 0036 0037 #include "html/html_documentimpl.h" 0038 #include "html/html_inlineimpl.h" 0039 #include "html/html_formimpl.h" 0040 #include "html/htmltokenizer.h" 0041 #include "editing/editor.h" 0042 #include "rendering/render_arena.h" 0043 #include "rendering/render_canvas.h" 0044 #include "rendering/render_frames.h" 0045 #include "rendering/render_replaced.h" 0046 #include "rendering/render_form.h" 0047 #include "rendering/render_layer.h" 0048 #include "rendering/render_line.h" 0049 #include "rendering/render_table.h" 0050 // removeme 0051 #define protected public 0052 #include "rendering/render_text.h" 0053 #undef protected 0054 #include "xml/dom2_eventsimpl.h" 0055 #include "css/cssstyleselector.h" 0056 #include "misc/loader.h" 0057 #include "khtml_settings.h" 0058 #include "khtml_printsettings.h" 0059 0060 #include "khtmlpart_p.h" 0061 0062 #include <kcursor.h> 0063 #include "khtml_debug.h" 0064 #include <kiconloader.h> 0065 #include <knotification.h> 0066 #include <kconfig.h> 0067 #include <../khtml_version.h> 0068 0069 #include <kstringhandler.h> 0070 #include <kconfiggroup.h> 0071 #include <ksharedconfig.h> 0072 #include <KWindowSystem> 0073 0074 #include <QBitmap> 0075 #include <QDialog> 0076 #include <QDialogButtonBox> 0077 #include <QLabel> 0078 #include <QKeyEvent> 0079 #include <QObject> 0080 #include <QPainter> 0081 #include <QHash> 0082 #include <QToolTip> 0083 #include <QString> 0084 #include <QTextDocument> 0085 #include <QTimer> 0086 #include <QAbstractEventDispatcher> 0087 #include <QVector> 0088 #include <QAbstractScrollArea> 0089 #include <QPrinter> 0090 #include <QPrintDialog> 0091 #include <qstandardpaths.h> 0092 0093 //#define DEBUG_FLICKER 0094 0095 #include <limits.h> 0096 #if HAVE_X11 0097 #include <X11/Xlib.h> 0098 #include <fixx11h.h> 0099 #elif defined(Q_OS_WIN) 0100 #include <windows.h> 0101 #endif 0102 0103 #if 0 0104 namespace khtml 0105 { 0106 void dumpLineBoxes(RenderFlow *flow); 0107 } 0108 #endif 0109 0110 using namespace DOM; 0111 using namespace khtml; 0112 0113 #ifndef NDEBUG 0114 static const int sFirstLayoutDelay = 520; 0115 static const int sParsingLayoutsInterval = 380; 0116 static const int sLayoutAttemptDelay = 300; 0117 #else 0118 static const int sFirstLayoutDelay = 280; 0119 static const int sParsingLayoutsInterval = 320; 0120 static const int sLayoutAttemptDelay = 200; 0121 #endif 0122 static const int sLayoutAttemptIncrement = 20; 0123 static const int sParsingLayoutsIncrement = 60; 0124 0125 static const int sSmoothScrollTime = 128; 0126 static const int sSmoothScrollTick = 16; 0127 static const int sSmoothScrollMinStaticPixels = 320 * 200; 0128 0129 static const int sMaxMissedDeadlines = 12; 0130 static const int sWayTooMany = -1; 0131 0132 class KHTMLViewPrivate 0133 { 0134 friend class KHTMLView; 0135 public: 0136 0137 enum PseudoFocusNodes { 0138 PFNone, 0139 PFTop, 0140 PFBottom 0141 }; 0142 0143 enum StaticBackgroundState { 0144 SBNone = 0, 0145 SBPartial, 0146 SBFull 0147 }; 0148 0149 enum CompletedState { 0150 CSNone = 0, 0151 CSFull, 0152 CSActionPending 0153 }; 0154 0155 KHTMLViewPrivate(KHTMLView *v) 0156 : underMouse(nullptr), underMouseNonShared(nullptr), oldUnderMouse(nullptr) 0157 { 0158 postponed_autorepeat = nullptr; 0159 scrollingFromWheelTimerId = 0; 0160 smoothScrollMode = KHTMLView::SSMWhenEfficient; 0161 0162 reset(); 0163 vpolicy = Qt::ScrollBarAsNeeded; 0164 hpolicy = Qt::ScrollBarAsNeeded; 0165 formCompletions = nullptr; 0166 prevScrollbarVisible = true; 0167 0168 possibleTripleClick = false; 0169 emitCompletedAfterRepaint = CSNone; 0170 cursorIconWidget = nullptr; 0171 cursorIconType = KHTMLView::LINK_NORMAL; 0172 m_mouseScrollTimer = nullptr; 0173 m_mouseScrollIndicator = nullptr; 0174 contentsX = 0; 0175 contentsY = 0; 0176 view = v; 0177 } 0178 ~KHTMLViewPrivate() 0179 { 0180 delete formCompletions; 0181 delete postponed_autorepeat; 0182 if (underMouse) { 0183 underMouse->deref(); 0184 } 0185 if (underMouseNonShared) { 0186 underMouseNonShared->deref(); 0187 } 0188 if (oldUnderMouse) { 0189 oldUnderMouse->deref(); 0190 } 0191 0192 delete cursorIconWidget; 0193 delete m_mouseScrollTimer; 0194 delete m_mouseScrollIndicator; 0195 } 0196 void reset() 0197 { 0198 if (underMouse) { 0199 underMouse->deref(); 0200 } 0201 underMouse = nullptr; 0202 if (underMouseNonShared) { 0203 underMouseNonShared->deref(); 0204 } 0205 underMouseNonShared = nullptr; 0206 if (oldUnderMouse) { 0207 oldUnderMouse->deref(); 0208 } 0209 oldUnderMouse = nullptr; 0210 linkPressed = false; 0211 staticWidget = SBNone; 0212 fixedObjectsCount = 0; 0213 staticObjectsCount = 0; 0214 tabMovePending = false; 0215 lastTabbingDirection = true; 0216 pseudoFocusNode = PFNone; 0217 zoomLevel = 100; 0218 #ifndef KHTML_NO_SCROLLBARS 0219 //We don't turn off the toolbars here 0220 //since if the user turns them 0221 //off, then chances are they want them turned 0222 //off always - even after a reset. 0223 #else 0224 vpolicy = ScrollBarAlwaysOff; 0225 hpolicy = ScrollBarAlwaysOff; 0226 #endif 0227 scrollBarMoved = false; 0228 contentsMoving = false; 0229 ignoreWheelEvents = false; 0230 scrollingFromWheel = QPoint(-1, -1); 0231 borderX = 30; 0232 borderY = 30; 0233 steps = 0; 0234 dx = dy = 0; 0235 paged = false; 0236 clickX = -1; 0237 clickY = -1; 0238 clickCount = 0; 0239 isDoubleClick = false; 0240 scrollingSelf = false; 0241 delete postponed_autorepeat; 0242 postponed_autorepeat = nullptr; 0243 layoutTimerId = 0; 0244 repaintTimerId = 0; 0245 scrollTimerId = 0; 0246 scrollSuspended = false; 0247 scrollSuspendPreActivate = false; 0248 smoothScrolling = false; 0249 smoothScrollModeIsDefault = true; 0250 shouldSmoothScroll = false; 0251 smoothScrollMissedDeadlines = 0; 0252 hasFrameset = false; 0253 complete = false; 0254 firstLayoutPending = true; 0255 #ifdef SPEED_DEBUG 0256 firstRepaintPending = true; 0257 #endif 0258 needsFullRepaint = true; 0259 dirtyLayout = false; 0260 layoutSchedulingEnabled = true; 0261 painting = false; 0262 layoutCounter = 0; 0263 layoutAttemptCounter = 0; 0264 scheduledLayoutCounter = 0; 0265 updateRegion = QRegion(); 0266 m_dialogsAllowed = true; 0267 accessKeysActivated = false; 0268 accessKeysPreActivate = false; 0269 0270 // the view might have been built before the part it will be assigned to, 0271 // so exceptionally, we need to directly ref/deref KHTMLGlobal to 0272 // account for this transitory case. 0273 KHTMLGlobal::ref(); 0274 accessKeysEnabled = KHTMLGlobal::defaultHTMLSettings()->accessKeysEnabled(); 0275 KHTMLGlobal::deref(); 0276 0277 emitCompletedAfterRepaint = CSNone; 0278 m_mouseEventsTarget = nullptr; 0279 m_clipHolder = nullptr; 0280 } 0281 void newScrollTimer(QWidget *view, int tid) 0282 { 0283 //qCDebug(KHTML_LOG) << "newScrollTimer timer " << tid; 0284 view->killTimer(scrollTimerId); 0285 scrollTimerId = tid; 0286 scrollSuspended = false; 0287 } 0288 enum ScrollDirection { ScrollLeft, ScrollRight, ScrollUp, ScrollDown }; 0289 0290 void adjustScroller(QWidget *view, ScrollDirection direction, ScrollDirection oppositedir) 0291 { 0292 static const struct { 0293 int msec, pixels; 0294 } timings [] = { 0295 {320, 1}, {224, 1}, {160, 1}, {112, 1}, {80, 1}, {56, 1}, {40, 1}, 0296 {28, 1}, {20, 1}, {20, 2}, {20, 3}, {20, 4}, {20, 6}, {20, 8}, {0, 0} 0297 }; 0298 if (!scrollTimerId || 0299 (static_cast<int>(scrollDirection) != direction && 0300 (static_cast<int>(scrollDirection) != oppositedir || scrollSuspended))) { 0301 scrollTiming = 6; 0302 scrollBy = timings[scrollTiming].pixels; 0303 scrollDirection = direction; 0304 newScrollTimer(view, view->startTimer(timings[scrollTiming].msec)); 0305 } else if (scrollDirection == direction && 0306 timings[scrollTiming + 1].msec && !scrollSuspended) { 0307 scrollBy = timings[++scrollTiming].pixels; 0308 newScrollTimer(view, view->startTimer(timings[scrollTiming].msec)); 0309 } else if (scrollDirection == oppositedir) { 0310 if (scrollTiming) { 0311 scrollBy = timings[--scrollTiming].pixels; 0312 newScrollTimer(view, view->startTimer(timings[scrollTiming].msec)); 0313 } 0314 } 0315 scrollSuspended = false; 0316 } 0317 0318 bool haveZoom() const 0319 { 0320 return zoomLevel != 100; 0321 } 0322 0323 void startScrolling() 0324 { 0325 smoothScrolling = true; 0326 smoothScrollTimer.start(sSmoothScrollTick); 0327 shouldSmoothScroll = false; 0328 } 0329 0330 void stopScrolling() 0331 { 0332 smoothScrollTimer.stop(); 0333 dx = dy = 0; 0334 steps = 0; 0335 updateContentsXY(); 0336 smoothScrolling = false; 0337 shouldSmoothScroll = false; 0338 } 0339 0340 void updateContentsXY() 0341 { 0342 contentsX = QApplication::isRightToLeft() ? 0343 view->horizontalScrollBar()->maximum() - view->horizontalScrollBar()->value() : view->horizontalScrollBar()->value(); 0344 contentsY = view->verticalScrollBar()->value(); 0345 } 0346 void scrollAccessKeys(int dx, int dy) 0347 { 0348 QList<QLabel *> wl = view->widget()->findChildren<QLabel *>("KHTMLAccessKey"); 0349 foreach (QLabel *w, wl) { 0350 w->move(w->pos() + QPoint(dx, dy)); 0351 } 0352 } 0353 void scrollExternalWidgets(int dx, int dy) 0354 { 0355 if (visibleWidgets.isEmpty()) { 0356 return; 0357 } 0358 0359 QHashIterator<void *, QWidget *> it(visibleWidgets); 0360 while (it.hasNext()) { 0361 it.next(); 0362 it.value()->move(it.value()->pos() + QPoint(dx, dy)); 0363 } 0364 } 0365 0366 NodeImpl *underMouse; 0367 NodeImpl *underMouseNonShared; 0368 NodeImpl *oldUnderMouse; 0369 0370 // Do not adjust bitfield enums sizes. 0371 // They are oversized because they are signed on some platforms. 0372 bool tabMovePending: 1; 0373 bool lastTabbingDirection: 1; 0374 PseudoFocusNodes pseudoFocusNode: 3; 0375 bool scrollBarMoved: 1; 0376 bool contentsMoving: 1; 0377 0378 Qt::ScrollBarPolicy vpolicy; 0379 Qt::ScrollBarPolicy hpolicy; 0380 bool prevScrollbarVisible: 1; 0381 bool linkPressed: 1; 0382 bool ignoreWheelEvents: 1; 0383 StaticBackgroundState staticWidget: 3; 0384 int staticObjectsCount; 0385 int fixedObjectsCount; 0386 0387 int zoomLevel; 0388 int borderX, borderY; 0389 int dx, dy; 0390 int steps; 0391 KConfig *formCompletions; 0392 0393 int clickX, clickY, clickCount; 0394 bool isDoubleClick; 0395 0396 bool paged; 0397 0398 bool scrollingSelf; 0399 int contentsX, contentsY; 0400 int layoutTimerId; 0401 QKeyEvent *postponed_autorepeat; 0402 0403 int repaintTimerId; 0404 int scrollTimerId; 0405 int scrollTiming; 0406 int scrollBy; 0407 ScrollDirection scrollDirection : 3; 0408 bool scrollSuspended : 1; 0409 bool scrollSuspendPreActivate : 1; 0410 KHTMLView::SmoothScrollingMode smoothScrollMode : 3; 0411 bool smoothScrolling : 1; 0412 bool smoothScrollModeIsDefault : 1; 0413 bool shouldSmoothScroll : 1; 0414 bool hasFrameset : 1; 0415 bool complete : 1; 0416 bool firstLayoutPending : 1; 0417 #ifdef SPEED_DEBUG 0418 bool firstRepaintPending : 1; 0419 #endif 0420 bool layoutSchedulingEnabled : 1; 0421 bool needsFullRepaint : 1; 0422 bool painting : 1; 0423 bool possibleTripleClick : 1; 0424 bool dirtyLayout : 1; 0425 bool m_dialogsAllowed : 1; 0426 short smoothScrollMissedDeadlines; 0427 int layoutCounter; 0428 int layoutAttemptCounter; 0429 int scheduledLayoutCounter; 0430 QRegion updateRegion; 0431 QTimer smoothScrollTimer; 0432 QTime smoothScrollStopwatch; 0433 QHash<void *, QWidget *> visibleWidgets; 0434 bool accessKeysEnabled; 0435 bool accessKeysActivated; 0436 bool accessKeysPreActivate; 0437 CompletedState emitCompletedAfterRepaint; 0438 0439 QLabel *cursorIconWidget; 0440 KHTMLView::LinkCursor cursorIconType; 0441 0442 // scrolling activated by MMB 0443 short m_mouseScroll_byX; 0444 short m_mouseScroll_byY; 0445 QPoint scrollingFromWheel; 0446 int scrollingFromWheelTimerId; 0447 QTimer *m_mouseScrollTimer; 0448 QWidget *m_mouseScrollIndicator; 0449 QPointer<QWidget> m_mouseEventsTarget; 0450 QStack<QRegion> *m_clipHolder; 0451 KHTMLView *view; 0452 }; 0453 0454 #ifndef QT_NO_TOOLTIP 0455 0456 /** calculates the client-side image map rectangle for the given image element 0457 * @param img image element 0458 * @param scrollOfs scroll offset of viewport in content coordinates 0459 * @param p position to be probed in viewport coordinates 0460 * @param r returns the bounding rectangle in content coordinates 0461 * @param s returns the title string 0462 * @return true if an appropriate area was found -- only in this case r and 0463 * s are valid, false otherwise 0464 */ 0465 static bool findImageMapRect(HTMLImageElementImpl *img, const QPoint &scrollOfs, 0466 const QPoint &p, QRect &r, QString &s) 0467 { 0468 HTMLMapElementImpl *map; 0469 if (img && img->document()->isHTMLDocument() && 0470 (map = static_cast<HTMLDocumentImpl *>(img->document())->getMap(img->imageMap()))) { 0471 RenderObject::NodeInfo info(true, false); 0472 RenderObject *rend = img->renderer(); 0473 int ax, ay; 0474 if (!rend || !rend->absolutePosition(ax, ay)) { 0475 return false; 0476 } 0477 // we're a client side image map 0478 bool inside = map->mapMouseEvent(p.x() - ax + scrollOfs.x(), 0479 p.y() - ay + scrollOfs.y(), rend->contentWidth(), 0480 rend->contentHeight(), info); 0481 if (inside && info.URLElement()) { 0482 HTMLAreaElementImpl *area = static_cast<HTMLAreaElementImpl *>(info.URLElement()); 0483 Q_ASSERT(area->id() == ID_AREA); 0484 s = area->getAttribute(ATTR_TITLE).string(); 0485 QRegion reg = area->cachedRegion(); 0486 if (!s.isEmpty() && !reg.isEmpty()) { 0487 r = reg.boundingRect(); 0488 r.translate(ax, ay); 0489 return true; 0490 } 0491 } 0492 } 0493 return false; 0494 } 0495 0496 bool KHTMLView::event(QEvent *e) 0497 { 0498 switch (e->type()) { 0499 case QEvent::ToolTip: { 0500 QHelpEvent *he = static_cast<QHelpEvent *>(e); 0501 QPoint p = he->pos(); 0502 0503 DOM::NodeImpl *node = d->underMouseNonShared; 0504 QRect region; 0505 while (node) { 0506 if (node->isElementNode()) { 0507 DOM::ElementImpl *e = static_cast<DOM::ElementImpl *>(node); 0508 QRect r; 0509 QString s; 0510 bool found = false; 0511 // for images, check if it is part of a client-side image map, 0512 // and query the <area>s' title attributes, too 0513 if (e->id() == ID_IMG && !e->getAttribute(ATTR_USEMAP).isEmpty()) { 0514 found = findImageMapRect(static_cast<HTMLImageElementImpl *>(e), 0515 viewportToContents(QPoint(0, 0)), p, r, s); 0516 } 0517 if (!found) { 0518 s = e->getAttribute(ATTR_TITLE).string().trimmed(); 0519 r = node->getRect(); 0520 } 0521 region |= QRect(contentsToViewport(r.topLeft()), r.size()); 0522 if (!s.isEmpty()) { 0523 QToolTip::showText(he->globalPos(), 0524 Qt::convertFromPlainText(s, Qt::WhiteSpaceNormal), 0525 widget(), region); 0526 break; 0527 } 0528 } 0529 node = node->parentNode(); 0530 } 0531 // Qt makes tooltip events happen nearly immediately when a preceding one was processed in the past few seconds. 0532 // We don't want that feature to apply to web tootlips however, as it clashes with dhtml menus. 0533 // So we'll just pretend we did not process that event. 0534 return false; 0535 } 0536 0537 case QEvent::DragEnter: 0538 case QEvent::DragMove: 0539 case QEvent::DragLeave: 0540 case QEvent::Drop: 0541 // In Qt4, one needs to both call accept() on the DND event and return 0542 // true on ::event for the candidate widget for the drop to be possible. 0543 // Apps hosting us, such as konq, can do the former but not the later. 0544 // We will do the second bit, as it's a no-op unless someone else explicitly 0545 // accepts the event. We need to skip the scrollarea to do that, 0546 // since it will just skip the events, both killing the drop, and 0547 // not permitting us to forward it up the part hiearchy in our dragEnterEvent, 0548 // etc. handlers 0549 return QWidget::event(e); 0550 case QEvent::StyleChange: 0551 case QEvent::LayoutRequest: { 0552 updateScrollBars(); 0553 return QAbstractScrollArea::event(e); 0554 } 0555 case QEvent::PaletteChange: 0556 slotPaletteChanged(); 0557 return QScrollArea::event(e); 0558 default: 0559 return QScrollArea::event(e); 0560 } 0561 } 0562 #endif 0563 0564 KHTMLView::KHTMLView(KHTMLPart *part, QWidget *parent) 0565 : QScrollArea(parent), d(new KHTMLViewPrivate(this)) 0566 { 0567 m_medium = "screen"; 0568 0569 m_part = part; 0570 0571 QScrollArea::setVerticalScrollBarPolicy(d->vpolicy); 0572 QScrollArea::setHorizontalScrollBarPolicy(d->hpolicy); 0573 0574 initWidget(); 0575 widget()->setMouseTracking(true); 0576 } 0577 0578 KHTMLView::~KHTMLView() 0579 { 0580 closeChildDialogs(); 0581 if (m_part) { 0582 DOM::DocumentImpl *doc = m_part->xmlDocImpl(); 0583 if (doc) { 0584 doc->detach(); 0585 } 0586 } 0587 delete d; 0588 } 0589 0590 void KHTMLView::setPart(KHTMLPart *part) 0591 { 0592 assert(part && !m_part); 0593 m_part = part; 0594 } 0595 0596 void KHTMLView::initWidget() 0597 { 0598 // Do not access the part here. It might not be fully constructed. 0599 0600 setFrameStyle(QFrame::NoFrame); 0601 setFocusPolicy(Qt::StrongFocus); 0602 viewport()->setFocusProxy(this); 0603 0604 _marginWidth = -1; // undefined 0605 _marginHeight = -1; 0606 _width = 0; 0607 _height = 0; 0608 0609 installEventFilter(this); 0610 0611 setAcceptDrops(true); 0612 if (!widget()) { 0613 setWidget(new QWidget(this)); 0614 } 0615 widget()->setAttribute(Qt::WA_NoSystemBackground); 0616 0617 // Do *not* remove this attribute frivolously. 0618 // You might not notice a change of behaviour in Debug builds 0619 // but removing opaque events will make QWidget::scroll fail horribly 0620 // in Release builds. 0621 widget()->setAttribute(Qt::WA_OpaquePaintEvent); 0622 0623 verticalScrollBar()->setCursor(Qt::ArrowCursor); 0624 horizontalScrollBar()->setCursor(Qt::ArrowCursor); 0625 0626 connect(&d->smoothScrollTimer, SIGNAL(timeout()), this, SLOT(scrollTick())); 0627 } 0628 0629 void KHTMLView::resizeContentsToViewport() 0630 { 0631 QSize s = viewport()->size(); 0632 resizeContents(s.width(), s.height()); 0633 } 0634 0635 // called by KHTMLPart::clear() 0636 void KHTMLView::clear() 0637 { 0638 if (d->accessKeysEnabled && d->accessKeysActivated) { 0639 accessKeysTimeout(); 0640 } 0641 viewport()->unsetCursor(); 0642 if (d->cursorIconWidget) { 0643 d->cursorIconWidget->hide(); 0644 } 0645 if (d->smoothScrolling) { 0646 d->stopScrolling(); 0647 } 0648 d->reset(); 0649 QAbstractEventDispatcher::instance()->unregisterTimers(this); 0650 emit cleared(); 0651 0652 QScrollArea::setHorizontalScrollBarPolicy(d->hpolicy); 0653 QScrollArea::setVerticalScrollBarPolicy(d->vpolicy); 0654 verticalScrollBar()->setEnabled(false); 0655 horizontalScrollBar()->setEnabled(false); 0656 0657 } 0658 0659 void KHTMLView::hideEvent(QHideEvent *e) 0660 { 0661 QScrollArea::hideEvent(e); 0662 } 0663 0664 void KHTMLView::showEvent(QShowEvent *e) 0665 { 0666 QScrollArea::showEvent(e); 0667 } 0668 0669 void KHTMLView::setMouseEventsTarget(QWidget *w) 0670 { 0671 d->m_mouseEventsTarget = w; 0672 } 0673 0674 QWidget *KHTMLView::mouseEventsTarget() const 0675 { 0676 return d->m_mouseEventsTarget; 0677 } 0678 0679 void KHTMLView::setClipHolder(QStack<QRegion> *ch) 0680 { 0681 d->m_clipHolder = ch; 0682 } 0683 0684 QStack<QRegion> *KHTMLView::clipHolder() const 0685 { 0686 return d->m_clipHolder; 0687 } 0688 0689 int KHTMLView::contentsWidth() const 0690 { 0691 return widget() ? widget()->width() : 0; 0692 } 0693 0694 int KHTMLView::contentsHeight() const 0695 { 0696 return widget() ? widget()->height() : 0; 0697 } 0698 0699 void KHTMLView::resizeContents(int w, int h) 0700 { 0701 if (!widget()) { 0702 return; 0703 } 0704 widget()->resize(w, h); 0705 if (!widget()->isVisible()) { 0706 updateScrollBars(); 0707 } 0708 } 0709 0710 int KHTMLView::contentsX() const 0711 { 0712 return d->contentsX; 0713 } 0714 0715 int KHTMLView::contentsY() const 0716 { 0717 return d->contentsY; 0718 } 0719 0720 int KHTMLView::visibleWidth() const 0721 { 0722 if (m_kwp->isRedirected()) { 0723 // our RenderWidget knows better 0724 if (RenderWidget *rw = m_kwp->renderWidget()) { 0725 int ret = rw->width() - rw->paddingLeft() - rw->paddingRight() - rw->borderLeft() - rw->borderRight(); 0726 if (verticalScrollBar()->isVisible()) { 0727 ret -= verticalScrollBar()->sizeHint().width(); 0728 ret = qMax(0, ret); 0729 } 0730 return ret; 0731 } 0732 } 0733 return viewport()->width(); 0734 } 0735 0736 int KHTMLView::visibleHeight() const 0737 { 0738 if (m_kwp->isRedirected()) { 0739 // our RenderWidget knows better 0740 if (RenderWidget *rw = m_kwp->renderWidget()) { 0741 int ret = rw->height() - rw->paddingBottom() - rw->paddingTop() - rw->borderTop() - rw->borderBottom(); 0742 if (horizontalScrollBar()->isVisible()) { 0743 ret -= horizontalScrollBar()->sizeHint().height(); 0744 ret = qMax(0, ret); 0745 } 0746 return ret; 0747 } 0748 } 0749 return viewport()->height(); 0750 } 0751 0752 void KHTMLView::setContentsPos(int x, int y) 0753 { 0754 horizontalScrollBar()->setValue(QApplication::isRightToLeft() ? 0755 horizontalScrollBar()->maximum() - x : x); 0756 verticalScrollBar()->setValue(y); 0757 } 0758 0759 void KHTMLView::scrollBy(int x, int y) 0760 { 0761 if (d->scrollTimerId) { 0762 d->newScrollTimer(this, 0); 0763 } 0764 horizontalScrollBar()->setValue(horizontalScrollBar()->value() + x); 0765 verticalScrollBar()->setValue(verticalScrollBar()->value() + y); 0766 } 0767 0768 QPoint KHTMLView::contentsToViewport(const QPoint &p) const 0769 { 0770 return QPoint(p.x() - contentsX(), p.y() - contentsY()); 0771 } 0772 0773 void KHTMLView::contentsToViewport(int x, int y, int &cx, int &cy) const 0774 { 0775 QPoint p(x, y); 0776 p = contentsToViewport(p); 0777 cx = p.x(); 0778 cy = p.y(); 0779 } 0780 0781 QPoint KHTMLView::viewportToContents(const QPoint &p) const 0782 { 0783 return QPoint(p.x() + contentsX(), p.y() + contentsY()); 0784 } 0785 0786 void KHTMLView::viewportToContents(int x, int y, int &cx, int &cy) const 0787 { 0788 QPoint p(x, y); 0789 p = viewportToContents(p); 0790 cx = p.x(); 0791 cy = p.y(); 0792 } 0793 0794 void KHTMLView::updateContents(int x, int y, int w, int h) 0795 { 0796 applyTransforms(x, y, w, h); 0797 if (m_kwp->isRedirected()) { 0798 QPoint off = m_kwp->absolutePos(); 0799 KHTMLView *pview = m_part->parentPart()->view(); 0800 pview->updateContents(x + off.x(), y + off.y(), w, h); 0801 } else { 0802 widget()->update(x, y, w, h); 0803 } 0804 } 0805 0806 void KHTMLView::updateContents(const QRect &r) 0807 { 0808 updateContents(r.x(), r.y(), r.width(), r.height()); 0809 } 0810 0811 void KHTMLView::repaintContents(int x, int y, int w, int h) 0812 { 0813 applyTransforms(x, y, w, h); 0814 if (m_kwp->isRedirected()) { 0815 QPoint off = m_kwp->absolutePos(); 0816 KHTMLView *pview = m_part->parentPart()->view(); 0817 pview->repaintContents(x + off.x(), y + off.y(), w, h); 0818 } else { 0819 widget()->repaint(x, y, w, h); 0820 } 0821 } 0822 0823 void KHTMLView::repaintContents(const QRect &r) 0824 { 0825 repaintContents(r.x(), r.y(), r.width(), r.height()); 0826 } 0827 0828 void KHTMLView::applyTransforms(int &x, int &y, int &w, int &h) const 0829 { 0830 if (d->haveZoom()) { 0831 const int z = d->zoomLevel; 0832 x = x * z / 100; 0833 y = y * z / 100; 0834 w = w * z / 100; 0835 h = h * z / 100; 0836 } 0837 x -= contentsX(); 0838 y -= contentsY(); 0839 } 0840 0841 void KHTMLView::revertTransforms(int &x, int &y, int &w, int &h) const 0842 { 0843 x += contentsX(); 0844 y += contentsY(); 0845 if (d->haveZoom()) { 0846 const int z = d->zoomLevel; 0847 x = x * 100 / z; 0848 y = y * 100 / z; 0849 w = w * 100 / z; 0850 h = h * 100 / z; 0851 } 0852 } 0853 0854 void KHTMLView::revertTransforms(int &x, int &y) const 0855 { 0856 int dummy = 0; 0857 revertTransforms(x, y, dummy, dummy); 0858 } 0859 0860 void KHTMLView::resizeEvent(QResizeEvent * /*e*/) 0861 { 0862 updateScrollBars(); 0863 0864 // If we didn't load anything, make white area as big as the view 0865 if (!m_part->xmlDocImpl()) { 0866 resizeContentsToViewport(); 0867 } 0868 0869 // Viewport-dependent media queries may cause us to need completely different style information. 0870 if (m_part->xmlDocImpl() && m_part->xmlDocImpl()->styleSelector()->affectedByViewportChange()) { 0871 m_part->xmlDocImpl()->updateStyleSelector(); 0872 } 0873 0874 if (d->layoutSchedulingEnabled) { 0875 layout(); 0876 } 0877 0878 QApplication::sendPostedEvents(viewport(), QEvent::Paint); 0879 0880 if (m_part && m_part->xmlDocImpl()) { 0881 if (m_part->parentPart()) { 0882 // sub-frame : queue the resize event until our toplevel is done layouting 0883 khtml::ChildFrame *cf = m_part->parentPart()->frame(m_part); 0884 if (cf && !cf->m_partContainerElement.isNull()) { 0885 cf->m_partContainerElement.data()->postResizeEvent(); 0886 } 0887 } else { 0888 // toplevel : dispatch sub-frames'resize events before our own 0889 HTMLPartContainerElementImpl::sendPostedResizeEvents(); 0890 m_part->xmlDocImpl()->dispatchWindowEvent(EventImpl::RESIZE_EVENT, false, false); 0891 } 0892 } 0893 } 0894 0895 void KHTMLView::paintEvent(QPaintEvent *e) 0896 { 0897 QRect r = e->rect(); 0898 QRect v(contentsX(), contentsY(), visibleWidth(), visibleHeight()); 0899 QPoint off(contentsX(), contentsY()); 0900 r.translate(off); 0901 r = r.intersected(v); 0902 if (!r.isValid() || r.isEmpty()) { 0903 return; 0904 } 0905 0906 QPainter p(widget()); 0907 p.translate(-off); 0908 0909 if (d->haveZoom()) { 0910 p.scale(d->zoomLevel / 100., d->zoomLevel / 100.); 0911 0912 r.setX(r.x() * 100 / d->zoomLevel); 0913 r.setY(r.y() * 100 / d->zoomLevel); 0914 r.setWidth(r.width() * 100 / d->zoomLevel); 0915 r.setHeight(r.height() * 100 / d->zoomLevel); 0916 r.adjust(-1, -1, 1, 1); 0917 } 0918 p.setClipRect(r); 0919 0920 int ex = r.x(); 0921 int ey = r.y(); 0922 int ew = r.width(); 0923 int eh = r.height(); 0924 0925 if (!m_part || !m_part->xmlDocImpl() || !m_part->xmlDocImpl()->renderer()) { 0926 p.fillRect(ex, ey, ew, eh, palette().brush(QPalette::Active, QPalette::Base)); 0927 return; 0928 } else if (d->complete && static_cast<RenderCanvas *>(m_part->xmlDocImpl()->renderer())->needsLayout()) { 0929 // an external update request happens while we have a layout scheduled 0930 unscheduleRelayout(); 0931 layout(); 0932 } else if (m_part->xmlDocImpl()->tokenizer()) { 0933 m_part->xmlDocImpl()->tokenizer()->setNormalYieldDelay(); 0934 } 0935 0936 if (d->painting) { 0937 // qCDebug(KHTML_LOG) << "WARNING: paintEvent reentered! "; 0938 return; 0939 } 0940 d->painting = true; 0941 0942 m_part->xmlDocImpl()->renderer()->layer()->paint(&p, r); 0943 0944 if (d->hasFrameset) { 0945 NodeImpl *body = static_cast<HTMLDocumentImpl *>(m_part->xmlDocImpl())->body(); 0946 if (body && body->renderer() && body->id() == ID_FRAMESET) { 0947 static_cast<RenderFrameSet *>(body->renderer())->paintFrameSetRules(&p, r); 0948 } else { 0949 d->hasFrameset = false; 0950 } 0951 } 0952 0953 khtml::DrawContentsEvent event(&p, ex, ey, ew, eh); 0954 QApplication::sendEvent(m_part, &event); 0955 0956 if (d->contentsMoving && !d->smoothScrolling && widget()->underMouse()) { 0957 QMouseEvent *tempEvent = new QMouseEvent(QEvent::MouseMove, widget()->mapFromGlobal(QCursor::pos()), 0958 Qt::NoButton, Qt::NoButton, Qt::NoModifier); 0959 QApplication::postEvent(widget(), tempEvent); 0960 } 0961 #ifdef SPEED_DEBUG 0962 if (d->firstRepaintPending && !m_part->parentPart()) { 0963 qCDebug(KHTML_LOG) << "FIRST PAINT:" << m_part->d->m_parsetime.elapsed(); 0964 } 0965 d->firstRepaintPending = false; 0966 #endif 0967 d->painting = false; 0968 } 0969 0970 void KHTMLView::setMarginWidth(int w) 0971 { 0972 // make it update the rendering area when set 0973 _marginWidth = w; 0974 } 0975 0976 void KHTMLView::setMarginHeight(int h) 0977 { 0978 // make it update the rendering area when set 0979 _marginHeight = h; 0980 } 0981 0982 void KHTMLView::layout() 0983 { 0984 if (m_part && m_part->xmlDocImpl()) { 0985 DOM::DocumentImpl *document = m_part->xmlDocImpl(); 0986 0987 khtml::RenderCanvas *canvas = static_cast<khtml::RenderCanvas *>(document->renderer()); 0988 if (!canvas) { 0989 return; 0990 } 0991 0992 d->layoutSchedulingEnabled = false; 0993 d->dirtyLayout = true; 0994 0995 // the reference object for the overflow property on canvas 0996 RenderObject *ref = nullptr; 0997 RenderObject *root = document->documentElement() ? document->documentElement()->renderer() : nullptr; 0998 0999 if (document->isHTMLDocument()) { 1000 NodeImpl *body = static_cast<HTMLDocumentImpl *>(document)->body(); 1001 if (body && body->renderer() && body->id() == ID_FRAMESET) { 1002 QScrollArea::setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); 1003 QScrollArea::setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); 1004 body->renderer()->setNeedsLayout(true); 1005 d->hasFrameset = true; 1006 } else if (root) { // only apply body's overflow to canvas if root has a visible overflow 1007 ref = (!body || root->style()->hidesOverflow()) ? root : body->renderer(); 1008 } 1009 } else { 1010 ref = root; 1011 } 1012 if (ref) { 1013 if (ref->style()->overflowX() == OHIDDEN) { 1014 if (d->hpolicy == Qt::ScrollBarAsNeeded) { 1015 QScrollArea::setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); 1016 } 1017 } else if (ref->style()->overflowX() == OSCROLL) { 1018 if (d->hpolicy == Qt::ScrollBarAsNeeded) { 1019 QScrollArea::setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn); 1020 } 1021 } else if (horizontalScrollBarPolicy() != d->hpolicy) { 1022 QScrollArea::setHorizontalScrollBarPolicy(d->hpolicy); 1023 } 1024 if (ref->style()->overflowY() == OHIDDEN) { 1025 if (d->vpolicy == Qt::ScrollBarAsNeeded) { 1026 QScrollArea::setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); 1027 } 1028 } else if (ref->style()->overflowY() == OSCROLL) { 1029 if (d->vpolicy == Qt::ScrollBarAsNeeded) { 1030 QScrollArea::setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn); 1031 } 1032 } else if (verticalScrollBarPolicy() != d->vpolicy) { 1033 QScrollArea::setVerticalScrollBarPolicy(d->vpolicy); 1034 } 1035 } 1036 d->needsFullRepaint = d->firstLayoutPending; 1037 if (_height != visibleHeight() || _width != visibleWidth()) { 1038 ; 1039 d->needsFullRepaint = true; 1040 _height = visibleHeight(); 1041 _width = visibleWidth(); 1042 } 1043 1044 canvas->layout(); 1045 1046 emit finishedLayout(); 1047 if (d->firstLayoutPending) { 1048 // make sure firstLayoutPending is set to false now in case this layout 1049 // wasn't scheduled 1050 d->firstLayoutPending = false; 1051 verticalScrollBar()->setEnabled(true); 1052 horizontalScrollBar()->setEnabled(true); 1053 } 1054 d->layoutCounter++; 1055 1056 if (d->accessKeysEnabled && d->accessKeysActivated) { 1057 emit hideAccessKeys(); 1058 displayAccessKeys(); 1059 } 1060 } else { 1061 _width = visibleWidth(); 1062 } 1063 1064 if (d->layoutTimerId) { 1065 killTimer(d->layoutTimerId); 1066 } 1067 d->layoutTimerId = 0; 1068 d->layoutSchedulingEnabled = true; 1069 } 1070 1071 void KHTMLView::closeChildDialogs() 1072 { 1073 QList<QDialog *> dlgs = findChildren<QDialog *>(); 1074 foreach (QDialog *dlg, dlgs) { 1075 if (dlg->testAttribute(Qt::WA_ShowModal)) { 1076 // qCDebug(KHTML_LOG) << "closeChildDialogs: closing dialog " << dlg; 1077 // close() ends up calling QButton::animateClick, which isn't immediate 1078 // we need something the exits the event loop immediately (#49068) 1079 dlg->reject(); 1080 } 1081 } 1082 d->m_dialogsAllowed = false; 1083 } 1084 1085 bool KHTMLView::dialogsAllowed() 1086 { 1087 bool allowed = d->m_dialogsAllowed; 1088 KHTMLPart *p = m_part->parentPart(); 1089 if (p && p->view()) { 1090 allowed &= p->view()->dialogsAllowed(); 1091 } 1092 return allowed; 1093 } 1094 1095 void KHTMLView::closeEvent(QCloseEvent *ev) 1096 { 1097 closeChildDialogs(); 1098 QScrollArea::closeEvent(ev); 1099 } 1100 1101 void KHTMLView::setZoomLevel(int percent) 1102 { 1103 percent = percent < 20 ? 20 : (percent > 800 ? 800 : percent); 1104 int oldpercent = d->zoomLevel; 1105 d->zoomLevel = percent; 1106 if (percent != oldpercent) { 1107 if (d->layoutSchedulingEnabled) { 1108 layout(); 1109 } 1110 widget()->update(); 1111 } 1112 } 1113 1114 int KHTMLView::zoomLevel() const 1115 { 1116 return d->zoomLevel; 1117 } 1118 1119 void KHTMLView::setSmoothScrollingMode(SmoothScrollingMode m) 1120 { 1121 d->smoothScrollMode = m; 1122 d->smoothScrollModeIsDefault = false; 1123 if (d->smoothScrolling && !m) { 1124 d->stopScrolling(); 1125 } 1126 } 1127 1128 void KHTMLView::setSmoothScrollingModeDefault(SmoothScrollingMode m) 1129 { 1130 // check for manual override 1131 if (!d->smoothScrollModeIsDefault) { 1132 return; 1133 } 1134 d->smoothScrollMode = m; 1135 if (d->smoothScrolling && !m) { 1136 d->stopScrolling(); 1137 } 1138 } 1139 1140 KHTMLView::SmoothScrollingMode KHTMLView::smoothScrollingMode() const 1141 { 1142 return d->smoothScrollMode; 1143 } 1144 1145 // 1146 // Event Handling 1147 // 1148 ///////////////// 1149 1150 void KHTMLView::mousePressEvent(QMouseEvent *_mouse) 1151 { 1152 if (!m_part->xmlDocImpl()) { 1153 return; 1154 } 1155 if (d->possibleTripleClick && (_mouse->button() & Qt::MouseButtonMask) == Qt::LeftButton) { 1156 mouseDoubleClickEvent(_mouse); // it handles triple clicks too 1157 return; 1158 } 1159 1160 int xm = _mouse->x(); 1161 int ym = _mouse->y(); 1162 revertTransforms(xm, ym); 1163 1164 // qCDebug(KHTML_LOG) << "mousePressEvent: viewport=("<<_mouse->x()-contentsX()<<"/"<<_mouse->y()-contentsY()<<"), contents=(" << xm << "/" << ym << ")\n"; 1165 1166 d->isDoubleClick = false; 1167 1168 DOM::NodeImpl::MouseEvent mev(_mouse->buttons(), DOM::NodeImpl::MousePress); 1169 m_part->xmlDocImpl()->prepareMouseEvent(false, xm, ym, &mev); 1170 1171 //qCDebug(KHTML_LOG) << "innerNode="<<mev.innerNode.nodeName().string(); 1172 1173 if ((_mouse->button() == Qt::MidButton) && 1174 !m_part->d->m_bOpenMiddleClick && !d->m_mouseScrollTimer && 1175 mev.url.isNull() && (mev.innerNode.elementId() != ID_INPUT)) { 1176 QPoint point = mapFromGlobal(_mouse->globalPos()); 1177 1178 d->m_mouseScroll_byX = 0; 1179 d->m_mouseScroll_byY = 0; 1180 1181 d->m_mouseScrollTimer = new QTimer(this); 1182 connect(d->m_mouseScrollTimer, SIGNAL(timeout()), this, SLOT(slotMouseScrollTimer())); 1183 1184 if (!d->m_mouseScrollIndicator) { 1185 QPixmap pixmap(48, 48), icon; 1186 pixmap.fill(QColor(qRgba(127, 127, 127, 127))); 1187 1188 QPainter p(&pixmap); 1189 QStyleOption option; 1190 1191 option.rect.setRect(16, 0, 16, 16); 1192 QApplication::style()->drawPrimitive(QStyle::PE_IndicatorArrowUp, &option, &p); 1193 option.rect.setRect(0, 16, 16, 16); 1194 QApplication::style()->drawPrimitive(QStyle::PE_IndicatorArrowLeft, &option, &p); 1195 option.rect.setRect(16, 32, 16, 16); 1196 QApplication::style()->drawPrimitive(QStyle::PE_IndicatorArrowDown, &option, &p); 1197 option.rect.setRect(32, 16, 16, 16); 1198 QApplication::style()->drawPrimitive(QStyle::PE_IndicatorArrowRight, &option, &p); 1199 p.drawEllipse(23, 23, 2, 2); 1200 1201 d->m_mouseScrollIndicator = new QWidget(this); 1202 d->m_mouseScrollIndicator->setFixedSize(48, 48); 1203 QPalette palette; 1204 palette.setBrush(d->m_mouseScrollIndicator->backgroundRole(), QBrush(pixmap)); 1205 d->m_mouseScrollIndicator->setPalette(palette); 1206 } 1207 d->m_mouseScrollIndicator->move(point.x() - 24, point.y() - 24); 1208 1209 bool hasHorBar = visibleWidth() < contentsWidth(); 1210 bool hasVerBar = visibleHeight() < contentsHeight(); 1211 1212 KConfigGroup cg(KSharedConfig::openConfig(), "HTML Settings"); 1213 if (cg.readEntry("ShowMouseScrollIndicator", true)) { 1214 d->m_mouseScrollIndicator->show(); 1215 d->m_mouseScrollIndicator->unsetCursor(); 1216 1217 QBitmap mask = d->m_mouseScrollIndicator->palette().brush(d->m_mouseScrollIndicator->backgroundRole()).texture().createHeuristicMask(true); 1218 1219 if (hasHorBar && !hasVerBar) { 1220 QBitmap bm(16, 16); 1221 bm.clear(); 1222 QPainter painter(&mask); 1223 painter.drawPixmap(QRectF(16, 0, bm.width(), bm.height()), bm, bm.rect()); 1224 painter.drawPixmap(QRectF(16, 32, bm.width(), bm.height()), bm, bm.rect()); 1225 d->m_mouseScrollIndicator->setCursor(Qt::SizeHorCursor); 1226 } else if (!hasHorBar && hasVerBar) { 1227 QBitmap bm(16, 16); 1228 bm.clear(); 1229 QPainter painter(&mask); 1230 painter.drawPixmap(QRectF(0, 16, bm.width(), bm.height()), bm, bm.rect()); 1231 painter.drawPixmap(QRectF(32, 16, bm.width(), bm.height()), bm, bm.rect()); 1232 d->m_mouseScrollIndicator->setCursor(Qt::SizeVerCursor); 1233 } else { 1234 d->m_mouseScrollIndicator->setCursor(Qt::SizeAllCursor); 1235 } 1236 1237 d->m_mouseScrollIndicator->setMask(mask); 1238 } else { 1239 if (hasHorBar && !hasVerBar) { 1240 viewport()->setCursor(Qt::SizeHorCursor); 1241 } else if (!hasHorBar && hasVerBar) { 1242 viewport()->setCursor(Qt::SizeVerCursor); 1243 } else { 1244 viewport()->setCursor(Qt::SizeAllCursor); 1245 } 1246 } 1247 1248 return; 1249 } else if (d->m_mouseScrollTimer) { 1250 delete d->m_mouseScrollTimer; 1251 d->m_mouseScrollTimer = nullptr; 1252 1253 if (d->m_mouseScrollIndicator) { 1254 d->m_mouseScrollIndicator->hide(); 1255 } 1256 } 1257 1258 if (d->clickCount > 0 && 1259 QPoint(d->clickX - xm, d->clickY - ym).manhattanLength() <= QApplication::startDragDistance()) { 1260 d->clickCount++; 1261 } else { 1262 d->clickCount = 1; 1263 d->clickX = xm; 1264 d->clickY = ym; 1265 } 1266 1267 bool swallowEvent = dispatchMouseEvent(EventImpl::MOUSEDOWN_EVENT, mev.innerNode.handle(), mev.innerNonSharedNode.handle(), true, 1268 d->clickCount, _mouse, true, DOM::NodeImpl::MousePress); 1269 1270 if (!swallowEvent) { 1271 emit m_part->nodeActivated(mev.innerNode); 1272 1273 khtml::MousePressEvent event(_mouse, xm, ym, mev.url, mev.target, mev.innerNode); 1274 QApplication::sendEvent(m_part, &event); 1275 // we might be deleted after this 1276 } 1277 } 1278 1279 void KHTMLView::mouseDoubleClickEvent(QMouseEvent *_mouse) 1280 { 1281 if (!m_part->xmlDocImpl()) { 1282 return; 1283 } 1284 1285 int xm = _mouse->x(); 1286 int ym = _mouse->y(); 1287 revertTransforms(xm, ym); 1288 1289 // qCDebug(KHTML_LOG) << "mouseDblClickEvent: x=" << xm << ", y=" << ym; 1290 1291 d->isDoubleClick = true; 1292 1293 DOM::NodeImpl::MouseEvent mev(_mouse->buttons(), DOM::NodeImpl::MouseDblClick); 1294 m_part->xmlDocImpl()->prepareMouseEvent(false, xm, ym, &mev); 1295 1296 // We do the same thing as mousePressEvent() here, since the DOM does not treat 1297 // single and double-click events as separate (only the detail, i.e. number of clicks differs) 1298 if (d->clickCount > 0 && 1299 QPoint(d->clickX - xm, d->clickY - ym).manhattanLength() <= QApplication::startDragDistance()) { 1300 d->clickCount++; 1301 } else { // shouldn't happen, if Qt has the same criterias for double clicks. 1302 d->clickCount = 1; 1303 d->clickX = xm; 1304 d->clickY = ym; 1305 } 1306 bool swallowEvent = dispatchMouseEvent(EventImpl::MOUSEDOWN_EVENT, mev.innerNode.handle(), mev.innerNonSharedNode.handle(), true, 1307 d->clickCount, _mouse, true, DOM::NodeImpl::MouseDblClick); 1308 1309 if (!swallowEvent) { 1310 khtml::MouseDoubleClickEvent event(_mouse, xm, ym, mev.url, mev.target, mev.innerNode, d->clickCount); 1311 QApplication::sendEvent(m_part, &event); 1312 } 1313 1314 d->possibleTripleClick = true; 1315 QTimer::singleShot(QApplication::doubleClickInterval(), this, SLOT(tripleClickTimeout())); 1316 } 1317 1318 void KHTMLView::tripleClickTimeout() 1319 { 1320 d->possibleTripleClick = false; 1321 d->clickCount = 0; 1322 } 1323 1324 static bool targetOpensNewWindow(KHTMLPart *part, QString target) 1325 { 1326 if (!target.isEmpty() && (target.toLower() != "_top") && 1327 (target.toLower() != "_self") && (target.toLower() != "_parent")) { 1328 if (target.toLower() == "_blank") { 1329 return true; 1330 } else { 1331 while (part->parentPart()) { 1332 part = part->parentPart(); 1333 } 1334 if (!part->frameExists(target)) { 1335 return true; 1336 } 1337 } 1338 } 1339 return false; 1340 } 1341 1342 void KHTMLView::mouseMoveEvent(QMouseEvent *_mouse) 1343 { 1344 if (d->m_mouseScrollTimer) { 1345 QPoint point = mapFromGlobal(_mouse->globalPos()); 1346 1347 int deltaX = point.x() - d->m_mouseScrollIndicator->x() - 24; 1348 int deltaY = point.y() - d->m_mouseScrollIndicator->y() - 24; 1349 1350 (deltaX > 0) ? d->m_mouseScroll_byX = 1 : d->m_mouseScroll_byX = -1; 1351 (deltaY > 0) ? d->m_mouseScroll_byY = 1 : d->m_mouseScroll_byY = -1; 1352 1353 double adX = qAbs(deltaX) / 30.0; 1354 double adY = qAbs(deltaY) / 30.0; 1355 1356 d->m_mouseScroll_byX = qMax(qMin(d->m_mouseScroll_byX * int(adX * adX), SHRT_MAX), SHRT_MIN); 1357 d->m_mouseScroll_byY = qMax(qMin(d->m_mouseScroll_byY * int(adY * adY), SHRT_MAX), SHRT_MIN); 1358 1359 if (d->m_mouseScroll_byX == 0 && d->m_mouseScroll_byY == 0) { 1360 d->m_mouseScrollTimer->stop(); 1361 } else if (!d->m_mouseScrollTimer->isActive()) { 1362 d->m_mouseScrollTimer->start(20); 1363 } 1364 } 1365 1366 if (!m_part->xmlDocImpl()) { 1367 return; 1368 } 1369 1370 int xm = _mouse->x(); 1371 int ym = _mouse->y(); 1372 revertTransforms(xm, ym); 1373 1374 DOM::NodeImpl::MouseEvent mev(_mouse->buttons(), DOM::NodeImpl::MouseMove); 1375 // Do not modify :hover/:active state while mouse is pressed. 1376 m_part->xmlDocImpl()->prepareMouseEvent(_mouse->buttons() /*readonly ?*/, xm, ym, &mev); 1377 1378 // qCDebug(KHTML_LOG) << "mouse move: " << _mouse->pos() 1379 // << " button " << _mouse->button() 1380 // << " state " << _mouse->state(); 1381 1382 DOM::NodeImpl *target = mev.innerNode.handle(); 1383 DOM::NodeImpl *fn = m_part->xmlDocImpl()->focusNode(); 1384 1385 // a widget may be the real target of this event (e.g. if a scrollbar's slider is being moved) 1386 if (d->m_mouseEventsTarget && fn && fn->renderer() && fn->renderer()->isWidget()) { 1387 target = fn; 1388 } 1389 1390 bool swallowEvent = dispatchMouseEvent(EventImpl::MOUSEMOVE_EVENT, target, mev.innerNonSharedNode.handle(), false, 1391 0, _mouse, true, DOM::NodeImpl::MouseMove); 1392 1393 if (d->clickCount > 0 && 1394 QPoint(d->clickX - xm, d->clickY - ym).manhattanLength() > QApplication::startDragDistance()) { 1395 d->clickCount = 0; // moving the mouse outside the threshold invalidates the click 1396 } 1397 1398 khtml::RenderObject *r = target ? target->renderer() : nullptr; 1399 bool setCursor = true; 1400 bool forceDefault = false; 1401 if (r && r->isWidget()) { 1402 RenderWidget *rw = static_cast<RenderWidget *>(r); 1403 KHTMLWidget *kw = qobject_cast<KHTMLView *>(rw->widget()) ? dynamic_cast<KHTMLWidget *>(rw->widget()) : nullptr; 1404 if (kw && kw->m_kwp->isRedirected()) { 1405 setCursor = false; 1406 } else if (QLineEdit *le = qobject_cast<QLineEdit *>(rw->widget())) { 1407 QList<QWidget *> wl = le->findChildren<QWidget *>("KLineEditButton"); 1408 // force arrow cursor above lineedit clear button 1409 foreach (QWidget *w, wl) { 1410 if (w->underMouse()) { 1411 forceDefault = true; 1412 break; 1413 } 1414 } 1415 } else if (QTextEdit *te = qobject_cast<QTextEdit *>(rw->widget())) { 1416 if (te->verticalScrollBar()->underMouse() || te->horizontalScrollBar()->underMouse()) { 1417 forceDefault = true; 1418 } 1419 } 1420 } 1421 khtml::RenderStyle *style = (r && r->style()) ? r->style() : nullptr; 1422 QCursor c; 1423 LinkCursor linkCursor = LINK_NORMAL; 1424 switch (!forceDefault ? (style ? style->cursor() : CURSOR_AUTO) : CURSOR_DEFAULT) { 1425 case CURSOR_AUTO: 1426 if (r && r->isText() && ((m_part->d->m_bMousePressed && m_part->d->editor_context.m_beganSelectingText) || 1427 !r->isPointInsideSelection(xm, ym, m_part->caret()))) { 1428 c = QCursor(Qt::IBeamCursor); 1429 } 1430 if (mev.url.length() && m_part->settings()->changeCursor()) { 1431 c = m_part->urlCursor(); 1432 if (mev.url.string().startsWith("mailto:") && mev.url.string().indexOf('@') > 0) { 1433 linkCursor = LINK_MAILTO; 1434 } else if (targetOpensNewWindow(m_part, mev.target.string())) { 1435 linkCursor = LINK_NEWWINDOW; 1436 } 1437 } 1438 1439 if (r && r->isFrameSet() && !static_cast<RenderFrameSet *>(r)->noResize()) { 1440 c = QCursor(static_cast<RenderFrameSet *>(r)->cursorShape()); 1441 } 1442 1443 break; 1444 case CURSOR_CROSS: 1445 c = QCursor(Qt::CrossCursor); 1446 break; 1447 case CURSOR_POINTER: 1448 c = m_part->urlCursor(); 1449 if (mev.url.string().startsWith("mailto:") && mev.url.string().indexOf('@') > 0) { 1450 linkCursor = LINK_MAILTO; 1451 } else if (targetOpensNewWindow(m_part, mev.target.string())) { 1452 linkCursor = LINK_NEWWINDOW; 1453 } 1454 break; 1455 case CURSOR_PROGRESS: 1456 c = QCursor(Qt::BusyCursor); // working_cursor 1457 break; 1458 case CURSOR_MOVE: 1459 case CURSOR_ALL_SCROLL: 1460 c = QCursor(Qt::SizeAllCursor); 1461 break; 1462 case CURSOR_E_RESIZE: 1463 case CURSOR_W_RESIZE: 1464 case CURSOR_EW_RESIZE: 1465 c = QCursor(Qt::SizeHorCursor); 1466 break; 1467 case CURSOR_N_RESIZE: 1468 case CURSOR_S_RESIZE: 1469 case CURSOR_NS_RESIZE: 1470 c = QCursor(Qt::SizeVerCursor); 1471 break; 1472 case CURSOR_NE_RESIZE: 1473 case CURSOR_SW_RESIZE: 1474 case CURSOR_NESW_RESIZE: 1475 c = QCursor(Qt::SizeBDiagCursor); 1476 break; 1477 case CURSOR_NW_RESIZE: 1478 case CURSOR_SE_RESIZE: 1479 case CURSOR_NWSE_RESIZE: 1480 c = QCursor(Qt::SizeFDiagCursor); 1481 break; 1482 case CURSOR_TEXT: 1483 c = QCursor(Qt::IBeamCursor); 1484 break; 1485 case CURSOR_WAIT: 1486 c = QCursor(Qt::WaitCursor); 1487 break; 1488 case CURSOR_HELP: 1489 c = QCursor(Qt::WhatsThisCursor); 1490 break; 1491 case CURSOR_DEFAULT: 1492 break; 1493 case CURSOR_NONE: 1494 case CURSOR_NOT_ALLOWED: 1495 c = QCursor(Qt::ForbiddenCursor); 1496 break; 1497 case CURSOR_ROW_RESIZE: 1498 c = QCursor(Qt::SplitVCursor); 1499 break; 1500 case CURSOR_COL_RESIZE: 1501 c = QCursor(Qt::SplitHCursor); 1502 break; 1503 case CURSOR_VERTICAL_TEXT: 1504 case CURSOR_CONTEXT_MENU: 1505 case CURSOR_NO_DROP: 1506 case CURSOR_CELL: 1507 case CURSOR_COPY: 1508 case CURSOR_ALIAS: 1509 c = QCursor(Qt::ArrowCursor); 1510 break; 1511 } 1512 1513 if (!setCursor && style && style->cursor() != CURSOR_AUTO) { 1514 setCursor = true; 1515 } 1516 1517 QWidget *vp = viewport(); 1518 for (KHTMLPart *p = m_part; p; p = p->parentPart()) 1519 if (!p->parentPart()) { 1520 vp = p->view()->viewport(); 1521 } 1522 if (setCursor && (vp->cursor().shape() != c.shape() || c.shape() == Qt::BitmapCursor)) { 1523 if (c.shape() == Qt::ArrowCursor) { 1524 for (KHTMLPart *p = m_part; p; p = p->parentPart()) { 1525 p->view()->viewport()->unsetCursor(); 1526 } 1527 } else { 1528 vp->setCursor(c); 1529 } 1530 } 1531 1532 if (linkCursor != LINK_NORMAL && isVisible() && hasFocus()) { 1533 #if HAVE_X11 1534 // ensure we don't trigger this code paths if we run in a Wayland session 1535 if (KWindowSystem::isPlatformX11()) { 1536 if (!d->cursorIconWidget) { 1537 #if HAVE_X11 1538 d->cursorIconWidget = new QLabel(nullptr, Qt::X11BypassWindowManagerHint); 1539 XSetWindowAttributes attr; 1540 attr.save_under = True; 1541 XChangeWindowAttributes(QX11Info::display(), d->cursorIconWidget->winId(), CWSaveUnder, &attr); 1542 #else 1543 d->cursorIconWidget = new QLabel(NULL, NULL); 1544 //TODO 1545 #endif 1546 } 1547 1548 // Update the pixmap if need be. 1549 if (linkCursor != d->cursorIconType) { 1550 d->cursorIconType = linkCursor; 1551 QString cursorIcon; 1552 switch (linkCursor) { 1553 case LINK_MAILTO: cursorIcon = "mail-message-new"; break; 1554 case LINK_NEWWINDOW: cursorIcon = "window-new"; break; 1555 default: cursorIcon = "dialog-error"; break; 1556 } 1557 1558 QPixmap icon_pixmap = KHTMLGlobal::iconLoader()->loadIcon(cursorIcon, KIconLoader::Small, 0, KIconLoader::DefaultState, QStringList(), nullptr, true); 1559 1560 d->cursorIconWidget->resize(icon_pixmap.width(), icon_pixmap.height()); 1561 d->cursorIconWidget->setMask(icon_pixmap.createMaskFromColor(Qt::transparent)); 1562 d->cursorIconWidget->setPixmap(icon_pixmap); 1563 d->cursorIconWidget->update(); 1564 } 1565 1566 QPoint c_pos = QCursor::pos(); 1567 d->cursorIconWidget->move(c_pos.x() + 15, c_pos.y() + 15); 1568 #if HAVE_X11 1569 XRaiseWindow(QX11Info::display(), d->cursorIconWidget->winId()); 1570 QApplication::flush(); 1571 #elif defined(Q_OS_WIN) 1572 SetWindowPos(d->cursorIconWidget->winId(), HWND_TOP, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE); 1573 #else 1574 //TODO? 1575 #endif 1576 d->cursorIconWidget->show(); 1577 } 1578 #endif 1579 } else if (d->cursorIconWidget) { 1580 d->cursorIconWidget->hide(); 1581 } 1582 1583 if (r && r->isWidget()) { 1584 _mouse->ignore(); 1585 } 1586 1587 if (!swallowEvent) { 1588 khtml::MouseMoveEvent event(_mouse, xm, ym, mev.url, mev.target, mev.innerNode); 1589 QApplication::sendEvent(m_part, &event); 1590 } 1591 } 1592 1593 void KHTMLView::mouseReleaseEvent(QMouseEvent *_mouse) 1594 { 1595 bool swallowEvent = false; 1596 1597 int xm = _mouse->x(); 1598 int ym = _mouse->y(); 1599 revertTransforms(xm, ym); 1600 1601 DOM::NodeImpl::MouseEvent mev(_mouse->buttons(), DOM::NodeImpl::MouseRelease); 1602 1603 if (m_part->xmlDocImpl()) { 1604 m_part->xmlDocImpl()->prepareMouseEvent(false, xm, ym, &mev); 1605 1606 DOM::NodeImpl *target = mev.innerNode.handle(); 1607 DOM::NodeImpl *fn = m_part->xmlDocImpl()->focusNode(); 1608 1609 // a widget may be the real target of this event (e.g. if a scrollbar's slider is being moved) 1610 if (d->m_mouseEventsTarget && fn && fn->renderer() && fn->renderer()->isWidget()) { 1611 target = fn; 1612 } 1613 1614 swallowEvent = dispatchMouseEvent(EventImpl::MOUSEUP_EVENT, target, mev.innerNonSharedNode.handle(), true, 1615 d->clickCount, _mouse, false, DOM::NodeImpl::MouseRelease); 1616 1617 // clear our sticky event target on any mouseRelease event 1618 if (d->m_mouseEventsTarget) { 1619 d->m_mouseEventsTarget = nullptr; 1620 } 1621 1622 if (d->clickCount > 0 && 1623 QPoint(d->clickX - xm, d->clickY - ym).manhattanLength() <= QApplication::startDragDistance()) { 1624 QMouseEvent me(d->isDoubleClick ? QEvent::MouseButtonDblClick : QEvent::MouseButtonRelease, 1625 _mouse->pos(), _mouse->button(), _mouse->buttons(), _mouse->modifiers()); 1626 dispatchMouseEvent(EventImpl::CLICK_EVENT, mev.innerNode.handle(), mev.innerNonSharedNode.handle(), true, 1627 d->clickCount, &me, true, DOM::NodeImpl::MouseRelease); 1628 } 1629 1630 khtml::RenderObject *r = target ? target->renderer() : nullptr; 1631 if (r && r->isWidget()) { 1632 _mouse->ignore(); 1633 } 1634 } 1635 1636 if (!swallowEvent) { 1637 khtml::MouseReleaseEvent event(_mouse, xm, ym, mev.url, mev.target, mev.innerNode); 1638 QApplication::sendEvent(m_part, &event); 1639 } 1640 } 1641 1642 // returns true if event should be swallowed 1643 bool KHTMLView::dispatchKeyEvent(QKeyEvent *_ke) 1644 { 1645 if (!m_part->xmlDocImpl()) { 1646 return false; 1647 } 1648 // Pressing and releasing a key should generate keydown, keypress and keyup events 1649 // Holding it down should generated keydown, keypress (repeatedly) and keyup events 1650 // The problem here is that Qt generates two autorepeat events (keyrelease+keypress) 1651 // for autorepeating, while DOM wants only one autorepeat event (keypress), so one 1652 // of the Qt events shouldn't be passed to DOM, but it should be still filtered 1653 // out if DOM would filter the autorepeat event. Additional problem is that Qt keyrelease 1654 // events don't have text() set (Qt bug?), so DOM often would ignore the keypress event 1655 // if it was created using Qt keyrelease, but Qt autorepeat keyrelease comes 1656 // before Qt autorepeat keypress (i.e. problem whether to filter it out or not). 1657 // The solution is to filter out and postpone the Qt autorepeat keyrelease until 1658 // the following Qt keypress event comes. If DOM accepts the DOM keypress event, 1659 // the postponed event will be simply discarded. If not, it will be passed to keyPressEvent() 1660 // again, and here it will be ignored. 1661 // 1662 // Qt: Press | Release(autorepeat) Press(autorepeat) etc. | Release 1663 // DOM: Down + Press | (nothing) Press | Up 1664 1665 // It's also possible to get only Releases. E.g. the release of alt-tab, 1666 // or when the keypresses get captured by an accel. 1667 1668 if (_ke == d->postponed_autorepeat) { // replayed event 1669 return false; 1670 } 1671 1672 if (_ke->type() == QEvent::KeyPress) { 1673 if (!_ke->isAutoRepeat()) { 1674 bool ret = dispatchKeyEventHelper(_ke, false); // keydown 1675 // don't send keypress even if keydown was blocked, like IE (and unlike Mozilla) 1676 if (!ret && dispatchKeyEventHelper(_ke, true)) { // keypress 1677 ret = true; 1678 } 1679 return ret; 1680 } else { // autorepeat 1681 bool ret = dispatchKeyEventHelper(_ke, true); // keypress 1682 if (!ret && d->postponed_autorepeat) { 1683 keyPressEvent(d->postponed_autorepeat); 1684 } 1685 delete d->postponed_autorepeat; 1686 d->postponed_autorepeat = nullptr; 1687 return ret; 1688 } 1689 } else { // QEvent::KeyRelease 1690 // Discard postponed "autorepeat key-release" events that didn't see 1691 // a keypress after them (e.g. due to QAccel) 1692 delete d->postponed_autorepeat; 1693 d->postponed_autorepeat = nullptr; 1694 1695 if (!_ke->isAutoRepeat()) { 1696 return dispatchKeyEventHelper(_ke, false); // keyup 1697 } else { 1698 d->postponed_autorepeat = new QKeyEvent(_ke->type(), _ke->key(), _ke->modifiers(), 1699 _ke->text(), _ke->isAutoRepeat(), _ke->count()); 1700 if (_ke->isAccepted()) { 1701 d->postponed_autorepeat->accept(); 1702 } else { 1703 d->postponed_autorepeat->ignore(); 1704 } 1705 return true; 1706 } 1707 } 1708 } 1709 1710 // returns true if event should be swallowed 1711 bool KHTMLView::dispatchKeyEventHelper(QKeyEvent *_ke, bool keypress) 1712 { 1713 DOM::NodeImpl *keyNode = m_part->xmlDocImpl()->focusNode(); 1714 if (keyNode) { 1715 return keyNode->dispatchKeyEvent(_ke, keypress); 1716 } else { // no focused node, send to document 1717 return m_part->xmlDocImpl()->dispatchKeyEvent(_ke, keypress); 1718 } 1719 } 1720 1721 void KHTMLView::keyPressEvent(QKeyEvent *_ke) 1722 { 1723 // If CTRL was hit, be prepared for access keys 1724 if (d->accessKeysEnabled && _ke->key() == Qt::Key_Control && !(_ke->modifiers() & ~Qt::ControlModifier) && !d->accessKeysActivated) { 1725 d->accessKeysPreActivate = true; 1726 _ke->accept(); 1727 return; 1728 } 1729 1730 if (_ke->key() == Qt::Key_Shift && !(_ke->modifiers() & ~Qt::ShiftModifier)) { 1731 d->scrollSuspendPreActivate = true; 1732 } 1733 1734 // accesskey handling needs to be done before dispatching, otherwise e.g. lineedits 1735 // may eat the event 1736 1737 if (d->accessKeysEnabled && d->accessKeysActivated) { 1738 int state = (_ke->modifiers() & (Qt::ShiftModifier | Qt::ControlModifier | Qt::AltModifier | Qt::MetaModifier)); 1739 if (state == 0 || state == Qt::ShiftModifier) { 1740 if (_ke->key() != Qt::Key_Shift) { 1741 accessKeysTimeout(); 1742 } 1743 handleAccessKey(_ke); 1744 _ke->accept(); 1745 return; 1746 } 1747 accessKeysTimeout(); 1748 _ke->accept(); 1749 return; 1750 } 1751 1752 if (dispatchKeyEvent(_ke)) { 1753 // If either keydown or keypress was accepted by a widget, or canceled by JS, stop here. 1754 _ke->accept(); 1755 return; 1756 } 1757 1758 int offs = (viewport()->height() < 30) ? viewport()->height() : 30; // ### ?? 1759 if (_ke->modifiers() & Qt::ShiftModifier) 1760 switch (_ke->key()) { 1761 case Qt::Key_Space: 1762 verticalScrollBar()->setValue(verticalScrollBar()->value() - viewport()->height() + offs); 1763 if (d->scrollSuspended) { 1764 d->newScrollTimer(this, 0); 1765 } 1766 break; 1767 1768 case Qt::Key_Down: 1769 case Qt::Key_J: 1770 d->adjustScroller(this, KHTMLViewPrivate::ScrollDown, KHTMLViewPrivate::ScrollUp); 1771 break; 1772 1773 case Qt::Key_Up: 1774 case Qt::Key_K: 1775 d->adjustScroller(this, KHTMLViewPrivate::ScrollUp, KHTMLViewPrivate::ScrollDown); 1776 break; 1777 1778 case Qt::Key_Left: 1779 case Qt::Key_H: 1780 d->adjustScroller(this, KHTMLViewPrivate::ScrollLeft, KHTMLViewPrivate::ScrollRight); 1781 break; 1782 1783 case Qt::Key_Right: 1784 case Qt::Key_L: 1785 d->adjustScroller(this, KHTMLViewPrivate::ScrollRight, KHTMLViewPrivate::ScrollLeft); 1786 break; 1787 } 1788 else 1789 switch (_ke->key()) { 1790 case Qt::Key_Down: 1791 case Qt::Key_J: 1792 if (!d->scrollTimerId || d->scrollSuspended) { 1793 verticalScrollBar()->setValue(verticalScrollBar()->value() + 10); 1794 } 1795 if (d->scrollTimerId) { 1796 d->newScrollTimer(this, 0); 1797 } 1798 break; 1799 1800 case Qt::Key_Space: 1801 case Qt::Key_PageDown: 1802 d->shouldSmoothScroll = true; 1803 verticalScrollBar()->setValue(verticalScrollBar()->value() + viewport()->height() - offs); 1804 if (d->scrollSuspended) { 1805 d->newScrollTimer(this, 0); 1806 } 1807 break; 1808 1809 case Qt::Key_Up: 1810 case Qt::Key_K: 1811 if (!d->scrollTimerId || d->scrollSuspended) { 1812 verticalScrollBar()->setValue(verticalScrollBar()->value() - 10); 1813 } 1814 if (d->scrollTimerId) { 1815 d->newScrollTimer(this, 0); 1816 } 1817 break; 1818 1819 case Qt::Key_PageUp: 1820 d->shouldSmoothScroll = true; 1821 verticalScrollBar()->setValue(verticalScrollBar()->value() - viewport()->height() + offs); 1822 if (d->scrollSuspended) { 1823 d->newScrollTimer(this, 0); 1824 } 1825 break; 1826 case Qt::Key_Right: 1827 case Qt::Key_L: 1828 if (!d->scrollTimerId || d->scrollSuspended) { 1829 horizontalScrollBar()->setValue(horizontalScrollBar()->value() + 10); 1830 } 1831 if (d->scrollTimerId) { 1832 d->newScrollTimer(this, 0); 1833 } 1834 break; 1835 1836 case Qt::Key_Left: 1837 case Qt::Key_H: 1838 if (!d->scrollTimerId || d->scrollSuspended) { 1839 horizontalScrollBar()->setValue(horizontalScrollBar()->value() - 10); 1840 } 1841 if (d->scrollTimerId) { 1842 d->newScrollTimer(this, 0); 1843 } 1844 break; 1845 case Qt::Key_Enter: 1846 case Qt::Key_Return: 1847 // ### FIXME: 1848 // or even better to HTMLAnchorElementImpl::event() 1849 if (m_part->xmlDocImpl()) { 1850 NodeImpl *n = m_part->xmlDocImpl()->focusNode(); 1851 if (n) { 1852 n->setActive(); 1853 } 1854 } 1855 break; 1856 case Qt::Key_Home: 1857 verticalScrollBar()->setValue(0); 1858 horizontalScrollBar()->setValue(0); 1859 if (d->scrollSuspended) { 1860 d->newScrollTimer(this, 0); 1861 } 1862 break; 1863 case Qt::Key_End: 1864 verticalScrollBar()->setValue(contentsHeight() - visibleHeight()); 1865 if (d->scrollSuspended) { 1866 d->newScrollTimer(this, 0); 1867 } 1868 break; 1869 case Qt::Key_Shift: 1870 // what are you doing here? 1871 _ke->ignore(); 1872 return; 1873 default: 1874 if (d->scrollTimerId) { 1875 d->newScrollTimer(this, 0); 1876 } 1877 _ke->ignore(); 1878 return; 1879 } 1880 1881 _ke->accept(); 1882 } 1883 1884 void KHTMLView::keyReleaseEvent(QKeyEvent *_ke) 1885 { 1886 if (d->scrollSuspendPreActivate && _ke->key() != Qt::Key_Shift) { 1887 d->scrollSuspendPreActivate = false; 1888 } 1889 if (_ke->key() == Qt::Key_Shift && d->scrollSuspendPreActivate && !(_ke->modifiers() & Qt::ShiftModifier)) 1890 if (d->scrollTimerId) { 1891 d->scrollSuspended = !d->scrollSuspended; 1892 if (d->scrollSuspended) { 1893 d->stopScrolling(); 1894 } 1895 } 1896 1897 if (d->accessKeysEnabled) { 1898 if (d->accessKeysPreActivate && _ke->key() != Qt::Key_Control) { 1899 d->accessKeysPreActivate = false; 1900 } 1901 if (d->accessKeysPreActivate && !(_ke->modifiers() & Qt::ControlModifier)) { 1902 displayAccessKeys(); 1903 m_part->setStatusBarText(i18n("Access Keys activated"), KHTMLPart::BarOverrideText); 1904 d->accessKeysActivated = true; 1905 d->accessKeysPreActivate = false; 1906 _ke->accept(); 1907 return; 1908 } else if (d->accessKeysActivated) { 1909 accessKeysTimeout(); 1910 _ke->accept(); 1911 return; 1912 } 1913 } 1914 1915 // Send keyup event 1916 if (dispatchKeyEvent(_ke)) { 1917 _ke->accept(); 1918 return; 1919 } 1920 1921 QScrollArea::keyReleaseEvent(_ke); 1922 } 1923 1924 bool KHTMLView::focusNextPrevChild(bool next) 1925 { 1926 // Now try to find the next child 1927 if (m_part->xmlDocImpl() && focusNextPrevNode(next)) { 1928 //if (m_part->xmlDocImpl()->focusNode()) 1929 // qCDebug(KHTML_LOG) << "focusNode.name: " 1930 // << m_part->xmlDocImpl()->focusNode()->nodeName().string(); 1931 return true; // focus node found 1932 } 1933 1934 // If we get here, pass tabbing control up to the next/previous child in our parent 1935 d->pseudoFocusNode = KHTMLViewPrivate::PFNone; 1936 if (m_part->parentPart() && m_part->parentPart()->view()) { 1937 return m_part->parentPart()->view()->focusNextPrevChild(next); 1938 } 1939 1940 return QWidget::focusNextPrevChild(next); 1941 } 1942 1943 void KHTMLView::doAutoScroll() 1944 { 1945 QPoint pos = QCursor::pos(); 1946 QPoint off; 1947 KHTMLView *v = m_kwp->isRedirected() ? m_kwp->rootViewPos(off) : this; 1948 pos = v->viewport()->mapFromGlobal(pos); 1949 pos -= off; 1950 int xm, ym; 1951 viewportToContents(pos.x(), pos.y(), xm, ym); // ### 1952 1953 pos = QPoint(pos.x() - viewport()->x(), pos.y() - viewport()->y()); 1954 if ((pos.y() < 0) || (pos.y() > visibleHeight()) || 1955 (pos.x() < 0) || (pos.x() > visibleWidth())) { 1956 ensureVisible(xm, ym, 0, 5); 1957 1958 #ifndef KHTML_NO_SELECTION 1959 // extend the selection while scrolling 1960 DOM::Node innerNode; 1961 if (m_part->isExtendingSelection()) { 1962 RenderObject::NodeInfo renderInfo(true/*readonly*/, false/*active*/); 1963 m_part->xmlDocImpl()->renderer()->layer() 1964 ->nodeAtPoint(renderInfo, xm, ym); 1965 innerNode = renderInfo.innerNode(); 1966 }/*end if*/ 1967 1968 if (innerNode.handle() && innerNode.handle()->renderer() 1969 && innerNode.handle()->renderer()->shouldSelect()) { 1970 m_part->extendSelectionTo(xm, ym, innerNode); 1971 }/*end if*/ 1972 #endif // KHTML_NO_SELECTION 1973 } 1974 } 1975 1976 // KHTML defines its own stacking order for any object and thus takes 1977 // control of widget painting whenever it can. This is called "redirection". 1978 // 1979 // Redirected widgets are placed off screen. When they are declared as a child of our view (ChildPolished event), 1980 // an event filter is installed, so as to catch any paint event and translate them as update() of the view's main widget. 1981 // 1982 // Painting also happens spontaneously within widgets. In this case, the widget would update() parts of itself. 1983 // While this ordinarily results in a paintEvent being schedduled, it is not the case with off screen widgets. 1984 // Thus update() is monitored by using the mechanism that deffers any update call happening during a paint event, 1985 // transforming it into a posted UpdateLater event. Hence the need to set Qt::WA_WState_InPaintEvent on redirected widgets. 1986 // 1987 // Once the UpdateLater event has been received, Qt::WA_WState_InPaintEvent is removed and the process continues 1988 // with the update of the corresponding rect on the view. That in turn will make our painting subsystem render() 1989 // the widget at the correct stacking position. 1990 // 1991 // For non-redirected (e.g. external) widgets, z-order is honoured through masking. cf.RenderLayer::updateWidgetMasks 1992 1993 static void handleWidget(QWidget *w, KHTMLView *view, bool recurse = true) 1994 { 1995 if (w->isWindow()) { 1996 return; 1997 } 1998 1999 if (!qobject_cast<QFrame *>(w)) { 2000 w->setAttribute(Qt::WA_NoSystemBackground); 2001 } 2002 2003 w->setAttribute(Qt::WA_WState_InPaintEvent); 2004 2005 if (!(w->objectName() == "KLineEditButton")) { 2006 w->setAttribute(Qt::WA_OpaquePaintEvent); 2007 } 2008 2009 w->installEventFilter(view); 2010 2011 if (!recurse) { 2012 return; 2013 } 2014 if (qobject_cast<KHTMLView *>(w)) { 2015 handleWidget(static_cast<KHTMLView *>(w)->widget(), view, false); 2016 handleWidget(static_cast<KHTMLView *>(w)->horizontalScrollBar(), view, false); 2017 handleWidget(static_cast<KHTMLView *>(w)->verticalScrollBar(), view, false); 2018 return; 2019 } 2020 2021 QObjectList children = w->children(); 2022 foreach (QObject *object, children) { 2023 QWidget *widget = qobject_cast<QWidget *>(object); 2024 if (widget) { 2025 handleWidget(widget, view); 2026 } 2027 } 2028 } 2029 2030 class KHTMLBackingStoreHackWidget : public QWidget 2031 { 2032 public: 2033 void publicEvent(QEvent *e) 2034 { 2035 QWidget::event(e); 2036 } 2037 }; 2038 2039 bool KHTMLView::viewportEvent(QEvent *e) 2040 { 2041 switch (e->type()) { 2042 // those must not be dispatched to the specialized handlers 2043 // as widgetEvent() already took care of that 2044 case QEvent::MouseButtonPress: 2045 case QEvent::MouseButtonRelease: 2046 case QEvent::MouseButtonDblClick: 2047 case QEvent::MouseMove: 2048 #ifndef QT_NO_WHEELEVENT 2049 case QEvent::Wheel: 2050 #endif 2051 case QEvent::ContextMenu: 2052 case QEvent::DragEnter: 2053 case QEvent::DragMove: 2054 case QEvent::DragLeave: 2055 case QEvent::Drop: 2056 return false; 2057 default: 2058 break; 2059 } 2060 return QScrollArea::viewportEvent(e); 2061 } 2062 2063 static void setInPaintEventFlag(QWidget *w, bool b = true, bool recurse = true) 2064 { 2065 w->setAttribute(Qt::WA_WState_InPaintEvent, b); 2066 2067 if (!recurse) { 2068 return; 2069 } 2070 if (qobject_cast<KHTMLView *>(w)) { 2071 setInPaintEventFlag(static_cast<KHTMLView *>(w)->widget(), b, false); 2072 setInPaintEventFlag(static_cast<KHTMLView *>(w)->horizontalScrollBar(), b, false); 2073 setInPaintEventFlag(static_cast<KHTMLView *>(w)->verticalScrollBar(), b, false); 2074 return; 2075 } 2076 2077 foreach (QObject *cw, w->children()) { 2078 if (cw->isWidgetType() && ! static_cast<QWidget *>(cw)->isWindow() 2079 && !(static_cast<QWidget *>(cw)->windowModality() & Qt::ApplicationModal)) { 2080 setInPaintEventFlag(static_cast<QWidget *>(cw), b); 2081 } 2082 } 2083 } 2084 2085 bool KHTMLView::eventFilter(QObject *o, QEvent *e) 2086 { 2087 if (e->type() == QEvent::ShortcutOverride) { 2088 QKeyEvent *ke = (QKeyEvent *) e; 2089 if (m_part->isEditable() || m_part->isCaretMode() 2090 || (m_part->xmlDocImpl() && m_part->xmlDocImpl()->focusNode() 2091 && m_part->xmlDocImpl()->focusNode()->isContentEditable())) { 2092 if ((ke->modifiers() & Qt::ControlModifier) || (ke->modifiers() & Qt::ShiftModifier)) { 2093 switch (ke->key()) { 2094 case Qt::Key_Left: 2095 case Qt::Key_Right: 2096 case Qt::Key_Up: 2097 case Qt::Key_Down: 2098 case Qt::Key_Home: 2099 case Qt::Key_End: 2100 ke->accept(); 2101 return true; 2102 default: 2103 break; 2104 } 2105 } 2106 } 2107 } 2108 2109 if (e->type() == QEvent::Leave) { 2110 if (d->cursorIconWidget) { 2111 d->cursorIconWidget->hide(); 2112 } 2113 m_part->resetHoverText(); 2114 } 2115 2116 QWidget *view = widget(); 2117 if (o == view) { 2118 if (widgetEvent(e)) { 2119 return true; 2120 } else if (e->type() == QEvent::Resize) { 2121 updateScrollBars(); 2122 return false; 2123 } 2124 } else if (o->isWidgetType()) { 2125 QWidget *v = static_cast<QWidget *>(o); 2126 QWidget *c = v; 2127 while (v && v != view) { 2128 c = v; 2129 v = v->parentWidget(); 2130 } 2131 KHTMLWidget *k = dynamic_cast<KHTMLWidget *>(c); 2132 if (v && k && k->m_kwp->isRedirected()) { 2133 bool block = false; 2134 QWidget *w = static_cast<QWidget *>(o); 2135 switch (e->type()) { 2136 case QEvent::UpdateRequest: { 2137 // implicitly call qt_syncBackingStore(w) 2138 static_cast<KHTMLBackingStoreHackWidget *>(w)->publicEvent(e); 2139 block = true; 2140 break; 2141 } 2142 case QEvent::UpdateLater: 2143 // no break; 2144 case QEvent::Paint: 2145 if (!allowWidgetPaintEvents) { 2146 // eat the event. Like this we can control exactly when the widget 2147 // gets repainted. 2148 block = true; 2149 int x = 0, y = 0; 2150 QWidget *v = w; 2151 while (v && v->parentWidget() != view) { 2152 x += v->x(); 2153 y += v->y(); 2154 v = v->parentWidget(); 2155 } 2156 2157 QPoint ap = k->m_kwp->absolutePos(); 2158 x += ap.x(); 2159 y += ap.y(); 2160 } 2161 break; 2162 case QEvent::MouseMove: 2163 case QEvent::MouseButtonPress: 2164 case QEvent::MouseButtonRelease: 2165 case QEvent::MouseButtonDblClick: { 2166 2167 if (0 && w->parentWidget() == view && !qobject_cast<QScrollBar *>(w) && !::qobject_cast<QScrollBar *>(w)) { 2168 QMouseEvent *me = static_cast<QMouseEvent *>(e); 2169 QPoint pt = w->mapTo(view, me->pos()); 2170 QMouseEvent me2(me->type(), pt, me->button(), me->buttons(), me->modifiers()); 2171 2172 if (e->type() == QEvent::MouseMove) { 2173 mouseMoveEvent(&me2); 2174 } else if (e->type() == QEvent::MouseButtonPress) { 2175 mousePressEvent(&me2); 2176 } else if (e->type() == QEvent::MouseButtonRelease) { 2177 mouseReleaseEvent(&me2); 2178 } else { 2179 mouseDoubleClickEvent(&me2); 2180 } 2181 block = true; 2182 } 2183 break; 2184 } 2185 case QEvent::KeyPress: 2186 case QEvent::KeyRelease: 2187 if (w->parentWidget() == view && !qobject_cast<QScrollBar *>(w)) { 2188 QKeyEvent *ke = static_cast<QKeyEvent *>(e); 2189 if (e->type() == QEvent::KeyPress) { 2190 keyPressEvent(ke); 2191 ke->accept(); 2192 } else { 2193 keyReleaseEvent(ke); 2194 ke->accept(); 2195 } 2196 block = true; 2197 } 2198 2199 if (qobject_cast<KUrlRequester *>(w->parentWidget()) && 2200 e->type() == QEvent::KeyPress) { 2201 // Since keypress events on the upload widget will 2202 // be forwarded to the lineedit anyway, 2203 // block the original copy at this level to prevent 2204 // double-emissions of events it doesn't accept 2205 e->ignore(); 2206 block = true; 2207 } 2208 2209 break; 2210 case QEvent::FocusIn: 2211 case QEvent::FocusOut: { 2212 QPoint dummy; 2213 KHTMLView *root = m_kwp->rootViewPos(dummy); 2214 if (!root) { 2215 root = this; 2216 } 2217 block = static_cast<QFocusEvent *>(e)->reason() != Qt::MouseFocusReason || root->underMouse(); 2218 break; 2219 } 2220 default: 2221 break; 2222 } 2223 if (block) { 2224 //qDebug("eating event"); 2225 return true; 2226 } 2227 } 2228 } 2229 2230 // qCDebug(KHTML_LOG) <<"passing event on to sv event filter object=" << o->className() << " event=" << e->type(); 2231 return QScrollArea::eventFilter(o, e); 2232 } 2233 2234 bool KHTMLView::widgetEvent(QEvent *e) 2235 { 2236 switch (e->type()) { 2237 case QEvent::MouseButtonPress: 2238 case QEvent::MouseButtonRelease: 2239 case QEvent::MouseButtonDblClick: 2240 case QEvent::MouseMove: 2241 case QEvent::Paint: 2242 #ifndef QT_NO_WHEELEVENT 2243 case QEvent::Wheel: 2244 #endif 2245 case QEvent::ContextMenu: 2246 case QEvent::DragEnter: 2247 case QEvent::DragMove: 2248 case QEvent::DragLeave: 2249 case QEvent::Drop: 2250 return QFrame::event(e); 2251 case QEvent::ChildPolished: { 2252 // we need to install an event filter on all children of the widget() to 2253 // be able to get correct stacking of children within the document. 2254 QObject *c = static_cast<QChildEvent *>(e)->child(); 2255 if (c->isWidgetType()) { 2256 QWidget *w = static_cast<QWidget *>(c); 2257 // don't install the event filter on toplevels 2258 if (!(w->windowFlags() & Qt::Window) && !(w->windowModality() & Qt::ApplicationModal)) { 2259 KHTMLWidget *k = dynamic_cast<KHTMLWidget *>(w); 2260 if (k && k->m_kwp->isRedirected()) { 2261 w->unsetCursor(); 2262 handleWidget(w, this); 2263 } 2264 } 2265 } 2266 break; 2267 } 2268 case QEvent::Move: { 2269 if (static_cast<QMoveEvent *>(e)->pos() != QPoint(0, 0)) { 2270 widget()->move(0, 0); 2271 updateScrollBars(); 2272 return true; 2273 } 2274 break; 2275 } 2276 default: 2277 break; 2278 } 2279 return false; 2280 } 2281 2282 bool KHTMLView::hasLayoutPending() 2283 { 2284 return d->layoutTimerId && !d->firstLayoutPending; 2285 } 2286 2287 DOM::NodeImpl *KHTMLView::nodeUnderMouse() const 2288 { 2289 return d->underMouse; 2290 } 2291 2292 DOM::NodeImpl *KHTMLView::nonSharedNodeUnderMouse() const 2293 { 2294 return d->underMouseNonShared; 2295 } 2296 2297 bool KHTMLView::scrollTo(const QRect &bounds) 2298 { 2299 d->scrollingSelf = true; // so scroll events get ignored 2300 2301 int x, y, xe, ye; 2302 x = bounds.left(); 2303 y = bounds.top(); 2304 xe = bounds.right(); 2305 ye = bounds.bottom(); 2306 2307 //qCDebug(KHTML_LOG)<<"scrolling coords: x="<<x<<" y="<<y<<" width="<<xe-x<<" height="<<ye-y; 2308 2309 int deltax; 2310 int deltay; 2311 2312 int curHeight = visibleHeight(); 2313 int curWidth = visibleWidth(); 2314 2315 if (ye - y > curHeight - d->borderY) { 2316 ye = y + curHeight - d->borderY; 2317 } 2318 2319 if (xe - x > curWidth - d->borderX) { 2320 xe = x + curWidth - d->borderX; 2321 } 2322 2323 // is xpos of target left of the view's border? 2324 if (x < contentsX() + d->borderX) { 2325 deltax = x - contentsX() - d->borderX; 2326 } 2327 // is xpos of target right of the view's right border? 2328 else if (xe + d->borderX > contentsX() + curWidth) { 2329 deltax = xe + d->borderX - (contentsX() + curWidth); 2330 } else { 2331 deltax = 0; 2332 } 2333 2334 // is ypos of target above upper border? 2335 if (y < contentsY() + d->borderY) { 2336 deltay = y - contentsY() - d->borderY; 2337 } 2338 // is ypos of target below lower border? 2339 else if (ye + d->borderY > contentsY() + curHeight) { 2340 deltay = ye + d->borderY - (contentsY() + curHeight); 2341 } else { 2342 deltay = 0; 2343 } 2344 2345 int maxx = curWidth - d->borderX; 2346 int maxy = curHeight - d->borderY; 2347 2348 int scrollX, scrollY; 2349 2350 scrollX = deltax > 0 ? (deltax > maxx ? maxx : deltax) : deltax == 0 ? 0 : (deltax > -maxx ? deltax : -maxx); 2351 scrollY = deltay > 0 ? (deltay > maxy ? maxy : deltay) : deltay == 0 ? 0 : (deltay > -maxy ? deltay : -maxy); 2352 2353 if (contentsX() + scrollX < 0) { 2354 scrollX = -contentsX(); 2355 } else if (contentsWidth() - visibleWidth() - contentsX() < scrollX) { 2356 scrollX = contentsWidth() - visibleWidth() - contentsX(); 2357 } 2358 2359 if (contentsY() + scrollY < 0) { 2360 scrollY = -contentsY(); 2361 } else if (contentsHeight() - visibleHeight() - contentsY() < scrollY) { 2362 scrollY = contentsHeight() - visibleHeight() - contentsY(); 2363 } 2364 2365 horizontalScrollBar()->setValue(horizontalScrollBar()->value() + scrollX); 2366 verticalScrollBar()->setValue(verticalScrollBar()->value() + scrollY); 2367 2368 d->scrollingSelf = false; 2369 2370 if ((abs(deltax) <= maxx) && (abs(deltay) <= maxy)) { 2371 return true; 2372 } else { 2373 return false; 2374 } 2375 2376 } 2377 2378 bool KHTMLView::focusNextPrevNode(bool next) 2379 { 2380 // Sets the focus node of the document to be the node after (or if 2381 // next is false, before) the current focus node. Only nodes that 2382 // are selectable (i.e. for which isFocusable() returns true) are 2383 // taken into account, and the order used is that specified in the 2384 // HTML spec (see DocumentImpl::nextFocusNode() and 2385 // DocumentImpl::previousFocusNode() for details). 2386 2387 DocumentImpl *doc = m_part->xmlDocImpl(); 2388 NodeImpl *oldFocusNode = doc->focusNode(); 2389 2390 // See whether we're in the middle of a detach, or hiding of the 2391 // widget. In this case, we will just clear focus, being careful not to emit events 2392 // or update rendering. Doing this also prevents the code below from going bonkers with 2393 // oldFocusNode not actually being focusable, etc. 2394 if (oldFocusNode) { 2395 if ((oldFocusNode->renderer() && !oldFocusNode->renderer()->parent()) 2396 || !oldFocusNode->isTabFocusable()) { 2397 doc->quietResetFocus(); 2398 return true; 2399 } 2400 } 2401 2402 #if 1 2403 // If the user has scrolled the document, then instead of picking 2404 // the next focusable node in the document, use the first one that 2405 // is within the visible area (if possible). 2406 if (d->scrollBarMoved) { 2407 NodeImpl *toFocus; 2408 if (next) { 2409 toFocus = doc->nextFocusNode(oldFocusNode); 2410 } else { 2411 toFocus = doc->previousFocusNode(oldFocusNode); 2412 } 2413 2414 if (!toFocus && oldFocusNode) { 2415 if (next) { 2416 toFocus = doc->nextFocusNode(nullptr); 2417 } else { 2418 toFocus = doc->previousFocusNode(nullptr); 2419 } 2420 } 2421 2422 while (toFocus && toFocus != oldFocusNode) { 2423 2424 QRect focusNodeRect = toFocus->getRect(); 2425 if ((focusNodeRect.left() > contentsX()) && (focusNodeRect.right() < contentsX() + visibleWidth()) && 2426 (focusNodeRect.top() > contentsY()) && (focusNodeRect.bottom() < contentsY() + visibleHeight())) { 2427 { 2428 QRect r = toFocus->getRect(); 2429 ensureVisible(r.right(), r.bottom()); 2430 ensureVisible(r.left(), r.top()); 2431 d->scrollBarMoved = false; 2432 d->tabMovePending = false; 2433 d->lastTabbingDirection = next; 2434 d->pseudoFocusNode = KHTMLViewPrivate::PFNone; 2435 m_part->xmlDocImpl()->setFocusNode(toFocus); 2436 Node guard(toFocus); 2437 if (!toFocus->hasOneRef()) { 2438 emit m_part->nodeActivated(Node(toFocus)); 2439 } 2440 return true; 2441 } 2442 } 2443 if (next) { 2444 toFocus = doc->nextFocusNode(toFocus); 2445 } else { 2446 toFocus = doc->previousFocusNode(toFocus); 2447 } 2448 2449 if (!toFocus && oldFocusNode) { 2450 if (next) { 2451 toFocus = doc->nextFocusNode(nullptr); 2452 } else { 2453 toFocus = doc->previousFocusNode(nullptr); 2454 } 2455 } 2456 } 2457 2458 d->scrollBarMoved = false; 2459 } 2460 #endif 2461 2462 if (!oldFocusNode && d->pseudoFocusNode == KHTMLViewPrivate::PFNone) { 2463 ensureVisible(contentsX(), next ? 0 : contentsHeight()); 2464 d->scrollBarMoved = false; 2465 d->pseudoFocusNode = next ? KHTMLViewPrivate::PFTop : KHTMLViewPrivate::PFBottom; 2466 return true; 2467 } 2468 2469 NodeImpl *newFocusNode = nullptr; 2470 2471 if (d->tabMovePending && next != d->lastTabbingDirection) { 2472 //qCDebug(KHTML_LOG) << " tab move pending and tabbing direction changed!\n"; 2473 newFocusNode = oldFocusNode; 2474 } else if (next) { 2475 if (oldFocusNode || d->pseudoFocusNode == KHTMLViewPrivate::PFTop) { 2476 newFocusNode = doc->nextFocusNode(oldFocusNode); 2477 } 2478 } else { 2479 if (oldFocusNode || d->pseudoFocusNode == KHTMLViewPrivate::PFBottom) { 2480 newFocusNode = doc->previousFocusNode(oldFocusNode); 2481 } 2482 } 2483 2484 bool targetVisible = false; 2485 if (!newFocusNode) { 2486 if (next) { 2487 targetVisible = scrollTo(QRect(contentsX() + visibleWidth() / 2, contentsHeight() - d->borderY, 0, 0)); 2488 } else { 2489 targetVisible = scrollTo(QRect(contentsX() + visibleWidth() / 2, d->borderY, 0, 0)); 2490 } 2491 } else { 2492 // if it's an editable element, activate the caret 2493 if (!m_part->isCaretMode() && newFocusNode->isContentEditable()) { 2494 // qCDebug(KHTML_LOG) << "show caret! fn: " << newFocusNode->nodeName().string(); 2495 m_part->clearCaretRectIfNeeded(); 2496 m_part->d->editor_context.m_selection.moveTo(Position(newFocusNode, 0L)); 2497 m_part->setCaretVisible(true); 2498 } else { 2499 m_part->setCaretVisible(false); 2500 // qCDebug(KHTML_LOG) << "hide caret! fn: " << newFocusNode->nodeName().string(); 2501 } 2502 m_part->notifySelectionChanged(); 2503 2504 targetVisible = scrollTo(newFocusNode->getRect()); 2505 } 2506 2507 if (targetVisible) { 2508 //qCDebug(KHTML_LOG) << " target reached.\n"; 2509 d->tabMovePending = false; 2510 2511 m_part->xmlDocImpl()->setFocusNode(newFocusNode); 2512 if (newFocusNode) { 2513 Node guard(newFocusNode); 2514 if (!newFocusNode->hasOneRef()) { 2515 emit m_part->nodeActivated(Node(newFocusNode)); 2516 } 2517 return true; 2518 } else { 2519 d->pseudoFocusNode = next ? KHTMLViewPrivate::PFBottom : KHTMLViewPrivate::PFTop; 2520 return false; 2521 } 2522 } else { 2523 if (!d->tabMovePending) { 2524 d->lastTabbingDirection = next; 2525 } 2526 d->tabMovePending = true; 2527 return true; 2528 } 2529 } 2530 2531 void KHTMLView::displayAccessKeys() 2532 { 2533 QVector< QChar > taken; 2534 displayAccessKeys(nullptr, this, taken, false); 2535 displayAccessKeys(nullptr, this, taken, true); 2536 } 2537 2538 void KHTMLView::displayAccessKeys(KHTMLView *caller, KHTMLView *origview, QVector< QChar > &taken, bool use_fallbacks) 2539 { 2540 QMap< ElementImpl *, QChar > fallbacks; 2541 if (use_fallbacks) { 2542 fallbacks = buildFallbackAccessKeys(); 2543 } 2544 for (NodeImpl *n = m_part->xmlDocImpl(); n != nullptr; n = n->traverseNextNode()) { 2545 if (n->isElementNode()) { 2546 ElementImpl *en = static_cast< ElementImpl * >(n); 2547 DOMString s = en->getAttribute(ATTR_ACCESSKEY); 2548 QString accesskey; 2549 if (s.length() == 1) { 2550 QChar a = s.string()[ 0 ].toUpper(); 2551 if (qFind(taken.begin(), taken.end(), a) == taken.end()) { // !contains 2552 accesskey = a; 2553 } 2554 } 2555 if (accesskey.isNull() && fallbacks.contains(en)) { 2556 QChar a = fallbacks[ en ].toUpper(); 2557 if (qFind(taken.begin(), taken.end(), a) == taken.end()) { // !contains 2558 accesskey = QString("<qt><i>") + a + "</i></qt>"; 2559 } 2560 } 2561 if (!accesskey.isNull()) { 2562 QRect rec = en->getRect(); 2563 QLabel *lab = new QLabel(accesskey, widget()); 2564 lab->setAttribute(Qt::WA_DeleteOnClose); 2565 lab->setObjectName("KHTMLAccessKey"); 2566 connect(origview, SIGNAL(hideAccessKeys()), lab, SLOT(close())); 2567 connect(this, SIGNAL(repaintAccessKeys()), lab, SLOT(repaint())); 2568 lab->setPalette(QToolTip::palette()); 2569 lab->setLineWidth(2); 2570 lab->setFrameStyle(QFrame::Box | QFrame::Plain); 2571 lab->setContentsMargins(3, 3, 3, 3); 2572 lab->adjustSize(); 2573 lab->setParent(widget()); 2574 lab->setAutoFillBackground(true); 2575 lab->move( 2576 qMin(rec.left() + rec.width() / 2 - contentsX(), contentsWidth() - lab->width()), 2577 qMin(rec.top() + rec.height() / 2 - contentsY(), contentsHeight() - lab->height())); 2578 lab->show(); 2579 taken.append(accesskey[ 0 ]); 2580 } 2581 } 2582 } 2583 if (use_fallbacks) { 2584 return; 2585 } 2586 2587 QList<KParts::ReadOnlyPart *> frames = m_part->frames(); 2588 foreach (KParts::ReadOnlyPart *cur, frames) { 2589 if (!qobject_cast<KHTMLPart *>(cur)) { 2590 continue; 2591 } 2592 KHTMLPart *part = static_cast< KHTMLPart * >(cur); 2593 if (part->view() && part->view() != caller) { 2594 part->view()->displayAccessKeys(this, origview, taken, use_fallbacks); 2595 } 2596 } 2597 2598 // pass up to the parent 2599 if (m_part->parentPart() && m_part->parentPart()->view() 2600 && m_part->parentPart()->view() != caller) { 2601 m_part->parentPart()->view()->displayAccessKeys(this, origview, taken, use_fallbacks); 2602 } 2603 } 2604 2605 bool KHTMLView::isScrollingFromMouseWheel() const 2606 { 2607 return d->scrollingFromWheel != QPoint(-1, -1); 2608 } 2609 2610 void KHTMLView::accessKeysTimeout() 2611 { 2612 d->accessKeysActivated = false; 2613 d->accessKeysPreActivate = false; 2614 m_part->setStatusBarText(QString(), KHTMLPart::BarOverrideText); 2615 emit hideAccessKeys(); 2616 } 2617 2618 // Handling of the HTML accesskey attribute. 2619 bool KHTMLView::handleAccessKey(const QKeyEvent *ev) 2620 { 2621 // Qt interprets the keyevent also with the modifiers, and ev->text() matches that, 2622 // but this code must act as if the modifiers weren't pressed 2623 QChar c; 2624 if (ev->key() >= Qt::Key_A && ev->key() <= Qt::Key_Z) { 2625 c = 'A' + ev->key() - Qt::Key_A; 2626 } else if (ev->key() >= Qt::Key_0 && ev->key() <= Qt::Key_9) { 2627 c = '0' + ev->key() - Qt::Key_0; 2628 } else { 2629 // TODO fake XKeyEvent and XLookupString ? 2630 // This below seems to work e.g. for eacute though. 2631 if (ev->text().length() == 1) { 2632 c = ev->text()[ 0 ]; 2633 } 2634 } 2635 if (c.isNull()) { 2636 return false; 2637 } 2638 return focusNodeWithAccessKey(c); 2639 } 2640 2641 bool KHTMLView::focusNodeWithAccessKey(QChar c, KHTMLView *caller) 2642 { 2643 DocumentImpl *doc = m_part->xmlDocImpl(); 2644 if (!doc) { 2645 return false; 2646 } 2647 ElementImpl *node = doc->findAccessKeyElement(c); 2648 if (!node) { 2649 QList<KParts::ReadOnlyPart *> frames = m_part->frames(); 2650 foreach (KParts::ReadOnlyPart *cur, frames) { 2651 if (!qobject_cast<KHTMLPart *>(cur)) { 2652 continue; 2653 } 2654 KHTMLPart *part = static_cast< KHTMLPart * >(cur); 2655 if (part->view() && part->view() != caller 2656 && part->view()->focusNodeWithAccessKey(c, this)) { 2657 return true; 2658 } 2659 } 2660 // pass up to the parent 2661 if (m_part->parentPart() && m_part->parentPart()->view() 2662 && m_part->parentPart()->view() != caller 2663 && m_part->parentPart()->view()->focusNodeWithAccessKey(c, this)) { 2664 return true; 2665 } 2666 if (caller == nullptr) { // the active frame (where the accesskey was pressed) 2667 const QMap< ElementImpl *, QChar > fallbacks = buildFallbackAccessKeys(); 2668 for (QMap< ElementImpl *, QChar >::ConstIterator it = fallbacks.begin(); 2669 it != fallbacks.end(); 2670 ++it) 2671 if (*it == c) { 2672 node = it.key(); 2673 break; 2674 } 2675 } 2676 if (node == nullptr) { 2677 return false; 2678 } 2679 } 2680 2681 // Scroll the view as necessary to ensure that the new focus node is visible 2682 2683 QRect r = node->getRect(); 2684 ensureVisible(r.right(), r.bottom()); 2685 ensureVisible(r.left(), r.top()); 2686 2687 Node guard(node); 2688 if (node->isFocusable()) { 2689 if (node->id() == ID_LABEL) { 2690 // if Accesskey is a label, give focus to the label's referrer. 2691 node = static_cast<ElementImpl *>(static_cast< HTMLLabelElementImpl * >(node)->getFormElement()); 2692 if (!node) { 2693 return true; 2694 } 2695 guard = node; 2696 } 2697 // Set focus node on the document 2698 m_part->xmlDocImpl()->setFocusNode(node); 2699 2700 if (node != nullptr && node->hasOneRef()) { // deleted, only held by guard 2701 return true; 2702 } 2703 emit m_part->nodeActivated(Node(node)); 2704 if (node != nullptr && node->hasOneRef()) { 2705 return true; 2706 } 2707 } 2708 2709 switch (node->id()) { 2710 case ID_A: 2711 static_cast< HTMLAnchorElementImpl * >(node)->click(); 2712 break; 2713 case ID_INPUT: 2714 static_cast< HTMLInputElementImpl * >(node)->click(); 2715 break; 2716 case ID_BUTTON: 2717 static_cast< HTMLButtonElementImpl * >(node)->click(); 2718 break; 2719 case ID_AREA: 2720 static_cast< HTMLAreaElementImpl * >(node)->click(); 2721 break; 2722 case ID_TEXTAREA: 2723 break; // just focusing it is enough 2724 case ID_LEGEND: 2725 // TODO 2726 break; 2727 } 2728 return true; 2729 } 2730 2731 static QString getElementText(NodeImpl *start, bool after) 2732 { 2733 QString ret; // nextSibling(), to go after e.g. </select> 2734 for (NodeImpl *n = after ? start->nextSibling() : start->traversePreviousNode(); 2735 n != nullptr; 2736 n = after ? n->traverseNextNode() : n->traversePreviousNode()) { 2737 if (n->isTextNode()) { 2738 if (after) { 2739 ret += static_cast< TextImpl * >(n)->toString().string(); 2740 } else { 2741 ret.prepend(static_cast< TextImpl * >(n)->toString().string()); 2742 } 2743 } else { 2744 switch (n->id()) { 2745 case ID_A: 2746 case ID_FONT: 2747 case ID_TT: 2748 case ID_U: 2749 case ID_B: 2750 case ID_I: 2751 case ID_S: 2752 case ID_STRIKE: 2753 case ID_BIG: 2754 case ID_SMALL: 2755 case ID_EM: 2756 case ID_STRONG: 2757 case ID_DFN: 2758 case ID_CODE: 2759 case ID_SAMP: 2760 case ID_KBD: 2761 case ID_VAR: 2762 case ID_CITE: 2763 case ID_ABBR: 2764 case ID_ACRONYM: 2765 case ID_SUB: 2766 case ID_SUP: 2767 case ID_SPAN: 2768 case ID_NOBR: 2769 case ID_WBR: 2770 break; 2771 case ID_TD: 2772 if (ret.trimmed().isEmpty()) { 2773 break; 2774 } 2775 // fall through 2776 default: 2777 return ret.simplified(); 2778 } 2779 } 2780 } 2781 return ret.simplified(); 2782 } 2783 2784 static QMap< NodeImpl *, QString > buildLabels(NodeImpl *start) 2785 { 2786 QMap< NodeImpl *, QString > ret; 2787 for (NodeImpl *n = start; 2788 n != nullptr; 2789 n = n->traverseNextNode()) { 2790 if (n->id() == ID_LABEL) { 2791 HTMLLabelElementImpl *label = static_cast< HTMLLabelElementImpl * >(n); 2792 NodeImpl *labelfor = label->getFormElement(); 2793 if (labelfor) { 2794 ret[ labelfor ] = label->innerText().string().simplified(); 2795 } 2796 } 2797 } 2798 return ret; 2799 } 2800 2801 namespace khtml 2802 { 2803 struct AccessKeyData { 2804 ElementImpl *element; 2805 QString text; 2806 QString url; 2807 int priority; // 10(highest) - 0(lowest) 2808 }; 2809 } 2810 2811 QMap< ElementImpl *, QChar > KHTMLView::buildFallbackAccessKeys() const 2812 { 2813 // build a list of all possible candidate elements that could use an accesskey 2814 QLinkedList< AccessKeyData > data; // Note: this has to be a list type that keep iterators valid 2815 // when other entries are removed 2816 QMap< NodeImpl *, QString > labels = buildLabels(m_part->xmlDocImpl()); 2817 QMap< QString, QChar > hrefs; 2818 2819 for (NodeImpl *n = m_part->xmlDocImpl(); 2820 n != nullptr; 2821 n = n->traverseNextNode()) { 2822 if (n->isElementNode()) { 2823 ElementImpl *element = static_cast< ElementImpl * >(n); 2824 if (element->renderer() == nullptr) { 2825 continue; // not visible 2826 } 2827 QString text; 2828 QString url; 2829 int priority = 0; 2830 bool ignore = false; 2831 bool text_after = false; 2832 bool text_before = false; 2833 switch (element->id()) { 2834 case ID_A: 2835 url = element->getAttribute(ATTR_HREF).trimSpaces().string(); 2836 if (url.isEmpty()) { // doesn't have href, it's only an anchor 2837 continue; 2838 } 2839 text = static_cast< HTMLElementImpl * >(element)->innerText().string().simplified(); 2840 priority = 2; 2841 break; 2842 case ID_INPUT: { 2843 HTMLInputElementImpl *in = static_cast< HTMLInputElementImpl * >(element); 2844 switch (in->inputType()) { 2845 case HTMLInputElementImpl::SUBMIT: 2846 text = in->value().string(); 2847 if (text.isEmpty()) { 2848 text = i18n("Submit"); 2849 } 2850 priority = 7; 2851 break; 2852 case HTMLInputElementImpl::IMAGE: 2853 text = in->altText().string(); 2854 priority = 7; 2855 break; 2856 case HTMLInputElementImpl::BUTTON: 2857 text = in->value().string(); 2858 priority = 5; 2859 break; 2860 case HTMLInputElementImpl::RESET: 2861 text = in->value().string(); 2862 if (text.isEmpty()) { 2863 text = i18n("Reset"); 2864 } 2865 priority = 5; 2866 break; 2867 case HTMLInputElementImpl::HIDDEN: 2868 ignore = true; 2869 break; 2870 case HTMLInputElementImpl::CHECKBOX: 2871 case HTMLInputElementImpl::RADIO: 2872 text_after = true; 2873 priority = 5; 2874 break; 2875 case HTMLInputElementImpl::TEXT: 2876 case HTMLInputElementImpl::PASSWORD: 2877 case HTMLInputElementImpl::FILE: 2878 text_before = true; 2879 priority = 5; 2880 break; 2881 default: 2882 priority = 5; 2883 break; 2884 } 2885 break; 2886 } 2887 case ID_BUTTON: 2888 text = static_cast< HTMLElementImpl * >(element)->innerText().string().simplified(); 2889 switch (static_cast< HTMLButtonElementImpl * >(element)->buttonType()) { 2890 case HTMLButtonElementImpl::SUBMIT: 2891 if (text.isEmpty()) { 2892 text = i18n("Submit"); 2893 } 2894 priority = 7; 2895 break; 2896 case HTMLButtonElementImpl::RESET: 2897 if (text.isEmpty()) { 2898 text = i18n("Reset"); 2899 } 2900 priority = 5; 2901 break; 2902 default: 2903 priority = 5; 2904 break; 2905 } 2906 break; 2907 case ID_SELECT: // these don't have accesskey attribute, but quick access may be handy 2908 text_before = true; 2909 text_after = true; 2910 priority = 5; 2911 break; 2912 case ID_FRAME: 2913 ignore = true; 2914 break; 2915 default: 2916 ignore = !element->isFocusable(); 2917 priority = 2; 2918 break; 2919 } 2920 if (ignore) { 2921 continue; 2922 } 2923 2924 // build map of manually assigned accesskeys and their targets 2925 DOMString akey = element->getAttribute(ATTR_ACCESSKEY); 2926 if (akey.length() == 1) { 2927 hrefs[url] = akey.string()[ 0 ].toUpper(); 2928 continue; // has accesskey set, ignore 2929 } 2930 if (text.isNull() && labels.contains(element)) { 2931 text = labels[ element ]; 2932 } 2933 if (text.isNull() && text_before) { 2934 text = getElementText(element, false); 2935 } 2936 if (text.isNull() && text_after) { 2937 text = getElementText(element, true); 2938 } 2939 text = text.trimmed(); 2940 // increase priority of items which have explicitly specified accesskeys in the config 2941 const QList< QPair< QString, QChar > > priorities 2942 = m_part->settings()->fallbackAccessKeysAssignments(); 2943 for (QList< QPair< QString, QChar > >::ConstIterator it = priorities.begin(); 2944 it != priorities.end(); 2945 ++it) { 2946 if (text == (*it).first) { 2947 priority = 10; 2948 } 2949 } 2950 AccessKeyData tmp = { element, text, url, priority }; 2951 data.append(tmp); 2952 } 2953 } 2954 2955 QList< QChar > keys; 2956 for (char c = 'A'; c <= 'Z'; ++c) { 2957 keys << c; 2958 } 2959 for (char c = '0'; c <= '9'; ++c) { 2960 keys << c; 2961 } 2962 for (NodeImpl *n = m_part->xmlDocImpl(); 2963 n != nullptr; 2964 n = n->traverseNextNode()) { 2965 if (n->isElementNode()) { 2966 ElementImpl *en = static_cast< ElementImpl * >(n); 2967 DOMString s = en->getAttribute(ATTR_ACCESSKEY); 2968 if (s.length() == 1) { 2969 QChar c = s.string()[ 0 ].toUpper(); 2970 keys.removeAll(c); // remove manually assigned accesskeys 2971 } 2972 } 2973 } 2974 2975 QMap< ElementImpl *, QChar > ret; 2976 for (int priority = 10; priority >= 0; --priority) { 2977 for (QLinkedList< AccessKeyData >::Iterator it = data.begin(); 2978 it != data.end(); 2979 ) { 2980 if ((*it).priority != priority) { 2981 ++it; 2982 continue; 2983 } 2984 if (keys.isEmpty()) { 2985 break; 2986 } 2987 QString text = (*it).text; 2988 QChar key; 2989 const QString url = (*it).url; 2990 // an identical link already has an accesskey assigned 2991 if (hrefs.contains(url)) { 2992 it = data.erase(it); 2993 continue; 2994 } 2995 if (!text.isEmpty()) { 2996 const QList< QPair< QString, QChar > > priorities 2997 = m_part->settings()->fallbackAccessKeysAssignments(); 2998 for (QList< QPair< QString, QChar > >::ConstIterator it = priorities.begin(); 2999 it != priorities.end(); 3000 ++it) 3001 if (text == (*it).first && keys.contains((*it).second)) { 3002 key = (*it).second; 3003 break; 3004 } 3005 } 3006 // try first to select the first character as the accesskey, 3007 // then first character of the following words, 3008 // and then simply the first free character 3009 if (key.isNull() && !text.isEmpty()) { 3010 const QStringList words = text.split(' '); 3011 for (QStringList::ConstIterator it = words.begin(); 3012 it != words.end(); 3013 ++it) { 3014 if (keys.contains((*it)[ 0 ].toUpper())) { 3015 key = (*it)[ 0 ].toUpper(); 3016 break; 3017 } 3018 } 3019 } 3020 if (key.isNull() && !text.isEmpty()) { 3021 for (int i = 0; i < text.length(); ++i) { 3022 if (keys.contains(text[ i ].toUpper())) { 3023 key = text[ i ].toUpper(); 3024 break; 3025 } 3026 } 3027 } 3028 if (key.isNull()) { 3029 key = keys.front(); 3030 } 3031 ret[(*it).element ] = key; 3032 keys.removeAll(key); 3033 it = data.erase(it); 3034 // assign the same accesskey also to other elements pointing to the same url 3035 if (!url.isEmpty() && !url.startsWith("javascript:", Qt::CaseInsensitive)) { 3036 for (QLinkedList< AccessKeyData >::Iterator it2 = data.begin(); 3037 it2 != data.end(); 3038 ) { 3039 if ((*it2).url == url) { 3040 ret[(*it2).element ] = key; 3041 if (it == it2) { 3042 ++it; 3043 } 3044 it2 = data.erase(it2); 3045 } else { 3046 ++it2; 3047 } 3048 } 3049 } 3050 } 3051 } 3052 return ret; 3053 } 3054 3055 void KHTMLView::setMediaType(const QString &medium) 3056 { 3057 m_medium = medium; 3058 } 3059 3060 QString KHTMLView::mediaType() const 3061 { 3062 return m_medium; 3063 } 3064 3065 bool KHTMLView::pagedMode() const 3066 { 3067 return d->paged; 3068 } 3069 3070 void KHTMLView::setWidgetVisible(RenderWidget *w, bool vis) 3071 { 3072 if (vis) { 3073 d->visibleWidgets.insert(w, w->widget()); 3074 } else { 3075 d->visibleWidgets.remove(w); 3076 } 3077 } 3078 3079 bool KHTMLView::needsFullRepaint() const 3080 { 3081 return d->needsFullRepaint; 3082 } 3083 3084 namespace 3085 { 3086 class QPointerDeleter 3087 { 3088 public: 3089 explicit QPointerDeleter(QObject *o) : obj(o) {} 3090 ~QPointerDeleter() 3091 { 3092 delete obj; 3093 } 3094 private: 3095 const QPointer<QObject> obj; 3096 }; 3097 } 3098 3099 void KHTMLView::print(bool quick) 3100 { 3101 QPrinter printer; 3102 print(&printer, quick); 3103 } 3104 3105 void KHTMLView::print(QPrinter *_printer, bool quick) 3106 { 3107 if (!m_part->xmlDocImpl()) { 3108 return; 3109 } 3110 khtml::RenderCanvas *root = static_cast<khtml::RenderCanvas *>(m_part->xmlDocImpl()->renderer()); 3111 if (!root) { 3112 return; 3113 } 3114 3115 QPrinter &printer = *_printer; 3116 QPointer<QPrintDialog> dialog(new QPrintDialog(&printer, this)); 3117 QPointer<KHTMLPrintSettings> printSettings(new KHTMLPrintSettings(dialog)); //XXX: doesn't save settings between prints like this 3118 dialog->setOptionTabs(QList<QWidget *>() << printSettings.data()); 3119 3120 const QPointerDeleter dialogDeleter(dialog); 3121 3122 QString docname = m_part->xmlDocImpl()->URL().toDisplayString(); 3123 if (!docname.isEmpty()) { 3124 docname = KStringHandler::csqueeze(docname, 80); 3125 } 3126 3127 if (quick || (dialog->exec() && dialog)) { /*'this' and thus dialog might have been deleted while exec()!*/ 3128 viewport()->setCursor(Qt::WaitCursor); // only viewport(), no QApplication::, otherwise we get the busy cursor in kdeprint's dialogs 3129 // set up KPrinter 3130 printer.setFullPage(false); 3131 printer.setCreator(QString("KDE %1.%2.%3 HTML Library").arg(KHTML_VERSION_MAJOR).arg(KHTML_VERSION_MINOR).arg(KHTML_VERSION_PATCH)); 3132 printer.setDocName(docname); 3133 3134 QPainter *p = new QPainter; 3135 p->begin(&printer); 3136 khtml::setPrintPainter(p); 3137 3138 m_part->xmlDocImpl()->setPaintDevice(&printer); 3139 QString oldMediaType = mediaType(); 3140 setMediaType("print"); 3141 // We ignore margin settings for html and body when printing 3142 // and use the default margins from the print-system 3143 // (In Qt 3.0.x the default margins are hardcoded in Qt) 3144 m_part->xmlDocImpl()->setPrintStyleSheet(printSettings->printFriendly() ? 3145 "* { background-image: none !important;" 3146 " background-color: white !important;" 3147 " color: black !important; }" 3148 "body { margin: 0px !important; }" 3149 "html { margin: 0px !important; }" : 3150 "body { margin: 0px !important; }" 3151 "html { margin: 0px !important; }" 3152 ); 3153 3154 // qCDebug(KHTML_LOG) << "printing: physical page width = " << printer.width() 3155 // << " height = " << printer.height(); 3156 root->setStaticMode(true); 3157 root->setPagedMode(true); 3158 root->setWidth(printer.width()); 3159 // root->setHeight(printer.height()); 3160 root->setPageTop(0); 3161 root->setPageBottom(0); 3162 d->paged = true; 3163 3164 m_part->xmlDocImpl()->styleSelector()->computeFontSizes(printer.logicalDpiY(), 100); 3165 m_part->xmlDocImpl()->updateStyleSelector(); 3166 root->setPrintImages(printSettings->printImages()); 3167 root->makePageBreakAvoidBlocks(); 3168 3169 root->setNeedsLayoutAndMinMaxRecalc(); 3170 root->layout(); 3171 3172 // check sizes ask for action.. (scale or clip) 3173 3174 bool printHeader = printSettings->printHeader(); 3175 3176 int headerHeight = 0; 3177 QFont headerFont("Sans Serif", 8); 3178 3179 QString headerLeft = QDate::currentDate().toString(Qt::DefaultLocaleShortDate); 3180 QString headerMid = docname; 3181 QString headerRight; 3182 3183 if (printHeader) { 3184 p->setFont(headerFont); 3185 headerHeight = (p->fontMetrics().lineSpacing() * 3) / 2; 3186 } 3187 3188 // ok. now print the pages. 3189 // qCDebug(KHTML_LOG) << "printing: html page width = " << root->docWidth() 3190 // << " height = " << root->docHeight(); 3191 // qCDebug(KHTML_LOG) << "printing: margins left = " << printer.pageRect().left() - printer.paperRect().left() 3192 // << " top = " << printer.pageRect().top() - printer.paperRect().top(); 3193 // qCDebug(KHTML_LOG) << "printing: paper width = " << printer.width() 3194 // << " height = " << printer.height(); 3195 // if the width is too large to fit on the paper we just scale 3196 // the whole thing. 3197 int pageWidth = printer.width(); 3198 int pageHeight = printer.height(); 3199 p->setClipRect(0, 0, pageWidth, pageHeight); 3200 3201 pageHeight -= headerHeight; 3202 3203 #ifndef QT_NO_TRANSFORMATIONS 3204 bool scalePage = false; 3205 double scale = 0.0; 3206 if (root->docWidth() > printer.width()) { 3207 scalePage = true; 3208 scale = ((double) printer.width()) / ((double) root->docWidth()); 3209 pageHeight = (int)(pageHeight / scale); 3210 pageWidth = (int)(pageWidth / scale); 3211 headerHeight = (int)(headerHeight / scale); 3212 } 3213 #endif 3214 // qCDebug(KHTML_LOG) << "printing: scaled html width = " << pageWidth 3215 // << " height = " << pageHeight; 3216 3217 root->setHeight(pageHeight); 3218 root->setPageBottom(pageHeight); 3219 root->setNeedsLayout(true); 3220 root->layoutIfNeeded(); 3221 // m_part->slotDebugRenderTree(); 3222 3223 // Squeeze header to make it it on the page. 3224 if (printHeader) { 3225 int available_width = printer.width() - 10 - 3226 2 * qMax(p->boundingRect(0, 0, printer.width(), p->fontMetrics().lineSpacing(), Qt::AlignLeft, headerLeft).width(), 3227 p->boundingRect(0, 0, printer.width(), p->fontMetrics().lineSpacing(), Qt::AlignLeft, headerRight).width()); 3228 if (available_width < 150) { 3229 available_width = 150; 3230 } 3231 int mid_width; 3232 int squeeze = 120; 3233 do { 3234 headerMid = KStringHandler::csqueeze(docname, squeeze); 3235 mid_width = p->boundingRect(0, 0, printer.width(), p->fontMetrics().lineSpacing(), Qt::AlignLeft, headerMid).width(); 3236 squeeze -= 10; 3237 } while (mid_width > available_width); 3238 } 3239 3240 int top = 0; 3241 int bottom = 0; 3242 int page = 1; 3243 while (top < root->docHeight()) { 3244 if (top > 0) { 3245 printer.newPage(); 3246 } 3247 #ifndef QT_NO_TRANSFORMATIONS 3248 if (scalePage) { 3249 p->scale(scale, scale); 3250 } 3251 #endif 3252 p->save(); 3253 p->setClipRect(0, 0, pageWidth, headerHeight); 3254 if (printHeader) { 3255 int dy = p->fontMetrics().lineSpacing(); 3256 p->setPen(Qt::black); 3257 p->setFont(headerFont); 3258 3259 headerRight = QString("#%1").arg(page); 3260 3261 p->drawText(0, 0, printer.width(), dy, Qt::AlignLeft, headerLeft); 3262 p->drawText(0, 0, printer.width(), dy, Qt::AlignHCenter, headerMid); 3263 p->drawText(0, 0, printer.width(), dy, Qt::AlignRight, headerRight); 3264 } 3265 3266 p->restore(); 3267 p->translate(0, headerHeight - top); 3268 3269 bottom = top + pageHeight; 3270 3271 root->setPageTop(top); 3272 root->setPageBottom(bottom); 3273 root->setPageNumber(page); 3274 3275 root->layer()->paint(p, QRect(0, top, pageWidth, pageHeight)); 3276 // qCDebug(KHTML_LOG) << "printed: page " << page <<" bottom At = " << bottom; 3277 3278 top = bottom; 3279 p->resetTransform(); 3280 page++; 3281 } 3282 3283 p->end(); 3284 delete p; 3285 3286 // and now reset the layout to the usual one... 3287 root->setPagedMode(false); 3288 root->setStaticMode(false); 3289 d->paged = false; 3290 khtml::setPrintPainter(nullptr); 3291 setMediaType(oldMediaType); 3292 m_part->xmlDocImpl()->setPaintDevice(this); 3293 m_part->xmlDocImpl()->styleSelector()->computeFontSizes(m_part->xmlDocImpl()->logicalDpiY(), m_part->fontScaleFactor()); 3294 m_part->xmlDocImpl()->updateStyleSelector(); 3295 viewport()->unsetCursor(); 3296 } 3297 } 3298 3299 void KHTMLView::slotPaletteChanged() 3300 { 3301 if (!m_part->xmlDocImpl()) { 3302 return; 3303 } 3304 DOM::DocumentImpl *document = m_part->xmlDocImpl(); 3305 if (!document->isHTMLDocument()) { 3306 return; 3307 } 3308 khtml::RenderCanvas *root = static_cast<khtml::RenderCanvas *>(document->renderer()); 3309 if (!root) { 3310 return; 3311 } 3312 root->style()->resetPalette(); 3313 NodeImpl *body = static_cast<HTMLDocumentImpl *>(document)->body(); 3314 if (!body) { 3315 return; 3316 } 3317 body->setChanged(true); 3318 body->recalcStyle(NodeImpl::Force); 3319 } 3320 3321 void KHTMLView::paint(QPainter *p, const QRect &rc, int yOff, bool *more) 3322 { 3323 if (!m_part->xmlDocImpl()) { 3324 return; 3325 } 3326 khtml::RenderCanvas *root = static_cast<khtml::RenderCanvas *>(m_part->xmlDocImpl()->renderer()); 3327 if (!root) { 3328 return; 3329 } 3330 #ifdef SPEED_DEBUG 3331 d->firstRepaintPending = false; 3332 #endif 3333 3334 QPaintDevice *opd = m_part->xmlDocImpl()->paintDevice(); 3335 m_part->xmlDocImpl()->setPaintDevice(p->device()); 3336 root->setPagedMode(true); 3337 root->setStaticMode(true); 3338 root->setWidth(rc.width()); 3339 3340 // save() 3341 QRegion creg = p->clipRegion(); 3342 QTransform t = p->worldTransform(); 3343 QRect w = p->window(); 3344 QRect v = p->viewport(); 3345 bool vte = p->viewTransformEnabled(); 3346 bool wme = p->worldMatrixEnabled(); 3347 3348 p->setClipRect(rc); 3349 p->translate(rc.left(), rc.top()); 3350 double scale = ((double) rc.width() / (double) root->docWidth()); 3351 int height = (int)((double) rc.height() / scale); 3352 #ifndef QT_NO_TRANSFORMATIONS 3353 p->scale(scale, scale); 3354 #endif 3355 root->setPageTop(yOff); 3356 root->setPageBottom(yOff + height); 3357 3358 root->layer()->paint(p, QRect(0, yOff, root->docWidth(), height)); 3359 if (more) { 3360 *more = yOff + height < root->docHeight(); 3361 } 3362 3363 // restore() 3364 p->setWorldTransform(t); 3365 p->setWindow(w); 3366 p->setViewport(v); 3367 p->setViewTransformEnabled(vte); 3368 p->setWorldMatrixEnabled(wme); 3369 if (!creg.isEmpty()) { 3370 p->setClipRegion(creg); 3371 } else { 3372 p->setClipRegion(QRegion(), Qt::NoClip); 3373 } 3374 3375 root->setPagedMode(false); 3376 root->setStaticMode(false); 3377 m_part->xmlDocImpl()->setPaintDevice(opd); 3378 } 3379 3380 void KHTMLView::render(QPainter *p, const QRect &r, const QPoint &off) 3381 { 3382 #ifdef SPEED_DEBUG 3383 d->firstRepaintPending = false; 3384 #endif 3385 QRect clip(off.x() + r.x(), off.y() + r.y(), r.width(), r.height()); 3386 if (!m_part || !m_part->xmlDocImpl() || !m_part->xmlDocImpl()->renderer()) { 3387 p->fillRect(clip, palette().brush(QPalette::Active, QPalette::Base)); 3388 return; 3389 } 3390 QPaintDevice *opd = m_part->xmlDocImpl()->paintDevice(); 3391 m_part->xmlDocImpl()->setPaintDevice(p->device()); 3392 3393 // save() 3394 QRegion creg = p->clipRegion(); 3395 QTransform t = p->worldTransform(); 3396 QRect w = p->window(); 3397 QRect v = p->viewport(); 3398 bool vte = p->viewTransformEnabled(); 3399 bool wme = p->worldMatrixEnabled(); 3400 3401 p->setClipRect(clip); 3402 QRect rect = r.translated(contentsX(), contentsY()); 3403 p->translate(off.x() - contentsX(), off.y() - contentsY()); 3404 3405 m_part->xmlDocImpl()->renderer()->layer()->paint(p, rect); 3406 3407 // restore() 3408 p->setWorldTransform(t); 3409 p->setWindow(w); 3410 p->setViewport(v); 3411 p->setViewTransformEnabled(vte); 3412 p->setWorldMatrixEnabled(wme); 3413 if (!creg.isEmpty()) { 3414 p->setClipRegion(creg); 3415 } else { 3416 p->setClipRegion(QRegion(), Qt::NoClip); 3417 } 3418 3419 m_part->xmlDocImpl()->setPaintDevice(opd); 3420 } 3421 3422 void KHTMLView::setHasStaticBackground(bool partial) 3423 { 3424 // full static iframe is irreversible for now 3425 if (d->staticWidget == KHTMLViewPrivate::SBFull && m_kwp->isRedirected()) { 3426 return; 3427 } 3428 3429 d->staticWidget = partial ? 3430 KHTMLViewPrivate::SBPartial : KHTMLViewPrivate::SBFull; 3431 } 3432 3433 void KHTMLView::setHasNormalBackground() 3434 { 3435 // full static iframe is irreversible for now 3436 if (d->staticWidget == KHTMLViewPrivate::SBFull && m_kwp->isRedirected()) { 3437 return; 3438 } 3439 3440 d->staticWidget = KHTMLViewPrivate::SBNone; 3441 } 3442 3443 void KHTMLView::addStaticObject(bool fixed) 3444 { 3445 if (fixed) { 3446 d->fixedObjectsCount++; 3447 } else { 3448 d->staticObjectsCount++; 3449 } 3450 3451 setHasStaticBackground(true /*partial*/); 3452 } 3453 3454 void KHTMLView::removeStaticObject(bool fixed) 3455 { 3456 if (fixed) { 3457 d->fixedObjectsCount--; 3458 } else { 3459 d->staticObjectsCount--; 3460 } 3461 3462 assert(d->fixedObjectsCount >= 0 && d->staticObjectsCount >= 0); 3463 3464 if (!d->staticObjectsCount && !d->fixedObjectsCount) { 3465 setHasNormalBackground(); 3466 } else { 3467 setHasStaticBackground(true /*partial*/); 3468 } 3469 } 3470 3471 void KHTMLView::setVerticalScrollBarPolicy(Qt::ScrollBarPolicy policy) 3472 { 3473 #ifndef KHTML_NO_SCROLLBARS 3474 d->vpolicy = policy; 3475 QScrollArea::setVerticalScrollBarPolicy(policy); 3476 #else 3477 Q_UNUSED(policy); 3478 #endif 3479 } 3480 3481 void KHTMLView::setHorizontalScrollBarPolicy(Qt::ScrollBarPolicy policy) 3482 { 3483 #ifndef KHTML_NO_SCROLLBARS 3484 d->hpolicy = policy; 3485 QScrollArea::setHorizontalScrollBarPolicy(policy); 3486 #else 3487 Q_UNUSED(policy); 3488 #endif 3489 } 3490 3491 void KHTMLView::restoreScrollBar() 3492 { 3493 int ow = visibleWidth(); 3494 QScrollArea::setVerticalScrollBarPolicy(d->vpolicy); 3495 if (visibleWidth() != ow) { 3496 layout(); 3497 } 3498 d->prevScrollbarVisible = verticalScrollBar()->isVisible(); 3499 } 3500 3501 QStringList KHTMLView::formCompletionItems(const QString &name) const 3502 { 3503 if (!m_part->settings()->isFormCompletionEnabled()) { 3504 return QStringList(); 3505 } 3506 if (!d->formCompletions) { 3507 d->formCompletions = new KConfig(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1Char('/') + "khtml/formcompletions"); 3508 } 3509 return d->formCompletions->group("").readEntry(name, QStringList()); 3510 } 3511 3512 void KHTMLView::clearCompletionHistory(const QString &name) 3513 { 3514 if (!d->formCompletions) { 3515 d->formCompletions = new KConfig(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1Char('/') + "khtml/formcompletions"); 3516 } 3517 d->formCompletions->group("").writeEntry(name, ""); 3518 d->formCompletions->sync(); 3519 } 3520 3521 void KHTMLView::addFormCompletionItem(const QString &name, const QString &value) 3522 { 3523 if (!m_part->settings()->isFormCompletionEnabled()) { 3524 return; 3525 } 3526 // don't store values that are all numbers or just numbers with 3527 // dashes or spaces as those are likely credit card numbers or 3528 // something similar 3529 bool cc_number(true); 3530 for (int i = 0; i < value.length(); ++i) { 3531 QChar c(value[i]); 3532 if (!c.isNumber() && c != '-' && !c.isSpace()) { 3533 cc_number = false; 3534 break; 3535 } 3536 } 3537 if (cc_number) { 3538 return; 3539 } 3540 QStringList items = formCompletionItems(name); 3541 if (!items.contains(value)) { 3542 items.prepend(value); 3543 } 3544 while ((int)items.count() > m_part->settings()->maxFormCompletionItems()) { 3545 items.erase(items.isEmpty() ? items.end() : --items.end()); 3546 } 3547 d->formCompletions->group("").writeEntry(name, items); 3548 } 3549 3550 void KHTMLView::addNonPasswordStorableSite(const QString &host) 3551 { 3552 if (!d->formCompletions) { 3553 d->formCompletions = new KConfig(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1Char('/') + "khtml/formcompletions"); 3554 } 3555 3556 KConfigGroup cg(d->formCompletions, "NonPasswordStorableSites"); 3557 QStringList sites = cg.readEntry("Sites", QStringList()); 3558 sites.append(host); 3559 cg.writeEntry("Sites", sites); 3560 cg.sync(); 3561 } 3562 3563 void KHTMLView::delNonPasswordStorableSite(const QString &host) 3564 { 3565 if (!d->formCompletions) { 3566 d->formCompletions = new KConfig(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1Char('/') + "khtml/formcompletions"); 3567 } 3568 3569 KConfigGroup cg(d->formCompletions, "NonPasswordStorableSites"); 3570 QStringList sites = cg.readEntry("Sites", QStringList()); 3571 sites.removeOne(host); 3572 cg.writeEntry("Sites", sites); 3573 cg.sync(); 3574 } 3575 3576 bool KHTMLView::nonPasswordStorableSite(const QString &host) const 3577 { 3578 if (!d->formCompletions) { 3579 d->formCompletions = new KConfig(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1Char('/') + "khtml/formcompletions"); 3580 } 3581 QStringList sites = d->formCompletions->group("NonPasswordStorableSites").readEntry("Sites", QStringList()); 3582 return (sites.indexOf(host) != -1); 3583 } 3584 3585 // returns true if event should be swallowed 3586 bool KHTMLView::dispatchMouseEvent(int eventId, DOM::NodeImpl *targetNode, 3587 DOM::NodeImpl *targetNodeNonShared, bool cancelable, 3588 int detail, QMouseEvent *_mouse, bool setUnder, 3589 int mouseEventType, int orient) 3590 { 3591 // if the target node is a text node, dispatch on the parent node - rdar://4196646 (and #76948) 3592 if (targetNode && targetNode->isTextNode()) { 3593 targetNode = targetNode->parentNode(); 3594 } 3595 3596 if (d->underMouse) { 3597 d->underMouse->deref(); 3598 } 3599 d->underMouse = targetNode; 3600 if (d->underMouse) { 3601 d->underMouse->ref(); 3602 } 3603 3604 if (d->underMouseNonShared) { 3605 d->underMouseNonShared->deref(); 3606 } 3607 d->underMouseNonShared = targetNodeNonShared; 3608 if (d->underMouseNonShared) { 3609 d->underMouseNonShared->ref(); 3610 } 3611 3612 bool isWheelEvent = (mouseEventType == DOM::NodeImpl::MouseWheel); 3613 3614 int exceptioncode = 0; 3615 int pageX = _mouse->x(); 3616 int pageY = _mouse->y(); 3617 revertTransforms(pageX, pageY); 3618 int clientX = pageX - contentsX(); 3619 int clientY = pageY - contentsY(); 3620 int screenX = _mouse->globalX(); 3621 int screenY = _mouse->globalY(); 3622 int button = -1; 3623 switch (_mouse->button()) { 3624 case Qt::LeftButton: 3625 button = 0; 3626 break; 3627 case Qt::MidButton: 3628 button = 1; 3629 break; 3630 case Qt::RightButton: 3631 button = 2; 3632 break; 3633 default: 3634 break; 3635 } 3636 if (d->accessKeysEnabled && d->accessKeysPreActivate && button != -1) { 3637 d->accessKeysPreActivate = false; 3638 } 3639 3640 bool ctrlKey = (_mouse->modifiers() & Qt::ControlModifier); 3641 bool altKey = (_mouse->modifiers() & Qt::AltModifier); 3642 bool shiftKey = (_mouse->modifiers() & Qt::ShiftModifier); 3643 bool metaKey = (_mouse->modifiers() & Qt::MetaModifier); 3644 3645 // mouseout/mouseover 3646 if (setUnder && d->oldUnderMouse != targetNode) { 3647 if (d->oldUnderMouse && d->oldUnderMouse->document() != m_part->xmlDocImpl()) { 3648 d->oldUnderMouse->deref(); 3649 d->oldUnderMouse = nullptr; 3650 } 3651 // send mouseout event to the old node 3652 if (d->oldUnderMouse) { 3653 // send mouseout event to the old node 3654 MouseEventImpl *me = new MouseEventImpl(EventImpl::MOUSEOUT_EVENT, 3655 true, true, m_part->xmlDocImpl()->defaultView(), 3656 0, screenX, screenY, clientX, clientY, pageX, pageY, 3657 ctrlKey, altKey, shiftKey, metaKey, 3658 button, targetNode); 3659 me->ref(); 3660 d->oldUnderMouse->dispatchEvent(me, exceptioncode, true); 3661 me->deref(); 3662 } 3663 // send mouseover event to the new node 3664 if (targetNode) { 3665 MouseEventImpl *me = new MouseEventImpl(EventImpl::MOUSEOVER_EVENT, 3666 true, true, m_part->xmlDocImpl()->defaultView(), 3667 0, screenX, screenY, clientX, clientY, pageX, pageY, 3668 ctrlKey, altKey, shiftKey, metaKey, 3669 button, d->oldUnderMouse); 3670 3671 me->ref(); 3672 targetNode->dispatchEvent(me, exceptioncode, true); 3673 me->deref(); 3674 } 3675 if (d->oldUnderMouse) { 3676 d->oldUnderMouse->deref(); 3677 } 3678 d->oldUnderMouse = targetNode; 3679 if (d->oldUnderMouse) { 3680 d->oldUnderMouse->ref(); 3681 } 3682 } 3683 3684 bool swallowEvent = false; 3685 3686 if (targetNode) { 3687 // if the target node is a disabled widget, we don't want any full-blown mouse events 3688 if (targetNode->isGenericFormElement() 3689 && static_cast<HTMLGenericFormElementImpl *>(targetNode)->disabled()) { 3690 return true; 3691 } 3692 3693 // send the actual event 3694 bool dblclick = (eventId == EventImpl::CLICK_EVENT && 3695 _mouse->type() == QEvent::MouseButtonDblClick); 3696 MouseEventImpl *me = new MouseEventImpl(static_cast<EventImpl::EventId>(eventId), 3697 true, cancelable, m_part->xmlDocImpl()->defaultView(), 3698 detail, screenX, screenY, clientX, clientY, pageX, pageY, 3699 ctrlKey, altKey, shiftKey, metaKey, 3700 button, nullptr, isWheelEvent ? nullptr : _mouse, dblclick, 3701 isWheelEvent ? static_cast<MouseEventImpl::Orientation>(orient) : MouseEventImpl::ONone); 3702 me->ref(); 3703 if (!d->m_mouseEventsTarget && RenderLayer::gScrollBar && eventId == EventImpl::MOUSEDOWN_EVENT) 3704 // button is pressed inside a layer scrollbar, so make it the target for future mousemove events until released 3705 { 3706 d->m_mouseEventsTarget = RenderLayer::gScrollBar; 3707 } 3708 if (d->m_mouseEventsTarget && qobject_cast<QScrollBar *>(d->m_mouseEventsTarget) && 3709 dynamic_cast<KHTMLWidget *>(static_cast<QWidget *>(d->m_mouseEventsTarget))) { 3710 // we have a sticky mouse event target and it is a layer's scrollbar. Forward events manually. 3711 // ### should use the dom 3712 KHTMLWidget *w = dynamic_cast<KHTMLWidget *>(static_cast<QWidget *>(d->m_mouseEventsTarget)); 3713 QPoint p = w->m_kwp->absolutePos(); 3714 QMouseEvent fw(_mouse->type(), QPoint(pageX, pageY) - p, _mouse->button(), _mouse->buttons(), _mouse->modifiers()); 3715 static_cast<RenderWidget::EventPropagator *>(static_cast<QWidget *>(d->m_mouseEventsTarget))->sendEvent(&fw); 3716 if (_mouse->type() == QMouseEvent::MouseButtonPress && _mouse->button() == Qt::RightButton) { 3717 QContextMenuEvent cme(QContextMenuEvent::Mouse, p); 3718 static_cast<RenderWidget::EventPropagator *>(static_cast<QWidget *>(d->m_mouseEventsTarget))->sendEvent(&cme); 3719 d->m_mouseEventsTarget = nullptr; 3720 } 3721 swallowEvent = true; 3722 } else { 3723 targetNode->dispatchEvent(me, exceptioncode, true); 3724 bool defaultHandled = me->defaultHandled(); 3725 if (defaultHandled || me->defaultPrevented()) { 3726 swallowEvent = true; 3727 } 3728 } 3729 if (eventId == EventImpl::MOUSEDOWN_EVENT && !me->defaultPrevented()) { 3730 // Focus should be shifted on mouse down, not on a click. -dwh 3731 // Blur current focus node when a link/button is clicked; this 3732 // is expected by some sites that rely on onChange handlers running 3733 // from form fields before the button click is processed. 3734 DOM::NodeImpl *nodeImpl = targetNode; 3735 for (; nodeImpl && !nodeImpl->isFocusable(); nodeImpl = nodeImpl->parentNode()) { 3736 } 3737 if (nodeImpl && nodeImpl->isMouseFocusable()) { 3738 m_part->xmlDocImpl()->setFocusNode(nodeImpl); 3739 } else if (!nodeImpl || !nodeImpl->focused()) { 3740 m_part->xmlDocImpl()->setFocusNode(nullptr); 3741 } 3742 } 3743 me->deref(); 3744 } 3745 3746 return swallowEvent; 3747 } 3748 3749 void KHTMLView::setIgnoreWheelEvents(bool e) 3750 { 3751 d->ignoreWheelEvents = e; 3752 } 3753 3754 #ifndef QT_NO_WHEELEVENT 3755 3756 void KHTMLView::wheelEvent(QWheelEvent *e) 3757 { 3758 // check if we should reset the state of the indicator describing if 3759 // we are currently scrolling the view as a result of wheel events 3760 if (d->scrollingFromWheel != QPoint(-1, -1) && d->scrollingFromWheel != QCursor::pos()) { 3761 d->scrollingFromWheel = d->scrollingFromWheelTimerId ? QCursor::pos() : QPoint(-1, -1); 3762 } 3763 3764 if (d->accessKeysEnabled && d->accessKeysPreActivate) { 3765 d->accessKeysPreActivate = false; 3766 } 3767 3768 if ((e->modifiers() & Qt::ControlModifier) == Qt::ControlModifier) { 3769 emit zoomView(- e->delta()); 3770 e->accept(); 3771 } else if (d->firstLayoutPending) { 3772 e->accept(); 3773 } else if (!m_kwp->isRedirected() && 3774 ((e->orientation() == Qt::Vertical && 3775 ((d->ignoreWheelEvents && !verticalScrollBar()->isVisible()) 3776 || (e->delta() > 0 && contentsY() <= 0) 3777 || (e->delta() < 0 && contentsY() >= contentsHeight() - visibleHeight()))) 3778 || 3779 (e->orientation() == Qt::Horizontal && 3780 ((d->ignoreWheelEvents && !horizontalScrollBar()->isVisible()) 3781 || (e->delta() > 0 && contentsX() <= 0) 3782 || (e->delta() < 0 && contentsX() >= contentsWidth() - visibleWidth())))) 3783 && m_part->parentPart()) { 3784 if (m_part->parentPart()->view()) { 3785 m_part->parentPart()->view()->wheelEvent(e); 3786 } 3787 e->ignore(); 3788 } else { 3789 int xm = e->x(); 3790 int ym = e->y(); 3791 revertTransforms(xm, ym); 3792 3793 DOM::NodeImpl::MouseEvent mev(e->buttons(), DOM::NodeImpl::MouseWheel); 3794 m_part->xmlDocImpl()->prepareMouseEvent(false, xm, ym, &mev); 3795 3796 MouseEventImpl::Orientation o = MouseEventImpl::OVertical; 3797 if (e->orientation() == Qt::Horizontal) { 3798 o = MouseEventImpl::OHorizontal; 3799 } 3800 3801 QMouseEvent _mouse(QEvent::MouseMove, e->pos(), Qt::NoButton, e->buttons(), e->modifiers()); 3802 bool swallow = dispatchMouseEvent(EventImpl::KHTML_MOUSEWHEEL_EVENT, mev.innerNode.handle(), mev.innerNonSharedNode.handle(), 3803 true, -e->delta() / 40, &_mouse, true, DOM::NodeImpl::MouseWheel, o); 3804 3805 if (swallow) { 3806 return; 3807 } 3808 3809 d->scrollBarMoved = true; 3810 d->scrollingFromWheel = QCursor::pos(); 3811 if (d->smoothScrollMode != SSMDisabled) { 3812 d->shouldSmoothScroll = true; 3813 } 3814 if (d->scrollingFromWheelTimerId) { 3815 killTimer(d->scrollingFromWheelTimerId); 3816 } 3817 d->scrollingFromWheelTimerId = startTimer(400); 3818 3819 if (m_part->parentPart()) { 3820 // don't propagate if we are a sub-frame and our scrollbars are already at end of range 3821 bool h = (static_cast<QWheelEvent *>(e)->orientation() == Qt::Horizontal); 3822 bool d = (static_cast<QWheelEvent *>(e)->delta() < 0); 3823 QScrollBar *hsb = horizontalScrollBar(); 3824 QScrollBar *vsb = verticalScrollBar(); 3825 if ((h && ((d && hsb->value() == hsb->maximum()) || (!d && hsb->value() == hsb->minimum()))) || 3826 (!h && ((d && vsb->value() == vsb->maximum()) || (!d && vsb->value() == vsb->minimum())))) { 3827 e->accept(); 3828 return; 3829 } 3830 } 3831 QScrollArea::wheelEvent(e); 3832 } 3833 3834 } 3835 #endif 3836 3837 void KHTMLView::dragEnterEvent(QDragEnterEvent *ev) 3838 { 3839 // Still overridden for BC reasons only... 3840 QScrollArea::dragEnterEvent(ev); 3841 } 3842 3843 void KHTMLView::dropEvent(QDropEvent *ev) 3844 { 3845 // Still overridden for BC reasons only... 3846 QScrollArea::dropEvent(ev); 3847 } 3848 3849 void KHTMLView::focusInEvent(QFocusEvent *e) 3850 { 3851 DOM::NodeImpl *fn = m_part->xmlDocImpl() ? m_part->xmlDocImpl()->focusNode() : nullptr; 3852 if (fn && fn->renderer() && fn->renderer()->isWidget() && 3853 (e->reason() != Qt::MouseFocusReason) && 3854 static_cast<khtml::RenderWidget *>(fn->renderer())->widget()) { 3855 static_cast<khtml::RenderWidget *>(fn->renderer())->widget()->setFocus(); 3856 } 3857 m_part->setSelectionVisible(); 3858 QScrollArea::focusInEvent(e); 3859 } 3860 3861 void KHTMLView::focusOutEvent(QFocusEvent *e) 3862 { 3863 if (m_part) { 3864 m_part->stopAutoScroll(); 3865 m_part->setSelectionVisible(false); 3866 } 3867 3868 if (d->cursorIconWidget) { 3869 d->cursorIconWidget->hide(); 3870 } 3871 3872 QScrollArea::focusOutEvent(e); 3873 } 3874 3875 void KHTMLView::scrollContentsBy(int dx, int dy) 3876 { 3877 if (!dx && !dy) { 3878 return; 3879 } 3880 3881 if (!d->firstLayoutPending && !d->complete && m_part->xmlDocImpl() && 3882 d->layoutSchedulingEnabled) { 3883 // contents scroll while we are not complete: we need to check our layout *now* 3884 khtml::RenderCanvas *root = static_cast<khtml::RenderCanvas *>(m_part->xmlDocImpl()->renderer()); 3885 if (root && root->needsLayout()) { 3886 unscheduleRelayout(); 3887 layout(); 3888 } 3889 } 3890 3891 if (d->shouldSmoothScroll && d->smoothScrollMode != SSMDisabled && m_part->xmlDocImpl() && 3892 m_part->xmlDocImpl()->renderer() && (d->smoothScrollMode != SSMWhenEfficient || d->smoothScrollMissedDeadlines != sWayTooMany)) { 3893 3894 bool doSmoothScroll = (!d->staticWidget || d->smoothScrollMode == SSMEnabled); 3895 3896 int numStaticPixels = 0; 3897 QRegion r = static_cast<RenderCanvas *>(m_part->xmlDocImpl()->renderer())->staticRegion(); 3898 3899 // only do smooth scrolling if static region is relatively small 3900 if (!doSmoothScroll && d->staticWidget == KHTMLViewPrivate::SBPartial && r.rects().size() <= 10) { 3901 foreach (const QRect &rr, r.rects()) { 3902 numStaticPixels += rr.width() * rr.height(); 3903 } 3904 if ((numStaticPixels < sSmoothScrollMinStaticPixels) || (numStaticPixels * 8 < visibleWidth()*visibleHeight())) { 3905 doSmoothScroll = true; 3906 } 3907 } 3908 if (doSmoothScroll) { 3909 setupSmoothScrolling(dx, dy); 3910 return; 3911 } 3912 } 3913 3914 if (underMouse() && QToolTip::isVisible()) { 3915 QToolTip::hideText(); 3916 } 3917 3918 if (!d->scrollingSelf) { 3919 d->scrollBarMoved = true; 3920 d->contentsMoving = true; 3921 // ensure quick reset of contentsMoving flag 3922 scheduleRepaint(0, 0, 0, 0); 3923 } 3924 3925 if (m_part->xmlDocImpl() && m_part->xmlDocImpl()->documentElement()) { 3926 m_part->xmlDocImpl()->documentElement()->dispatchHTMLEvent(EventImpl::SCROLL_EVENT, true, false); 3927 } 3928 3929 if (QApplication::isRightToLeft()) { 3930 dx = -dx; 3931 } 3932 3933 if (!d->smoothScrolling) { 3934 d->updateContentsXY(); 3935 } else { 3936 d->contentsX -= dx; 3937 d->contentsY -= dy; 3938 } 3939 if (widget()->pos() != QPoint(0, 0)) { 3940 // qCDebug(KHTML_LOG) << "Static widget wasn't positioned at (0,0). This should NOT happen. Please report this event to developers."; 3941 widget()->move(0, 0); 3942 } 3943 3944 QWidget *w = widget(); 3945 QPoint off; 3946 if (m_kwp->isRedirected()) { 3947 // This is a redirected sub frame. Translate to root view context 3948 KHTMLView *v = m_kwp->rootViewPos(off); 3949 if (v) { 3950 w = v->widget(); 3951 } 3952 off = viewport()->mapTo(this, off); 3953 } 3954 3955 if (d->staticWidget) { 3956 3957 // now remove from view the external widgets that must have completely 3958 // disappeared after dx/dy scroll delta is effective 3959 if (!d->visibleWidgets.isEmpty()) { 3960 checkExternalWidgetsPosition(); 3961 } 3962 3963 if (d->staticWidget == KHTMLViewPrivate::SBPartial 3964 && m_part->xmlDocImpl() && m_part->xmlDocImpl()->renderer()) { 3965 // static objects might be selectively repainted, like stones in flowing water 3966 QRegion r = static_cast<RenderCanvas *>(m_part->xmlDocImpl()->renderer())->staticRegion(); 3967 r.translate(-contentsX(), -contentsY()); 3968 QVector<QRect> ar = r.rects(); 3969 3970 for (int i = 0; i < ar.size(); ++i) { 3971 widget()->update(ar[i]); 3972 } 3973 r = QRegion(QRect(0, 0, visibleWidth(), visibleHeight())) - r; 3974 ar = r.rects(); 3975 for (int i = 0; i < ar.size(); ++i) { 3976 w->scroll(dx, dy, ar[i].translated(off)); 3977 } 3978 d->scrollExternalWidgets(dx, dy); 3979 } else { 3980 // we can't avoid a full update 3981 widget()->update(); 3982 } 3983 if (d->accessKeysActivated) { 3984 d->scrollAccessKeys(dx, dy); 3985 } 3986 3987 return; 3988 } 3989 3990 if (m_kwp->isRedirected()) { 3991 const QRect rect(off.x(), off.y(), visibleWidth() * d->zoomLevel / 100, visibleHeight() * d->zoomLevel / 100); 3992 w->scroll(dx, dy, rect); 3993 if (d->zoomLevel != 100) { 3994 w->update(rect); // without this update we are getting bad rendering when an iframe is zoomed in 3995 } 3996 } else { 3997 widget()->scroll(dx, dy, widget()->rect() & viewport()->rect()); 3998 } 3999 4000 d->scrollExternalWidgets(dx, dy); 4001 if (d->accessKeysActivated) { 4002 d->scrollAccessKeys(dx, dy); 4003 } 4004 } 4005 4006 void KHTMLView::setupSmoothScrolling(int dx, int dy) 4007 { 4008 // old or minimum speed 4009 int ddx = qMax(d->steps ? abs(d->dx) / d->steps : 0, 3); 4010 int ddy = qMax(d->steps ? abs(d->dy) / d->steps : 0, 3); 4011 4012 // full scroll is remaining scroll plus new scroll 4013 d->dx = d->dx + dx; 4014 d->dy = d->dy + dy; 4015 4016 if (d->dx == 0 && d->dy == 0) { 4017 d->stopScrolling(); 4018 return; 4019 } 4020 4021 d->steps = (sSmoothScrollTime - 1) / sSmoothScrollTick + 1; 4022 4023 if (qMax(abs(d->dx), abs(d->dy)) / d->steps < qMax(ddx, ddy)) { 4024 // Don't move slower than average 4px/step in minimum one direction 4025 // This means fewer than normal steps 4026 d->steps = qMax((abs(d->dx) + ddx - 1) / ddx, (abs(d->dy) + ddy - 1) / ddy); 4027 if (d->steps < 1) { 4028 d->steps = 1; 4029 } 4030 } 4031 4032 d->smoothScrollStopwatch.start(); 4033 if (!d->smoothScrolling) { 4034 d->startScrolling(); 4035 scrollTick(); 4036 } 4037 } 4038 4039 void KHTMLView::scrollTick() 4040 { 4041 if (d->dx == 0 && d->dy == 0) { 4042 d->stopScrolling(); 4043 return; 4044 } 4045 4046 if (d->steps < 1) { 4047 d->steps = 1; 4048 } 4049 int takesteps = d->smoothScrollStopwatch.restart() / sSmoothScrollTick; 4050 int scroll_x = 0; 4051 int scroll_y = 0; 4052 if (takesteps < 1) { 4053 takesteps = 1; 4054 } 4055 if (takesteps > d->steps) { 4056 takesteps = d->steps; 4057 } 4058 for (int i = 0; i < takesteps; i++) { 4059 int ddx = (d->dx / (d->steps + 1)) * 2; 4060 int ddy = (d->dy / (d->steps + 1)) * 2; 4061 4062 // limit step to requested scrolling distance 4063 if (abs(ddx) > abs(d->dx)) { 4064 ddx = d->dx; 4065 } 4066 if (abs(ddy) > abs(d->dy)) { 4067 ddy = d->dy; 4068 } 4069 4070 // update remaining scroll 4071 d->dx -= ddx; 4072 d->dy -= ddy; 4073 scroll_x += ddx; 4074 scroll_y += ddy; 4075 d->steps--; 4076 } 4077 4078 d->shouldSmoothScroll = false; 4079 scrollContentsBy(scroll_x, scroll_y); 4080 4081 if (takesteps < 2) { 4082 d->smoothScrollMissedDeadlines = 0; 4083 } else { 4084 if (d->smoothScrollMissedDeadlines != sWayTooMany && 4085 (!m_part->xmlDocImpl() || !m_part->xmlDocImpl()->parsing())) { 4086 d->smoothScrollMissedDeadlines++; 4087 if (d->smoothScrollMissedDeadlines >= sMaxMissedDeadlines) { 4088 // we missed many deadlines in a row! 4089 // time to signal we had enough.. 4090 d->smoothScrollMissedDeadlines = sWayTooMany; 4091 } 4092 } 4093 } 4094 } 4095 4096 void KHTMLView::addChild(QWidget *child, int x, int y) 4097 { 4098 if (!child) { 4099 return; 4100 } 4101 4102 if (child->parent() != widget()) { 4103 child->setParent(widget()); 4104 } 4105 4106 // ### handle pseudo-zooming of non-redirected widgets (e.g. just resize'em) 4107 4108 child->move(x - contentsX(), y - contentsY()); 4109 } 4110 4111 void KHTMLView::timerEvent(QTimerEvent *e) 4112 { 4113 // qCDebug(KHTML_LOG) << "timer event " << e->timerId(); 4114 if (e->timerId() == d->scrollTimerId) { 4115 if (d->scrollSuspended) { 4116 return; 4117 } 4118 switch (d->scrollDirection) { 4119 case KHTMLViewPrivate::ScrollDown: 4120 if (contentsY() + visibleHeight() >= contentsHeight()) { 4121 d->newScrollTimer(this, 0); 4122 } else { 4123 verticalScrollBar()->setValue(verticalScrollBar()->value() + d->scrollBy); 4124 } 4125 break; 4126 case KHTMLViewPrivate::ScrollUp: 4127 if (contentsY() <= 0) { 4128 d->newScrollTimer(this, 0); 4129 } else { 4130 verticalScrollBar()->setValue(verticalScrollBar()->value() - d->scrollBy); 4131 } 4132 break; 4133 case KHTMLViewPrivate::ScrollRight: 4134 if (contentsX() + visibleWidth() >= contentsWidth()) { 4135 d->newScrollTimer(this, 0); 4136 } else { 4137 horizontalScrollBar()->setValue(horizontalScrollBar()->value() + d->scrollBy); 4138 } 4139 break; 4140 case KHTMLViewPrivate::ScrollLeft: 4141 if (contentsX() <= 0) { 4142 d->newScrollTimer(this, 0); 4143 } else { 4144 horizontalScrollBar()->setValue(horizontalScrollBar()->value() - d->scrollBy); 4145 } 4146 break; 4147 } 4148 return; 4149 } else if (e->timerId() == d->scrollingFromWheelTimerId) { 4150 killTimer(d->scrollingFromWheelTimerId); 4151 d->scrollingFromWheelTimerId = 0; 4152 } else if (e->timerId() == d->layoutTimerId) { 4153 if (d->firstLayoutPending && d->layoutAttemptCounter < 4 4154 && (!m_part->xmlDocImpl() || !m_part->xmlDocImpl()->readyForLayout())) { 4155 d->layoutAttemptCounter++; 4156 killTimer(d->layoutTimerId); 4157 d->layoutTimerId = 0; 4158 scheduleRelayout(); 4159 return; 4160 } 4161 layout(); 4162 d->scheduledLayoutCounter++; 4163 if (d->firstLayoutPending) { 4164 d->firstLayoutPending = false; 4165 verticalScrollBar()->setEnabled(true); 4166 horizontalScrollBar()->setEnabled(true); 4167 } 4168 } 4169 4170 d->contentsMoving = false; 4171 if (m_part->xmlDocImpl()) { 4172 DOM::DocumentImpl *document = m_part->xmlDocImpl(); 4173 khtml::RenderCanvas *root = static_cast<khtml::RenderCanvas *>(document->renderer()); 4174 4175 if (root && root->needsLayout()) { 4176 if (d->repaintTimerId) { 4177 killTimer(d->repaintTimerId); 4178 } 4179 d->repaintTimerId = 0; 4180 scheduleRelayout(); 4181 return; 4182 } 4183 } 4184 4185 if (d->repaintTimerId) { 4186 killTimer(d->repaintTimerId); 4187 } 4188 d->repaintTimerId = 0; 4189 4190 QRect updateRegion; 4191 const QVector<QRect> rects = d->updateRegion.rects(); 4192 4193 d->updateRegion = QRegion(); 4194 4195 if (rects.size()) { 4196 updateRegion = rects[0]; 4197 } 4198 4199 for (int i = 1; i < rects.size(); ++i) { 4200 QRect newRegion = updateRegion.united(rects[i]); 4201 if (2 * newRegion.height() > 3 * updateRegion.height()) { 4202 repaintContents(updateRegion); 4203 updateRegion = rects[i]; 4204 } else { 4205 updateRegion = newRegion; 4206 } 4207 } 4208 4209 if (!updateRegion.isNull()) { 4210 repaintContents(updateRegion); 4211 } 4212 4213 // As widgets can only be accurately positioned during painting, every layout might 4214 // dissociate a widget from its RenderWidget. E.g: if a RenderWidget was visible before layout, but the layout 4215 // pushed it out of the viewport, it will not be repainted, and consequently it's associated widget won't be repositioned. 4216 // Thus we need to check each supposedly 'visible' widget at the end of layout, and remove it in case it's no more in sight. 4217 4218 if (d->dirtyLayout && !d->visibleWidgets.isEmpty()) { 4219 checkExternalWidgetsPosition(); 4220 } 4221 4222 d->dirtyLayout = false; 4223 4224 emit repaintAccessKeys(); 4225 if (d->emitCompletedAfterRepaint) { 4226 bool full = d->emitCompletedAfterRepaint == KHTMLViewPrivate::CSFull; 4227 d->emitCompletedAfterRepaint = KHTMLViewPrivate::CSNone; 4228 if (full) { 4229 emit m_part->completed(); 4230 } else { 4231 emit m_part->completed(true); 4232 } 4233 } 4234 } 4235 4236 void KHTMLView::checkExternalWidgetsPosition() 4237 { 4238 QWidget *w; 4239 QRect visibleRect(contentsX(), contentsY(), visibleWidth(), visibleHeight()); 4240 QList<RenderWidget *> toRemove; 4241 QHashIterator<void *, QWidget *> it(d->visibleWidgets); 4242 while (it.hasNext()) { 4243 int xp = 0, yp = 0; 4244 it.next(); 4245 RenderWidget *rw = static_cast<RenderWidget *>(it.key()); 4246 if (!rw->absolutePosition(xp, yp) || 4247 !visibleRect.intersects(QRect(xp, yp, it.value()->width(), it.value()->height()))) { 4248 toRemove.append(rw); 4249 } 4250 } 4251 foreach (RenderWidget *r, toRemove) 4252 if ((w = d->visibleWidgets.take(r))) { 4253 w->move(0, -500000); 4254 } 4255 } 4256 4257 void KHTMLView::scheduleRelayout(khtml::RenderObject * /*clippedObj*/) 4258 { 4259 if (!d->layoutSchedulingEnabled || d->layoutTimerId) { 4260 return; 4261 } 4262 4263 int time = 0; 4264 if (d->firstLayoutPending) { 4265 // Any repaint happening while we have no content blanks the viewport ("white flash"). 4266 // Hence the need to delay the first layout as much as we can. 4267 // Only if the document gets stuck for too long in incomplete state will we allow the blanking. 4268 time = d->layoutAttemptCounter ? 4269 sLayoutAttemptDelay + sLayoutAttemptIncrement * d->layoutAttemptCounter : sFirstLayoutDelay; 4270 } else if (m_part->xmlDocImpl() && m_part->xmlDocImpl()->parsing()) { 4271 // Delay between successive layouts in parsing mode. 4272 // Increment reflects the decaying importance of visual feedback over time. 4273 time = qMin(2000, sParsingLayoutsInterval + d->scheduledLayoutCounter * sParsingLayoutsIncrement); 4274 } 4275 d->layoutTimerId = startTimer(time); 4276 } 4277 4278 void KHTMLView::unscheduleRelayout() 4279 { 4280 if (!d->layoutTimerId) { 4281 return; 4282 } 4283 4284 killTimer(d->layoutTimerId); 4285 d->layoutTimerId = 0; 4286 } 4287 4288 void KHTMLView::unscheduleRepaint() 4289 { 4290 if (!d->repaintTimerId) { 4291 return; 4292 } 4293 4294 killTimer(d->repaintTimerId); 4295 d->repaintTimerId = 0; 4296 } 4297 4298 void KHTMLView::scheduleRepaint(int x, int y, int w, int h, bool asap) 4299 { 4300 bool parsing = !m_part->xmlDocImpl() || m_part->xmlDocImpl()->parsing(); 4301 4302 // qCDebug(KHTML_LOG) << "parsing " << parsing; 4303 // qCDebug(KHTML_LOG) << "complete " << d->complete; 4304 4305 int time = parsing && !d->firstLayoutPending ? 150 : (!asap ? (!d->complete ? 80 : 20) : 0); 4306 4307 #ifdef DEBUG_FLICKER 4308 QPainter p; 4309 p.begin(viewport()); 4310 4311 int vx, vy; 4312 contentsToViewport(x, y, vx, vy); 4313 p.fillRect(vx, vy, w, h, Qt::red); 4314 p.end(); 4315 #endif 4316 4317 d->updateRegion = d->updateRegion.united(QRect(x, y, w, h)); 4318 4319 if (asap && !parsing) { 4320 unscheduleRepaint(); 4321 } 4322 4323 if (!d->repaintTimerId) { 4324 d->repaintTimerId = startTimer(time); 4325 } 4326 4327 // qCDebug(KHTML_LOG) << "starting timer " << time; 4328 } 4329 4330 void KHTMLView::complete(bool pendingAction) 4331 { 4332 // qCDebug(KHTML_LOG) << "KHTMLView::complete()"; 4333 4334 d->complete = true; 4335 4336 // is there a relayout pending? 4337 if (d->layoutTimerId) { 4338 // qCDebug(KHTML_LOG) << "requesting relayout now"; 4339 // do it now 4340 killTimer(d->layoutTimerId); 4341 d->layoutTimerId = startTimer(0); 4342 d->emitCompletedAfterRepaint = pendingAction ? 4343 KHTMLViewPrivate::CSActionPending : KHTMLViewPrivate::CSFull; 4344 } 4345 4346 // is there a repaint pending? 4347 if (d->repaintTimerId) { 4348 // qCDebug(KHTML_LOG) << "requesting repaint now"; 4349 // do it now 4350 killTimer(d->repaintTimerId); 4351 d->repaintTimerId = startTimer(0); 4352 d->emitCompletedAfterRepaint = pendingAction ? 4353 KHTMLViewPrivate::CSActionPending : KHTMLViewPrivate::CSFull; 4354 } 4355 4356 if (!d->emitCompletedAfterRepaint) { 4357 if (!pendingAction) { 4358 emit m_part->completed(); 4359 } else { 4360 emit m_part->completed(true); 4361 } 4362 } 4363 4364 } 4365 4366 void KHTMLView::updateScrollBars() 4367 { 4368 const QWidget *view = widget(); 4369 if (!view) { 4370 return; 4371 } 4372 4373 QSize p = viewport()->size(); 4374 QSize m = maximumViewportSize(); 4375 4376 if (m.expandedTo(view->size()) == m) { 4377 p = m; // no scroll bars needed 4378 } 4379 4380 QSize v = view->size(); 4381 horizontalScrollBar()->setRange(0, v.width() - p.width()); 4382 horizontalScrollBar()->setPageStep(p.width()); 4383 verticalScrollBar()->setRange(0, v.height() - p.height()); 4384 verticalScrollBar()->setPageStep(p.height()); 4385 if (!d->smoothScrolling) { 4386 d->updateContentsXY(); 4387 } 4388 } 4389 4390 void KHTMLView::slotMouseScrollTimer() 4391 { 4392 horizontalScrollBar()->setValue(horizontalScrollBar()->value() + d->m_mouseScroll_byX); 4393 verticalScrollBar()->setValue(verticalScrollBar()->value() + d->m_mouseScroll_byY); 4394 } 4395 4396 static DOM::Position positionOfLineBoundary(const DOM::Position &pos, bool toEnd) 4397 { 4398 Selection sel = pos; 4399 sel.expandUsingGranularity(Selection::LINE); 4400 return toEnd ? sel.end() : sel.start(); 4401 } 4402 4403 inline static DOM::Position positionOfLineBegin(const DOM::Position &pos) 4404 { 4405 return positionOfLineBoundary(pos, false); 4406 } 4407 4408 inline static DOM::Position positionOfLineEnd(const DOM::Position &pos) 4409 { 4410 return positionOfLineBoundary(pos, true); 4411 } 4412 4413 bool KHTMLView::caretKeyPressEvent(QKeyEvent *_ke) 4414 { 4415 EditorContext *ec = &m_part->d->editor_context; 4416 Selection &caret = ec->m_selection; 4417 Position old_pos = caret.caretPos(); 4418 Position pos = old_pos; 4419 bool recalcXPos = true; 4420 bool handled = true; 4421 4422 bool ctrl = _ke->modifiers() & Qt::ControlModifier; 4423 bool shift = _ke->modifiers() & Qt::ShiftModifier; 4424 4425 switch (_ke->key()) { 4426 4427 // -- Navigational keys 4428 case Qt::Key_Down: 4429 pos = old_pos.nextLinePosition(caret.xPosForVerticalArrowNavigation(Selection::EXTENT)); 4430 recalcXPos = false; 4431 break; 4432 4433 case Qt::Key_Up: 4434 pos = old_pos.previousLinePosition(caret.xPosForVerticalArrowNavigation(Selection::EXTENT)); 4435 recalcXPos = false; 4436 break; 4437 4438 case Qt::Key_Left: 4439 pos = ctrl ? old_pos.previousWordPosition() : old_pos.previousCharacterPosition(); 4440 break; 4441 4442 case Qt::Key_Right: 4443 pos = ctrl ? old_pos.nextWordPosition() : old_pos.nextCharacterPosition(); 4444 break; 4445 4446 case Qt::Key_PageDown: 4447 // moveCaretNextPage(); ### 4448 break; 4449 4450 case Qt::Key_PageUp: 4451 // moveCaretPrevPage(); ### 4452 break; 4453 4454 case Qt::Key_Home: 4455 if (ctrl) 4456 /*moveCaretToDocumentBoundary(false)*/; // ### 4457 else { 4458 pos = positionOfLineBegin(old_pos); 4459 } 4460 break; 4461 4462 case Qt::Key_End: 4463 if (ctrl) 4464 /*moveCaretToDocumentBoundary(true)*/; // ### 4465 else { 4466 pos = positionOfLineEnd(old_pos); 4467 } 4468 break; 4469 4470 default: 4471 handled = false; 4472 4473 }/*end switch*/ 4474 4475 if (pos != old_pos) { 4476 m_part->clearCaretRectIfNeeded(); 4477 4478 caret.moveTo(shift ? caret.nonCaretPos() : pos, pos); 4479 int old_x = caret.xPosForVerticalArrowNavigation(Selection::CARETPOS); 4480 4481 m_part->selectionLayoutChanged(); 4482 4483 // restore old x-position to prevent recalculation 4484 if (!recalcXPos) { 4485 m_part->d->editor_context.m_xPosForVerticalArrowNavigation = old_x; 4486 } 4487 4488 m_part->emitCaretPositionChanged(pos); 4489 // ### check when to emit it 4490 m_part->notifySelectionChanged(); 4491 4492 } 4493 4494 if (handled) { 4495 _ke->accept(); 4496 } 4497 return handled; 4498 } 4499 4500 #undef DEBUG_CARETMODE