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