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->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, "KHTMLLineEditButton" ); 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 } 01376 khtml::RenderStyle* style = (r && r->style()) ? r->style() : 0; 01377 QCursor c; 01378 LinkCursor linkCursor = LINK_NORMAL; 01379 switch (!forceDefault ? (style ? style->cursor() : CURSOR_AUTO) : CURSOR_DEFAULT) { 01380 case CURSOR_AUTO: 01381 if ( r && r->isText() && ((m_part->d->m_bMousePressed && m_part->d->editor_context.m_beganSelectingText) || 01382 !r->isPointInsideSelection(xm, ym, m_part->caret())) ) 01383 c = QCursor(Qt::IBeamCursor); 01384 if ( mev.url.length() && m_part->settings()->changeCursor() ) { 01385 c = m_part->urlCursor(); 01386 if (mev.url.string().startsWith("mailto:") && mev.url.string().indexOf('@')>0) 01387 linkCursor = LINK_MAILTO; 01388 else 01389 if ( targetOpensNewWindow( m_part, mev.target.string() ) ) 01390 linkCursor = LINK_NEWWINDOW; 01391 } 01392 01393 if (r && r->isFrameSet() && !static_cast<RenderFrameSet*>(r)->noResize()) 01394 c = QCursor(static_cast<RenderFrameSet*>(r)->cursorShape()); 01395 01396 break; 01397 case CURSOR_CROSS: 01398 c = QCursor(Qt::CrossCursor); 01399 break; 01400 case CURSOR_POINTER: 01401 c = m_part->urlCursor(); 01402 if (mev.url.string().startsWith("mailto:") && mev.url.string().indexOf('@')>0) 01403 linkCursor = LINK_MAILTO; 01404 else 01405 if ( targetOpensNewWindow( m_part, mev.target.string() ) ) 01406 linkCursor = LINK_NEWWINDOW; 01407 break; 01408 case CURSOR_PROGRESS: 01409 c = QCursor(Qt::BusyCursor); // working_cursor 01410 break; 01411 case CURSOR_MOVE: 01412 case CURSOR_ALL_SCROLL: 01413 c = QCursor(Qt::SizeAllCursor); 01414 break; 01415 case CURSOR_E_RESIZE: 01416 case CURSOR_W_RESIZE: 01417 case CURSOR_EW_RESIZE: 01418 c = QCursor(Qt::SizeHorCursor); 01419 break; 01420 case CURSOR_N_RESIZE: 01421 case CURSOR_S_RESIZE: 01422 case CURSOR_NS_RESIZE: 01423 c = QCursor(Qt::SizeVerCursor); 01424 break; 01425 case CURSOR_NE_RESIZE: 01426 case CURSOR_SW_RESIZE: 01427 case CURSOR_NESW_RESIZE: 01428 c = QCursor(Qt::SizeBDiagCursor); 01429 break; 01430 case CURSOR_NW_RESIZE: 01431 case CURSOR_SE_RESIZE: 01432 case CURSOR_NWSE_RESIZE: 01433 c = QCursor(Qt::SizeFDiagCursor); 01434 break; 01435 case CURSOR_TEXT: 01436 c = QCursor(Qt::IBeamCursor); 01437 break; 01438 case CURSOR_WAIT: 01439 c = QCursor(Qt::WaitCursor); 01440 break; 01441 case CURSOR_HELP: 01442 c = QCursor(Qt::WhatsThisCursor); 01443 break; 01444 case CURSOR_DEFAULT: 01445 break; 01446 case CURSOR_NONE: 01447 case CURSOR_NOT_ALLOWED: 01448 c = QCursor(Qt::ForbiddenCursor); 01449 break; 01450 case CURSOR_ROW_RESIZE: 01451 c = QCursor(Qt::SplitVCursor); 01452 break; 01453 case CURSOR_COL_RESIZE: 01454 c = QCursor(Qt::SplitHCursor); 01455 break; 01456 case CURSOR_VERTICAL_TEXT: 01457 case CURSOR_CONTEXT_MENU: 01458 case CURSOR_NO_DROP: 01459 case CURSOR_CELL: 01460 case CURSOR_COPY: 01461 case CURSOR_ALIAS: 01462 c = QCursor(Qt::ArrowCursor); 01463 break; 01464 } 01465 01466 if (!setCursor && style && style->cursor() != CURSOR_AUTO) 01467 setCursor = true; 01468 01469 QWidget* vp = viewport(); 01470 for (KHTMLPart* p = m_part; p; p = p->parentPart()) 01471 if (!p->parentPart()) 01472 vp = p->view()->viewport(); 01473 if ( setCursor && vp->cursor().handle() != c.handle() ) { 01474 if( c.shape() == Qt::ArrowCursor) { 01475 for (KHTMLPart* p = m_part; p; p = p->parentPart()) 01476 p->view()->viewport()->unsetCursor(); 01477 } 01478 else { 01479 vp->setCursor( c ); 01480 } 01481 } 01482 01483 if ( linkCursor!=LINK_NORMAL && isVisible() && hasFocus() ) { 01484 #ifdef Q_WS_X11 01485 01486 if( !d->cursorIconWidget ) { 01487 #ifdef Q_WS_X11 01488 d->cursorIconWidget = new QLabel( 0, Qt::X11BypassWindowManagerHint ); 01489 XSetWindowAttributes attr; 01490 attr.save_under = True; 01491 XChangeWindowAttributes( QX11Info::display(), d->cursorIconWidget->winId(), CWSaveUnder, &attr ); 01492 #else 01493 d->cursorIconWidget = new QLabel( NULL, NULL ); 01494 //TODO 01495 #endif 01496 } 01497 01498 // Update the pixmap if need be. 01499 if (linkCursor != d->cursorIconType) { 01500 d->cursorIconType = linkCursor; 01501 QString cursorIcon; 01502 switch (linkCursor) 01503 { 01504 case LINK_MAILTO: cursorIcon = "mail-message-new"; break; 01505 case LINK_NEWWINDOW: cursorIcon = "window-new"; break; 01506 default: cursorIcon = "dialog-error"; break; 01507 } 01508 01509 QPixmap icon_pixmap = KHTMLGlobal::iconLoader()->loadIcon( cursorIcon, KIconLoader::Small, 0, KIconLoader::DefaultState, QStringList(), 0, true ); 01510 01511 d->cursorIconWidget->resize( icon_pixmap.width(), icon_pixmap.height()); 01512 d->cursorIconWidget->setMask( icon_pixmap.createMaskFromColor(Qt::transparent)); 01513 d->cursorIconWidget->setPixmap( icon_pixmap); 01514 d->cursorIconWidget->update(); 01515 } 01516 01517 QPoint c_pos = QCursor::pos(); 01518 d->cursorIconWidget->move( c_pos.x() + 15, c_pos.y() + 15 ); 01519 #ifdef Q_WS_X11 01520 XRaiseWindow( QX11Info::display(), d->cursorIconWidget->winId()); 01521 QApplication::flush(); 01522 #elif defined(Q_WS_WIN) 01523 SetWindowPos( d->cursorIconWidget->winId(), HWND_TOP, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE ); 01524 #else 01525 //TODO? 01526 #endif 01527 d->cursorIconWidget->show(); 01528 #endif 01529 } 01530 else if ( d->cursorIconWidget ) 01531 d->cursorIconWidget->hide(); 01532 01533 if (r && r->isWidget()) { 01534 _mouse->ignore(); 01535 } 01536 01537 if (!swallowEvent) { 01538 khtml::MouseMoveEvent event( _mouse, xm, ym, mev.url, mev.target, mev.innerNode ); 01539 QApplication::sendEvent( m_part, &event ); 01540 } 01541 } 01542 01543 void KHTMLView::mouseReleaseEvent( QMouseEvent * _mouse ) 01544 { 01545 bool swallowEvent = false; 01546 01547 int xm = _mouse->x(); 01548 int ym = _mouse->y(); 01549 revertTransforms(xm, ym); 01550 01551 DOM::NodeImpl::MouseEvent mev( _mouse->buttons(), DOM::NodeImpl::MouseRelease ); 01552 01553 if ( m_part->xmlDocImpl() ) 01554 { 01555 m_part->xmlDocImpl()->prepareMouseEvent( false, xm, ym, &mev ); 01556 01557 DOM::NodeImpl* target = mev.innerNode.handle(); 01558 DOM::NodeImpl* fn = m_part->xmlDocImpl()->focusNode(); 01559 01560 // a widget may be the real target of this event (e.g. if a scrollbar's slider is being moved) 01561 if (d->m_mouseEventsTarget && fn && fn->renderer() && fn->renderer()->isWidget()) 01562 target = fn; 01563 01564 swallowEvent = dispatchMouseEvent(EventImpl::MOUSEUP_EVENT,target,mev.innerNonSharedNode.handle(),true, 01565 d->clickCount,_mouse,false,DOM::NodeImpl::MouseRelease); 01566 01567 // clear our sticky event target on any mouseRelease event 01568 if (d->m_mouseEventsTarget) 01569 d->m_mouseEventsTarget = 0; 01570 01571 if (d->clickCount > 0 && 01572 QPoint(d->clickX-xm,d->clickY-ym).manhattanLength() <= QApplication::startDragDistance()) { 01573 QMouseEvent me(d->isDoubleClick ? QEvent::MouseButtonDblClick : QEvent::MouseButtonRelease, 01574 _mouse->pos(), _mouse->button(), _mouse->buttons(), _mouse->modifiers()); 01575 dispatchMouseEvent(EventImpl::CLICK_EVENT, mev.innerNode.handle(),mev.innerNonSharedNode.handle(),true, 01576 d->clickCount, &me, true, DOM::NodeImpl::MouseRelease); 01577 } 01578 01579 khtml::RenderObject* r = target ? target->renderer() : 0; 01580 if (r && r->isWidget()) 01581 _mouse->ignore(); 01582 } 01583 01584 if (!swallowEvent) { 01585 khtml::MouseReleaseEvent event( _mouse, xm, ym, mev.url, mev.target, mev.innerNode ); 01586 QApplication::sendEvent( m_part, &event ); 01587 } 01588 } 01589 01590 // returns true if event should be swallowed 01591 bool KHTMLView::dispatchKeyEvent( QKeyEvent *_ke ) 01592 { 01593 if (!m_part->xmlDocImpl()) 01594 return false; 01595 // Pressing and releasing a key should generate keydown, keypress and keyup events 01596 // Holding it down should generated keydown, keypress (repeatedly) and keyup events 01597 // The problem here is that Qt generates two autorepeat events (keyrelease+keypress) 01598 // for autorepeating, while DOM wants only one autorepeat event (keypress), so one 01599 // of the Qt events shouldn't be passed to DOM, but it should be still filtered 01600 // out if DOM would filter the autorepeat event. Additional problem is that Qt keyrelease 01601 // events don't have text() set (Qt bug?), so DOM often would ignore the keypress event 01602 // if it was created using Qt keyrelease, but Qt autorepeat keyrelease comes 01603 // before Qt autorepeat keypress (i.e. problem whether to filter it out or not). 01604 // The solution is to filter out and postpone the Qt autorepeat keyrelease until 01605 // the following Qt keypress event comes. If DOM accepts the DOM keypress event, 01606 // the postponed event will be simply discarded. If not, it will be passed to keyPressEvent() 01607 // again, and here it will be ignored. 01608 // 01609 // Qt: Press | Release(autorepeat) Press(autorepeat) etc. | Release 01610 // DOM: Down + Press | (nothing) Press | Up 01611 01612 // It's also possible to get only Releases. E.g. the release of alt-tab, 01613 // or when the keypresses get captured by an accel. 01614 01615 if( _ke == d->postponed_autorepeat ) // replayed event 01616 { 01617 return false; 01618 } 01619 01620 if( _ke->type() == QEvent::KeyPress ) 01621 { 01622 if( !_ke->isAutoRepeat()) 01623 { 01624 bool ret = dispatchKeyEventHelper( _ke, false ); // keydown 01625 // don't send keypress even if keydown was blocked, like IE (and unlike Mozilla) 01626 if( !ret && dispatchKeyEventHelper( _ke, true )) // keypress 01627 ret = true; 01628 return ret; 01629 } 01630 else // autorepeat 01631 { 01632 bool ret = dispatchKeyEventHelper( _ke, true ); // keypress 01633 if( !ret && d->postponed_autorepeat ) 01634 keyPressEvent( d->postponed_autorepeat ); 01635 delete d->postponed_autorepeat; 01636 d->postponed_autorepeat = NULL; 01637 return ret; 01638 } 01639 } 01640 else // QEvent::KeyRelease 01641 { 01642 // Discard postponed "autorepeat key-release" events that didn't see 01643 // a keypress after them (e.g. due to QAccel) 01644 if ( d->postponed_autorepeat ) { 01645 delete d->postponed_autorepeat; 01646 d->postponed_autorepeat = 0; 01647 } 01648 01649 if( !_ke->isAutoRepeat()) { 01650 return dispatchKeyEventHelper( _ke, false ); // keyup 01651 } 01652 else 01653 { 01654 d->postponed_autorepeat = new QKeyEvent( _ke->type(), _ke->key(), _ke->modifiers(), 01655 _ke->text(), _ke->isAutoRepeat(), _ke->count()); 01656 if( _ke->isAccepted()) 01657 d->postponed_autorepeat->accept(); 01658 else 01659 d->postponed_autorepeat->ignore(); 01660 return true; 01661 } 01662 } 01663 } 01664 01665 // returns true if event should be swallowed 01666 bool KHTMLView::dispatchKeyEventHelper( QKeyEvent *_ke, bool keypress ) 01667 { 01668 DOM::NodeImpl* keyNode = m_part->xmlDocImpl()->focusNode(); 01669 if (keyNode) { 01670 return keyNode->dispatchKeyEvent(_ke, keypress); 01671 } else { // no focused node, send to document 01672 return m_part->xmlDocImpl()->dispatchKeyEvent(_ke, keypress); 01673 } 01674 } 01675 01676 void KHTMLView::keyPressEvent( QKeyEvent *_ke ) 01677 { 01678 // If CTRL was hit, be prepared for access keys 01679 if (d->accessKeysEnabled && _ke->key() == Qt::Key_Control && !(_ke->modifiers() & ~Qt::ControlModifier) && !d->accessKeysActivated) 01680 { 01681 d->accessKeysPreActivate=true; 01682 _ke->accept(); 01683 return; 01684 } 01685 01686 if (_ke->key() == Qt::Key_Shift && !(_ke->modifiers() & ~Qt::ShiftModifier)) 01687 d->scrollSuspendPreActivate=true; 01688 01689 // accesskey handling needs to be done before dispatching, otherwise e.g. lineedits 01690 // may eat the event 01691 01692 if (d->accessKeysEnabled && d->accessKeysActivated) 01693 { 01694 int state = ( _ke->modifiers() & ( Qt::ShiftModifier | Qt::ControlModifier | Qt::AltModifier | Qt::MetaModifier )); 01695 if ( state==0 || state==Qt::ShiftModifier ) { 01696 if (_ke->key() != Qt::Key_Shift) 01697 accessKeysTimeout(); 01698 handleAccessKey( _ke ); 01699 _ke->accept(); 01700 return; 01701 } 01702 accessKeysTimeout(); 01703 _ke->accept(); 01704 return; 01705 } 01706 01707 if ( dispatchKeyEvent( _ke )) { 01708 // If either keydown or keypress was accepted by a widget, or canceled by JS, stop here. 01709 _ke->accept(); 01710 return; 01711 } 01712 01713 int offs = (viewport()->height() < 30) ? viewport()->height() : 30; // ### ?? 01714 if (_ke->modifiers() & Qt::ShiftModifier) 01715 switch(_ke->key()) 01716 { 01717 case Qt::Key_Space: 01718 verticalScrollBar()->setValue( verticalScrollBar()->value() -viewport()->height() + offs ); 01719 if(d->scrollSuspended) 01720 d->newScrollTimer(this, 0); 01721 break; 01722 01723 case Qt::Key_Down: 01724 case Qt::Key_J: 01725 d->adjustScroller(this, KHTMLViewPrivate::ScrollDown, KHTMLViewPrivate::ScrollUp); 01726 break; 01727 01728 case Qt::Key_Up: 01729 case Qt::Key_K: 01730 d->adjustScroller(this, KHTMLViewPrivate::ScrollUp, KHTMLViewPrivate::ScrollDown); 01731 break; 01732 01733 case Qt::Key_Left: 01734 case Qt::Key_H: 01735 d->adjustScroller(this, KHTMLViewPrivate::ScrollLeft, KHTMLViewPrivate::ScrollRight); 01736 break; 01737 01738 case Qt::Key_Right: 01739 case Qt::Key_L: 01740 d->adjustScroller(this, KHTMLViewPrivate::ScrollRight, KHTMLViewPrivate::ScrollLeft); 01741 break; 01742 } 01743 else 01744 switch ( _ke->key() ) 01745 { 01746 case Qt::Key_Down: 01747 case Qt::Key_J: 01748 if (!d->scrollTimerId || d->scrollSuspended) 01749 verticalScrollBar()->setValue( verticalScrollBar()->value()+10 ); 01750 if (d->scrollTimerId) 01751 d->newScrollTimer(this, 0); 01752 break; 01753 01754 case Qt::Key_Space: 01755 case Qt::Key_PageDown: 01756 d->shouldSmoothScroll = true; 01757 verticalScrollBar()->setValue( verticalScrollBar()->value() +viewport()->height() - offs ); 01758 if(d->scrollSuspended) 01759 d->newScrollTimer(this, 0); 01760 break; 01761 01762 case Qt::Key_Up: 01763 case Qt::Key_K: 01764 if (!d->scrollTimerId || d->scrollSuspended) 01765 verticalScrollBar()->setValue( verticalScrollBar()->value()-10 ); 01766 if (d->scrollTimerId) 01767 d->newScrollTimer(this, 0); 01768 break; 01769 01770 case Qt::Key_PageUp: 01771 d->shouldSmoothScroll = true; 01772 verticalScrollBar()->setValue( verticalScrollBar()->value() -viewport()->height() + offs ); 01773 if(d->scrollSuspended) 01774 d->newScrollTimer(this, 0); 01775 break; 01776 case Qt::Key_Right: 01777 case Qt::Key_L: 01778 if (!d->scrollTimerId || d->scrollSuspended) 01779 horizontalScrollBar()->setValue( horizontalScrollBar()->value()+10 ); 01780 if (d->scrollTimerId) 01781 d->newScrollTimer(this, 0); 01782 break; 01783 01784 case Qt::Key_Left: 01785 case Qt::Key_H: 01786 if (!d->scrollTimerId || d->scrollSuspended) 01787 horizontalScrollBar()->setValue( horizontalScrollBar()->value()-10 ); 01788 if (d->scrollTimerId) 01789 d->newScrollTimer(this, 0); 01790 break; 01791 case Qt::Key_Enter: 01792 case Qt::Key_Return: 01793 // ### FIXME: 01794 // or even better to HTMLAnchorElementImpl::event() 01795 if (m_part->xmlDocImpl()) { 01796 NodeImpl *n = m_part->xmlDocImpl()->focusNode(); 01797 if (n) 01798 n->setActive(); 01799 } 01800 break; 01801 case Qt::Key_Home: 01802 verticalScrollBar()->setValue( 0 ); 01803 horizontalScrollBar()->setValue( 0 ); 01804 if(d->scrollSuspended) 01805 d->newScrollTimer(this, 0); 01806 break; 01807 case Qt::Key_End: 01808 verticalScrollBar()->setValue( contentsHeight() - visibleHeight() ); 01809 if(d->scrollSuspended) 01810 d->newScrollTimer(this, 0); 01811 break; 01812 case Qt::Key_Shift: 01813 // what are you doing here? 01814 _ke->ignore(); 01815 return; 01816 default: 01817 if (d->scrollTimerId) 01818 d->newScrollTimer(this, 0); 01819 _ke->ignore(); 01820 return; 01821 } 01822 01823 _ke->accept(); 01824 } 01825 01826 void KHTMLView::keyReleaseEvent(QKeyEvent *_ke) 01827 { 01828 if( d->scrollSuspendPreActivate && _ke->key() != Qt::Key_Shift ) 01829 d->scrollSuspendPreActivate = false; 01830 if( _ke->key() == Qt::Key_Shift && d->scrollSuspendPreActivate && !(_ke->modifiers() & Qt::ShiftModifier)) 01831 if (d->scrollTimerId) { 01832 d->scrollSuspended = !d->scrollSuspended; 01833 if (d->scrollSuspended) 01834 d->stopScrolling(); 01835 } 01836 01837 if (d->accessKeysEnabled) 01838 { 01839 if (d->accessKeysPreActivate && _ke->key() != Qt::Key_Control) 01840 d->accessKeysPreActivate=false; 01841 if (d->accessKeysPreActivate && !(_ke->modifiers() & Qt::ControlModifier)) 01842 { 01843 displayAccessKeys(); 01844 m_part->setStatusBarText(i18n("Access Keys activated"),KHTMLPart::BarOverrideText); 01845 d->accessKeysActivated = true; 01846 d->accessKeysPreActivate = false; 01847 _ke->accept(); 01848 return; 01849 } 01850 else if (d->accessKeysActivated) 01851 { 01852 accessKeysTimeout(); 01853 _ke->accept(); 01854 return; 01855 } 01856 } 01857 01858 // Send keyup event 01859 if ( dispatchKeyEvent( _ke ) ) 01860 { 01861 _ke->accept(); 01862 return; 01863 } 01864 01865 QScrollArea::keyReleaseEvent(_ke); 01866 } 01867 01868 bool KHTMLView::focusNextPrevChild( bool next ) 01869 { 01870 // Now try to find the next child 01871 if (m_part->xmlDocImpl() && focusNextPrevNode(next)) 01872 { 01873 if (m_part->xmlDocImpl()->focusNode()) 01874 kDebug() << "focusNode.name: " 01875 << m_part->xmlDocImpl()->focusNode()->nodeName().string() << endl; 01876 return true; // focus node found 01877 } 01878 01879 // If we get here, pass tabbing control up to the next/previous child in our parent 01880 d->pseudoFocusNode = KHTMLViewPrivate::PFNone; 01881 if (m_part->parentPart() && m_part->parentPart()->view()) 01882 return m_part->parentPart()->view()->focusNextPrevChild(next); 01883 01884 return QWidget::focusNextPrevChild(next); 01885 } 01886 01887 void KHTMLView::doAutoScroll() 01888 { 01889 QPoint pos = QCursor::pos(); 01890 QPoint off; 01891 KHTMLView* v = m_kwp->isRedirected() ? m_kwp->rootViewPos(off) : this; 01892 pos = v->viewport()->mapFromGlobal( pos ); 01893 pos -= off; 01894 int xm, ym; 01895 viewportToContents(pos.x(), pos.y(), xm, ym); // ### 01896 01897 pos = QPoint(pos.x() - viewport()->x(), pos.y() - viewport()->y()); 01898 if ( (pos.y() < 0) || (pos.y() > visibleHeight()) || 01899 (pos.x() < 0) || (pos.x() > visibleWidth()) ) 01900 { 01901 ensureVisible( xm, ym, 0, 5 ); 01902 01903 #ifndef KHTML_NO_SELECTION 01904 // extend the selection while scrolling 01905 DOM::Node innerNode; 01906 if (m_part->isExtendingSelection()) { 01907 RenderObject::NodeInfo renderInfo(true/*readonly*/, false/*active*/); 01908 m_part->xmlDocImpl()->renderer()->layer() 01909 ->nodeAtPoint(renderInfo, xm, ym); 01910 innerNode = renderInfo.innerNode(); 01911 }/*end if*/ 01912 01913 if (innerNode.handle() && innerNode.handle()->renderer() 01914 && innerNode.handle()->renderer()->shouldSelect()) { 01915 m_part->extendSelectionTo(xm, ym, innerNode); 01916 }/*end if*/ 01917 #endif // KHTML_NO_SELECTION 01918 } 01919 } 01920 01921 // KHTML defines its own stacking order for any object and thus takes 01922 // control of widget painting whenever it can. This is called "redirection". 01923 // 01924 // Redirected widgets are placed off screen. When they are declared as a child of our view (ChildPolished event), 01925 // an event filter is installed, so as to catch any paint event and translate them as update() of the view's main widget. 01926 // 01927 // Painting also happens spontaneously within widgets. In this case, the widget would update() parts of itself. 01928 // While this ordinarily results in a paintEvent being schedduled, it is not the case with off screen widgets. 01929 // Thus update() is monitored by using the mechanism that deffers any update call happening during a paint event, 01930 // transforming it into a posted UpdateLater event. Hence the need to set Qt::WA_WState_InPaintEvent on redirected widgets. 01931 // 01932 // Once the UpdateLater event has been received, Qt::WA_WState_InPaintEvent is removed and the process continues 01933 // with the update of the corresponding rect on the view. That in turn will make our painting subsystem render() 01934 // the widget at the correct stacking position. 01935 // 01936 // For non-redirected (e.g. external) widgets, z-order is honoured through masking. cf.RenderLayer::updateWidgetMasks 01937 01938 static void handleWidget(QWidget* w, KHTMLView* view, bool recurse=true) 01939 { 01940 if (w->isWindow()) 01941 return; 01942 01943 if (!qobject_cast<QFrame*>(w)) 01944 w->setAttribute( Qt::WA_NoSystemBackground ); 01945 01946 w->setAttribute(Qt::WA_WState_InPaintEvent); 01947 w->setAttribute(Qt::WA_OpaquePaintEvent); 01948 w->installEventFilter(view); 01949 01950 if (!recurse) 01951 return; 01952 if (qobject_cast<KHTMLView*>(w)) { 01953 handleWidget(static_cast<KHTMLView*>(w)->widget(), view, false); 01954 handleWidget(static_cast<KHTMLView*>(w)->horizontalScrollBar(), view, false); 01955 handleWidget(static_cast<KHTMLView*>(w)->verticalScrollBar(), view, false); 01956 return; 01957 } 01958 01959 QObjectList children = w->children(); 01960 foreach (QObject* object, children) { 01961 QWidget *widget = qobject_cast<QWidget*>(object); 01962 if (widget) 01963 handleWidget(widget, view); 01964 } 01965 } 01966 01967 class KHTMLBackingStoreHackWidget : public QWidget 01968 { 01969 public: 01970 void publicEvent(QEvent *e) 01971 { 01972 QWidget::event(e); 01973 } 01974 }; 01975 01976 bool KHTMLView::viewportEvent ( QEvent * e ) 01977 { 01978 switch (e->type()) { 01979 // those must not be dispatched to the specialized handlers 01980 // as widgetEvent() already took care of that 01981 case QEvent::MouseButtonPress: 01982 case QEvent::MouseButtonRelease: 01983 case QEvent::MouseButtonDblClick: 01984 case QEvent::MouseMove: 01985 #ifndef QT_NO_WHEELEVENT 01986 case QEvent::Wheel: 01987 #endif 01988 case QEvent::ContextMenu: 01989 case QEvent::DragEnter: 01990 case QEvent::DragMove: 01991 case QEvent::DragLeave: 01992 case QEvent::Drop: 01993 return false; 01994 default: 01995 break; 01996 } 01997 return QScrollArea::viewportEvent(e); 01998 } 01999 02000 static void setInPaintEventFlag(QWidget* w, bool b = true, bool recurse=true) 02001 { 02002 w->setAttribute(Qt::WA_WState_InPaintEvent, b); 02003 02004 if (!recurse) 02005 return; 02006 if (qobject_cast<KHTMLView*>(w)) { 02007 setInPaintEventFlag(static_cast<KHTMLView*>(w)->widget(), b, false); 02008 setInPaintEventFlag(static_cast<KHTMLView*>(w)->horizontalScrollBar(), b, false); 02009 setInPaintEventFlag(static_cast<KHTMLView*>(w)->verticalScrollBar(), b, false); 02010 return; 02011 } 02012 02013 foreach(QObject* cw, w->children()) { 02014 if (cw->isWidgetType() && ! static_cast<QWidget*>(cw)->isWindow() 02015 && !(static_cast<QWidget*>(cw)->windowModality() & Qt::ApplicationModal)) { 02016 setInPaintEventFlag(static_cast<QWidget*>(cw), b); 02017 } 02018 } 02019 } 02020 02021 bool KHTMLView::eventFilter(QObject *o, QEvent *e) 02022 { 02023 if ( e->type() == QEvent::ShortcutOverride ) { 02024 QKeyEvent* ke = (QKeyEvent*) e; 02025 if (m_part->isEditable() || m_part->isCaretMode() 02026 || (m_part->xmlDocImpl() && m_part->xmlDocImpl()->focusNode() 02027 && m_part->xmlDocImpl()->focusNode()->isContentEditable())) { 02028 if ( (ke->modifiers() & Qt::ControlModifier) || (ke->modifiers() & Qt::ShiftModifier) ) { 02029 switch ( ke->key() ) { 02030 case Qt::Key_Left: 02031 case Qt::Key_Right: 02032 case Qt::Key_Up: 02033 case Qt::Key_Down: 02034 case Qt::Key_Home: 02035 case Qt::Key_End: 02036 ke->accept(); 02037 return true; 02038 default: 02039 break; 02040 } 02041 } 02042 } 02043 } 02044 02045 if ( e->type() == QEvent::Leave ) { 02046 if ( d->cursorIconWidget ) 02047 d->cursorIconWidget->hide(); 02048 m_part->resetHoverText(); 02049 } 02050 02051 QWidget *view = widget(); 02052 if (o == view) { 02053 if (widgetEvent(e)) 02054 return true; 02055 else if (e->type() == QEvent::Resize) { 02056 updateScrollBars(); 02057 return false; 02058 } 02059 } else if (o->isWidgetType()) { 02060 QWidget *v = static_cast<QWidget *>(o); 02061 QWidget *c = v; 02062 while (v && v != view) { 02063 c = v; 02064 v = v->parentWidget(); 02065 } 02066 KHTMLWidget* k = dynamic_cast<KHTMLWidget*>(c); 02067 if (v && k && k->m_kwp->isRedirected()) { 02068 bool block = false; 02069 bool isUpdate = false; 02070 QWidget *w = static_cast<QWidget *>(o); 02071 switch(e->type()) { 02072 case QEvent::UpdateRequest: { 02073 // implicitly call qt_syncBackingStore(w) 02074 static_cast<KHTMLBackingStoreHackWidget *>(w)->publicEvent(e); 02075 block = true; 02076 break; 02077 } 02078 case QEvent::UpdateLater: 02079 isUpdate = true; 02080 // no break; 02081 case QEvent::Paint: 02082 if (!allowWidgetPaintEvents) { 02083 // eat the event. Like this we can control exactly when the widget 02084 // gets repainted. 02085 block = true; 02086 int x = 0, y = 0; 02087 QWidget *v = w; 02088 while (v && v->parentWidget() != view) { 02089 x += v->x(); 02090 y += v->y(); 02091 v = v->parentWidget(); 02092 } 02093 02094 QPoint ap = k->m_kwp->absolutePos(); 02095 x += ap.x(); 02096 y += ap.y(); 02097 02098 QRect pr = isUpdate ? static_cast<QUpdateLaterEvent*>(e)->region().boundingRect() : static_cast<QPaintEvent*>(e)->rect(); 02099 bool asap = !d->contentsMoving && qobject_cast<QAbstractScrollArea*>(c); 02100 02101 if (isUpdate) { 02102 setInPaintEventFlag(w, false); 02103 if (asap) 02104 w->repaint(static_cast<QUpdateLaterEvent*>(e)->region()); 02105 else 02106 w->update(static_cast<QUpdateLaterEvent*>(e)->region()); 02107 setInPaintEventFlag(w); 02108 } 02109 02110 // QScrollView needs fast repaints 02111 if ( asap && !isUpdate && !d->painting && m_part->xmlDocImpl() && m_part->xmlDocImpl()->renderer() && 02112 !static_cast<khtml::RenderCanvas *>(m_part->xmlDocImpl()->renderer())->needsLayout() ) { 02113 repaintContents(x + pr.x(), y + pr.y(), 02114 pr.width(), pr.height()+1); // ### investigate that +1 (shows up when 02115 // updating e.g a textarea's blinking cursor) 02116 } else if (!d->painting) { 02117 scheduleRepaint(x + pr.x(), y + pr.y(), 02118 pr.width(), pr.height()+1, asap); 02119 } 02120 } 02121 break; 02122 case QEvent::MouseMove: 02123 case QEvent::MouseButtonPress: 02124 case QEvent::MouseButtonRelease: 02125 case QEvent::MouseButtonDblClick: { 02126 02127 if (0 && w->parentWidget() == view && !qobject_cast<QScrollBar*>(w) && !::qobject_cast<QScrollBar *>(w)) { 02128 QMouseEvent *me = static_cast<QMouseEvent *>(e); 02129 QPoint pt = w->mapTo( view, me->pos()); 02130 QMouseEvent me2(me->type(), pt, me->button(), me->buttons(), me->modifiers()); 02131 02132 if (e->type() == QEvent::MouseMove) 02133 mouseMoveEvent(&me2); 02134 else if(e->type() == QEvent::MouseButtonPress) 02135 mousePressEvent(&me2); 02136 else if(e->type() == QEvent::MouseButtonRelease) 02137 mouseReleaseEvent(&me2); 02138 else 02139 mouseDoubleClickEvent(&me2); 02140 block = true; 02141 } 02142 break; 02143 } 02144 case QEvent::KeyPress: 02145 case QEvent::KeyRelease: 02146 if (w->parentWidget() == view && !qobject_cast<QScrollBar*>(w)) { 02147 QKeyEvent *ke = static_cast<QKeyEvent *>(e); 02148 if (e->type() == QEvent::KeyPress) { 02149 keyPressEvent(ke); 02150 ke->accept(); 02151 } else{ 02152 keyReleaseEvent(ke); 02153 ke->accept(); 02154 } 02155 block = true; 02156 } 02157 02158 if (qobject_cast<KUrlRequester*>(w->parentWidget()) && 02159 e->type() == QEvent::KeyPress) { 02160 // Since keypress events on the upload widget will 02161 // be forwarded to the lineedit anyway, 02162 // block the original copy at this level to prevent 02163 // double-emissions of events it doesn't accept 02164 e->ignore(); 02165 block = true; 02166 } 02167 02168 break; 02169 case QEvent::FocusIn: 02170 case QEvent::FocusOut: { 02171 QPoint dummy; 02172 KHTMLView* root = m_kwp->rootViewPos(dummy); 02173 if (!root) 02174 root = this; 02175 block = static_cast<QFocusEvent*>(e)->reason() != Qt::MouseFocusReason || root->underMouse(); 02176 break; 02177 } 02178 default: 02179 break; 02180 } 02181 if (block) { 02182 //qDebug("eating event"); 02183 return true; 02184 } 02185 } 02186 } 02187 02188 // kDebug(6000) <<"passing event on to sv event filter object=" << o->className() << " event=" << e->type(); 02189 return QScrollArea::eventFilter(o, e); 02190 } 02191 02192 bool KHTMLView::widgetEvent(QEvent* e) 02193 { 02194 switch (e->type()) { 02195 case QEvent::MouseButtonPress: 02196 case QEvent::MouseButtonRelease: 02197 case QEvent::MouseButtonDblClick: 02198 case QEvent::MouseMove: 02199 case QEvent::Paint: 02200 #ifndef QT_NO_WHEELEVENT 02201 case QEvent::Wheel: 02202 #endif 02203 case QEvent::ContextMenu: 02204 case QEvent::DragEnter: 02205 case QEvent::DragMove: 02206 case QEvent::DragLeave: 02207 case QEvent::Drop: 02208 return QFrame::event(e); 02209 case QEvent::ChildPolished: { 02210 // we need to install an event filter on all children of the widget() to 02211 // be able to get correct stacking of children within the document. 02212 QObject *c = static_cast<QChildEvent *>(e)->child(); 02213 if (c->isWidgetType()) { 02214 QWidget *w = static_cast<QWidget *>(c); 02215 // don't install the event filter on toplevels 02216 if (!(w->windowFlags() & Qt::Window) && !(w->windowModality() & Qt::ApplicationModal)) { 02217 KHTMLWidget* k = dynamic_cast<KHTMLWidget*>(w); 02218 if (k && k->m_kwp->isRedirected()) { 02219 w->unsetCursor(); 02220 handleWidget(w, this); 02221 } 02222 } 02223 } 02224 break; 02225 } 02226 case QEvent::Move: { 02227 if (static_cast<QMoveEvent*>(e)->pos() != QPoint(0,0)) { 02228 widget()->move(0,0); 02229 updateScrollBars(); 02230 return true; 02231 } 02232 break; 02233 } 02234 default: 02235 break; 02236 } 02237 return false; 02238 } 02239 02240 bool KHTMLView::hasLayoutPending() 02241 { 02242 return d->layoutTimerId && !d->firstLayoutPending; 02243 } 02244 02245 DOM::NodeImpl *KHTMLView::nodeUnderMouse() const 02246 { 02247 return d->underMouse; 02248 } 02249 02250 DOM::NodeImpl *KHTMLView::nonSharedNodeUnderMouse() const 02251 { 02252 return d->underMouseNonShared; 02253 } 02254 02255 bool KHTMLView::scrollTo(const QRect &bounds) 02256 { 02257 d->scrollingSelf = true; // so scroll events get ignored 02258 02259 int x, y, xe, ye; 02260 x = bounds.left(); 02261 y = bounds.top(); 02262 xe = bounds.right(); 02263 ye = bounds.bottom(); 02264 02265 //kDebug(6000)<<"scrolling coords: x="<<x<<" y="<<y<<" width="<<xe-x<<" height="<<ye-y; 02266 02267 int deltax; 02268 int deltay; 02269 02270 int curHeight = visibleHeight(); 02271 int curWidth = visibleWidth(); 02272 02273 if (ye-y>curHeight-d->borderY) 02274 ye = y + curHeight - d->borderY; 02275 02276 if (xe-x>curWidth-d->borderX) 02277 xe = x + curWidth - d->borderX; 02278 02279 // is xpos of target left of the view's border? 02280 if (x < contentsX() + d->borderX ) 02281 deltax = x - contentsX() - d->borderX; 02282 // is xpos of target right of the view's right border? 02283 else if (xe + d->borderX > contentsX() + curWidth) 02284 deltax = xe + d->borderX - ( contentsX() + curWidth ); 02285 else 02286 deltax = 0; 02287 02288 // is ypos of target above upper border? 02289 if (y < contentsY() + d->borderY) 02290 deltay = y - contentsY() - d->borderY; 02291 // is ypos of target below lower border? 02292 else if (ye + d->borderY > contentsY() + curHeight) 02293 deltay = ye + d->borderY - ( contentsY() + curHeight ); 02294 else 02295 deltay = 0; 02296 02297 int maxx = curWidth-d->borderX; 02298 int maxy = curHeight-d->borderY; 02299 02300 int scrollX, scrollY; 02301 02302 scrollX = deltax > 0 ? (deltax > maxx ? maxx : deltax) : deltax == 0 ? 0 : (deltax>-maxx ? deltax : -maxx); 02303 scrollY = deltay > 0 ? (deltay > maxy ? maxy : deltay) : deltay == 0 ? 0 : (deltay>-maxy ? deltay : -maxy); 02304 02305 if (contentsX() + scrollX < 0) 02306 scrollX = -contentsX(); 02307 else if (contentsWidth() - visibleWidth() - contentsX() < scrollX) 02308 scrollX = contentsWidth() - visibleWidth() - contentsX(); 02309 02310 if (contentsY() + scrollY < 0) 02311 scrollY = -contentsY(); 02312 else if (contentsHeight() - visibleHeight() - contentsY() < scrollY) 02313 scrollY = contentsHeight() - visibleHeight() - contentsY(); 02314 02315 horizontalScrollBar()->setValue( horizontalScrollBar()->value()+scrollX ); 02316 verticalScrollBar()->setValue( verticalScrollBar()->value()+scrollY ); 02317 02318 d->scrollingSelf = false; 02319 02320 if ( (abs(deltax)<=maxx) && (abs(deltay)<=maxy) ) 02321 return true; 02322 else return false; 02323 02324 } 02325 02326 bool KHTMLView::focusNextPrevNode(bool next) 02327 { 02328 // Sets the focus node of the document to be the node after (or if 02329 // next is false, before) the current focus node. Only nodes that 02330 // are selectable (i.e. for which isFocusable() returns true) are 02331 // taken into account, and the order used is that specified in the 02332 // HTML spec (see DocumentImpl::nextFocusNode() and 02333 // DocumentImpl::previousFocusNode() for details). 02334 02335 DocumentImpl *doc = m_part->xmlDocImpl(); 02336 NodeImpl *oldFocusNode = doc->focusNode(); 02337 02338 // See whether we're in the middle of a detach, or hiding of the 02339 // widget. In this case, we will just clear focus, being careful not to emit events 02340 // or update rendering. Doing this also prevents the code below from going bonkers with 02341 // oldFocusNode not actually being focusable, etc. 02342 if (oldFocusNode) { 02343 if ((oldFocusNode->renderer() && !oldFocusNode->renderer()->parent()) 02344 || !oldFocusNode->isTabFocusable()) { 02345 doc->quietResetFocus(); 02346 return true; 02347 } 02348 } 02349 02350 #if 1 02351 // If the user has scrolled the document, then instead of picking 02352 // the next focusable node in the document, use the first one that 02353 // is within the visible area (if possible). 02354 if (d->scrollBarMoved) 02355 { 02356 NodeImpl *toFocus; 02357 if (next) 02358 toFocus = doc->nextFocusNode(oldFocusNode); 02359 else 02360 toFocus = doc->previousFocusNode(oldFocusNode); 02361 02362 if (!toFocus && oldFocusNode) { 02363 if (next) 02364 toFocus = doc->nextFocusNode(NULL); 02365 else 02366 toFocus = doc->previousFocusNode(NULL); 02367 } 02368 02369 while (toFocus && toFocus != oldFocusNode) 02370 { 02371 02372 QRect focusNodeRect = toFocus->getRect(); 02373 if ((focusNodeRect.left() > contentsX()) && (focusNodeRect.right() < contentsX() + visibleWidth()) && 02374 (focusNodeRect.top() > contentsY()) && (focusNodeRect.bottom() < contentsY() + visibleHeight())) { 02375 { 02376 QRect r = toFocus->getRect(); 02377 ensureVisible( r.right(), r.bottom()); 02378 ensureVisible( r.left(), r.top()); 02379 d->scrollBarMoved = false; 02380 d->tabMovePending = false; 02381 d->lastTabbingDirection = next; 02382 d->pseudoFocusNode = KHTMLViewPrivate::PFNone; 02383 m_part->xmlDocImpl()->setFocusNode(toFocus); 02384 Node guard(toFocus); 02385 if (!toFocus->hasOneRef() ) 02386 { 02387 emit m_part->nodeActivated(Node(toFocus)); 02388 } 02389 return true; 02390 } 02391 } 02392 if (next) 02393 toFocus = doc->nextFocusNode(toFocus); 02394 else 02395 toFocus = doc->previousFocusNode(toFocus); 02396 02397 if (!toFocus && oldFocusNode) 02398 { 02399 if (next) 02400 { 02401 toFocus = doc->nextFocusNode(NULL); 02402 } 02403 else 02404 { 02405 toFocus = doc->previousFocusNode(NULL); 02406 } 02407 } 02408 } 02409 02410 d->scrollBarMoved = false; 02411 } 02412 #endif 02413 02414 if (!oldFocusNode && d->pseudoFocusNode == KHTMLViewPrivate::PFNone) 02415 { 02416 ensureVisible(contentsX(), next?0:contentsHeight()); 02417 d->scrollBarMoved = false; 02418 d->pseudoFocusNode = next?KHTMLViewPrivate::PFTop:KHTMLViewPrivate::PFBottom; 02419 return true; 02420 } 02421 02422 NodeImpl *newFocusNode = NULL; 02423 02424 if (d->tabMovePending && next != d->lastTabbingDirection) 02425 { 02426 //kDebug ( 6000 ) << " tab move pending and tabbing direction changed!\n"; 02427 newFocusNode = oldFocusNode; 02428 } 02429 else if (next) 02430 { 02431 if (oldFocusNode || d->pseudoFocusNode == KHTMLViewPrivate::PFTop ) 02432 newFocusNode = doc->nextFocusNode(oldFocusNode); 02433 } 02434 else 02435 { 02436 if (oldFocusNode || d->pseudoFocusNode == KHTMLViewPrivate::PFBottom ) 02437 newFocusNode = doc->previousFocusNode(oldFocusNode); 02438 } 02439 02440 bool targetVisible = false; 02441 if (!newFocusNode) 02442 { 02443 if ( next ) 02444 { 02445 targetVisible = scrollTo(QRect(contentsX()+visibleWidth()/2,contentsHeight()-d->borderY,0,0)); 02446 } 02447 else 02448 { 02449 targetVisible = scrollTo(QRect(contentsX()+visibleWidth()/2,d->borderY,0,0)); 02450 } 02451 } 02452 else 02453 { 02454 // if it's an editable element, activate the caret 02455 if (!m_part->isCaretMode() && newFocusNode->isContentEditable()) { 02456 kDebug(6200) << "show caret! fn: " << newFocusNode->nodeName().string() << endl; 02457 m_part->clearCaretRectIfNeeded(); 02458 m_part->d->editor_context.m_selection.moveTo(Position(newFocusNode, 0L)); 02459 m_part->setCaretVisible(true); 02460 } else { 02461 m_part->setCaretVisible(false); 02462 kDebug(6200) << "hide caret! fn: " << newFocusNode->nodeName().string() << endl; 02463 } 02464 m_part->notifySelectionChanged(); 02465 02466 targetVisible = scrollTo(newFocusNode->getRect()); 02467 } 02468 02469 if (targetVisible) 02470 { 02471 //kDebug ( 6000 ) << " target reached.\n"; 02472 d->tabMovePending = false; 02473 02474 m_part->xmlDocImpl()->setFocusNode(newFocusNode); 02475 if (newFocusNode) 02476 { 02477 Node guard(newFocusNode); 02478 if (!newFocusNode->hasOneRef() ) 02479 { 02480 emit m_part->nodeActivated(Node(newFocusNode)); 02481 } 02482 return true; 02483 } 02484 else 02485 { 02486 d->pseudoFocusNode = next?KHTMLViewPrivate::PFBottom:KHTMLViewPrivate::PFTop; 02487 return false; 02488 } 02489 } 02490 else 02491 { 02492 if (!d->tabMovePending) 02493 d->lastTabbingDirection = next; 02494 d->tabMovePending = true; 02495 return true; 02496 } 02497 } 02498 02499 void KHTMLView::displayAccessKeys() 02500 { 02501 QVector< QChar > taken; 02502 displayAccessKeys( NULL, this, taken, false ); 02503 displayAccessKeys( NULL, this, taken, true ); 02504 } 02505 02506 void KHTMLView::displayAccessKeys( KHTMLView* caller, KHTMLView* origview, QVector< QChar >& taken, bool use_fallbacks ) 02507 { 02508 QMap< ElementImpl*, QChar > fallbacks; 02509 if( use_fallbacks ) 02510 fallbacks = buildFallbackAccessKeys(); 02511 for( NodeImpl* n = m_part->xmlDocImpl(); n != NULL; n = n->traverseNextNode()) { 02512 if( n->isElementNode()) { 02513 ElementImpl* en = static_cast< ElementImpl* >( n ); 02514 DOMString s = en->getAttribute( ATTR_ACCESSKEY ); 02515 QString accesskey; 02516 if( s.length() == 1 ) { 02517 QChar a = s.string()[ 0 ].toUpper(); 02518 if( qFind( taken.begin(), taken.end(), a ) == taken.end()) // !contains 02519 accesskey = a; 02520 } 02521 if( accesskey.isNull() && fallbacks.contains( en )) { 02522 QChar a = fallbacks[ en ].toUpper(); 02523 if( qFind( taken.begin(), taken.end(), a ) == taken.end()) // !contains 02524 accesskey = QString( "<qt><i>" ) + a + "</i></qt>"; 02525 } 02526 if( !accesskey.isNull()) { 02527 QRect rec=en->getRect(); 02528 QLabel *lab=new QLabel(accesskey,widget()); 02529 lab->setAttribute(Qt::WA_DeleteOnClose); 02530 lab->setObjectName("KHTMLAccessKey"); 02531 connect( origview, SIGNAL(hideAccessKeys()), lab, SLOT(close()) ); 02532 connect( this, SIGNAL(repaintAccessKeys()), lab, SLOT(repaint())); 02533 lab->setPalette(QToolTip::palette()); 02534 lab->setLineWidth(2); 02535 lab->setFrameStyle(QFrame::Box | QFrame::Plain); 02536 lab->setMargin(3); 02537 lab->adjustSize(); 02538 lab->setParent( widget() ); 02539 lab->setAutoFillBackground(true); 02540 lab->move( 02541 qMin(rec.left()+rec.width()/2 - contentsX(), contentsWidth() - lab->width()), 02542 qMin(rec.top()+rec.height()/2 - contentsY(), contentsHeight() - lab->height())); 02543 lab->show(); 02544 taken.append( accesskey[ 0 ] ); 02545 } 02546 } 02547 } 02548 if( use_fallbacks ) 02549 return; 02550 02551 QList<KParts::ReadOnlyPart*> frames = m_part->frames(); 02552 foreach( KParts::ReadOnlyPart* cur, frames ) { 02553 if( !qobject_cast<KHTMLPart*>(cur) ) 02554 continue; 02555 KHTMLPart* part = static_cast< KHTMLPart* >( cur ); 02556 if( part->view() && part->view() != caller ) 02557 part->view()->displayAccessKeys( this, origview, taken, use_fallbacks ); 02558 } 02559 02560 // pass up to the parent 02561 if (m_part->parentPart() && m_part->parentPart()->view() 02562 && m_part->parentPart()->view() != caller) 02563 m_part->parentPart()->view()->displayAccessKeys( this, origview, taken, use_fallbacks ); 02564 } 02565 02566 bool KHTMLView::isScrollingFromMouseWheel() const 02567 { 02568 return d->scrollingFromWheel != QPoint(-1,-1); 02569 } 02570 02571 void KHTMLView::accessKeysTimeout() 02572 { 02573 d->accessKeysActivated=false; 02574 d->accessKeysPreActivate = false; 02575 m_part->setStatusBarText(QString(), KHTMLPart::BarOverrideText); 02576 emit hideAccessKeys(); 02577 } 02578 02579 // Handling of the HTML accesskey attribute. 02580 bool KHTMLView::handleAccessKey( const QKeyEvent* ev ) 02581 { 02582 // Qt interprets the keyevent also with the modifiers, and ev->text() matches that, 02583 // but this code must act as if the modifiers weren't pressed 02584 QChar c; 02585 if( ev->key() >= Qt::Key_A && ev->key() <= Qt::Key_Z ) 02586 c = 'A' + ev->key() - Qt::Key_A; 02587 else if( ev->key() >= Qt::Key_0 && ev->key() <= Qt::Key_9 ) 02588 c = '0' + ev->key() - Qt::Key_0; 02589 else { 02590 // TODO fake XKeyEvent and XLookupString ? 02591 // This below seems to work e.g. for eacute though. 02592 if( ev->text().length() == 1 ) 02593 c = ev->text()[ 0 ]; 02594 } 02595 if( c.isNull()) 02596 return false; 02597 return focusNodeWithAccessKey( c ); 02598 } 02599 02600 bool KHTMLView::focusNodeWithAccessKey( QChar c, KHTMLView* caller ) 02601 { 02602 DocumentImpl *doc = m_part->xmlDocImpl(); 02603 if( !doc ) 02604 return false; 02605 ElementImpl* node = doc->findAccessKeyElement( c ); 02606 if( !node ) { 02607 QList<KParts::ReadOnlyPart*> frames = m_part->frames(); 02608 foreach( KParts::ReadOnlyPart* cur, frames ) { 02609 if( !qobject_cast<KHTMLPart*>(cur) ) 02610 continue; 02611 KHTMLPart* part = static_cast< KHTMLPart* >( cur ); 02612 if( part->view() && part->view() != caller 02613 && part->view()->focusNodeWithAccessKey( c, this )) 02614 return true; 02615 } 02616 // pass up to the parent 02617 if (m_part->parentPart() && m_part->parentPart()->view() 02618 && m_part->parentPart()->view() != caller 02619 && m_part->parentPart()->view()->focusNodeWithAccessKey( c, this )) 02620 return true; 02621 if( caller == NULL ) { // the active frame (where the accesskey was pressed) 02622 const QMap< ElementImpl*, QChar > fallbacks = buildFallbackAccessKeys(); 02623 for( QMap< ElementImpl*, QChar >::ConstIterator it = fallbacks.begin(); 02624 it != fallbacks.end(); 02625 ++it ) 02626 if( *it == c ) { 02627 node = it.key(); 02628 break; 02629 } 02630 } 02631 if( node == NULL ) 02632 return false; 02633 } 02634 02635 // Scroll the view as necessary to ensure that the new focus node is visible 02636 02637 QRect r = node->getRect(); 02638 ensureVisible( r.right(), r.bottom()); 02639 ensureVisible( r.left(), r.top()); 02640 02641 Node guard( node ); 02642 if( node->isFocusable()) { 02643 if (node->id()==ID_LABEL) { 02644 // if Accesskey is a label, give focus to the label's referrer. 02645 node=static_cast<ElementImpl *>(static_cast< HTMLLabelElementImpl* >( node )->getFormElement()); 02646 if (!node) return true; 02647 guard = node; 02648 } 02649 // Set focus node on the document 02650 #ifdef __GNUC__ 02651 #warning "port QFocusEvent::setReason( QFocusEvent::Shortcut ); to qt4" 02652 #endif 02653 //QFocusEvent::setReason( QFocusEvent::Shortcut ); 02654 m_part->xmlDocImpl()->setFocusNode(node); 02655 #ifdef __GNUC__ 02656 #warning "port QFocusEvent::resetReason(); to qt4" 02657 #endif 02658 //QFocusEvent::resetReason(); 02659 if( node != NULL && node->hasOneRef()) // deleted, only held by guard 02660 return true; 02661 emit m_part->nodeActivated(Node(node)); 02662 if( node != NULL && node->hasOneRef()) 02663 return true; 02664 } 02665 02666 switch( node->id()) { 02667 case ID_A: 02668 static_cast< HTMLAnchorElementImpl* >( node )->click(); 02669 break; 02670 case ID_INPUT: 02671 static_cast< HTMLInputElementImpl* >( node )->click(); 02672 break; 02673 case ID_BUTTON: 02674 static_cast< HTMLButtonElementImpl* >( node )->click(); 02675 break; 02676 case ID_AREA: 02677 static_cast< HTMLAreaElementImpl* >( node )->click(); 02678 break; 02679 case ID_TEXTAREA: 02680 break; // just focusing it is enough 02681 case ID_LEGEND: 02682 // TODO 02683 break; 02684 } 02685 return true; 02686 } 02687 02688 static QString getElementText( NodeImpl* start, bool after ) 02689 { 02690 QString ret; // nextSibling(), to go after e.g. </select> 02691 for( NodeImpl* n = after ? start->nextSibling() : start->traversePreviousNode(); 02692 n != NULL; 02693 n = after ? n->traverseNextNode() : n->traversePreviousNode()) { 02694 if( n->isTextNode()) { 02695 if( after ) 02696 ret += static_cast< TextImpl* >( n )->toString().string(); 02697 else 02698 ret.prepend( static_cast< TextImpl* >( n )->toString().string()); 02699 } else { 02700 switch( n->id()) { 02701 case ID_A: 02702 case ID_FONT: 02703 case ID_TT: 02704 case ID_U: 02705 case ID_B: 02706 case ID_I: 02707 case ID_S: 02708 case ID_STRIKE: 02709 case ID_BIG: 02710 case ID_SMALL: 02711 case ID_EM: 02712 case ID_STRONG: 02713 case ID_DFN: 02714 case ID_CODE: 02715 case ID_SAMP: 02716 case ID_KBD: 02717 case ID_VAR: 02718 case ID_CITE: 02719 case ID_ABBR: 02720 case ID_ACRONYM: 02721 case ID_SUB: 02722 case ID_SUP: 02723 case ID_SPAN: 02724 case ID_NOBR: 02725 case ID_WBR: 02726 break; 02727 case ID_TD: 02728 if( ret.trimmed().isEmpty()) 02729 break; 02730 // fall through 02731 default: 02732 return ret.simplified(); 02733 } 02734 } 02735 } 02736 return ret.simplified(); 02737 } 02738 02739 static QMap< NodeImpl*, QString > buildLabels( NodeImpl* start ) 02740 { 02741 QMap< NodeImpl*, QString > ret; 02742 for( NodeImpl* n = start; 02743 n != NULL; 02744 n = n->traverseNextNode()) { 02745 if( n->id() == ID_LABEL ) { 02746 HTMLLabelElementImpl* label = static_cast< HTMLLabelElementImpl* >( n ); 02747 NodeImpl* labelfor = label->getFormElement(); 02748 if( labelfor ) 02749 ret[ labelfor ] = label->innerText().string().simplified(); 02750 } 02751 } 02752 return ret; 02753 } 02754 02755 namespace khtml { 02756 struct AccessKeyData { 02757 ElementImpl* element; 02758 QString text; 02759 QString url; 02760 int priority; // 10(highest) - 0(lowest) 02761 }; 02762 } 02763 02764 QMap< ElementImpl*, QChar > KHTMLView::buildFallbackAccessKeys() const 02765 { 02766 // build a list of all possible candidate elements that could use an accesskey 02767 QLinkedList< AccessKeyData > data; // Note: this has to be a list type that keep iterators valid 02768 // when other entries are removed 02769 QMap< NodeImpl*, QString > labels = buildLabels( m_part->xmlDocImpl()); 02770 QMap< QString, QChar > hrefs; 02771 02772 for( NodeImpl* n = m_part->xmlDocImpl(); 02773 n != NULL; 02774 n = n->traverseNextNode()) { 02775 if( n->isElementNode()) { 02776 ElementImpl* element = static_cast< ElementImpl* >( n ); 02777 if( element->renderer() == NULL ) 02778 continue; // not visible 02779 QString text; 02780 QString url; 02781 int priority = 0; 02782 bool ignore = false; 02783 bool text_after = false; 02784 bool text_before = false; 02785 switch( element->id()) { 02786 case ID_A: 02787 url = khtml::parseURL(element->getAttribute(ATTR_HREF)).string(); 02788 if( url.isEmpty()) // doesn't have href, it's only an anchor 02789 continue; 02790 text = static_cast< HTMLElementImpl* >( element )->innerText().string().simplified(); 02791 priority = 2; 02792 break; 02793 case ID_INPUT: { 02794 HTMLInputElementImpl* in = static_cast< HTMLInputElementImpl* >( element ); 02795 switch( in->inputType()) { 02796 case HTMLInputElementImpl::SUBMIT: 02797 text = in->value().string(); 02798 if( text.isEmpty()) 02799 text = i18n( "Submit" ); 02800 priority = 7; 02801 break; 02802 case HTMLInputElementImpl::IMAGE: 02803 text = in->altText().string(); 02804 priority = 7; 02805 break; 02806 case HTMLInputElementImpl::BUTTON: 02807 text = in->value().string(); 02808 priority = 5; 02809 break; 02810 case HTMLInputElementImpl::RESET: 02811 text = in->value().string(); 02812 if( text.isEmpty()) 02813 text = i18n( "Reset" ); 02814 priority = 5; 02815 break; 02816 case HTMLInputElementImpl::HIDDEN: 02817 ignore = true; 02818 break; 02819 case HTMLInputElementImpl::CHECKBOX: 02820 case HTMLInputElementImpl::RADIO: 02821 text_after = true; 02822 priority = 5; 02823 break; 02824 case HTMLInputElementImpl::TEXT: 02825 case HTMLInputElementImpl::PASSWORD: 02826 case HTMLInputElementImpl::FILE: 02827 text_before = true; 02828 priority = 5; 02829 break; 02830 default: 02831 priority = 5; 02832 break; 02833 } 02834 break; 02835 } 02836 case ID_BUTTON: 02837 text = static_cast< HTMLElementImpl* >( element )->innerText().string().simplified(); 02838 switch( static_cast< HTMLButtonElementImpl* >( element )->buttonType()) { 02839 case HTMLButtonElementImpl::SUBMIT: 02840 if( text.isEmpty()) 02841 text = i18n( "Submit" ); 02842 priority = 7; 02843 break; 02844 case HTMLButtonElementImpl::RESET: 02845 if( text.isEmpty()) 02846 text = i18n( "Reset" ); 02847 priority = 5; 02848 break; 02849 default: 02850 priority = 5; 02851 break; 02852 } 02853 break; 02854 case ID_SELECT: // these don't have accesskey attribute, but quick access may be handy 02855 text_before = true; 02856 text_after = true; 02857 priority = 5; 02858 break; 02859 case ID_FRAME: 02860 ignore = true; 02861 break; 02862 default: 02863 ignore = !element->isFocusable(); 02864 priority = 2; 02865 break; 02866 } 02867 if( ignore ) 02868 continue; 02869 02870 // build map of manually assigned accesskeys and their targets 02871 DOMString akey = element->getAttribute( ATTR_ACCESSKEY ); 02872 if( akey.length() == 1 ) { 02873 hrefs[url] = akey.string()[ 0 ].toUpper(); 02874 continue; // has accesskey set, ignore 02875 } 02876 if( text.isNull() && labels.contains( element )) 02877 text = labels[ element ]; 02878 if( text.isNull() && text_before ) 02879 text = getElementText( element, false ); 02880 if( text.isNull() && text_after ) 02881 text = getElementText( element, true ); 02882 text = text.trimmed(); 02883 // increase priority of items which have explicitly specified accesskeys in the config 02884 const QList< QPair< QString, QChar > > priorities 02885 = m_part->settings()->fallbackAccessKeysAssignments(); 02886 for( QList< QPair< QString, QChar > >::ConstIterator it = priorities.begin(); 02887 it != priorities.end(); 02888 ++it ) { 02889 if( text == (*it).first ) 02890 priority = 10; 02891 } 02892 AccessKeyData tmp = { element, text, url, priority }; 02893 data.append( tmp ); 02894 } 02895 } 02896 02897 QList< QChar > keys; 02898 for( char c = 'A'; c <= 'Z'; ++c ) 02899 keys << c; 02900 for( char c = '0'; c <= '9'; ++c ) 02901 keys << c; 02902 for( NodeImpl* n = m_part->xmlDocImpl(); 02903 n != NULL; 02904 n = n->traverseNextNode()) { 02905 if( n->isElementNode()) { 02906 ElementImpl* en = static_cast< ElementImpl* >( n ); 02907 DOMString s = en->getAttribute( ATTR_ACCESSKEY ); 02908 if( s.length() == 1 ) { 02909 QChar c = s.string()[ 0 ].toUpper(); 02910 keys.removeAll( c ); // remove manually assigned accesskeys 02911 } 02912 } 02913 } 02914 02915 QMap< ElementImpl*, QChar > ret; 02916 for( int priority = 10; priority >= 0; --priority ) { 02917 for( QLinkedList< AccessKeyData >::Iterator it = data.begin(); 02918 it != data.end(); 02919 ) { 02920 if( (*it).priority != priority ) { 02921 ++it; 02922 continue; 02923 } 02924 if( keys.isEmpty()) 02925 break; 02926 QString text = (*it).text; 02927 QChar key; 02928 const QString url = (*it).url; 02929 // an identical link already has an accesskey assigned 02930 if( hrefs.contains( url ) ) { 02931 it = data.erase( it ); 02932 continue; 02933 } 02934 if( !text.isEmpty()) { 02935 const QList< QPair< QString, QChar > > priorities 02936 = m_part->settings()->fallbackAccessKeysAssignments(); 02937 for( QList< QPair< QString, QChar > >::ConstIterator it = priorities.begin(); 02938 it != priorities.end(); 02939 ++it ) 02940 if( text == (*it).first && keys.contains( (*it).second )) { 02941 key = (*it).second; 02942 break; 02943 } 02944 } 02945 // try first to select the first character as the accesskey, 02946 // then first character of the following words, 02947 // and then simply the first free character 02948 if( key.isNull() && !text.isEmpty()) { 02949 const QStringList words = text.split( ' ' ); 02950 for( QStringList::ConstIterator it = words.begin(); 02951 it != words.end(); 02952 ++it ) { 02953 if( keys.contains( (*it)[ 0 ].toUpper())) { 02954 key = (*it)[ 0 ].toUpper(); 02955 break; 02956 } 02957 } 02958 } 02959 if( key.isNull() && !text.isEmpty()) { 02960 for( int i = 0; i < text.length(); ++i ) { 02961 if( keys.contains( text[ i ].toUpper())) { 02962 key = text[ i ].toUpper(); 02963 break; 02964 } 02965 } 02966 } 02967 if( key.isNull()) 02968 key = keys.front(); 02969 ret[ (*it).element ] = key; 02970 keys.removeAll( key ); 02971 it = data.erase( it ); 02972 // assign the same accesskey also to other elements pointing to the same url 02973 if( !url.isEmpty() && !url.startsWith( "javascript:", Qt::CaseInsensitive )) { 02974 for( QLinkedList< AccessKeyData >::Iterator it2 = data.begin(); 02975 it2 != data.end(); 02976 ) { 02977 if( (*it2).url == url ) { 02978 ret[ (*it2).element ] = key; 02979 if( it == it2 ) 02980 ++it; 02981 it2 = data.erase( it2 ); 02982 } else 02983 ++it2; 02984 } 02985 } 02986 } 02987 } 02988 return ret; 02989 } 02990 02991 void KHTMLView::setMediaType( const QString &medium ) 02992 { 02993 m_medium = medium; 02994 } 02995 02996 QString KHTMLView::mediaType() const 02997 { 02998 return m_medium; 02999 } 03000 03001 bool KHTMLView::pagedMode() const 03002 { 03003 return d->paged; 03004 } 03005 03006 void KHTMLView::setWidgetVisible(RenderWidget* w, bool vis) 03007 { 03008 if (vis) { 03009 d->visibleWidgets.insert(w, w->widget()); 03010 } 03011 else 03012 d->visibleWidgets.remove(w); 03013 } 03014 03015 bool KHTMLView::needsFullRepaint() const 03016 { 03017 return d->needsFullRepaint; 03018 } 03019 03020 namespace { 03021 class QPointerDeleter 03022 { 03023 public: 03024 explicit QPointerDeleter(QObject* o) : obj(o) {} 03025 ~QPointerDeleter() { delete obj; } 03026 private: 03027 const QPointer<QObject> obj; 03028 }; 03029 } 03030 03031 void KHTMLView::print(bool quick) 03032 { 03033 if(!m_part->xmlDocImpl()) return; 03034 khtml::RenderCanvas *root = static_cast<khtml::RenderCanvas *>(m_part->xmlDocImpl()->renderer()); 03035 if(!root) return; 03036 03037 QPointer<KHTMLPrintSettings> printSettings(new KHTMLPrintSettings); //XXX: doesn't save settings between prints like this 03038 const QPointerDeleter settingsDeleter(printSettings); //the printdialog takes ownership of the settings widget, thus this workaround to avoid double deletion 03039 QPrinter printer; 03040 QPointer<QPrintDialog> dialog = KdePrint::createPrintDialog(&printer, KdePrint::SystemSelectsPages, QList<QWidget*>() << printSettings.data(), this); 03041 03042 const QPointerDeleter dialogDeleter(dialog); 03043 03044 QString docname = m_part->xmlDocImpl()->URL().prettyUrl(); 03045 if ( !docname.isEmpty() ) 03046 docname = KStringHandler::csqueeze(docname, 80); 03047 03048 if(quick || (dialog->exec() && dialog)) { /*'this' and thus dialog might have been deleted while exec()!*/ 03049 viewport()->setCursor( Qt::WaitCursor ); // only viewport(), no QApplication::, otherwise we get the busy cursor in kdeprint's dialogs 03050 // set up KPrinter 03051 printer.setFullPage(false); 03052 printer.setCreator(QString("KDE %1.%2.%3 HTML Library").arg(KDE_VERSION_MAJOR).arg(KDE_VERSION_MINOR).arg(KDE_VERSION_RELEASE)); 03053 printer.setDocName(docname); 03054 03055 QPainter *p = new QPainter; 03056 p->begin( &printer ); 03057 khtml::setPrintPainter( p ); 03058 03059 m_part->xmlDocImpl()->setPaintDevice( &printer ); 03060 QString oldMediaType = mediaType(); 03061 setMediaType( "print" ); 03062 // We ignore margin settings for html and body when printing 03063 // and use the default margins from the print-system 03064 // (In Qt 3.0.x the default margins are hardcoded in Qt) 03065 m_part->xmlDocImpl()->setPrintStyleSheet( printSettings->printFriendly() ? 03066 "* { background-image: none !important;" 03067 " background-color: white !important;" 03068 " color: black !important; }" 03069 "body { margin: 0px !important; }" 03070 "html { margin: 0px !important; }" : 03071 "body { margin: 0px !important; }" 03072 "html { margin: 0px !important; }" 03073 ); 03074 03075 kDebug(6000) << "printing: physical page width = " << printer.width() 03076 << " height = " << printer.height() << endl; 03077 root->setStaticMode(true); 03078 root->setPagedMode(true); 03079 root->setWidth(printer.width()); 03080 // root->setHeight(printer.height()); 03081 root->setPageTop(0); 03082 root->setPageBottom(0); 03083 d->paged = true; 03084 03085 m_part->xmlDocImpl()->styleSelector()->computeFontSizes(printer.logicalDpiY(), 100); 03086 m_part->xmlDocImpl()->updateStyleSelector(); 03087 root->setPrintImages(printSettings->printImages()); 03088 root->makePageBreakAvoidBlocks(); 03089 03090 root->setNeedsLayoutAndMinMaxRecalc(); 03091 root->layout(); 03092 03093 // check sizes ask for action.. (scale or clip) 03094 03095 bool printHeader = printSettings->printHeader(); 03096 03097 int headerHeight = 0; 03098 QFont headerFont("Sans Serif", 8); 03099 03100 QString headerLeft = KGlobal::locale()->formatDate(QDate::currentDate(),KLocale::ShortDate); 03101 QString headerMid = docname; 03102 QString headerRight; 03103 03104 if (printHeader) 03105 { 03106 p->setFont(headerFont); 03107 headerHeight = (p->fontMetrics().lineSpacing() * 3) / 2; 03108 } 03109 03110 // ok. now print the pages. 03111 kDebug(6000) << "printing: html page width = " << root->docWidth() 03112 << " height = " << root->docHeight() << endl; 03113 kDebug(6000) << "printing: margins left = " << printer.pageRect().left() - printer.paperRect().left() 03114 << " top = " << printer.pageRect().top() - printer.paperRect().top() << endl; 03115 kDebug(6000) << "printing: paper width = " << printer.width() 03116 << " height = " << printer.height() << endl; 03117 // if the width is too large to fit on the paper we just scale 03118 // the whole thing. 03119 int pageWidth = printer.width(); 03120 int pageHeight = printer.height(); 03121 p->setClipRect(0,0, pageWidth, pageHeight); 03122 03123 pageHeight -= headerHeight; 03124 03125 bool scalePage = false; 03126 double scale = 0.0; 03127 #ifndef QT_NO_TRANSFORMATIONS 03128 if(root->docWidth() > printer.width()) { 03129 scalePage = true; 03130 scale = ((double) printer.width())/((double) root->docWidth()); 03131 pageHeight = (int) (pageHeight/scale); 03132 pageWidth = (int) (pageWidth/scale); 03133 headerHeight = (int) (headerHeight/scale); 03134 } 03135 #endif 03136 kDebug(6000) << "printing: scaled html width = " << pageWidth 03137 << " height = " << pageHeight << endl; 03138 03139 root->setHeight(pageHeight); 03140 root->setPageBottom(pageHeight); 03141 root->setNeedsLayout(true); 03142 root->layoutIfNeeded(); 03143 // m_part->slotDebugRenderTree(); 03144 03145 // Squeeze header to make it it on the page. 03146 if (printHeader) 03147 { 03148 int available_width = printer.width() - 10 - 03149 2 * qMax(p->boundingRect(0, 0, printer.width(), p->fontMetrics().lineSpacing(), Qt::AlignLeft, headerLeft).width(), 03150 p->boundingRect(0, 0, printer.width(), p->fontMetrics().lineSpacing(), Qt::AlignLeft, headerRight).width()); 03151 if (available_width < 150) 03152 available_width = 150; 03153 int mid_width; 03154 int squeeze = 120; 03155 do { 03156 headerMid = KStringHandler::csqueeze(docname, squeeze); 03157 mid_width = p->boundingRect(0, 0, printer.width(), p->fontMetrics().lineSpacing(), Qt::AlignLeft, headerMid).width(); 03158 squeeze -= 10; 03159 } while (mid_width > available_width); 03160 } 03161 03162 int top = 0; 03163 int bottom = 0; 03164 int page = 1; 03165 while(top < root->docHeight()) { 03166 if(top > 0) printer.newPage(); 03167 #ifndef QT_NO_TRANSFORMATIONS 03168 if (scalePage) 03169 p->scale(scale, scale); 03170 #endif 03171 p->save(); 03172 p->setClipRect(0, 0, pageWidth, headerHeight); 03173 if (printHeader) 03174 { 03175 int dy = p->fontMetrics().lineSpacing(); 03176 p->setPen(Qt::black); 03177 p->setFont(headerFont); 03178 03179 headerRight = QString("#%1").arg(page); 03180 03181 p->drawText(0, 0, printer.width(), dy, Qt::AlignLeft, headerLeft); 03182 p->drawText(0, 0, printer.width(), dy, Qt::AlignHCenter, headerMid); 03183 p->drawText(0, 0, printer.width(), dy, Qt::AlignRight, headerRight); 03184 } 03185 03186 p->restore(); 03187 p->translate(0, headerHeight-top); 03188 03189 bottom = top+pageHeight; 03190 03191 root->setPageTop(top); 03192 root->setPageBottom(bottom); 03193 root->setPageNumber(page); 03194 03195 root->layer()->paint(p, QRect(0, top, pageWidth, pageHeight)); 03196 kDebug(6000) << "printed: page " << page <<" bottom At = " << bottom; 03197 03198 top = bottom; 03199 p->resetTransform(); 03200 page++; 03201 } 03202 03203 p->end(); 03204 delete p; 03205 03206 // and now reset the layout to the usual one... 03207 root->setPagedMode(false); 03208 root->setStaticMode(false); 03209 d->paged = false; 03210 khtml::setPrintPainter( 0 ); 03211 setMediaType( oldMediaType ); 03212 m_part->xmlDocImpl()->setPaintDevice( this ); 03213 m_part->xmlDocImpl()->styleSelector()->computeFontSizes(m_part->xmlDocImpl()->logicalDpiY(), m_part->fontScaleFactor()); 03214 m_part->xmlDocImpl()->updateStyleSelector(); 03215 viewport()->unsetCursor(); 03216 } 03217 } 03218 03219 void KHTMLView::slotPaletteChanged() 03220 { 03221 if(!m_part->xmlDocImpl()) return; 03222 DOM::DocumentImpl *document = m_part->xmlDocImpl(); 03223 if (!document->isHTMLDocument()) return; 03224 khtml::RenderCanvas *root = static_cast<khtml::RenderCanvas *>(document->renderer()); 03225 if(!root) return; 03226 root->style()->resetPalette(); 03227 NodeImpl *body = static_cast<HTMLDocumentImpl*>(document)->body(); 03228 if(!body) return; 03229 body->setChanged(true); 03230 body->recalcStyle( NodeImpl::Force ); 03231 } 03232 03233 void KHTMLView::paint(QPainter *p, const QRect &rc, int yOff, bool *more) 03234 { 03235 if(!m_part->xmlDocImpl()) return; 03236 khtml::RenderCanvas *root = static_cast<khtml::RenderCanvas *>(m_part->xmlDocImpl()->renderer()); 03237 if(!root) return; 03238 #ifdef SPEED_DEBUG 03239 d->firstRepaintPending = false; 03240 #endif 03241 03242 QPaintDevice* opd = m_part->xmlDocImpl()->paintDevice(); 03243 m_part->xmlDocImpl()->setPaintDevice(p->device()); 03244 root->setPagedMode(true); 03245 root->setStaticMode(true); 03246 root->setWidth(rc.width()); 03247 03248 // save() 03249 QRegion creg = p->clipRegion(); 03250 QTransform t = p->worldTransform(); 03251 QRect w = p->window(); 03252 QRect v = p->viewport(); 03253 bool vte = p->viewTransformEnabled(); 03254 bool wme = p->worldMatrixEnabled(); 03255 03256 p->setClipRect(rc); 03257 p->translate(rc.left(), rc.top()); 03258 double scale = ((double) rc.width()/(double) root->docWidth()); 03259 int height = (int) ((double) rc.height() / scale); 03260 #ifndef QT_NO_TRANSFORMATIONS 03261 p->scale(scale, scale); 03262 #endif 03263 root->setPageTop(yOff); 03264 root->setPageBottom(yOff+height); 03265 03266 root->layer()->paint(p, QRect(0, yOff, root->docWidth(), height)); 03267 if (more) 03268 *more = yOff + height < root->docHeight(); 03269 03270 // restore() 03271 p->setWorldTransform(t); 03272 p->setWindow(w); 03273 p->setViewport(v); 03274 p->setViewTransformEnabled( vte ); 03275 p->setWorldMatrixEnabled( wme ); 03276 if (!creg.isEmpty()) 03277 p->setClipRegion( creg ); 03278 else 03279 p->setClipRegion(QRegion(), Qt::NoClip); 03280 03281 root->setPagedMode(false); 03282 root->setStaticMode(false); 03283 m_part->xmlDocImpl()->setPaintDevice( opd ); 03284 } 03285 03286 void KHTMLView::render(QPainter* p, const QRect& r, const QPoint& off) 03287 { 03288 #ifdef SPEED_DEBUG 03289 d->firstRepaintPending = false; 03290 #endif 03291 QRect clip(off.x()+r.x(), off.y()+r.y(),r.width(),r.height()); 03292 if(!m_part || !m_part->xmlDocImpl() || !m_part->xmlDocImpl()->renderer()) { 03293 p->fillRect(clip, palette().brush(QPalette::Active, QPalette::Base)); 03294 return; 03295 } 03296 QPaintDevice* opd = m_part->xmlDocImpl()->paintDevice(); 03297 m_part->xmlDocImpl()->setPaintDevice(p->device()); 03298 03299 // save() 03300 QRegion creg = p->clipRegion(); 03301 QTransform t = p->worldTransform(); 03302 QRect w = p->window(); 03303 QRect v = p->viewport(); 03304 bool vte = p->viewTransformEnabled(); 03305 bool wme = p->worldMatrixEnabled(); 03306 03307 p->setClipRect(clip); 03308 QRect rect = r.translated(contentsX(),contentsY()); 03309 p->translate(off.x()-contentsX(), off.y()-contentsY()); 03310 03311 m_part->xmlDocImpl()->renderer()->layer()->paint(p, rect); 03312 03313 // restore() 03314 p->setWorldTransform(t); 03315 p->setWindow(w); 03316 p->setViewport(v); 03317 p->setViewTransformEnabled( vte ); 03318 p->setWorldMatrixEnabled( wme ); 03319 if (!creg.isEmpty()) 03320 p->setClipRegion( creg ); 03321 else 03322 p->setClipRegion(QRegion(), Qt::NoClip); 03323 03324 m_part->xmlDocImpl()->setPaintDevice( opd ); 03325 } 03326 03327 void KHTMLView::setHasStaticBackground(bool partial) 03328 { 03329 // full static iframe is irreversible for now 03330 if (d->staticWidget == KHTMLViewPrivate::SBFull && m_kwp->isRedirected()) 03331 return; 03332 03333 d->staticWidget = partial ? 03334 KHTMLViewPrivate::SBPartial : KHTMLViewPrivate::SBFull; 03335 } 03336 03337 void KHTMLView::setHasNormalBackground() 03338 { 03339 // full static iframe is irreversible for now 03340 if (d->staticWidget == KHTMLViewPrivate::SBFull && m_kwp->isRedirected()) 03341 return; 03342 03343 d->staticWidget = KHTMLViewPrivate::SBNone; 03344 } 03345 03346 void KHTMLView::addStaticObject(bool fixed) 03347 { 03348 if (fixed) 03349 d->fixedObjectsCount++; 03350 else 03351 d->staticObjectsCount++; 03352 03353 setHasStaticBackground( true /*partial*/ ); 03354 } 03355 03356 void KHTMLView::removeStaticObject(bool fixed) 03357 { 03358 if (fixed) 03359 d->fixedObjectsCount--; 03360 else 03361 d->staticObjectsCount--; 03362 03363 assert( d->fixedObjectsCount >= 0 && d->staticObjectsCount >= 0 ); 03364 03365 if (!d->staticObjectsCount && !d->fixedObjectsCount) 03366 setHasNormalBackground(); 03367 else 03368 setHasStaticBackground( true /*partial*/ ); 03369 } 03370 03371 void KHTMLView::setVerticalScrollBarPolicy( Qt::ScrollBarPolicy policy ) 03372 { 03373 #ifndef KHTML_NO_SCROLLBARS 03374 d->vpolicy = policy; 03375 QScrollArea::setVerticalScrollBarPolicy(policy); 03376 #else 03377 Q_UNUSED( policy ); 03378 #endif 03379 } 03380 03381 void KHTMLView::setHorizontalScrollBarPolicy( Qt::ScrollBarPolicy policy ) 03382 { 03383 #ifndef KHTML_NO_SCROLLBARS 03384 d->hpolicy = policy; 03385 QScrollArea::setHorizontalScrollBarPolicy(policy); 03386 #else 03387 Q_UNUSED( policy ); 03388 #endif 03389 } 03390 03391 void KHTMLView::restoreScrollBar() 03392 { 03393 int ow = visibleWidth(); 03394 QScrollArea::setVerticalScrollBarPolicy(d->vpolicy); 03395 if (visibleWidth() != ow) 03396 layout(); 03397 d->prevScrollbarVisible = verticalScrollBar()->isVisible(); 03398 } 03399 03400 QStringList KHTMLView::formCompletionItems(const QString &name) const 03401 { 03402 if (!m_part->settings()->isFormCompletionEnabled()) 03403 return QStringList(); 03404 if (!d->formCompletions) 03405 d->formCompletions = new KConfig(KStandardDirs::locateLocal("data", "khtml/formcompletions")); 03406 return d->formCompletions->group("").readEntry(name, QStringList()); 03407 } 03408 03409 void KHTMLView::clearCompletionHistory(const QString& name) 03410 { 03411 if (!d->formCompletions) 03412 { 03413 d->formCompletions = new KConfig(KStandardDirs::locateLocal("data", "khtml/formcompletions")); 03414 } 03415 d->formCompletions->group("").writeEntry(name, ""); 03416 d->formCompletions->sync(); 03417 } 03418 03419 void KHTMLView::addFormCompletionItem(const QString &name, const QString &value) 03420 { 03421 if (!m_part->settings()->isFormCompletionEnabled()) 03422 return; 03423 // don't store values that are all numbers or just numbers with 03424 // dashes or spaces as those are likely credit card numbers or 03425 // something similar 03426 bool cc_number(true); 03427 for ( int i = 0; i < value.length(); ++i) 03428 { 03429 QChar c(value[i]); 03430 if (!c.isNumber() && c != '-' && !c.isSpace()) 03431 { 03432 cc_number = false; 03433 break; 03434 } 03435 } 03436 if (cc_number) 03437 return; 03438 QStringList items = formCompletionItems(name); 03439 if (!items.contains(value)) 03440 items.prepend(value); 03441 while ((int)items.count() > m_part->settings()->maxFormCompletionItems()) 03442 items.erase(items.isEmpty() ? items.end() : --items.end()); 03443 d->formCompletions->group("").writeEntry(name, items); 03444 } 03445 03446 void KHTMLView::addNonPasswordStorableSite(const QString& host) 03447 { 03448 if (!d->formCompletions) { 03449 d->formCompletions = new KConfig(KStandardDirs::locateLocal("data", "khtml/formcompletions")); 03450 } 03451 03452 KConfigGroup cg( d->formCompletions, "NonPasswordStorableSites"); 03453 QStringList sites = cg.readEntry("Sites", QStringList()); 03454 sites.append(host); 03455 cg.writeEntry("Sites", sites); 03456 cg.sync(); 03457 } 03458 03459 03460 void KHTMLView::delNonPasswordStorableSite(const QString& host) 03461 { 03462 if (!d->formCompletions) { 03463 d->formCompletions = new KConfig(KStandardDirs::locateLocal("data", "khtml/formcompletions")); 03464 } 03465 03466 KConfigGroup cg( d->formCompletions, "NonPasswordStorableSites"); 03467 QStringList sites = cg.readEntry("Sites", QStringList()); 03468 sites.removeOne(host); 03469 cg.writeEntry("Sites", sites); 03470 cg.sync(); 03471 } 03472 03473 bool KHTMLView::nonPasswordStorableSite(const QString& host) const 03474 { 03475 if (!d->formCompletions) { 03476 d->formCompletions = new KConfig(KStandardDirs::locateLocal("data", "khtml/formcompletions")); 03477 } 03478 QStringList sites = d->formCompletions->group( "NonPasswordStorableSites" ).readEntry("Sites", QStringList()); 03479 return (sites.indexOf(host) != -1); 03480 } 03481 03482 // returns true if event should be swallowed 03483 bool KHTMLView::dispatchMouseEvent(int eventId, DOM::NodeImpl *targetNode, 03484 DOM::NodeImpl *targetNodeNonShared, bool cancelable, 03485 int detail,QMouseEvent *_mouse, bool setUnder, 03486 int mouseEventType, int orient) 03487 { 03488 // if the target node is a text node, dispatch on the parent node - rdar://4196646 (and #76948) 03489 if (targetNode && targetNode->isTextNode()) 03490 targetNode = targetNode->parentNode(); 03491 03492 if (d->underMouse) 03493 d->underMouse->deref(); 03494 d->underMouse = targetNode; 03495 if (d->underMouse) 03496 d->underMouse->ref(); 03497 03498 if (d->underMouseNonShared) 03499 d->underMouseNonShared->deref(); 03500 d->underMouseNonShared = targetNodeNonShared; 03501 if (d->underMouseNonShared) 03502 d->underMouseNonShared->ref(); 03503 03504 bool isWheelEvent = (mouseEventType == DOM::NodeImpl::MouseWheel); 03505 03506 int exceptioncode = 0; 03507 int pageX = _mouse->x(); 03508 int pageY = _mouse->y(); 03509 revertTransforms(pageX, pageY); 03510 int clientX = pageX - contentsX(); 03511 int clientY = pageY - contentsY(); 03512 int screenX = _mouse->globalX(); 03513 int screenY = _mouse->globalY(); 03514 int button = -1; 03515 switch (_mouse->button()) { 03516 case Qt::LeftButton: 03517 button = 0; 03518 break; 03519 case Qt::MidButton: 03520 button = 1; 03521 break; 03522 case Qt::RightButton: 03523 button = 2; 03524 break; 03525 default: 03526 break; 03527 } 03528 if (d->accessKeysEnabled && d->accessKeysPreActivate && button!=-1) 03529 d->accessKeysPreActivate=false; 03530 03531 bool ctrlKey = (_mouse->modifiers() & Qt::ControlModifier); 03532 bool altKey = (_mouse->modifiers() & Qt::AltModifier); 03533 bool shiftKey = (_mouse->modifiers() & Qt::ShiftModifier); 03534 bool metaKey = (_mouse->modifiers() & Qt::MetaModifier); 03535 03536 // mouseout/mouseover 03537 if (setUnder && d->oldUnderMouse != targetNode) { 03538 if (d->oldUnderMouse && d->oldUnderMouse->document() != m_part->xmlDocImpl()) { 03539 d->oldUnderMouse->deref(); 03540 d->oldUnderMouse = 0; 03541 } 03542 // send mouseout event to the old node 03543 if (d->oldUnderMouse) { 03544 // send mouseout event to the old node 03545 MouseEventImpl *me = new MouseEventImpl(EventImpl::MOUSEOUT_EVENT, 03546 true,true,m_part->xmlDocImpl()->defaultView(), 03547 0,screenX,screenY,clientX,clientY,pageX, pageY, 03548 ctrlKey,altKey,shiftKey,metaKey, 03549 button,targetNode); 03550 me->ref(); 03551 d->oldUnderMouse->dispatchEvent(me,exceptioncode,true); 03552 me->deref(); 03553 } 03554 // send mouseover event to the new node 03555 if (targetNode) { 03556 MouseEventImpl *me = new MouseEventImpl(EventImpl::MOUSEOVER_EVENT, 03557 true,true,m_part->xmlDocImpl()->defaultView(), 03558 0,screenX,screenY,clientX,clientY,pageX, pageY, 03559 ctrlKey,altKey,shiftKey,metaKey, 03560 button,d->oldUnderMouse); 03561 03562 me->ref(); 03563 targetNode->dispatchEvent(me,exceptioncode,true); 03564 me->deref(); 03565 } 03566 if (d->oldUnderMouse) 03567 d->oldUnderMouse->deref(); 03568 d->oldUnderMouse = targetNode; 03569 if (d->oldUnderMouse) 03570 d->oldUnderMouse->ref(); 03571 } 03572 03573 bool swallowEvent = false; 03574 03575 if (targetNode) { 03576 // if the target node is a disabled widget, we don't want any full-blown mouse events 03577 if (targetNode->isGenericFormElement() 03578 && static_cast<HTMLGenericFormElementImpl*>(targetNode)->disabled()) 03579 return true; 03580 03581 // send the actual event 03582 bool dblclick = ( eventId == EventImpl::CLICK_EVENT && 03583 _mouse->type() == QEvent::MouseButtonDblClick ); 03584 MouseEventImpl *me = new MouseEventImpl(static_cast<EventImpl::EventId>(eventId), 03585 true,cancelable,m_part->xmlDocImpl()->defaultView(), 03586 detail,screenX,screenY,clientX,clientY,pageX, pageY, 03587 ctrlKey,altKey,shiftKey,metaKey, 03588 button,0, isWheelEvent ? 0 : _mouse, dblclick, 03589 isWheelEvent ? static_cast<MouseEventImpl::Orientation>(orient) : MouseEventImpl::ONone ); 03590 me->ref(); 03591 if ( !d->m_mouseEventsTarget && RenderLayer::gScrollBar && eventId == EventImpl::MOUSEDOWN_EVENT ) 03592 // button is pressed inside a layer scrollbar, so make it the target for future mousemove events until released 03593 d->m_mouseEventsTarget = RenderLayer::gScrollBar; 03594 if ( d->m_mouseEventsTarget && qobject_cast<QScrollBar*>(d->m_mouseEventsTarget) && 03595 dynamic_cast<KHTMLWidget*>(static_cast<QWidget*>(d->m_mouseEventsTarget)) ) { 03596 // we have a sticky mouse event target and it is a layer's scrollbar. Forward events manually. 03597 // ### should use the dom 03598 KHTMLWidget*w = dynamic_cast<KHTMLWidget*>(static_cast<QWidget*>(d->m_mouseEventsTarget)); 03599 QPoint p = w->m_kwp->absolutePos(); 03600 QMouseEvent fw(_mouse->type(), QPoint(pageX, pageY)-p, _mouse->button(), _mouse->buttons(), _mouse->modifiers()); 03601 static_cast<RenderWidget::EventPropagator *>(static_cast<QWidget*>(d->m_mouseEventsTarget))->sendEvent(&fw); 03602 if (_mouse->type() == QMouseEvent::MouseButtonPress && _mouse->button() == Qt::RightButton) { 03603 QContextMenuEvent cme(QContextMenuEvent::Mouse, p); 03604 static_cast<RenderWidget::EventPropagator *>(static_cast<QWidget*>(d->m_mouseEventsTarget))->sendEvent(&cme); 03605 d->m_mouseEventsTarget = 0; 03606 } 03607 swallowEvent = true; 03608 } else { 03609 targetNode->dispatchEvent(me,exceptioncode,true); 03610 bool defaultHandled = me->defaultHandled(); 03611 if (defaultHandled || me->defaultPrevented()) 03612 swallowEvent = true; 03613 } 03614 if (eventId == EventImpl::MOUSEDOWN_EVENT && !me->defaultPrevented()) { 03615 // Focus should be shifted on mouse down, not on a click. -dwh 03616 // Blur current focus node when a link/button is clicked; this 03617 // is expected by some sites that rely on onChange handlers running 03618 // from form fields before the button click is processed. 03619 DOM::NodeImpl* nodeImpl = targetNode; 03620 for ( ; nodeImpl && !nodeImpl->isFocusable(); nodeImpl = nodeImpl->parentNode()) 03621 {} 03622 if (nodeImpl && nodeImpl->isMouseFocusable()) 03623 m_part->xmlDocImpl()->setFocusNode(nodeImpl); 03624 else if (!nodeImpl || !nodeImpl->focused()) 03625 m_part->xmlDocImpl()->setFocusNode(0); 03626 } 03627 me->deref(); 03628 } 03629 03630 return swallowEvent; 03631 } 03632 03633 void KHTMLView::setIgnoreWheelEvents( bool e ) 03634 { 03635 d->ignoreWheelEvents = e; 03636 } 03637 03638 #ifndef QT_NO_WHEELEVENT 03639 03640 void KHTMLView::wheelEvent(QWheelEvent* e) 03641 { 03642 // check if we should reset the state of the indicator describing if 03643 // we are currently scrolling the view as a result of wheel events 03644 if (d->scrollingFromWheel != QPoint(-1,-1) && d->scrollingFromWheel != QCursor::pos()) 03645 d->scrollingFromWheel = d->scrollingFromWheelTimerId ? QCursor::pos() : QPoint(-1,-1); 03646 03647 if (d->accessKeysEnabled && d->accessKeysPreActivate) d->accessKeysPreActivate=false; 03648 03649 if ( ( e->modifiers() & Qt::ControlModifier) == Qt::ControlModifier ) 03650 { 03651 emit zoomView( - e->delta() ); 03652 e->accept(); 03653 } 03654 else if (d->firstLayoutPending) 03655 { 03656 e->accept(); 03657 } 03658 else if( !m_kwp->isRedirected() && 03659 ( (e->orientation() == Qt::Vertical && 03660 ((d->ignoreWheelEvents && !verticalScrollBar()->isVisible()) 03661 || (e->delta() > 0 && contentsY() <= 0) 03662 || (e->delta() < 0 && contentsY() >= contentsHeight() - visibleHeight()))) 03663 || 03664 (e->orientation() == Qt::Horizontal && 03665 ((d->ignoreWheelEvents && !horizontalScrollBar()->isVisible()) 03666 || (e->delta() > 0 && contentsX() <=0) 03667 || (e->delta() < 0 && contentsX() >= contentsWidth() - visibleWidth())))) 03668 && m_part->parentPart()) 03669 { 03670 if ( m_part->parentPart()->view() ) 03671 m_part->parentPart()->view()->wheelEvent( e ); 03672 e->ignore(); 03673 } 03674 else 03675 { 03676 int xm = e->x(); 03677 int ym = e->y(); 03678 revertTransforms(xm, ym); 03679 03680 DOM::NodeImpl::MouseEvent mev( e->buttons(), DOM::NodeImpl::MouseWheel ); 03681 m_part->xmlDocImpl()->prepareMouseEvent( false, xm, ym, &mev ); 03682 03683 MouseEventImpl::Orientation o = MouseEventImpl::OVertical; 03684 if (e->orientation() == Qt::Horizontal) 03685 o = MouseEventImpl::OHorizontal; 03686 03687 QMouseEvent _mouse(QEvent::MouseMove, e->pos(), Qt::NoButton, e->buttons(), e->modifiers()); 03688 bool swallow = dispatchMouseEvent(EventImpl::KHTML_MOUSEWHEEL_EVENT,mev.innerNode.handle(),mev.innerNonSharedNode.handle(), 03689 true,-e->delta()/40,&_mouse,true,DOM::NodeImpl::MouseWheel,o); 03690 03691 if (swallow) 03692 return; 03693 03694 d->scrollBarMoved = true; 03695 d->scrollingFromWheel = QCursor::pos(); 03696 if (d->smoothScrollMode != SSMDisabled) 03697 d->shouldSmoothScroll = true; 03698 if (d->scrollingFromWheelTimerId) 03699 killTimer(d->scrollingFromWheelTimerId); 03700 d->scrollingFromWheelTimerId = startTimer(400); 03701 03702 if (m_part->parentPart()) { 03703 // don't propagate if we are a sub-frame and our scrollbars are already at end of range 03704 bool h = (static_cast<QWheelEvent*>(e)->orientation() == Qt::Horizontal); 03705 bool d = (static_cast<QWheelEvent*>(e)->delta() < 0); 03706 QScrollBar* hsb = horizontalScrollBar(); 03707 QScrollBar* vsb = verticalScrollBar(); 03708 if ( (h && ((d && hsb->value() == hsb->maximum()) || (!d && hsb->value() == hsb->minimum()))) || 03709 (!h && ((d && vsb->value() == vsb->maximum()) || (!d && vsb->value() == vsb->minimum()))) ) { 03710 e->accept(); 03711 return; 03712 } 03713 } 03714 QScrollArea::wheelEvent( e ); 03715 } 03716 03717 } 03718 #endif 03719 03720 void KHTMLView::dragEnterEvent( QDragEnterEvent* ev ) 03721 { 03722 // Still overridden for BC reasons only... 03723 QScrollArea::dragEnterEvent( ev ); 03724 } 03725 03726 void KHTMLView::dropEvent( QDropEvent *ev ) 03727 { 03728 // Still overridden for BC reasons only... 03729 QScrollArea::dropEvent( ev ); 03730 } 03731 03732 void KHTMLView::focusInEvent( QFocusEvent *e ) 03733 { 03734 DOM::NodeImpl* fn = m_part->xmlDocImpl() ? m_part->xmlDocImpl()->focusNode() : 0; 03735 if (fn && fn->renderer() && fn->renderer()->isWidget() && 03736 (e->reason() != Qt::MouseFocusReason) && 03737 static_cast<khtml::RenderWidget*>(fn->renderer())->widget()) 03738 static_cast<khtml::RenderWidget*>(fn->renderer())->widget()->setFocus(); 03739 m_part->setSelectionVisible(); 03740 QScrollArea::focusInEvent( e ); 03741 } 03742 03743 void KHTMLView::focusOutEvent( QFocusEvent *e ) 03744 { 03745 if (m_part) { 03746 m_part->stopAutoScroll(); 03747 m_part->setSelectionVisible(false); 03748 } 03749 03750 if ( d->cursorIconWidget ) 03751 d->cursorIconWidget->hide(); 03752 03753 QScrollArea::focusOutEvent( e ); 03754 } 03755 03756 void KHTMLView::scrollContentsBy( int dx, int dy ) 03757 { 03758 if (!dx && !dy) return; 03759 03760 if ( !d->firstLayoutPending && !d->complete && m_part->xmlDocImpl() && 03761 d->layoutSchedulingEnabled) { 03762 // contents scroll while we are not complete: we need to check our layout *now* 03763 khtml::RenderCanvas* root = static_cast<khtml::RenderCanvas *>( m_part->xmlDocImpl()->renderer() ); 03764 if (root && root->needsLayout()) { 03765 unscheduleRelayout(); 03766 layout(); 03767 } 03768 } 03769 03770 if ( d->shouldSmoothScroll && d->smoothScrollMode != SSMDisabled && m_part->xmlDocImpl() && 03771 m_part->xmlDocImpl()->renderer() && (d->smoothScrollMode != SSMWhenEfficient || d->smoothScrollMissedDeadlines != sWayTooMany)) { 03772 03773 bool doSmoothScroll = (!d->staticWidget || d->smoothScrollMode == SSMEnabled); 03774 03775 int numStaticPixels = 0; 03776 QRegion r = static_cast<RenderCanvas*>(m_part->xmlDocImpl()->renderer())->staticRegion(); 03777 03778 // only do smooth scrolling if static region is relatively small 03779 if (!doSmoothScroll && d->staticWidget == KHTMLViewPrivate::SBPartial && r.rects().size() <= 10) { 03780 foreach(const QRect &rr, r.rects()) 03781 numStaticPixels += rr.width()*rr.height(); 03782 if ((numStaticPixels < sSmoothScrollMinStaticPixels) || (numStaticPixels*8 < visibleWidth()*visibleHeight())) 03783 doSmoothScroll = true; 03784 } 03785 if (doSmoothScroll) { 03786 setupSmoothScrolling(dx, dy); 03787 return; 03788 } 03789 } 03790 03791 if ( underMouse() && QToolTip::isVisible() ) 03792 QToolTip::hideText(); 03793 03794 if (!d->scrollingSelf) { 03795 d->scrollBarMoved = true; 03796 d->contentsMoving = true; 03797 // ensure quick reset of contentsMoving flag 03798 scheduleRepaint(0, 0, 0, 0); 03799 } 03800 03801 if (m_part->xmlDocImpl() && m_part->xmlDocImpl()->documentElement()) { 03802 m_part->xmlDocImpl()->documentElement()->dispatchHTMLEvent(EventImpl::SCROLL_EVENT, true, false); 03803 } 03804 03805 if (QApplication::isRightToLeft()) 03806 dx = -dx; 03807 03808 if (!d->smoothScrolling) { 03809 d->updateContentsXY(); 03810 } else { 03811 d->contentsX -= dx; 03812 d->contentsY -= dy; 03813 } 03814 if (widget()->pos() != QPoint(0,0)) { 03815 kDebug(6000) << "Static widget wasn't positioned at (0,0). This should NOT happen. Please report this event to developers."; 03816 kDebug(6000) << kBacktrace(); 03817 widget()->move(0,0); 03818 } 03819 03820 QWidget *w = widget(); 03821 QPoint off; 03822 if (m_kwp->isRedirected()) { 03823 // This is a redirected sub frame. Translate to root view context 03824 KHTMLView* v = m_kwp->rootViewPos( off ); 03825 if (v) 03826 w = v->widget(); 03827 off = viewport()->mapTo(this, off); 03828 } 03829 03830 if ( d->staticWidget ) { 03831 03832 // now remove from view the external widgets that must have completely 03833 // disappeared after dx/dy scroll delta is effective 03834 if (!d->visibleWidgets.isEmpty()) 03835 checkExternalWidgetsPosition(); 03836 03837 if ( d->staticWidget == KHTMLViewPrivate::SBPartial 03838 && m_part->xmlDocImpl() && m_part->xmlDocImpl()->renderer() ) { 03839 // static objects might be selectively repainted, like stones in flowing water 03840 QRegion r = static_cast<RenderCanvas*>(m_part->xmlDocImpl()->renderer())->staticRegion(); 03841 r.translate( -contentsX(), -contentsY()); 03842 QVector<QRect> ar = r.rects(); 03843 03844 for (int i = 0; i < ar.size() ; ++i) { 03845 widget()->update( ar[i] ); 03846 } 03847 r = QRegion(QRect(0, 0, visibleWidth(), visibleHeight())) - r; 03848 ar = r.rects(); 03849 for (int i = 0; i < ar.size() ; ++i) { 03850 w->scroll( dx, dy, ar[i].translated(off) ); 03851 } 03852 d->scrollExternalWidgets(dx, dy); 03853 } else { 03854 // we can't avoid a full update 03855 widget()->update(); 03856 } 03857 if (d->accessKeysActivated) 03858 d->scrollAccessKeys(dx, dy); 03859 03860 return; 03861 } 03862 03863 if (m_kwp->isRedirected()) { 03864 const QRect rect(off.x(), off.y(), visibleWidth() * d->zoomLevel / 100, visibleHeight() * d->zoomLevel / 100); 03865 w->scroll(dx, dy, rect); 03866 if (d->zoomLevel != 100) { 03867 w->update(rect); // without this update we are getting bad rendering when an iframe is zoomed in 03868 } 03869 } else { 03870 widget()->scroll(dx, dy, widget()->rect() & viewport()->rect()); 03871 } 03872 03873 d->scrollExternalWidgets(dx, dy); 03874 if (d->accessKeysActivated) 03875 d->scrollAccessKeys(dx, dy); 03876 } 03877 03878 void KHTMLView::setupSmoothScrolling(int dx, int dy) 03879 { 03880 // old or minimum speed 03881 int ddx = qMax(d->steps ? abs(d->dx)/d->steps : 0,3); 03882 int ddy = qMax(d->steps ? abs(d->dy)/d->steps : 0,3); 03883 03884 // full scroll is remaining scroll plus new scroll 03885 d->dx = d->dx + dx; 03886 d->dy = d->dy + dy; 03887 03888 if (d->dx == 0 && d->dy == 0) { 03889 d->stopScrolling(); 03890 return; 03891 } 03892 03893 d->steps = (sSmoothScrollTime-1)/sSmoothScrollTick + 1; 03894 03895 if (qMax(abs(d->dx), abs(d->dy)) / d->steps < qMax(ddx,ddy)) { 03896 // Don't move slower than average 4px/step in minimum one direction 03897 // This means fewer than normal steps 03898 d->steps = qMax((abs(d->dx)+ddx-1)/ddx, (abs(d->dy)+ddy-1)/ddy); 03899 if (d->steps < 1) d->steps = 1; 03900 } 03901 03902 d->smoothScrollStopwatch.start(); 03903 if (!d->smoothScrolling) { 03904 d->startScrolling(); 03905 scrollTick(); 03906 } 03907 } 03908 03909 void KHTMLView::scrollTick() { 03910 if (d->dx == 0 && d->dy == 0) { 03911 d->stopScrolling(); 03912 return; 03913 } 03914 03915 if (d->steps < 1) d->steps = 1; 03916 int takesteps = d->smoothScrollStopwatch.restart() / sSmoothScrollTick; 03917 int scroll_x = 0; 03918 int scroll_y = 0; 03919 if (takesteps < 1) takesteps = 1; 03920 if (takesteps > d->steps) takesteps = d->steps; 03921 for(int i = 0; i < takesteps; i++) { 03922 int ddx = (d->dx / (d->steps+1)) * 2; 03923 int ddy = (d->dy / (d->steps+1)) * 2; 03924 03925 // limit step to requested scrolling distance 03926 if (abs(ddx) > abs(d->dx)) ddx = d->dx; 03927 if (abs(ddy) > abs(d->dy)) ddy = d->dy; 03928 03929 // update remaining scroll 03930 d->dx -= ddx; 03931 d->dy -= ddy; 03932 scroll_x += ddx; 03933 scroll_y += ddy; 03934 d->steps--; 03935 } 03936 03937 d->shouldSmoothScroll = false; 03938 scrollContentsBy(scroll_x, scroll_y); 03939 03940 if (takesteps < 2) { 03941 d->smoothScrollMissedDeadlines = 0; 03942 } else { 03943 if (d->smoothScrollMissedDeadlines != sWayTooMany && 03944 (!m_part->xmlDocImpl() || !m_part->xmlDocImpl()->parsing())) { 03945 d->smoothScrollMissedDeadlines++; 03946 if (d->smoothScrollMissedDeadlines >= sMaxMissedDeadlines) { 03947 // we missed many deadlines in a row! 03948 // time to signal we had enough.. 03949 d->smoothScrollMissedDeadlines = sWayTooMany; 03950 } 03951 } 03952 } 03953 } 03954 03955 03956 void KHTMLView::addChild(QWidget * child, int x, int y) 03957 { 03958 if (!child) 03959 return; 03960 03961 if (child->parent() != widget()) 03962 child->setParent( widget() ); 03963 03964 // ### handle pseudo-zooming of non-redirected widgets (e.g. just resize'em) 03965 03966 child->move(x-contentsX(), y-contentsY()); 03967 } 03968 03969 void KHTMLView::timerEvent ( QTimerEvent *e ) 03970 { 03971 // kDebug() << "timer event " << e->timerId(); 03972 if ( e->timerId() == d->scrollTimerId ) { 03973 if( d->scrollSuspended ) 03974 return; 03975 switch (d->scrollDirection) { 03976 case KHTMLViewPrivate::ScrollDown: 03977 if (contentsY() + visibleHeight () >= contentsHeight()) 03978 d->newScrollTimer(this, 0); 03979 else 03980 verticalScrollBar()->setValue( verticalScrollBar()->value() +d->scrollBy ); 03981 break; 03982 case KHTMLViewPrivate::ScrollUp: 03983 if (contentsY() <= 0) 03984 d->newScrollTimer(this, 0); 03985 else 03986 verticalScrollBar()->setValue( verticalScrollBar()->value() -d->scrollBy ); 03987 break; 03988 case KHTMLViewPrivate::ScrollRight: 03989 if (contentsX() + visibleWidth () >= contentsWidth()) 03990 d->newScrollTimer(this, 0); 03991 else 03992 horizontalScrollBar()->setValue( horizontalScrollBar()->value() +d->scrollBy ); 03993 break; 03994 case KHTMLViewPrivate::ScrollLeft: 03995 if (contentsX() <= 0) 03996 d->newScrollTimer(this, 0); 03997 else 03998 horizontalScrollBar()->setValue( horizontalScrollBar()->value() -d->scrollBy ); 03999 break; 04000 } 04001 return; 04002 } 04003 else if ( e->timerId() == d->scrollingFromWheelTimerId ) { 04004 killTimer( d->scrollingFromWheelTimerId ); 04005 d->scrollingFromWheelTimerId = 0; 04006 } else if ( e->timerId() == d->layoutTimerId ) { 04007 if (d->firstLayoutPending && d->layoutAttemptCounter < 4 04008 && (!m_part->xmlDocImpl() || !m_part->xmlDocImpl()->readyForLayout())) { 04009 d->layoutAttemptCounter++; 04010 killTimer(d->layoutTimerId); 04011 d->layoutTimerId = 0; 04012 scheduleRelayout(); 04013 return; 04014 } 04015 layout(); 04016 d->scheduledLayoutCounter++; 04017 if (d->firstLayoutPending) { 04018 d->firstLayoutPending = false; 04019 verticalScrollBar()->setEnabled( true ); 04020 horizontalScrollBar()->setEnabled( true ); 04021 } 04022 } 04023 04024 d->contentsMoving = false; 04025 if( m_part->xmlDocImpl() ) { 04026 DOM::DocumentImpl *document = m_part->xmlDocImpl(); 04027 khtml::RenderCanvas* root = static_cast<khtml::RenderCanvas *>(document->renderer()); 04028 04029 if ( root && root->needsLayout() ) { 04030 if (d->repaintTimerId) 04031 killTimer(d->repaintTimerId); 04032 d->repaintTimerId = 0; 04033 scheduleRelayout(); 04034 return; 04035 } 04036 } 04037 04038 if (d->repaintTimerId) 04039 killTimer(d->repaintTimerId); 04040 d->repaintTimerId = 0; 04041 04042 QRect updateRegion; 04043 const QVector<QRect> rects = d->updateRegion.rects(); 04044 04045 d->updateRegion = QRegion(); 04046 04047 if ( rects.size() ) 04048 updateRegion = rects[0]; 04049 04050 for ( int i = 1; i < rects.size(); ++i ) { 04051 QRect newRegion = updateRegion.unite(rects[i]); 04052 if (2*newRegion.height() > 3*updateRegion.height() ) 04053 { 04054 repaintContents( updateRegion ); 04055 updateRegion = rects[i]; 04056 } 04057 else 04058 updateRegion = newRegion; 04059 } 04060 04061 if ( !updateRegion.isNull() ) 04062 repaintContents( updateRegion ); 04063 04064 // As widgets can only be accurately positioned during painting, every layout might 04065 // dissociate a widget from its RenderWidget. E.g: if a RenderWidget was visible before layout, but the layout 04066 // pushed it out of the viewport, it will not be repainted, and consequently it's associated widget won't be repositioned. 04067 // 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. 04068 04069 if (d->dirtyLayout && !d->visibleWidgets.isEmpty()) 04070 checkExternalWidgetsPosition(); 04071 04072 d->dirtyLayout = false; 04073 04074 emit repaintAccessKeys(); 04075 if (d->emitCompletedAfterRepaint) { 04076 bool full = d->emitCompletedAfterRepaint == KHTMLViewPrivate::CSFull; 04077 d->emitCompletedAfterRepaint = KHTMLViewPrivate::CSNone; 04078 if ( full ) 04079 emit m_part->completed(); 04080 else 04081 emit m_part->completed(true); 04082 } 04083 } 04084 04085 void KHTMLView::checkExternalWidgetsPosition() 04086 { 04087 QWidget* w; 04088 QRect visibleRect(contentsX(), contentsY(), visibleWidth(), visibleHeight()); 04089 QList<RenderWidget*> toRemove; 04090 QHashIterator<void*, QWidget*> it(d->visibleWidgets); 04091 while (it.hasNext()) { 04092 int xp = 0, yp = 0; 04093 it.next(); 04094 RenderWidget* rw = static_cast<RenderWidget*>( it.key() ); 04095 if (!rw->absolutePosition(xp, yp) || 04096 !visibleRect.intersects(QRect(xp, yp, it.value()->width(), it.value()->height()))) 04097 toRemove.append(rw); 04098 } 04099 foreach (RenderWidget* r, toRemove) 04100 if ( (w = d->visibleWidgets.take(r) ) ) 04101 w->move( 0, -500000); 04102 } 04103 04104 void KHTMLView::scheduleRelayout(khtml::RenderObject * /*clippedObj*/) 04105 { 04106 if (!d->layoutSchedulingEnabled || d->layoutTimerId) 04107 return; 04108 04109 int time = 0; 04110 if (d->firstLayoutPending) { 04111 // Any repaint happening while we have no content blanks the viewport ("white flash"). 04112 // Hence the need to delay the first layout as much as we can. 04113 // Only if the document gets stuck for too long in incomplete state will we allow the blanking. 04114 time = d->layoutAttemptCounter ? 04115 sLayoutAttemptDelay + sLayoutAttemptIncrement*d->layoutAttemptCounter : sFirstLayoutDelay; 04116 } else if (m_part->xmlDocImpl() && m_part->xmlDocImpl()->parsing()) { 04117 // Delay between successive layouts in parsing mode. 04118 // Increment reflects the decaying importance of visual feedback over time. 04119 time = qMin(2000, sParsingLayoutsInterval + d->scheduledLayoutCounter*sParsingLayoutsIncrement); 04120 } 04121 d->layoutTimerId = startTimer( time ); 04122 } 04123 04124 void KHTMLView::unscheduleRelayout() 04125 { 04126 if (!d->layoutTimerId) 04127 return; 04128 04129 killTimer(d->layoutTimerId); 04130 d->layoutTimerId = 0; 04131 } 04132 04133 void KHTMLView::unscheduleRepaint() 04134 { 04135 if (!d->repaintTimerId) 04136 return; 04137 04138 killTimer(d->repaintTimerId); 04139 d->repaintTimerId = 0; 04140 } 04141 04142 void KHTMLView::scheduleRepaint(int x, int y, int w, int h, bool asap) 04143 { 04144 bool parsing = !m_part->xmlDocImpl() || m_part->xmlDocImpl()->parsing(); 04145 04146 // kDebug() << "parsing " << parsing; 04147 // kDebug() << "complete " << d->complete; 04148 04149 int time = parsing && !d->firstLayoutPending ? 150 : (!asap ? ( !d->complete ? 80 : 20 ) : 0); 04150 04151 #ifdef DEBUG_FLICKER 04152 QPainter p; 04153 p.begin( viewport() ); 04154 04155 int vx, vy; 04156 contentsToViewport( x, y, vx, vy ); 04157 p.fillRect( vx, vy, w, h, Qt::red ); 04158 p.end(); 04159 #endif 04160 04161 d->updateRegion = d->updateRegion.unite(QRect(x,y,w,h)); 04162 04163 if (asap && !parsing) 04164 unscheduleRepaint(); 04165 04166 if ( !d->repaintTimerId ) 04167 d->repaintTimerId = startTimer( time ); 04168 04169 // kDebug() << "starting timer " << time; 04170 } 04171 04172 void KHTMLView::complete( bool pendingAction ) 04173 { 04174 // kDebug() << "KHTMLView::complete()"; 04175 04176 d->complete = true; 04177 04178 // is there a relayout pending? 04179 if (d->layoutTimerId) 04180 { 04181 // kDebug() << "requesting relayout now"; 04182 // do it now 04183 killTimer(d->layoutTimerId); 04184 d->layoutTimerId = startTimer( 0 ); 04185 d->emitCompletedAfterRepaint = pendingAction ? 04186 KHTMLViewPrivate::CSActionPending : KHTMLViewPrivate::CSFull; 04187 } 04188 04189 // is there a repaint pending? 04190 if (d->repaintTimerId) 04191 { 04192 // kDebug() << "requesting repaint now"; 04193 // do it now 04194 killTimer(d->repaintTimerId); 04195 d->repaintTimerId = startTimer( 0 ); 04196 d->emitCompletedAfterRepaint = pendingAction ? 04197 KHTMLViewPrivate::CSActionPending : KHTMLViewPrivate::CSFull; 04198 } 04199 04200 if (!d->emitCompletedAfterRepaint) 04201 { 04202 if (!pendingAction) 04203 emit m_part->completed(); 04204 else 04205 emit m_part->completed(true); 04206 } 04207 04208 } 04209 04210 void KHTMLView::updateScrollBars() 04211 { 04212 const QWidget *view = widget(); 04213 if (!view) 04214 return; 04215 04216 QSize p = viewport()->size(); 04217 QSize m = maximumViewportSize(); 04218 04219 if (m.expandedTo(view->size()) == m) 04220 p = m; // no scroll bars needed 04221 04222 QSize v = view->size(); 04223 horizontalScrollBar()->setRange(0, v.width() - p.width()); 04224 horizontalScrollBar()->setPageStep(p.width()); 04225 verticalScrollBar()->setRange(0, v.height() - p.height()); 04226 verticalScrollBar()->setPageStep(p.height()); 04227 if (!d->smoothScrolling) { 04228 d->updateContentsXY(); 04229 } 04230 } 04231 04232 void KHTMLView::slotMouseScrollTimer() 04233 { 04234 horizontalScrollBar()->setValue( horizontalScrollBar()->value() +d->m_mouseScroll_byX ); 04235 verticalScrollBar()->setValue( verticalScrollBar()->value() +d->m_mouseScroll_byY); 04236 } 04237 04238 04239 static DOM::Position positionOfLineBoundary(const DOM::Position &pos, bool toEnd) 04240 { 04241 Selection sel = pos; 04242 sel.expandUsingGranularity(Selection::LINE); 04243 return toEnd ? sel.end() : sel.start(); 04244 } 04245 04246 inline static DOM::Position positionOfLineBegin(const DOM::Position &pos) 04247 { 04248 return positionOfLineBoundary(pos, false); 04249 } 04250 04251 inline static DOM::Position positionOfLineEnd(const DOM::Position &pos) 04252 { 04253 return positionOfLineBoundary(pos, true); 04254 } 04255 04256 bool KHTMLView::caretKeyPressEvent(QKeyEvent *_ke) 04257 { 04258 EditorContext *ec = &m_part->d->editor_context; 04259 Selection &caret = ec->m_selection; 04260 Position old_pos = caret.caretPos(); 04261 Position pos = old_pos; 04262 bool recalcXPos = true; 04263 bool handled = true; 04264 04265 bool ctrl = _ke->modifiers() & Qt::ControlModifier; 04266 bool shift = _ke->modifiers() & Qt::ShiftModifier; 04267 04268 switch(_ke->key()) { 04269 04270 // -- Navigational keys 04271 case Qt::Key_Down: 04272 pos = old_pos.nextLinePosition(caret.xPosForVerticalArrowNavigation(Selection::EXTENT)); 04273 recalcXPos = false; 04274 break; 04275 04276 case Qt::Key_Up: 04277 pos = old_pos.previousLinePosition(caret.xPosForVerticalArrowNavigation(Selection::EXTENT)); 04278 recalcXPos = false; 04279 break; 04280 04281 case Qt::Key_Left: 04282 pos = ctrl ? old_pos.previousWordPosition() : old_pos.previousCharacterPosition(); 04283 break; 04284 04285 case Qt::Key_Right: 04286 pos = ctrl ? old_pos.nextWordPosition() : old_pos.nextCharacterPosition(); 04287 break; 04288 04289 case Qt::Key_PageDown: 04290 // moveCaretNextPage(); ### 04291 break; 04292 04293 case Qt::Key_PageUp: 04294 // moveCaretPrevPage(); ### 04295 break; 04296 04297 case Qt::Key_Home: 04298 if (ctrl) 04299 /*moveCaretToDocumentBoundary(false)*/; // ### 04300 else 04301 pos = positionOfLineBegin(old_pos); 04302 break; 04303 04304 case Qt::Key_End: 04305 if (ctrl) 04306 /*moveCaretToDocumentBoundary(true)*/; // ### 04307 else 04308 pos = positionOfLineEnd(old_pos); 04309 break; 04310 04311 default: 04312 handled = false; 04313 04314 }/*end switch*/ 04315 04316 if (pos != old_pos) { 04317 m_part->clearCaretRectIfNeeded(); 04318 04319 caret.moveTo(shift ? caret.nonCaretPos() : pos, pos); 04320 int old_x = caret.xPosForVerticalArrowNavigation(Selection::CARETPOS); 04321 04322 m_part->selectionLayoutChanged(); 04323 04324 // restore old x-position to prevent recalculation 04325 if (!recalcXPos) 04326 m_part->d->editor_context.m_xPosForVerticalArrowNavigation = old_x; 04327 04328 m_part->emitCaretPositionChanged(pos); 04329 // ### check when to emit it 04330 m_part->notifySelectionChanged(); 04331 04332 } 04333 04334 if (handled) _ke->accept(); 04335 return handled; 04336 } 04337 04338 #undef DEBUG_CARETMODE
KDE 4.6 API Reference