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