KHTML
khtmlview.cpp
Go to the documentation of this file.
00001 /* This file is part of the KDE project 00002 * 00003 * Copyright (C) 1998, 1999 Torben Weis <weis@kde.org> 00004 * 1999 Lars Knoll <knoll@kde.org> 00005 * 1999 Antti Koivisto <koivisto@kde.org> 00006 * 2000-2004 Dirk Mueller <mueller@kde.org> 00007 * 2003 Leo Savernik <l.savernik@aon.at> 00008 * 2003-2008 Apple Computer, Inc. 00009 * 2008 Allan Sandfeld Jensen <kde@carewolf.com> 00010 * 2006-2008 Germain Garand <germain@ebooksfrance.org> 00011 * 00012 * This library is free software; you can redistribute it and/or 00013 * modify it under the terms of the GNU Library General Public 00014 * License as published by the Free Software Foundation; either 00015 * version 2 of the License, or (at your option) any later version. 00016 * 00017 * This library is distributed in the hope that it will be useful, 00018 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00019 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00020 * Library General Public License for more details. 00021 * 00022 * You should have received a copy of the GNU Library General Public License 00023 * along with this library; see the file COPYING.LIB. If not, write to 00024 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 00025 * Boston, MA 02110-1301, USA. 00026 */ 00027 00028 00029 #include "khtmlview.h" 00030 00031 #include "khtmlview.moc" 00032 00033 #include "khtml_part.h" 00034 #include "khtml_events.h" 00035 #ifdef Q_WS_X11 00036 #include <qx11info_x11.h> 00037 #endif 00038 00039 #include "html/html_documentimpl.h" 00040 #include "html/html_inlineimpl.h" 00041 #include "html/html_formimpl.h" 00042 #include "html/htmltokenizer.h" 00043 #include "editing/editor.h" 00044 #include "rendering/render_arena.h" 00045 #include "rendering/render_canvas.h" 00046 #include "rendering/render_frames.h" 00047 #include "rendering/render_replaced.h" 00048 #include "rendering/render_form.h" 00049 #include "rendering/render_layer.h" 00050 #include "rendering/render_line.h" 00051 #include "rendering/render_table.h" 00052 // removeme 00053 #define protected public 00054 #include "rendering/render_text.h" 00055 #undef protected 00056 #include "xml/dom2_eventsimpl.h" 00057 #include "css/cssstyleselector.h" 00058 #include "css/csshelper.h" 00059 #include "misc/helper.h" 00060 #include "misc/loader.h" 00061 #include "khtml_settings.h" 00062 #include "khtml_printsettings.h" 00063 00064 #include "khtmlpart_p.h" 00065 00066 #include <kcursor.h> 00067 #include <kdebug.h> 00068 #include <kglobalsettings.h> 00069 #include <kdialog.h> 00070 #include <kiconloader.h> 00071 #include <klocale.h> 00072 #include <knotification.h> 00073 #include <kdeprintdialog.h> 00074 #include <kconfig.h> 00075 #include <kstandarddirs.h> 00076 #include <kstandardshortcut.h> 00077 #include <kstringhandler.h> 00078 #include <kconfiggroup.h> 00079 00080 #include <QtGui/QBitmap> 00081 #include <QtGui/QLabel> 00082 #include <QtCore/QObject> 00083 #include <QtGui/QPainter> 00084 #include <QtCore/QHash> 00085 #include <QtGui/QToolTip> 00086 #include <QtCore/QString> 00087 #include <QtGui/QTextDocument> 00088 #include <QtCore/QTimer> 00089 #include <QtCore/QAbstractEventDispatcher> 00090 #include <QtCore/QVector> 00091 #include <QtGui/QAbstractScrollArea> 00092 #include <QtGui/QPrinter> 00093 #include <QtGui/QPrintDialog> 00094 00095 //#define DEBUG_FLICKER 00096 00097 #include <limits.h> 00098 #ifdef Q_WS_X11 00099 #include <X11/Xlib.h> 00100 #include <fixx11h.h> 00101 #elif defined(Q_WS_WIN) 00102 #include <windows.h> 00103 #endif 00104 00105 #if 0 00106 namespace khtml { 00107 void dumpLineBoxes(RenderFlow *flow); 00108 } 00109 #endif 00110 00111 using namespace DOM; 00112 using namespace khtml; 00113 00114 #ifndef NDEBUG 00115 static const int sFirstLayoutDelay = 520; 00116 static const int sParsingLayoutsInterval = 380; 00117 static const int sLayoutAttemptDelay = 300; 00118 #else 00119 static const int sFirstLayoutDelay = 280; 00120 static const int sParsingLayoutsInterval = 320; 00121 static const int sLayoutAttemptDelay = 200; 00122 #endif 00123 static const int sLayoutAttemptIncrement = 20; 00124 static const int sParsingLayoutsIncrement = 60; 00125 00126 static const int sSmoothScrollTime = 128; 00127 static const int sSmoothScrollTick = 16; 00128 static const int sSmoothScrollMinStaticPixels = 320*200; 00129 00130 static const int sMaxMissedDeadlines = 12; 00131 static const int sWayTooMany = -1; 00132 00133 class KHTMLViewPrivate { 00134 friend class KHTMLView; 00135 public: 00136 00137 enum PseudoFocusNodes { 00138 PFNone, 00139 PFTop, 00140 PFBottom 00141 }; 00142 00143 enum StaticBackgroundState { 00144 SBNone = 0, 00145 SBPartial, 00146 SBFull 00147 }; 00148 00149 enum CompletedState { 00150 CSNone = 0, 00151 CSFull, 00152 CSActionPending 00153 }; 00154 00155 KHTMLViewPrivate(KHTMLView* v) 00156 : underMouse( 0 ), underMouseNonShared( 0 ), oldUnderMouse( 0 ) 00157 { 00158 postponed_autorepeat = NULL; 00159 scrollingFromWheelTimerId = 0; 00160 smoothScrollMode = KHTMLView::SSMWhenEfficient; 00161 00162 reset(); 00163 vpolicy = Qt::ScrollBarAsNeeded; 00164 hpolicy = Qt::ScrollBarAsNeeded; 00165 formCompletions=0; 00166 prevScrollbarVisible = true; 00167 00168 possibleTripleClick = false; 00169 emitCompletedAfterRepaint = CSNone; 00170 cursorIconWidget = 0; 00171 cursorIconType = KHTMLView::LINK_NORMAL; 00172 m_mouseScrollTimer = 0; 00173 m_mouseScrollIndicator = 0; 00174 contentsX = 0; 00175 contentsY = 0; 00176 view = v; 00177 } 00178 ~KHTMLViewPrivate() 00179 { 00180 delete formCompletions; 00181 delete postponed_autorepeat; 00182 if (underMouse) 00183 underMouse->deref(); 00184 if (underMouseNonShared) 00185 underMouseNonShared->deref(); 00186 if (oldUnderMouse) 00187 oldUnderMouse->deref(); 00188 00189 delete cursorIconWidget; 00190 delete m_mouseScrollTimer; 00191 delete m_mouseScrollIndicator; 00192 } 00193 void reset() 00194 { 00195 if (underMouse) 00196 underMouse->deref(); 00197 underMouse = 0; 00198 if (underMouseNonShared) 00199 underMouseNonShared->deref(); 00200 underMouseNonShared = 0; 00201 if (oldUnderMouse) 00202 oldUnderMouse->deref(); 00203 oldUnderMouse = 0; 00204 linkPressed = false; 00205 staticWidget = SBNone; 00206 fixedObjectsCount = 0; 00207 staticObjectsCount = 0; 00208 tabMovePending = false; 00209 lastTabbingDirection = true; 00210 pseudoFocusNode = PFNone; 00211 zoomLevel = 100; 00212 #ifndef KHTML_NO_SCROLLBARS 00213 //We don't turn off the toolbars here 00214 //since if the user turns them 00215 //off, then chances are they want them turned 00216 //off always - even after a reset. 00217 #else 00218 vpolicy = ScrollBarAlwaysOff; 00219 hpolicy = ScrollBarAlwaysOff; 00220 #endif 00221 scrollBarMoved = false; 00222 contentsMoving = false; 00223 ignoreWheelEvents = false; 00224 scrollingFromWheel = QPoint(-1,-1); 00225 borderX = 30; 00226 borderY = 30; 00227 steps = 0; 00228 dx = dy = 0; 00229 paged = false; 00230 clickX = -1; 00231 clickY = -1; 00232 clickCount = 0; 00233 isDoubleClick = false; 00234 scrollingSelf = false; 00235 delete postponed_autorepeat; 00236 postponed_autorepeat = NULL; 00237 layoutTimerId = 0; 00238 repaintTimerId = 0; 00239 scrollTimerId = 0; 00240 scrollSuspended = false; 00241 scrollSuspendPreActivate = false; 00242 smoothScrolling = false; 00243 smoothScrollModeIsDefault = true; 00244 shouldSmoothScroll = false; 00245 smoothScrollMissedDeadlines = 0; 00246 hasFrameset = false; 00247 complete = false; 00248 firstLayoutPending = true; 00249 #ifdef SPEED_DEBUG 00250 firstRepaintPending = true; 00251 #endif 00252 needsFullRepaint = true; 00253 dirtyLayout = false; 00254 layoutSchedulingEnabled = true; 00255 painting = false; 00256 layoutCounter = 0; 00257 layoutAttemptCounter = 0; 00258 scheduledLayoutCounter = 0; 00259 updateRegion = QRegion(); 00260 m_dialogsAllowed = true; 00261 accessKeysActivated = false; 00262 accessKeysPreActivate = false; 00263 00264 // the view might have been built before the part it will be assigned to, 00265 // so exceptionally, we need to directly ref/deref KHTMLGlobal to 00266 // account for this transitory case. 00267 KHTMLGlobal::ref(); 00268 accessKeysEnabled = KHTMLGlobal::defaultHTMLSettings()->accessKeysEnabled(); 00269 KHTMLGlobal::deref(); 00270 00271 emitCompletedAfterRepaint = CSNone; 00272 m_mouseEventsTarget = 0; 00273 m_clipHolder = 0; 00274 } 00275 void newScrollTimer(QWidget *view, int tid) 00276 { 00277 //kDebug(6000) << "newScrollTimer timer " << tid; 00278 view->killTimer(scrollTimerId); 00279 scrollTimerId = tid; 00280 scrollSuspended = false; 00281 } 00282 enum ScrollDirection { ScrollLeft, ScrollRight, ScrollUp, ScrollDown }; 00283 00284 void adjustScroller(QWidget *view, ScrollDirection direction, ScrollDirection oppositedir) 00285 { 00286 static const struct { int msec, pixels; } timings [] = { 00287 {320,1}, {224,1}, {160,1}, {112,1}, {80,1}, {56,1}, {40,1}, 00288 {28,1}, {20,1}, {20,2}, {20,3}, {20,4}, {20,6}, {20,8}, {0,0} 00289 }; 00290 if (!scrollTimerId || 00291 (static_cast<int>(scrollDirection) != direction && 00292 (static_cast<int>(scrollDirection) != oppositedir || scrollSuspended))) { 00293 scrollTiming = 6; 00294 scrollBy = timings[scrollTiming].pixels; 00295 scrollDirection = direction; 00296 newScrollTimer(view, view->startTimer(timings[scrollTiming].msec)); 00297 } else if (scrollDirection == direction && 00298 timings[scrollTiming+1].msec && !scrollSuspended) { 00299 scrollBy = timings[++scrollTiming].pixels; 00300 newScrollTimer(view, view->startTimer(timings[scrollTiming].msec)); 00301 } else if (scrollDirection == oppositedir) { 00302 if (scrollTiming) { 00303 scrollBy = timings[--scrollTiming].pixels; 00304 newScrollTimer(view, view->startTimer(timings[scrollTiming].msec)); 00305 } 00306 } 00307 scrollSuspended = false; 00308 } 00309 00310 bool haveZoom() const { return zoomLevel != 100; } 00311 00312 void startScrolling() 00313 { 00314 smoothScrolling = true; 00315 smoothScrollTimer.start(sSmoothScrollTick); 00316 shouldSmoothScroll = false; 00317 } 00318 00319 void stopScrolling() 00320 { 00321 smoothScrollTimer.stop(); 00322 dx = dy = 0; 00323 steps = 0; 00324 updateContentsXY(); 00325 smoothScrolling = false; 00326 shouldSmoothScroll = false; 00327 } 00328 00329 void updateContentsXY() 00330 { 00331 contentsX = QApplication::isRightToLeft() ? 00332 view->horizontalScrollBar()->maximum()-view->horizontalScrollBar()->value() : view->horizontalScrollBar()->value(); 00333 contentsY = view->verticalScrollBar()->value(); 00334 } 00335 void scrollAccessKeys(int dx, int dy) 00336 { 00337 QList<QLabel*> wl = qFindChildren<QLabel*>(view->widget(), "KHTMLAccessKey"); 00338 foreach(QLabel* w, wl) { 00339 w->move( w->pos() + QPoint(dx, dy) ); 00340 } 00341 } 00342 void scrollExternalWidgets(int dx, int dy) 00343 { 00344 if (visibleWidgets.isEmpty()) 00345 return; 00346 00347 QHashIterator<void*, QWidget*> it(visibleWidgets); 00348 while (it.hasNext()) { 00349 it.next(); 00350 it.value()->move( it.value()->pos() + QPoint(dx, dy) ); 00351 } 00352 } 00353 00354 NodeImpl *underMouse; 00355 NodeImpl *underMouseNonShared; 00356 NodeImpl *oldUnderMouse; 00357 00358 // Do not adjust bitfield enums sizes. 00359 // They are oversized because they are signed on some platforms. 00360 bool tabMovePending:1; 00361 bool lastTabbingDirection:1; 00362 PseudoFocusNodes pseudoFocusNode:3; 00363 bool scrollBarMoved:1; 00364 bool contentsMoving:1; 00365 00366 Qt::ScrollBarPolicy vpolicy; 00367 Qt::ScrollBarPolicy hpolicy; 00368 bool prevScrollbarVisible:1; 00369 bool linkPressed:1; 00370 bool ignoreWheelEvents:1; 00371 StaticBackgroundState staticWidget: 3; 00372 int staticObjectsCount; 00373 int fixedObjectsCount; 00374 00375 int zoomLevel; 00376 int borderX, borderY; 00377 int dx, dy; 00378 int steps; 00379 KConfig *formCompletions; 00380 00381 int clickX, clickY, clickCount; 00382 bool isDoubleClick; 00383 00384 bool paged; 00385 00386 bool scrollingSelf; 00387 int contentsX, contentsY; 00388 int layoutTimerId; 00389 QKeyEvent* postponed_autorepeat; 00390 00391 int repaintTimerId; 00392 int scrollTimerId; 00393 int scrollTiming; 00394 int scrollBy; 00395 ScrollDirection scrollDirection :3; 00396 bool scrollSuspended :1; 00397 bool scrollSuspendPreActivate :1; 00398 KHTMLView::SmoothScrollingMode smoothScrollMode :3; 00399 bool smoothScrolling :1; 00400 bool smoothScrollModeIsDefault :1; 00401 bool shouldSmoothScroll :1; 00402 bool hasFrameset :1; 00403 bool complete :1; 00404 bool firstLayoutPending :1; 00405 #ifdef SPEED_DEBUG 00406 bool firstRepaintPending :1; 00407 #endif 00408 bool layoutSchedulingEnabled :1; 00409 bool needsFullRepaint :1; 00410 bool painting :1; 00411 bool possibleTripleClick :1; 00412 bool dirtyLayout :1; 00413 bool m_dialogsAllowed :1; 00414 short smoothScrollMissedDeadlines; 00415 int layoutCounter; 00416 int layoutAttemptCounter; 00417 int scheduledLayoutCounter; 00418 QRegion updateRegion; 00419 QTimer smoothScrollTimer; 00420 QTime smoothScrollStopwatch; 00421 QHash<void*, QWidget*> visibleWidgets; 00422 bool accessKeysEnabled; 00423 bool accessKeysActivated; 00424 bool accessKeysPreActivate; 00425 CompletedState emitCompletedAfterRepaint; 00426 00427 QLabel* cursorIconWidget; 00428 KHTMLView::LinkCursor cursorIconType; 00429 00430 // scrolling activated by MMB 00431 short m_mouseScroll_byX; 00432 short m_mouseScroll_byY; 00433 QPoint scrollingFromWheel; 00434 int scrollingFromWheelTimerId; 00435 QTimer *m_mouseScrollTimer; 00436 QWidget *m_mouseScrollIndicator; 00437 QPointer<QWidget> m_mouseEventsTarget; 00438 QStack<QRegion>* m_clipHolder; 00439 KHTMLView* view; 00440 }; 00441 00442 #ifndef QT_NO_TOOLTIP 00443 00453 static bool findImageMapRect(HTMLImageElementImpl *img, const QPoint &scrollOfs, 00454 const QPoint &p, QRect &r, QString &s) 00455 { 00456 HTMLMapElementImpl* map; 00457 if (img && img->document()->isHTMLDocument() && 00458 (map = static_cast<HTMLDocumentImpl*>(img->document())->getMap(img->imageMap()))) { 00459 RenderObject::NodeInfo info(true, false); 00460 RenderObject *rend = img->renderer(); 00461 int ax, ay; 00462 if (!rend || !rend->absolutePosition(ax, ay)) 00463 return false; 00464 // we're a client side image map 00465 bool inside = map->mapMouseEvent(p.x() - ax + scrollOfs.x(), 00466 p.y() - ay + scrollOfs.y(), rend->contentWidth(), 00467 rend->contentHeight(), info); 00468 if (inside && info.URLElement()) { 00469 HTMLAreaElementImpl *area = static_cast<HTMLAreaElementImpl *>(info.URLElement()); 00470 Q_ASSERT(area->id() == ID_AREA); 00471 s = area->getAttribute(ATTR_TITLE).string(); 00472 QRegion reg = area->cachedRegion(); 00473 if (!s.isEmpty() && !reg.isEmpty()) { 00474 r = reg.boundingRect(); 00475 r.translate(ax, ay); 00476 return true; 00477 } 00478 } 00479 } 00480 return false; 00481 } 00482 00483 bool KHTMLView::event( QEvent* e ) 00484 { 00485 switch ( e->type() ) { 00486 case QEvent::ToolTip: { 00487 QHelpEvent *he = static_cast<QHelpEvent*>(e); 00488 QPoint p = he->pos(); 00489 00490 DOM::NodeImpl *node = d->underMouseNonShared; 00491 QRect region; 00492 while ( node ) { 00493 if ( node->isElementNode() ) { 00494 DOM::ElementImpl *e = static_cast<DOM::ElementImpl*>( node ); 00495 QRect r; 00496 QString s; 00497 bool found = false; 00498 // for images, check if it is part of a client-side image map, 00499 // and query the <area>s' title attributes, too 00500 if (e->id() == ID_IMG && !e->getAttribute( ATTR_USEMAP ).isEmpty()) { 00501 found = findImageMapRect(static_cast<HTMLImageElementImpl *>(e), 00502 viewportToContents(QPoint(0, 0)), p, r, s); 00503 } 00504 if (!found) { 00505 s = e->getAttribute( ATTR_TITLE ).string(); 00506 r = node->getRect(); 00507 } 00508 region |= QRect( contentsToViewport( r.topLeft() ), r.size() ); 00509 if ( !s.isEmpty() ) { 00510 QToolTip::showText( he->globalPos(), 00511 Qt::convertFromPlainText( s, Qt::WhiteSpaceNormal ), 00512 widget(), region ); 00513 break; 00514 } 00515 } 00516 node = node->parentNode(); 00517 } 00518 // Qt makes tooltip events happen nearly immediately when a preceding one was processed in the past few seconds. 00519 // We don't want that feature to apply to web tootlips however, as it clashes with dhtml menus. 00520 // So we'll just pretend we did not process that event. 00521 return false; 00522 } 00523 00524 case QEvent::DragEnter: 00525 case QEvent::DragMove: 00526 case QEvent::DragLeave: 00527 case QEvent::Drop: 00528 // In Qt4, one needs to both call accept() on the DND event and return 00529 // true on ::event for the candidate widget for the drop to be possible. 00530 // Apps hosting us, such as konq, can do the former but not the later. 00531 // We will do the second bit, as it's a no-op unless someone else explicitly 00532 // accepts the event. We need to skip the scrollarea to do that, 00533 // since it will just skip the events, both killing the drop, and 00534 // not permitting us to forward it up the part hiearchy in our dragEnterEvent, 00535 // etc. handlers 00536 return QWidget::event(e); 00537 case QEvent::StyleChange: 00538 case QEvent::LayoutRequest: { 00539 updateScrollBars(); 00540 return QAbstractScrollArea::event(e); 00541 } 00542 case QEvent::PaletteChange: 00543 slotPaletteChanged(); 00544 return QScrollArea::event(e); 00545 default: 00546 return QScrollArea::event(e); 00547 } 00548 } 00549 #endif 00550 00551 KHTMLView::KHTMLView( KHTMLPart *part, QWidget *parent ) 00552 : QScrollArea( parent ), d( new KHTMLViewPrivate( this ) ) 00553 { 00554 m_medium = "screen"; 00555 00556 m_part = part; 00557 00558 QScrollArea::setVerticalScrollBarPolicy(d->vpolicy); 00559 QScrollArea::setHorizontalScrollBarPolicy(d->hpolicy); 00560 00561 init(); 00562 widget()->setMouseTracking(true); 00563 } 00564 00565 KHTMLView::~KHTMLView() 00566 { 00567 closeChildDialogs(); 00568 if (m_part) 00569 { 00570 DOM::DocumentImpl *doc = m_part->xmlDocImpl(); 00571 if (doc) 00572 doc->detach(); 00573 } 00574 delete d; 00575 } 00576 00577 void KHTMLView::setPart(KHTMLPart *part) 00578 { 00579 assert(part && !m_part); 00580 m_part = part; 00581 } 00582 00583 void KHTMLView::init() 00584 { 00585 // Do not access the part here. It might not be fully constructed. 00586 00587 setFrameStyle(QFrame::NoFrame); 00588 setFocusPolicy(Qt::StrongFocus); 00589 viewport()->setFocusProxy(this); 00590 00591 _marginWidth = -1; // undefined 00592 _marginHeight = -1; 00593 _width = 0; 00594 _height = 0; 00595 00596 installEventFilter(this); 00597 00598 setAcceptDrops(true); 00599 if (!widget()) 00600 setWidget( new QWidget(this) ); 00601 widget()->setAttribute( Qt::WA_NoSystemBackground ); 00602 00603 // Do *not* remove this attribute frivolously. 00604 // You might not notice a change of behaviour in Debug builds 00605 // but removing opaque events will make QWidget::scroll fail horribly 00606 // in Release builds. 00607 widget()->setAttribute( Qt::WA_OpaquePaintEvent ); 00608 00609 verticalScrollBar()->setCursor( Qt::ArrowCursor ); 00610 horizontalScrollBar()->setCursor( Qt::ArrowCursor ); 00611 00612 connect(&d->smoothScrollTimer, SIGNAL(timeout()), this, SLOT(scrollTick())); 00613 } 00614 00615 void KHTMLView::resizeContentsToViewport() 00616 { 00617 QSize s = viewport()->size(); 00618 resizeContents(s.width(), s.height()); 00619 } 00620 00621 00622 // called by KHTMLPart::clear() 00623 void KHTMLView::clear() 00624 { 00625 if (d->accessKeysEnabled && d->accessKeysActivated) 00626 accessKeysTimeout(); 00627 viewport()->unsetCursor(); 00628 if ( d->cursorIconWidget ) 00629 d->cursorIconWidget->hide(); 00630 if (d->smoothScrolling) 00631 d->stopScrolling(); 00632 d->reset(); 00633 QAbstractEventDispatcher::instance()->unregisterTimers(this); 00634 emit cleared(); 00635 00636 QScrollArea::setHorizontalScrollBarPolicy(d->hpolicy); 00637 QScrollArea::setVerticalScrollBarPolicy(d->vpolicy); 00638 verticalScrollBar()->setEnabled( false ); 00639 horizontalScrollBar()->setEnabled( false ); 00640 00641 } 00642 00643 void KHTMLView::hideEvent(QHideEvent* e) 00644 { 00645 QScrollArea::hideEvent(e); 00646 } 00647 00648 void KHTMLView::showEvent(QShowEvent* e) 00649 { 00650 QScrollArea::showEvent(e); 00651 } 00652 00653 void KHTMLView::setMouseEventsTarget( QWidget* w ) 00654 { 00655 d->m_mouseEventsTarget = w; 00656 } 00657 00658 QWidget* KHTMLView::mouseEventsTarget() const 00659 { 00660 return d->m_mouseEventsTarget; 00661 } 00662 00663 void KHTMLView::setClipHolder( QStack<QRegion>* ch ) 00664 { 00665 d->m_clipHolder = ch; 00666 } 00667 00668 QStack<QRegion>* KHTMLView::clipHolder() const 00669 { 00670 return d->m_clipHolder; 00671 } 00672 00673 int KHTMLView::contentsWidth() const 00674 { 00675 return widget() ? widget()->width() : 0; 00676 } 00677 00678 int KHTMLView::contentsHeight() const 00679 { 00680 return widget() ? widget()->height() : 0; 00681 } 00682 00683 void KHTMLView::resizeContents(int w, int h) 00684 { 00685 if (!widget()) 00686 return; 00687 widget()->resize(w, h); 00688 if (!widget()->isVisible()) 00689 updateScrollBars(); 00690 } 00691 00692 int KHTMLView::contentsX() const 00693 { 00694 return d->contentsX; 00695 } 00696 00697 int KHTMLView::contentsY() const 00698 { 00699 return d->contentsY; 00700 } 00701 00702 int KHTMLView::visibleWidth() const 00703 { 00704 if (m_kwp->isRedirected()) { 00705 // our RenderWidget knows better 00706 if (RenderWidget* rw = m_kwp->renderWidget()) { 00707 int ret = rw->width()-rw->paddingLeft()-rw->paddingRight()-rw->borderLeft()-rw->borderRight(); 00708 if (verticalScrollBar()->isVisible()) { 00709 ret -= verticalScrollBar()->sizeHint().width(); 00710 ret = qMax(0, ret); 00711 } 00712 return ret; 00713 } 00714 } 00715 return viewport()->width(); 00716 } 00717 00718 int KHTMLView::visibleHeight() const 00719 { 00720 if (m_kwp->isRedirected()) { 00721 // our RenderWidget knows better 00722 if (RenderWidget* rw = m_kwp->renderWidget()) { 00723 int ret = rw->height()-rw->paddingBottom()-rw->paddingTop()-rw->borderTop()-rw->borderBottom(); 00724 if (horizontalScrollBar()->isVisible()) { 00725 ret -= horizontalScrollBar()->sizeHint().height(); 00726 ret = qMax(0, ret); 00727 } 00728 return ret; 00729 } 00730 } 00731 return viewport()->height(); 00732 } 00733 00734 void KHTMLView::setContentsPos( int x, int y) 00735 { 00736 horizontalScrollBar()->setValue( QApplication::isRightToLeft() ? 00737 horizontalScrollBar()->maximum()-x : x ); 00738 verticalScrollBar()->setValue( y ); 00739 } 00740 00741 void KHTMLView::scrollBy(int x, int y) 00742 { 00743 if (d->scrollTimerId) 00744 d->newScrollTimer(this, 0); 00745 horizontalScrollBar()->setValue( horizontalScrollBar()->value()+x ); 00746 verticalScrollBar()->setValue( verticalScrollBar()->value()+y ); 00747 } 00748 00749 QPoint KHTMLView::contentsToViewport(const QPoint& p) const 00750 { 00751 return QPoint(p.x()-contentsX(), p.y()-contentsY()); 00752 } 00753 00754 void KHTMLView::contentsToViewport(int x, int y, int& cx, int& cy) const 00755 { 00756 QPoint p(x,y); 00757 p = contentsToViewport(p); 00758 cx = p.x(); 00759 cy = p.y(); 00760 } 00761 00762 QPoint KHTMLView::viewportToContents(const QPoint& p) const 00763 { 00764 return QPoint(p.x()+contentsX(), p.y()+contentsY()); 00765 } 00766 00767 void KHTMLView::viewportToContents(int x, int y, int& cx, int& cy) const 00768 { 00769 QPoint p(x,y); 00770 p = viewportToContents(p); 00771 cx = p.x(); 00772 cy = p.y(); 00773 } 00774 00775 void KHTMLView::updateContents(int x, int y, int w, int h) 00776 { 00777 applyTransforms(x, y, w, h); 00778 if (m_kwp->isRedirected()) { 00779 QPoint off = m_kwp->absolutePos(); 00780 KHTMLView* pview = m_part->parentPart()->view(); 00781 pview->updateContents(x+off.x(), y+off.y(), w, h); 00782 } else 00783 widget()->update(x, y, w, h); 00784 } 00785 00786 void KHTMLView::updateContents( const QRect& r ) 00787 { 00788 updateContents( r.x(), r.y(), r.width(), r.height() ); 00789 } 00790 00791 void KHTMLView::repaintContents(int x, int y, int w, int h) 00792 { 00793 applyTransforms(x, y, w, h); 00794 if (m_kwp->isRedirected()) { 00795 QPoint off = m_kwp->absolutePos(); 00796 KHTMLView* pview = m_part->parentPart()->view(); 00797 pview->repaintContents(x+off.x(), y+off.y(), w, h); 00798 } else 00799 widget()->repaint(x, y, w, h); 00800 } 00801 00802 void KHTMLView::repaintContents( const QRect& r ) 00803 { 00804 repaintContents( r.x(), r.y(), r.width(), r.height() ); 00805 } 00806 00807 void KHTMLView::applyTransforms( int& x, int& y, int& w, int& h) const 00808 { 00809 if (d->haveZoom()) { 00810 const int z = d->zoomLevel; 00811 x = x*z/100; 00812 y = y*z/100; 00813 w = w*z/100; 00814 h = h*z/100; 00815 } 00816 x -= contentsX(); 00817 y -= contentsY(); 00818 } 00819 00820 void KHTMLView::revertTransforms( int& x, int& y, int& w, int& h) const 00821 { 00822 x += contentsX(); 00823 y += contentsY(); 00824 if (d->haveZoom()) { 00825 const int z = d->zoomLevel; 00826 x = x*100/z; 00827 y = y*100/z; 00828 w = w*100/z; 00829 h = h*100/z; 00830 } 00831 } 00832 00833 void KHTMLView::revertTransforms( int& x, int& y ) const 00834 { 00835 int dummy = 0; 00836 revertTransforms(x, y, dummy, dummy); 00837 } 00838 00839 void KHTMLView::resizeEvent (QResizeEvent* /*e*/) 00840 { 00841 updateScrollBars(); 00842 00843 // If we didn't load anything, make white area as big as the view 00844 if (!m_part->xmlDocImpl()) 00845 resizeContentsToViewport(); 00846 00847 // Viewport-dependent media queries may cause us to need completely different style information. 00848 if (m_part->xmlDocImpl() && m_part->xmlDocImpl()->styleSelector()->affectedByViewportChange()) { 00849 m_part->xmlDocImpl()->updateStyleSelector(); 00850 } 00851 00852 if (d->layoutSchedulingEnabled) 00853 layout(); 00854 00855 QApplication::sendPostedEvents(viewport(), QEvent::Paint); 00856 00857 if ( m_part && m_part->xmlDocImpl() ) { 00858 if (m_part->parentPart()) { 00859 // sub-frame : queue the resize event until our toplevel is done layouting 00860 khtml::ChildFrame *cf = m_part->parentPart()->frame( m_part ); 00861 if (cf && !cf->m_partContainerElement.isNull()) 00862 cf->m_partContainerElement.data()->postResizeEvent(); 00863 } else { 00864 // toplevel : dispatch sub-frames'resize events before our own 00865 HTMLPartContainerElementImpl::sendPostedResizeEvents(); 00866 m_part->xmlDocImpl()->dispatchWindowEvent( EventImpl::RESIZE_EVENT, false, false ); 00867 } 00868 } 00869 } 00870 00871 void KHTMLView::paintEvent( QPaintEvent *e ) 00872 { 00873 QRect r = e->rect(); 00874 QRect v(contentsX(), contentsY(), visibleWidth(), visibleHeight()); 00875 QPoint off(contentsX(),contentsY()); 00876 r.translate(off); 00877 r = r.intersect(v); 00878 if (!r.isValid() || r.isEmpty()) return; 00879 00880 QPainter p(widget()); 00881 p.translate(-off); 00882 00883 if (d->haveZoom()) { 00884 p.scale( d->zoomLevel/100., d->zoomLevel/100.); 00885 00886 r.setX(r.x()*100/d->zoomLevel); 00887 r.setY(r.y()*100/d->zoomLevel); 00888 r.setWidth(r.width()*100/d->zoomLevel); 00889 r.setHeight(r.height()*100/d->zoomLevel); 00890 r.adjust(-1,-1,1,1); 00891 } 00892 p.setClipRect(r); 00893 00894 int ex = r.x(); 00895 int ey = r.y(); 00896 int ew = r.width(); 00897 int eh = r.height(); 00898 00899 if(!m_part || !m_part->xmlDocImpl() || !m_part->xmlDocImpl()->renderer()) { 00900 p.fillRect(ex, ey, ew, eh, palette().brush(QPalette::Active, QPalette::Base)); 00901 return; 00902 } else if ( d->complete && static_cast<RenderCanvas*>(m_part->xmlDocImpl()->renderer())->needsLayout() ) { 00903 // an external update request happens while we have a layout scheduled 00904 unscheduleRelayout(); 00905 layout(); 00906 } else if (m_part->xmlDocImpl()->tokenizer()) { 00907 m_part->xmlDocImpl()->tokenizer()->setNormalYieldDelay(); 00908 } 00909 00910 if (d->painting) { 00911 kDebug( 6000 ) << "WARNING: paintEvent reentered! "; 00912 kDebug( 6000 ) << kBacktrace(); 00913 return; 00914 } 00915 d->painting = true; 00916 00917 m_part->xmlDocImpl()->renderer()->layer()->paint(&p, r); 00918 00919 if (d->hasFrameset) { 00920 NodeImpl *body = static_cast<HTMLDocumentImpl*>(m_part->xmlDocImpl())->body(); 00921 if(body && body->renderer() && body->id() == ID_FRAMESET) 00922 static_cast<RenderFrameSet*>(body->renderer())->paintFrameSetRules(&p, r); 00923 else 00924 d->hasFrameset = false; 00925 } 00926 00927 khtml::DrawContentsEvent event( &p, ex, ey, ew, eh ); 00928 QApplication::sendEvent( m_part, &event ); 00929 00930 if (d->contentsMoving && !d->smoothScrolling && widget()->underMouse()) { 00931 QMouseEvent *tempEvent = new QMouseEvent( QEvent::MouseMove, widget()->mapFromGlobal( QCursor::pos() ), 00932 Qt::NoButton, Qt::NoButton, Qt::NoModifier ); 00933 QApplication::postEvent(widget(), tempEvent); 00934 } 00935 #ifdef SPEED_DEBUG 00936 if (d->firstRepaintPending && !m_part->parentPart()) { 00937 kDebug(6080) << "FIRST PAINT:" << m_part->d->m_parsetime.elapsed(); 00938 } 00939 d->firstRepaintPending = false; 00940 #endif 00941 d->painting = false; 00942 } 00943 00944 void KHTMLView::setMarginWidth(int w) 00945 { 00946 // make it update the rendering area when set 00947 _marginWidth = w; 00948 } 00949 00950 void KHTMLView::setMarginHeight(int h) 00951 { 00952 // make it update the rendering area when set 00953 _marginHeight = h; 00954 } 00955 00956 void KHTMLView::layout() 00957 { 00958 if( m_part && m_part->xmlDocImpl() ) { 00959 DOM::DocumentImpl *document = m_part->xmlDocImpl(); 00960 00961 khtml::RenderCanvas* canvas = static_cast<khtml::RenderCanvas *>(document->renderer()); 00962 if ( !canvas ) return; 00963 00964 d->layoutSchedulingEnabled=false; 00965 d->dirtyLayout = true; 00966 00967 // the reference object for the overflow property on canvas 00968 RenderObject * ref = 0; 00969 RenderObject* root = document->documentElement() ? document->documentElement()->renderer() : 0; 00970 00971 if (document->isHTMLDocument()) { 00972 NodeImpl *body = static_cast<HTMLDocumentImpl*>(document)->body(); 00973 if(body && body->renderer() && body->id() == ID_FRAMESET) { 00974 QScrollArea::setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); 00975 QScrollArea::setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); 00976 body->renderer()->setNeedsLayout(true); 00977 d->hasFrameset = true; 00978 } 00979 else if (root) // only apply body's overflow to canvas if root has a visible overflow 00980 ref = (!body || root->style()->hidesOverflow()) ? root : body->renderer(); 00981 } else { 00982 ref = root; 00983 } 00984 if (ref) { 00985 if( ref->style()->overflowX() == OHIDDEN ) { 00986 if (d->hpolicy == Qt::ScrollBarAsNeeded) QScrollArea::setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); 00987 } else if (ref->style()->overflowX() == OSCROLL ) { 00988 if (d->hpolicy == Qt::ScrollBarAsNeeded) QScrollArea::setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn); 00989 } else if (horizontalScrollBarPolicy() != d->hpolicy) { 00990 QScrollArea::setHorizontalScrollBarPolicy(d->hpolicy); 00991 } 00992 if ( ref->style()->overflowY() == OHIDDEN ) { 00993 if (d->vpolicy == Qt::ScrollBarAsNeeded) QScrollArea::setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); 00994 } else if (ref->style()->overflowY() == OSCROLL ) { 00995 if (d->vpolicy == Qt::ScrollBarAsNeeded) QScrollArea::setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn); 00996 } else if (verticalScrollBarPolicy() != d->vpolicy) { 00997 QScrollArea::setVerticalScrollBarPolicy(d->vpolicy); 00998 } 00999 } 01000 d->needsFullRepaint = d->firstLayoutPending; 01001 if (_height != visibleHeight() || _width != visibleWidth()) {; 01002 d->needsFullRepaint = true; 01003 _height = visibleHeight(); 01004 _width = visibleWidth(); 01005 } 01006 01007 canvas->layout(); 01008 01009 emit finishedLayout(); 01010 if (d->firstLayoutPending) { 01011 // make sure firstLayoutPending is set to false now in case this layout 01012 // wasn't scheduled 01013 d->firstLayoutPending = false; 01014 verticalScrollBar()->setEnabled( true ); 01015 horizontalScrollBar()->setEnabled( true ); 01016 } 01017 d->layoutCounter++; 01018 01019 if (d->accessKeysEnabled && d->accessKeysActivated) { 01020 emit hideAccessKeys(); 01021 displayAccessKeys(); 01022 } 01023 } 01024 else 01025 _width = visibleWidth(); 01026 01027 if (d->layoutTimerId) 01028 killTimer(d->layoutTimerId); 01029 d->layoutTimerId = 0; 01030 d->layoutSchedulingEnabled=true; 01031 } 01032 01033 void KHTMLView::closeChildDialogs() 01034 { 01035 QList<QDialog *> dlgs = findChildren<QDialog *>(); 01036 foreach (QDialog *dlg, dlgs) 01037 { 01038 KDialog* dlgbase = dynamic_cast<KDialog*>( dlg ); 01039 if ( dlgbase ) { 01040 if ( dlgbase->testAttribute( Qt::WA_ShowModal ) ) { 01041 kDebug(6000) << "closeChildDialogs: closing dialog " << dlgbase; 01042 // close() ends up calling QButton::animateClick, which isn't immediate 01043 // we need something the exits the event loop immediately (#49068) 01044 dlgbase->reject(); 01045 } 01046 } 01047 else 01048 { 01049 kWarning() << "closeChildDialogs: not a KDialog! Don't use QDialogs in KDE! " << static_cast<QWidget*>(dlg); 01050 static_cast<QWidget*>(dlg)->hide(); 01051 } 01052 } 01053 d->m_dialogsAllowed = false; 01054 } 01055 01056 bool KHTMLView::dialogsAllowed() { 01057 bool allowed = d->m_dialogsAllowed; 01058 KHTMLPart* p = m_part->parentPart(); 01059 if (p && p->view()) 01060 allowed &= p->view()->dialogsAllowed(); 01061 return allowed; 01062 } 01063 01064 void KHTMLView::closeEvent( QCloseEvent* ev ) 01065 { 01066 closeChildDialogs(); 01067 QScrollArea::closeEvent( ev ); 01068 } 01069 01070 void KHTMLView::setZoomLevel(int percent) 01071 { 01072 percent = percent < 20 ? 20 : (percent > 800 ? 800 : percent); 01073 int oldpercent = d->zoomLevel; 01074 d->zoomLevel = percent; 01075 if (percent != oldpercent) { 01076 if (d->layoutSchedulingEnabled) 01077 layout(); 01078 widget()->update(); 01079 } 01080 } 01081 01082 int KHTMLView::zoomLevel() const 01083 { 01084 return d->zoomLevel; 01085 } 01086 01087 void KHTMLView::setSmoothScrollingMode( SmoothScrollingMode m ) 01088 { 01089 d->smoothScrollMode = m; 01090 d->smoothScrollModeIsDefault = false; 01091 if (d->smoothScrolling && !m) 01092 d->stopScrolling(); 01093 } 01094 01095 void KHTMLView::setSmoothScrollingModeDefault( SmoothScrollingMode m ) 01096 { 01097 // check for manual override 01098 if (!d->smoothScrollModeIsDefault) 01099 return; 01100 d->smoothScrollMode = m; 01101 if (d->smoothScrolling && !m) 01102 d->stopScrolling(); 01103 } 01104 01105 KHTMLView::SmoothScrollingMode KHTMLView::smoothScrollingMode( ) const 01106 { 01107 return d->smoothScrollMode; 01108 } 01109 01110 // 01111 // Event Handling 01112 // 01114 01115 void KHTMLView::mousePressEvent( QMouseEvent *_mouse ) 01116 { 01117 if (!m_part->xmlDocImpl()) return; 01118 if (d->possibleTripleClick && ( _mouse->button() & Qt::MouseButtonMask ) == Qt::LeftButton) 01119 { 01120 mouseDoubleClickEvent( _mouse ); // it handles triple clicks too 01121 return; 01122 } 01123 01124 int xm = _mouse->x(); 01125 int ym = _mouse->y(); 01126 revertTransforms(xm, ym); 01127 01128 // kDebug( 6000 ) << "mousePressEvent: viewport=("<<_mouse->x()-contentsX()<<"/"<<_mouse->y()-contentsY()<<"), contents=(" << xm << "/" << ym << ")\n"; 01129 01130 d->isDoubleClick = false; 01131 01132 DOM::NodeImpl::MouseEvent mev( _mouse->buttons(), DOM::NodeImpl::MousePress ); 01133 m_part->xmlDocImpl()->prepareMouseEvent( false, xm, ym, &mev ); 01134 01135 //kDebug(6000) << "innerNode="<<mev.innerNode.nodeName().string(); 01136 01137 if ( (_mouse->button() == Qt::MidButton) && 01138 !m_part->d->m_bOpenMiddleClick && !d->m_mouseScrollTimer && 01139 mev.url.isNull() && (mev.innerNode.elementId() != ID_INPUT) ) { 01140 QPoint point = mapFromGlobal( _mouse->globalPos() ); 01141 01142 d->m_mouseScroll_byX = 0; 01143 d->m_mouseScroll_byY = 0; 01144 01145 d->m_mouseScrollTimer = new QTimer( this ); 01146 connect( d->m_mouseScrollTimer, SIGNAL(timeout()), this, SLOT(slotMouseScrollTimer()) ); 01147 01148 if ( !d->m_mouseScrollIndicator ) { 01149 QPixmap pixmap( 48, 48 ), icon; 01150 pixmap.fill( QColor( qRgba( 127, 127, 127, 127 ) ) ); 01151 01152 QPainter p( &pixmap ); 01153 QStyleOption option; 01154 01155 option.rect.setRect( 16, 0, 16, 16 ); 01156 QApplication::style()->drawPrimitive( QStyle::PE_IndicatorArrowUp, &option, &p ); 01157 option.rect.setRect( 0, 16, 16, 16 ); 01158 QApplication::style()->drawPrimitive( QStyle::PE_IndicatorArrowLeft, &option, &p ); 01159 option.rect.setRect( 16, 32, 16, 16 ); 01160 QApplication::style()->drawPrimitive( QStyle::PE_IndicatorArrowDown, &option, &p ); 01161 option.rect.setRect( 32, 16, 16, 16 ); 01162 QApplication::style()->drawPrimitive( QStyle::PE_IndicatorArrowRight, &option, &p ); 01163 p.drawEllipse( 23, 23, 2, 2 ); 01164 01165 d->m_mouseScrollIndicator = new QWidget( this ); 01166 d->m_mouseScrollIndicator->setFixedSize( 48, 48 ); 01167 QPalette palette; 01168 palette.setBrush( d->m_mouseScrollIndicator->backgroundRole(), QBrush( pixmap ) ); 01169 d->m_mouseScrollIndicator->setPalette( palette ); 01170 } 01171 d->m_mouseScrollIndicator->move( point.x()-24, point.y()-24 ); 01172 01173 bool hasHorBar = visibleWidth() < contentsWidth(); 01174 bool hasVerBar = visibleHeight() < contentsHeight(); 01175 01176 KConfigGroup cg( KGlobal::config(), "HTML Settings" ); 01177 if ( cg.readEntry( "ShowMouseScrollIndicator", true ) ) { 01178 d->m_mouseScrollIndicator->show(); 01179 d->m_mouseScrollIndicator->unsetCursor(); 01180 01181 QBitmap mask = d->m_mouseScrollIndicator->palette().brush(d->m_mouseScrollIndicator->backgroundRole()).texture().createHeuristicMask( true ); 01182 01183 if ( hasHorBar && !hasVerBar ) { 01184 QBitmap bm( 16, 16 ); 01185 bm.clear(); 01186 QPainter painter( &mask ); 01187 painter.drawPixmap( QRectF( 16, 0, bm.width(), bm.height() ), bm, bm.rect() ); 01188 painter.drawPixmap( QRectF( 16, 32, bm.width(), bm.height() ), bm, bm.rect() ); 01189 d->m_mouseScrollIndicator->setCursor( Qt::SizeHorCursor ); 01190 } 01191 else if ( !hasHorBar && hasVerBar ) { 01192 QBitmap bm( 16, 16 ); 01193 bm.clear(); 01194 QPainter painter( &mask ); 01195 painter.drawPixmap( QRectF( 0, 16, bm.width(), bm.height() ), bm, bm.rect() ); 01196 painter.drawPixmap( QRectF( 32, 16, bm.width(), bm.height() ), bm, bm.rect() ); 01197 d->m_mouseScrollIndicator->setCursor( Qt::SizeVerCursor ); 01198 } 01199 else 01200 d->m_mouseScrollIndicator->setCursor( Qt::SizeAllCursor ); 01201 01202 d->m_mouseScrollIndicator->setMask( mask ); 01203 } 01204 else { 01205 if ( hasHorBar && !hasVerBar ) 01206 viewport()->setCursor( Qt::SizeHorCursor ); 01207 else if ( !hasHorBar && hasVerBar ) 01208 viewport()->setCursor( Qt::SizeVerCursor ); 01209 else 01210 viewport()->setCursor( Qt::SizeAllCursor ); 01211 } 01212 01213 return; 01214 } 01215 else if ( d->m_mouseScrollTimer ) { 01216 delete d->m_mouseScrollTimer; 01217 d->m_mouseScrollTimer = 0; 01218 01219 if ( d->m_mouseScrollIndicator ) 01220 d->m_mouseScrollIndicator->hide(); 01221 } 01222 01223 if (d->clickCount > 0 && 01224 QPoint(d->clickX-xm,d->clickY-ym).manhattanLength() <= QApplication::startDragDistance()) 01225 d->clickCount++; 01226 else { 01227 d->clickCount = 1; 01228 d->clickX = xm; 01229 d->clickY = ym; 01230 } 01231 01232 bool swallowEvent = dispatchMouseEvent(EventImpl::MOUSEDOWN_EVENT,mev.innerNode.handle(),mev.innerNonSharedNode.handle(),true, 01233 d->clickCount,_mouse,true,DOM::NodeImpl::MousePress); 01234 01235 if (!swallowEvent) { 01236 emit m_part->nodeActivated(mev.innerNode); 01237 01238 khtml::MousePressEvent event( _mouse, xm, ym, mev.url, mev.target, mev.innerNode ); 01239 QApplication::sendEvent( m_part, &event ); 01240 // we might be deleted after this 01241 } 01242 } 01243 01244 void KHTMLView::mouseDoubleClickEvent( QMouseEvent *_mouse ) 01245 { 01246 if(!m_part->xmlDocImpl()) return; 01247 01248 int xm = _mouse->x(); 01249 int ym = _mouse->y(); 01250 revertTransforms(xm, ym); 01251 01252 // kDebug( 6000 ) << "mouseDblClickEvent: x=" << xm << ", y=" << ym; 01253 01254 d->isDoubleClick = true; 01255 01256 DOM::NodeImpl::MouseEvent mev( _mouse->buttons(), DOM::NodeImpl::MouseDblClick ); 01257 m_part->xmlDocImpl()->prepareMouseEvent( false, xm, ym, &mev ); 01258 01259 // We do the same thing as mousePressEvent() here, since the DOM does not treat 01260 // single and double-click events as separate (only the detail, i.e. number of clicks differs) 01261 if (d->clickCount > 0 && 01262 QPoint(d->clickX-xm,d->clickY-ym).manhattanLength() <= QApplication::startDragDistance()) 01263 d->clickCount++; 01264 else { // shouldn't happen, if Qt has the same criterias for double clicks. 01265 d->clickCount = 1; 01266 d->clickX = xm; 01267 d->clickY = ym; 01268 } 01269 bool swallowEvent = dispatchMouseEvent(EventImpl::MOUSEDOWN_EVENT,mev.innerNode.handle(),mev.innerNonSharedNode.handle(),true, 01270 d->clickCount,_mouse,true,DOM::NodeImpl::MouseDblClick); 01271 01272 if (!swallowEvent) { 01273 khtml::MouseDoubleClickEvent event( _mouse, xm, ym, mev.url, mev.target, mev.innerNode, d->clickCount ); 01274 QApplication::sendEvent( m_part, &event ); 01275 } 01276 01277 d->possibleTripleClick=true; 01278 QTimer::singleShot(QApplication::doubleClickInterval(),this,SLOT(tripleClickTimeout())); 01279 } 01280 01281 void KHTMLView::tripleClickTimeout() 01282 { 01283 d->possibleTripleClick = false; 01284 d->clickCount = 0; 01285 } 01286 01287 static bool targetOpensNewWindow(KHTMLPart *part, QString target) 01288 { 01289 if (!target.isEmpty() && (target.toLower() != "_top") && 01290 (target.toLower() != "_self") && (target.toLower() != "_parent")) { 01291 if (target.toLower() == "_blank") 01292 return true; 01293 else { 01294 while (part->parentPart()) 01295 part = part->parentPart(); 01296 if (!part->frameExists(target)) 01297 return true; 01298 } 01299 } 01300 return false; 01301 } 01302 01303 void KHTMLView::mouseMoveEvent( QMouseEvent * _mouse ) 01304 { 01305 if ( d->m_mouseScrollTimer ) { 01306 QPoint point = mapFromGlobal( _mouse->globalPos() ); 01307 01308 int deltaX = point.x() - d->m_mouseScrollIndicator->x() - 24; 01309 int deltaY = point.y() - d->m_mouseScrollIndicator->y() - 24; 01310 01311 (deltaX > 0) ? d->m_mouseScroll_byX = 1 : d->m_mouseScroll_byX = -1; 01312 (deltaY > 0) ? d->m_mouseScroll_byY = 1 : d->m_mouseScroll_byY = -1; 01313 01314 double adX = qAbs(deltaX)/30.0; 01315 double adY = qAbs(deltaY)/30.0; 01316 01317 d->m_mouseScroll_byX = qMax(qMin(d->m_mouseScroll_byX * int(adX*adX), SHRT_MAX), SHRT_MIN); 01318 d->m_mouseScroll_byY = qMax(qMin(d->m_mouseScroll_byY * int(adY*adY), SHRT_MAX), SHRT_MIN); 01319 01320 if (d->m_mouseScroll_byX == 0 && d->m_mouseScroll_byY == 0) { 01321 d->m_mouseScrollTimer->stop(); 01322 } 01323 else if (!d->m_mouseScrollTimer->isActive()) { 01324 d->m_mouseScrollTimer->start( 20 ); 01325 } 01326 } 01327 01328 if(!m_part->xmlDocImpl()) return; 01329 01330 int xm = _mouse->x(); 01331 int ym = _mouse->y(); 01332 revertTransforms(xm, ym); 01333 01334 DOM::NodeImpl::MouseEvent mev( _mouse->buttons(), DOM::NodeImpl::MouseMove ); 01335 // Do not modify :hover/:active state while mouse is pressed. 01336 m_part->xmlDocImpl()->prepareMouseEvent( _mouse->buttons() /*readonly ?*/, xm, ym, &mev ); 01337 01338 // kDebug(6000) << "mouse move: " << _mouse->pos() 01339 // << " button " << _mouse->button() 01340 // << " state " << _mouse->state() << endl; 01341 01342 DOM::NodeImpl* target = mev.innerNode.handle(); 01343 DOM::NodeImpl* fn = m_part->xmlDocImpl()->focusNode(); 01344 01345 // a widget may be the real target of this event (e.g. if a scrollbar's slider is being moved) 01346 if (d->m_mouseEventsTarget && fn && fn->renderer() && fn->renderer()->isWidget()) 01347 target = fn; 01348 01349 bool swallowEvent = dispatchMouseEvent(EventImpl::MOUSEMOVE_EVENT,target,mev.innerNonSharedNode.handle(),false, 01350 0,_mouse,true,DOM::NodeImpl::MouseMove); 01351 01352 if (d->clickCount > 0 && 01353 QPoint(d->clickX-xm,d->clickY-ym).manhattanLength() > QApplication::startDragDistance()) { 01354 d->clickCount = 0; // moving the mouse outside the threshold invalidates the click 01355 } 01356 01357 khtml::RenderObject* r = target ? target->renderer() : 0; 01358 bool setCursor = true; 01359 bool forceDefault = false; 01360 if (r && r->isWidget()) { 01361 RenderWidget* rw = static_cast<RenderWidget*>(r); 01362 KHTMLWidget* kw = qobject_cast<KHTMLView*>(rw->widget())? dynamic_cast<KHTMLWidget*>(rw->widget()) : 0; 01363 if (kw && kw->m_kwp->isRedirected()) 01364 setCursor = false; 01365 else if (QLineEdit* le = qobject_cast<QLineEdit*>(rw->widget())) { 01366 QList<QWidget*> wl = qFindChildren<QWidget *>( le, "KLineEditButton" ); 01367 // force arrow cursor above lineedit clear button 01368 foreach (QWidget*w, wl) { 01369 if (w->underMouse()) { 01370 forceDefault = true; 01371 break; 01372 } 01373 } 01374 } 01375 else if (QTextEdit* te = qobject_cast<QTextEdit*>(rw->widget())) { 01376 if (te->verticalScrollBar()->underMouse() || te->horizontalScrollBar()->underMouse()) 01377 forceDefault = true; 01378 } 01379 } 01380 khtml::RenderStyle* style = (r && r->style()) ? r->style() : 0; 01381 QCursor c; 01382 LinkCursor linkCursor = LINK_NORMAL; 01383 switch (!forceDefault ? (style ? style->cursor() : CURSOR_AUTO) : CURSOR_DEFAULT) { 01384 case CURSOR_AUTO: 01385 if ( r && r->isText() && ((m_part->d->m_bMousePressed && m_part->d->editor_context.m_beganSelectingText) || 01386 !r->isPointInsideSelection(xm, ym, m_part->caret())) ) 01387 c = QCursor(Qt::IBeamCursor); 01388 if ( mev.url.length() && m_part->settings()->changeCursor() ) { 01389 c = m_part->urlCursor(); 01390 if (mev.url.string().startsWith("mailto:") && mev.url.string().indexOf('@')>0) 01391 linkCursor = LINK_MAILTO; 01392 else 01393 if ( targetOpensNewWindow( m_part, mev.target.string() ) ) 01394 linkCursor = LINK_NEWWINDOW; 01395 } 01396 01397 if (r && r->isFrameSet() && !static_cast<RenderFrameSet*>(r)->noResize()) 01398 c = QCursor(static_cast<RenderFrameSet*>(r)->cursorShape()); 01399 01400 break; 01401 case CURSOR_CROSS: 01402 c = QCursor(Qt::CrossCursor); 01403 break; 01404 case CURSOR_POINTER: 01405 c = m_part->urlCursor(); 01406 if (mev.url.string().startsWith("mailto:") && mev.url.string().indexOf('@')>0) 01407 linkCursor = LINK_MAILTO; 01408 else 01409 if ( targetOpensNewWindow( m_part, mev.target.string() ) ) 01410 linkCursor = LINK_NEWWINDOW; 01411 break; 01412 case CURSOR_PROGRESS: 01413 c = QCursor(Qt::BusyCursor); // working_cursor 01414 break; 01415 case CURSOR_MOVE: 01416 case CURSOR_ALL_SCROLL: 01417 c = QCursor(Qt::SizeAllCursor); 01418 break; 01419 case CURSOR_E_RESIZE: 01420 case CURSOR_W_RESIZE: 01421 case CURSOR_EW_RESIZE: 01422 c = QCursor(Qt::SizeHorCursor); 01423 break; 01424 case CURSOR_N_RESIZE: 01425 case CURSOR_S_RESIZE: 01426 case CURSOR_NS_RESIZE: 01427 c = QCursor(Qt::SizeVerCursor); 01428 break; 01429 case CURSOR_NE_RESIZE: 01430 case CURSOR_SW_RESIZE: 01431 case CURSOR_NESW_RESIZE: 01432 c = QCursor(Qt::SizeBDiagCursor); 01433 break; 01434 case CURSOR_NW_RESIZE: 01435 case CURSOR_SE_RESIZE: 01436 case CURSOR_NWSE_RESIZE: 01437 c = QCursor(Qt::SizeFDiagCursor); 01438 break; 01439 case CURSOR_TEXT: 01440 c = QCursor(Qt::IBeamCursor); 01441 break; 01442 case CURSOR_WAIT: 01443 c = QCursor(Qt::WaitCursor); 01444 break; 01445 case CURSOR_HELP: 01446 c = QCursor(Qt::WhatsThisCursor); 01447 break; 01448 case CURSOR_DEFAULT: 01449 break; 01450 case CURSOR_NONE: 01451 case CURSOR_NOT_ALLOWED: 01452 c = QCursor(Qt::ForbiddenCursor); 01453 break; 01454 case CURSOR_ROW_RESIZE: 01455 c = QCursor(Qt::SplitVCursor); 01456 break; 01457 case CURSOR_COL_RESIZE: 01458 c = QCursor(Qt::SplitHCursor); 01459 break; 01460 case CURSOR_VERTICAL_TEXT: 01461 case CURSOR_CONTEXT_MENU: 01462 case CURSOR_NO_DROP: 01463 case CURSOR_CELL: 01464 case CURSOR_COPY: 01465 case CURSOR_ALIAS: 01466 c = QCursor(Qt::ArrowCursor); 01467 break; 01468 } 01469 01470 if (!setCursor && style && style->cursor() != CURSOR_AUTO) 01471 setCursor = true; 01472 01473 QWidget* vp = viewport(); 01474 for (KHTMLPart* p = m_part; p; p = p->parentPart()) 01475 if (!p->parentPart()) 01476 vp = p->view()->viewport(); 01477 if ( setCursor && vp->cursor().handle() != c.handle() ) { 01478 if( c.shape() == Qt::ArrowCursor) { 01479 for (KHTMLPart* p = m_part; p; p = p->parentPart()) 01480 p->view()->viewport()->unsetCursor(); 01481 } 01482 else { 01483 vp->setCursor( c ); 01484 } 01485 } 01486 01487 if ( linkCursor!=LINK_NORMAL && isVisible() && hasFocus() ) { 01488 #ifdef Q_WS_X11 01489 01490 if( !d->cursorIconWidget ) { 01491 #ifdef Q_WS_X11 01492 d->cursorIconWidget = new QLabel( 0, Qt::X11BypassWindowManagerHint ); 01493 XSetWindowAttributes attr; 01494 attr.save_under = True; 01495 XChangeWindowAttributes( QX11Info::display(), d->cursorIconWidget->winId(), CWSaveUnder, &attr ); 01496 #else 01497 d->cursorIconWidget = new QLabel( NULL, NULL ); 01498 //TODO 01499 #endif 01500 } 01501 01502 // Update the pixmap if need be. 01503 if (linkCursor != d->cursorIconType) { 01504 d->cursorIconType = linkCursor; 01505 QString cursorIcon; 01506 switch (linkCursor) 01507 { 01508 case LINK_MAILTO: cursorIcon = "mail-message-new"; break; 01509 case LINK_NEWWINDOW: cursorIcon = "window-new"; break; 01510 default: cursorIcon = "dialog-error"; break; 01511 } 01512 01513 QPixmap icon_pixmap = KHTMLGlobal::iconLoader()->loadIcon( cursorIcon, KIconLoader::Small, 0, KIconLoader::DefaultState, QStringList(), 0, true ); 01514 01515 d->cursorIconWidget->resize( icon_pixmap.width(), icon_pixmap.height()); 01516 d->cursorIconWidget->setMask( icon_pixmap.createMaskFromColor(Qt::transparent)); 01517 d->cursorIconWidget->setPixmap( icon_pixmap); 01518 d->cursorIconWidget->update(); 01519 } 01520 01521 QPoint c_pos = QCursor::pos(); 01522 d->cursorIconWidget->move( c_pos.x() + 15, c_pos.y() + 15 ); 01523 #ifdef Q_WS_X11 01524 XRaiseWindow( QX11Info::display(), d->cursorIconWidget->winId()); 01525 QApplication::flush(); 01526 #elif defined(Q_WS_WIN) 01527 SetWindowPos( d->cursorIconWidget->winId(), HWND_TOP, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE ); 01528 #else 01529 //TODO? 01530 #endif 01531 d->cursorIconWidget->show(); 01532 #endif 01533 } 01534 else if ( d->cursorIconWidget ) 01535 d->cursorIconWidget->hide(); 01536 01537 if (r && r->isWidget()) { 01538 _mouse->ignore(); 01539 } 01540 01541 if (!swallowEvent) { 01542 khtml::MouseMoveEvent event( _mouse, xm, ym, mev.url, mev.target, mev.innerNode ); 01543 QApplication::sendEvent( m_part, &event ); 01544 } 01545 } 01546 01547 void KHTMLView::mouseReleaseEvent( QMouseEvent * _mouse ) 01548 { 01549 bool swallowEvent = false; 01550 01551 int xm = _mouse->x(); 01552 int ym = _mouse->y(); 01553 revertTransforms(xm, ym); 01554 01555 DOM::NodeImpl::MouseEvent mev( _mouse->buttons(), DOM::NodeImpl::MouseRelease ); 01556 01557 if ( m_part->xmlDocImpl() ) 01558 { 01559 m_part->xmlDocImpl()->prepareMouseEvent( false, xm, ym, &mev ); 01560 01561 DOM::NodeImpl* target = mev.innerNode.handle(); 01562 DOM::NodeImpl* fn = m_part->xmlDocImpl()->focusNode(); 01563 01564 // a widget may be the real target of this event (e.g. if a scrollbar's slider is being moved) 01565 if (d->m_mouseEventsTarget && fn && fn->renderer() && fn->renderer()->isWidget()) 01566 target = fn; 01567 01568 swallowEvent = dispatchMouseEvent(EventImpl::MOUSEUP_EVENT,target,mev.innerNonSharedNode.handle(),true, 01569 d->clickCount,_mouse,false,DOM::NodeImpl::MouseRelease); 01570 01571 // clear our sticky event target on any mouseRelease event 01572 if (d->m_mouseEventsTarget) 01573 d->m_mouseEventsTarget = 0; 01574 01575 if (d->clickCount > 0 && 01576 QPoint(d->clickX-xm,d->clickY-ym).manhattanLength() <= QApplication::startDragDistance()) { 01577 QMouseEvent me(d->isDoubleClick ? QEvent::MouseButtonDblClick : QEvent::MouseButtonRelease, 01578 _mouse->pos(), _mouse->button(), _mouse->buttons(), _mouse->modifiers()); 01579 dispatchMouseEvent(EventImpl::CLICK_EVENT, mev.innerNode.handle(),mev.innerNonSharedNode.handle(),true, 01580 d->clickCount, &me, true, DOM::NodeImpl::MouseRelease); 01581 } 01582 01583 khtml::RenderObject* r = target ? target->renderer() : 0; 01584 if (r && r->isWidget()) 01585 _mouse->ignore(); 01586 } 01587 01588 if (!swallowEvent) { 01589 khtml::MouseReleaseEvent event( _mouse, xm, ym, mev.url, mev.target, mev.innerNode ); 01590 QApplication::sendEvent( m_part, &event ); 01591 } 01592 } 01593 01594 // returns true if event should be swallowed 01595 bool KHTMLView::dispatchKeyEvent( QKeyEvent *_ke ) 01596 { 01597 if (!m_part->xmlDocImpl()) 01598 return false; 01599 // Pressing and releasing a key should generate keydown, keypress and keyup events 01600 // Holding it down should generated keydown, keypress (repeatedly) and keyup events 01601 // The problem here is that Qt generates two autorepeat events (keyrelease+keypress) 01602 // for autorepeating, while DOM wants only one autorepeat event (keypress), so one 01603 // of the Qt events shouldn't be passed to DOM, but it should be still filtered 01604 // out if DOM would filter the autorepeat event. Additional problem is that Qt keyrelease 01605 // events don't have text() set (Qt bug?), so DOM often would ignore the keypress event 01606 // if it was created using Qt keyrelease, but Qt autorepeat keyrelease comes 01607 // before Qt autorepeat keypress (i.e. problem whether to filter it out or not). 01608 // The solution is to filter out and postpone the Qt autorepeat keyrelease until 01609 // the following Qt keypress event comes. If DOM accepts the DOM keypress event, 01610 // the postponed event will be simply discarded. If not, it will be passed to keyPressEvent() 01611 // again, and here it will be ignored. 01612 // 01613 // Qt: Press | Release(autorepeat) Press(autorepeat) etc. | Release 01614 // DOM: Down + Press | (nothing) Press | Up 01615 01616 // It's also possible to get only Releases. E.g. the release of alt-tab, 01617 // or when the keypresses get captured by an accel. 01618 01619 if( _ke == d->postponed_autorepeat ) // replayed event 01620 { 01621 return false; 01622 } 01623 01624 if( _ke->type() == QEvent::KeyPress ) 01625 { 01626 if( !_ke->isAutoRepeat()) 01627 { 01628 bool ret = dispatchKeyEventHelper( _ke, false ); // keydown 01629 // don't send keypress even if keydown was blocked, like IE (and unlike Mozilla) 01630 if( !ret && dispatchKeyEventHelper( _ke, true )) // keypress 01631 ret = true; 01632 return ret; 01633 } 01634 else // autorepeat 01635 { 01636 bool ret = dispatchKeyEventHelper( _ke, true ); // keypress 01637 if( !ret && d->postponed_autorepeat ) 01638 keyPressEvent( d->postponed_autorepeat ); 01639 delete d->postponed_autorepeat; 01640 d->postponed_autorepeat = NULL; 01641 return ret; 01642 } 01643 } 01644 else // QEvent::KeyRelease 01645 { 01646 // Discard postponed "autorepeat key-release" events that didn't see 01647 // a keypress after them (e.g. due to QAccel) 01648 delete d->postponed_autorepeat; 01649 d->postponed_autorepeat = 0; 01650 01651 if( !_ke->isAutoRepeat()) { 01652 return dispatchKeyEventHelper( _ke, false ); // keyup 01653 } 01654 else 01655 { 01656 d->postponed_autorepeat = new QKeyEvent( _ke->type(), _ke->key(), _ke->modifiers(), 01657 _ke->text(), _ke->isAutoRepeat(), _ke->count()); 01658 if( _ke->isAccepted()) 01659 d->postponed_autorepeat->accept(); 01660 else 01661 d->postponed_autorepeat->ignore(); 01662 return true; 01663 } 01664 } 01665 } 01666 01667 // returns true if event should be swallowed 01668 bool KHTMLView::dispatchKeyEventHelper( QKeyEvent *_ke, bool keypress ) 01669 { 01670 DOM::NodeImpl* keyNode = m_part->xmlDocImpl()->focusNode(); 01671 if (keyNode) { 01672 return keyNode->dispatchKeyEvent(_ke, keypress); 01673 } else { // no focused node, send to document 01674 return m_part->xmlDocImpl()->dispatchKeyEvent(_ke, keypress); 01675 } 01676 } 01677 01678 void KHTMLView::keyPressEvent( QKeyEvent *_ke ) 01679 { 01680 // If CTRL was hit, be prepared for access keys 01681 if (d->accessKeysEnabled && _ke->key() == Qt::Key_Control && !(_ke->modifiers() & ~Qt::ControlModifier) && !d->accessKeysActivated) 01682 { 01683 d->accessKeysPreActivate=true; 01684 _ke->accept(); 01685 return; 01686 } 01687 01688 if (_ke->key() == Qt::Key_Shift && !(_ke->modifiers() & ~Qt::ShiftModifier)) 01689 d->scrollSuspendPreActivate=true; 01690 01691 // accesskey handling needs to be done before dispatching, otherwise e.g. lineedits 01692 // may eat the event 01693 01694 if (d->accessKeysEnabled && d->accessKeysActivated) 01695 { 01696 int state = ( _ke->modifiers() & ( Qt::ShiftModifier | Qt::ControlModifier | Qt::AltModifier | Qt::MetaModifier )); 01697 if ( state==0 || state==Qt::ShiftModifier ) { 01698 if (_ke->key() != Qt::Key_Shift) 01699 accessKeysTimeout(); 01700 handleAccessKey( _ke ); 01701 _ke->accept(); 01702 return; 01703 } 01704 accessKeysTimeout(); 01705 _ke->accept(); 01706 return; 01707 } 01708 01709 if ( dispatchKeyEvent( _ke )) { 01710 // If either keydown or keypress was accepted by a widget, or canceled by JS, stop here. 01711 _ke->accept(); 01712 return; 01713 } 01714 01715 int offs = (viewport()->height() < 30) ? viewport()->height() : 30; // ### ?? 01716 if (_ke->modifiers() & Qt::ShiftModifier) 01717 switch(_ke->key()) 01718 { 01719 case Qt::Key_Space: 01720 verticalScrollBar()->setValue( verticalScrollBar()->value() -viewport()->height() + offs ); 01721 if(d->scrollSuspended) 01722 d->newScrollTimer(this, 0); 01723 break; 01724 01725 case Qt::Key_Down: 01726 case Qt::Key_J: 01727 d->adjustScroller(this, KHTMLViewPrivate::ScrollDown, KHTMLViewPrivate::ScrollUp); 01728 break; 01729 01730 case Qt::Key_Up: 01731 case Qt::Key_K: 01732 d->adjustScroller(this, KHTMLViewPrivate::ScrollUp, KHTMLViewPrivate::ScrollDown); 01733 break; 01734 01735 case Qt::Key_Left: 01736 case Qt::Key_H: 01737 d->adjustScroller(this, KHTMLViewPrivate::ScrollLeft, KHTMLViewPrivate::ScrollRight); 01738 break; 01739 01740 case Qt::Key_Right: 01741 case Qt::Key_L: 01742 d->adjustScroller(this, KHTMLViewPrivate::ScrollRight, KHTMLViewPrivate::ScrollLeft); 01743 break; 01744 } 01745 else 01746 switch ( _ke->key() ) 01747 { 01748 case Qt::Key_Down: 01749 case Qt::Key_J: 01750 if (!d->scrollTimerId || d->scrollSuspended) 01751 verticalScrollBar()->setValue( verticalScrollBar()->value()+10 ); 01752 if (d->scrollTimerId) 01753 d->newScrollTimer(this, 0); 01754 break; 01755 01756 case Qt::Key_Space: 01757 case Qt::Key_PageDown: 01758 d->shouldSmoothScroll = true; 01759 verticalScrollBar()->setValue( verticalScrollBar()->value() +viewport()->height() - offs ); 01760 if(d->scrollSuspended) 01761 d->newScrollTimer(this, 0); 01762 break; 01763 01764 case Qt::Key_Up: 01765 case Qt::Key_K: 01766 if (!d->scrollTimerId || d->scrollSuspended) 01767 verticalScrollBar()->setValue( verticalScrollBar()->value()-10 ); 01768 if (d->scrollTimerId) 01769 d->newScrollTimer(this, 0); 01770 break; 01771 01772 case Qt::Key_PageUp: 01773 d->shouldSmoothScroll = true; 01774 verticalScrollBar()->setValue( verticalScrollBar()->value() -viewport()->height() + offs ); 01775 if(d->scrollSuspended) 01776 d->newScrollTimer(this, 0); 01777 break; 01778 case Qt::Key_Right: 01779 case Qt::Key_L: 01780 if (!d->scrollTimerId || d->scrollSuspended) 01781 horizontalScrollBar()->setValue( horizontalScrollBar()->value()+10 ); 01782 if (d->scrollTimerId) 01783 d->newScrollTimer(this, 0); 01784 break; 01785 01786 case Qt::Key_Left: 01787 case Qt::Key_H: 01788 if (!d->scrollTimerId || d->scrollSuspended) 01789 horizontalScrollBar()->setValue( horizontalScrollBar()->value()-10 ); 01790 if (d->scrollTimerId) 01791 d->newScrollTimer(this, 0); 01792 break; 01793 case Qt::Key_Enter: 01794 case Qt::Key_Return: 01795 // ### FIXME: 01796 // or even better to HTMLAnchorElementImpl::event() 01797 if (m_part->xmlDocImpl()) { 01798 NodeImpl *n = m_part->xmlDocImpl()->focusNode(); 01799 if (n) 01800 n->setActive(); 01801 } 01802 break; 01803 case Qt::Key_Home: 01804 verticalScrollBar()->setValue( 0 ); 01805 horizontalScrollBar()->setValue( 0 ); 01806 if(d->scrollSuspended) 01807 d->newScrollTimer(this, 0); 01808 break; 01809 case Qt::Key_End: 01810 verticalScrollBar()->setValue( contentsHeight() - visibleHeight() ); 01811 if(d->scrollSuspended) 01812 d->newScrollTimer(this, 0); 01813 break; 01814 case Qt::Key_Shift: 01815 // what are you doing here? 01816 _ke->ignore(); 01817 return; 01818 default: 01819 if (d->scrollTimerId) 01820 d->newScrollTimer(this, 0); 01821 _ke->ignore(); 01822 return; 01823 } 01824 01825 _ke->accept(); 01826 } 01827 01828 void KHTMLView::keyReleaseEvent(QKeyEvent *_ke) 01829 { 01830 if( d->scrollSuspendPreActivate && _ke->key() != Qt::Key_Shift ) 01831 d->scrollSuspendPreActivate = false; 01832 if( _ke->key() == Qt::Key_Shift && d->scrollSuspendPreActivate && !(_ke->modifiers() & Qt::ShiftModifier)) 01833 if (d->scrollTimerId) { 01834 d->scrollSuspended = !d->scrollSuspended; 01835 if (d->scrollSuspended) 01836 d->stopScrolling(); 01837 } 01838 01839 if (d->accessKeysEnabled) 01840 { 01841 if (d->accessKeysPreActivate && _ke->key() != Qt::Key_Control) 01842 d->accessKeysPreActivate=false; 01843 if (d->accessKeysPreActivate && !(_ke->modifiers() & Qt::ControlModifier)) 01844 { 01845 displayAccessKeys(); 01846 m_part->setStatusBarText(i18n("Access Keys activated"),KHTMLPart::BarOverrideText); 01847 d->accessKeysActivated = true; 01848 d->accessKeysPreActivate = false; 01849 _ke->accept(); 01850 return; 01851 } 01852 else if (d->accessKeysActivated) 01853 { 01854 accessKeysTimeout(); 01855 _ke->accept(); 01856 return; 01857 } 01858 } 01859 01860 // Send keyup event 01861 if ( dispatchKeyEvent( _ke ) ) 01862 { 01863 _ke->accept(); 01864 return; 01865 } 01866 01867 QScrollArea::keyReleaseEvent(_ke); 01868 } 01869 01870 bool KHTMLView::focusNextPrevChild( bool next ) 01871 { 01872 // Now try to find the next child 01873 if (m_part->xmlDocImpl() && focusNextPrevNode(next)) 01874 { 01875 if (m_part->xmlDocImpl()->focusNode()) 01876 kDebug() << "focusNode.name: " 01877 << m_part->xmlDocImpl()->focusNode()->nodeName().string() << endl; 01878 return true; // focus node found 01879 } 01880 01881 // If we get here, pass tabbing control up to the next/previous child in our parent 01882 d->pseudoFocusNode = KHTMLViewPrivate::PFNone; 01883 if (m_part->parentPart() && m_part->parentPart()->view()) 01884 return m_part->parentPart()->view()->focusNextPrevChild(next); 01885 01886 return QWidget::focusNextPrevChild(next); 01887 } 01888 01889 void KHTMLView::doAutoScroll() 01890 { 01891 QPoint pos = QCursor::pos(); 01892 QPoint off; 01893 KHTMLView* v = m_kwp->isRedirected() ? m_kwp->rootViewPos(off) : this; 01894 pos = v->viewport()->mapFromGlobal( pos ); 01895 pos -= off; 01896 int xm, ym; 01897 viewportToContents(pos.x(), pos.y(), xm, ym); // ### 01898 01899 pos = QPoint(pos.x() - viewport()->x(), pos.y() - viewport()->y()); 01900 if ( (pos.y() < 0) || (pos.y() > visibleHeight()) || 01901 (pos.x() < 0) || (pos.x() > visibleWidth()) ) 01902 { 01903 ensureVisible( xm, ym, 0, 5 ); 01904 01905 #ifndef KHTML_NO_SELECTION 01906 // extend the selection while scrolling 01907 DOM::Node innerNode; 01908 if (m_part->isExtendingSelection()) { 01909 RenderObject::NodeInfo renderInfo(true/*readonly*/, false/*active*/); 01910 m_part->xmlDocImpl()->renderer()->layer() 01911 ->nodeAtPoint(renderInfo, xm, ym); 01912 innerNode = renderInfo.innerNode(); 01913 }/*end if*/ 01914 01915 if (innerNode.handle() && innerNode.handle()->renderer() 01916 && innerNode.handle()->renderer()->shouldSelect()) { 01917 m_part->extendSelectionTo(xm, ym, innerNode); 01918 }/*end if*/ 01919 #endif // KHTML_NO_SELECTION 01920 } 01921 } 01922 01923 // KHTML defines its own stacking order for any object and thus takes 01924 // control of widget painting whenever it can. This is called "redirection". 01925 // 01926 // Redirected widgets are placed off screen. When they are declared as a child of our view (ChildPolished event), 01927 // an event filter is installed, so as to catch any paint event and translate them as update() of the view's main widget. 01928 // 01929 // Painting also happens spontaneously within widgets. In this case, the widget would update() parts of itself. 01930 // While this ordinarily results in a paintEvent being schedduled, it is not the case with off screen widgets. 01931 // Thus update() is monitored by using the mechanism that deffers any update call happening during a paint event, 01932 // transforming it into a posted UpdateLater event. Hence the need to set Qt::WA_WState_InPaintEvent on redirected widgets. 01933 // 01934 // Once the UpdateLater event has been received, Qt::WA_WState_InPaintEvent is removed and the process continues 01935 // with the update of the corresponding rect on the view. That in turn will make our painting subsystem render() 01936 // the widget at the correct stacking position. 01937 // 01938 // For non-redirected (e.g. external) widgets, z-order is honoured through masking. cf.RenderLayer::updateWidgetMasks 01939 01940 static void handleWidget(QWidget* w, KHTMLView* view, bool recurse=true) 01941 { 01942 if (w->isWindow()) 01943 return; 01944 01945 if (!qobject_cast<QFrame*>(w)) 01946 w->setAttribute( Qt::WA_NoSystemBackground ); 01947 01948 w->setAttribute(Qt::WA_WState_InPaintEvent); 01949 01950 if (!(w->objectName() == "KLineEditButton")) 01951 w->setAttribute(Qt::WA_OpaquePaintEvent); 01952 01953 w->installEventFilter(view); 01954 01955 if (!recurse) 01956 return; 01957 if (qobject_cast<KHTMLView*>(w)) { 01958 handleWidget(static_cast<KHTMLView*>(w)->widget(), view, false); 01959 handleWidget(static_cast<KHTMLView*>(w)->horizontalScrollBar(), view, false); 01960 handleWidget(static_cast<KHTMLView*>(w)->verticalScrollBar(), view, false); 01961 return; 01962 } 01963 01964 QObjectList children = w->children(); 01965 foreach (QObject* object, children) { 01966 QWidget *widget = qobject_cast<QWidget*>(object); 01967 if (widget) 01968 handleWidget(widget, view); 01969 } 01970 } 01971 01972 class KHTMLBackingStoreHackWidget : public QWidget 01973 { 01974 public: 01975 void publicEvent(QEvent *e) 01976 { 01977 QWidget::event(e); 01978 } 01979 }; 01980 01981 bool KHTMLView::viewportEvent ( QEvent * e ) 01982 { 01983 switch (e->type()) { 01984 // those must not be dispatched to the specialized handlers 01985 // as widgetEvent() already took care of that 01986 case QEvent::MouseButtonPress: 01987 case QEvent::MouseButtonRelease: 01988 case QEvent::MouseButtonDblClick: 01989 case QEvent::MouseMove: 01990 #ifndef QT_NO_WHEELEVENT 01991 case QEvent::Wheel: 01992 #endif 01993 case QEvent::ContextMenu: 01994 case QEvent::DragEnter: 01995 case QEvent::DragMove: 01996 case QEvent::DragLeave: 01997 case QEvent::Drop: 01998 return false; 01999 default: 02000 break; 02001 } 02002 return QScrollArea::viewportEvent(e); 02003 } 02004 02005 static void setInPaintEventFlag(QWidget* w, bool b = true, bool recurse=true) 02006 { 02007 w->setAttribute(Qt::WA_WState_InPaintEvent, b); 02008 02009 if (!recurse) 02010 return; 02011 if (qobject_cast<KHTMLView*>(w)) { 02012 setInPaintEventFlag(static_cast<KHTMLView*>(w)->widget(), b, false); 02013 setInPaintEventFlag(static_cast<KHTMLView*>(w)->horizontalScrollBar(), b, false); 02014 setInPaintEventFlag(static_cast<KHTMLView*>(w)->verticalScrollBar(), b, false); 02015 return; 02016 } 02017 02018 foreach(QObject* cw, w->children()) { 02019 if (cw->isWidgetType() && ! static_cast<QWidget*>(cw)->isWindow() 02020 && !(static_cast<QWidget*>(cw)->windowModality() & Qt::ApplicationModal)) { 02021 setInPaintEventFlag(static_cast<QWidget*>(cw), b); 02022 } 02023 } 02024 } 02025 02026 bool KHTMLView::eventFilter(QObject *o, QEvent *e) 02027 { 02028 if ( e->type() == QEvent::ShortcutOverride ) { 02029 QKeyEvent* ke = (QKeyEvent*) e; 02030 if (m_part->isEditable() || m_part->isCaretMode() 02031 || (m_part->xmlDocImpl() && m_part->xmlDocImpl()->focusNode() 02032 && m_part->xmlDocImpl()->focusNode()->isContentEditable())) { 02033 if ( (ke->modifiers() & Qt::ControlModifier) || (ke->modifiers() & Qt::ShiftModifier) ) { 02034 switch ( ke->key() ) { 02035 case Qt::Key_Left: 02036 case Qt::Key_Right: 02037 case Qt::Key_Up: 02038 case Qt::Key_Down: 02039 case Qt::Key_Home: 02040 case Qt::Key_End: 02041 ke->accept(); 02042 return true; 02043 default: 02044 break; 02045 } 02046 } 02047 } 02048 } 02049 02050 if ( e->type() == QEvent::Leave ) { 02051 if ( d->cursorIconWidget ) 02052 d->cursorIconWidget->hide(); 02053 m_part->resetHoverText(); 02054 } 02055 02056 QWidget *view = widget(); 02057 if (o == view) { 02058 if (widgetEvent(e)) 02059 return true; 02060 else if (e->type() == QEvent::Resize) { 02061 updateScrollBars(); 02062 return false; 02063 } 02064 } else if (o->isWidgetType()) { 02065 QWidget *v = static_cast<QWidget *>(o); 02066 QWidget *c = v; 02067 while (v && v != view) { 02068 c = v; 02069 v = v->parentWidget(); 02070 } 02071 KHTMLWidget* k = dynamic_cast<KHTMLWidget*>(c); 02072 if (v && k && k->m_kwp->isRedirected()) { 02073 bool block = false; 02074 bool isUpdate = false; 02075 QWidget *w = static_cast<QWidget *>(o); 02076 switch(e->type()) { 02077 case QEvent::UpdateRequest: { 02078 // implicitly call qt_syncBackingStore(w) 02079 static_cast<KHTMLBackingStoreHackWidget *>(w)->publicEvent(e); 02080 block = true; 02081 break; 02082 } 02083 case QEvent::UpdateLater: 02084 isUpdate = true; 02085 // no break; 02086 case QEvent::Paint: 02087 if (!allowWidgetPaintEvents) { 02088 // eat the event. Like this we can control exactly when the widget 02089 // gets repainted. 02090 block = true; 02091 int x = 0, y = 0; 02092 QWidget *v = w; 02093 while (v && v->parentWidget() != view) { 02094 x += v->x(); 02095 y += v->y(); 02096 v = v->parentWidget(); 02097 } 02098 02099 QPoint ap = k->m_kwp->absolutePos(); 02100 x += ap.x(); 02101 y += ap.y(); 02102 02103 QRect pr = isUpdate ? static_cast<QUpdateLaterEvent*>(e)->region().boundingRect() : static_cast<QPaintEvent*>(e)->rect(); 02104 bool asap = !d->contentsMoving && qobject_cast<QAbstractScrollArea*>(c); 02105 02106 if (isUpdate) { 02107 setInPaintEventFlag(w, false); 02108 if (asap) 02109 w->repaint(static_cast<QUpdateLaterEvent*>(e)->region()); 02110 else 02111 w->update(static_cast<QUpdateLaterEvent*>(e)->region()); 02112 setInPaintEventFlag(w); 02113 } 02114 02115 // QScrollView needs fast repaints 02116 if ( asap && !isUpdate && !d->painting && m_part->xmlDocImpl() && m_part->xmlDocImpl()->renderer() && 02117 !static_cast<khtml::RenderCanvas *>(m_part->xmlDocImpl()->renderer())->needsLayout() ) { 02118 repaintContents(x + pr.x(), y + pr.y(), 02119 pr.width(), pr.height()+1); // ### investigate that +1 (shows up when 02120 // updating e.g a textarea's blinking cursor) 02121 } else if (!d->painting) { 02122 scheduleRepaint(x + pr.x(), y + pr.y(), 02123 pr.width(), pr.height()+1, asap); 02124 } 02125 } 02126 break; 02127 case QEvent::MouseMove: 02128 case QEvent::MouseButtonPress: 02129 case QEvent::MouseButtonRelease: 02130 case QEvent::MouseButtonDblClick: { 02131 02132 if (0 && w->parentWidget() == view && !qobject_cast<QScrollBar*>(w) && !::qobject_cast<QScrollBar *>(w)) { 02133 QMouseEvent *me = static_cast<QMouseEvent *>(e); 02134 QPoint pt = w->mapTo( view, me->pos()); 02135 QMouseEvent me2(me->type(), pt, me->button(), me->buttons(), me->modifiers()); 02136 02137 if (e->type() == QEvent::MouseMove) 02138 mouseMoveEvent(&me2); 02139 else if(e->type() == QEvent::MouseButtonPress) 02140 mousePressEvent(&me2); 02141 else if(e->type() == QEvent::MouseButtonRelease) 02142 mouseReleaseEvent(&me2); 02143 else 02144 mouseDoubleClickEvent(&me2); 02145 block = true; 02146 } 02147 break; 02148 } 02149 case QEvent::KeyPress: 02150 case QEvent::KeyRelease: 02151 if (w->parentWidget() == view && !qobject_cast<QScrollBar*>(w)) { 02152 QKeyEvent *ke = static_cast<QKeyEvent *>(e); 02153 if (e->type() == QEvent::KeyPress) { 02154 keyPressEvent(ke); 02155 ke->accept(); 02156 } else{ 02157 keyReleaseEvent(ke); 02158 ke->accept(); 02159 } 02160 block = true; 02161 } 02162 02163 if (qobject_cast<KUrlRequester*>(w->parentWidget()) && 02164 e->type() == QEvent::KeyPress) { 02165 // Since keypress events on the upload widget will 02166 // be forwarded to the lineedit anyway, 02167 // block the original copy at this level to prevent 02168 // double-emissions of events it doesn't accept 02169 e->ignore(); 02170 block = true; 02171 } 02172 02173 break; 02174 case QEvent::FocusIn: 02175 case QEvent::FocusOut: { 02176 QPoint dummy; 02177 KHTMLView* root = m_kwp->rootViewPos(dummy); 02178 if (!root) 02179 root = this; 02180 block = static_cast<QFocusEvent*>(e)->reason() != Qt::MouseFocusReason || root->underMouse(); 02181 break; 02182 } 02183 default: 02184 break; 02185 } 02186 if (block) { 02187 //qDebug("eating event"); 02188 return true; 02189 } 02190 } 02191 } 02192 02193 // kDebug(6000) <<"passing event on to sv event filter object=" << o->className() << " event=" << e->type(); 02194 return QScrollArea::eventFilter(o, e); 02195 } 02196 02197 bool KHTMLView::widgetEvent(QEvent* e) 02198 { 02199 switch (e->type()) { 02200 case QEvent::MouseButtonPress: 02201 case QEvent::MouseButtonRelease: 02202 case QEvent::MouseButtonDblClick: 02203 case QEvent::MouseMove: 02204 case QEvent::Paint: 02205 #ifndef QT_NO_WHEELEVENT 02206 case QEvent::Wheel: 02207 #endif 02208 case QEvent::ContextMenu: 02209 case QEvent::DragEnter: 02210 case QEvent::DragMove: 02211 case QEvent::DragLeave: 02212 case QEvent::Drop: 02213 return QFrame::event(e); 02214 case QEvent::ChildPolished: { 02215 // we need to install an event filter on all children of the widget() to 02216 // be able to get correct stacking of children within the document. 02217 QObject *c = static_cast<QChildEvent *>(e)->child(); 02218 if (c->isWidgetType()) { 02219 QWidget *w = static_cast<QWidget *>(c); 02220 // don't install the event filter on toplevels 02221 if (!(w->windowFlags() & Qt::Window) && !(w->windowModality() & Qt::ApplicationModal)) { 02222 KHTMLWidget* k = dynamic_cast<KHTMLWidget*>(w); 02223 if (k && k->m_kwp->isRedirected()) { 02224 w->unsetCursor(); 02225 handleWidget(w, this); 02226 } 02227 } 02228 } 02229 break; 02230 } 02231 case QEvent::Move: { 02232 if (static_cast<QMoveEvent*>(e)->pos() != QPoint(0,0)) { 02233 widget()->move(0,0); 02234 updateScrollBars(); 02235 return true; 02236 } 02237 break; 02238 } 02239 default: 02240 break; 02241 } 02242 return false; 02243 } 02244 02245 bool KHTMLView::hasLayoutPending() 02246 { 02247 return d->layoutTimerId && !d->firstLayoutPending; 02248 } 02249 02250 DOM::NodeImpl *KHTMLView::nodeUnderMouse() const 02251 { 02252 return d->underMouse; 02253 } 02254 02255 DOM::NodeImpl *KHTMLView::nonSharedNodeUnderMouse() const 02256 { 02257 return d->underMouseNonShared; 02258 } 02259 02260 bool KHTMLView::scrollTo(const QRect &bounds) 02261 { 02262 d->scrollingSelf = true; // so scroll events get ignored 02263 02264 int x, y, xe, ye; 02265 x = bounds.left(); 02266 y = bounds.top(); 02267 xe = bounds.right(); 02268 ye = bounds.bottom(); 02269 02270 //kDebug(6000)<<"scrolling coords: x="<<x<<" y="<<y<<" width="<<xe-x<<" height="<<ye-y; 02271 02272 int deltax; 02273 int deltay; 02274 02275 int curHeight = visibleHeight(); 02276 int curWidth = visibleWidth(); 02277 02278 if (ye-y>curHeight-d->borderY) 02279 ye = y + curHeight - d->borderY; 02280 02281 if (xe-x>curWidth-d->borderX) 02282 xe = x + curWidth - d->borderX; 02283 02284 // is xpos of target left of the view's border? 02285 if (x < contentsX() + d->borderX ) 02286 deltax = x - contentsX() - d->borderX; 02287 // is xpos of target right of the view's right border? 02288 else if (xe + d->borderX > contentsX() + curWidth) 02289 deltax = xe + d->borderX - ( contentsX() + curWidth ); 02290 else 02291 deltax = 0; 02292 02293 // is ypos of target above upper border? 02294 if (y < contentsY() + d->borderY) 02295 deltay = y - contentsY() - d->borderY; 02296 // is ypos of target below lower border? 02297 else if (ye + d->borderY > contentsY() + curHeight) 02298 deltay = ye + d->borderY - ( contentsY() + curHeight ); 02299 else 02300 deltay = 0; 02301 02302 int maxx = curWidth-d->borderX; 02303 int maxy = curHeight-d->borderY; 02304 02305 int scrollX, scrollY; 02306 02307 scrollX = deltax > 0 ? (deltax > maxx ? maxx : deltax) : deltax == 0 ? 0 : (deltax>-maxx ? deltax : -maxx); 02308 scrollY = deltay > 0 ? (deltay > maxy ? maxy : deltay) : deltay == 0 ? 0 : (deltay>-maxy ? deltay : -maxy); 02309 02310 if (contentsX() + scrollX < 0) 02311 scrollX = -contentsX(); 02312 else if (contentsWidth() - visibleWidth() - contentsX() < scrollX) 02313 scrollX = contentsWidth() - visibleWidth() - contentsX(); 02314 02315 if (contentsY() + scrollY < 0) 02316 scrollY = -contentsY(); 02317 else if (contentsHeight() - visibleHeight() - contentsY() < scrollY) 02318 scrollY = contentsHeight() - visibleHeight() - contentsY(); 02319 02320 horizontalScrollBar()->setValue( horizontalScrollBar()->value()+scrollX ); 02321 verticalScrollBar()->setValue( verticalScrollBar()->value()+scrollY ); 02322 02323 d->scrollingSelf = false; 02324 02325 if ( (abs(deltax)<=maxx) && (abs(deltay)<=maxy) ) 02326 return true; 02327 else return false; 02328 02329 } 02330 02331 bool KHTMLView::focusNextPrevNode(bool next) 02332 { 02333 // Sets the focus node of the document to be the node after (or if 02334 // next is false, before) the current focus node. Only nodes that 02335 // are selectable (i.e. for which isFocusable() returns true) are 02336 // taken into account, and the order used is that specified in the 02337 // HTML spec (see DocumentImpl::nextFocusNode() and 02338 // DocumentImpl::previousFocusNode() for details). 02339 02340 DocumentImpl *doc = m_part->xmlDocImpl(); 02341 NodeImpl *oldFocusNode = doc->focusNode(); 02342 02343 // See whether we're in the middle of a detach, or hiding of the 02344 // widget. In this case, we will just clear focus, being careful not to emit events 02345 // or update rendering. Doing this also prevents the code below from going bonkers with 02346 // oldFocusNode not actually being focusable, etc. 02347 if (oldFocusNode) { 02348 if ((oldFocusNode->renderer() && !oldFocusNode->renderer()->parent()) 02349 || !oldFocusNode->isTabFocusable()) { 02350 doc->quietResetFocus(); 02351 return true; 02352 } 02353 } 02354 02355 #if 1 02356 // If the user has scrolled the document, then instead of picking 02357 // the next focusable node in the document, use the first one that 02358 // is within the visible area (if possible). 02359 if (d->scrollBarMoved) 02360 { 02361 NodeImpl *toFocus; 02362 if (next) 02363 toFocus = doc->nextFocusNode(oldFocusNode); 02364 else 02365 toFocus = doc->previousFocusNode(oldFocusNode); 02366 02367 if (!toFocus && oldFocusNode) { 02368 if (next) 02369 toFocus = doc->nextFocusNode(NULL); 02370 else 02371 toFocus = doc->previousFocusNode(NULL); 02372 } 02373 02374 while (toFocus && toFocus != oldFocusNode) 02375 { 02376 02377 QRect focusNodeRect = toFocus->getRect(); 02378 if ((focusNodeRect.left() > contentsX()) && (focusNodeRect.right() < contentsX() + visibleWidth()) && 02379 (focusNodeRect.top() > contentsY()) && (focusNodeRect.bottom() < contentsY() + visibleHeight())) { 02380 { 02381 QRect r = toFocus->getRect(); 02382 ensureVisible( r.right(), r.bottom()); 02383 ensureVisible( r.left(), r.top()); 02384 d->scrollBarMoved = false; 02385 d->tabMovePending = false; 02386 d->lastTabbingDirection = next; 02387 d->pseudoFocusNode = KHTMLViewPrivate::PFNone; 02388 m_part->xmlDocImpl()->setFocusNode(toFocus); 02389 Node guard(toFocus); 02390 if (!toFocus->hasOneRef() ) 02391 { 02392 emit m_part->nodeActivated(Node(toFocus)); 02393 } 02394 return true; 02395 } 02396 } 02397 if (next) 02398 toFocus = doc->nextFocusNode(toFocus); 02399 else 02400 toFocus = doc->previousFocusNode(toFocus); 02401 02402 if (!toFocus && oldFocusNode) 02403 { 02404 if (next) 02405 { 02406 toFocus = doc->nextFocusNode(NULL); 02407 } 02408 else 02409 { 02410 toFocus = doc->previousFocusNode(NULL); 02411 } 02412 } 02413 } 02414 02415 d->scrollBarMoved = false; 02416 } 02417 #endif 02418 02419 if (!oldFocusNode && d->pseudoFocusNode == KHTMLViewPrivate::PFNone) 02420 { 02421 ensureVisible(contentsX(), next?0:contentsHeight()); 02422 d->scrollBarMoved = false; 02423 d->pseudoFocusNode = next?KHTMLViewPrivate::PFTop:KHTMLViewPrivate::PFBottom; 02424 return true; 02425 } 02426 02427 NodeImpl *newFocusNode = NULL; 02428 02429 if (d->tabMovePending && next != d->lastTabbingDirection) 02430 { 02431 //kDebug ( 6000 ) << " tab move pending and tabbing direction changed!\n"; 02432 newFocusNode = oldFocusNode; 02433 } 02434 else if (next) 02435 { 02436 if (oldFocusNode || d->pseudoFocusNode == KHTMLViewPrivate::PFTop ) 02437 newFocusNode = doc->nextFocusNode(oldFocusNode); 02438 } 02439 else 02440 { 02441 if (oldFocusNode || d->pseudoFocusNode == KHTMLViewPrivate::PFBottom ) 02442 newFocusNode = doc->previousFocusNode(oldFocusNode); 02443 } 02444 02445 bool targetVisible = false; 02446 if (!newFocusNode) 02447 { 02448 if ( next ) 02449 { 02450 targetVisible = scrollTo(QRect(contentsX()+visibleWidth()/2,contentsHeight()-d->borderY,0,0)); 02451 } 02452 else 02453 { 02454 targetVisible = scrollTo(QRect(contentsX()+visibleWidth()/2,d->borderY,0,0)); 02455 } 02456 } 02457 else 02458 { 02459 // if it's an editable element, activate the caret 02460 if (!m_part->isCaretMode() && newFocusNode->isContentEditable()) { 02461 kDebug(6200) << "show caret! fn: " << newFocusNode->nodeName().string() << endl; 02462 m_part->clearCaretRectIfNeeded(); 02463 m_part->d->editor_context.m_selection.moveTo(Position(newFocusNode, 0L)); 02464 m_part->setCaretVisible(true); 02465 } else { 02466 m_part->setCaretVisible(false); 02467 kDebug(6200) << "hide caret! fn: " << newFocusNode->nodeName().string() << endl; 02468 } 02469 m_part->notifySelectionChanged(); 02470 02471 targetVisible = scrollTo(newFocusNode->getRect()); 02472 } 02473 02474 if (targetVisible) 02475 { 02476 //kDebug ( 6000 ) << " target reached.\n"; 02477 d->tabMovePending = false; 02478 02479 m_part->xmlDocImpl()->setFocusNode(newFocusNode); 02480 if (newFocusNode) 02481 { 02482 Node guard(newFocusNode); 02483 if (!newFocusNode->hasOneRef() ) 02484 { 02485 emit m_part->nodeActivated(Node(newFocusNode)); 02486 } 02487 return true; 02488 } 02489 else 02490 { 02491 d->pseudoFocusNode = next?KHTMLViewPrivate::PFBottom:KHTMLViewPrivate::PFTop; 02492 return false; 02493 } 02494 } 02495 else 02496 { 02497 if (!d->tabMovePending) 02498 d->lastTabbingDirection = next; 02499 d->tabMovePending = true; 02500 return true; 02501 } 02502 } 02503 02504 void KHTMLView::displayAccessKeys() 02505 { 02506 QVector< QChar > taken; 02507 displayAccessKeys( NULL, this, taken, false ); 02508 displayAccessKeys( NULL, this, taken, true ); 02509 } 02510 02511 void KHTMLView::displayAccessKeys( KHTMLView* caller, KHTMLView* origview, QVector< QChar >& taken, bool use_fallbacks ) 02512 { 02513 QMap< ElementImpl*, QChar > fallbacks; 02514 if( use_fallbacks ) 02515 fallbacks = buildFallbackAccessKeys(); 02516 for( NodeImpl* n = m_part->xmlDocImpl(); n != NULL; n = n->traverseNextNode()) { 02517 if( n->isElementNode()) { 02518 ElementImpl* en = static_cast< ElementImpl* >( n ); 02519 DOMString s = en->getAttribute( ATTR_ACCESSKEY ); 02520 QString accesskey; 02521 if( s.length() == 1 ) { 02522 QChar a = s.string()[ 0 ].toUpper(); 02523 if( qFind( taken.begin(), taken.end(), a ) == taken.end()) // !contains 02524 accesskey = a; 02525 } 02526 if( accesskey.isNull() && fallbacks.contains( en )) { 02527 QChar a = fallbacks[ en ].toUpper(); 02528 if( qFind( taken.begin(), taken.end(), a ) == taken.end()) // !contains 02529 accesskey = QString( "<qt><i>" ) + a + "</i></qt>"; 02530 } 02531 if( !accesskey.isNull()) { 02532 QRect rec=en->getRect(); 02533 QLabel *lab=new QLabel(accesskey,widget()); 02534 lab->setAttribute(Qt::WA_DeleteOnClose); 02535 lab->setObjectName("KHTMLAccessKey"); 02536 connect( origview, SIGNAL(hideAccessKeys()), lab, SLOT(close()) ); 02537 connect( this, SIGNAL(repaintAccessKeys()), lab, SLOT(repaint())); 02538 lab->setPalette(QToolTip::palette()); 02539 lab->setLineWidth(2); 02540 lab->setFrameStyle(QFrame::Box | QFrame::Plain); 02541 lab->setMargin(3); 02542 lab->adjustSize(); 02543 lab->setParent( widget() ); 02544 lab->setAutoFillBackground(true); 02545 lab->move( 02546 qMin(rec.left()+rec.width()/2 - contentsX(), contentsWidth() - lab->width()), 02547 qMin(rec.top()+rec.height()/2 - contentsY(), contentsHeight() - lab->height())); 02548 lab->show(); 02549 taken.append( accesskey[ 0 ] ); 02550 } 02551 } 02552 } 02553 if( use_fallbacks ) 02554 return; 02555 02556 QList<KParts::ReadOnlyPart*> frames = m_part->frames(); 02557 foreach( KParts::ReadOnlyPart* cur, frames ) { 02558 if( !qobject_cast<KHTMLPart*>(cur) ) 02559 continue; 02560 KHTMLPart* part = static_cast< KHTMLPart* >( cur ); 02561 if( part->view() && part->view() != caller ) 02562 part->view()->displayAccessKeys( this, origview, taken, use_fallbacks ); 02563 } 02564 02565 // pass up to the parent 02566 if (m_part->parentPart() && m_part->parentPart()->view() 02567 && m_part->parentPart()->view() != caller) 02568 m_part->parentPart()->view()->displayAccessKeys( this, origview, taken, use_fallbacks ); 02569 } 02570 02571 bool KHTMLView::isScrollingFromMouseWheel() const 02572 { 02573 return d->scrollingFromWheel != QPoint(-1,-1); 02574 } 02575 02576 void KHTMLView::accessKeysTimeout() 02577 { 02578 d->accessKeysActivated=false; 02579 d->accessKeysPreActivate = false; 02580 m_part->setStatusBarText(QString(), KHTMLPart::BarOverrideText); 02581 emit hideAccessKeys(); 02582 } 02583 02584 // Handling of the HTML accesskey attribute. 02585 bool KHTMLView::handleAccessKey( const QKeyEvent* ev ) 02586 { 02587 // Qt interprets the keyevent also with the modifiers, and ev->text() matches that, 02588 // but this code must act as if the modifiers weren't pressed 02589 QChar c; 02590 if( ev->key() >= Qt::Key_A && ev->key() <= Qt::Key_Z ) 02591 c = 'A' + ev->key() - Qt::Key_A; 02592 else if( ev->key() >= Qt::Key_0 && ev->key() <= Qt::Key_9 ) 02593 c = '0' + ev->key() - Qt::Key_0; 02594 else { 02595 // TODO fake XKeyEvent and XLookupString ? 02596 // This below seems to work e.g. for eacute though. 02597 if( ev->text().length() == 1 ) 02598 c = ev->text()[ 0 ]; 02599 } 02600 if( c.isNull()) 02601 return false; 02602 return focusNodeWithAccessKey( c ); 02603 } 02604 02605 bool KHTMLView::focusNodeWithAccessKey( QChar c, KHTMLView* caller ) 02606 { 02607 DocumentImpl *doc = m_part->xmlDocImpl(); 02608 if( !doc ) 02609 return false; 02610 ElementImpl* node = doc->findAccessKeyElement( c ); 02611 if( !node ) { 02612 QList<KParts::ReadOnlyPart*> frames = m_part->frames(); 02613 foreach( KParts::ReadOnlyPart* cur, frames ) { 02614 if( !qobject_cast<KHTMLPart*>(cur) ) 02615 continue; 02616 KHTMLPart* part = static_cast< KHTMLPart* >( cur ); 02617 if( part->view() && part->view() != caller 02618 && part->view()->focusNodeWithAccessKey( c, this )) 02619 return true; 02620 } 02621 // pass up to the parent 02622 if (m_part->parentPart() && m_part->parentPart()->view() 02623 && m_part->parentPart()->view() != caller 02624 && m_part->parentPart()->view()->focusNodeWithAccessKey( c, this )) 02625 return true; 02626 if( caller == NULL ) { // the active frame (where the accesskey was pressed) 02627 const QMap< ElementImpl*, QChar > fallbacks = buildFallbackAccessKeys(); 02628 for( QMap< ElementImpl*, QChar >::ConstIterator it = fallbacks.begin(); 02629 it != fallbacks.end(); 02630 ++it ) 02631 if( *it == c ) { 02632 node = it.key(); 02633 break; 02634 } 02635 } 02636 if( node == NULL ) 02637 return false; 02638 } 02639 02640 // Scroll the view as necessary to ensure that the new focus node is visible 02641 02642 QRect r = node->getRect(); 02643 ensureVisible( r.right(), r.bottom()); 02644 ensureVisible( r.left(), r.top()); 02645 02646 Node guard( node ); 02647 if( node->isFocusable()) { 02648 if (node->id()==ID_LABEL) { 02649 // if Accesskey is a label, give focus to the label's referrer. 02650 node=static_cast<ElementImpl *>(static_cast< HTMLLabelElementImpl* >( node )->getFormElement()); 02651 if (!node) return true; 02652 guard = node; 02653 } 02654 // Set focus node on the document 02655 m_part->xmlDocImpl()->setFocusNode(node); 02656 02657 if( node != NULL && node->hasOneRef()) // deleted, only held by guard 02658 return true; 02659 emit m_part->nodeActivated(Node(node)); 02660 if( node != NULL && node->hasOneRef()) 02661 return true; 02662 } 02663 02664 switch( node->id()) { 02665 case ID_A: 02666 static_cast< HTMLAnchorElementImpl* >( node )->click(); 02667 break; 02668 case ID_INPUT: 02669 static_cast< HTMLInputElementImpl* >( node )->click(); 02670 break; 02671 case ID_BUTTON: 02672 static_cast< HTMLButtonElementImpl* >( node )->click(); 02673 break; 02674 case ID_AREA: 02675 static_cast< HTMLAreaElementImpl* >( node )->click(); 02676 break; 02677 case ID_TEXTAREA: 02678 break; // just focusing it is enough 02679 case ID_LEGEND: 02680 // TODO 02681 break; 02682 } 02683 return true; 02684 } 02685 02686 static QString getElementText( NodeImpl* start, bool after ) 02687 { 02688 QString ret; // nextSibling(), to go after e.g. </select> 02689 for( NodeImpl* n = after ? start->nextSibling() : start->traversePreviousNode(); 02690 n != NULL; 02691 n = after ? n->traverseNextNode() : n->traversePreviousNode()) { 02692 if( n->isTextNode()) { 02693 if( after ) 02694 ret += static_cast< TextImpl* >( n )->toString().string(); 02695 else 02696 ret.prepend( static_cast< TextImpl* >( n )->toString().string()); 02697 } else { 02698 switch( n->id()) { 02699 case ID_A: 02700 case ID_FONT: 02701 case ID_TT: 02702 case ID_U: 02703 case ID_B: 02704 case ID_I: 02705 case ID_S: 02706 case ID_STRIKE: 02707 case ID_BIG: 02708 case ID_SMALL: 02709 case ID_EM: 02710 case ID_STRONG: 02711 case ID_DFN: 02712 case ID_CODE: 02713 case ID_SAMP: 02714 case ID_KBD: 02715 case ID_VAR: 02716 case ID_CITE: 02717 case ID_ABBR: 02718 case ID_ACRONYM: 02719 case ID_SUB: 02720 case ID_SUP: 02721 case ID_SPAN: 02722 case ID_NOBR: 02723 case ID_WBR: 02724 break; 02725 case ID_TD: 02726 if( ret.trimmed().isEmpty()) 02727 break; 02728 // fall through 02729 default: 02730 return ret.simplified(); 02731 } 02732 } 02733 } 02734 return ret.simplified(); 02735 } 02736 02737 static QMap< NodeImpl*, QString > buildLabels( NodeImpl* start ) 02738 { 02739 QMap< NodeImpl*, QString > ret; 02740 for( NodeImpl* n = start; 02741 n != NULL; 02742 n = n->traverseNextNode()) { 02743 if( n->id() == ID_LABEL ) { 02744 HTMLLabelElementImpl* label = static_cast< HTMLLabelElementImpl* >( n ); 02745 NodeImpl* labelfor = label->getFormElement(); 02746 if( labelfor ) 02747 ret[ labelfor ] = label->innerText().string().simplified(); 02748 } 02749 } 02750 return ret; 02751 } 02752 02753 namespace khtml { 02754 struct AccessKeyData { 02755 ElementImpl* element; 02756 QString text; 02757 QString url; 02758 int priority; // 10(highest) - 0(lowest) 02759 }; 02760 } 02761 02762 QMap< ElementImpl*, QChar > KHTMLView::buildFallbackAccessKeys() const 02763 { 02764 // build a list of all possible candidate elements that could use an accesskey 02765 QLinkedList< AccessKeyData > data; // Note: this has to be a list type that keep iterators valid 02766 // when other entries are removed 02767 QMap< NodeImpl*, QString > labels = buildLabels( m_part->xmlDocImpl()); 02768 QMap< QString, QChar > hrefs; 02769 02770 for( NodeImpl* n = m_part->xmlDocImpl(); 02771 n != NULL; 02772 n = n->traverseNextNode()) { 02773 if( n->isElementNode()) { 02774 ElementImpl* element = static_cast< ElementImpl* >( n ); 02775 if( element->renderer() == NULL ) 02776 continue; // not visible 02777 QString text; 02778 QString url; 02779 int priority = 0; 02780 bool ignore = false; 02781 bool text_after = false; 02782 bool text_before = false; 02783 switch( element->id()) { 02784 case ID_A: 02785 url = khtml::parseURL(element->getAttribute(ATTR_HREF)).string(); 02786 if( url.isEmpty()) // doesn't have href, it's only an anchor 02787 continue; 02788 text = static_cast< HTMLElementImpl* >( element )->innerText().string().simplified(); 02789 priority = 2; 02790 break; 02791 case ID_INPUT: { 02792 HTMLInputElementImpl* in = static_cast< HTMLInputElementImpl* >( element ); 02793 switch( in->inputType()) { 02794 case HTMLInputElementImpl::SUBMIT: 02795 text = in->value().string(); 02796 if( text.isEmpty()) 02797 text = i18n( "Submit" ); 02798 priority = 7; 02799 break; 02800 case HTMLInputElementImpl::IMAGE: 02801 text = in->altText().string(); 02802 priority = 7; 02803 break; 02804 case HTMLInputElementImpl::BUTTON: 02805 text = in->value().string(); 02806 priority = 5; 02807 break; 02808 case HTMLInputElementImpl::RESET: 02809 text = in->value().string(); 02810 if( text.isEmpty()) 02811 text = i18n( "Reset" ); 02812 priority = 5; 02813 break; 02814 case HTMLInputElementImpl::HIDDEN: 02815 ignore = true; 02816 break; 02817 case HTMLInputElementImpl::CHECKBOX: 02818 case HTMLInputElementImpl::RADIO: 02819 text_after = true; 02820 priority = 5; 02821 break; 02822 case HTMLInputElementImpl::TEXT: 02823 case HTMLInputElementImpl::PASSWORD: 02824 case HTMLInputElementImpl::FILE: 02825 text_before = true; 02826 priority = 5; 02827 break; 02828 default: 02829 priority = 5; 02830 break; 02831 } 02832 break; 02833 } 02834 case ID_BUTTON: 02835 text = static_cast< HTMLElementImpl* >( element )->innerText().string().simplified(); 02836 switch( static_cast< HTMLButtonElementImpl* >( element )->buttonType()) { 02837 case HTMLButtonElementImpl::SUBMIT: 02838 if( text.isEmpty()) 02839 text = i18n( "Submit" ); 02840 priority = 7; 02841 break; 02842 case HTMLButtonElementImpl::RESET: 02843 if( text.isEmpty()) 02844 text = i18n( "Reset" ); 02845 priority = 5; 02846 break; 02847 default: 02848 priority = 5; 02849 break; 02850 } 02851 break; 02852 case ID_SELECT: // these don't have accesskey attribute, but quick access may be handy 02853 text_before = true; 02854 text_after = true; 02855 priority = 5; 02856 break; 02857 case ID_FRAME: 02858 ignore = true; 02859 break; 02860 default: 02861 ignore = !element->isFocusable(); 02862 priority = 2; 02863 break; 02864 } 02865 if( ignore ) 02866 continue; 02867 02868 // build map of manually assigned accesskeys and their targets 02869 DOMString akey = element->getAttribute( ATTR_ACCESSKEY ); 02870 if( akey.length() == 1 ) { 02871 hrefs[url] = akey.string()[ 0 ].toUpper(); 02872 continue; // has accesskey set, ignore 02873 } 02874 if( text.isNull() && labels.contains( element )) 02875 text = labels[ element ]; 02876 if( text.isNull() && text_before ) 02877 text = getElementText( element, false ); 02878 if( text.isNull() && text_after ) 02879 text = getElementText( element, true ); 02880 text = text.trimmed(); 02881 // increase priority of items which have explicitly specified accesskeys in the config 02882 const QList< QPair< QString, QChar > > priorities 02883 = m_part->settings()->fallbackAccessKeysAssignments(); 02884 for( QList< QPair< QString, QChar > >::ConstIterator it = priorities.begin(); 02885 it != priorities.end(); 02886 ++it ) { 02887 if( text == (*it).first ) 02888 priority = 10; 02889 } 02890 AccessKeyData tmp = { element, text, url, priority }; 02891 data.append( tmp ); 02892 } 02893 } 02894 02895 QList< QChar > keys; 02896 for( char c = 'A'; c <= 'Z'; ++c ) 02897 keys << c; 02898 for( char c = '0'; c <= '9'; ++c ) 02899 keys << c; 02900 for( NodeImpl* n = m_part->xmlDocImpl(); 02901 n != NULL; 02902 n = n->traverseNextNode()) { 02903 if( n->isElementNode()) { 02904 ElementImpl* en = static_cast< ElementImpl* >( n ); 02905 DOMString s = en->getAttribute( ATTR_ACCESSKEY ); 02906 if( s.length() == 1 ) { 02907 QChar c = s.string()[ 0 ].toUpper(); 02908 keys.removeAll( c ); // remove manually assigned accesskeys 02909 } 02910 } 02911 } 02912 02913 QMap< ElementImpl*, QChar > ret; 02914 for( int priority = 10; priority >= 0; --priority ) { 02915 for( QLinkedList< AccessKeyData >::Iterator it = data.begin(); 02916 it != data.end(); 02917 ) { 02918 if( (*it).priority != priority ) { 02919 ++it; 02920 continue; 02921 } 02922 if( keys.isEmpty()) 02923 break; 02924 QString text = (*it).text; 02925 QChar key; 02926 const QString url = (*it).url; 02927 // an identical link already has an accesskey assigned 02928 if( hrefs.contains( url ) ) { 02929 it = data.erase( it ); 02930 continue; 02931 } 02932 if( !text.isEmpty()) { 02933 const QList< QPair< QString, QChar > > priorities 02934 = m_part->settings()->fallbackAccessKeysAssignments(); 02935 for( QList< QPair< QString, QChar > >::ConstIterator it = priorities.begin(); 02936 it != priorities.end(); 02937 ++it ) 02938 if( text == (*it).first && keys.contains( (*it).second )) { 02939 key = (*it).second; 02940 break; 02941 } 02942 } 02943 // try first to select the first character as the accesskey, 02944 // then first character of the following words, 02945 // and then simply the first free character 02946 if( key.isNull() && !text.isEmpty()) { 02947 const QStringList words = text.split( ' ' ); 02948 for( QStringList::ConstIterator it = words.begin(); 02949 it != words.end(); 02950 ++it ) { 02951 if( keys.contains( (*it)[ 0 ].toUpper())) { 02952 key = (*it)[ 0 ].toUpper(); 02953 break; 02954 } 02955 } 02956 } 02957 if( key.isNull() && !text.isEmpty()) { 02958 for( int i = 0; i < text.length(); ++i ) { 02959 if( keys.contains( text[ i ].toUpper())) { 02960 key = text[ i ].toUpper(); 02961 break; 02962 } 02963 } 02964 } 02965 if( key.isNull()) 02966 key = keys.front(); 02967 ret[ (*it).element ] = key; 02968 keys.removeAll( key ); 02969 it = data.erase( it ); 02970 // assign the same accesskey also to other elements pointing to the same url 02971 if( !url.isEmpty() && !url.startsWith( "javascript:", Qt::CaseInsensitive )) { 02972 for( QLinkedList< AccessKeyData >::Iterator it2 = data.begin(); 02973 it2 != data.end(); 02974 ) { 02975 if( (*it2).url == url ) { 02976 ret[ (*it2).element ] = key; 02977 if( it == it2 ) 02978 ++it; 02979 it2 = data.erase( it2 ); 02980 } else 02981 ++it2; 02982 } 02983 } 02984 } 02985 } 02986 return ret; 02987 } 02988 02989 void KHTMLView::setMediaType( const QString &medium ) 02990 { 02991 m_medium = medium; 02992 } 02993 02994 QString KHTMLView::mediaType() const 02995 { 02996 return m_medium; 02997 } 02998 02999 bool KHTMLView::pagedMode() const 03000 { 03001 return d->paged; 03002 } 03003 03004 void KHTMLView::setWidgetVisible(RenderWidget* w, bool vis) 03005 { 03006 if (vis) { 03007 d->visibleWidgets.insert(w, w->widget()); 03008 } 03009 else 03010 d->visibleWidgets.remove(w); 03011 } 03012 03013 bool KHTMLView::needsFullRepaint() const 03014 { 03015 return d->needsFullRepaint; 03016 } 03017 03018 namespace { 03019 class QPointerDeleter 03020 { 03021 public: 03022 explicit QPointerDeleter(QObject* o) : obj(o) {} 03023 ~QPointerDeleter() { delete obj; } 03024 private: 03025 const QPointer<QObject> obj; 03026 }; 03027 } 03028 03029 void KHTMLView::print(bool quick) 03030 { 03031 if(!m_part->xmlDocImpl()) return; 03032 khtml::RenderCanvas *root = static_cast<khtml::RenderCanvas *>(m_part->xmlDocImpl()->renderer()); 03033 if(!root) return; 03034 03035 QPointer<KHTMLPrintSettings> printSettings(new KHTMLPrintSettings); //XXX: doesn't save settings between prints like this 03036 const QPointerDeleter settingsDeleter(printSettings); //the printdialog takes ownership of the settings widget, thus this workaround to avoid double deletion 03037 QPrinter printer; 03038 QPointer<QPrintDialog> dialog = KdePrint::createPrintDialog(&printer, KdePrint::SystemSelectsPages, QList<QWidget*>() << printSettings.data(), this); 03039 03040 const QPointerDeleter dialogDeleter(dialog); 03041 03042 QString docname = m_part->xmlDocImpl()->URL().prettyUrl(); 03043 if ( !docname.isEmpty() ) 03044 docname = KStringHandler::csqueeze(docname, 80); 03045 03046 if(quick || (dialog->exec() && dialog)) { /*'this' and thus dialog might have been deleted while exec()!*/ 03047 viewport()->setCursor( Qt::WaitCursor ); // only viewport(), no QApplication::, otherwise we get the busy cursor in kdeprint's dialogs 03048 // set up KPrinter 03049 printer.setFullPage(false); 03050 printer.setCreator(QString("KDE %1.%2.%3 HTML Library").arg(KDE_VERSION_MAJOR).arg(KDE_VERSION_MINOR).arg(KDE_VERSION_RELEASE)); 03051 printer.setDocName(docname); 03052 03053 QPainter *p = new QPainter; 03054 p->begin( &printer ); 03055 khtml::setPrintPainter( p ); 03056 03057 m_part->xmlDocImpl()->setPaintDevice( &printer ); 03058 QString oldMediaType = mediaType(); 03059 setMediaType( "print" ); 03060 // We ignore margin settings for html and body when printing 03061 // and use the default margins from the print-system 03062 // (In Qt 3.0.x the default margins are hardcoded in Qt) 03063 m_part->xmlDocImpl()->setPrintStyleSheet( printSettings->printFriendly() ? 03064 "* { background-image: none !important;" 03065 " background-color: white !important;" 03066 " color: black !important; }" 03067 "body { margin: 0px !important; }" 03068 "html { margin: 0px !important; }" : 03069 "body { margin: 0px !important; }" 03070 "html { margin: 0px !important; }" 03071 ); 03072 03073 kDebug(6000) << "printing: physical page width = " << printer.width() 03074 << " height = " << printer.height() << endl; 03075 root->setStaticMode(true); 03076 root->setPagedMode(true); 03077 root->setWidth(printer.width()); 03078 // root->setHeight(printer.height()); 03079 root->setPageTop(0); 03080 root->setPageBottom(0); 03081 d->paged = true; 03082 03083 m_part->xmlDocImpl()->styleSelector()->computeFontSizes(printer.logicalDpiY(), 100); 03084 m_part->xmlDocImpl()->updateStyleSelector(); 03085 root->setPrintImages(printSettings->printImages()); 03086 root->makePageBreakAvoidBlocks(); 03087 03088 root->setNeedsLayoutAndMinMaxRecalc(); 03089 root->layout(); 03090 03091 // check sizes ask for action.. (scale or clip) 03092 03093 bool printHeader = printSettings->printHeader(); 03094 03095 int headerHeight = 0; 03096 QFont headerFont("Sans Serif", 8); 03097 03098 QString headerLeft = KGlobal::locale()->formatDate(QDate::currentDate(),KLocale::ShortDate); 03099 QString headerMid = docname; 03100 QString headerRight; 03101 03102 if (printHeader) 03103 { 03104 p->setFont(headerFont); 03105 headerHeight = (p->fontMetrics().lineSpacing() * 3) / 2; 03106 } 03107 03108 // ok. now print the pages. 03109 kDebug(6000) << "printing: html page width = " << root->docWidth() 03110 << " height = " << root->docHeight() << endl; 03111 kDebug(6000) << "printing: margins left = " << printer.pageRect().left() - printer.paperRect().left() 03112 << " top = " << printer.pageRect().top() - printer.paperRect().top() << endl; 03113 kDebug(6000) << "printing: paper width = " << printer.width() 03114 << " height = " << printer.height() << endl; 03115 // if the width is too large to fit on the paper we just scale 03116 // the whole thing. 03117 int pageWidth = printer.width(); 03118 int pageHeight = printer.height(); 03119 p->setClipRect(0,0, pageWidth, pageHeight); 03120 03121 pageHeight -= headerHeight; 03122 03123 #ifndef QT_NO_TRANSFORMATIONS 03124 bool scalePage = false; 03125 double scale = 0.0; 03126 if(root->docWidth() > printer.width()) { 03127 scalePage = true; 03128 scale = ((double) printer.width())/((double) root->docWidth()); 03129 pageHeight = (int) (pageHeight/scale); 03130 pageWidth = (int) (pageWidth/scale); 03131 headerHeight = (int) (headerHeight/scale); 03132 } 03133 #endif 03134 kDebug(6000) << "printing: scaled html width = " << pageWidth 03135 << " height = " << pageHeight << endl; 03136 03137 root->setHeight(pageHeight); 03138 root->setPageBottom(pageHeight); 03139 root->setNeedsLayout(true); 03140 root->layoutIfNeeded(); 03141 // m_part->slotDebugRenderTree(); 03142 03143 // Squeeze header to make it it on the page. 03144 if (printHeader) 03145 { 03146 int available_width = printer.width() - 10 - 03147 2 * qMax(p->boundingRect(0, 0, printer.width(), p->fontMetrics().lineSpacing(), Qt::AlignLeft, headerLeft).width(), 03148 p->boundingRect(0, 0, printer.width(), p->fontMetrics().lineSpacing(), Qt::AlignLeft, headerRight).width()); 03149 if (available_width < 150) 03150 available_width = 150; 03151 int mid_width; 03152 int squeeze = 120; 03153 do { 03154 headerMid = KStringHandler::csqueeze(docname, squeeze); 03155 mid_width = p->boundingRect(0, 0, printer.width(), p->fontMetrics().lineSpacing(), Qt::AlignLeft, headerMid).width(); 03156 squeeze -= 10; 03157 } while (mid_width > available_width); 03158 } 03159 03160 int top = 0; 03161 int bottom = 0; 03162 int page = 1; 03163 while(top < root->docHeight()) { 03164 if(top > 0) printer.newPage(); 03165 #ifndef QT_NO_TRANSFORMATIONS 03166 if (scalePage) 03167 p->scale(scale, scale); 03168 #endif 03169 p->save(); 03170 p->setClipRect(0, 0, pageWidth, headerHeight); 03171 if (printHeader) 03172 { 03173 int dy = p->fontMetrics().lineSpacing(); 03174 p->setPen(Qt::black); 03175 p->setFont(headerFont); 03176 03177 headerRight = QString("#%1").arg(page); 03178 03179 p->drawText(0, 0, printer.width(), dy, Qt::AlignLeft, headerLeft); 03180 p->drawText(0, 0, printer.width(), dy, Qt::AlignHCenter, headerMid); 03181 p->drawText(0, 0, printer.width(), dy, Qt::AlignRight, headerRight); 03182 } 03183 03184 p->restore(); 03185 p->translate(0, headerHeight-top); 03186 03187 bottom = top+pageHeight; 03188 03189 root->setPageTop(top); 03190 root->setPageBottom(bottom); 03191 root->setPageNumber(page); 03192 03193 root->layer()->paint(p, QRect(0, top, pageWidth, pageHeight)); 03194 kDebug(6000) << "printed: page " << page <<" bottom At = " << bottom; 03195 03196 top = bottom; 03197 p->resetTransform(); 03198 page++; 03199 } 03200 03201 p->end(); 03202 delete p; 03203 03204 // and now reset the layout to the usual one... 03205 root->setPagedMode(false); 03206 root->setStaticMode(false); 03207 d->paged = false; 03208 khtml::setPrintPainter( 0 ); 03209 setMediaType( oldMediaType ); 03210 m_part->xmlDocImpl()->setPaintDevice( this ); 03211 m_part->xmlDocImpl()->styleSelector()->computeFontSizes(m_part->xmlDocImpl()->logicalDpiY(), m_part->fontScaleFactor()); 03212 m_part->xmlDocImpl()->updateStyleSelector(); 03213 viewport()->unsetCursor(); 03214 } 03215 } 03216 03217 void KHTMLView::slotPaletteChanged() 03218 { 03219 if(!m_part->xmlDocImpl()) return; 03220 DOM::DocumentImpl *document = m_part->xmlDocImpl(); 03221 if (!document->isHTMLDocument()) return; 03222 khtml::RenderCanvas *root = static_cast<khtml::RenderCanvas *>(document->renderer()); 03223 if(!root) return; 03224 root->style()->resetPalette(); 03225 NodeImpl *body = static_cast<HTMLDocumentImpl*>(document)->body(); 03226 if(!body) return; 03227 body->setChanged(true); 03228 body->recalcStyle( NodeImpl::Force ); 03229 } 03230 03231 void KHTMLView::paint(QPainter *p, const QRect &rc, int yOff, bool *more) 03232 { 03233 if(!m_part->xmlDocImpl()) return; 03234 khtml::RenderCanvas *root = static_cast<khtml::RenderCanvas *>(m_part->xmlDocImpl()->renderer()); 03235 if(!root) return; 03236 #ifdef SPEED_DEBUG 03237 d->firstRepaintPending = false; 03238 #endif 03239 03240 QPaintDevice* opd = m_part->xmlDocImpl()->paintDevice(); 03241 m_part->xmlDocImpl()->setPaintDevice(p->device()); 03242 root->setPagedMode(true); 03243 root->setStaticMode(true); 03244 root->setWidth(rc.width()); 03245 03246 // save() 03247 QRegion creg = p->clipRegion(); 03248 QTransform t = p->worldTransform(); 03249 QRect w = p->window(); 03250 QRect v = p->viewport(); 03251 bool vte = p->viewTransformEnabled(); 03252 bool wme = p->worldMatrixEnabled(); 03253 03254 p->setClipRect(rc); 03255 p->translate(rc.left(), rc.top()); 03256 double scale = ((double) rc.width()/(double) root->docWidth()); 03257 int height = (int) ((double) rc.height() / scale); 03258 #ifndef QT_NO_TRANSFORMATIONS 03259 p->scale(scale, scale); 03260 #endif 03261 root->setPageTop(yOff); 03262 root->setPageBottom(yOff+height); 03263 03264 root->layer()->paint(p, QRect(0, yOff, root->docWidth(), height)); 03265 if (more) 03266 *more = yOff + height < root->docHeight(); 03267 03268 // restore() 03269 p->setWorldTransform(t); 03270 p->setWindow(w); 03271 p->setViewport(v); 03272 p->setViewTransformEnabled( vte ); 03273 p->setWorldMatrixEnabled( wme ); 03274 if (!creg.isEmpty()) 03275 p->setClipRegion( creg ); 03276 else 03277 p->setClipRegion(QRegion(), Qt::NoClip); 03278 03279 root->setPagedMode(false); 03280 root->setStaticMode(false); 03281 m_part->xmlDocImpl()->setPaintDevice( opd ); 03282 } 03283 03284 void KHTMLView::render(QPainter* p, const QRect& r, const QPoint& off) 03285 { 03286 #ifdef SPEED_DEBUG 03287 d->firstRepaintPending = false; 03288 #endif 03289 QRect clip(off.x()+r.x(), off.y()+r.y(),r.width(),r.height()); 03290 if(!m_part || !m_part->xmlDocImpl() || !m_part->xmlDocImpl()->renderer()) { 03291 p->fillRect(clip, palette().brush(QPalette::Active, QPalette::Base)); 03292 return; 03293 } 03294 QPaintDevice* opd = m_part->xmlDocImpl()->paintDevice(); 03295 m_part->xmlDocImpl()->setPaintDevice(p->device()); 03296 03297 // save() 03298 QRegion creg = p->clipRegion(); 03299 QTransform t = p->worldTransform(); 03300 QRect w = p->window(); 03301 QRect v = p->viewport(); 03302 bool vte = p->viewTransformEnabled(); 03303 bool wme = p->worldMatrixEnabled(); 03304 03305 p->setClipRect(clip); 03306 QRect rect = r.translated(contentsX(),contentsY()); 03307 p->translate(off.x()-contentsX(), off.y()-contentsY()); 03308 03309 m_part->xmlDocImpl()->renderer()->layer()->paint(p, rect); 03310 03311 // restore() 03312 p->setWorldTransform(t); 03313 p->setWindow(w); 03314 p->setViewport(v); 03315 p->setViewTransformEnabled( vte ); 03316 p->setWorldMatrixEnabled( wme ); 03317 if (!creg.isEmpty()) 03318 p->setClipRegion( creg ); 03319 else 03320 p->setClipRegion(QRegion(), Qt::NoClip); 03321 03322 m_part->xmlDocImpl()->setPaintDevice( opd ); 03323 } 03324 03325 void KHTMLView::setHasStaticBackground(bool partial) 03326 { 03327 // full static iframe is irreversible for now 03328 if (d->staticWidget == KHTMLViewPrivate::SBFull && m_kwp->isRedirected()) 03329 return; 03330 03331 d->staticWidget = partial ? 03332 KHTMLViewPrivate::SBPartial : KHTMLViewPrivate::SBFull; 03333 } 03334 03335 void KHTMLView::setHasNormalBackground() 03336 { 03337 // full static iframe is irreversible for now 03338 if (d->staticWidget == KHTMLViewPrivate::SBFull && m_kwp->isRedirected()) 03339 return; 03340 03341 d->staticWidget = KHTMLViewPrivate::SBNone; 03342 } 03343 03344 void KHTMLView::addStaticObject(bool fixed) 03345 { 03346 if (fixed) 03347 d->fixedObjectsCount++; 03348 else 03349 d->staticObjectsCount++; 03350 03351 setHasStaticBackground( true /*partial*/ ); 03352 } 03353 03354 void KHTMLView::removeStaticObject(bool fixed) 03355 { 03356 if (fixed) 03357 d->fixedObjectsCount--; 03358 else 03359 d->staticObjectsCount--; 03360 03361 assert( d->fixedObjectsCount >= 0 && d->staticObjectsCount >= 0 ); 03362 03363 if (!d->staticObjectsCount && !d->fixedObjectsCount) 03364 setHasNormalBackground(); 03365 else 03366 setHasStaticBackground( true /*partial*/ ); 03367 } 03368 03369 void KHTMLView::setVerticalScrollBarPolicy( Qt::ScrollBarPolicy policy ) 03370 { 03371 #ifndef KHTML_NO_SCROLLBARS 03372 d->vpolicy = policy; 03373 QScrollArea::setVerticalScrollBarPolicy(policy); 03374 #else 03375 Q_UNUSED( policy ); 03376 #endif 03377 } 03378 03379 void KHTMLView::setHorizontalScrollBarPolicy( Qt::ScrollBarPolicy policy ) 03380 { 03381 #ifndef KHTML_NO_SCROLLBARS 03382 d->hpolicy = policy; 03383 QScrollArea::setHorizontalScrollBarPolicy(policy); 03384 #else 03385 Q_UNUSED( policy ); 03386 #endif 03387 } 03388 03389 void KHTMLView::restoreScrollBar() 03390 { 03391 int ow = visibleWidth(); 03392 QScrollArea::setVerticalScrollBarPolicy(d->vpolicy); 03393 if (visibleWidth() != ow) 03394 layout(); 03395 d->prevScrollbarVisible = verticalScrollBar()->isVisible(); 03396 } 03397 03398 QStringList KHTMLView::formCompletionItems(const QString &name) const 03399 { 03400 if (!m_part->settings()->isFormCompletionEnabled()) 03401 return QStringList(); 03402 if (!d->formCompletions) 03403 d->formCompletions = new KConfig(KStandardDirs::locateLocal("data", "khtml/formcompletions")); 03404 return d->formCompletions->group("").readEntry(name, QStringList()); 03405 } 03406 03407 void KHTMLView::clearCompletionHistory(const QString& name) 03408 { 03409 if (!d->formCompletions) 03410 { 03411 d->formCompletions = new KConfig(KStandardDirs::locateLocal("data", "khtml/formcompletions")); 03412 } 03413 d->formCompletions->group("").writeEntry(name, ""); 03414 d->formCompletions->sync(); 03415 } 03416 03417 void KHTMLView::addFormCompletionItem(const QString &name, const QString &value) 03418 { 03419 if (!m_part->settings()->isFormCompletionEnabled()) 03420 return; 03421 // don't store values that are all numbers or just numbers with 03422 // dashes or spaces as those are likely credit card numbers or 03423 // something similar 03424 bool cc_number(true); 03425 for ( int i = 0; i < value.length(); ++i) 03426 { 03427 QChar c(value[i]); 03428 if (!c.isNumber() && c != '-' && !c.isSpace()) 03429 { 03430 cc_number = false; 03431 break; 03432 } 03433 } 03434 if (cc_number) 03435 return; 03436 QStringList items = formCompletionItems(name); 03437 if (!items.contains(value)) 03438 items.prepend(value); 03439 while ((int)items.count() > m_part->settings()->maxFormCompletionItems()) 03440 items.erase(items.isEmpty() ? items.end() : --items.end()); 03441 d->formCompletions->group("").writeEntry(name, items); 03442 } 03443 03444 void KHTMLView::addNonPasswordStorableSite(const QString& host) 03445 { 03446 if (!d->formCompletions) { 03447 d->formCompletions = new KConfig(KStandardDirs::locateLocal("data", "khtml/formcompletions")); 03448 } 03449 03450 KConfigGroup cg( d->formCompletions, "NonPasswordStorableSites"); 03451 QStringList sites = cg.readEntry("Sites", QStringList()); 03452 sites.append(host); 03453 cg.writeEntry("Sites", sites); 03454 cg.sync(); 03455 } 03456 03457 03458 void KHTMLView::delNonPasswordStorableSite(const QString& host) 03459 { 03460 if (!d->formCompletions) { 03461 d->formCompletions = new KConfig(KStandardDirs::locateLocal("data", "khtml/formcompletions")); 03462 } 03463 03464 KConfigGroup cg( d->formCompletions, "NonPasswordStorableSites"); 03465 QStringList sites = cg.readEntry("Sites", QStringList()); 03466 sites.removeOne(host); 03467 cg.writeEntry("Sites", sites); 03468 cg.sync(); 03469 } 03470 03471 bool KHTMLView::nonPasswordStorableSite(const QString& host) const 03472 { 03473 if (!d->formCompletions) { 03474 d->formCompletions = new KConfig(KStandardDirs::locateLocal("data", "khtml/formcompletions")); 03475 } 03476 QStringList sites = d->formCompletions->group( "NonPasswordStorableSites" ).readEntry("Sites", QStringList()); 03477 return (sites.indexOf(host) != -1); 03478 } 03479 03480 // returns true if event should be swallowed 03481 bool KHTMLView::dispatchMouseEvent(int eventId, DOM::NodeImpl *targetNode, 03482 DOM::NodeImpl *targetNodeNonShared, bool cancelable, 03483 int detail,QMouseEvent *_mouse, bool setUnder, 03484 int mouseEventType, int orient) 03485 { 03486 // if the target node is a text node, dispatch on the parent node - rdar://4196646 (and #76948) 03487 if (targetNode && targetNode->isTextNode()) 03488 targetNode = targetNode->parentNode(); 03489 03490 if (d->underMouse) 03491 d->underMouse->deref(); 03492 d->underMouse = targetNode; 03493 if (d->underMouse) 03494 d->underMouse->ref(); 03495 03496 if (d->underMouseNonShared) 03497 d->underMouseNonShared->deref(); 03498 d->underMouseNonShared = targetNodeNonShared; 03499 if (d->underMouseNonShared) 03500 d->underMouseNonShared->ref(); 03501 03502 bool isWheelEvent = (mouseEventType == DOM::NodeImpl::MouseWheel); 03503 03504 int exceptioncode = 0; 03505 int pageX = _mouse->x(); 03506 int pageY = _mouse->y(); 03507 revertTransforms(pageX, pageY); 03508 int clientX = pageX - contentsX(); 03509 int clientY = pageY - contentsY(); 03510 int screenX = _mouse->globalX(); 03511 int screenY = _mouse->globalY(); 03512 int button = -1; 03513 switch (_mouse->button()) { 03514 case Qt::LeftButton: 03515 button = 0; 03516 break; 03517 case Qt::MidButton: 03518 button = 1; 03519 break; 03520 case Qt::RightButton: 03521 button = 2; 03522 break; 03523 default: 03524 break; 03525 } 03526 if (d->accessKeysEnabled && d->accessKeysPreActivate && button!=-1) 03527 d->accessKeysPreActivate=false; 03528 03529 bool ctrlKey = (_mouse->modifiers() & Qt::ControlModifier); 03530 bool altKey = (_mouse->modifiers() & Qt::AltModifier); 03531 bool shiftKey = (_mouse->modifiers() & Qt::ShiftModifier); 03532 bool metaKey = (_mouse->modifiers() & Qt::MetaModifier); 03533 03534 // mouseout/mouseover 03535 if (setUnder && d->oldUnderMouse != targetNode) { 03536 if (d->oldUnderMouse && d->oldUnderMouse->document() != m_part->xmlDocImpl()) { 03537 d->oldUnderMouse->deref(); 03538 d->oldUnderMouse = 0; 03539 } 03540 // send mouseout event to the old node 03541 if (d->oldUnderMouse) { 03542 // send mouseout event to the old node 03543 MouseEventImpl *me = new MouseEventImpl(EventImpl::MOUSEOUT_EVENT, 03544 true,true,m_part->xmlDocImpl()->defaultView(), 03545 0,screenX,screenY,clientX,clientY,pageX, pageY, 03546 ctrlKey,altKey,shiftKey,metaKey, 03547 button,targetNode); 03548 me->ref(); 03549 d->oldUnderMouse->dispatchEvent(me,exceptioncode,true); 03550 me->deref(); 03551 } 03552 // send mouseover event to the new node 03553 if (targetNode) { 03554 MouseEventImpl *me = new MouseEventImpl(EventImpl::MOUSEOVER_EVENT, 03555 true,true,m_part->xmlDocImpl()->defaultView(), 03556 0,screenX,screenY,clientX,clientY,pageX, pageY, 03557 ctrlKey,altKey,shiftKey,metaKey, 03558 button,d->oldUnderMouse); 03559 03560 me->ref(); 03561 targetNode->dispatchEvent(me,exceptioncode,true); 03562 me->deref(); 03563 } 03564 if (d->oldUnderMouse) 03565 d->oldUnderMouse->deref(); 03566 d->oldUnderMouse = targetNode; 03567 if (d->oldUnderMouse) 03568 d->oldUnderMouse->ref(); 03569 } 03570 03571 bool swallowEvent = false; 03572 03573 if (targetNode) { 03574 // if the target node is a disabled widget, we don't want any full-blown mouse events 03575 if (targetNode->isGenericFormElement() 03576 && static_cast<HTMLGenericFormElementImpl*>(targetNode)->disabled()) 03577 return true; 03578 03579 // send the actual event 03580 bool dblclick = ( eventId == EventImpl::CLICK_EVENT && 03581 _mouse->type() == QEvent::MouseButtonDblClick ); 03582 MouseEventImpl *me = new MouseEventImpl(static_cast<EventImpl::EventId>(eventId), 03583 true,cancelable,m_part->xmlDocImpl()->defaultView(), 03584 detail,screenX,screenY,clientX,clientY,pageX, pageY, 03585 ctrlKey,altKey,shiftKey,metaKey, 03586 button,0, isWheelEvent ? 0 : _mouse, dblclick, 03587 isWheelEvent ? static_cast<MouseEventImpl::Orientation>(orient) : MouseEventImpl::ONone ); 03588 me->ref(); 03589 if ( !d->m_mouseEventsTarget && RenderLayer::gScrollBar && eventId == EventImpl::MOUSEDOWN_EVENT ) 03590 // button is pressed inside a layer scrollbar, so make it the target for future mousemove events until released 03591 d->m_mouseEventsTarget = RenderLayer::gScrollBar; 03592 if ( d->m_mouseEventsTarget && qobject_cast<QScrollBar*>(d->m_mouseEventsTarget) && 03593 dynamic_cast<KHTMLWidget*>(static_cast<QWidget*>(d->m_mouseEventsTarget)) ) { 03594 // we have a sticky mouse event target and it is a layer's scrollbar. Forward events manually. 03595 // ### should use the dom 03596 KHTMLWidget*w = dynamic_cast<KHTMLWidget*>(static_cast<QWidget*>(d->m_mouseEventsTarget)); 03597 QPoint p = w->m_kwp->absolutePos(); 03598 QMouseEvent fw(_mouse->type(), QPoint(pageX, pageY)-p, _mouse->button(), _mouse->buttons(), _mouse->modifiers()); 03599 static_cast<RenderWidget::EventPropagator *>(static_cast<QWidget*>(d->m_mouseEventsTarget))->sendEvent(&fw); 03600 if (_mouse->type() == QMouseEvent::MouseButtonPress && _mouse->button() == Qt::RightButton) { 03601 QContextMenuEvent cme(QContextMenuEvent::Mouse, p); 03602 static_cast<RenderWidget::EventPropagator *>(static_cast<QWidget*>(d->m_mouseEventsTarget))->sendEvent(&cme); 03603 d->m_mouseEventsTarget = 0; 03604 } 03605 swallowEvent = true; 03606 } else { 03607 targetNode->dispatchEvent(me,exceptioncode,true); 03608 bool defaultHandled = me->defaultHandled(); 03609 if (defaultHandled || me->defaultPrevented()) 03610 swallowEvent = true; 03611 } 03612 if (eventId == EventImpl::MOUSEDOWN_EVENT && !me->defaultPrevented()) { 03613 // Focus should be shifted on mouse down, not on a click. -dwh 03614 // Blur current focus node when a link/button is clicked; this 03615 // is expected by some sites that rely on onChange handlers running 03616 // from form fields before the button click is processed. 03617 DOM::NodeImpl* nodeImpl = targetNode; 03618 for ( ; nodeImpl && !nodeImpl->isFocusable(); nodeImpl = nodeImpl->parentNode()) 03619 {} 03620 if (nodeImpl && nodeImpl->isMouseFocusable()) 03621 m_part->xmlDocImpl()->setFocusNode(nodeImpl); 03622 else if (!nodeImpl || !nodeImpl->focused()) 03623 m_part->xmlDocImpl()->setFocusNode(0); 03624 } 03625 me->deref(); 03626 } 03627 03628 return swallowEvent; 03629 } 03630 03631 void KHTMLView::setIgnoreWheelEvents( bool e ) 03632 { 03633 d->ignoreWheelEvents = e; 03634 } 03635 03636 #ifndef QT_NO_WHEELEVENT 03637 03638 void KHTMLView::wheelEvent(QWheelEvent* e) 03639 { 03640 // check if we should reset the state of the indicator describing if 03641 // we are currently scrolling the view as a result of wheel events 03642 if (d->scrollingFromWheel != QPoint(-1,-1) && d->scrollingFromWheel != QCursor::pos()) 03643 d->scrollingFromWheel = d->scrollingFromWheelTimerId ? QCursor::pos() : QPoint(-1,-1); 03644 03645 if (d->accessKeysEnabled && d->accessKeysPreActivate) d->accessKeysPreActivate=false; 03646 03647 if ( ( e->modifiers() & Qt::ControlModifier) == Qt::ControlModifier ) 03648 { 03649 emit zoomView( - e->delta() ); 03650 e->accept(); 03651 } 03652 else if (d->firstLayoutPending) 03653 { 03654 e->accept(); 03655 } 03656 else if( !m_kwp->isRedirected() && 03657 ( (e->orientation() == Qt::Vertical && 03658 ((d->ignoreWheelEvents && !verticalScrollBar()->isVisible()) 03659 || (e->delta() > 0 && contentsY() <= 0) 03660 || (e->delta() < 0 && contentsY() >= contentsHeight() - visibleHeight()))) 03661 || 03662 (e->orientation() == Qt::Horizontal && 03663 ((d->ignoreWheelEvents && !horizontalScrollBar()->isVisible()) 03664 || (e->delta() > 0 && contentsX() <=0) 03665 || (e->delta() < 0 && contentsX() >= contentsWidth() - visibleWidth())))) 03666 && m_part->parentPart()) 03667 { 03668 if ( m_part->parentPart()->view() ) 03669 m_part->parentPart()->view()->wheelEvent( e ); 03670 e->ignore(); 03671 } 03672 else 03673 { 03674 int xm = e->x(); 03675 int ym = e->y(); 03676 revertTransforms(xm, ym); 03677 03678 DOM::NodeImpl::MouseEvent mev( e->buttons(), DOM::NodeImpl::MouseWheel ); 03679 m_part->xmlDocImpl()->prepareMouseEvent( false, xm, ym, &mev ); 03680 03681 MouseEventImpl::Orientation o = MouseEventImpl::OVertical; 03682 if (e->orientation() == Qt::Horizontal) 03683 o = MouseEventImpl::OHorizontal; 03684 03685 QMouseEvent _mouse(QEvent::MouseMove, e->pos(), Qt::NoButton, e->buttons(), e->modifiers()); 03686 bool swallow = dispatchMouseEvent(EventImpl::KHTML_MOUSEWHEEL_EVENT,mev.innerNode.handle(),mev.innerNonSharedNode.handle(), 03687 true,-e->delta()/40,&_mouse,true,DOM::NodeImpl::MouseWheel,o); 03688 03689 if (swallow) 03690 return; 03691 03692 d->scrollBarMoved = true; 03693 d->scrollingFromWheel = QCursor::pos(); 03694 if (d->smoothScrollMode != SSMDisabled) 03695 d->shouldSmoothScroll = true; 03696 if (d->scrollingFromWheelTimerId) 03697 killTimer(d->scrollingFromWheelTimerId); 03698 d->scrollingFromWheelTimerId = startTimer(400); 03699 03700 if (m_part->parentPart()) { 03701 // don't propagate if we are a sub-frame and our scrollbars are already at end of range 03702 bool h = (static_cast<QWheelEvent*>(e)->orientation() == Qt::Horizontal); 03703 bool d = (static_cast<QWheelEvent*>(e)->delta() < 0); 03704 QScrollBar* hsb = horizontalScrollBar(); 03705 QScrollBar* vsb = verticalScrollBar(); 03706 if ( (h && ((d && hsb->value() == hsb->maximum()) || (!d && hsb->value() == hsb->minimum()))) || 03707 (!h && ((d && vsb->value() == vsb->maximum()) || (!d && vsb->value() == vsb->minimum()))) ) { 03708 e->accept(); 03709 return; 03710 } 03711 } 03712 QScrollArea::wheelEvent( e ); 03713 } 03714 03715 } 03716 #endif 03717 03718 void KHTMLView::dragEnterEvent( QDragEnterEvent* ev ) 03719 { 03720 // Still overridden for BC reasons only... 03721 QScrollArea::dragEnterEvent( ev ); 03722 } 03723 03724 void KHTMLView::dropEvent( QDropEvent *ev ) 03725 { 03726 // Still overridden for BC reasons only... 03727 QScrollArea::dropEvent( ev ); 03728 } 03729 03730 void KHTMLView::focusInEvent( QFocusEvent *e ) 03731 { 03732 DOM::NodeImpl* fn = m_part->xmlDocImpl() ? m_part->xmlDocImpl()->focusNode() : 0; 03733 if (fn && fn->renderer() && fn->renderer()->isWidget() && 03734 (e->reason() != Qt::MouseFocusReason) && 03735 static_cast<khtml::RenderWidget*>(fn->renderer())->widget()) 03736 static_cast<khtml::RenderWidget*>(fn->renderer())->widget()->setFocus(); 03737 m_part->setSelectionVisible(); 03738 QScrollArea::focusInEvent( e ); 03739 } 03740 03741 void KHTMLView::focusOutEvent( QFocusEvent *e ) 03742 { 03743 if (m_part) { 03744 m_part->stopAutoScroll(); 03745 m_part->setSelectionVisible(false); 03746 } 03747 03748 if ( d->cursorIconWidget ) 03749 d->cursorIconWidget->hide(); 03750 03751 QScrollArea::focusOutEvent( e ); 03752 } 03753 03754 void KHTMLView::scrollContentsBy( int dx, int dy ) 03755 { 03756 if (!dx && !dy) return; 03757 03758 if ( !d->firstLayoutPending && !d->complete && m_part->xmlDocImpl() && 03759 d->layoutSchedulingEnabled) { 03760 // contents scroll while we are not complete: we need to check our layout *now* 03761 khtml::RenderCanvas* root = static_cast<khtml::RenderCanvas *>( m_part->xmlDocImpl()->renderer() ); 03762 if (root && root->needsLayout()) { 03763 unscheduleRelayout(); 03764 layout(); 03765 } 03766 } 03767 03768 if ( d->shouldSmoothScroll && d->smoothScrollMode != SSMDisabled && m_part->xmlDocImpl() && 03769 m_part->xmlDocImpl()->renderer() && (d->smoothScrollMode != SSMWhenEfficient || d->smoothScrollMissedDeadlines != sWayTooMany)) { 03770 03771 bool doSmoothScroll = (!d->staticWidget || d->smoothScrollMode == SSMEnabled); 03772 03773 int numStaticPixels = 0; 03774 QRegion r = static_cast<RenderCanvas*>(m_part->xmlDocImpl()->renderer())->staticRegion(); 03775 03776 // only do smooth scrolling if static region is relatively small 03777 if (!doSmoothScroll && d->staticWidget == KHTMLViewPrivate::SBPartial && r.rects().size() <= 10) { 03778 foreach(const QRect &rr, r.rects()) 03779 numStaticPixels += rr.width()*rr.height(); 03780 if ((numStaticPixels < sSmoothScrollMinStaticPixels) || (numStaticPixels*8 < visibleWidth()*visibleHeight())) 03781 doSmoothScroll = true; 03782 } 03783 if (doSmoothScroll) { 03784 setupSmoothScrolling(dx, dy); 03785 return; 03786 } 03787 } 03788 03789 if ( underMouse() && QToolTip::isVisible() ) 03790 QToolTip::hideText(); 03791 03792 if (!d->scrollingSelf) { 03793 d->scrollBarMoved = true; 03794 d->contentsMoving = true; 03795 // ensure quick reset of contentsMoving flag 03796 scheduleRepaint(0, 0, 0, 0); 03797 } 03798 03799 if (m_part->xmlDocImpl() && m_part->xmlDocImpl()->documentElement()) { 03800 m_part->xmlDocImpl()->documentElement()->dispatchHTMLEvent(EventImpl::SCROLL_EVENT, true, false); 03801 } 03802 03803 if (QApplication::isRightToLeft()) 03804 dx = -dx; 03805 03806 if (!d->smoothScrolling) { 03807 d->updateContentsXY(); 03808 } else { 03809 d->contentsX -= dx; 03810 d->contentsY -= dy; 03811 } 03812 if (widget()->pos() != QPoint(0,0)) { 03813 kDebug(6000) << "Static widget wasn't positioned at (0,0). This should NOT happen. Please report this event to developers."; 03814 kDebug(6000) << kBacktrace(); 03815 widget()->move(0,0); 03816 } 03817 03818 QWidget *w = widget(); 03819 QPoint off; 03820 if (m_kwp->isRedirected()) { 03821 // This is a redirected sub frame. Translate to root view context 03822 KHTMLView* v = m_kwp->rootViewPos( off ); 03823 if (v) 03824 w = v->widget(); 03825 off = viewport()->mapTo(this, off); 03826 } 03827 03828 if ( d->staticWidget ) { 03829 03830 // now remove from view the external widgets that must have completely 03831 // disappeared after dx/dy scroll delta is effective 03832 if (!d->visibleWidgets.isEmpty()) 03833 checkExternalWidgetsPosition(); 03834 03835 if ( d->staticWidget == KHTMLViewPrivate::SBPartial 03836 && m_part->xmlDocImpl() && m_part->xmlDocImpl()->renderer() ) { 03837 // static objects might be selectively repainted, like stones in flowing water 03838 QRegion r = static_cast<RenderCanvas*>(m_part->xmlDocImpl()->renderer())->staticRegion(); 03839 r.translate( -contentsX(), -contentsY()); 03840 QVector<QRect> ar = r.rects(); 03841 03842 for (int i = 0; i < ar.size() ; ++i) { 03843 widget()->update( ar[i] ); 03844 } 03845 r = QRegion(QRect(0, 0, visibleWidth(), visibleHeight())) - r; 03846 ar = r.rects(); 03847 for (int i = 0; i < ar.size() ; ++i) { 03848 w->scroll( dx, dy, ar[i].translated(off) ); 03849 } 03850 d->scrollExternalWidgets(dx, dy); 03851 } else { 03852 // we can't avoid a full update 03853 widget()->update(); 03854 } 03855 if (d->accessKeysActivated) 03856 d->scrollAccessKeys(dx, dy); 03857 03858 return; 03859 } 03860 03861 if (m_kwp->isRedirected()) { 03862 const QRect rect(off.x(), off.y(), visibleWidth() * d->zoomLevel / 100, visibleHeight() * d->zoomLevel / 100); 03863 w->scroll(dx, dy, rect); 03864 if (d->zoomLevel != 100) { 03865 w->update(rect); // without this update we are getting bad rendering when an iframe is zoomed in 03866 } 03867 } else { 03868 widget()->scroll(dx, dy, widget()->rect() & viewport()->rect()); 03869 } 03870 03871 d->scrollExternalWidgets(dx, dy); 03872 if (d->accessKeysActivated) 03873 d->scrollAccessKeys(dx, dy); 03874 } 03875 03876 void KHTMLView::setupSmoothScrolling(int dx, int dy) 03877 { 03878 // old or minimum speed 03879 int ddx = qMax(d->steps ? abs(d->dx)/d->steps : 0,3); 03880 int ddy = qMax(d->steps ? abs(d->dy)/d->steps : 0,3); 03881 03882 // full scroll is remaining scroll plus new scroll 03883 d->dx = d->dx + dx; 03884 d->dy = d->dy + dy; 03885 03886 if (d->dx == 0 && d->dy == 0) { 03887 d->stopScrolling(); 03888 return; 03889 } 03890 03891 d->steps = (sSmoothScrollTime-1)/sSmoothScrollTick + 1; 03892 03893 if (qMax(abs(d->dx), abs(d->dy)) / d->steps < qMax(ddx,ddy)) { 03894 // Don't move slower than average 4px/step in minimum one direction 03895 // This means fewer than normal steps 03896 d->steps = qMax((abs(d->dx)+ddx-1)/ddx, (abs(d->dy)+ddy-1)/ddy); 03897 if (d->steps < 1) d->steps = 1; 03898 } 03899 03900 d->smoothScrollStopwatch.start(); 03901 if (!d->smoothScrolling) { 03902 d->startScrolling(); 03903 scrollTick(); 03904 } 03905 } 03906 03907 void KHTMLView::scrollTick() { 03908 if (d->dx == 0 && d->dy == 0) { 03909 d->stopScrolling(); 03910 return; 03911 } 03912 03913 if (d->steps < 1) d->steps = 1; 03914 int takesteps = d->smoothScrollStopwatch.restart() / sSmoothScrollTick; 03915 int scroll_x = 0; 03916 int scroll_y = 0; 03917 if (takesteps < 1) takesteps = 1; 03918 if (takesteps > d->steps) takesteps = d->steps; 03919 for(int i = 0; i < takesteps; i++) { 03920 int ddx = (d->dx / (d->steps+1)) * 2; 03921 int ddy = (d->dy / (d->steps+1)) * 2; 03922 03923 // limit step to requested scrolling distance 03924 if (abs(ddx) > abs(d->dx)) ddx = d->dx; 03925 if (abs(ddy) > abs(d->dy)) ddy = d->dy; 03926 03927 // update remaining scroll 03928 d->dx -= ddx; 03929 d->dy -= ddy; 03930 scroll_x += ddx; 03931 scroll_y += ddy; 03932 d->steps--; 03933 } 03934 03935 d->shouldSmoothScroll = false; 03936 scrollContentsBy(scroll_x, scroll_y); 03937 03938 if (takesteps < 2) { 03939 d->smoothScrollMissedDeadlines = 0; 03940 } else { 03941 if (d->smoothScrollMissedDeadlines != sWayTooMany && 03942 (!m_part->xmlDocImpl() || !m_part->xmlDocImpl()->parsing())) { 03943 d->smoothScrollMissedDeadlines++; 03944 if (d->smoothScrollMissedDeadlines >= sMaxMissedDeadlines) { 03945 // we missed many deadlines in a row! 03946 // time to signal we had enough.. 03947 d->smoothScrollMissedDeadlines = sWayTooMany; 03948 } 03949 } 03950 } 03951 } 03952 03953 03954 void KHTMLView::addChild(QWidget * child, int x, int y) 03955 { 03956 if (!child) 03957 return; 03958 03959 if (child->parent() != widget()) 03960 child->setParent( widget() ); 03961 03962 // ### handle pseudo-zooming of non-redirected widgets (e.g. just resize'em) 03963 03964 child->move(x-contentsX(), y-contentsY()); 03965 } 03966 03967 void KHTMLView::timerEvent ( QTimerEvent *e ) 03968 { 03969 // kDebug() << "timer event " << e->timerId(); 03970 if ( e->timerId() == d->scrollTimerId ) { 03971 if( d->scrollSuspended ) 03972 return; 03973 switch (d->scrollDirection) { 03974 case KHTMLViewPrivate::ScrollDown: 03975 if (contentsY() + visibleHeight () >= contentsHeight()) 03976 d->newScrollTimer(this, 0); 03977 else 03978 verticalScrollBar()->setValue( verticalScrollBar()->value() +d->scrollBy ); 03979 break; 03980 case KHTMLViewPrivate::ScrollUp: 03981 if (contentsY() <= 0) 03982 d->newScrollTimer(this, 0); 03983 else 03984 verticalScrollBar()->setValue( verticalScrollBar()->value() -d->scrollBy ); 03985 break; 03986 case KHTMLViewPrivate::ScrollRight: 03987 if (contentsX() + visibleWidth () >= contentsWidth()) 03988 d->newScrollTimer(this, 0); 03989 else 03990 horizontalScrollBar()->setValue( horizontalScrollBar()->value() +d->scrollBy ); 03991 break; 03992 case KHTMLViewPrivate::ScrollLeft: 03993 if (contentsX() <= 0) 03994 d->newScrollTimer(this, 0); 03995 else 03996 horizontalScrollBar()->setValue( horizontalScrollBar()->value() -d->scrollBy ); 03997 break; 03998 } 03999 return; 04000 } 04001 else if ( e->timerId() == d->scrollingFromWheelTimerId ) { 04002 killTimer( d->scrollingFromWheelTimerId ); 04003 d->scrollingFromWheelTimerId = 0; 04004 } else if ( e->timerId() == d->layoutTimerId ) { 04005 if (d->firstLayoutPending && d->layoutAttemptCounter < 4 04006 && (!m_part->xmlDocImpl() || !m_part->xmlDocImpl()->readyForLayout())) { 04007 d->layoutAttemptCounter++; 04008 killTimer(d->layoutTimerId); 04009 d->layoutTimerId = 0; 04010 scheduleRelayout(); 04011 return; 04012 } 04013 layout(); 04014 d->scheduledLayoutCounter++; 04015 if (d->firstLayoutPending) { 04016 d->firstLayoutPending = false; 04017 verticalScrollBar()->setEnabled( true ); 04018 horizontalScrollBar()->setEnabled( true ); 04019 } 04020 } 04021 04022 d->contentsMoving = false; 04023 if( m_part->xmlDocImpl() ) { 04024 DOM::DocumentImpl *document = m_part->xmlDocImpl(); 04025 khtml::RenderCanvas* root = static_cast<khtml::RenderCanvas *>(document->renderer()); 04026 04027 if ( root && root->needsLayout() ) { 04028 if (d->repaintTimerId) 04029 killTimer(d->repaintTimerId); 04030 d->repaintTimerId = 0; 04031 scheduleRelayout(); 04032 return; 04033 } 04034 } 04035 04036 if (d->repaintTimerId) 04037 killTimer(d->repaintTimerId); 04038 d->repaintTimerId = 0; 04039 04040 QRect updateRegion; 04041 const QVector<QRect> rects = d->updateRegion.rects(); 04042 04043 d->updateRegion = QRegion(); 04044 04045 if ( rects.size() ) 04046 updateRegion = rects[0]; 04047 04048 for ( int i = 1; i < rects.size(); ++i ) { 04049 QRect newRegion = updateRegion.unite(rects[i]); 04050 if (2*newRegion.height() > 3*updateRegion.height() ) 04051 { 04052 repaintContents( updateRegion ); 04053 updateRegion = rects[i]; 04054 } 04055 else 04056 updateRegion = newRegion; 04057 } 04058 04059 if ( !updateRegion.isNull() ) 04060 repaintContents( updateRegion ); 04061 04062 // As widgets can only be accurately positioned during painting, every layout might 04063 // dissociate a widget from its RenderWidget. E.g: if a RenderWidget was visible before layout, but the layout 04064 // pushed it out of the viewport, it will not be repainted, and consequently it's associated widget won't be repositioned. 04065 // 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. 04066 04067 if (d->dirtyLayout && !d->visibleWidgets.isEmpty()) 04068 checkExternalWidgetsPosition(); 04069 04070 d->dirtyLayout = false; 04071 04072 emit repaintAccessKeys(); 04073 if (d->emitCompletedAfterRepaint) { 04074 bool full = d->emitCompletedAfterRepaint == KHTMLViewPrivate::CSFull; 04075 d->emitCompletedAfterRepaint = KHTMLViewPrivate::CSNone; 04076 if ( full ) 04077 emit m_part->completed(); 04078 else 04079 emit m_part->completed(true); 04080 } 04081 } 04082 04083 void KHTMLView::checkExternalWidgetsPosition() 04084 { 04085 QWidget* w; 04086 QRect visibleRect(contentsX(), contentsY(), visibleWidth(), visibleHeight()); 04087 QList<RenderWidget*> toRemove; 04088 QHashIterator<void*, QWidget*> it(d->visibleWidgets); 04089 while (it.hasNext()) { 04090 int xp = 0, yp = 0; 04091 it.next(); 04092 RenderWidget* rw = static_cast<RenderWidget*>( it.key() ); 04093 if (!rw->absolutePosition(xp, yp) || 04094 !visibleRect.intersects(QRect(xp, yp, it.value()->width(), it.value()->height()))) 04095 toRemove.append(rw); 04096 } 04097 foreach (RenderWidget* r, toRemove) 04098 if ( (w = d->visibleWidgets.take(r) ) ) 04099 w->move( 0, -500000); 04100 } 04101 04102 void KHTMLView::scheduleRelayout(khtml::RenderObject * /*clippedObj*/) 04103 { 04104 if (!d->layoutSchedulingEnabled || d->layoutTimerId) 04105 return; 04106 04107 int time = 0; 04108 if (d->firstLayoutPending) { 04109 // Any repaint happening while we have no content blanks the viewport ("white flash"). 04110 // Hence the need to delay the first layout as much as we can. 04111 // Only if the document gets stuck for too long in incomplete state will we allow the blanking. 04112 time = d->layoutAttemptCounter ? 04113 sLayoutAttemptDelay + sLayoutAttemptIncrement*d->layoutAttemptCounter : sFirstLayoutDelay; 04114 } else if (m_part->xmlDocImpl() && m_part->xmlDocImpl()->parsing()) { 04115 // Delay between successive layouts in parsing mode. 04116 // Increment reflects the decaying importance of visual feedback over time. 04117 time = qMin(2000, sParsingLayoutsInterval + d->scheduledLayoutCounter*sParsingLayoutsIncrement); 04118 } 04119 d->layoutTimerId = startTimer( time ); 04120 } 04121 04122 void KHTMLView::unscheduleRelayout() 04123 { 04124 if (!d->layoutTimerId) 04125 return; 04126 04127 killTimer(d->layoutTimerId); 04128 d->layoutTimerId = 0; 04129 } 04130 04131 void KHTMLView::unscheduleRepaint() 04132 { 04133 if (!d->repaintTimerId) 04134 return; 04135 04136 killTimer(d->repaintTimerId); 04137 d->repaintTimerId = 0; 04138 } 04139 04140 void KHTMLView::scheduleRepaint(int x, int y, int w, int h, bool asap) 04141 { 04142 bool parsing = !m_part->xmlDocImpl() || m_part->xmlDocImpl()->parsing(); 04143 04144 // kDebug() << "parsing " << parsing; 04145 // kDebug() << "complete " << d->complete; 04146 04147 int time = parsing && !d->firstLayoutPending ? 150 : (!asap ? ( !d->complete ? 80 : 20 ) : 0); 04148 04149 #ifdef DEBUG_FLICKER 04150 QPainter p; 04151 p.begin( viewport() ); 04152 04153 int vx, vy; 04154 contentsToViewport( x, y, vx, vy ); 04155 p.fillRect( vx, vy, w, h, Qt::red ); 04156 p.end(); 04157 #endif 04158 04159 d->updateRegion = d->updateRegion.unite(QRect(x,y,w,h)); 04160 04161 if (asap && !parsing) 04162 unscheduleRepaint(); 04163 04164 if ( !d->repaintTimerId ) 04165 d->repaintTimerId = startTimer( time ); 04166 04167 // kDebug() << "starting timer " << time; 04168 } 04169 04170 void KHTMLView::complete( bool pendingAction ) 04171 { 04172 // kDebug() << "KHTMLView::complete()"; 04173 04174 d->complete = true; 04175 04176 // is there a relayout pending? 04177 if (d->layoutTimerId) 04178 { 04179 // kDebug() << "requesting relayout now"; 04180 // do it now 04181 killTimer(d->layoutTimerId); 04182 d->layoutTimerId = startTimer( 0 ); 04183 d->emitCompletedAfterRepaint = pendingAction ? 04184 KHTMLViewPrivate::CSActionPending : KHTMLViewPrivate::CSFull; 04185 } 04186 04187 // is there a repaint pending? 04188 if (d->repaintTimerId) 04189 { 04190 // kDebug() << "requesting repaint now"; 04191 // do it now 04192 killTimer(d->repaintTimerId); 04193 d->repaintTimerId = startTimer( 0 ); 04194 d->emitCompletedAfterRepaint = pendingAction ? 04195 KHTMLViewPrivate::CSActionPending : KHTMLViewPrivate::CSFull; 04196 } 04197 04198 if (!d->emitCompletedAfterRepaint) 04199 { 04200 if (!pendingAction) 04201 emit m_part->completed(); 04202 else 04203 emit m_part->completed(true); 04204 } 04205 04206 } 04207 04208 void KHTMLView::updateScrollBars() 04209 { 04210 const QWidget *view = widget(); 04211 if (!view) 04212 return; 04213 04214 QSize p = viewport()->size(); 04215 QSize m = maximumViewportSize(); 04216 04217 if (m.expandedTo(view->size()) == m) 04218 p = m; // no scroll bars needed 04219 04220 QSize v = view->size(); 04221 horizontalScrollBar()->setRange(0, v.width() - p.width()); 04222 horizontalScrollBar()->setPageStep(p.width()); 04223 verticalScrollBar()->setRange(0, v.height() - p.height()); 04224 verticalScrollBar()->setPageStep(p.height()); 04225 if (!d->smoothScrolling) { 04226 d->updateContentsXY(); 04227 } 04228 } 04229 04230 void KHTMLView::slotMouseScrollTimer() 04231 { 04232 horizontalScrollBar()->setValue( horizontalScrollBar()->value() +d->m_mouseScroll_byX ); 04233 verticalScrollBar()->setValue( verticalScrollBar()->value() +d->m_mouseScroll_byY); 04234 } 04235 04236 04237 static DOM::Position positionOfLineBoundary(const DOM::Position &pos, bool toEnd) 04238 { 04239 Selection sel = pos; 04240 sel.expandUsingGranularity(Selection::LINE); 04241 return toEnd ? sel.end() : sel.start(); 04242 } 04243 04244 inline static DOM::Position positionOfLineBegin(const DOM::Position &pos) 04245 { 04246 return positionOfLineBoundary(pos, false); 04247 } 04248 04249 inline static DOM::Position positionOfLineEnd(const DOM::Position &pos) 04250 { 04251 return positionOfLineBoundary(pos, true); 04252 } 04253 04254 bool KHTMLView::caretKeyPressEvent(QKeyEvent *_ke) 04255 { 04256 EditorContext *ec = &m_part->d->editor_context; 04257 Selection &caret = ec->m_selection; 04258 Position old_pos = caret.caretPos(); 04259 Position pos = old_pos; 04260 bool recalcXPos = true; 04261 bool handled = true; 04262 04263 bool ctrl = _ke->modifiers() & Qt::ControlModifier; 04264 bool shift = _ke->modifiers() & Qt::ShiftModifier; 04265 04266 switch(_ke->key()) { 04267 04268 // -- Navigational keys 04269 case Qt::Key_Down: 04270 pos = old_pos.nextLinePosition(caret.xPosForVerticalArrowNavigation(Selection::EXTENT)); 04271 recalcXPos = false; 04272 break; 04273 04274 case Qt::Key_Up: 04275 pos = old_pos.previousLinePosition(caret.xPosForVerticalArrowNavigation(Selection::EXTENT)); 04276 recalcXPos = false; 04277 break; 04278 04279 case Qt::Key_Left: 04280 pos = ctrl ? old_pos.previousWordPosition() : old_pos.previousCharacterPosition(); 04281 break; 04282 04283 case Qt::Key_Right: 04284 pos = ctrl ? old_pos.nextWordPosition() : old_pos.nextCharacterPosition(); 04285 break; 04286 04287 case Qt::Key_PageDown: 04288 // moveCaretNextPage(); ### 04289 break; 04290 04291 case Qt::Key_PageUp: 04292 // moveCaretPrevPage(); ### 04293 break; 04294 04295 case Qt::Key_Home: 04296 if (ctrl) 04297 /*moveCaretToDocumentBoundary(false)*/; // ### 04298 else 04299 pos = positionOfLineBegin(old_pos); 04300 break; 04301 04302 case Qt::Key_End: 04303 if (ctrl) 04304 /*moveCaretToDocumentBoundary(true)*/; // ### 04305 else 04306 pos = positionOfLineEnd(old_pos); 04307 break; 04308 04309 default: 04310 handled = false; 04311 04312 }/*end switch*/ 04313 04314 if (pos != old_pos) { 04315 m_part->clearCaretRectIfNeeded(); 04316 04317 caret.moveTo(shift ? caret.nonCaretPos() : pos, pos); 04318 int old_x = caret.xPosForVerticalArrowNavigation(Selection::CARETPOS); 04319 04320 m_part->selectionLayoutChanged(); 04321 04322 // restore old x-position to prevent recalculation 04323 if (!recalcXPos) 04324 m_part->d->editor_context.m_xPosForVerticalArrowNavigation = old_x; 04325 04326 m_part->emitCaretPositionChanged(pos); 04327 // ### check when to emit it 04328 m_part->notifySelectionChanged(); 04329 04330 } 04331 04332 if (handled) _ke->accept(); 04333 return handled; 04334 } 04335 04336 #undef DEBUG_CARETMODE
This file is part of the KDE documentation.
Documentation copyright © 1996-2019 The KDE developers.
Generated on Mon Jan 21 2019 12:38:19 by doxygen 1.7.5.1 written by Dimitri van Heesch, © 1997-2006
Documentation copyright © 1996-2019 The KDE developers.
Generated on Mon Jan 21 2019 12:38:19 by doxygen 1.7.5.1 written by Dimitri van Heesch, © 1997-2006
KDE's Doxygen guidelines are available online.