khtmlview.cpp
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-2004 Apple Computer, Inc. 00009 * 00010 * This library is free software; you can redistribute it and/or 00011 * modify it under the terms of the GNU Library General Public 00012 * License as published by the Free Software Foundation; either 00013 * version 2 of the License, or (at your option) any later version. 00014 * 00015 * This library is distributed in the hope that it will be useful, 00016 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00017 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00018 * Library General Public License for more details. 00019 * 00020 * You should have received a copy of the GNU Library General Public License 00021 * along with this library; see the file COPYING.LIB. If not, write to 00022 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 00023 * Boston, MA 02110-1301, USA. 00024 */ 00025 00026 00027 #include "khtmlview.moc" 00028 00029 #include "khtmlview.h" 00030 00031 #include "khtml_part.h" 00032 #include "khtml_events.h" 00033 00034 #include "html/html_documentimpl.h" 00035 #include "html/html_inlineimpl.h" 00036 #include "html/html_formimpl.h" 00037 #include "rendering/render_arena.h" 00038 #include "rendering/render_canvas.h" 00039 #include "rendering/render_frames.h" 00040 #include "rendering/render_replaced.h" 00041 #include "rendering/render_layer.h" 00042 #include "rendering/render_line.h" 00043 #include "rendering/render_table.h" 00044 // removeme 00045 #define protected public 00046 #include "rendering/render_text.h" 00047 #undef protected 00048 #include "xml/dom2_eventsimpl.h" 00049 #include "css/cssstyleselector.h" 00050 #include "css/csshelper.h" 00051 #include "misc/htmlhashes.h" 00052 #include "misc/helper.h" 00053 #include "misc/loader.h" 00054 #include "khtml_settings.h" 00055 #include "khtml_printsettings.h" 00056 00057 #include "khtmlpart_p.h" 00058 00059 #ifndef KHTML_NO_CARET 00060 #include "khtml_caret_p.h" 00061 #include "xml/dom2_rangeimpl.h" 00062 #endif 00063 00064 #include <kapplication.h> 00065 #include <kcursor.h> 00066 #include <kdebug.h> 00067 #include <kdialogbase.h> 00068 #include <kiconloader.h> 00069 #include <kimageio.h> 00070 #include <klocale.h> 00071 #include <knotifyclient.h> 00072 #include <kprinter.h> 00073 #include <ksimpleconfig.h> 00074 #include <kstandarddirs.h> 00075 #include <kstdaccel.h> 00076 #include <kstringhandler.h> 00077 #include <kurldrag.h> 00078 00079 #include <qbitmap.h> 00080 #include <qlabel.h> 00081 #include <qobjectlist.h> 00082 #include <qpaintdevicemetrics.h> 00083 #include <qpainter.h> 00084 #include <qptrdict.h> 00085 #include <qtooltip.h> 00086 #include <qstring.h> 00087 #include <qstylesheet.h> 00088 #include <qtimer.h> 00089 #include <qvaluevector.h> 00090 00091 //#define DEBUG_NO_PAINT_BUFFER 00092 00093 //#define DEBUG_FLICKER 00094 00095 //#define DEBUG_PIXEL 00096 00097 #ifdef Q_WS_X11 00098 #include <X11/Xlib.h> 00099 #include <fixx11h.h> 00100 #endif 00101 00102 #define PAINT_BUFFER_HEIGHT 128 00103 00104 #if 0 00105 namespace khtml { 00106 void dumpLineBoxes(RenderFlow *flow); 00107 } 00108 #endif 00109 00110 using namespace DOM; 00111 using namespace khtml; 00112 class KHTMLToolTip; 00113 00114 00115 #ifndef QT_NO_TOOLTIP 00116 00117 class KHTMLToolTip : public QToolTip 00118 { 00119 public: 00120 KHTMLToolTip(KHTMLView *view, KHTMLViewPrivate* vp) : QToolTip(view->viewport()) 00121 { 00122 m_view = view; 00123 m_viewprivate = vp; 00124 }; 00125 00126 protected: 00127 virtual void maybeTip(const QPoint &); 00128 00129 private: 00130 KHTMLView *m_view; 00131 KHTMLViewPrivate* m_viewprivate; 00132 }; 00133 00134 #endif 00135 00136 class KHTMLViewPrivate { 00137 friend class KHTMLToolTip; 00138 public: 00139 00140 enum PseudoFocusNodes { 00141 PFNone, 00142 PFTop, 00143 PFBottom 00144 }; 00145 00146 enum CompletedState { 00147 CSNone = 0, 00148 CSFull, 00149 CSActionPending 00150 }; 00151 00152 KHTMLViewPrivate() 00153 : underMouse( 0 ), underMouseNonShared( 0 ), visibleWidgets( 107 ) 00154 #ifndef NO_SMOOTH_SCROLL_HACK 00155 , dx(0), dy(0), ddx(0), ddy(0), rdx(0), rdy(0), scrolling(false) 00156 #endif 00157 { 00158 #ifndef KHTML_NO_CARET 00159 m_caretViewContext = 0; 00160 m_editorContext = 0; 00161 #endif // KHTML_NO_CARET 00162 postponed_autorepeat = NULL; 00163 reset(); 00164 vmode = QScrollView::Auto; 00165 hmode = QScrollView::Auto; 00166 tp=0; 00167 paintBuffer=0; 00168 vertPaintBuffer=0; 00169 formCompletions=0; 00170 prevScrollbarVisible = true; 00171 tooltip = 0; 00172 possibleTripleClick = false; 00173 emitCompletedAfterRepaint = CSNone; 00174 cursor_icon_widget = NULL; 00175 m_mouseScrollTimer = 0; 00176 m_mouseScrollIndicator = 0; 00177 } 00178 ~KHTMLViewPrivate() 00179 { 00180 delete formCompletions; 00181 delete tp; tp = 0; 00182 delete paintBuffer; paintBuffer =0; 00183 delete vertPaintBuffer; 00184 delete postponed_autorepeat; 00185 if (underMouse) 00186 underMouse->deref(); 00187 if (underMouseNonShared) 00188 underMouseNonShared->deref(); 00189 delete tooltip; 00190 #ifndef KHTML_NO_CARET 00191 delete m_caretViewContext; 00192 delete m_editorContext; 00193 #endif // KHTML_NO_CARET 00194 delete cursor_icon_widget; 00195 delete m_mouseScrollTimer; 00196 delete m_mouseScrollIndicator; 00197 } 00198 void reset() 00199 { 00200 if (underMouse) 00201 underMouse->deref(); 00202 underMouse = 0; 00203 if (underMouseNonShared) 00204 underMouseNonShared->deref(); 00205 underMouseNonShared = 0; 00206 linkPressed = false; 00207 useSlowRepaints = false; 00208 tabMovePending = false; 00209 lastTabbingDirection = true; 00210 pseudoFocusNode = PFNone; 00211 #ifndef KHTML_NO_SCROLLBARS 00212 //We don't turn off the toolbars here 00213 //since if the user turns them 00214 //off, then chances are they want them turned 00215 //off always - even after a reset. 00216 #else 00217 vmode = QScrollView::AlwaysOff; 00218 hmode = QScrollView::AlwaysOff; 00219 #endif 00220 #ifdef DEBUG_PIXEL 00221 timer.start(); 00222 pixelbooth = 0; 00223 repaintbooth = 0; 00224 #endif 00225 scrollBarMoved = false; 00226 contentsMoving = false; 00227 ignoreWheelEvents = false; 00228 borderX = 30; 00229 borderY = 30; 00230 paged = false; 00231 clickX = -1; 00232 clickY = -1; 00233 prevMouseX = -1; 00234 prevMouseY = -1; 00235 clickCount = 0; 00236 isDoubleClick = false; 00237 scrollingSelf = false; 00238 delete postponed_autorepeat; 00239 postponed_autorepeat = NULL; 00240 layoutTimerId = 0; 00241 repaintTimerId = 0; 00242 scrollTimerId = 0; 00243 scrollSuspended = false; 00244 scrollSuspendPreActivate = false; 00245 complete = false; 00246 firstRelayout = true; 00247 needsFullRepaint = true; 00248 dirtyLayout = false; 00249 layoutSchedulingEnabled = true; 00250 painting = false; 00251 updateRegion = QRegion(); 00252 m_dialogsAllowed = true; 00253 #ifndef KHTML_NO_CARET 00254 if (m_caretViewContext) { 00255 m_caretViewContext->caretMoved = false; 00256 m_caretViewContext->keyReleasePending = false; 00257 }/*end if*/ 00258 #endif // KHTML_NO_CARET 00259 #ifndef KHTML_NO_TYPE_AHEAD_FIND 00260 typeAheadActivated = false; 00261 #endif // KHTML_NO_TYPE_AHEAD_FIND 00262 accessKeysActivated = false; 00263 accessKeysPreActivate = false; 00264 00265 // We ref/deref to ensure defaultHTMLSettings is available 00266 KHTMLFactory::ref(); 00267 accessKeysEnabled = KHTMLFactory::defaultHTMLSettings()->accessKeysEnabled(); 00268 KHTMLFactory::deref(); 00269 00270 emitCompletedAfterRepaint = CSNone; 00271 } 00272 void newScrollTimer(QWidget *view, int tid) 00273 { 00274 //kdDebug(6000) << "newScrollTimer timer " << tid << endl; 00275 view->killTimer(scrollTimerId); 00276 scrollTimerId = tid; 00277 scrollSuspended = false; 00278 } 00279 enum ScrollDirection { ScrollLeft, ScrollRight, ScrollUp, ScrollDown }; 00280 00281 void adjustScroller(QWidget *view, ScrollDirection direction, ScrollDirection oppositedir) 00282 { 00283 static const struct { int msec, pixels; } timings [] = { 00284 {320,1}, {224,1}, {160,1}, {112,1}, {80,1}, {56,1}, {40,1}, 00285 {28,1}, {20,1}, {20,2}, {20,3}, {20,4}, {20,6}, {20,8}, {0,0} 00286 }; 00287 if (!scrollTimerId || 00288 (static_cast<int>(scrollDirection) != direction && 00289 (static_cast<int>(scrollDirection) != oppositedir || scrollSuspended))) { 00290 scrollTiming = 6; 00291 scrollBy = timings[scrollTiming].pixels; 00292 scrollDirection = direction; 00293 newScrollTimer(view, view->startTimer(timings[scrollTiming].msec)); 00294 } else if (scrollDirection == direction && 00295 timings[scrollTiming+1].msec && !scrollSuspended) { 00296 scrollBy = timings[++scrollTiming].pixels; 00297 newScrollTimer(view, view->startTimer(timings[scrollTiming].msec)); 00298 } else if (scrollDirection == oppositedir) { 00299 if (scrollTiming) { 00300 scrollBy = timings[--scrollTiming].pixels; 00301 newScrollTimer(view, view->startTimer(timings[scrollTiming].msec)); 00302 } 00303 } 00304 scrollSuspended = false; 00305 } 00306 00307 #ifndef KHTML_NO_CARET 00308 00311 CaretViewContext *caretViewContext() { 00312 if (!m_caretViewContext) m_caretViewContext = new CaretViewContext(); 00313 return m_caretViewContext; 00314 } 00318 EditorContext *editorContext() { 00319 if (!m_editorContext) m_editorContext = new EditorContext(); 00320 return m_editorContext; 00321 } 00322 #endif // KHTML_NO_CARET 00323 00324 #ifdef DEBUG_PIXEL 00325 QTime timer; 00326 unsigned int pixelbooth; 00327 unsigned int repaintbooth; 00328 #endif 00329 00330 QPainter *tp; 00331 QPixmap *paintBuffer; 00332 QPixmap *vertPaintBuffer; 00333 NodeImpl *underMouse; 00334 NodeImpl *underMouseNonShared; 00335 00336 bool tabMovePending:1; 00337 bool lastTabbingDirection:1; 00338 PseudoFocusNodes pseudoFocusNode:2; 00339 bool scrollBarMoved:1; 00340 bool contentsMoving:1; 00341 00342 QScrollView::ScrollBarMode vmode; 00343 QScrollView::ScrollBarMode hmode; 00344 bool prevScrollbarVisible:1; 00345 bool linkPressed:1; 00346 bool useSlowRepaints:1; 00347 bool ignoreWheelEvents:1; 00348 00349 int borderX, borderY; 00350 KSimpleConfig *formCompletions; 00351 00352 bool paged; 00353 00354 int clickX, clickY, clickCount; 00355 bool isDoubleClick; 00356 00357 int prevMouseX, prevMouseY; 00358 bool scrollingSelf; 00359 int layoutTimerId; 00360 QKeyEvent* postponed_autorepeat; 00361 00362 int repaintTimerId; 00363 int scrollTimerId; 00364 int scrollTiming; 00365 int scrollBy; 00366 ScrollDirection scrollDirection :2; 00367 bool scrollSuspended :1; 00368 bool scrollSuspendPreActivate :1; 00369 bool complete :1; 00370 bool firstRelayout :1; 00371 bool layoutSchedulingEnabled :1; 00372 bool needsFullRepaint :1; 00373 bool painting :1; 00374 bool possibleTripleClick :1; 00375 bool dirtyLayout :1; 00376 bool m_dialogsAllowed :1; 00377 QRegion updateRegion; 00378 KHTMLToolTip *tooltip; 00379 QPtrDict<QWidget> visibleWidgets; 00380 #ifndef KHTML_NO_CARET 00381 CaretViewContext *m_caretViewContext; 00382 EditorContext *m_editorContext; 00383 #endif // KHTML_NO_CARET 00384 #ifndef KHTML_NO_TYPE_AHEAD_FIND 00385 QString findString; 00386 QTimer timer; 00387 bool findLinksOnly; 00388 bool typeAheadActivated; 00389 #endif // KHTML_NO_TYPE_AHEAD_FIND 00390 bool accessKeysEnabled; 00391 bool accessKeysActivated; 00392 bool accessKeysPreActivate; 00393 CompletedState emitCompletedAfterRepaint; 00394 00395 QWidget* cursor_icon_widget; 00396 00397 // scrolling activated by MMB 00398 short m_mouseScroll_byX; 00399 short m_mouseScroll_byY; 00400 QTimer *m_mouseScrollTimer; 00401 QWidget *m_mouseScrollIndicator; 00402 #ifndef NO_SMOOTH_SCROLL_HACK 00403 QTimer timer2; 00404 int dx; 00405 int dy; 00406 // Step size * 16 and residual to avoid huge difference between 1px/step and 2px/step 00407 int ddx; 00408 int ddy; 00409 int rdx; 00410 int rdy; 00411 bool scrolling; 00412 #endif 00413 }; 00414 00415 #ifndef QT_NO_TOOLTIP 00416 00426 static bool findImageMapRect(HTMLImageElementImpl *img, const QPoint &scrollOfs, 00427 const QPoint &p, QRect &r, QString &s) 00428 { 00429 HTMLMapElementImpl* map; 00430 if (img && img->getDocument()->isHTMLDocument() && 00431 (map = static_cast<HTMLDocumentImpl*>(img->getDocument())->getMap(img->imageMap()))) { 00432 RenderObject::NodeInfo info(true, false); 00433 RenderObject *rend = img->renderer(); 00434 int ax, ay; 00435 if (!rend || !rend->absolutePosition(ax, ay)) 00436 return false; 00437 // we're a client side image map 00438 bool inside = map->mapMouseEvent(p.x() - ax + scrollOfs.x(), 00439 p.y() - ay + scrollOfs.y(), rend->contentWidth(), 00440 rend->contentHeight(), info); 00441 if (inside && info.URLElement()) { 00442 HTMLAreaElementImpl *area = static_cast<HTMLAreaElementImpl *>(info.URLElement()); 00443 Q_ASSERT(area->id() == ID_AREA); 00444 s = area->getAttribute(ATTR_TITLE).string(); 00445 QRegion reg = area->cachedRegion(); 00446 if (!s.isEmpty() && !reg.isEmpty()) { 00447 r = reg.boundingRect(); 00448 r.moveBy(ax, ay); 00449 return true; 00450 } 00451 } 00452 } 00453 return false; 00454 } 00455 00456 void KHTMLToolTip::maybeTip(const QPoint& p) 00457 { 00458 DOM::NodeImpl *node = m_viewprivate->underMouseNonShared; 00459 QRect region; 00460 while ( node ) { 00461 if ( node->isElementNode() ) { 00462 DOM::ElementImpl *e = static_cast<DOM::ElementImpl*>( node ); 00463 QRect r; 00464 QString s; 00465 bool found = false; 00466 // for images, check if it is part of a client-side image map, 00467 // and query the <area>s' title attributes, too 00468 if (e->id() == ID_IMG && !e->getAttribute( ATTR_USEMAP ).isEmpty()) { 00469 found = findImageMapRect(static_cast<HTMLImageElementImpl *>(e), 00470 m_view->viewportToContents(QPoint(0, 0)), p, r, s); 00471 } 00472 if (!found) { 00473 s = e->getAttribute( ATTR_TITLE ).string(); 00474 r = node->getRect(); 00475 } 00476 region |= QRect( m_view->contentsToViewport( r.topLeft() ), r.size() ); 00477 if ( !s.isEmpty() ) { 00478 tip( region, QStyleSheet::convertFromPlainText( s, QStyleSheetItem::WhiteSpaceNormal ) ); 00479 break; 00480 } 00481 } 00482 node = node->parentNode(); 00483 } 00484 } 00485 #endif 00486 00487 KHTMLView::KHTMLView( KHTMLPart *part, QWidget *parent, const char *name) 00488 : QScrollView( parent, name, WResizeNoErase | WRepaintNoErase ) 00489 { 00490 m_medium = "screen"; 00491 00492 m_part = part; 00493 d = new KHTMLViewPrivate; 00494 QScrollView::setVScrollBarMode(d->vmode); 00495 QScrollView::setHScrollBarMode(d->hmode); 00496 connect(kapp, SIGNAL(kdisplayPaletteChanged()), this, SLOT(slotPaletteChanged())); 00497 connect(this, SIGNAL(contentsMoving(int, int)), this, SLOT(slotScrollBarMoved())); 00498 00499 // initialize QScrollView 00500 enableClipper(true); 00501 // hack to get unclipped painting on the viewport. 00502 static_cast<KHTMLView *>(static_cast<QWidget *>(viewport()))->setWFlags(WPaintUnclipped); 00503 00504 setResizePolicy(Manual); 00505 viewport()->setMouseTracking(true); 00506 viewport()->setBackgroundMode(NoBackground); 00507 00508 KImageIO::registerFormats(); 00509 00510 #ifndef QT_NO_TOOLTIP 00511 d->tooltip = new KHTMLToolTip( this, d ); 00512 #endif 00513 00514 #ifndef KHTML_NO_TYPE_AHEAD_FIND 00515 connect(&d->timer, SIGNAL(timeout()), this, SLOT(findTimeout())); 00516 #endif // KHTML_NO_TYPE_AHEAD_FIND 00517 00518 init(); 00519 00520 viewport()->show(); 00521 #ifndef NO_SMOOTH_SCROLL_HACK 00522 #define timer timer2 00523 connect(&d->timer, SIGNAL(timeout()), this, SLOT(scrollTick())); 00524 #undef timer 00525 #endif 00526 } 00527 00528 KHTMLView::~KHTMLView() 00529 { 00530 closeChildDialogs(); 00531 if (m_part) 00532 { 00533 //WABA: Is this Ok? Do I need to deref it as well? 00534 //Does this need to be done somewhere else? 00535 DOM::DocumentImpl *doc = m_part->xmlDocImpl(); 00536 if (doc) 00537 doc->detach(); 00538 } 00539 delete d; d = 0; 00540 } 00541 00542 void KHTMLView::init() 00543 { 00544 if(!d->paintBuffer) d->paintBuffer = new QPixmap(PAINT_BUFFER_HEIGHT, PAINT_BUFFER_HEIGHT); 00545 if(!d->vertPaintBuffer) 00546 d->vertPaintBuffer = new QPixmap(10, PAINT_BUFFER_HEIGHT); 00547 if(!d->tp) d->tp = new QPainter(); 00548 00549 setFocusPolicy(QWidget::StrongFocus); 00550 viewport()->setFocusProxy(this); 00551 00552 _marginWidth = -1; // undefined 00553 _marginHeight = -1; 00554 _width = 0; 00555 _height = 0; 00556 00557 installEventFilter(this); 00558 00559 setAcceptDrops(true); 00560 QSize s = viewportSize(4095, 4095); 00561 resizeContents(s.width(), s.height()); 00562 } 00563 00564 void KHTMLView::clear() 00565 { 00566 // work around QScrollview's unbelievable bugginess 00567 setStaticBackground(true); 00568 #ifndef KHTML_NO_CARET 00569 if (!m_part->isCaretMode() && !m_part->isEditable()) caretOff(); 00570 #endif 00571 00572 #ifndef KHTML_NO_TYPE_AHEAD_FIND 00573 if( d->typeAheadActivated ) 00574 findTimeout(); 00575 #endif 00576 if (d->accessKeysEnabled && d->accessKeysActivated) 00577 accessKeysTimeout(); 00578 viewport()->unsetCursor(); 00579 if ( d->cursor_icon_widget ) 00580 d->cursor_icon_widget->hide(); 00581 d->reset(); 00582 killTimers(); 00583 emit cleared(); 00584 00585 QScrollView::setHScrollBarMode(d->hmode); 00586 QScrollView::setVScrollBarMode(d->vmode); 00587 verticalScrollBar()->setEnabled( false ); 00588 horizontalScrollBar()->setEnabled( false ); 00589 } 00590 00591 void KHTMLView::hideEvent(QHideEvent* e) 00592 { 00593 QScrollView::hideEvent(e); 00594 if ( m_part && m_part->xmlDocImpl() ) 00595 m_part->xmlDocImpl()->docLoader()->pauseAnimations(); 00596 } 00597 00598 void KHTMLView::showEvent(QShowEvent* e) 00599 { 00600 QScrollView::showEvent(e); 00601 if ( m_part && m_part->xmlDocImpl() ) 00602 m_part->xmlDocImpl()->docLoader()->resumeAnimations(); 00603 } 00604 00605 void KHTMLView::resizeEvent (QResizeEvent* e) 00606 { 00607 int dw = e->oldSize().width() - e->size().width(); 00608 int dh = e->oldSize().height() - e->size().height(); 00609 00610 // if we are shrinking the view, don't allow the content to overflow 00611 // before the layout occurs - we don't know if we need scrollbars yet 00612 dw = dw>0 ? kMax(0, contentsWidth()-dw) : contentsWidth(); 00613 dh = dh>0 ? kMax(0, contentsHeight()-dh) : contentsHeight(); 00614 00615 resizeContents(dw, dh); 00616 00617 QScrollView::resizeEvent(e); 00618 00619 if ( m_part && m_part->xmlDocImpl() ) 00620 m_part->xmlDocImpl()->dispatchWindowEvent( EventImpl::RESIZE_EVENT, false, false ); 00621 } 00622 00623 void KHTMLView::viewportResizeEvent (QResizeEvent* e) 00624 { 00625 QScrollView::viewportResizeEvent(e); 00626 00627 //int w = visibleWidth(); 00628 //int h = visibleHeight(); 00629 00630 if (d->layoutSchedulingEnabled) 00631 layout(); 00632 #ifndef KHTML_NO_CARET 00633 else { 00634 hideCaret(); 00635 recalcAndStoreCaretPos(); 00636 showCaret(); 00637 }/*end if*/ 00638 #endif 00639 00640 KApplication::sendPostedEvents(viewport(), QEvent::Paint); 00641 } 00642 00643 // this is to get rid of a compiler virtual overload mismatch warning. do not remove 00644 void KHTMLView::drawContents( QPainter*) 00645 { 00646 } 00647 00648 void KHTMLView::drawContents( QPainter *p, int ex, int ey, int ew, int eh ) 00649 { 00650 #ifdef DEBUG_PIXEL 00651 00652 if ( d->timer.elapsed() > 5000 ) { 00653 qDebug( "drawed %d pixels in %d repaints the last %d milliseconds", 00654 d->pixelbooth, d->repaintbooth, d->timer.elapsed() ); 00655 d->timer.restart(); 00656 d->pixelbooth = 0; 00657 d->repaintbooth = 0; 00658 } 00659 d->pixelbooth += ew*eh; 00660 d->repaintbooth++; 00661 #endif 00662 00663 //kdDebug( 6000 ) << "drawContents this="<< this <<" x=" << ex << ",y=" << ey << ",w=" << ew << ",h=" << eh << endl; 00664 if(!m_part || !m_part->xmlDocImpl() || !m_part->xmlDocImpl()->renderer()) { 00665 p->fillRect(ex, ey, ew, eh, palette().active().brush(QColorGroup::Base)); 00666 return; 00667 } else if ( d->complete && static_cast<RenderCanvas*>(m_part->xmlDocImpl()->renderer())->needsLayout() ) { 00668 // an external update request happens while we have a layout scheduled 00669 unscheduleRelayout(); 00670 layout(); 00671 } 00672 00673 if (d->painting) { 00674 kdDebug( 6000 ) << "WARNING: drawContents reentered! " << endl; 00675 return; 00676 } 00677 d->painting = true; 00678 00679 QPoint pt = contentsToViewport(QPoint(ex, ey)); 00680 QRegion cr = QRect(pt.x(), pt.y(), ew, eh); 00681 00682 // kdDebug(6000) << "clip rect: " << QRect(pt.x(), pt.y(), ew, eh) << endl; 00683 for (QPtrDictIterator<QWidget> it(d->visibleWidgets); it.current(); ++it) { 00684 QWidget *w = it.current(); 00685 RenderWidget* rw = static_cast<RenderWidget*>( it.currentKey() ); 00686 if (w && rw && !rw->isKHTMLWidget()) { 00687 int x, y; 00688 rw->absolutePosition(x, y); 00689 contentsToViewport(x, y, x, y); 00690 int pbx = rw->borderLeft()+rw->paddingLeft(); 00691 int pby = rw->borderTop()+rw->paddingTop(); 00692 QRect g = QRect(x+pbx, y+pby, 00693 rw->width()-pbx-rw->borderRight()-rw->paddingRight(), 00694 rw->height()-pby-rw->borderBottom()-rw->paddingBottom()); 00695 if ( !rw->isFrame() && ((g.top() > pt.y()+eh) || (g.bottom() <= pt.y()) || 00696 (g.right() <= pt.x()) || (g.left() > pt.x()+ew) )) 00697 continue; 00698 RenderLayer* rl = rw->needsMask() ? rw->enclosingStackingContext() : 0; 00699 QRegion mask = rl ? rl->getMask() : QRegion(); 00700 if (!mask.isNull()) { 00701 QPoint o(0,0); 00702 o = contentsToViewport(o); 00703 mask.translate(o.x(),o.y()); 00704 mask = mask.intersect( QRect(g.x(),g.y(),g.width(),g.height()) ); 00705 cr -= mask; 00706 } else { 00707 cr -= g; 00708 } 00709 } 00710 } 00711 00712 #if 0 00713 // this is commonly the case with framesets. we still do 00714 // want to paint them, otherwise the widgets don't get placed. 00715 if (cr.isEmpty()) { 00716 d->painting = false; 00717 return; 00718 } 00719 #endif 00720 00721 #ifndef DEBUG_NO_PAINT_BUFFER 00722 p->setClipRegion(cr); 00723 00724 if (eh > PAINT_BUFFER_HEIGHT && ew <= 10) { 00725 if ( d->vertPaintBuffer->height() < visibleHeight() ) 00726 d->vertPaintBuffer->resize(10, visibleHeight()); 00727 d->tp->begin(d->vertPaintBuffer); 00728 d->tp->translate(-ex, -ey); 00729 d->tp->fillRect(ex, ey, ew, eh, palette().active().brush(QColorGroup::Base)); 00730 m_part->xmlDocImpl()->renderer()->layer()->paint(d->tp, QRect(ex, ey, ew, eh)); 00731 d->tp->end(); 00732 p->drawPixmap(ex, ey, *d->vertPaintBuffer, 0, 0, ew, eh); 00733 } 00734 else { 00735 if ( d->paintBuffer->width() < visibleWidth() ) 00736 d->paintBuffer->resize(visibleWidth(),PAINT_BUFFER_HEIGHT); 00737 00738 int py=0; 00739 while (py < eh) { 00740 int ph = eh-py < PAINT_BUFFER_HEIGHT ? eh-py : PAINT_BUFFER_HEIGHT; 00741 d->tp->begin(d->paintBuffer); 00742 d->tp->translate(-ex, -ey-py); 00743 d->tp->fillRect(ex, ey+py, ew, ph, palette().active().brush(QColorGroup::Base)); 00744 m_part->xmlDocImpl()->renderer()->layer()->paint(d->tp, QRect(ex, ey+py, ew, ph)); 00745 d->tp->end(); 00746 00747 p->drawPixmap(ex, ey+py, *d->paintBuffer, 0, 0, ew, ph); 00748 py += PAINT_BUFFER_HEIGHT; 00749 } 00750 } 00751 #else // !DEBUG_NO_PAINT_BUFFER 00752 static int cnt=0; 00753 ex = contentsX(); ey = contentsY(); 00754 ew = visibleWidth(); eh = visibleHeight(); 00755 QRect pr(ex,ey,ew,eh); 00756 kdDebug() << "[" << ++cnt << "]" << " clip region: " << pr << endl; 00757 // p->setClipRegion(QRect(0,0,ew,eh)); 00758 // p->translate(-ex, -ey); 00759 p->fillRect(ex, ey, ew, eh, palette().active().brush(QColorGroup::Base)); 00760 m_part->xmlDocImpl()->renderer()->layer()->paint(p, pr); 00761 #endif // DEBUG_NO_PAINT_BUFFER 00762 00763 #ifndef KHTML_NO_CARET 00764 if (d->m_caretViewContext && d->m_caretViewContext->visible) { 00765 QRect pos(d->m_caretViewContext->x, d->m_caretViewContext->y, 00766 d->m_caretViewContext->width, d->m_caretViewContext->height); 00767 if (pos.intersects(QRect(ex, ey, ew, eh))) { 00768 p->setRasterOp(XorROP); 00769 p->setPen(white); 00770 if (pos.width() == 1) 00771 p->drawLine(pos.topLeft(), pos.bottomRight()); 00772 else { 00773 p->fillRect(pos, white); 00774 }/*end if*/ 00775 }/*end if*/ 00776 }/*end if*/ 00777 #endif // KHTML_NO_CARET 00778 00779 // p->setPen(QPen(magenta,0,DashDotDotLine)); 00780 // p->drawRect(dbg_paint_rect); 00781 00782 khtml::DrawContentsEvent event( p, ex, ey, ew, eh ); 00783 QApplication::sendEvent( m_part, &event ); 00784 00785 d->painting = false; 00786 } 00787 00788 void KHTMLView::setMarginWidth(int w) 00789 { 00790 // make it update the rendering area when set 00791 _marginWidth = w; 00792 } 00793 00794 void KHTMLView::setMarginHeight(int h) 00795 { 00796 // make it update the rendering area when set 00797 _marginHeight = h; 00798 } 00799 00800 void KHTMLView::layout() 00801 { 00802 if( m_part && m_part->xmlDocImpl() ) { 00803 DOM::DocumentImpl *document = m_part->xmlDocImpl(); 00804 00805 khtml::RenderCanvas* canvas = static_cast<khtml::RenderCanvas *>(document->renderer()); 00806 if ( !canvas ) return; 00807 00808 d->layoutSchedulingEnabled=false; 00809 00810 // the reference object for the overflow property on canvas 00811 RenderObject * ref = 0; 00812 RenderObject* root = document->documentElement() ? document->documentElement()->renderer() : 0; 00813 00814 if (document->isHTMLDocument()) { 00815 NodeImpl *body = static_cast<HTMLDocumentImpl*>(document)->body(); 00816 if(body && body->renderer() && body->id() == ID_FRAMESET) { 00817 QScrollView::setVScrollBarMode(AlwaysOff); 00818 QScrollView::setHScrollBarMode(AlwaysOff); 00819 body->renderer()->setNeedsLayout(true); 00820 // if (d->tooltip) { 00821 // delete d->tooltip; 00822 // d->tooltip = 0; 00823 // } 00824 } 00825 else { 00826 if (!d->tooltip) 00827 d->tooltip = new KHTMLToolTip( this, d ); 00828 // only apply body's overflow to canvas if root as a visible overflow 00829 if (root) 00830 ref = (!body || root->style()->hidesOverflow()) ? root : body->renderer(); 00831 } 00832 } else { 00833 ref = root; 00834 } 00835 if (ref) { 00836 if( ref->style()->overflowX() == OHIDDEN ) { 00837 if (d->hmode == Auto) QScrollView::setHScrollBarMode(AlwaysOff); 00838 } else if (ref->style()->overflowX() == OSCROLL ) { 00839 if (d->hmode == Auto) QScrollView::setHScrollBarMode(AlwaysOn); 00840 } else { 00841 if (QScrollView::hScrollBarMode() == AlwaysOff) QScrollView::setHScrollBarMode(d->hmode); 00842 } if ( ref->style()->overflowY() == OHIDDEN ) { 00843 if (d->vmode == Auto) QScrollView::setVScrollBarMode(AlwaysOff); 00844 } else if (ref->style()->overflowY() == OSCROLL ) { 00845 if (d->vmode == Auto) QScrollView::setVScrollBarMode(AlwaysOn); 00846 } else { 00847 if (QScrollView::vScrollBarMode() == AlwaysOff) QScrollView::setVScrollBarMode(d->vmode); 00848 } 00849 } 00850 d->needsFullRepaint = d->firstRelayout; 00851 if (_height != visibleHeight() || _width != visibleWidth()) {; 00852 d->needsFullRepaint = true; 00853 _height = visibleHeight(); 00854 _width = visibleWidth(); 00855 } 00856 //QTime qt; 00857 //qt.start(); 00858 canvas->layout(); 00859 00860 emit finishedLayout(); 00861 if (d->firstRelayout) { 00862 // make sure firstRelayout is set to false now in case this layout 00863 // wasn't scheduled 00864 d->firstRelayout = false; 00865 verticalScrollBar()->setEnabled( true ); 00866 horizontalScrollBar()->setEnabled( true ); 00867 } 00868 #if 0 00869 ElementImpl *listitem = m_part->xmlDocImpl()->getElementById("__test_element__"); 00870 if (listitem) kdDebug(6000) << "after layout, before repaint" << endl; 00871 if (listitem) dumpLineBoxes(static_cast<RenderFlow *>(listitem->renderer())); 00872 #endif 00873 #ifndef KHTML_NO_CARET 00874 hideCaret(); 00875 if ((m_part->isCaretMode() || m_part->isEditable()) 00876 && !d->complete && d->m_caretViewContext 00877 && !d->m_caretViewContext->caretMoved) { 00878 initCaret(); 00879 } else { 00880 recalcAndStoreCaretPos(); 00881 showCaret(); 00882 }/*end if*/ 00883 #endif 00884 if (d->accessKeysEnabled && d->accessKeysActivated) { 00885 emit hideAccessKeys(); 00886 displayAccessKeys(); 00887 } 00888 //kdDebug( 6000 ) << "TIME: layout() dt=" << qt.elapsed() << endl; 00889 } 00890 else 00891 _width = visibleWidth(); 00892 00893 killTimer(d->layoutTimerId); 00894 d->layoutTimerId = 0; 00895 d->layoutSchedulingEnabled=true; 00896 } 00897 00898 void KHTMLView::closeChildDialogs() 00899 { 00900 QObjectList *dlgs = queryList("QDialog"); 00901 for (QObject *dlg = dlgs->first(); dlg; dlg = dlgs->next()) 00902 { 00903 KDialogBase* dlgbase = dynamic_cast<KDialogBase *>( dlg ); 00904 if ( dlgbase ) { 00905 if ( dlgbase->testWFlags( WShowModal ) ) { 00906 kdDebug(6000) << "closeChildDialogs: closing dialog " << dlgbase << endl; 00907 // close() ends up calling QButton::animateClick, which isn't immediate 00908 // we need something the exits the event loop immediately (#49068) 00909 dlgbase->cancel(); 00910 } 00911 } 00912 else 00913 { 00914 kdWarning() << "closeChildDialogs: not a KDialogBase! Don't use QDialogs in KDE! " << static_cast<QWidget*>(dlg) << endl; 00915 static_cast<QWidget*>(dlg)->hide(); 00916 } 00917 } 00918 delete dlgs; 00919 d->m_dialogsAllowed = false; 00920 } 00921 00922 bool KHTMLView::dialogsAllowed() { 00923 bool allowed = d->m_dialogsAllowed; 00924 KHTMLPart* p = m_part->parentPart(); 00925 if (p && p->view()) 00926 allowed &= p->view()->dialogsAllowed(); 00927 return allowed; 00928 } 00929 00930 void KHTMLView::closeEvent( QCloseEvent* ev ) 00931 { 00932 closeChildDialogs(); 00933 QScrollView::closeEvent( ev ); 00934 } 00935 00936 // 00937 // Event Handling 00938 // 00940 00941 void KHTMLView::viewportMousePressEvent( QMouseEvent *_mouse ) 00942 { 00943 if (!m_part->xmlDocImpl()) return; 00944 if (d->possibleTripleClick && ( _mouse->button() & MouseButtonMask ) == LeftButton) 00945 { 00946 viewportMouseDoubleClickEvent( _mouse ); // it handles triple clicks too 00947 return; 00948 } 00949 00950 int xm, ym; 00951 viewportToContents(_mouse->x(), _mouse->y(), xm, ym); 00952 //kdDebug( 6000 ) << "mousePressEvent: viewport=("<<_mouse->x()<<"/"<<_mouse->y()<<"), contents=(" << xm << "/" << ym << ")\n"; 00953 00954 d->isDoubleClick = false; 00955 00956 DOM::NodeImpl::MouseEvent mev( _mouse->stateAfter(), DOM::NodeImpl::MousePress ); 00957 m_part->xmlDocImpl()->prepareMouseEvent( false, xm, ym, &mev ); 00958 00959 //kdDebug(6000) << "innerNode="<<mev.innerNode.nodeName().string()<<endl; 00960 00961 if ( (_mouse->button() == MidButton) && 00962 !m_part->d->m_bOpenMiddleClick && !d->m_mouseScrollTimer && 00963 mev.url.isNull() && (mev.innerNode.elementId() != ID_INPUT) ) { 00964 QPoint point = mapFromGlobal( _mouse->globalPos() ); 00965 00966 d->m_mouseScroll_byX = 0; 00967 d->m_mouseScroll_byY = 0; 00968 00969 d->m_mouseScrollTimer = new QTimer( this ); 00970 connect( d->m_mouseScrollTimer, SIGNAL(timeout()), this, SLOT(slotMouseScrollTimer()) ); 00971 00972 if ( !d->m_mouseScrollIndicator ) { 00973 QPixmap pixmap, icon; 00974 pixmap.resize( 48, 48 ); 00975 pixmap.fill( QColor( qRgba( 127, 127, 127, 127 ) ) ); 00976 00977 QPainter p( &pixmap ); 00978 icon = KGlobal::iconLoader()->loadIcon( "1uparrow", KIcon::Small ); 00979 p.drawPixmap( 16, 0, icon ); 00980 icon = KGlobal::iconLoader()->loadIcon( "1leftarrow", KIcon::Small ); 00981 p.drawPixmap( 0, 16, icon ); 00982 icon = KGlobal::iconLoader()->loadIcon( "1downarrow", KIcon::Small ); 00983 p.drawPixmap( 16, 32,icon ); 00984 icon = KGlobal::iconLoader()->loadIcon( "1rightarrow", KIcon::Small ); 00985 p.drawPixmap( 32, 16, icon ); 00986 p.drawEllipse( 23, 23, 2, 2 ); 00987 00988 d->m_mouseScrollIndicator = new QWidget( this, 0 ); 00989 d->m_mouseScrollIndicator->setFixedSize( 48, 48 ); 00990 d->m_mouseScrollIndicator->setPaletteBackgroundPixmap( pixmap ); 00991 } 00992 d->m_mouseScrollIndicator->move( point.x()-24, point.y()-24 ); 00993 00994 bool hasHorBar = visibleWidth() < contentsWidth(); 00995 bool hasVerBar = visibleHeight() < contentsHeight(); 00996 00997 KConfig *config = KGlobal::config(); 00998 KConfigGroupSaver saver( config, "HTML Settings" ); 00999 if ( config->readBoolEntry( "ShowMouseScrollIndicator", true ) ) { 01000 d->m_mouseScrollIndicator->show(); 01001 d->m_mouseScrollIndicator->unsetCursor(); 01002 01003 QBitmap mask = d->m_mouseScrollIndicator->paletteBackgroundPixmap()->createHeuristicMask( true ); 01004 01005 if ( hasHorBar && !hasVerBar ) { 01006 QBitmap bm( 16, 16, true ); 01007 bitBlt( &mask, 16, 0, &bm, 0, 0, -1, -1 ); 01008 bitBlt( &mask, 16, 32, &bm, 0, 0, -1, -1 ); 01009 d->m_mouseScrollIndicator->setCursor( KCursor::SizeHorCursor ); 01010 } 01011 else if ( !hasHorBar && hasVerBar ) { 01012 QBitmap bm( 16, 16, true ); 01013 bitBlt( &mask, 0, 16, &bm, 0, 0, -1, -1 ); 01014 bitBlt( &mask, 32, 16, &bm, 0, 0, -1, -1 ); 01015 d->m_mouseScrollIndicator->setCursor( KCursor::SizeVerCursor ); 01016 } 01017 else 01018 d->m_mouseScrollIndicator->setCursor( KCursor::SizeAllCursor ); 01019 01020 d->m_mouseScrollIndicator->setMask( mask ); 01021 } 01022 else { 01023 if ( hasHorBar && !hasVerBar ) 01024 viewport()->setCursor( KCursor::SizeHorCursor ); 01025 else if ( !hasHorBar && hasVerBar ) 01026 viewport()->setCursor( KCursor::SizeVerCursor ); 01027 else 01028 viewport()->setCursor( KCursor::SizeAllCursor ); 01029 } 01030 01031 return; 01032 } 01033 else if ( d->m_mouseScrollTimer ) { 01034 delete d->m_mouseScrollTimer; 01035 d->m_mouseScrollTimer = 0; 01036 01037 if ( d->m_mouseScrollIndicator ) 01038 d->m_mouseScrollIndicator->hide(); 01039 } 01040 01041 d->clickCount = 1; 01042 d->clickX = xm; 01043 d->clickY = ym; 01044 01045 bool swallowEvent = dispatchMouseEvent(EventImpl::MOUSEDOWN_EVENT,mev.innerNode.handle(),mev.innerNonSharedNode.handle(),true, 01046 d->clickCount,_mouse,true,DOM::NodeImpl::MousePress); 01047 01048 khtml::RenderObject* r = mev.innerNode.handle() ? mev.innerNode.handle()->renderer() : 0; 01049 if (r && r->isWidget()) 01050 _mouse->ignore(); 01051 01052 if (!swallowEvent) { 01053 emit m_part->nodeActivated(mev.innerNode); 01054 01055 khtml::MousePressEvent event( _mouse, xm, ym, mev.url, mev.target, mev.innerNode ); 01056 QApplication::sendEvent( m_part, &event ); 01057 // we might be deleted after this 01058 } 01059 } 01060 01061 void KHTMLView::viewportMouseDoubleClickEvent( QMouseEvent *_mouse ) 01062 { 01063 if(!m_part->xmlDocImpl()) return; 01064 01065 int xm, ym; 01066 viewportToContents(_mouse->x(), _mouse->y(), xm, ym); 01067 01068 kdDebug( 6000 ) << "mouseDblClickEvent: x=" << xm << ", y=" << ym << endl; 01069 01070 d->isDoubleClick = true; 01071 01072 DOM::NodeImpl::MouseEvent mev( _mouse->stateAfter(), DOM::NodeImpl::MouseDblClick ); 01073 m_part->xmlDocImpl()->prepareMouseEvent( false, xm, ym, &mev ); 01074 01075 // We do the same thing as viewportMousePressEvent() here, since the DOM does not treat 01076 // single and double-click events as separate (only the detail, i.e. number of clicks differs) 01077 if (d->clickCount > 0 && 01078 QPoint(d->clickX-xm,d->clickY-ym).manhattanLength() <= QApplication::startDragDistance()) 01079 d->clickCount++; 01080 else { // shouldn't happen, if Qt has the same criterias for double clicks. 01081 d->clickCount = 1; 01082 d->clickX = xm; 01083 d->clickY = ym; 01084 } 01085 bool swallowEvent = dispatchMouseEvent(EventImpl::MOUSEDOWN_EVENT,mev.innerNode.handle(),mev.innerNonSharedNode.handle(),true, 01086 d->clickCount,_mouse,true,DOM::NodeImpl::MouseDblClick); 01087 01088 khtml::RenderObject* r = mev.innerNode.handle() ? mev.innerNode.handle()->renderer() : 0; 01089 if (r && r->isWidget()) 01090 _mouse->ignore(); 01091 01092 if (!swallowEvent) { 01093 khtml::MouseDoubleClickEvent event( _mouse, xm, ym, mev.url, mev.target, mev.innerNode, d->clickCount ); 01094 QApplication::sendEvent( m_part, &event ); 01095 } 01096 01097 d->possibleTripleClick=true; 01098 QTimer::singleShot(QApplication::doubleClickInterval(),this,SLOT(tripleClickTimeout())); 01099 } 01100 01101 void KHTMLView::tripleClickTimeout() 01102 { 01103 d->possibleTripleClick = false; 01104 d->clickCount = 0; 01105 } 01106 01107 static inline void forwardPeripheralEvent(khtml::RenderWidget* r, QMouseEvent* me, int x, int y) 01108 { 01109 int absx = 0; 01110 int absy = 0; 01111 r->absolutePosition(absx, absy); 01112 QPoint p(x-absx, y-absy); 01113 QMouseEvent fw(me->type(), p, me->button(), me->state()); 01114 QWidget* w = r->widget(); 01115 QScrollView* sc = ::qt_cast<QScrollView*>(w); 01116 if (sc && !::qt_cast<QListBox*>(w)) 01117 static_cast<khtml::RenderWidget::ScrollViewEventPropagator*>(sc)->sendEvent(&fw); 01118 else if(w) 01119 static_cast<khtml::RenderWidget::EventPropagator*>(w)->sendEvent(&fw); 01120 } 01121 01122 01123 static bool targetOpensNewWindow(KHTMLPart *part, QString target) 01124 { 01125 if (!target.isEmpty() && (target.lower() != "_top") && 01126 (target.lower() != "_self") && (target.lower() != "_parent")) { 01127 if (target.lower() == "_blank") 01128 return true; 01129 else { 01130 while (part->parentPart()) 01131 part = part->parentPart(); 01132 if (!part->frameExists(target)) 01133 return true; 01134 } 01135 } 01136 return false; 01137 } 01138 01139 void KHTMLView::viewportMouseMoveEvent( QMouseEvent * _mouse ) 01140 { 01141 if ( d->m_mouseScrollTimer ) { 01142 QPoint point = mapFromGlobal( _mouse->globalPos() ); 01143 01144 int deltaX = point.x() - d->m_mouseScrollIndicator->x() - 24; 01145 int deltaY = point.y() - d->m_mouseScrollIndicator->y() - 24; 01146 01147 (deltaX > 0) ? d->m_mouseScroll_byX = 1 : d->m_mouseScroll_byX = -1; 01148 (deltaY > 0) ? d->m_mouseScroll_byY = 1 : d->m_mouseScroll_byY = -1; 01149 01150 double adX = QABS(deltaX)/30.0; 01151 double adY = QABS(deltaY)/30.0; 01152 01153 d->m_mouseScroll_byX = kMax(kMin(d->m_mouseScroll_byX * int(adX*adX), SHRT_MAX), SHRT_MIN); 01154 d->m_mouseScroll_byY = kMax(kMin(d->m_mouseScroll_byY * int(adY*adY), SHRT_MAX), SHRT_MIN); 01155 01156 if (d->m_mouseScroll_byX == 0 && d->m_mouseScroll_byY == 0) { 01157 d->m_mouseScrollTimer->stop(); 01158 } 01159 else if (!d->m_mouseScrollTimer->isActive()) { 01160 d->m_mouseScrollTimer->changeInterval( 20 ); 01161 } 01162 } 01163 01164 if(!m_part->xmlDocImpl()) return; 01165 01166 int xm, ym; 01167 viewportToContents(_mouse->x(), _mouse->y(), xm, ym); 01168 01169 DOM::NodeImpl::MouseEvent mev( _mouse->stateAfter(), DOM::NodeImpl::MouseMove ); 01170 // Do not modify :hover/:active state while mouse is pressed. 01171 m_part->xmlDocImpl()->prepareMouseEvent( _mouse->state() & Qt::MouseButtonMask /*readonly ?*/, xm, ym, &mev ); 01172 01173 // kdDebug(6000) << "mouse move: " << _mouse->pos() 01174 // << " button " << _mouse->button() 01175 // << " state " << _mouse->state() << endl; 01176 01177 bool swallowEvent = dispatchMouseEvent(EventImpl::MOUSEMOVE_EVENT,mev.innerNode.handle(),mev.innerNonSharedNode.handle(),false, 01178 0,_mouse,true,DOM::NodeImpl::MouseMove); 01179 01180 if (d->clickCount > 0 && 01181 QPoint(d->clickX-xm,d->clickY-ym).manhattanLength() > QApplication::startDragDistance()) { 01182 d->clickCount = 0; // moving the mouse outside the threshold invalidates the click 01183 } 01184 01185 // execute the scheduled script. This is to make sure the mouseover events come after the mouseout events 01186 m_part->executeScheduledScript(); 01187 01188 DOM::NodeImpl* fn = m_part->xmlDocImpl()->focusNode(); 01189 if (fn && fn != mev.innerNode.handle() && 01190 fn->renderer() && fn->renderer()->isWidget()) { 01191 forwardPeripheralEvent(static_cast<khtml::RenderWidget*>(fn->renderer()), _mouse, xm, ym); 01192 } 01193 01194 khtml::RenderObject* r = mev.innerNode.handle() ? mev.innerNode.handle()->renderer() : 0; 01195 khtml::RenderStyle* style = (r && r->style()) ? r->style() : 0; 01196 QCursor c; 01197 bool mailtoCursor = false; 01198 bool newWindowCursor = false; 01199 switch ( style ? style->cursor() : CURSOR_AUTO) { 01200 case CURSOR_AUTO: 01201 if ( r && r->isText() ) 01202 c = KCursor::ibeamCursor(); 01203 if ( mev.url.length() && m_part->settings()->changeCursor() ) { 01204 c = m_part->urlCursor(); 01205 if (mev.url.string().startsWith("mailto:") && mev.url.string().find('@')>0) 01206 mailtoCursor = true; 01207 else 01208 newWindowCursor = targetOpensNewWindow( m_part, mev.target.string() ); 01209 } 01210 01211 if (r && r->isFrameSet() && !static_cast<RenderFrameSet*>(r)->noResize()) 01212 c = QCursor(static_cast<RenderFrameSet*>(r)->cursorShape()); 01213 01214 break; 01215 case CURSOR_CROSS: 01216 c = KCursor::crossCursor(); 01217 break; 01218 case CURSOR_POINTER: 01219 c = m_part->urlCursor(); 01220 if (mev.url.string().startsWith("mailto:") && mev.url.string().find('@')>0) 01221 mailtoCursor = true; 01222 else 01223 newWindowCursor = targetOpensNewWindow( m_part, mev.target.string() ); 01224 break; 01225 case CURSOR_PROGRESS: 01226 c = KCursor::workingCursor(); 01227 break; 01228 case CURSOR_MOVE: 01229 c = KCursor::sizeAllCursor(); 01230 break; 01231 case CURSOR_E_RESIZE: 01232 case CURSOR_W_RESIZE: 01233 c = KCursor::sizeHorCursor(); 01234 break; 01235 case CURSOR_N_RESIZE: 01236 case CURSOR_S_RESIZE: 01237 c = KCursor::sizeVerCursor(); 01238 break; 01239 case CURSOR_NE_RESIZE: 01240 case CURSOR_SW_RESIZE: 01241 c = KCursor::sizeBDiagCursor(); 01242 break; 01243 case CURSOR_NW_RESIZE: 01244 case CURSOR_SE_RESIZE: 01245 c = KCursor::sizeFDiagCursor(); 01246 break; 01247 case CURSOR_TEXT: 01248 c = KCursor::ibeamCursor(); 01249 break; 01250 case CURSOR_WAIT: 01251 c = KCursor::waitCursor(); 01252 break; 01253 case CURSOR_HELP: 01254 c = KCursor::whatsThisCursor(); 01255 break; 01256 case CURSOR_DEFAULT: 01257 break; 01258 } 01259 01260 if ( viewport()->cursor().handle() != c.handle() ) { 01261 if( c.handle() == KCursor::arrowCursor().handle()) { 01262 for (KHTMLPart* p = m_part; p; p = p->parentPart()) 01263 p->view()->viewport()->unsetCursor(); 01264 } 01265 else { 01266 viewport()->setCursor( c ); 01267 } 01268 } 01269 01270 if ( ( mailtoCursor || newWindowCursor ) && isVisible() && hasFocus() ) { 01271 #ifdef Q_WS_X11 01272 QPixmap icon_pixmap = KGlobal::iconLoader()->loadIcon( mailtoCursor ? "mail_generic" : "window_new", KIcon::Small, 0, KIcon::DefaultState, 0, true ); 01273 01274 if (d->cursor_icon_widget) { 01275 const QPixmap *pm = d->cursor_icon_widget->backgroundPixmap(); 01276 if (!pm || pm->serialNumber()!=icon_pixmap.serialNumber()) { 01277 delete d->cursor_icon_widget; 01278 d->cursor_icon_widget = 0; 01279 } 01280 } 01281 01282 if( !d->cursor_icon_widget ) { 01283 d->cursor_icon_widget = new QWidget( NULL, NULL, WX11BypassWM ); 01284 XSetWindowAttributes attr; 01285 attr.save_under = True; 01286 XChangeWindowAttributes( qt_xdisplay(), d->cursor_icon_widget->winId(), CWSaveUnder, &attr ); 01287 d->cursor_icon_widget->resize( icon_pixmap.width(), icon_pixmap.height()); 01288 if( icon_pixmap.mask() ) 01289 d->cursor_icon_widget->setMask( *icon_pixmap.mask()); 01290 else 01291 d->cursor_icon_widget->clearMask(); 01292 d->cursor_icon_widget->setBackgroundPixmap( icon_pixmap ); 01293 d->cursor_icon_widget->erase(); 01294 } 01295 QPoint c_pos = QCursor::pos(); 01296 d->cursor_icon_widget->move( c_pos.x() + 15, c_pos.y() + 15 ); 01297 XRaiseWindow( qt_xdisplay(), d->cursor_icon_widget->winId()); 01298 QApplication::flushX(); 01299 d->cursor_icon_widget->show(); 01300 #endif 01301 } 01302 else if ( d->cursor_icon_widget ) 01303 d->cursor_icon_widget->hide(); 01304 01305 if (r && r->isWidget()) { 01306 _mouse->ignore(); 01307 } 01308 01309 01310 d->prevMouseX = xm; 01311 d->prevMouseY = ym; 01312 01313 if (!swallowEvent) { 01314 khtml::MouseMoveEvent event( _mouse, xm, ym, mev.url, mev.target, mev.innerNode ); 01315 QApplication::sendEvent( m_part, &event ); 01316 } 01317 } 01318 01319 void KHTMLView::viewportMouseReleaseEvent( QMouseEvent * _mouse ) 01320 { 01321 bool swallowEvent = false; 01322 int xm, ym; 01323 viewportToContents(_mouse->x(), _mouse->y(), xm, ym); 01324 DOM::NodeImpl::MouseEvent mev( _mouse->stateAfter(), DOM::NodeImpl::MouseRelease ); 01325 01326 if ( m_part->xmlDocImpl() ) 01327 { 01328 m_part->xmlDocImpl()->prepareMouseEvent( false, xm, ym, &mev ); 01329 01330 swallowEvent = dispatchMouseEvent(EventImpl::MOUSEUP_EVENT,mev.innerNode.handle(),mev.innerNonSharedNode.handle(),true, 01331 d->clickCount,_mouse,false,DOM::NodeImpl::MouseRelease); 01332 01333 if (d->clickCount > 0 && 01334 QPoint(d->clickX-xm,d->clickY-ym).manhattanLength() <= QApplication::startDragDistance()) { 01335 QMouseEvent me(d->isDoubleClick ? QEvent::MouseButtonDblClick : QEvent::MouseButtonRelease, 01336 _mouse->pos(), _mouse->button(), _mouse->state()); 01337 dispatchMouseEvent(EventImpl::CLICK_EVENT, mev.innerNode.handle(),mev.innerNonSharedNode.handle(),true, 01338 d->clickCount, &me, true, DOM::NodeImpl::MouseRelease); 01339 } 01340 01341 DOM::NodeImpl* fn = m_part->xmlDocImpl()->focusNode(); 01342 if (fn && fn != mev.innerNode.handle() && 01343 fn->renderer() && fn->renderer()->isWidget() && 01344 _mouse->button() != MidButton) { 01345 forwardPeripheralEvent(static_cast<khtml::RenderWidget*>(fn->renderer()), _mouse, xm, ym); 01346 } 01347 01348 khtml::RenderObject* r = mev.innerNode.handle() ? mev.innerNode.handle()->renderer() : 0; 01349 if (r && r->isWidget()) 01350 _mouse->ignore(); 01351 } 01352 01353 if (!swallowEvent) { 01354 khtml::MouseReleaseEvent event( _mouse, xm, ym, mev.url, mev.target, mev.innerNode ); 01355 QApplication::sendEvent( m_part, &event ); 01356 } 01357 } 01358 01359 // returns true if event should be swallowed 01360 bool KHTMLView::dispatchKeyEvent( QKeyEvent *_ke ) 01361 { 01362 if (!m_part->xmlDocImpl()) 01363 return false; 01364 // Pressing and releasing a key should generate keydown, keypress and keyup events 01365 // Holding it down should generated keydown, keypress (repeatedly) and keyup events 01366 // The problem here is that Qt generates two autorepeat events (keyrelease+keypress) 01367 // for autorepeating, while DOM wants only one autorepeat event (keypress), so one 01368 // of the Qt events shouldn't be passed to DOM, but it should be still filtered 01369 // out if DOM would filter the autorepeat event. Additional problem is that Qt keyrelease 01370 // events don't have text() set (Qt bug?), so DOM often would ignore the keypress event 01371 // if it was created using Qt keyrelease, but Qt autorepeat keyrelease comes 01372 // before Qt autorepeat keypress (i.e. problem whether to filter it out or not). 01373 // The solution is to filter out and postpone the Qt autorepeat keyrelease until 01374 // the following Qt keypress event comes. If DOM accepts the DOM keypress event, 01375 // the postponed event will be simply discarded. If not, it will be passed to keyPressEvent() 01376 // again, and here it will be ignored. 01377 // 01378 // Qt: Press | Release(autorepeat) Press(autorepeat) etc. | Release 01379 // DOM: Down + Press | (nothing) Press | Up 01380 01381 // It's also possible to get only Releases. E.g. the release of alt-tab, 01382 // or when the keypresses get captured by an accel. 01383 01384 if( _ke == d->postponed_autorepeat ) // replayed event 01385 { 01386 return false; 01387 } 01388 01389 if( _ke->type() == QEvent::KeyPress ) 01390 { 01391 if( !_ke->isAutoRepeat()) 01392 { 01393 bool ret = dispatchKeyEventHelper( _ke, false ); // keydown 01394 // don't send keypress even if keydown was blocked, like IE (and unlike Mozilla) 01395 if( !ret && dispatchKeyEventHelper( _ke, true )) // keypress 01396 ret = true; 01397 return ret; 01398 } 01399 else // autorepeat 01400 { 01401 bool ret = dispatchKeyEventHelper( _ke, true ); // keypress 01402 if( !ret && d->postponed_autorepeat ) 01403 keyPressEvent( d->postponed_autorepeat ); 01404 delete d->postponed_autorepeat; 01405 d->postponed_autorepeat = NULL; 01406 return ret; 01407 } 01408 } 01409 else // QEvent::KeyRelease 01410 { 01411 // Discard postponed "autorepeat key-release" events that didn't see 01412 // a keypress after them (e.g. due to QAccel) 01413 if ( d->postponed_autorepeat ) { 01414 delete d->postponed_autorepeat; 01415 d->postponed_autorepeat = 0; 01416 } 01417 01418 if( !_ke->isAutoRepeat()) { 01419 return dispatchKeyEventHelper( _ke, false ); // keyup 01420 } 01421 else 01422 { 01423 d->postponed_autorepeat = new QKeyEvent( _ke->type(), _ke->key(), _ke->ascii(), _ke->state(), 01424 _ke->text(), _ke->isAutoRepeat(), _ke->count()); 01425 if( _ke->isAccepted()) 01426 d->postponed_autorepeat->accept(); 01427 else 01428 d->postponed_autorepeat->ignore(); 01429 return true; 01430 } 01431 } 01432 } 01433 01434 // returns true if event should be swallowed 01435 bool KHTMLView::dispatchKeyEventHelper( QKeyEvent *_ke, bool keypress ) 01436 { 01437 DOM::NodeImpl* keyNode = m_part->xmlDocImpl()->focusNode(); 01438 if (keyNode) { 01439 return keyNode->dispatchKeyEvent(_ke, keypress); 01440 } else { // no focused node, send to document 01441 return m_part->xmlDocImpl()->dispatchKeyEvent(_ke, keypress); 01442 } 01443 } 01444 01445 void KHTMLView::keyPressEvent( QKeyEvent *_ke ) 01446 { 01447 #ifndef KHTML_NO_TYPE_AHEAD_FIND 01448 if(d->typeAheadActivated) 01449 { 01450 // type-ahead find aka find-as-you-type 01451 if(_ke->key() == Key_BackSpace) 01452 { 01453 d->findString = d->findString.left(d->findString.length() - 1); 01454 01455 if(!d->findString.isEmpty()) 01456 { 01457 findAhead(false); 01458 } 01459 else 01460 { 01461 findTimeout(); 01462 } 01463 01464 d->timer.start(3000, true); 01465 _ke->accept(); 01466 return; 01467 } 01468 else if(_ke->key() == Key_Escape) 01469 { 01470 findTimeout(); 01471 01472 _ke->accept(); 01473 return; 01474 } 01475 else if(_ke->key() == Key_Space || !_ke->text().stripWhiteSpace().isEmpty()) 01476 { 01477 d->findString += _ke->text(); 01478 01479 findAhead(true); 01480 01481 d->timer.start(3000, true); 01482 _ke->accept(); 01483 return; 01484 } 01485 } 01486 #endif // KHTML_NO_TYPE_AHEAD_FIND 01487 01488 #ifndef KHTML_NO_CARET 01489 if (m_part->isEditable() || m_part->isCaretMode() 01490 || (m_part->xmlDocImpl() && m_part->xmlDocImpl()->focusNode() 01491 && m_part->xmlDocImpl()->focusNode()->contentEditable())) { 01492 d->caretViewContext()->keyReleasePending = true; 01493 caretKeyPressEvent(_ke); 01494 return; 01495 } 01496 #endif // KHTML_NO_CARET 01497 01498 // If CTRL was hit, be prepared for access keys 01499 if (d->accessKeysEnabled && _ke->key() == Key_Control && _ke->state()==0 && !d->accessKeysActivated) 01500 { 01501 d->accessKeysPreActivate=true; 01502 _ke->accept(); 01503 return; 01504 } 01505 01506 if (_ke->key() == Key_Shift && _ke->state()==0) 01507 d->scrollSuspendPreActivate=true; 01508 01509 // accesskey handling needs to be done before dispatching, otherwise e.g. lineedits 01510 // may eat the event 01511 01512 if (d->accessKeysEnabled && d->accessKeysActivated) 01513 { 01514 int state = ( _ke->state() & ( ShiftButton | ControlButton | AltButton | MetaButton )); 01515 if ( state==0 || state==ShiftButton) { 01516 if (_ke->key() != Key_Shift) accessKeysTimeout(); 01517 handleAccessKey( _ke ); 01518 _ke->accept(); 01519 return; 01520 } 01521 accessKeysTimeout(); 01522 } 01523 01524 if ( dispatchKeyEvent( _ke )) { 01525 // If either keydown or keypress was accepted by a widget, or canceled by JS, stop here. 01526 _ke->accept(); 01527 return; 01528 } 01529 01530 int offs = (clipper()->height() < 30) ? clipper()->height() : 30; 01531 if (_ke->state() & Qt::ShiftButton) 01532 switch(_ke->key()) 01533 { 01534 case Key_Space: 01535 scrollBy( 0, -clipper()->height() + offs ); 01536 if(d->scrollSuspended) 01537 d->newScrollTimer(this, 0); 01538 break; 01539 01540 case Key_Down: 01541 case Key_J: 01542 d->adjustScroller(this, KHTMLViewPrivate::ScrollDown, KHTMLViewPrivate::ScrollUp); 01543 break; 01544 01545 case Key_Up: 01546 case Key_K: 01547 d->adjustScroller(this, KHTMLViewPrivate::ScrollUp, KHTMLViewPrivate::ScrollDown); 01548 break; 01549 01550 case Key_Left: 01551 case Key_H: 01552 d->adjustScroller(this, KHTMLViewPrivate::ScrollLeft, KHTMLViewPrivate::ScrollRight); 01553 break; 01554 01555 case Key_Right: 01556 case Key_L: 01557 d->adjustScroller(this, KHTMLViewPrivate::ScrollRight, KHTMLViewPrivate::ScrollLeft); 01558 break; 01559 } 01560 else 01561 switch ( _ke->key() ) 01562 { 01563 case Key_Down: 01564 case Key_J: 01565 if (!d->scrollTimerId || d->scrollSuspended) 01566 scrollBy( 0, 10 ); 01567 if (d->scrollTimerId) 01568 d->newScrollTimer(this, 0); 01569 break; 01570 01571 case Key_Space: 01572 case Key_Next: 01573 scrollBy( 0, clipper()->height() - offs ); 01574 if(d->scrollSuspended) 01575 d->newScrollTimer(this, 0); 01576 break; 01577 01578 case Key_Up: 01579 case Key_K: 01580 if (!d->scrollTimerId || d->scrollSuspended) 01581 scrollBy( 0, -10 ); 01582 if (d->scrollTimerId) 01583 d->newScrollTimer(this, 0); 01584 break; 01585 01586 case Key_Prior: 01587 scrollBy( 0, -clipper()->height() + offs ); 01588 if(d->scrollSuspended) 01589 d->newScrollTimer(this, 0); 01590 break; 01591 case Key_Right: 01592 case Key_L: 01593 if (!d->scrollTimerId || d->scrollSuspended) 01594 scrollBy( 10, 0 ); 01595 if (d->scrollTimerId) 01596 d->newScrollTimer(this, 0); 01597 break; 01598 case Key_Left: 01599 case Key_H: 01600 if (!d->scrollTimerId || d->scrollSuspended) 01601 scrollBy( -10, 0 ); 01602 if (d->scrollTimerId) 01603 d->newScrollTimer(this, 0); 01604 break; 01605 case Key_Enter: 01606 case Key_Return: 01607 // ### FIXME: 01608 // or even better to HTMLAnchorElementImpl::event() 01609 if (m_part->xmlDocImpl()) { 01610 NodeImpl *n = m_part->xmlDocImpl()->focusNode(); 01611 if (n) 01612 n->setActive(); 01613 } 01614 break; 01615 case Key_Home: 01616 setContentsPos( 0, 0 ); 01617 if(d->scrollSuspended) 01618 d->newScrollTimer(this, 0); 01619 break; 01620 case Key_End: 01621 setContentsPos( 0, contentsHeight() - visibleHeight() ); 01622 if(d->scrollSuspended) 01623 d->newScrollTimer(this, 0); 01624 break; 01625 case Key_Shift: 01626 // what are you doing here? 01627 _ke->ignore(); 01628 return; 01629 default: 01630 if (d->scrollTimerId) 01631 d->newScrollTimer(this, 0); 01632 _ke->ignore(); 01633 return; 01634 } 01635 01636 _ke->accept(); 01637 } 01638 01639 void KHTMLView::findTimeout() 01640 { 01641 #ifndef KHTML_NO_TYPE_AHEAD_FIND 01642 d->typeAheadActivated = false; 01643 d->findString = ""; 01644 m_part->setStatusBarText(i18n("Find stopped."), KHTMLPart::BarDefaultText); 01645 m_part->enableFindAheadActions( true ); 01646 #endif // KHTML_NO_TYPE_AHEAD_FIND 01647 } 01648 01649 #ifndef KHTML_NO_TYPE_AHEAD_FIND 01650 void KHTMLView::startFindAhead( bool linksOnly ) 01651 { 01652 if( linksOnly ) 01653 { 01654 d->findLinksOnly = true; 01655 m_part->setStatusBarText(i18n("Starting -- find links as you type"), 01656 KHTMLPart::BarDefaultText); 01657 } 01658 else 01659 { 01660 d->findLinksOnly = false; 01661 m_part->setStatusBarText(i18n("Starting -- find text as you type"), 01662 KHTMLPart::BarDefaultText); 01663 } 01664 01665 m_part->findTextBegin(); 01666 d->typeAheadActivated = true; 01667 // disable, so that the shortcut ( / or ' by default ) doesn't interfere 01668 m_part->enableFindAheadActions( false ); 01669 d->timer.start(3000, true); 01670 } 01671 01672 void KHTMLView::findAhead(bool increase) 01673 { 01674 QString status; 01675 01676 if(d->findLinksOnly) 01677 { 01678 m_part->findText(d->findString, KHTMLPart::FindNoPopups | 01679 KHTMLPart::FindLinksOnly, this); 01680 if(m_part->findTextNext()) 01681 { 01682 status = i18n("Link found: \"%1\"."); 01683 } 01684 else 01685 { 01686 if(increase) KNotifyClient::beep(); 01687 status = i18n("Link not found: \"%1\"."); 01688 } 01689 } 01690 else 01691 { 01692 m_part->findText(d->findString, KHTMLPart::FindNoPopups, this); 01693 if(m_part->findTextNext()) 01694 { 01695 status = i18n("Text found: \"%1\"."); 01696 } 01697 else 01698 { 01699 if(increase) KNotifyClient::beep(); 01700 status = i18n("Text not found: \"%1\"."); 01701 } 01702 } 01703 01704 m_part->setStatusBarText(status.arg(d->findString.lower()), 01705 KHTMLPart::BarDefaultText); 01706 } 01707 01708 void KHTMLView::updateFindAheadTimeout() 01709 { 01710 if( d->typeAheadActivated ) 01711 d->timer.start( 3000, true ); 01712 } 01713 01714 #endif // KHTML_NO_TYPE_AHEAD_FIND 01715 01716 void KHTMLView::keyReleaseEvent(QKeyEvent *_ke) 01717 { 01718 #ifndef KHTML_NO_TYPE_AHEAD_FIND 01719 if(d->typeAheadActivated) { 01720 _ke->accept(); 01721 return; 01722 } 01723 #endif 01724 if (d->m_caretViewContext && d->m_caretViewContext->keyReleasePending) { 01725 //caretKeyReleaseEvent(_ke); 01726 d->m_caretViewContext->keyReleasePending = false; 01727 return; 01728 } 01729 01730 if( d->scrollSuspendPreActivate && _ke->key() != Key_Shift ) 01731 d->scrollSuspendPreActivate = false; 01732 if( _ke->key() == Key_Shift && d->scrollSuspendPreActivate && _ke->state() == Qt::ShiftButton 01733 && !(KApplication::keyboardMouseState() & Qt::ShiftButton)) 01734 { 01735 if (d->scrollTimerId) 01736 { 01737 d->scrollSuspended = !d->scrollSuspended; 01738 #ifndef NO_SMOOTH_SCROLL_HACK 01739 if( d->scrollSuspended ) 01740 stopScrolling(); 01741 #endif 01742 } 01743 } 01744 01745 if (d->accessKeysEnabled) 01746 { 01747 if (d->accessKeysPreActivate && _ke->key() != Key_Control) 01748 d->accessKeysPreActivate=false; 01749 if (d->accessKeysPreActivate && _ke->state() == Qt::ControlButton && !(KApplication::keyboardMouseState() & Qt::ControlButton)) 01750 { 01751 displayAccessKeys(); 01752 m_part->setStatusBarText(i18n("Access Keys activated"),KHTMLPart::BarOverrideText); 01753 d->accessKeysActivated = true; 01754 d->accessKeysPreActivate = false; 01755 _ke->accept(); 01756 return; 01757 } 01758 else if (d->accessKeysActivated) 01759 { 01760 accessKeysTimeout(); 01761 _ke->accept(); 01762 return; 01763 } 01764 } 01765 01766 // Send keyup event 01767 if ( dispatchKeyEvent( _ke ) ) 01768 { 01769 _ke->accept(); 01770 return; 01771 } 01772 01773 QScrollView::keyReleaseEvent(_ke); 01774 } 01775 01776 void KHTMLView::contentsContextMenuEvent ( QContextMenuEvent * /*ce*/ ) 01777 { 01778 // ### what kind of c*** is that ? 01779 #if 0 01780 if (!m_part->xmlDocImpl()) return; 01781 int xm = _ce->x(); 01782 int ym = _ce->y(); 01783 01784 DOM::NodeImpl::MouseEvent mev( _ce->state(), DOM::NodeImpl::MouseMove ); // ### not a mouse event! 01785 m_part->xmlDocImpl()->prepareMouseEvent( xm, ym, &mev ); 01786 01787 NodeImpl *targetNode = mev.innerNode.handle(); 01788 if (targetNode && targetNode->renderer() && targetNode->renderer()->isWidget()) { 01789 int absx = 0; 01790 int absy = 0; 01791 targetNode->renderer()->absolutePosition(absx,absy); 01792 QPoint pos(xm-absx,ym-absy); 01793 01794 QWidget *w = static_cast<RenderWidget*>(targetNode->renderer())->widget(); 01795 QContextMenuEvent cme(_ce->reason(),pos,_ce->globalPos(),_ce->state()); 01796 setIgnoreEvents(true); 01797 QApplication::sendEvent(w,&cme); 01798 setIgnoreEvents(false); 01799 } 01800 #endif 01801 } 01802 01803 bool KHTMLView::focusNextPrevChild( bool next ) 01804 { 01805 // Now try to find the next child 01806 if (m_part->xmlDocImpl() && focusNextPrevNode(next)) 01807 { 01808 if (m_part->xmlDocImpl()->focusNode()) 01809 kdDebug() << "focusNode.name: " 01810 << m_part->xmlDocImpl()->focusNode()->nodeName().string() << endl; 01811 return true; // focus node found 01812 } 01813 01814 // If we get here, pass tabbing control up to the next/previous child in our parent 01815 d->pseudoFocusNode = KHTMLViewPrivate::PFNone; 01816 if (m_part->parentPart() && m_part->parentPart()->view()) 01817 return m_part->parentPart()->view()->focusNextPrevChild(next); 01818 01819 return QWidget::focusNextPrevChild(next); 01820 } 01821 01822 void KHTMLView::doAutoScroll() 01823 { 01824 QPoint pos = QCursor::pos(); 01825 pos = viewport()->mapFromGlobal( pos ); 01826 01827 int xm, ym; 01828 viewportToContents(pos.x(), pos.y(), xm, ym); 01829 01830 pos = QPoint(pos.x() - viewport()->x(), pos.y() - viewport()->y()); 01831 if ( (pos.y() < 0) || (pos.y() > visibleHeight()) || 01832 (pos.x() < 0) || (pos.x() > visibleWidth()) ) 01833 { 01834 ensureVisible( xm, ym, 0, 5 ); 01835 01836 #ifndef KHTML_NO_SELECTION 01837 // extend the selection while scrolling 01838 DOM::Node innerNode; 01839 if (m_part->isExtendingSelection()) { 01840 RenderObject::NodeInfo renderInfo(true/*readonly*/, false/*active*/); 01841 m_part->xmlDocImpl()->renderer()->layer() 01842 ->nodeAtPoint(renderInfo, xm, ym); 01843 innerNode = renderInfo.innerNode(); 01844 }/*end if*/ 01845 01846 if (innerNode.handle() && innerNode.handle()->renderer()) { 01847 int absX, absY; 01848 innerNode.handle()->renderer()->absolutePosition(absX, absY); 01849 01850 m_part->extendSelectionTo(xm, ym, absX, absY, innerNode); 01851 }/*end if*/ 01852 #endif // KHTML_NO_SELECTION 01853 } 01854 } 01855 01856 01857 class HackWidget : public QWidget 01858 { 01859 public: 01860 inline void setNoErase() { setWFlags(getWFlags()|WRepaintNoErase); } 01861 }; 01862 01863 bool KHTMLView::eventFilter(QObject *o, QEvent *e) 01864 { 01865 if ( e->type() == QEvent::AccelOverride ) { 01866 QKeyEvent* ke = (QKeyEvent*) e; 01867 //kdDebug(6200) << "QEvent::AccelOverride" << endl; 01868 if (m_part->isEditable() || m_part->isCaretMode() 01869 || (m_part->xmlDocImpl() && m_part->xmlDocImpl()->focusNode() 01870 && m_part->xmlDocImpl()->focusNode()->contentEditable())) { 01871 //kdDebug(6200) << "editable/navigable" << endl; 01872 if ( (ke->state() & ControlButton) || (ke->state() & ShiftButton) ) { 01873 switch ( ke->key() ) { 01874 case Key_Left: 01875 case Key_Right: 01876 case Key_Up: 01877 case Key_Down: 01878 case Key_Home: 01879 case Key_End: 01880 ke->accept(); 01881 //kdDebug(6200) << "eaten" << endl; 01882 return true; 01883 default: 01884 break; 01885 } 01886 } 01887 } 01888 } 01889 01890 if ( e->type() == QEvent::Leave ) { 01891 if ( d->cursor_icon_widget ) 01892 d->cursor_icon_widget->hide(); 01893 m_part->resetHoverText(); 01894 } 01895 01896 QWidget *view = viewport(); 01897 01898 if (o == view) { 01899 // we need to install an event filter on all children of the viewport to 01900 // be able to get correct stacking of children within the document. 01901 if(e->type() == QEvent::ChildInserted) { 01902 QObject *c = static_cast<QChildEvent *>(e)->child(); 01903 if (c->isWidgetType()) { 01904 QWidget *w = static_cast<QWidget *>(c); 01905 // don't install the event filter on toplevels 01906 if (w->parentWidget(true) == view) { 01907 if (!strcmp(w->name(), "__khtml")) { 01908 w->installEventFilter(this); 01909 w->unsetCursor(); 01910 if (!::qt_cast<QFrame*>(w)) 01911 w->setBackgroundMode( QWidget::NoBackground ); 01912 static_cast<HackWidget *>(w)->setNoErase(); 01913 if (w->children()) { 01914 QObjectListIterator it(*w->children()); 01915 for (; it.current(); ++it) { 01916 QWidget *widget = ::qt_cast<QWidget *>(it.current()); 01917 if (widget && !widget->isTopLevel()) { 01918 if (!::qt_cast<QFrame*>(w)) 01919 widget->setBackgroundMode( QWidget::NoBackground ); 01920 static_cast<HackWidget *>(widget)->setNoErase(); 01921 widget->installEventFilter(this); 01922 } 01923 } 01924 } 01925 } 01926 } 01927 } 01928 } 01929 } else if (o->isWidgetType()) { 01930 QWidget *v = static_cast<QWidget *>(o); 01931 QWidget *c = v; 01932 while (v && v != view) { 01933 c = v; 01934 v = v->parentWidget(true); 01935 } 01936 01937 if (v && !strcmp(c->name(), "__khtml")) { 01938 bool block = false; 01939 QWidget *w = static_cast<QWidget *>(o); 01940 switch(e->type()) { 01941 case QEvent::Paint: 01942 if (!allowWidgetPaintEvents) { 01943 // eat the event. Like this we can control exactly when the widget 01944 // get's repainted. 01945 block = true; 01946 int x = 0, y = 0; 01947 QWidget *v = w; 01948 while (v && v != view) { 01949 x += v->x(); 01950 y += v->y(); 01951 v = v->parentWidget(); 01952 } 01953 viewportToContents( x, y, x, y ); 01954 QPaintEvent *pe = static_cast<QPaintEvent *>(e); 01955 bool asap = !d->contentsMoving && ::qt_cast<QScrollView *>(c); 01956 01957 // QScrollView needs fast repaints 01958 if ( asap && !d->painting && m_part->xmlDocImpl() && m_part->xmlDocImpl()->renderer() && 01959 !static_cast<khtml::RenderCanvas *>(m_part->xmlDocImpl()->renderer())->needsLayout() ) { 01960 repaintContents(x + pe->rect().x(), y + pe->rect().y(), 01961 pe->rect().width(), pe->rect().height(), true); 01962 } else { 01963 scheduleRepaint(x + pe->rect().x(), y + pe->rect().y(), 01964 pe->rect().width(), pe->rect().height(), asap); 01965 } 01966 } 01967 break; 01968 case QEvent::MouseMove: 01969 case QEvent::MouseButtonPress: 01970 case QEvent::MouseButtonRelease: 01971 case QEvent::MouseButtonDblClick: { 01972 if ( (w->parentWidget() == view || ::qt_cast<QScrollView*>(c)) && !::qt_cast<QScrollBar *>(w)) { 01973 QMouseEvent *me = static_cast<QMouseEvent *>(e); 01974 QPoint pt = w->mapTo( view, me->pos()); 01975 QMouseEvent me2(me->type(), pt, me->button(), me->state()); 01976 01977 if (e->type() == QEvent::MouseMove) 01978 viewportMouseMoveEvent(&me2); 01979 else if(e->type() == QEvent::MouseButtonPress) 01980 viewportMousePressEvent(&me2); 01981 else if(e->type() == QEvent::MouseButtonRelease) 01982 viewportMouseReleaseEvent(&me2); 01983 else 01984 viewportMouseDoubleClickEvent(&me2); 01985 block = true; 01986 } 01987 break; 01988 } 01989 case QEvent::KeyPress: 01990 case QEvent::KeyRelease: 01991 if (w->parentWidget() == view && !::qt_cast<QScrollBar *>(w)) { 01992 QKeyEvent *ke = static_cast<QKeyEvent *>(e); 01993 if (e->type() == QEvent::KeyPress) 01994 keyPressEvent(ke); 01995 else 01996 keyReleaseEvent(ke); 01997 block = true; 01998 } 01999 default: 02000 break; 02001 } 02002 if (block) { 02003 //qDebug("eating event"); 02004 return true; 02005 } 02006 } 02007 } 02008 02009 // kdDebug(6000) <<"passing event on to sv event filter object=" << o->className() << " event=" << e->type() << endl; 02010 return QScrollView::eventFilter(o, e); 02011 } 02012 02013 02014 DOM::NodeImpl *KHTMLView::nodeUnderMouse() const 02015 { 02016 return d->underMouse; 02017 } 02018 02019 DOM::NodeImpl *KHTMLView::nonSharedNodeUnderMouse() const 02020 { 02021 return d->underMouseNonShared; 02022 } 02023 02024 bool KHTMLView::scrollTo(const QRect &bounds) 02025 { 02026 d->scrollingSelf = true; // so scroll events get ignored 02027 02028 int x, y, xe, ye; 02029 x = bounds.left(); 02030 y = bounds.top(); 02031 xe = bounds.right(); 02032 ye = bounds.bottom(); 02033 02034 //kdDebug(6000)<<"scrolling coords: x="<<x<<" y="<<y<<" width="<<xe-x<<" height="<<ye-y<<endl; 02035 02036 int deltax; 02037 int deltay; 02038 02039 int curHeight = visibleHeight(); 02040 int curWidth = visibleWidth(); 02041 02042 if (ye-y>curHeight-d->borderY) 02043 ye = y + curHeight - d->borderY; 02044 02045 if (xe-x>curWidth-d->borderX) 02046 xe = x + curWidth - d->borderX; 02047 02048 // is xpos of target left of the view's border? 02049 if (x < contentsX() + d->borderX ) 02050 deltax = x - contentsX() - d->borderX; 02051 // is xpos of target right of the view's right border? 02052 else if (xe + d->borderX > contentsX() + curWidth) 02053 deltax = xe + d->borderX - ( contentsX() + curWidth ); 02054 else 02055 deltax = 0; 02056 02057 // is ypos of target above upper border? 02058 if (y < contentsY() + d->borderY) 02059 deltay = y - contentsY() - d->borderY; 02060 // is ypos of target below lower border? 02061 else if (ye + d->borderY > contentsY() + curHeight) 02062 deltay = ye + d->borderY - ( contentsY() + curHeight ); 02063 else 02064 deltay = 0; 02065 02066 int maxx = curWidth-d->borderX; 02067 int maxy = curHeight-d->borderY; 02068 02069 int scrollX,scrollY; 02070 02071 scrollX = deltax > 0 ? (deltax > maxx ? maxx : deltax) : deltax == 0 ? 0 : (deltax>-maxx ? deltax : -maxx); 02072 scrollY = deltay > 0 ? (deltay > maxy ? maxy : deltay) : deltay == 0 ? 0 : (deltay>-maxy ? deltay : -maxy); 02073 02074 if (contentsX() + scrollX < 0) 02075 scrollX = -contentsX(); 02076 else if (contentsWidth() - visibleWidth() - contentsX() < scrollX) 02077 scrollX = contentsWidth() - visibleWidth() - contentsX(); 02078 02079 if (contentsY() + scrollY < 0) 02080 scrollY = -contentsY(); 02081 else if (contentsHeight() - visibleHeight() - contentsY() < scrollY) 02082 scrollY = contentsHeight() - visibleHeight() - contentsY(); 02083 02084 scrollBy(scrollX, scrollY); 02085 02086 d->scrollingSelf = false; 02087 02088 if ( (abs(deltax)<=maxx) && (abs(deltay)<=maxy) ) 02089 return true; 02090 else return false; 02091 02092 } 02093 02094 bool KHTMLView::focusNextPrevNode(bool next) 02095 { 02096 // Sets the focus node of the document to be the node after (or if 02097 // next is false, before) the current focus node. Only nodes that 02098 // are selectable (i.e. for which isFocusable() returns true) are 02099 // taken into account, and the order used is that specified in the 02100 // HTML spec (see DocumentImpl::nextFocusNode() and 02101 // DocumentImpl::previousFocusNode() for details). 02102 02103 DocumentImpl *doc = m_part->xmlDocImpl(); 02104 NodeImpl *oldFocusNode = doc->focusNode(); 02105 02106 // See whether we're in the middle of detach. If so, we want to 02107 // clear focus... The document code will be careful to not 02108 // emit events in that case.. 02109 if (oldFocusNode && oldFocusNode->renderer() && 02110 !oldFocusNode->renderer()->parent()) { 02111 doc->setFocusNode(0); 02112 return true; 02113 } 02114 02115 #if 1 02116 // If the user has scrolled the document, then instead of picking 02117 // the next focusable node in the document, use the first one that 02118 // is within the visible area (if possible). 02119 if (d->scrollBarMoved) 02120 { 02121 NodeImpl *toFocus; 02122 if (next) 02123 toFocus = doc->nextFocusNode(oldFocusNode); 02124 else 02125 toFocus = doc->previousFocusNode(oldFocusNode); 02126 02127 if (!toFocus && oldFocusNode) 02128 if (next) 02129 toFocus = doc->nextFocusNode(NULL); 02130 else 02131 toFocus = doc->previousFocusNode(NULL); 02132 02133 while (toFocus && toFocus != oldFocusNode) 02134 { 02135 02136 QRect focusNodeRect = toFocus->getRect(); 02137 if ((focusNodeRect.left() > contentsX()) && (focusNodeRect.right() < contentsX() + visibleWidth()) && 02138 (focusNodeRect.top() > contentsY()) && (focusNodeRect.bottom() < contentsY() + visibleHeight())) { 02139 { 02140 QRect r = toFocus->getRect(); 02141 ensureVisible( r.right(), r.bottom()); 02142 ensureVisible( r.left(), r.top()); 02143 d->scrollBarMoved = false; 02144 d->tabMovePending = false; 02145 d->lastTabbingDirection = next; 02146 d->pseudoFocusNode = KHTMLViewPrivate::PFNone; 02147 m_part->xmlDocImpl()->setFocusNode(toFocus); 02148 Node guard(toFocus); 02149 if (!toFocus->hasOneRef() ) 02150 { 02151 emit m_part->nodeActivated(Node(toFocus)); 02152 } 02153 return true; 02154 } 02155 } 02156 if (next) 02157 toFocus = doc->nextFocusNode(toFocus); 02158 else 02159 toFocus = doc->previousFocusNode(toFocus); 02160 02161 if (!toFocus && oldFocusNode) 02162 if (next) 02163 toFocus = doc->nextFocusNode(NULL); 02164 else 02165 toFocus = doc->previousFocusNode(NULL); 02166 } 02167 02168 d->scrollBarMoved = false; 02169 } 02170 #endif 02171 02172 if (!oldFocusNode && d->pseudoFocusNode == KHTMLViewPrivate::PFNone) 02173 { 02174 ensureVisible(contentsX(), next?0:contentsHeight()); 02175 d->scrollBarMoved = false; 02176 d->pseudoFocusNode = next?KHTMLViewPrivate::PFTop:KHTMLViewPrivate::PFBottom; 02177 return true; 02178 } 02179 02180 NodeImpl *newFocusNode = NULL; 02181 02182 if (d->tabMovePending && next != d->lastTabbingDirection) 02183 { 02184 //kdDebug ( 6000 ) << " tab move pending and tabbing direction changed!\n"; 02185 newFocusNode = oldFocusNode; 02186 } 02187 else if (next) 02188 { 02189 if (oldFocusNode || d->pseudoFocusNode == KHTMLViewPrivate::PFTop ) 02190 newFocusNode = doc->nextFocusNode(oldFocusNode); 02191 } 02192 else 02193 { 02194 if (oldFocusNode || d->pseudoFocusNode == KHTMLViewPrivate::PFBottom ) 02195 newFocusNode = doc->previousFocusNode(oldFocusNode); 02196 } 02197 02198 bool targetVisible = false; 02199 if (!newFocusNode) 02200 { 02201 if ( next ) 02202 { 02203 targetVisible = scrollTo(QRect(contentsX()+visibleWidth()/2,contentsHeight()-d->borderY,0,0)); 02204 } 02205 else 02206 { 02207 targetVisible = scrollTo(QRect(contentsX()+visibleWidth()/2,d->borderY,0,0)); 02208 } 02209 } 02210 else 02211 { 02212 #ifndef KHTML_NO_CARET 02213 // if it's an editable element, activate the caret 02214 if (!m_part->isCaretMode() && !m_part->isEditable() 02215 && newFocusNode->contentEditable()) { 02216 d->caretViewContext(); 02217 moveCaretTo(newFocusNode, 0L, true); 02218 } else { 02219 caretOff(); 02220 } 02221 #endif // KHTML_NO_CARET 02222 02223 targetVisible = scrollTo(newFocusNode->getRect()); 02224 } 02225 02226 if (targetVisible) 02227 { 02228 //kdDebug ( 6000 ) << " target reached.\n"; 02229 d->tabMovePending = false; 02230 02231 m_part->xmlDocImpl()->setFocusNode(newFocusNode); 02232 if (newFocusNode) 02233 { 02234 Node guard(newFocusNode); 02235 if (!newFocusNode->hasOneRef() ) 02236 { 02237 emit m_part->nodeActivated(Node(newFocusNode)); 02238 } 02239 return true; 02240 } 02241 else 02242 { 02243 d->pseudoFocusNode = next?KHTMLViewPrivate::PFBottom:KHTMLViewPrivate::PFTop; 02244 return false; 02245 } 02246 } 02247 else 02248 { 02249 if (!d->tabMovePending) 02250 d->lastTabbingDirection = next; 02251 d->tabMovePending = true; 02252 return true; 02253 } 02254 } 02255 02256 void KHTMLView::displayAccessKeys() 02257 { 02258 QValueVector< QChar > taken; 02259 displayAccessKeys( NULL, this, taken, false ); 02260 displayAccessKeys( NULL, this, taken, true ); 02261 } 02262 02263 void KHTMLView::displayAccessKeys( KHTMLView* caller, KHTMLView* origview, QValueVector< QChar >& taken, bool use_fallbacks ) 02264 { 02265 QMap< ElementImpl*, QChar > fallbacks; 02266 if( use_fallbacks ) 02267 fallbacks = buildFallbackAccessKeys(); 02268 for( NodeImpl* n = m_part->xmlDocImpl(); n != NULL; n = n->traverseNextNode()) { 02269 if( n->isElementNode()) { 02270 ElementImpl* en = static_cast< ElementImpl* >( n ); 02271 DOMString s = en->getAttribute( ATTR_ACCESSKEY ); 02272 QString accesskey; 02273 if( s.length() == 1 ) { 02274 QChar a = s.string()[ 0 ].upper(); 02275 if( qFind( taken.begin(), taken.end(), a ) == taken.end()) // !contains 02276 accesskey = a; 02277 } 02278 if( accesskey.isNull() && fallbacks.contains( en )) { 02279 QChar a = fallbacks[ en ].upper(); 02280 if( qFind( taken.begin(), taken.end(), a ) == taken.end()) // !contains 02281 accesskey = QString( "<qt><i>" ) + a + "</i></qt>"; 02282 } 02283 if( !accesskey.isNull()) { 02284 QRect rec=en->getRect(); 02285 QLabel *lab=new QLabel(accesskey,viewport(),0,Qt::WDestructiveClose); 02286 connect( origview, SIGNAL(hideAccessKeys()), lab, SLOT(close()) ); 02287 connect( this, SIGNAL(repaintAccessKeys()), lab, SLOT(repaint())); 02288 lab->setPalette(QToolTip::palette()); 02289 lab->setLineWidth(2); 02290 lab->setFrameStyle(QFrame::Box | QFrame::Plain); 02291 lab->setMargin(3); 02292 lab->adjustSize(); 02293 addChild(lab, 02294 KMIN(rec.left()+rec.width()/2, contentsWidth() - lab->width()), 02295 KMIN(rec.top()+rec.height()/2, contentsHeight() - lab->height())); 02296 showChild(lab); 02297 taken.append( accesskey[ 0 ] ); 02298 } 02299 } 02300 } 02301 if( use_fallbacks ) 02302 return; 02303 QPtrList<KParts::ReadOnlyPart> frames = m_part->frames(); 02304 for( QPtrListIterator<KParts::ReadOnlyPart> it( frames ); 02305 it != NULL; 02306 ++it ) { 02307 if( !(*it)->inherits( "KHTMLPart" )) 02308 continue; 02309 KHTMLPart* part = static_cast< KHTMLPart* >( *it ); 02310 if( part->view() && part->view() != caller ) 02311 part->view()->displayAccessKeys( this, origview, taken, use_fallbacks ); 02312 } 02313 // pass up to the parent 02314 if (m_part->parentPart() && m_part->parentPart()->view() 02315 && m_part->parentPart()->view() != caller) 02316 m_part->parentPart()->view()->displayAccessKeys( this, origview, taken, use_fallbacks ); 02317 } 02318 02319 02320 02321 void KHTMLView::accessKeysTimeout() 02322 { 02323 d->accessKeysActivated=false; 02324 d->accessKeysPreActivate = false; 02325 m_part->setStatusBarText(QString::null, KHTMLPart::BarOverrideText); 02326 emit hideAccessKeys(); 02327 } 02328 02329 // Handling of the HTML accesskey attribute. 02330 bool KHTMLView::handleAccessKey( const QKeyEvent* ev ) 02331 { 02332 // Qt interprets the keyevent also with the modifiers, and ev->text() matches that, 02333 // but this code must act as if the modifiers weren't pressed 02334 QChar c; 02335 if( ev->key() >= Key_A && ev->key() <= Key_Z ) 02336 c = 'A' + ev->key() - Key_A; 02337 else if( ev->key() >= Key_0 && ev->key() <= Key_9 ) 02338 c = '0' + ev->key() - Key_0; 02339 else { 02340 // TODO fake XKeyEvent and XLookupString ? 02341 // This below seems to work e.g. for eacute though. 02342 if( ev->text().length() == 1 ) 02343 c = ev->text()[ 0 ]; 02344 } 02345 if( c.isNull()) 02346 return false; 02347 return focusNodeWithAccessKey( c ); 02348 } 02349 02350 bool KHTMLView::focusNodeWithAccessKey( QChar c, KHTMLView* caller ) 02351 { 02352 DocumentImpl *doc = m_part->xmlDocImpl(); 02353 if( !doc ) 02354 return false; 02355 ElementImpl* node = doc->findAccessKeyElement( c ); 02356 if( !node ) { 02357 QPtrList<KParts::ReadOnlyPart> frames = m_part->frames(); 02358 for( QPtrListIterator<KParts::ReadOnlyPart> it( frames ); 02359 it != NULL; 02360 ++it ) { 02361 if( !(*it)->inherits( "KHTMLPart" )) 02362 continue; 02363 KHTMLPart* part = static_cast< KHTMLPart* >( *it ); 02364 if( part->view() && part->view() != caller 02365 && part->view()->focusNodeWithAccessKey( c, this )) 02366 return true; 02367 } 02368 // pass up to the parent 02369 if (m_part->parentPart() && m_part->parentPart()->view() 02370 && m_part->parentPart()->view() != caller 02371 && m_part->parentPart()->view()->focusNodeWithAccessKey( c, this )) 02372 return true; 02373 if( caller == NULL ) { // the active frame (where the accesskey was pressed) 02374 QMap< ElementImpl*, QChar > fallbacks = buildFallbackAccessKeys(); 02375 for( QMap< ElementImpl*, QChar >::ConstIterator it = fallbacks.begin(); 02376 it != fallbacks.end(); 02377 ++it ) 02378 if( *it == c ) { 02379 node = it.key(); 02380 break; 02381 } 02382 } 02383 if( node == NULL ) 02384 return false; 02385 } 02386 02387 // Scroll the view as necessary to ensure that the new focus node is visible 02388 #ifndef KHTML_NO_CARET 02389 // if it's an editable element, activate the caret 02390 if (!m_part->isCaretMode() && !m_part->isEditable() 02391 && node->contentEditable()) { 02392 d->caretViewContext(); 02393 moveCaretTo(node, 0L, true); 02394 } else { 02395 caretOff(); 02396 } 02397 #endif // KHTML_NO_CARET 02398 02399 QRect r = node->getRect(); 02400 ensureVisible( r.right(), r.bottom()); 02401 ensureVisible( r.left(), r.top()); 02402 02403 Node guard( node ); 02404 if( node->isFocusable()) { 02405 if (node->id()==ID_LABEL) { 02406 // if Accesskey is a label, give focus to the label's referrer. 02407 node=static_cast<ElementImpl *>(static_cast< HTMLLabelElementImpl* >( node )->getFormElement()); 02408 if (!node) return true; 02409 guard = node; 02410 } 02411 // Set focus node on the document 02412 QFocusEvent::setReason( QFocusEvent::Shortcut ); 02413 m_part->xmlDocImpl()->setFocusNode(node); 02414 QFocusEvent::resetReason(); 02415 if( node != NULL && node->hasOneRef()) // deleted, only held by guard 02416 return true; 02417 emit m_part->nodeActivated(Node(node)); 02418 if( node != NULL && node->hasOneRef()) 02419 return true; 02420 } 02421 02422 switch( node->id()) { 02423 case ID_A: 02424 static_cast< HTMLAnchorElementImpl* >( node )->click(); 02425 break; 02426 case ID_INPUT: 02427 static_cast< HTMLInputElementImpl* >( node )->click(); 02428 break; 02429 case ID_BUTTON: 02430 static_cast< HTMLButtonElementImpl* >( node )->click(); 02431 break; 02432 case ID_AREA: 02433 static_cast< HTMLAreaElementImpl* >( node )->click(); 02434 break; 02435 case ID_TEXTAREA: 02436 break; // just focusing it is enough 02437 case ID_LEGEND: 02438 // TODO 02439 break; 02440 } 02441 return true; 02442 } 02443 02444 static QString getElementText( NodeImpl* start, bool after ) 02445 { 02446 QString ret; // nextSibling(), to go after e.g. </select> 02447 for( NodeImpl* n = after ? start->nextSibling() : start->traversePreviousNode(); 02448 n != NULL; 02449 n = after ? n->traverseNextNode() : n->traversePreviousNode()) { 02450 if( n->isTextNode()) { 02451 if( after ) 02452 ret += static_cast< TextImpl* >( n )->toString().string(); 02453 else 02454 ret.prepend( static_cast< TextImpl* >( n )->toString().string()); 02455 } else { 02456 switch( n->id()) { 02457 case ID_A: 02458 case ID_FONT: 02459 case ID_TT: 02460 case ID_U: 02461 case ID_B: 02462 case ID_I: 02463 case ID_S: 02464 case ID_STRIKE: 02465 case ID_BIG: 02466 case ID_SMALL: 02467 case ID_EM: 02468 case ID_STRONG: 02469 case ID_DFN: 02470 case ID_CODE: 02471 case ID_SAMP: 02472 case ID_KBD: 02473 case ID_VAR: 02474 case ID_CITE: 02475 case ID_ABBR: 02476 case ID_ACRONYM: 02477 case ID_SUB: 02478 case ID_SUP: 02479 case ID_SPAN: 02480 case ID_NOBR: 02481 case ID_WBR: 02482 break; 02483 case ID_TD: 02484 if( ret.stripWhiteSpace().isEmpty()) 02485 break; 02486 // fall through 02487 default: 02488 return ret.simplifyWhiteSpace(); 02489 } 02490 } 02491 } 02492 return ret.simplifyWhiteSpace(); 02493 } 02494 02495 static QMap< NodeImpl*, QString > buildLabels( NodeImpl* start ) 02496 { 02497 QMap< NodeImpl*, QString > ret; 02498 for( NodeImpl* n = start; 02499 n != NULL; 02500 n = n->traverseNextNode()) { 02501 if( n->id() == ID_LABEL ) { 02502 HTMLLabelElementImpl* label = static_cast< HTMLLabelElementImpl* >( n ); 02503 NodeImpl* labelfor = label->getFormElement(); 02504 if( labelfor ) 02505 ret[ labelfor ] = label->innerText().string().simplifyWhiteSpace(); 02506 } 02507 } 02508 return ret; 02509 } 02510 02511 namespace khtml { 02512 struct AccessKeyData { 02513 ElementImpl* element; 02514 QString text; 02515 QString url; 02516 int priority; // 10(highest) - 0(lowest) 02517 }; 02518 } 02519 02520 QMap< ElementImpl*, QChar > KHTMLView::buildFallbackAccessKeys() const 02521 { 02522 // build a list of all possible candidate elements that could use an accesskey 02523 QValueList< AccessKeyData > data; 02524 QMap< NodeImpl*, QString > labels = buildLabels( m_part->xmlDocImpl()); 02525 for( NodeImpl* n = m_part->xmlDocImpl(); 02526 n != NULL; 02527 n = n->traverseNextNode()) { 02528 if( n->isElementNode()) { 02529 ElementImpl* element = static_cast< ElementImpl* >( n ); 02530 if( element->getAttribute( ATTR_ACCESSKEY ).length() == 1 ) 02531 continue; // has accesskey set, ignore 02532 if( element->renderer() == NULL ) 02533 continue; // not visible 02534 QString text; 02535 QString url; 02536 int priority = 0; 02537 bool ignore = false; 02538 bool text_after = false; 02539 bool text_before = false; 02540 switch( element->id()) { 02541 case ID_A: 02542 url = khtml::parseURL(element->getAttribute(ATTR_HREF)).string(); 02543 if( url.isEmpty()) // doesn't have href, it's only an anchor 02544 continue; 02545 text = static_cast< HTMLElementImpl* >( element )->innerText().string().simplifyWhiteSpace(); 02546 priority = 2; 02547 break; 02548 case ID_INPUT: { 02549 HTMLInputElementImpl* in = static_cast< HTMLInputElementImpl* >( element ); 02550 switch( in->inputType()) { 02551 case HTMLInputElementImpl::SUBMIT: 02552 text = in->value().string(); 02553 if( text.isEmpty()) 02554 text = i18n( "Submit" ); 02555 priority = 7; 02556 break; 02557 case HTMLInputElementImpl::IMAGE: 02558 text = in->altText().string(); 02559 priority = 7; 02560 break; 02561 case HTMLInputElementImpl::BUTTON: 02562 text = in->value().string(); 02563 priority = 5; 02564 break; 02565 case HTMLInputElementImpl::RESET: 02566 text = in->value().string(); 02567 if( text.isEmpty()) 02568 text = i18n( "Reset" ); 02569 priority = 5; 02570 break; 02571 case HTMLInputElementImpl::HIDDEN: 02572 ignore = true; 02573 break; 02574 case HTMLInputElementImpl::CHECKBOX: 02575 case HTMLInputElementImpl::RADIO: 02576 text_after = true; 02577 priority = 5; 02578 break; 02579 case HTMLInputElementImpl::TEXT: 02580 case HTMLInputElementImpl::PASSWORD: 02581 case HTMLInputElementImpl::FILE: 02582 text_before = true; 02583 priority = 5; 02584 break; 02585 default: 02586 priority = 5; 02587 break; 02588 } 02589 break; 02590 } 02591 case ID_BUTTON: 02592 text = static_cast< HTMLElementImpl* >( element )->innerText().string().simplifyWhiteSpace(); 02593 switch( static_cast< HTMLButtonElementImpl* >( element )->buttonType()) { 02594 case HTMLButtonElementImpl::SUBMIT: 02595 if( text.isEmpty()) 02596 text = i18n( "Submit" ); 02597 priority = 7; 02598 break; 02599 case HTMLButtonElementImpl::RESET: 02600 if( text.isEmpty()) 02601 text = i18n( "Reset" ); 02602 priority = 5; 02603 break; 02604 default: 02605 priority = 5; 02606 break; 02607 break; 02608 } 02609 case ID_SELECT: // these don't have accesskey attribute, but quick access may be handy 02610 text_before = true; 02611 text_after = true; 02612 priority = 5; 02613 break; 02614 case ID_FRAME: 02615 ignore = true; 02616 break; 02617 default: 02618 ignore = !element->isFocusable(); 02619 priority = 2; 02620 break; 02621 } 02622 if( ignore ) 02623 continue; 02624 if( text.isNull() && labels.contains( element )) 02625 text = labels[ element ]; 02626 if( text.isNull() && text_before ) 02627 text = getElementText( element, false ); 02628 if( text.isNull() && text_after ) 02629 text = getElementText( element, true ); 02630 text = text.stripWhiteSpace(); 02631 // increase priority of items which have explicitly specified accesskeys in the config 02632 QValueList< QPair< QString, QChar > > priorities 02633 = m_part->settings()->fallbackAccessKeysAssignments(); 02634 for( QValueList< QPair< QString, QChar > >::ConstIterator it = priorities.begin(); 02635 it != priorities.end(); 02636 ++it ) { 02637 if( text == (*it).first ) 02638 priority = 10; 02639 } 02640 AccessKeyData tmp = { element, text, url, priority }; 02641 data.append( tmp ); 02642 } 02643 } 02644 02645 QValueList< QChar > keys; 02646 for( char c = 'A'; c <= 'Z'; ++c ) 02647 keys << c; 02648 for( char c = '0'; c <= '9'; ++c ) 02649 keys << c; 02650 for( NodeImpl* n = m_part->xmlDocImpl(); 02651 n != NULL; 02652 n = n->traverseNextNode()) { 02653 if( n->isElementNode()) { 02654 ElementImpl* en = static_cast< ElementImpl* >( n ); 02655 DOMString s = en->getAttribute( ATTR_ACCESSKEY ); 02656 if( s.length() == 1 ) { 02657 QChar c = s.string()[ 0 ].upper(); 02658 keys.remove( c ); // remove manually assigned accesskeys 02659 } 02660 } 02661 } 02662 02663 QMap< ElementImpl*, QChar > ret; 02664 for( int priority = 10; 02665 priority >= 0; 02666 --priority ) { 02667 for( QValueList< AccessKeyData >::Iterator it = data.begin(); 02668 it != data.end(); 02669 ) { 02670 if( (*it).priority != priority ) { 02671 ++it; 02672 continue; 02673 } 02674 if( keys.isEmpty()) 02675 break; 02676 QString text = (*it).text; 02677 QChar key; 02678 if( key.isNull() && !text.isEmpty()) { 02679 QValueList< QPair< QString, QChar > > priorities 02680 = m_part->settings()->fallbackAccessKeysAssignments(); 02681 for( QValueList< QPair< QString, QChar > >::ConstIterator it = priorities.begin(); 02682 it != priorities.end(); 02683 ++it ) 02684 if( text == (*it).first && keys.contains( (*it).second )) { 02685 key = (*it).second; 02686 break; 02687 } 02688 } 02689 // try first to select the first character as the accesskey, 02690 // then first character of the following words, 02691 // and then simply the first free character 02692 if( key.isNull() && !text.isEmpty()) { 02693 QStringList words = QStringList::split( ' ', text ); 02694 for( QStringList::ConstIterator it = words.begin(); 02695 it != words.end(); 02696 ++it ) { 02697 if( keys.contains( (*it)[ 0 ].upper())) { 02698 key = (*it)[ 0 ].upper(); 02699 break; 02700 } 02701 } 02702 } 02703 if( key.isNull() && !text.isEmpty()) { 02704 for( unsigned int i = 0; 02705 i < text.length(); 02706 ++i ) { 02707 if( keys.contains( text[ i ].upper())) { 02708 key = text[ i ].upper(); 02709 break; 02710 } 02711 } 02712 } 02713 if( key.isNull()) 02714 key = keys.front(); 02715 ret[ (*it).element ] = key; 02716 keys.remove( key ); 02717 QString url = (*it).url; 02718 it = data.remove( it ); 02719 // assign the same accesskey also to other elements pointing to the same url 02720 if( !url.isEmpty() && !url.startsWith( "javascript:", false )) { 02721 for( QValueList< AccessKeyData >::Iterator it2 = data.begin(); 02722 it2 != data.end(); 02723 ) { 02724 if( (*it2).url == url ) { 02725 ret[ (*it2).element ] = key; 02726 if( it == it2 ) 02727 ++it; 02728 it2 = data.remove( it2 ); 02729 } else 02730 ++it2; 02731 } 02732 } 02733 } 02734 } 02735 return ret; 02736 } 02737 02738 void KHTMLView::setMediaType( const QString &medium ) 02739 { 02740 m_medium = medium; 02741 } 02742 02743 QString KHTMLView::mediaType() const 02744 { 02745 return m_medium; 02746 } 02747 02748 bool KHTMLView::pagedMode() const 02749 { 02750 return d->paged; 02751 } 02752 02753 void KHTMLView::setWidgetVisible(RenderWidget* w, bool vis) 02754 { 02755 if (vis) { 02756 d->visibleWidgets.replace(w, w->widget()); 02757 } 02758 else 02759 d->visibleWidgets.remove(w); 02760 } 02761 02762 bool KHTMLView::needsFullRepaint() const 02763 { 02764 return d->needsFullRepaint; 02765 } 02766 02767 void KHTMLView::print() 02768 { 02769 print( false ); 02770 } 02771 02772 void KHTMLView::print(bool quick) 02773 { 02774 if(!m_part->xmlDocImpl()) return; 02775 khtml::RenderCanvas *root = static_cast<khtml::RenderCanvas *>(m_part->xmlDocImpl()->renderer()); 02776 if(!root) return; 02777 02778 KPrinter *printer = new KPrinter(true, QPrinter::ScreenResolution); 02779 printer->addDialogPage(new KHTMLPrintSettings()); 02780 QString docname = m_part->xmlDocImpl()->URL().prettyURL(); 02781 if ( !docname.isEmpty() ) 02782 docname = KStringHandler::csqueeze(docname, 80); 02783 if(quick || printer->setup(this, i18n("Print %1").arg(docname))) { 02784 viewport()->setCursor( waitCursor ); // only viewport(), no QApplication::, otherwise we get the busy cursor in kdeprint's dialogs 02785 // set up KPrinter 02786 printer->setFullPage(false); 02787 printer->setCreator(QString("KDE %1.%2.%3 HTML Library").arg(KDE_VERSION_MAJOR).arg(KDE_VERSION_MINOR).arg(KDE_VERSION_RELEASE)); 02788 printer->setDocName(docname); 02789 02790 QPainter *p = new QPainter; 02791 p->begin( printer ); 02792 khtml::setPrintPainter( p ); 02793 02794 m_part->xmlDocImpl()->setPaintDevice( printer ); 02795 QString oldMediaType = mediaType(); 02796 setMediaType( "print" ); 02797 // We ignore margin settings for html and body when printing 02798 // and use the default margins from the print-system 02799 // (In Qt 3.0.x the default margins are hardcoded in Qt) 02800 m_part->xmlDocImpl()->setPrintStyleSheet( printer->option("app-khtml-printfriendly") == "true" ? 02801 "* { background-image: none !important;" 02802 " background-color: white !important;" 02803 " color: black !important; }" 02804 "body { margin: 0px !important; }" 02805 "html { margin: 0px !important; }" : 02806 "body { margin: 0px !important; }" 02807 "html { margin: 0px !important; }" 02808 ); 02809 02810 QPaintDeviceMetrics metrics( printer ); 02811 02812 kdDebug(6000) << "printing: physical page width = " << metrics.width() 02813 << " height = " << metrics.height() << endl; 02814 root->setStaticMode(true); 02815 root->setPagedMode(true); 02816 root->setWidth(metrics.width()); 02817 // root->setHeight(metrics.height()); 02818 root->setPageTop(0); 02819 root->setPageBottom(0); 02820 d->paged = true; 02821 02822 m_part->xmlDocImpl()->styleSelector()->computeFontSizes(&metrics, 100); 02823 m_part->xmlDocImpl()->updateStyleSelector(); 02824 root->setPrintImages( printer->option("app-khtml-printimages") == "true"); 02825 root->makePageBreakAvoidBlocks(); 02826 02827 root->setNeedsLayoutAndMinMaxRecalc(); 02828 root->layout(); 02829 khtml::RenderWidget::flushWidgetResizes(); // make sure widgets have their final size 02830 02831 // check sizes ask for action.. (scale or clip) 02832 02833 bool printHeader = (printer->option("app-khtml-printheader") == "true"); 02834 02835 int headerHeight = 0; 02836 QFont headerFont("Sans Serif", 8); 02837 02838 QString headerLeft = KGlobal::locale()->formatDate(QDate::currentDate(),true); 02839 QString headerMid = docname; 02840 QString headerRight; 02841 02842 if (printHeader) 02843 { 02844 p->setFont(headerFont); 02845 headerHeight = (p->fontMetrics().lineSpacing() * 3) / 2; 02846 } 02847 02848 // ok. now print the pages. 02849 kdDebug(6000) << "printing: html page width = " << root->docWidth() 02850 << " height = " << root->docHeight() << endl; 02851 kdDebug(6000) << "printing: margins left = " << printer->margins().width() 02852 << " top = " << printer->margins().height() << endl; 02853 kdDebug(6000) << "printing: paper width = " << metrics.width() 02854 << " height = " << metrics.height() << endl; 02855 // if the width is too large to fit on the paper we just scale 02856 // the whole thing. 02857 int pageWidth = metrics.width(); 02858 int pageHeight = metrics.height(); 02859 p->setClipRect(0,0, pageWidth, pageHeight); 02860 02861 pageHeight -= headerHeight; 02862 02863 bool scalePage = false; 02864 double scale = 0.0; 02865 #ifndef QT_NO_TRANSFORMATIONS 02866 if(root->docWidth() > metrics.width()) { 02867 scalePage = true; 02868 scale = ((double) metrics.width())/((double) root->docWidth()); 02869 pageHeight = (int) (pageHeight/scale); 02870 pageWidth = (int) (pageWidth/scale); 02871 headerHeight = (int) (headerHeight/scale); 02872 } 02873 #endif 02874 kdDebug(6000) << "printing: scaled html width = " << pageWidth 02875 << " height = " << pageHeight << endl; 02876 02877 root->setHeight(pageHeight); 02878 root->setPageBottom(pageHeight); 02879 root->setNeedsLayout(true); 02880 root->layoutIfNeeded(); 02881 // m_part->slotDebugRenderTree(); 02882 02883 // Squeeze header to make it it on the page. 02884 if (printHeader) 02885 { 02886 int available_width = metrics.width() - 10 - 02887 2 * kMax(p->boundingRect(0, 0, metrics.width(), p->fontMetrics().lineSpacing(), Qt::AlignLeft, headerLeft).width(), 02888 p->boundingRect(0, 0, metrics.width(), p->fontMetrics().lineSpacing(), Qt::AlignLeft, headerRight).width()); 02889 if (available_width < 150) 02890 available_width = 150; 02891 int mid_width; 02892 int squeeze = 120; 02893 do { 02894 headerMid = KStringHandler::csqueeze(docname, squeeze); 02895 mid_width = p->boundingRect(0, 0, metrics.width(), p->fontMetrics().lineSpacing(), Qt::AlignLeft, headerMid).width(); 02896 squeeze -= 10; 02897 } while (mid_width > available_width); 02898 } 02899 02900 int top = 0; 02901 int bottom = 0; 02902 int page = 1; 02903 while(top < root->docHeight()) { 02904 if(top > 0) printer->newPage(); 02905 p->setClipRect(0, 0, pageWidth, headerHeight, QPainter::CoordDevice); 02906 if (printHeader) 02907 { 02908 int dy = p->fontMetrics().lineSpacing(); 02909 p->setPen(Qt::black); 02910 p->setFont(headerFont); 02911 02912 headerRight = QString("#%1").arg(page); 02913 02914 p->drawText(0, 0, metrics.width(), dy, Qt::AlignLeft, headerLeft); 02915 p->drawText(0, 0, metrics.width(), dy, Qt::AlignHCenter, headerMid); 02916 p->drawText(0, 0, metrics.width(), dy, Qt::AlignRight, headerRight); 02917 } 02918 02919 02920 #ifndef QT_NO_TRANSFORMATIONS 02921 if (scalePage) 02922 p->scale(scale, scale); 02923 #endif 02924 02925 p->setClipRect(0, headerHeight, pageWidth, pageHeight, QPainter::CoordDevice); 02926 p->translate(0, headerHeight-top); 02927 02928 bottom = top+pageHeight; 02929 02930 root->setPageTop(top); 02931 root->setPageBottom(bottom); 02932 root->setPageNumber(page); 02933 02934 root->layer()->paint(p, QRect(0, top, pageWidth, pageHeight)); 02935 // m_part->xmlDocImpl()->renderer()->layer()->paint(p, QRect(0, top, pageWidth, pageHeight)); 02936 // root->repaint(); 02937 // p->flush(); 02938 kdDebug(6000) << "printed: page " << page <<" bottom At = " << bottom << endl; 02939 02940 top = bottom; 02941 p->resetXForm(); 02942 page++; 02943 } 02944 02945 p->end(); 02946 delete p; 02947 02948 // and now reset the layout to the usual one... 02949 root->setPagedMode(false); 02950 root->setStaticMode(false); 02951 d->paged = false; 02952 khtml::setPrintPainter( 0 ); 02953 setMediaType( oldMediaType ); 02954 m_part->xmlDocImpl()->setPaintDevice( this ); 02955 m_part->xmlDocImpl()->styleSelector()->computeFontSizes(m_part->xmlDocImpl()->paintDeviceMetrics(), m_part->zoomFactor()); 02956 m_part->xmlDocImpl()->updateStyleSelector(); 02957 viewport()->unsetCursor(); 02958 } 02959 delete printer; 02960 } 02961 02962 void KHTMLView::slotPaletteChanged() 02963 { 02964 if(!m_part->xmlDocImpl()) return; 02965 DOM::DocumentImpl *document = m_part->xmlDocImpl(); 02966 if (!document->isHTMLDocument()) return; 02967 khtml::RenderCanvas *root = static_cast<khtml::RenderCanvas *>(document->renderer()); 02968 if(!root) return; 02969 root->style()->resetPalette(); 02970 NodeImpl *body = static_cast<HTMLDocumentImpl*>(document)->body(); 02971 if(!body) return; 02972 body->setChanged(true); 02973 body->recalcStyle( NodeImpl::Force ); 02974 } 02975 02976 void KHTMLView::paint(QPainter *p, const QRect &rc, int yOff, bool *more) 02977 { 02978 if(!m_part->xmlDocImpl()) return; 02979 khtml::RenderCanvas *root = static_cast<khtml::RenderCanvas *>(m_part->xmlDocImpl()->renderer()); 02980 if(!root) return; 02981 02982 m_part->xmlDocImpl()->setPaintDevice(p->device()); 02983 root->setPagedMode(true); 02984 root->setStaticMode(true); 02985 root->setWidth(rc.width()); 02986 02987 p->save(); 02988 p->setClipRect(rc); 02989 p->translate(rc.left(), rc.top()); 02990 double scale = ((double) rc.width()/(double) root->docWidth()); 02991 int height = (int) ((double) rc.height() / scale); 02992 #ifndef QT_NO_TRANSFORMATIONS 02993 p->scale(scale, scale); 02994 #endif 02995 root->setPageTop(yOff); 02996 root->setPageBottom(yOff+height); 02997 02998 root->layer()->paint(p, QRect(0, yOff, root->docWidth(), height)); 02999 if (more) 03000 *more = yOff + height < root->docHeight(); 03001 p->restore(); 03002 03003 root->setPagedMode(false); 03004 root->setStaticMode(false); 03005 m_part->xmlDocImpl()->setPaintDevice( this ); 03006 } 03007 03008 03009 void KHTMLView::useSlowRepaints() 03010 { 03011 d->useSlowRepaints = true; 03012 setStaticBackground(true); 03013 } 03014 03015 03016 void KHTMLView::setVScrollBarMode ( ScrollBarMode mode ) 03017 { 03018 #ifndef KHTML_NO_SCROLLBARS 03019 d->vmode = mode; 03020 QScrollView::setVScrollBarMode(mode); 03021 #else 03022 Q_UNUSED( mode ); 03023 #endif 03024 } 03025 03026 void KHTMLView::setHScrollBarMode ( ScrollBarMode mode ) 03027 { 03028 #ifndef KHTML_NO_SCROLLBARS 03029 d->hmode = mode; 03030 QScrollView::setHScrollBarMode(mode); 03031 #else 03032 Q_UNUSED( mode ); 03033 #endif 03034 } 03035 03036 void KHTMLView::restoreScrollBar() 03037 { 03038 int ow = visibleWidth(); 03039 QScrollView::setVScrollBarMode(d->vmode); 03040 if (visibleWidth() != ow) 03041 layout(); 03042 d->prevScrollbarVisible = verticalScrollBar()->isVisible(); 03043 } 03044 03045 QStringList KHTMLView::formCompletionItems(const QString &name) const 03046 { 03047 if (!m_part->settings()->isFormCompletionEnabled()) 03048 return QStringList(); 03049 if (!d->formCompletions) 03050 d->formCompletions = new KSimpleConfig(locateLocal("data", "khtml/formcompletions")); 03051 return d->formCompletions->readListEntry(name); 03052 } 03053 03054 void KHTMLView::clearCompletionHistory(const QString& name) 03055 { 03056 if (!d->formCompletions) 03057 { 03058 d->formCompletions = new KSimpleConfig(locateLocal("data", "khtml/formcompletions")); 03059 } 03060 d->formCompletions->writeEntry(name, ""); 03061 d->formCompletions->sync(); 03062 } 03063 03064 void KHTMLView::addFormCompletionItem(const QString &name, const QString &value) 03065 { 03066 if (!m_part->settings()->isFormCompletionEnabled()) 03067 return; 03068 // don't store values that are all numbers or just numbers with 03069 // dashes or spaces as those are likely credit card numbers or 03070 // something similar 03071 bool cc_number(true); 03072 for (unsigned int i = 0; i < value.length(); ++i) 03073 { 03074 QChar c(value[i]); 03075 if (!c.isNumber() && c != '-' && !c.isSpace()) 03076 { 03077 cc_number = false; 03078 break; 03079 } 03080 } 03081 if (cc_number) 03082 return; 03083 QStringList items = formCompletionItems(name); 03084 if (!items.contains(value)) 03085 items.prepend(value); 03086 while ((int)items.count() > m_part->settings()->maxFormCompletionItems()) 03087 items.remove(items.fromLast()); 03088 d->formCompletions->writeEntry(name, items); 03089 } 03090 03091 void KHTMLView::addNonPasswordStorableSite(const QString& host) 03092 { 03093 if (!d->formCompletions) { 03094 d->formCompletions = new KSimpleConfig(locateLocal("data", "khtml/formcompletions")); 03095 } 03096 03097 d->formCompletions->setGroup("NonPasswordStorableSites"); 03098 QStringList sites = d->formCompletions->readListEntry("Sites"); 03099 sites.append(host); 03100 d->formCompletions->writeEntry("Sites", sites); 03101 d->formCompletions->sync(); 03102 d->formCompletions->setGroup(QString::null);//reset 03103 } 03104 03105 bool KHTMLView::nonPasswordStorableSite(const QString& host) const 03106 { 03107 if (!d->formCompletions) { 03108 d->formCompletions = new KSimpleConfig(locateLocal("data", "khtml/formcompletions")); 03109 } 03110 d->formCompletions->setGroup("NonPasswordStorableSites"); 03111 QStringList sites = d->formCompletions->readListEntry("Sites"); 03112 d->formCompletions->setGroup(QString::null);//reset 03113 03114 return (sites.find(host) != sites.end()); 03115 } 03116 03117 // returns true if event should be swallowed 03118 bool KHTMLView::dispatchMouseEvent(int eventId, DOM::NodeImpl *targetNode, 03119 DOM::NodeImpl *targetNodeNonShared, bool cancelable, 03120 int detail,QMouseEvent *_mouse, bool setUnder, 03121 int mouseEventType) 03122 { 03123 // if the target node is a text node, dispatch on the parent node - rdar://4196646 (and #76948) 03124 if (targetNode && targetNode->isTextNode()) 03125 targetNode = targetNode->parentNode(); 03126 03127 if (d->underMouse) 03128 d->underMouse->deref(); 03129 d->underMouse = targetNode; 03130 if (d->underMouse) 03131 d->underMouse->ref(); 03132 03133 if (d->underMouseNonShared) 03134 d->underMouseNonShared->deref(); 03135 d->underMouseNonShared = targetNodeNonShared; 03136 if (d->underMouseNonShared) 03137 d->underMouseNonShared->ref(); 03138 03139 int exceptioncode = 0; 03140 int pageX = 0; 03141 int pageY = 0; 03142 viewportToContents(_mouse->x(), _mouse->y(), pageX, pageY); 03143 int clientX = pageX - contentsX(); 03144 int clientY = pageY - contentsY(); 03145 int screenX = _mouse->globalX(); 03146 int screenY = _mouse->globalY(); 03147 int button = -1; 03148 switch (_mouse->button()) { 03149 case LeftButton: 03150 button = 0; 03151 break; 03152 case MidButton: 03153 button = 1; 03154 break; 03155 case RightButton: 03156 button = 2; 03157 break; 03158 default: 03159 break; 03160 } 03161 if (d->accessKeysEnabled && d->accessKeysPreActivate && button!=-1) 03162 d->accessKeysPreActivate=false; 03163 03164 bool ctrlKey = (_mouse->state() & ControlButton); 03165 bool altKey = (_mouse->state() & AltButton); 03166 bool shiftKey = (_mouse->state() & ShiftButton); 03167 bool metaKey = (_mouse->state() & MetaButton); 03168 03169 // mouseout/mouseover 03170 if (setUnder && (d->prevMouseX != pageX || d->prevMouseY != pageY)) { 03171 03172 // ### this code sucks. we should save the oldUnder instead of calculating 03173 // it again. calculating is expensive! (Dirk) 03174 NodeImpl *oldUnder = 0; 03175 if (d->prevMouseX >= 0 && d->prevMouseY >= 0) { 03176 NodeImpl::MouseEvent mev( _mouse->stateAfter(), static_cast<NodeImpl::MouseEventType>(mouseEventType)); 03177 m_part->xmlDocImpl()->prepareMouseEvent( true, d->prevMouseX, d->prevMouseY, &mev ); 03178 oldUnder = mev.innerNode.handle(); 03179 03180 if (oldUnder && oldUnder->isTextNode()) 03181 oldUnder = oldUnder->parentNode(); 03182 } 03183 // qDebug("oldunder=%p (%s), target=%p (%s) x/y=%d/%d", oldUnder, oldUnder ? oldUnder->renderer()->renderName() : 0, targetNode, targetNode ? targetNode->renderer()->renderName() : 0, _mouse->x(), _mouse->y()); 03184 if (oldUnder != targetNode) { 03185 // send mouseout event to the old node 03186 if (oldUnder){ 03187 oldUnder->ref(); 03188 MouseEventImpl *me = new MouseEventImpl(EventImpl::MOUSEOUT_EVENT, 03189 true,true,m_part->xmlDocImpl()->defaultView(), 03190 0,screenX,screenY,clientX,clientY,pageX, pageY, 03191 ctrlKey,altKey,shiftKey,metaKey, 03192 button,targetNode); 03193 me->ref(); 03194 oldUnder->dispatchEvent(me,exceptioncode,true); 03195 me->deref(); 03196 } 03197 03198 // send mouseover event to the new node 03199 if (targetNode) { 03200 MouseEventImpl *me = new MouseEventImpl(EventImpl::MOUSEOVER_EVENT, 03201 true,true,m_part->xmlDocImpl()->defaultView(), 03202 0,screenX,screenY,clientX,clientY,pageX, pageY, 03203 ctrlKey,altKey,shiftKey,metaKey, 03204 button,oldUnder); 03205 03206 me->ref(); 03207 targetNode->dispatchEvent(me,exceptioncode,true); 03208 me->deref(); 03209 } 03210 03211 if (oldUnder) 03212 oldUnder->deref(); 03213 } 03214 } 03215 03216 bool swallowEvent = false; 03217 03218 if (targetNode) { 03219 // send the actual event 03220 bool dblclick = ( eventId == EventImpl::CLICK_EVENT && 03221 _mouse->type() == QEvent::MouseButtonDblClick ); 03222 MouseEventImpl *me = new MouseEventImpl(static_cast<EventImpl::EventId>(eventId), 03223 true,cancelable,m_part->xmlDocImpl()->defaultView(), 03224 detail,screenX,screenY,clientX,clientY,pageX, pageY, 03225 ctrlKey,altKey,shiftKey,metaKey, 03226 button,0, _mouse, dblclick ); 03227 me->ref(); 03228 targetNode->dispatchEvent(me,exceptioncode,true); 03229 bool defaultHandled = me->defaultHandled(); 03230 if (defaultHandled || me->defaultPrevented()) 03231 swallowEvent = true; 03232 me->deref(); 03233 03234 if (eventId == EventImpl::MOUSEDOWN_EVENT) { 03235 // Focus should be shifted on mouse down, not on a click. -dwh 03236 // Blur current focus node when a link/button is clicked; this 03237 // is expected by some sites that rely on onChange handlers running 03238 // from form fields before the button click is processed. 03239 DOM::NodeImpl* nodeImpl = targetNode; 03240 for ( ; nodeImpl && !nodeImpl->isFocusable(); nodeImpl = nodeImpl->parentNode()); 03241 if (nodeImpl && nodeImpl->isMouseFocusable()) 03242 m_part->xmlDocImpl()->setFocusNode(nodeImpl); 03243 else if (!nodeImpl || !nodeImpl->focused()) 03244 m_part->xmlDocImpl()->setFocusNode(0); 03245 } 03246 } 03247 03248 return swallowEvent; 03249 } 03250 03251 void KHTMLView::setIgnoreWheelEvents( bool e ) 03252 { 03253 d->ignoreWheelEvents = e; 03254 } 03255 03256 #ifndef QT_NO_WHEELEVENT 03257 03258 void KHTMLView::viewportWheelEvent(QWheelEvent* e) 03259 { 03260 if (d->accessKeysEnabled && d->accessKeysPreActivate) d->accessKeysPreActivate=false; 03261 03262 if ( ( e->state() & ControlButton) == ControlButton ) 03263 { 03264 emit zoomView( - e->delta() ); 03265 e->accept(); 03266 } 03267 else if (d->firstRelayout) 03268 { 03269 e->accept(); 03270 } 03271 else if( ( (e->orientation() == Vertical && 03272 ((d->ignoreWheelEvents && !verticalScrollBar()->isVisible()) 03273 || e->delta() > 0 && contentsY() <= 0 03274 || e->delta() < 0 && contentsY() >= contentsHeight() - visibleHeight())) 03275 || 03276 (e->orientation() == Horizontal && 03277 ((d->ignoreWheelEvents && !horizontalScrollBar()->isVisible()) 03278 || e->delta() > 0 && contentsX() <=0 03279 || e->delta() < 0 && contentsX() >= contentsWidth() - visibleWidth()))) 03280 && m_part->parentPart()) 03281 { 03282 if ( m_part->parentPart()->view() ) 03283 m_part->parentPart()->view()->wheelEvent( e ); 03284 e->ignore(); 03285 } 03286 else 03287 { 03288 d->scrollBarMoved = true; 03289 #ifndef NO_SMOOTH_SCROLL_HACK 03290 scrollViewWheelEvent( e ); 03291 #else 03292 QScrollView::viewportWheelEvent( e ); 03293 #endif 03294 QMouseEvent *tempEvent = new QMouseEvent( QEvent::MouseMove, QPoint(-1,-1), QPoint(-1,-1), Qt::NoButton, e->state() ); 03295 emit viewportMouseMoveEvent ( tempEvent ); 03296 delete tempEvent; 03297 } 03298 03299 } 03300 #endif 03301 03302 void KHTMLView::dragEnterEvent( QDragEnterEvent* ev ) 03303 { 03304 // Handle drops onto frames (#16820) 03305 // Drops on the main html part is handled by Konqueror (and shouldn't do anything 03306 // in e.g. kmail, so not handled here). 03307 if ( m_part->parentPart() ) 03308 { 03309 QApplication::sendEvent(m_part->parentPart()->widget(), ev); 03310 return; 03311 } 03312 QScrollView::dragEnterEvent( ev ); 03313 } 03314 03315 void KHTMLView::dropEvent( QDropEvent *ev ) 03316 { 03317 // Handle drops onto frames (#16820) 03318 // Drops on the main html part is handled by Konqueror (and shouldn't do anything 03319 // in e.g. kmail, so not handled here). 03320 if ( m_part->parentPart() ) 03321 { 03322 QApplication::sendEvent(m_part->parentPart()->widget(), ev); 03323 return; 03324 } 03325 QScrollView::dropEvent( ev ); 03326 } 03327 03328 void KHTMLView::focusInEvent( QFocusEvent *e ) 03329 { 03330 #ifndef KHTML_NO_TYPE_AHEAD_FIND 03331 m_part->enableFindAheadActions( true ); 03332 #endif 03333 DOM::NodeImpl* fn = m_part->xmlDocImpl() ? m_part->xmlDocImpl()->focusNode() : 0; 03334 if (fn && fn->renderer() && fn->renderer()->isWidget() && 03335 (e->reason() != QFocusEvent::Mouse) && 03336 static_cast<khtml::RenderWidget*>(fn->renderer())->widget()) 03337 static_cast<khtml::RenderWidget*>(fn->renderer())->widget()->setFocus(); 03338 #ifndef KHTML_NO_CARET 03339 // Restart blink frequency timer if it has been killed, but only on 03340 // editable nodes 03341 if (d->m_caretViewContext && 03342 d->m_caretViewContext->freqTimerId == -1 && 03343 fn) { 03344 if (m_part->isCaretMode() 03345 || m_part->isEditable() 03346 || (fn && fn->renderer() 03347 && fn->renderer()->style()->userInput() 03348 == UI_ENABLED)) { 03349 d->m_caretViewContext->freqTimerId = startTimer(500); 03350 d->m_caretViewContext->visible = true; 03351 }/*end if*/ 03352 }/*end if*/ 03353 showCaret(); 03354 #endif // KHTML_NO_CARET 03355 QScrollView::focusInEvent( e ); 03356 } 03357 03358 void KHTMLView::focusOutEvent( QFocusEvent *e ) 03359 { 03360 if(m_part) m_part->stopAutoScroll(); 03361 03362 #ifndef KHTML_NO_TYPE_AHEAD_FIND 03363 if(d->typeAheadActivated) 03364 { 03365 findTimeout(); 03366 } 03367 m_part->enableFindAheadActions( false ); 03368 #endif // KHTML_NO_TYPE_AHEAD_FIND 03369 03370 #ifndef KHTML_NO_CARET 03371 if (d->m_caretViewContext) { 03372 switch (d->m_caretViewContext->displayNonFocused) { 03373 case KHTMLPart::CaretInvisible: 03374 hideCaret(); 03375 break; 03376 case KHTMLPart::CaretVisible: { 03377 killTimer(d->m_caretViewContext->freqTimerId); 03378 d->m_caretViewContext->freqTimerId = -1; 03379 NodeImpl *caretNode = m_part->xmlDocImpl()->focusNode(); 03380 if (!d->m_caretViewContext->visible && (m_part->isCaretMode() 03381 || m_part->isEditable() 03382 || (caretNode && caretNode->renderer() 03383 && caretNode->renderer()->style()->userInput() 03384 == UI_ENABLED))) { 03385 d->m_caretViewContext->visible = true; 03386 showCaret(true); 03387 }/*end if*/ 03388 break; 03389 } 03390 case KHTMLPart::CaretBlink: 03391 // simply leave as is 03392 break; 03393 }/*end switch*/ 03394 }/*end if*/ 03395 #endif // KHTML_NO_CARET 03396 03397 if ( d->cursor_icon_widget ) 03398 d->cursor_icon_widget->hide(); 03399 03400 QScrollView::focusOutEvent( e ); 03401 } 03402 03403 void KHTMLView::slotScrollBarMoved() 03404 { 03405 if ( !d->firstRelayout && !d->complete && m_part->xmlDocImpl() && 03406 d->layoutSchedulingEnabled) { 03407 // contents scroll while we are not complete: we need to check our layout *now* 03408 khtml::RenderCanvas* root = static_cast<khtml::RenderCanvas *>( m_part->xmlDocImpl()->renderer() ); 03409 if (root && root->needsLayout()) { 03410 unscheduleRelayout(); 03411 layout(); 03412 } 03413 } 03414 if (!d->scrollingSelf) { 03415 d->scrollBarMoved = true; 03416 d->contentsMoving = true; 03417 // ensure quick reset of contentsMoving flag 03418 scheduleRepaint(0, 0, 0, 0); 03419 } 03420 03421 if (m_part->xmlDocImpl() && m_part->xmlDocImpl()->documentElement()) 03422 m_part->xmlDocImpl()->documentElement()->dispatchHTMLEvent(EventImpl::SCROLL_EVENT, true, false); 03423 } 03424 03425 void KHTMLView::timerEvent ( QTimerEvent *e ) 03426 { 03427 // kdDebug() << "timer event " << e->timerId() << endl; 03428 if ( e->timerId() == d->scrollTimerId ) { 03429 if( d->scrollSuspended ) 03430 return; 03431 switch (d->scrollDirection) { 03432 case KHTMLViewPrivate::ScrollDown: 03433 if (contentsY() + visibleHeight () >= contentsHeight()) 03434 d->newScrollTimer(this, 0); 03435 else 03436 scrollBy( 0, d->scrollBy ); 03437 break; 03438 case KHTMLViewPrivate::ScrollUp: 03439 if (contentsY() <= 0) 03440 d->newScrollTimer(this, 0); 03441 else 03442 scrollBy( 0, -d->scrollBy ); 03443 break; 03444 case KHTMLViewPrivate::ScrollRight: 03445 if (contentsX() + visibleWidth () >= contentsWidth()) 03446 d->newScrollTimer(this, 0); 03447 else 03448 scrollBy( d->scrollBy, 0 ); 03449 break; 03450 case KHTMLViewPrivate::ScrollLeft: 03451 if (contentsX() <= 0) 03452 d->newScrollTimer(this, 0); 03453 else 03454 scrollBy( -d->scrollBy, 0 ); 03455 break; 03456 } 03457 return; 03458 } 03459 else if ( e->timerId() == d->layoutTimerId ) { 03460 d->dirtyLayout = true; 03461 layout(); 03462 if (d->firstRelayout) { 03463 d->firstRelayout = false; 03464 verticalScrollBar()->setEnabled( true ); 03465 horizontalScrollBar()->setEnabled( true ); 03466 } 03467 } 03468 #ifndef KHTML_NO_CARET 03469 else if (d->m_caretViewContext 03470 && e->timerId() == d->m_caretViewContext->freqTimerId) { 03471 d->m_caretViewContext->visible = !d->m_caretViewContext->visible; 03472 if (d->m_caretViewContext->displayed) { 03473 updateContents(d->m_caretViewContext->x, d->m_caretViewContext->y, 03474 d->m_caretViewContext->width, 03475 d->m_caretViewContext->height); 03476 }/*end if*/ 03477 // if (d->m_caretViewContext->visible) cout << "|" << flush; 03478 // else cout << "" << flush; 03479 return; 03480 } 03481 #endif 03482 03483 d->contentsMoving = false; 03484 if( m_part->xmlDocImpl() ) { 03485 DOM::DocumentImpl *document = m_part->xmlDocImpl(); 03486 khtml::RenderCanvas* root = static_cast<khtml::RenderCanvas *>(document->renderer()); 03487 03488 if ( root && root->needsLayout() ) { 03489 killTimer(d->repaintTimerId); 03490 d->repaintTimerId = 0; 03491 scheduleRelayout(); 03492 return; 03493 } 03494 } 03495 03496 setStaticBackground(d->useSlowRepaints); 03497 03498 // kdDebug() << "scheduled repaint "<< d->repaintTimerId << endl; 03499 killTimer(d->repaintTimerId); 03500 d->repaintTimerId = 0; 03501 03502 QRect updateRegion; 03503 QMemArray<QRect> rects = d->updateRegion.rects(); 03504 03505 d->updateRegion = QRegion(); 03506 03507 if ( rects.size() ) 03508 updateRegion = rects[0]; 03509 03510 for ( unsigned i = 1; i < rects.size(); ++i ) { 03511 QRect newRegion = updateRegion.unite(rects[i]); 03512 if (2*newRegion.height() > 3*updateRegion.height() ) 03513 { 03514 repaintContents( updateRegion ); 03515 updateRegion = rects[i]; 03516 } 03517 else 03518 updateRegion = newRegion; 03519 } 03520 03521 if ( !updateRegion.isNull() ) 03522 repaintContents( updateRegion ); 03523 03524 // As widgets can only be accurately positioned during painting, every layout might 03525 // dissociate a widget from its RenderWidget. E.g: if a RenderWidget was visible before layout, but the layout 03526 // pushed it out of the viewport, it will not be repainted, and consequently it's assocoated widget won't be repositioned! 03527 // Thus we need to check each supposedly 'visible' widget at the end of each layout, and remove it in case it's no more in sight. 03528 03529 if (d->dirtyLayout && !d->visibleWidgets.isEmpty()) { 03530 QWidget* w; 03531 d->dirtyLayout = false; 03532 03533 QRect visibleRect(contentsX(), contentsY(), visibleWidth(), visibleHeight()); 03534 QPtrList<RenderWidget> toRemove; 03535 for (QPtrDictIterator<QWidget> it(d->visibleWidgets); it.current(); ++it) { 03536 int xp = 0, yp = 0; 03537 w = it.current(); 03538 RenderWidget* rw = static_cast<RenderWidget*>( it.currentKey() ); 03539 if (!rw->absolutePosition(xp, yp) || 03540 !visibleRect.intersects(QRect(xp, yp, w->width(), w->height()))) 03541 toRemove.append(rw); 03542 } 03543 for (RenderWidget* r = toRemove.first(); r; r = toRemove.next()) 03544 if ( (w = d->visibleWidgets.take(r) ) ) 03545 addChild(w, 0, -500000); 03546 } 03547 03548 emit repaintAccessKeys(); 03549 if (d->emitCompletedAfterRepaint) { 03550 bool full = d->emitCompletedAfterRepaint == KHTMLViewPrivate::CSFull; 03551 d->emitCompletedAfterRepaint = KHTMLViewPrivate::CSNone; 03552 if ( full ) 03553 emit m_part->completed(); 03554 else 03555 emit m_part->completed(true); 03556 } 03557 } 03558 03559 void KHTMLView::scheduleRelayout(khtml::RenderObject * /*clippedObj*/) 03560 { 03561 if (!d->layoutSchedulingEnabled || d->layoutTimerId) 03562 return; 03563 03564 d->layoutTimerId = startTimer( m_part->xmlDocImpl() && m_part->xmlDocImpl()->parsing() 03565 ? 1000 : 0 ); 03566 } 03567 03568 void KHTMLView::unscheduleRelayout() 03569 { 03570 if (!d->layoutTimerId) 03571 return; 03572 03573 killTimer(d->layoutTimerId); 03574 d->layoutTimerId = 0; 03575 } 03576 03577 void KHTMLView::unscheduleRepaint() 03578 { 03579 if (!d->repaintTimerId) 03580 return; 03581 03582 killTimer(d->repaintTimerId); 03583 d->repaintTimerId = 0; 03584 } 03585 03586 void KHTMLView::scheduleRepaint(int x, int y, int w, int h, bool asap) 03587 { 03588 bool parsing = !m_part->xmlDocImpl() || m_part->xmlDocImpl()->parsing(); 03589 03590 // kdDebug() << "parsing " << parsing << endl; 03591 // kdDebug() << "complete " << d->complete << endl; 03592 03593 int time = parsing ? 300 : (!asap ? ( !d->complete ? 100 : 20 ) : 0); 03594 03595 #ifdef DEBUG_FLICKER 03596 QPainter p; 03597 p.begin( viewport() ); 03598 03599 int vx, vy; 03600 contentsToViewport( x, y, vx, vy ); 03601 p.fillRect( vx, vy, w, h, Qt::red ); 03602 p.end(); 03603 #endif 03604 03605 d->updateRegion = d->updateRegion.unite(QRect(x,y,w,h)); 03606 03607 if (asap && !parsing) 03608 unscheduleRepaint(); 03609 03610 if ( !d->repaintTimerId ) 03611 d->repaintTimerId = startTimer( time ); 03612 03613 // kdDebug() << "starting timer " << time << endl; 03614 } 03615 03616 void KHTMLView::complete( bool pendingAction ) 03617 { 03618 // kdDebug() << "KHTMLView::complete()" << endl; 03619 03620 d->complete = true; 03621 03622 // is there a relayout pending? 03623 if (d->layoutTimerId) 03624 { 03625 // kdDebug() << "requesting relayout now" << endl; 03626 // do it now 03627 killTimer(d->layoutTimerId); 03628 d->layoutTimerId = startTimer( 0 ); 03629 d->emitCompletedAfterRepaint = pendingAction ? 03630 KHTMLViewPrivate::CSActionPending : KHTMLViewPrivate::CSFull; 03631 } 03632 03633 // is there a repaint pending? 03634 if (d->repaintTimerId) 03635 { 03636 // kdDebug() << "requesting repaint now" << endl; 03637 // do it now 03638 killTimer(d->repaintTimerId); 03639 d->repaintTimerId = startTimer( 20 ); 03640 d->emitCompletedAfterRepaint = pendingAction ? 03641 KHTMLViewPrivate::CSActionPending : KHTMLViewPrivate::CSFull; 03642 } 03643 03644 if (!d->emitCompletedAfterRepaint) 03645 { 03646 if (!pendingAction) 03647 emit m_part->completed(); 03648 else 03649 emit m_part->completed(true); 03650 } 03651 03652 } 03653 03654 void KHTMLView::slotMouseScrollTimer() 03655 { 03656 scrollBy( d->m_mouseScroll_byX, d->m_mouseScroll_byY ); 03657 } 03658 03659 #ifndef KHTML_NO_CARET 03660 03661 // ### the dependencies on static functions are a nightmare. just be 03662 // hacky and include the implementation here. Clean me up, please. 03663 03664 #include "khtml_caret.cpp" 03665 03666 void KHTMLView::initCaret(bool keepSelection) 03667 { 03668 #if DEBUG_CARETMODE > 0 03669 kdDebug(6200) << "begin initCaret" << endl; 03670 #endif 03671 // save caretMoved state as moveCaretTo changes it 03672 if (m_part->xmlDocImpl()) { 03673 #if 0 03674 ElementImpl *listitem = m_part->xmlDocImpl()->getElementById("__test_element__"); 03675 if (listitem) dumpLineBoxes(static_cast<RenderFlow *>(listitem->renderer())); 03676 #endif 03677 d->caretViewContext(); 03678 bool cmoved = d->m_caretViewContext->caretMoved; 03679 if (m_part->d->caretNode().isNull()) { 03680 // set to document, position will be sanitized anyway 03681 m_part->d->caretNode() = m_part->document(); 03682 m_part->d->caretOffset() = 0L; 03683 // This sanity check is necessary for the not so unlikely case that 03684 // setEditable or setCaretMode is called before any render objects have 03685 // been created. 03686 if (!m_part->d->caretNode().handle()->renderer()) return; 03687 }/*end if*/ 03688 // kdDebug(6200) << "d->m_selectionStart " << m_part->d->m_selectionStart.handle() 03689 // << " d->m_selectionEnd " << m_part->d->m_selectionEnd.handle() << endl; 03690 // ### does not repaint the selection on keepSelection!=false 03691 moveCaretTo(m_part->d->caretNode().handle(), m_part->d->caretOffset(), !keepSelection); 03692 // kdDebug(6200) << "d->m_selectionStart " << m_part->d->m_selectionStart.handle() 03693 // << " d->m_selectionEnd " << m_part->d->m_selectionEnd.handle() << endl; 03694 d->m_caretViewContext->caretMoved = cmoved; 03695 }/*end if*/ 03696 #if DEBUG_CARETMODE > 0 03697 kdDebug(6200) << "end initCaret" << endl; 03698 #endif 03699 } 03700 03701 bool KHTMLView::caretOverrides() const 03702 { 03703 bool cm = m_part->isCaretMode(); 03704 bool dm = m_part->isEditable(); 03705 return cm && !dm ? false 03706 : (dm || m_part->d->caretNode().handle()->contentEditable()) 03707 && d->editorContext()->override; 03708 } 03709 03710 void KHTMLView::ensureNodeHasFocus(NodeImpl *node) 03711 { 03712 if (m_part->isCaretMode() || m_part->isEditable()) return; 03713 if (node->focused()) return; 03714 03715 // Find first ancestor whose "user-input" is "enabled" 03716 NodeImpl *firstAncestor = 0; 03717 while (node) { 03718 if (node->renderer() 03719 && node->renderer()->style()->userInput() != UI_ENABLED) 03720 break; 03721 firstAncestor = node; 03722 node = node->parentNode(); 03723 }/*wend*/ 03724 03725 if (!node) firstAncestor = 0; 03726 03727 DocumentImpl *doc = m_part->xmlDocImpl(); 03728 // ensure that embedded widgets don't lose their focus 03729 if (!firstAncestor && doc->focusNode() && doc->focusNode()->renderer() 03730 && doc->focusNode()->renderer()->isWidget()) 03731 return; 03732 03733 // Set focus node on the document 03734 #if DEBUG_CARETMODE > 1 03735 kdDebug(6200) << k_funcinfo << "firstAncestor " << firstAncestor << ": " 03736 << (firstAncestor ? firstAncestor->nodeName().string() : QString::null) << endl; 03737 #endif 03738 doc->setFocusNode(firstAncestor); 03739 emit m_part->nodeActivated(Node(firstAncestor)); 03740 } 03741 03742 void KHTMLView::recalcAndStoreCaretPos(CaretBox *hintBox) 03743 { 03744 if (!m_part || m_part->d->caretNode().isNull()) return; 03745 d->caretViewContext(); 03746 NodeImpl *caretNode = m_part->d->caretNode().handle(); 03747 #if DEBUG_CARETMODE > 0 03748 kdDebug(6200) << "recalcAndStoreCaretPos: caretNode=" << caretNode << (caretNode ? " "+caretNode->nodeName().string() : QString::null) << " r@" << caretNode->renderer() << (caretNode->renderer() && caretNode->renderer()->isText() ? " \"" + QConstString(static_cast<RenderText *>(caretNode->renderer())->str->s, kMin(static_cast<RenderText *>(caretNode->renderer())->str->l, 15u)).string() + "\"" : QString::null) << endl; 03749 #endif 03750 caretNode->getCaret(m_part->d->caretOffset(), caretOverrides(), 03751 d->m_caretViewContext->x, d->m_caretViewContext->y, 03752 d->m_caretViewContext->width, 03753 d->m_caretViewContext->height); 03754 03755 if (hintBox && d->m_caretViewContext->x == -1) { 03756 #if DEBUG_CARETMODE > 1 03757 kdDebug(6200) << "using hint inline box coordinates" << endl; 03758 #endif 03759 RenderObject *r = caretNode->renderer(); 03760 const QFontMetrics &fm = r->style()->fontMetrics(); 03761 int absx, absy; 03762 r->containingBlock()->absolutePosition(absx, absy, 03763 false); // ### what about fixed? 03764 d->m_caretViewContext->x = absx + hintBox->xPos(); 03765 d->m_caretViewContext->y = absy + hintBox->yPos(); 03766 // + hintBox->baseline() - fm.ascent(); 03767 d->m_caretViewContext->width = 1; 03768 // ### firstline not regarded. But I think it can be safely neglected 03769 // as hint boxes are only used for empty lines. 03770 d->m_caretViewContext->height = fm.height(); 03771 }/*end if*/ 03772 03773 #if DEBUG_CARETMODE > 4 03774 // kdDebug(6200) << "freqTimerId: "<<d->m_caretViewContext->freqTimerId<<endl; 03775 #endif 03776 #if DEBUG_CARETMODE > 0 03777 kdDebug(6200) << "caret: ofs="<<m_part->d->caretOffset()<<" " 03778 <<" x="<<d->m_caretViewContext->x<<" y="<<d->m_caretViewContext->y 03779 <<" h="<<d->m_caretViewContext->height<<endl; 03780 #endif 03781 } 03782 03783 void KHTMLView::caretOn() 03784 { 03785 if (d->m_caretViewContext) { 03786 killTimer(d->m_caretViewContext->freqTimerId); 03787 03788 if (hasFocus() || d->m_caretViewContext->displayNonFocused 03789 == KHTMLPart::CaretBlink) { 03790 d->m_caretViewContext->freqTimerId = startTimer(500); 03791 } else { 03792 d->m_caretViewContext->freqTimerId = -1; 03793 }/*end if*/ 03794 03795 d->m_caretViewContext->visible = true; 03796 if ((d->m_caretViewContext->displayed = (hasFocus() 03797 || d->m_caretViewContext->displayNonFocused 03798 != KHTMLPart::CaretInvisible))) { 03799 updateContents(d->m_caretViewContext->x, d->m_caretViewContext->y, 03800 d->m_caretViewContext->width, 03801 d->m_caretViewContext->height); 03802 }/*end if*/ 03803 // kdDebug(6200) << "caret on" << endl; 03804 }/*end if*/ 03805 } 03806 03807 void KHTMLView::caretOff() 03808 { 03809 if (d->m_caretViewContext) { 03810 killTimer(d->m_caretViewContext->freqTimerId); 03811 d->m_caretViewContext->freqTimerId = -1; 03812 d->m_caretViewContext->displayed = false; 03813 if (d->m_caretViewContext->visible) { 03814 d->m_caretViewContext->visible = false; 03815 updateContents(d->m_caretViewContext->x, d->m_caretViewContext->y, 03816 d->m_caretViewContext->width, 03817 d->m_caretViewContext->height); 03818 }/*end if*/ 03819 // kdDebug(6200) << "caret off" << endl; 03820 }/*end if*/ 03821 } 03822 03823 void KHTMLView::showCaret(bool forceRepaint) 03824 { 03825 if (d->m_caretViewContext) { 03826 d->m_caretViewContext->displayed = true; 03827 if (d->m_caretViewContext->visible) { 03828 if (!forceRepaint) { 03829 updateContents(d->m_caretViewContext->x, d->m_caretViewContext->y, 03830 d->m_caretViewContext->width, 03831 d->m_caretViewContext->height); 03832 } else { 03833 repaintContents(d->m_caretViewContext->x, d->m_caretViewContext->y, 03834 d->m_caretViewContext->width, 03835 d->m_caretViewContext->height); 03836 }/*end if*/ 03837 }/*end if*/ 03838 // kdDebug(6200) << "caret shown" << endl; 03839 }/*end if*/ 03840 } 03841 03842 bool KHTMLView::foldSelectionToCaret(NodeImpl *startNode, long startOffset, 03843 NodeImpl *endNode, long endOffset) 03844 { 03845 m_part->d->m_selectionStart = m_part->d->m_selectionEnd = m_part->d->caretNode(); 03846 m_part->d->m_startOffset = m_part->d->m_endOffset = m_part->d->caretOffset(); 03847 m_part->d->m_extendAtEnd = true; 03848 03849 bool folded = startNode != endNode || startOffset != endOffset; 03850 03851 // Only clear the selection if there has been one. 03852 if (folded) { 03853 m_part->xmlDocImpl()->clearSelection(); 03854 }/*end if*/ 03855 03856 return folded; 03857 } 03858 03859 void KHTMLView::hideCaret() 03860 { 03861 if (d->m_caretViewContext) { 03862 if (d->m_caretViewContext->visible) { 03863 // kdDebug(6200) << "redraw caret hidden" << endl; 03864 d->m_caretViewContext->visible = false; 03865 // force repaint, otherwise the event won't be handled 03866 // before the focus leaves the window 03867 repaintContents(d->m_caretViewContext->x, d->m_caretViewContext->y, 03868 d->m_caretViewContext->width, 03869 d->m_caretViewContext->height); 03870 d->m_caretViewContext->visible = true; 03871 }/*end if*/ 03872 d->m_caretViewContext->displayed = false; 03873 // kdDebug(6200) << "caret hidden" << endl; 03874 }/*end if*/ 03875 } 03876 03877 int KHTMLView::caretDisplayPolicyNonFocused() const 03878 { 03879 if (d->m_caretViewContext) 03880 return d->m_caretViewContext->displayNonFocused; 03881 else 03882 return KHTMLPart::CaretInvisible; 03883 } 03884 03885 void KHTMLView::setCaretDisplayPolicyNonFocused(int policy) 03886 { 03887 d->caretViewContext(); 03888 // int old = d->m_caretViewContext->displayNonFocused; 03889 d->m_caretViewContext->displayNonFocused = (KHTMLPart::CaretDisplayPolicy)policy; 03890 03891 // make change immediately take effect if not focused 03892 if (!hasFocus()) { 03893 switch (d->m_caretViewContext->displayNonFocused) { 03894 case KHTMLPart::CaretInvisible: 03895 hideCaret(); 03896 break; 03897 case KHTMLPart::CaretBlink: 03898 if (d->m_caretViewContext->freqTimerId != -1) break; 03899 d->m_caretViewContext->freqTimerId = startTimer(500); 03900 // fall through 03901 case KHTMLPart::CaretVisible: 03902 d->m_caretViewContext->displayed = true; 03903 showCaret(); 03904 break; 03905 }/*end switch*/ 03906 }/*end if*/ 03907 } 03908 03909 bool KHTMLView::placeCaret(CaretBox *hintBox) 03910 { 03911 CaretViewContext *cv = d->caretViewContext(); 03912 caretOff(); 03913 NodeImpl *caretNode = m_part->d->caretNode().handle(); 03914 // ### why is it sometimes null? 03915 if (!caretNode || !caretNode->renderer()) return false; 03916 ensureNodeHasFocus(caretNode); 03917 if (m_part->isCaretMode() || m_part->isEditable() 03918 || caretNode->renderer()->style()->userInput() == UI_ENABLED) { 03919 recalcAndStoreCaretPos(hintBox); 03920 03921 cv->origX = cv->x; 03922 03923 caretOn(); 03924 return true; 03925 }/*end if*/ 03926 return false; 03927 } 03928 03929 void KHTMLView::ensureCaretVisible() 03930 { 03931 CaretViewContext *cv = d->m_caretViewContext; 03932 if (!cv) return; 03933 ensureVisible(cv->x, cv->y, cv->width, cv->height); 03934 d->scrollBarMoved = false; 03935 } 03936 03937 bool KHTMLView::extendSelection(NodeImpl *oldStartSel, long oldStartOfs, 03938 NodeImpl *oldEndSel, long oldEndOfs) 03939 { 03940 bool changed = false; 03941 if (m_part->d->m_selectionStart == m_part->d->m_selectionEnd 03942 && m_part->d->m_startOffset == m_part->d->m_endOffset) { 03943 changed = foldSelectionToCaret(oldStartSel, oldStartOfs, oldEndSel, oldEndOfs); 03944 m_part->d->m_extendAtEnd = true; 03945 } else do { 03946 changed = m_part->d->m_selectionStart.handle() != oldStartSel 03947 || m_part->d->m_startOffset != oldStartOfs 03948 || m_part->d->m_selectionEnd.handle() != oldEndSel 03949 || m_part->d->m_endOffset != oldEndOfs; 03950 if (!changed) break; 03951 03952 // determine start position -- caret position is always at end. 03953 NodeImpl *startNode; 03954 long startOffset; 03955 if (m_part->d->m_extendAtEnd) { 03956 startNode = m_part->d->m_selectionStart.handle(); 03957 startOffset = m_part->d->m_startOffset; 03958 } else { 03959 startNode = m_part->d->m_selectionEnd.handle(); 03960 startOffset = m_part->d->m_endOffset; 03961 m_part->d->m_selectionEnd = m_part->d->m_selectionStart; 03962 m_part->d->m_endOffset = m_part->d->m_startOffset; 03963 m_part->d->m_extendAtEnd = true; 03964 }/*end if*/ 03965 03966 bool swapNeeded = false; 03967 if (!m_part->d->m_selectionEnd.isNull() && startNode) { 03968 swapNeeded = RangeImpl::compareBoundaryPoints(startNode, startOffset, 03969 m_part->d->m_selectionEnd.handle(), 03970 m_part->d->m_endOffset) >= 0; 03971 }/*end if*/ 03972 03973 m_part->d->m_selectionStart = startNode; 03974 m_part->d->m_startOffset = startOffset; 03975 03976 if (swapNeeded) { 03977 m_part->xmlDocImpl()->setSelection(m_part->d->m_selectionEnd.handle(), 03978 m_part->d->m_endOffset, m_part->d->m_selectionStart.handle(), 03979 m_part->d->m_startOffset); 03980 } else { 03981 m_part->xmlDocImpl()->setSelection(m_part->d->m_selectionStart.handle(), 03982 m_part->d->m_startOffset, m_part->d->m_selectionEnd.handle(), 03983 m_part->d->m_endOffset); 03984 }/*end if*/ 03985 } while(false);/*end if*/ 03986 return changed; 03987 } 03988 03989 void KHTMLView::updateSelection(NodeImpl *oldStartSel, long oldStartOfs, 03990 NodeImpl *oldEndSel, long oldEndOfs) 03991 { 03992 if (m_part->d->m_selectionStart == m_part->d->m_selectionEnd 03993 && m_part->d->m_startOffset == m_part->d->m_endOffset) { 03994 if (foldSelectionToCaret(oldStartSel, oldStartOfs, oldEndSel, oldEndOfs)) { 03995 m_part->emitSelectionChanged(); 03996 }/*end if*/ 03997 m_part->d->m_extendAtEnd = true; 03998 } else { 03999 // check if the extending end has passed the immobile end 04000 if (!m_part->d->m_selectionEnd.isNull() && !m_part->d->m_selectionEnd.isNull()) { 04001 bool swapNeeded = RangeImpl::compareBoundaryPoints( 04002 m_part->d->m_selectionStart.handle(), m_part->d->m_startOffset, 04003 m_part->d->m_selectionEnd.handle(), m_part->d->m_endOffset) >= 0; 04004 if (swapNeeded) { 04005 DOM::Node tmpNode = m_part->d->m_selectionStart; 04006 long tmpOffset = m_part->d->m_startOffset; 04007 m_part->d->m_selectionStart = m_part->d->m_selectionEnd; 04008 m_part->d->m_startOffset = m_part->d->m_endOffset; 04009 m_part->d->m_selectionEnd = tmpNode; 04010 m_part->d->m_endOffset = tmpOffset; 04011 m_part->d->m_startBeforeEnd = true; 04012 m_part->d->m_extendAtEnd = !m_part->d->m_extendAtEnd; 04013 }/*end if*/ 04014 }/*end if*/ 04015 04016 m_part->xmlDocImpl()->setSelection(m_part->d->m_selectionStart.handle(), 04017 m_part->d->m_startOffset, m_part->d->m_selectionEnd.handle(), 04018 m_part->d->m_endOffset); 04019 m_part->emitSelectionChanged(); 04020 }/*end if*/ 04021 } 04022 04023 void KHTMLView::caretKeyPressEvent(QKeyEvent *_ke) 04024 { 04025 NodeImpl *oldStartSel = m_part->d->m_selectionStart.handle(); 04026 long oldStartOfs = m_part->d->m_startOffset; 04027 NodeImpl *oldEndSel = m_part->d->m_selectionEnd.handle(); 04028 long oldEndOfs = m_part->d->m_endOffset; 04029 04030 NodeImpl *oldCaretNode = m_part->d->caretNode().handle(); 04031 long oldOffset = m_part->d->caretOffset(); 04032 04033 bool ctrl = _ke->state() & ControlButton; 04034 04035 // FIXME: this is that widely indented because I will write ifs around it. 04036 switch(_ke->key()) { 04037 case Key_Space: 04038 break; 04039 04040 case Key_Down: 04041 moveCaretNextLine(1); 04042 break; 04043 04044 case Key_Up: 04045 moveCaretPrevLine(1); 04046 break; 04047 04048 case Key_Left: 04049 moveCaretBy(false, ctrl ? CaretByWord : CaretByCharacter, 1); 04050 break; 04051 04052 case Key_Right: 04053 moveCaretBy(true, ctrl ? CaretByWord : CaretByCharacter, 1); 04054 break; 04055 04056 case Key_Next: 04057 moveCaretNextPage(); 04058 break; 04059 04060 case Key_Prior: 04061 moveCaretPrevPage(); 04062 break; 04063 04064 case Key_Home: 04065 if (ctrl) 04066 moveCaretToDocumentBoundary(false); 04067 else 04068 moveCaretToLineBegin(); 04069 break; 04070 04071 case Key_End: 04072 if (ctrl) 04073 moveCaretToDocumentBoundary(true); 04074 else 04075 moveCaretToLineEnd(); 04076 break; 04077 04078 }/*end switch*/ 04079 04080 if ((m_part->d->caretNode().handle() != oldCaretNode 04081 || m_part->d->caretOffset() != oldOffset) 04082 // node should never be null, but faulty conditions may cause it to be 04083 && !m_part->d->caretNode().isNull()) { 04084 04085 d->m_caretViewContext->caretMoved = true; 04086 04087 if (_ke->state() & ShiftButton) { // extend selection 04088 updateSelection(oldStartSel, oldStartOfs, oldEndSel, oldEndOfs); 04089 } else { // clear any selection 04090 if (foldSelectionToCaret(oldStartSel, oldStartOfs, oldEndSel, oldEndOfs)) 04091 m_part->emitSelectionChanged(); 04092 }/*end if*/ 04093 04094 m_part->emitCaretPositionChanged(m_part->d->caretNode(), m_part->d->caretOffset()); 04095 }/*end if*/ 04096 04097 _ke->accept(); 04098 } 04099 04100 bool KHTMLView::moveCaretTo(NodeImpl *node, long offset, bool clearSel) 04101 { 04102 if (!node) return false; 04103 ElementImpl *baseElem = determineBaseElement(node); 04104 RenderFlow *base = static_cast<RenderFlow *>(baseElem ? baseElem->renderer() : 0); 04105 if (!node) return false; 04106 04107 // need to find out the node's inline box. If there is none, this function 04108 // will snap to the next node that has one. This is necessary to make the 04109 // caret visible in any case. 04110 CaretBoxLineDeleter cblDeleter; 04111 // RenderBlock *cb; 04112 long r_ofs; 04113 CaretBoxIterator cbit; 04114 CaretBoxLine *cbl = findCaretBoxLine(node, offset, &cblDeleter, base, r_ofs, cbit); 04115 if(!cbl) { 04116 kdWarning() << "KHTMLView::moveCaretTo - findCaretBoxLine() returns NULL" << endl; 04117 return false; 04118 } 04119 04120 #if DEBUG_CARETMODE > 3 04121 if (cbl) kdDebug(6200) << cbl->information() << endl; 04122 #endif 04123 CaretBox *box = *cbit; 04124 if (cbit != cbl->end() && box->object() != node->renderer()) { 04125 if (box->object()->element()) { 04126 mapRenderPosToDOMPos(box->object(), r_ofs, box->isOutside(), 04127 box->isOutsideEnd(), node, offset); 04128 //if (!outside) offset = node->minOffset(); 04129 #if DEBUG_CARETMODE > 1 04130 kdDebug(6200) << "set new node " << node->nodeName().string() << "@" << node << endl; 04131 #endif 04132 } else { // box has no associated element -> do not use 04133 // this case should actually never happen. 04134 box = 0; 04135 kdError(6200) << "Box contains no node! Crash imminent" << endl; 04136 }/*end if*/ 04137 } 04138 04139 NodeImpl *oldStartSel = m_part->d->m_selectionStart.handle(); 04140 long oldStartOfs = m_part->d->m_startOffset; 04141 NodeImpl *oldEndSel = m_part->d->m_selectionEnd.handle(); 04142 long oldEndOfs = m_part->d->m_endOffset; 04143 04144 // test for position change 04145 bool posChanged = m_part->d->caretNode().handle() != node 04146 || m_part->d->caretOffset() != offset; 04147 bool selChanged = false; 04148 04149 m_part->d->caretNode() = node; 04150 m_part->d->caretOffset() = offset; 04151 if (clearSel || !oldStartSel || !oldEndSel) { 04152 selChanged = foldSelectionToCaret(oldStartSel, oldStartOfs, oldEndSel, oldEndOfs); 04153 } else { 04154 //kdDebug(6200) << "moveToCaret: extendSelection: m_extendAtEnd " << m_part->d->m_extendAtEnd << endl; 04155 //kdDebug(6200) << "selection: start(" << m_part->d->m_selectionStart.handle() << "," << m_part->d->m_startOffset << "), end(" << m_part->d->m_selectionEnd.handle() << "," << m_part->d->m_endOffset << "), caret(" << m_part->d->caretNode().handle() << "," << m_part->d->caretOffset() << ")" << endl; 04156 selChanged = extendSelection(oldStartSel, oldStartOfs, oldEndSel, oldEndOfs); 04157 //kdDebug(6200) << "after extendSelection: m_extendAtEnd " << m_part->d->m_extendAtEnd << endl; 04158 //kdDebug(6200) << "selection: start(" << m_part->d->m_selectionStart.handle() << "," << m_part->d->m_startOffset << "), end(" << m_part->d->m_selectionEnd.handle() << "," << m_part->d->m_endOffset << "), caret(" << m_part->d->caretNode().handle() << "," << m_part->d->caretOffset() << ")" << endl; 04159 }/*end if*/ 04160 04161 d->caretViewContext()->caretMoved = true; 04162 04163 bool visible_caret = placeCaret(box); 04164 04165 // FIXME: if the old position was !visible_caret, and the new position is 04166 // also, then two caretPositionChanged signals with a null Node are 04167 // emitted in series. 04168 if (posChanged) { 04169 m_part->emitCaretPositionChanged(visible_caret ? node : 0, offset); 04170 }/*end if*/ 04171 04172 return selChanged; 04173 } 04174 04175 void KHTMLView::moveCaretByLine(bool next, int count) 04176 { 04177 Node &caretNodeRef = m_part->d->caretNode(); 04178 if (caretNodeRef.isNull()) return; 04179 04180 NodeImpl *caretNode = caretNodeRef.handle(); 04181 // kdDebug(6200) << ": caretNode=" << caretNode << endl; 04182 long offset = m_part->d->caretOffset(); 04183 04184 CaretViewContext *cv = d->caretViewContext(); 04185 04186 ElementImpl *baseElem = determineBaseElement(caretNode); 04187 LinearDocument ld(m_part, caretNode, offset, LeafsOnly, baseElem); 04188 04189 ErgonomicEditableLineIterator it(ld.current(), cv->origX); 04190 04191 // move count lines vertically 04192 while (count > 0 && it != ld.end() && it != ld.preBegin()) { 04193 count--; 04194 if (next) ++it; else --it; 04195 }/*wend*/ 04196 04197 // Nothing? Then leave everything as is. 04198 if (it == ld.end() || it == ld.preBegin()) return; 04199 04200 int x, absx, absy; 04201 CaretBox *caretBox = nearestCaretBox(it, d->m_caretViewContext, x, absx, absy); 04202 04203 placeCaretOnLine(caretBox, x, absx, absy); 04204 } 04205 04206 void KHTMLView::placeCaretOnLine(CaretBox *caretBox, int x, int absx, int absy) 04207 { 04208 // paranoia sanity check 04209 if (!caretBox) return; 04210 04211 RenderObject *caretRender = caretBox->object(); 04212 04213 #if DEBUG_CARETMODE > 0 04214 kdDebug(6200) << "got valid caretBox " << caretBox << endl; 04215 kdDebug(6200) << "xPos: " << caretBox->xPos() << " yPos: " << caretBox->yPos() 04216 << " width: " << caretBox->width() << " height: " << caretBox->height() << endl; 04217 InlineTextBox *tb = static_cast<InlineTextBox *>(caretBox->inlineBox()); 04218 if (caretBox->isInlineTextBox()) { kdDebug(6200) << "contains \"" << QString(static_cast<RenderText *>(tb->object())->str->s + tb->m_start, tb->m_len) << "\"" << endl;} 04219 #endif 04220 // inquire height of caret 04221 int caretHeight = caretBox->height(); 04222 bool isText = caretBox->isInlineTextBox(); 04223 int yOfs = 0; // y-offset for text nodes 04224 if (isText) { 04225 // text boxes need extrawurst 04226 RenderText *t = static_cast<RenderText *>(caretRender); 04227 const QFontMetrics &fm = t->metrics(caretBox->inlineBox()->m_firstLine); 04228 caretHeight = fm.height(); 04229 yOfs = caretBox->inlineBox()->baseline() - fm.ascent(); 04230 }/*end if*/ 04231 04232 caretOff(); 04233 04234 // set new caret node 04235 NodeImpl *caretNode; 04236 long &offset = m_part->d->caretOffset(); 04237 mapRenderPosToDOMPos(caretRender, offset, caretBox->isOutside(), 04238 caretBox->isOutsideEnd(), caretNode, offset); 04239 04240 // set all variables not needing special treatment 04241 d->m_caretViewContext->y = caretBox->yPos() + yOfs; 04242 d->m_caretViewContext->height = caretHeight; 04243 d->m_caretViewContext->width = 1; // FIXME: regard override 04244 04245 int xPos = caretBox->xPos(); 04246 int caretBoxWidth = caretBox->width(); 04247 d->m_caretViewContext->x = xPos; 04248 04249 if (!caretBox->isOutside()) { 04250 // before or at beginning of inline box -> place at beginning 04251 long r_ofs = 0; 04252 if (x <= xPos) { 04253 r_ofs = caretBox->minOffset(); 04254 // somewhere within this block 04255 } else if (x > xPos && x <= xPos + caretBoxWidth) { 04256 if (isText) { // find out where exactly 04257 r_ofs = static_cast<InlineTextBox *>(caretBox->inlineBox()) 04258 ->offsetForPoint(x, d->m_caretViewContext->x); 04259 #if DEBUG_CARETMODE > 2 04260 kdDebug(6200) << "deviation from origX " << d->m_caretViewContext->x - x << endl; 04261 #endif 04262 #if 0 04263 } else { // snap to nearest end 04264 if (xPos + caretBoxWidth - x < x - xPos) { 04265 d->m_caretViewContext->x = xPos + caretBoxWidth; 04266 r_ofs = caretNode ? caretNode->maxOffset() : 1; 04267 } else { 04268 d->m_caretViewContext->x = xPos; 04269 r_ofs = caretNode ? caretNode->minOffset() : 0; 04270 }/*end if*/ 04271 #endif 04272 }/*end if*/ 04273 } else { // after the inline box -> place at end 04274 d->m_caretViewContext->x = xPos + caretBoxWidth; 04275 r_ofs = caretBox->maxOffset(); 04276 }/*end if*/ 04277 offset = r_ofs; 04278 }/*end if*/ 04279 #if DEBUG_CARETMODE > 0 04280 kdDebug(6200) << "new offset: " << offset << endl; 04281 #endif 04282 04283 m_part->d->caretNode() = caretNode; 04284 m_part->d->caretOffset() = offset; 04285 04286 d->m_caretViewContext->x += absx; 04287 d->m_caretViewContext->y += absy; 04288 04289 #if DEBUG_CARETMODE > 1 04290 kdDebug(6200) << "new caret position: x " << d->m_caretViewContext->x << " y " << d->m_caretViewContext->y << " w " << d->m_caretViewContext->width << " h " << d->m_caretViewContext->height << " absx " << absx << " absy " << absy << endl; 04291 #endif 04292 04293 ensureVisible(d->m_caretViewContext->x, d->m_caretViewContext->y, 04294 d->m_caretViewContext->width, d->m_caretViewContext->height); 04295 d->scrollBarMoved = false; 04296 04297 ensureNodeHasFocus(caretNode); 04298 caretOn(); 04299 } 04300 04301 void KHTMLView::moveCaretToLineBoundary(bool end) 04302 { 04303 Node &caretNodeRef = m_part->d->caretNode(); 04304 if (caretNodeRef.isNull()) return; 04305 04306 NodeImpl *caretNode = caretNodeRef.handle(); 04307 // kdDebug(6200) << ": caretNode=" << caretNode << endl; 04308 long offset = m_part->d->caretOffset(); 04309 04310 ElementImpl *baseElem = determineBaseElement(caretNode); 04311 LinearDocument ld(m_part, caretNode, offset, LeafsOnly, baseElem); 04312 04313 EditableLineIterator it = ld.current(); 04314 if (it == ld.end()) return; // should not happen, but who knows 04315 04316 EditableCaretBoxIterator fbit(it, end); 04317 Q_ASSERT(fbit != (*it)->end() && fbit != (*it)->preBegin()); 04318 CaretBox *b = *fbit; 04319 04320 RenderObject *cb = b->containingBlock(); 04321 int absx, absy; 04322 04323 if (cb) cb->absolutePosition(absx,absy); 04324 else absx = absy = 0; 04325 04326 int x = b->xPos() + (end && !b->isOutside() ? b->width() : 0); 04327 d->m_caretViewContext->origX = absx + x; 04328 placeCaretOnLine(b, x, absx, absy); 04329 } 04330 04331 void KHTMLView::moveCaretToDocumentBoundary(bool end) 04332 { 04333 Node &caretNodeRef = m_part->d->caretNode(); 04334 if (caretNodeRef.isNull()) return; 04335 04336 NodeImpl *caretNode = caretNodeRef.handle(); 04337 // kdDebug(6200) << ": caretNode=" << caretNode << endl; 04338 long offset = m_part->d->caretOffset(); 04339 04340 ElementImpl *baseElem = determineBaseElement(caretNode); 04341 LinearDocument ld(m_part, caretNode, offset, IndicatedFlows, baseElem); 04342 04343 EditableLineIterator it(end ? ld.preEnd() : ld.begin(), end); 04344 if (it == ld.end() || it == ld.preBegin()) return; // should not happen, but who knows 04345 04346 EditableCaretBoxIterator fbit = it; 04347 Q_ASSERT(fbit != (*it)->end() && fbit != (*it)->preBegin()); 04348 CaretBox *b = *fbit; 04349 04350 RenderObject *cb = (*it)->containingBlock(); 04351 int absx, absy; 04352 04353 if (cb) cb->absolutePosition(absx, absy); 04354 else absx = absy = 0; 04355 04356 int x = b->xPos()/* + (end ? b->width() : 0) reactivate for rtl*/; 04357 d->m_caretViewContext->origX = absx + x; 04358 placeCaretOnLine(b, x, absx, absy); 04359 } 04360 04361 void KHTMLView::moveCaretBy(bool next, CaretMovement cmv, int count) 04362 { 04363 if (!m_part) return; 04364 Node &caretNodeRef = m_part->d->caretNode(); 04365 if (caretNodeRef.isNull()) return; 04366 04367 NodeImpl *caretNode = caretNodeRef.handle(); 04368 // kdDebug(6200) << ": caretNode=" << caretNode << endl; 04369 long &offset = m_part->d->caretOffset(); 04370 04371 ElementImpl *baseElem = determineBaseElement(caretNode); 04372 CaretAdvancePolicy advpol = cmv != CaretByWord ? IndicatedFlows : LeafsOnly; 04373 LinearDocument ld(m_part, caretNode, offset, advpol, baseElem); 04374 04375 EditableCharacterIterator it(&ld); 04376 while (!it.isEnd() && count > 0) { 04377 count--; 04378 if (cmv == CaretByCharacter) { 04379 if (next) ++it; 04380 else --it; 04381 } else if (cmv == CaretByWord) { 04382 if (next) moveItToNextWord(it); 04383 else moveItToPrevWord(it); 04384 }/*end if*/ 04385 //kdDebug(6200) << "movecaret" << endl; 04386 }/*wend*/ 04387 CaretBox *hintBox = 0; // make gcc uninit warning disappear 04388 if (!it.isEnd()) { 04389 NodeImpl *node = caretNodeRef.handle(); 04390 hintBox = it.caretBox(); 04391 //kdDebug(6200) << "hintBox = " << hintBox << endl; 04392 //kdDebug(6200) << " outside " << hintBox->isOutside() << " outsideEnd " << hintBox->isOutsideEnd() << " r " << it.renderer() << " ofs " << it.offset() << " cb " << hintBox->containingBlock() << endl; 04393 mapRenderPosToDOMPos(it.renderer(), it.offset(), hintBox->isOutside(), 04394 hintBox->isOutsideEnd(), node, offset); 04395 //kdDebug(6200) << "mapRTD" << endl; 04396 caretNodeRef = node; 04397 #if DEBUG_CARETMODE > 2 04398 kdDebug(6200) << "set by valid node " << node << " " << (node?node->nodeName().string():QString::null) << " offset: " << offset << endl; 04399 #endif 04400 } else { 04401 offset = next ? caretNode->maxOffset() : caretNode->minOffset(); 04402 #if DEBUG_CARETMODE > 0 04403 kdDebug(6200) << "set by INvalid node. offset: " << offset << endl; 04404 #endif 04405 }/*end if*/ 04406 placeCaretOnChar(hintBox); 04407 } 04408 04409 void KHTMLView::placeCaretOnChar(CaretBox *hintBox) 04410 { 04411 caretOff(); 04412 recalcAndStoreCaretPos(hintBox); 04413 ensureVisible(d->m_caretViewContext->x, d->m_caretViewContext->y, 04414 d->m_caretViewContext->width, d->m_caretViewContext->height); 04415 d->m_caretViewContext->origX = d->m_caretViewContext->x; 04416 d->scrollBarMoved = false; 04417 #if DEBUG_CARETMODE > 3 04418 //if (caretNode->isTextNode()) kdDebug(6200) << "text[0] = " << (int)*((TextImpl *)caretNode)->data().unicode() << " text :\"" << ((TextImpl *)caretNode)->data().string() << "\"" << endl; 04419 #endif 04420 ensureNodeHasFocus(m_part->d->caretNode().handle()); 04421 caretOn(); 04422 } 04423 04424 void KHTMLView::moveCaretByPage(bool next) 04425 { 04426 Node &caretNodeRef = m_part->d->caretNode(); 04427 if (caretNodeRef.isNull()) return; 04428 04429 NodeImpl *caretNode = caretNodeRef.handle(); 04430 // kdDebug(6200) << ": caretNode=" << caretNode << endl; 04431 long offset = m_part->d->caretOffset(); 04432 04433 int offs = (clipper()->height() < 30) ? clipper()->height() : 30; 04434 // Minimum distance the caret must be moved 04435 int mindist = clipper()->height() - offs; 04436 04437 CaretViewContext *cv = d->caretViewContext(); 04438 // int y = cv->y; // we always measure the top border 04439 04440 ElementImpl *baseElem = determineBaseElement(caretNode); 04441 LinearDocument ld(m_part, caretNode, offset, LeafsOnly, baseElem); 04442 04443 ErgonomicEditableLineIterator it(ld.current(), cv->origX); 04444 04445 moveIteratorByPage(ld, it, mindist, next); 04446 04447 int x, absx, absy; 04448 CaretBox *caretBox = nearestCaretBox(it, d->m_caretViewContext, x, absx, absy); 04449 04450 placeCaretOnLine(caretBox, x, absx, absy); 04451 } 04452 04453 void KHTMLView::moveCaretPrevWord() 04454 { 04455 moveCaretBy(false, CaretByWord, 1); 04456 } 04457 04458 void KHTMLView::moveCaretNextWord() 04459 { 04460 moveCaretBy(true, CaretByWord, 1); 04461 } 04462 04463 void KHTMLView::moveCaretPrevLine(int n) 04464 { 04465 moveCaretByLine(false, n); 04466 } 04467 04468 void KHTMLView::moveCaretNextLine(int n) 04469 { 04470 moveCaretByLine(true, n); 04471 } 04472 04473 void KHTMLView::moveCaretPrevPage() 04474 { 04475 moveCaretByPage(false); 04476 } 04477 04478 void KHTMLView::moveCaretNextPage() 04479 { 04480 moveCaretByPage(true); 04481 } 04482 04483 void KHTMLView::moveCaretToLineBegin() 04484 { 04485 moveCaretToLineBoundary(false); 04486 } 04487 04488 void KHTMLView::moveCaretToLineEnd() 04489 { 04490 moveCaretToLineBoundary(true); 04491 } 04492 04493 #endif // KHTML_NO_CARET 04494 04495 #ifndef NO_SMOOTH_SCROLL_HACK 04496 #define timer timer2 04497 04498 // All scrolls must be completed within 240ms of last keypress 04499 static const int SCROLL_TIME = 240; 04500 // Each step is 20 ms == 50 frames/second 04501 static const int SCROLL_TICK = 20; 04502 04503 void KHTMLView::scrollBy(int dx, int dy) 04504 { 04505 KConfigGroup cfg( KGlobal::config(), "KDE" ); 04506 if( !cfg.readBoolEntry( "SmoothScrolling", true )) { 04507 QScrollView::scrollBy( dx, dy ); 04508 return; 04509 } 04510 // scrolling destination 04511 int full_dx = d->dx + dx; 04512 int full_dy = d->dy + dy; 04513 04514 // scrolling speed 04515 int ddx = 0; 04516 int ddy = 0; 04517 04518 int steps = SCROLL_TIME/SCROLL_TICK; 04519 04520 ddx = (full_dx*16)/steps; 04521 ddy = (full_dy*16)/steps; 04522 04523 // don't go under 1px/step 04524 if (ddx > 0 && ddx < 16) ddx = 16; 04525 if (ddy > 0 && ddy < 16) ddy = 16; 04526 if (ddx < 0 && ddx > -16) ddx = -16; 04527 if (ddy < 0 && ddy > -16) ddy = -16; 04528 04529 d->dx = full_dx; 04530 d->dy = full_dy; 04531 d->ddx = ddx; 04532 d->ddy = ddy; 04533 04534 if (!d->scrolling) { 04535 scrollTick(); 04536 startScrolling(); 04537 } 04538 } 04539 04540 void KHTMLView::scrollTick() { 04541 if (d->dx == 0 && d->dy == 0) { 04542 stopScrolling(); 04543 return; 04544 } 04545 04546 int tddx = d->ddx + d->rdx; 04547 int tddy = d->ddy + d->rdy; 04548 04549 int ddx = tddx / 16; 04550 int ddy = tddy / 16; 04551 d->rdx = tddx % 16; 04552 d->rdy = tddy % 16; 04553 04554 if (d->dx > 0 && ddx > d->dx) ddx = d->dx; 04555 else 04556 if (d->dx < 0 && ddx < d->dx) ddx = d->dx; 04557 04558 if (d->dy > 0 && ddy > d->dy) ddy = d->dy; 04559 else 04560 if (d->dy < 0 && ddy < d->dy) ddy = d->dy; 04561 04562 d->dx -= ddx; 04563 d->dy -= ddy; 04564 04565 // QScrollView::setContentsPos( contentsX() + ddx, contentsY() + ddy); 04566 QScrollView::scrollBy(ddx, ddy); 04567 } 04568 04569 void KHTMLView::startScrolling() 04570 { 04571 d->scrolling = true; 04572 d->timer.start(SCROLL_TICK, false); 04573 } 04574 04575 void KHTMLView::stopScrolling() 04576 { 04577 d->timer.stop(); 04578 d->dx = d->dy = 0; 04579 d->scrolling = false; 04580 } 04581 04582 // Overloaded from QScrollView and QScrollBar 04583 void KHTMLView::scrollViewWheelEvent( QWheelEvent *e ) 04584 { 04585 int pageStep = verticalScrollBar()->pageStep(); 04586 int lineStep = verticalScrollBar()->lineStep(); 04587 int step = QMIN( QApplication::wheelScrollLines()*lineStep, pageStep ); 04588 if ( ( e->state() & ControlButton ) || ( e->state() & ShiftButton ) ) 04589 step = pageStep; 04590 04591 if(e->orientation() == Horizontal) 04592 scrollBy(-((e->delta()*step)/120), 0); 04593 else if(e->orientation() == Vertical) 04594 scrollBy(0,-((e->delta()*step)/120)); 04595 04596 e->accept(); 04597 } 04598 04599 #undef timer 04600 04601 #endif // NO_SMOOTH_SCROLL_HACK 04602 04603 #undef DEBUG_CARETMODE