khtml Library API Documentation

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 * 00009 * This library is free software; you can redistribute it and/or 00010 * modify it under the terms of the GNU Library General Public 00011 * License as published by the Free Software Foundation; either 00012 * version 2 of the License, or (at your option) any later version. 00013 * 00014 * This library is distributed in the hope that it will be useful, 00015 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00016 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00017 * Library General Public License for more details. 00018 * 00019 * You should have received a copy of the GNU Library General Public License 00020 * along with this library; see the file COPYING.LIB. If not, write to 00021 * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, 00022 * Boston, MA 02111-1307, USA. 00023 */ 00024 00025 00026 #include "khtmlview.moc" 00027 00028 #include "khtmlview.h" 00029 00030 #include "khtml_part.h" 00031 #include "khtml_events.h" 00032 00033 #include "html/html_documentimpl.h" 00034 #include "html/html_inlineimpl.h" 00035 #include "html/html_formimpl.h" 00036 #include "rendering/render_arena.h" 00037 #include "rendering/render_canvas.h" 00038 #include "rendering/render_frames.h" 00039 #include "rendering/render_replaced.h" 00040 #include "rendering/render_layer.h" 00041 #include "rendering/render_line.h" 00042 #include "rendering/render_table.h" 00043 // removeme 00044 #define protected public 00045 #include "rendering/render_text.h" 00046 #undef protected 00047 #include "xml/dom2_eventsimpl.h" 00048 #include "css/cssstyleselector.h" 00049 #include "misc/htmlhashes.h" 00050 #include "misc/helper.h" 00051 #include "khtml_settings.h" 00052 #include "khtml_printsettings.h" 00053 00054 #include "khtmlpart_p.h" 00055 00056 #ifndef KHTML_NO_CARET 00057 #include "khtml_caret_p.h" 00058 #include "xml/dom2_rangeimpl.h" 00059 #endif 00060 00061 #include <kcursor.h> 00062 #include <knotifyclient.h> 00063 #include <ksimpleconfig.h> 00064 #include <kstringhandler.h> 00065 #include <kstandarddirs.h> 00066 #include <kprinter.h> 00067 #include <klocale.h> 00068 #include <kstdaccel.h> 00069 00070 #include <qtooltip.h> 00071 #include <qpainter.h> 00072 #include <qlabel.h> 00073 #include <qpaintdevicemetrics.h> 00074 #include <qstylesheet.h> 00075 #include <kapplication.h> 00076 00077 #include <kimageio.h> 00078 #include <kdebug.h> 00079 #include <kurldrag.h> 00080 #include <qobjectlist.h> 00081 #include <qtimer.h> 00082 #include <kdialogbase.h> 00083 #include <qptrdict.h> 00084 00085 00086 //#define DEBUG_NO_PAINT_BUFFER 00087 00088 //#define DEBUG_FLICKER 00089 00090 //#define DEBUG_PIXEL 00091 00092 #define PAINT_BUFFER_HEIGHT 128 00093 00094 #if 0 00095 namespace khtml { 00096 void dumpLineBoxes(RenderFlow *flow); 00097 } 00098 #endif 00099 00100 using namespace DOM; 00101 using namespace khtml; 00102 class KHTMLToolTip; 00103 00104 00105 #ifndef QT_NO_TOOLTIP 00106 00107 class KHTMLToolTip : public QToolTip 00108 { 00109 public: 00110 KHTMLToolTip(KHTMLView *view, KHTMLViewPrivate* vp) : QToolTip(view->viewport()) 00111 { 00112 m_view = view; 00113 m_viewprivate = vp; 00114 }; 00115 00116 protected: 00117 virtual void maybeTip(const QPoint &); 00118 00119 private: 00120 KHTMLView *m_view; 00121 KHTMLViewPrivate* m_viewprivate; 00122 }; 00123 00124 #endif 00125 00126 class KHTMLViewPrivate { 00127 friend class KHTMLToolTip; 00128 public: 00129 00130 enum PseudoFocusNodes { 00131 PFNone, 00132 PFTop, 00133 PFBottom 00134 }; 00135 00136 KHTMLViewPrivate() 00137 : underMouse( 0 ), underMouseNonShared( 0 ) 00138 { 00139 #ifndef KHTML_NO_CARET 00140 m_caretViewContext = 0; 00141 m_editorContext = 0; 00142 #endif // KHTML_NO_CARET 00143 postponed_autorepeat = NULL; 00144 reset(); 00145 tp=0; 00146 paintBuffer=0; 00147 vertPaintBuffer=0; 00148 formCompletions=0; 00149 prevScrollbarVisible = true; 00150 tooltip = 0; 00151 possibleTripleClick = false; 00152 } 00153 ~KHTMLViewPrivate() 00154 { 00155 delete formCompletions; 00156 delete tp; tp = 0; 00157 delete paintBuffer; paintBuffer =0; 00158 delete vertPaintBuffer; 00159 delete postponed_autorepeat; 00160 if (underMouse) 00161 underMouse->deref(); 00162 if (underMouseNonShared) 00163 underMouseNonShared->deref(); 00164 delete tooltip; 00165 #ifndef KHTML_NO_CARET 00166 delete m_caretViewContext; 00167 delete m_editorContext; 00168 #endif // KHTML_NO_CARET 00169 } 00170 void reset() 00171 { 00172 if (underMouse) 00173 underMouse->deref(); 00174 underMouse = 0; 00175 if (underMouseNonShared) 00176 underMouseNonShared->deref(); 00177 underMouseNonShared = 0; 00178 linkPressed = false; 00179 useSlowRepaints = false; 00180 tabMovePending = false; 00181 lastTabbingDirection = true; 00182 pseudoFocusNode = PFNone; 00183 #ifndef KHTML_NO_SCROLLBARS 00184 vmode = QScrollView::Auto; 00185 hmode = QScrollView::Auto; 00186 #else 00187 vmode = QScrollView::AlwaysOff; 00188 hmode = QScrollView::AlwaysOff; 00189 #endif 00190 #ifdef DEBUG_PIXEL 00191 timer.start(); 00192 pixelbooth = 0; 00193 repaintbooth = 0; 00194 #endif 00195 scrollBarMoved = false; 00196 ignoreWheelEvents = false; 00197 borderX = 30; 00198 borderY = 30; 00199 clickX = -1; 00200 clickY = -1; 00201 prevMouseX = -1; 00202 prevMouseY = -1; 00203 clickCount = 0; 00204 isDoubleClick = false; 00205 scrollingSelf = false; 00206 delete postponed_autorepeat; 00207 postponed_autorepeat = NULL; 00208 layoutTimerId = 0; 00209 repaintTimerId = 0; 00210 scrollTimerId = 0; 00211 scrollSuspended = false; 00212 scrollSuspendPreActivate = false; 00213 complete = false; 00214 firstRelayout = true; 00215 dirtyLayout = false; 00216 layoutSchedulingEnabled = true; 00217 updateRegion = QRegion(); 00218 m_dialogsAllowed = true; 00219 #ifndef KHTML_NO_CARET 00220 if (m_caretViewContext) { 00221 m_caretViewContext->caretMoved = false; 00222 m_caretViewContext->keyReleasePending = false; 00223 }/*end if*/ 00224 #endif // KHTML_NO_CARET 00225 #ifndef KHTML_NO_TYPE_AHEAD_FIND 00226 typeAheadActivated = false; 00227 #endif // KHTML_NO_TYPE_AHEAD_FIND 00228 accessKeysActivated = false; 00229 accessKeysPreActivate = false; 00230 } 00231 void newScrollTimer(QWidget *view, int tid) 00232 { 00233 //kdDebug(6000) << "newScrollTimer timer " << tid << endl; 00234 view->killTimer(scrollTimerId); 00235 scrollTimerId = tid; 00236 scrollSuspended = false; 00237 } 00238 enum ScrollDirection { ScrollLeft, ScrollRight, ScrollUp, ScrollDown }; 00239 00240 void adjustScroller(QWidget *view, ScrollDirection direction, ScrollDirection oppositedir) 00241 { 00242 static const struct { int msec, pixels; } timings [] = { 00243 {320,1}, {224,1}, {160,1}, {112,1}, {80,1}, {56,1}, {40,1}, 00244 {28,1}, {20,1}, {20,2}, {20,3}, {20,4}, {20,6}, {20,8}, {0,0} 00245 }; 00246 if (!scrollTimerId || 00247 (scrollDirection != direction && 00248 (scrollDirection != oppositedir || scrollSuspended))) { 00249 scrollTiming = 6; 00250 scrollBy = timings[scrollTiming].pixels; 00251 scrollDirection = direction; 00252 newScrollTimer(view, view->startTimer(timings[scrollTiming].msec)); 00253 } else if (scrollDirection == direction && 00254 timings[scrollTiming+1].msec && !scrollSuspended) { 00255 scrollBy = timings[++scrollTiming].pixels; 00256 newScrollTimer(view, view->startTimer(timings[scrollTiming].msec)); 00257 } else if (scrollDirection == oppositedir) { 00258 if (scrollTiming) { 00259 scrollBy = timings[--scrollTiming].pixels; 00260 newScrollTimer(view, view->startTimer(timings[scrollTiming].msec)); 00261 } 00262 } 00263 scrollSuspended = false; 00264 } 00265 00266 #ifndef KHTML_NO_CARET 00267 00270 CaretViewContext *caretViewContext() { 00271 if (!m_caretViewContext) m_caretViewContext = new CaretViewContext(); 00272 return m_caretViewContext; 00273 } 00277 EditorContext *editorContext() { 00278 if (!m_editorContext) m_editorContext = new EditorContext(); 00279 return m_editorContext; 00280 } 00281 #endif // KHTML_NO_CARET 00282 00283 #ifdef DEBUG_PIXEL 00284 QTime timer; 00285 unsigned int pixelbooth; 00286 unsigned int repaintbooth; 00287 #endif 00288 00289 QPainter *tp; 00290 QPixmap *paintBuffer; 00291 QPixmap *vertPaintBuffer; 00292 NodeImpl *underMouse; 00293 NodeImpl *underMouseNonShared; 00294 00295 bool tabMovePending:1; 00296 bool lastTabbingDirection:1; 00297 PseudoFocusNodes pseudoFocusNode:2; 00298 bool scrollBarMoved:1; 00299 00300 QScrollView::ScrollBarMode vmode; 00301 QScrollView::ScrollBarMode hmode; 00302 bool prevScrollbarVisible; 00303 bool linkPressed; 00304 bool useSlowRepaints; 00305 bool ignoreWheelEvents; 00306 00307 int borderX, borderY; 00308 KSimpleConfig *formCompletions; 00309 00310 int clickX, clickY, clickCount; 00311 bool isDoubleClick; 00312 00313 int prevMouseX, prevMouseY; 00314 bool scrollingSelf; 00315 int layoutTimerId; 00316 QKeyEvent* postponed_autorepeat; 00317 00318 int repaintTimerId; 00319 int scrollTimerId; 00320 bool scrollSuspended; 00321 bool scrollSuspendPreActivate; 00322 int scrollTiming; 00323 int scrollBy; 00324 ScrollDirection scrollDirection; 00325 bool complete; 00326 bool firstRelayout; 00327 bool layoutSchedulingEnabled; 00328 bool possibleTripleClick; 00329 bool dirtyLayout; 00330 bool m_dialogsAllowed; 00331 QRegion updateRegion; 00332 KHTMLToolTip *tooltip; 00333 QPtrDict<QWidget> visibleWidgets; 00334 #ifndef KHTML_NO_CARET 00335 CaretViewContext *m_caretViewContext; 00336 EditorContext *m_editorContext; 00337 #endif // KHTML_NO_CARET 00338 #ifndef KHTML_NO_TYPE_AHEAD_FIND 00339 QString findString; 00340 QTimer timer; 00341 bool findLinksOnly; 00342 bool typeAheadActivated; 00343 #endif // KHTML_NO_TYPE_AHEAD_FIND 00344 bool accessKeysActivated; 00345 bool accessKeysPreActivate; 00346 }; 00347 00348 #ifndef QT_NO_TOOLTIP 00349 00359 static bool findImageMapRect(HTMLImageElementImpl *img, const QPoint &scrollOfs, 00360 const QPoint &p, QRect &r, QString &s) 00361 { 00362 HTMLMapElementImpl* map; 00363 if (img && img->getDocument()->isHTMLDocument() && 00364 (map = static_cast<HTMLDocumentImpl*>(img->getDocument())->getMap(img->imageMap()))) { 00365 RenderObject::NodeInfo info(true, false); 00366 RenderObject *rend = img->renderer(); 00367 int ax, ay; 00368 if (!rend || !rend->absolutePosition(ax, ay)) 00369 return false; 00370 // we're a client side image map 00371 bool inside = map->mapMouseEvent(p.x() - ax + scrollOfs.x(), 00372 p.y() - ay + scrollOfs.y(), rend->contentWidth(), 00373 rend->contentHeight(), info); 00374 if (inside && info.URLElement()) { 00375 HTMLAreaElementImpl *area = static_cast<HTMLAreaElementImpl *>(info.URLElement()); 00376 Q_ASSERT(area->id() == ID_AREA); 00377 s = area->getAttribute(ATTR_TITLE).string(); 00378 QRegion reg = area->cachedRegion(); 00379 if (!s.isEmpty() && !reg.isEmpty()) { 00380 r = reg.boundingRect(); 00381 r.moveBy(ax, ay); 00382 return true; 00383 } 00384 } 00385 } 00386 return false; 00387 } 00388 00389 void KHTMLToolTip::maybeTip(const QPoint& p) 00390 { 00391 DOM::NodeImpl *node = m_viewprivate->underMouseNonShared; 00392 QRect region; 00393 while ( node ) { 00394 if ( node->isElementNode() ) { 00395 DOM::ElementImpl *e = static_cast<DOM::ElementImpl*>( node ); 00396 QRect r; 00397 QString s; 00398 bool found = false; 00399 // for images, check if it is part of a client-side image map, 00400 // and query the <area>s' title attributes, too 00401 if (e->id() == ID_IMG && !e->getAttribute( ATTR_USEMAP ).isEmpty()) { 00402 found = findImageMapRect(static_cast<HTMLImageElementImpl *>(e), 00403 m_view->viewportToContents(QPoint(0, 0)), p, r, s); 00404 } 00405 if (!found) { 00406 s = e->getAttribute( ATTR_TITLE ).string(); 00407 r = node->getRect(); 00408 } 00409 region |= QRect( m_view->contentsToViewport( r.topLeft() ), r.size() ); 00410 if ( !s.isEmpty() ) { 00411 tip( region, QStyleSheet::convertFromPlainText( s, QStyleSheetItem::WhiteSpaceNormal ) ); 00412 break; 00413 } 00414 } 00415 node = node->parentNode(); 00416 } 00417 } 00418 #endif 00419 00420 KHTMLView::KHTMLView( KHTMLPart *part, QWidget *parent, const char *name) 00421 : QScrollView( parent, name, WResizeNoErase | WRepaintNoErase ) 00422 { 00423 m_medium = "screen"; 00424 00425 m_part = part; 00426 d = new KHTMLViewPrivate; 00427 QScrollView::setVScrollBarMode(d->vmode); 00428 QScrollView::setHScrollBarMode(d->hmode); 00429 connect(kapp, SIGNAL(kdisplayPaletteChanged()), this, SLOT(slotPaletteChanged())); 00430 connect(this, SIGNAL(contentsMoving(int, int)), this, SLOT(slotScrollBarMoved())); 00431 00432 // initialize QScrollView 00433 enableClipper(true); 00434 // hack to get unclipped painting on the viewport. 00435 static_cast<KHTMLView *>(static_cast<QWidget *>(viewport()))->setWFlags(WPaintUnclipped); 00436 00437 setResizePolicy(Manual); 00438 viewport()->setMouseTracking(true); 00439 viewport()->setBackgroundMode(NoBackground); 00440 00441 KImageIO::registerFormats(); 00442 00443 #ifndef QT_NO_TOOLTIP 00444 d->tooltip = new KHTMLToolTip( this, d ); 00445 #endif 00446 00447 #ifndef KHTML_NO_TYPE_AHEAD_FIND 00448 connect(&d->timer, SIGNAL(timeout()), this, SLOT(findTimeout())); 00449 #endif // KHTML_NO_TYPE_AHEAD_FIND 00450 00451 init(); 00452 00453 viewport()->show(); 00454 } 00455 00456 KHTMLView::~KHTMLView() 00457 { 00458 closeChildDialogs(); 00459 if (m_part) 00460 { 00461 //WABA: Is this Ok? Do I need to deref it as well? 00462 //Does this need to be done somewhere else? 00463 DOM::DocumentImpl *doc = m_part->xmlDocImpl(); 00464 if (doc) 00465 doc->detach(); 00466 } 00467 delete d; d = 0; 00468 } 00469 00470 void KHTMLView::init() 00471 { 00472 if(!d->paintBuffer) d->paintBuffer = new QPixmap(PAINT_BUFFER_HEIGHT, PAINT_BUFFER_HEIGHT); 00473 if(!d->vertPaintBuffer) 00474 d->vertPaintBuffer = new QPixmap(10, PAINT_BUFFER_HEIGHT); 00475 if(!d->tp) d->tp = new QPainter(); 00476 00477 setFocusPolicy(QWidget::StrongFocus); 00478 viewport()->setFocusProxy(this); 00479 00480 _marginWidth = -1; // undefined 00481 _marginHeight = -1; 00482 _width = 0; 00483 _height = 0; 00484 00485 installEventFilter(this); 00486 00487 setAcceptDrops(true); 00488 QSize s = viewportSize(4095, 4095); 00489 resizeContents(s.width(), s.height()); 00490 } 00491 00492 void KHTMLView::clear() 00493 { 00494 // work around QScrollview's unbelievable bugginess 00495 setStaticBackground(true); 00496 #ifndef KHTML_NO_CARET 00497 if (!m_part->isCaretMode() && !m_part->isEditable()) caretOff(); 00498 #endif 00499 00500 if( d->typeAheadActivated ) 00501 findTimeout(); 00502 if (d->accessKeysActivated) 00503 accessKeysTimeout(); 00504 d->reset(); 00505 killTimers(); 00506 emit cleared(); 00507 00508 QScrollView::setHScrollBarMode(d->hmode); 00509 QScrollView::setVScrollBarMode(d->vmode); 00510 } 00511 00512 void KHTMLView::hideEvent(QHideEvent* e) 00513 { 00514 QScrollView::hideEvent(e); 00515 } 00516 00517 void KHTMLView::showEvent(QShowEvent* e) 00518 { 00519 QScrollView::showEvent(e); 00520 } 00521 00522 void KHTMLView::resizeEvent (QResizeEvent* e) 00523 { 00524 int dw = e->oldSize().width() - e->size().width(); 00525 int dh = e->oldSize().height() - e->size().height(); 00526 00527 // if we are shrinking the view, don't allow the content to overflow 00528 // before the layout occurs - we don't know if we need scrollbars yet 00529 dw = dw>0 ? kMax(0, contentsWidth()-dw) : contentsWidth(); 00530 dh = dh>0 ? kMax(0, contentsHeight()-dh) : contentsHeight(); 00531 00532 resizeContents(dw, dh); 00533 00534 QScrollView::resizeEvent(e); 00535 00536 if ( m_part && m_part->xmlDocImpl() ) 00537 m_part->xmlDocImpl()->dispatchWindowEvent( EventImpl::RESIZE_EVENT, false, false ); 00538 } 00539 00540 void KHTMLView::viewportResizeEvent (QResizeEvent* e) 00541 { 00542 QScrollView::viewportResizeEvent(e); 00543 00544 //int w = visibleWidth(); 00545 //int h = visibleHeight(); 00546 00547 if (d->layoutSchedulingEnabled) 00548 layout(); 00549 #ifndef KHTML_NO_CARET 00550 else { 00551 hideCaret(); 00552 recalcAndStoreCaretPos(); 00553 showCaret(); 00554 }/*end if*/ 00555 #endif 00556 00557 KApplication::sendPostedEvents(viewport(), QEvent::Paint); 00558 } 00559 00560 // this is to get rid of a compiler virtual overload mismatch warning. do not remove 00561 void KHTMLView::drawContents( QPainter*) 00562 { 00563 } 00564 00565 void KHTMLView::drawContents( QPainter *p, int ex, int ey, int ew, int eh ) 00566 { 00567 #ifdef DEBUG_PIXEL 00568 00569 if ( d->timer.elapsed() > 5000 ) { 00570 qDebug( "drawed %d pixels in %d repaints the last %d milliseconds", 00571 d->pixelbooth, d->repaintbooth, d->timer.elapsed() ); 00572 d->timer.restart(); 00573 d->pixelbooth = 0; 00574 d->repaintbooth = 0; 00575 } 00576 d->pixelbooth += ew*eh; 00577 d->repaintbooth++; 00578 #endif 00579 00580 //kdDebug( 6000 ) << "drawContents this="<< this <<" x=" << ex << ",y=" << ey << ",w=" << ew << ",h=" << eh << endl; 00581 if(!m_part || !m_part->xmlDocImpl() || !m_part->xmlDocImpl()->renderer()) { 00582 p->fillRect(ex, ey, ew, eh, palette().active().brush(QColorGroup::Base)); 00583 return; 00584 } 00585 00586 QPoint pt = contentsToViewport(QPoint(ex, ey)); 00587 QRegion cr = QRect(pt.x(), pt.y(), ew, eh); 00588 // kdDebug(6000) << "clip rect: " << QRect(pt.x(), pt.y(), ew, eh) << endl; 00589 for (QPtrDictIterator<QWidget> it(d->visibleWidgets); it.current(); ++it) { 00590 QWidget *w = it.current(); 00591 RenderWidget* rw = static_cast<RenderWidget*>( it.currentKey() ); 00592 QScrollView *sv = ::qt_cast<QScrollView *>(w); 00593 if (sv || !rw->isFormElement()) { 00594 // kdDebug(6000) << " removing scrollview " << sv; 00595 int x, y; 00596 rw->absolutePosition(x, y); 00597 contentsToViewport(x, y, x, y); 00598 cr -= QRect(x, y, rw->width(), rw->height()); 00599 } 00600 } 00601 00602 #if 0 00603 // this is commonly the case with framesets. we still do 00604 // want to paint them, otherwise the widgets don't get placed. 00605 if (cr.isEmpty()) 00606 return; 00607 #endif 00608 00609 #ifndef DEBUG_NO_PAINT_BUFFER 00610 p->setClipRegion(cr); 00611 00612 if (eh > PAINT_BUFFER_HEIGHT && ew <= 10) { 00613 if ( d->vertPaintBuffer->height() < visibleHeight() ) 00614 d->vertPaintBuffer->resize(10, visibleHeight()); 00615 d->tp->begin(d->vertPaintBuffer); 00616 d->tp->translate(-ex, -ey); 00617 d->tp->fillRect(ex, ey, ew, eh, palette().active().brush(QColorGroup::Base)); 00618 m_part->xmlDocImpl()->renderer()->layer()->paint(d->tp, QRect(ex, ey, ew, eh)); 00619 d->tp->end(); 00620 p->drawPixmap(ex, ey, *d->vertPaintBuffer, 0, 0, ew, eh); 00621 } 00622 else { 00623 if ( d->paintBuffer->width() < visibleWidth() ) 00624 d->paintBuffer->resize(visibleWidth(),PAINT_BUFFER_HEIGHT); 00625 00626 int py=0; 00627 while (py < eh) { 00628 int ph = eh-py < PAINT_BUFFER_HEIGHT ? eh-py : PAINT_BUFFER_HEIGHT; 00629 d->tp->begin(d->paintBuffer); 00630 d->tp->translate(-ex, -ey-py); 00631 d->tp->fillRect(ex, ey+py, ew, ph, palette().active().brush(QColorGroup::Base)); 00632 m_part->xmlDocImpl()->renderer()->layer()->paint(d->tp, QRect(ex, ey+py, ew, ph)); 00633 d->tp->end(); 00634 00635 p->drawPixmap(ex, ey+py, *d->paintBuffer, 0, 0, ew, ph); 00636 py += PAINT_BUFFER_HEIGHT; 00637 } 00638 } 00639 #else // !DEBUG_NO_PAINT_BUFFER 00640 static int cnt=0; 00641 ex = contentsX(); ey = contentsY(); 00642 ew = visibleWidth(); eh = visibleHeight(); 00643 QRect pr(ex,ey,ew,eh); 00644 kdDebug() << "[" << ++cnt << "]" << " clip region: " << pr << endl; 00645 // p->setClipRegion(QRect(0,0,ew,eh)); 00646 // p->translate(-ex, -ey); 00647 p->fillRect(ex, ey, ew, eh, palette().active().brush(QColorGroup::Base)); 00648 m_part->xmlDocImpl()->renderer()->layer()->paint(p, pr); 00649 #endif // DEBUG_NO_PAINT_BUFFER 00650 00651 #ifndef KHTML_NO_CARET 00652 if (d->m_caretViewContext && d->m_caretViewContext->visible) { 00653 QRect pos(d->m_caretViewContext->x, d->m_caretViewContext->y, 00654 d->m_caretViewContext->width, d->m_caretViewContext->height); 00655 if (pos.intersects(QRect(ex, ey, ew, eh))) { 00656 p->setRasterOp(XorROP); 00657 p->setPen(white); 00658 if (pos.width() == 1) 00659 p->drawLine(pos.topLeft(), pos.bottomRight()); 00660 else { 00661 p->fillRect(pos, white); 00662 }/*end if*/ 00663 }/*end if*/ 00664 }/*end if*/ 00665 #endif // KHTML_NO_CARET 00666 00667 // p->setPen(QPen(magenta,0,DashDotDotLine)); 00668 // p->drawRect(dbg_paint_rect); 00669 00670 khtml::DrawContentsEvent event( p, ex, ey, ew, eh ); 00671 QApplication::sendEvent( m_part, &event ); 00672 00673 } 00674 00675 void KHTMLView::setMarginWidth(int w) 00676 { 00677 // make it update the rendering area when set 00678 _marginWidth = w; 00679 } 00680 00681 void KHTMLView::setMarginHeight(int h) 00682 { 00683 // make it update the rendering area when set 00684 _marginHeight = h; 00685 } 00686 00687 void KHTMLView::layout() 00688 { 00689 if( m_part && m_part->xmlDocImpl() ) { 00690 DOM::DocumentImpl *document = m_part->xmlDocImpl(); 00691 00692 khtml::RenderCanvas* root = static_cast<khtml::RenderCanvas *>(document->renderer()); 00693 if ( !root ) return; 00694 00695 d->layoutSchedulingEnabled=false; 00696 00697 if (document->isHTMLDocument()) { 00698 NodeImpl *body = static_cast<HTMLDocumentImpl*>(document)->body(); 00699 if(body && body->renderer() && body->id() == ID_FRAMESET) { 00700 QScrollView::setVScrollBarMode(AlwaysOff); 00701 QScrollView::setHScrollBarMode(AlwaysOff); 00702 body->renderer()->setLayouted(false); 00703 // if (d->tooltip) { 00704 // delete d->tooltip; 00705 // d->tooltip = 0; 00706 // } 00707 } 00708 else if (!d->tooltip) 00709 d->tooltip = new KHTMLToolTip( this, d ); 00710 } 00711 00712 _height = visibleHeight(); 00713 _width = visibleWidth(); 00714 //QTime qt; 00715 //qt.start(); 00716 root->setMinMaxKnown(false); 00717 root->setLayouted(false); 00718 root->layout(); 00719 00720 emit finishedLayout(); 00721 #if 0 00722 ElementImpl *listitem = m_part->xmlDocImpl()->getElementById("__test_element__"); 00723 if (listitem) kdDebug(6000) << "after layout, before repaint" << endl; 00724 if (listitem) dumpLineBoxes(static_cast<RenderFlow *>(listitem->renderer())); 00725 #endif 00726 #ifndef KHTML_NO_CARET 00727 hideCaret(); 00728 if ((m_part->isCaretMode() || m_part->isEditable()) 00729 && !d->complete && d->m_caretViewContext 00730 && !d->m_caretViewContext->caretMoved) { 00731 initCaret(); 00732 } else { 00733 recalcAndStoreCaretPos(); 00734 showCaret(); 00735 }/*end if*/ 00736 #endif 00737 root->repaint(); 00738 if (d->accessKeysActivated) { 00739 emit hideAccessKeys(); 00740 displayAccessKeys(); 00741 } 00742 //kdDebug( 6000 ) << "TIME: layout() dt=" << qt.elapsed() << endl; 00743 } 00744 else 00745 _width = visibleWidth(); 00746 00747 killTimer(d->layoutTimerId); 00748 d->layoutTimerId = 0; 00749 d->layoutSchedulingEnabled=true; 00750 } 00751 00752 void KHTMLView::closeChildDialogs() 00753 { 00754 QObjectList *dlgs = queryList("QDialog"); 00755 for (QObject *dlg = dlgs->first(); dlg; dlg = dlgs->next()) 00756 { 00757 KDialogBase* dlgbase = dynamic_cast<KDialogBase *>( dlg ); 00758 if ( dlgbase ) { 00759 kdDebug(6000) << "closeChildDialogs: closing dialog " << dlgbase << endl; 00760 // close() ends up calling QButton::animateClick, which isn't immediate 00761 // we need something the exits the event loop immediately (#49068) 00762 dlgbase->cancel(); 00763 } 00764 else 00765 { 00766 kdWarning() << "closeChildDialogs: not a KDialogBase! Don't use QDialogs in KDE! " << static_cast<QWidget*>(dlg) << endl; 00767 static_cast<QWidget*>(dlg)->hide(); 00768 } 00769 } 00770 delete dlgs; 00771 d->m_dialogsAllowed = false; 00772 } 00773 00774 bool KHTMLView::dialogsAllowed() { 00775 bool allowed = d->m_dialogsAllowed; 00776 KHTMLPart* p = m_part->parentPart(); 00777 if (p && p->view()) 00778 allowed &= p->view()->dialogsAllowed(); 00779 return allowed; 00780 } 00781 00782 void KHTMLView::closeEvent( QCloseEvent* ev ) 00783 { 00784 closeChildDialogs(); 00785 QScrollView::closeEvent( ev ); 00786 } 00787 00788 // 00789 // Event Handling 00790 // 00792 00793 void KHTMLView::viewportMousePressEvent( QMouseEvent *_mouse ) 00794 { 00795 if(!m_part->xmlDocImpl()) return; 00796 if (d->possibleTripleClick) 00797 { 00798 viewportMouseDoubleClickEvent( _mouse ); // it handles triple clicks too 00799 return; 00800 } 00801 00802 int xm, ym; 00803 viewportToContents(_mouse->x(), _mouse->y(), xm, ym); 00804 //kdDebug( 6000 ) << "mousePressEvent: viewport=("<<_mouse->x()<<"/"<<_mouse->y()<<"), contents=(" << xm << "/" << ym << ")\n"; 00805 00806 d->isDoubleClick = false; 00807 00808 DOM::NodeImpl::MouseEvent mev( _mouse->stateAfter(), DOM::NodeImpl::MousePress ); 00809 m_part->xmlDocImpl()->prepareMouseEvent( false, xm, ym, &mev ); 00810 00811 //kdDebug(6000) << "innerNode="<<mev.innerNode.nodeName().string()<<endl; 00812 00813 if (d->clickCount > 0 && 00814 QPoint(d->clickX-xm,d->clickY-ym).manhattanLength() <= QApplication::startDragDistance()) 00815 d->clickCount++; 00816 else { 00817 d->clickCount = 1; 00818 d->clickX = xm; 00819 d->clickY = ym; 00820 } 00821 00822 bool swallowEvent = dispatchMouseEvent(EventImpl::MOUSEDOWN_EVENT,mev.innerNode.handle(),mev.innerNonSharedNode.handle(),true, 00823 d->clickCount,_mouse,true,DOM::NodeImpl::MousePress); 00824 00825 khtml::RenderObject* r = mev.innerNode.handle() ? mev.innerNode.handle()->renderer() : 0; 00826 if (r && r->isWidget()) 00827 _mouse->ignore(); 00828 00829 if (!swallowEvent) { 00830 emit m_part->nodeActivated(mev.innerNode); 00831 00832 khtml::MousePressEvent event( _mouse, xm, ym, mev.url, mev.target, mev.innerNode ); 00833 QApplication::sendEvent( m_part, &event ); 00834 // we might be deleted after this 00835 } 00836 } 00837 00838 void KHTMLView::viewportMouseDoubleClickEvent( QMouseEvent *_mouse ) 00839 { 00840 if(!m_part->xmlDocImpl()) return; 00841 00842 int xm, ym; 00843 viewportToContents(_mouse->x(), _mouse->y(), xm, ym); 00844 00845 kdDebug( 6000 ) << "mouseDblClickEvent: x=" << xm << ", y=" << ym << endl; 00846 00847 d->isDoubleClick = true; 00848 00849 DOM::NodeImpl::MouseEvent mev( _mouse->stateAfter(), DOM::NodeImpl::MouseDblClick ); 00850 m_part->xmlDocImpl()->prepareMouseEvent( false, xm, ym, &mev ); 00851 00852 // We do the same thing as viewportMousePressEvent() here, since the DOM does not treat 00853 // single and double-click events as separate (only the detail, i.e. number of clicks differs) 00854 if (d->clickCount > 0 && 00855 QPoint(d->clickX-xm,d->clickY-ym).manhattanLength() <= QApplication::startDragDistance()) 00856 d->clickCount++; 00857 else { // shouldn't happen, if Qt has the same criterias for double clicks. 00858 d->clickCount = 1; 00859 d->clickX = xm; 00860 d->clickY = ym; 00861 } 00862 bool swallowEvent = dispatchMouseEvent(EventImpl::MOUSEDOWN_EVENT,mev.innerNode.handle(),mev.innerNonSharedNode.handle(),true, 00863 d->clickCount,_mouse,true,DOM::NodeImpl::MouseDblClick); 00864 00865 khtml::RenderObject* r = mev.innerNode.handle() ? mev.innerNode.handle()->renderer() : 0; 00866 if (r && r->isWidget()) 00867 _mouse->ignore(); 00868 00869 if (!swallowEvent) { 00870 khtml::MouseDoubleClickEvent event( _mouse, xm, ym, mev.url, mev.target, mev.innerNode, d->clickCount ); 00871 QApplication::sendEvent( m_part, &event ); 00872 } 00873 00874 d->possibleTripleClick=true; 00875 QTimer::singleShot(QApplication::doubleClickInterval(),this,SLOT(tripleClickTimeout())); 00876 } 00877 00878 void KHTMLView::tripleClickTimeout() 00879 { 00880 d->possibleTripleClick = false; 00881 d->clickCount = 0; 00882 } 00883 00884 static inline void forwardPeripheralEvent(khtml::RenderWidget* r, QMouseEvent* me, int x, int y) 00885 { 00886 int absx = 0; 00887 int absy = 0; 00888 r->absolutePosition(absx, absy); 00889 QPoint p(x-absx, y-absy); 00890 QMouseEvent fw(me->type(), p, me->button(), me->state()); 00891 QWidget* w = r->widget(); 00892 if(w) 00893 static_cast<khtml::RenderWidget::EventPropagator*>(w)->sendEvent(&fw); 00894 } 00895 00896 void KHTMLView::viewportMouseMoveEvent( QMouseEvent * _mouse ) 00897 { 00898 00899 if(!m_part->xmlDocImpl()) return; 00900 00901 int xm, ym; 00902 viewportToContents(_mouse->x(), _mouse->y(), xm, ym); 00903 00904 DOM::NodeImpl::MouseEvent mev( _mouse->stateAfter(), DOM::NodeImpl::MouseMove ); 00905 // Do not modify :hover/:active state while mouse is pressed. 00906 m_part->xmlDocImpl()->prepareMouseEvent( _mouse->state() & Qt::MouseButtonMask /*readonly ?*/, xm, ym, &mev ); 00907 00908 // kdDebug(6000) << "mouse move: " << _mouse->pos() 00909 // << " button " << _mouse->button() 00910 // << " state " << _mouse->state() << endl; 00911 00912 bool swallowEvent = dispatchMouseEvent(EventImpl::MOUSEMOVE_EVENT,mev.innerNode.handle(),mev.innerNonSharedNode.handle(),false, 00913 0,_mouse,true,DOM::NodeImpl::MouseMove); 00914 00915 if (d->clickCount > 0 && 00916 QPoint(d->clickX-xm,d->clickY-ym).manhattanLength() > QApplication::startDragDistance()) { 00917 d->clickCount = 0; // moving the mouse outside the threshold invalidates the click 00918 } 00919 00920 // execute the scheduled script. This is to make sure the mouseover events come after the mouseout events 00921 m_part->executeScheduledScript(); 00922 00923 DOM::NodeImpl* fn = m_part->xmlDocImpl()->focusNode(); 00924 if (fn && fn != mev.innerNode.handle() && 00925 fn->renderer() && fn->renderer()->isWidget()) { 00926 forwardPeripheralEvent(static_cast<khtml::RenderWidget*>(fn->renderer()), _mouse, xm, ym); 00927 } 00928 00929 khtml::RenderObject* r = mev.innerNode.handle() ? mev.innerNode.handle()->renderer() : 0; 00930 khtml::RenderStyle* style = (r && r->style()) ? r->style() : 0; 00931 QCursor c; 00932 switch ( style ? style->cursor() : CURSOR_AUTO) { 00933 case CURSOR_AUTO: 00934 if ( r && r->isText() ) 00935 c = KCursor::ibeamCursor(); 00936 00937 if ( mev.url.length() && m_part->settings()->changeCursor() ) 00938 c = m_part->urlCursor(); 00939 00940 if (r && r->isFrameSet() && !static_cast<RenderFrameSet*>(r)->noResize()) 00941 c = QCursor(static_cast<RenderFrameSet*>(r)->cursorShape()); 00942 00943 break; 00944 case CURSOR_CROSS: 00945 c = KCursor::crossCursor(); 00946 break; 00947 case CURSOR_POINTER: 00948 c = m_part->urlCursor(); 00949 break; 00950 case CURSOR_PROGRESS: 00951 c = KCursor::workingCursor(); 00952 break; 00953 case CURSOR_MOVE: 00954 c = KCursor::sizeAllCursor(); 00955 break; 00956 case CURSOR_E_RESIZE: 00957 case CURSOR_W_RESIZE: 00958 c = KCursor::sizeHorCursor(); 00959 break; 00960 case CURSOR_N_RESIZE: 00961 case CURSOR_S_RESIZE: 00962 c = KCursor::sizeVerCursor(); 00963 break; 00964 case CURSOR_NE_RESIZE: 00965 case CURSOR_SW_RESIZE: 00966 c = KCursor::sizeBDiagCursor(); 00967 break; 00968 case CURSOR_NW_RESIZE: 00969 case CURSOR_SE_RESIZE: 00970 c = KCursor::sizeFDiagCursor(); 00971 break; 00972 case CURSOR_TEXT: 00973 c = KCursor::ibeamCursor(); 00974 break; 00975 case CURSOR_WAIT: 00976 c = KCursor::waitCursor(); 00977 break; 00978 case CURSOR_HELP: 00979 c = KCursor::whatsThisCursor(); 00980 break; 00981 case CURSOR_DEFAULT: 00982 break; 00983 } 00984 00985 if ( viewport()->cursor().handle() != c.handle() ) { 00986 if( c.handle() == KCursor::arrowCursor().handle()) { 00987 for (KHTMLPart* p = m_part; p; p = p->parentPart()) 00988 p->view()->viewport()->unsetCursor(); 00989 } 00990 else { 00991 viewport()->setCursor( c ); 00992 } 00993 } 00994 if (r && r->isWidget()) { 00995 _mouse->ignore(); 00996 } 00997 00998 00999 d->prevMouseX = xm; 01000 d->prevMouseY = ym; 01001 01002 if (!swallowEvent) { 01003 khtml::MouseMoveEvent event( _mouse, xm, ym, mev.url, mev.target, mev.innerNode ); 01004 QApplication::sendEvent( m_part, &event ); 01005 } 01006 } 01007 01008 void KHTMLView::viewportMouseReleaseEvent( QMouseEvent * _mouse ) 01009 { 01010 if ( !m_part->xmlDocImpl() ) return; 01011 01012 int xm, ym; 01013 viewportToContents(_mouse->x(), _mouse->y(), xm, ym); 01014 01015 DOM::NodeImpl::MouseEvent mev( _mouse->stateAfter(), DOM::NodeImpl::MouseRelease ); 01016 m_part->xmlDocImpl()->prepareMouseEvent( false, xm, ym, &mev ); 01017 01018 bool swallowEvent = dispatchMouseEvent(EventImpl::MOUSEUP_EVENT,mev.innerNode.handle(),mev.innerNonSharedNode.handle(),true, 01019 d->clickCount,_mouse,false,DOM::NodeImpl::MouseRelease); 01020 01021 if (d->clickCount > 0 && 01022 QPoint(d->clickX-xm,d->clickY-ym).manhattanLength() <= QApplication::startDragDistance()) { 01023 QMouseEvent me(d->isDoubleClick ? QEvent::MouseButtonDblClick : QEvent::MouseButtonRelease, 01024 _mouse->pos(), _mouse->button(), _mouse->state()); 01025 dispatchMouseEvent(EventImpl::CLICK_EVENT, mev.innerNode.handle(),mev.innerNonSharedNode.handle(),true, 01026 d->clickCount, &me, true, DOM::NodeImpl::MouseRelease); 01027 } 01028 01029 DOM::NodeImpl* fn = m_part->xmlDocImpl()->focusNode(); 01030 if (fn && fn != mev.innerNode.handle() && 01031 fn->renderer() && fn->renderer()->isWidget()) { 01032 forwardPeripheralEvent(static_cast<khtml::RenderWidget*>(fn->renderer()), _mouse, xm, ym); 01033 } 01034 01035 khtml::RenderObject* r = mev.innerNode.handle() ? mev.innerNode.handle()->renderer() : 0; 01036 if (r && r->isWidget()) 01037 _mouse->ignore(); 01038 01039 if (!swallowEvent) { 01040 khtml::MouseReleaseEvent event( _mouse, xm, ym, mev.url, mev.target, mev.innerNode ); 01041 QApplication::sendEvent( m_part, &event ); 01042 } 01043 } 01044 01045 // returns true if event should be swallowed 01046 bool KHTMLView::dispatchKeyEvent( QKeyEvent *_ke ) 01047 { 01048 if (!m_part->xmlDocImpl()) 01049 return false; 01050 // Pressing and releasing a key should generate keydown, keypress and keyup events 01051 // Holding it down should generated keydown, keypress (repeatedly) and keyup events 01052 // The problem here is that Qt generates two autorepeat events (keyrelease+keypress) 01053 // for autorepeating, while DOM wants only one autorepeat event (keypress), so one 01054 // of the Qt events shouldn't be passed to DOM, but it should be still filtered 01055 // out if DOM would filter the autorepeat event. Additional problem is that Qt keyrelease 01056 // events don't have text() set (Qt bug?), so DOM often would ignore the keypress event 01057 // if it was created using Qt keyrelease, but Qt autorepeat keyrelease comes 01058 // before Qt autorepeat keypress (i.e. problem whether to filter it out or not). 01059 // The solution is to filter out and postpone the Qt autorepeat keyrelease until 01060 // the following Qt keypress event comes. If DOM accepts the DOM keypress event, 01061 // the postponed event will be simply discarded. If not, it will be passed to keyPressEvent() 01062 // again, and here it will be ignored. 01063 // 01064 // Qt: Press | Release(autorepeat) Press(autorepeat) etc. | Release 01065 // DOM: Down + Press | (nothing) Press | Up 01066 01067 // It's also possible to get only Releases. E.g. the release of alt-tab, 01068 // or when the keypresses get captured by an accel. 01069 01070 if( _ke == d->postponed_autorepeat ) // replayed event 01071 { 01072 return false; 01073 } 01074 01075 if( _ke->type() == QEvent::KeyPress ) 01076 { 01077 if( !_ke->isAutoRepeat()) 01078 { 01079 bool ret = dispatchKeyEventHelper( _ke, false ); // keydown 01080 if( dispatchKeyEventHelper( _ke, true )) // keypress 01081 ret = true; 01082 return ret; 01083 } 01084 else // autorepeat 01085 { 01086 bool ret = dispatchKeyEventHelper( _ke, true ); // keypress 01087 if( !ret && d->postponed_autorepeat ) 01088 keyPressEvent( d->postponed_autorepeat ); 01089 delete d->postponed_autorepeat; 01090 d->postponed_autorepeat = NULL; 01091 return ret; 01092 } 01093 } 01094 else // QEvent::KeyRelease 01095 { 01096 // Discard postponed "autorepeat key-release" events that didn't see 01097 // a keypress after them (e.g. due to QAccel) 01098 if ( d->postponed_autorepeat ) { 01099 delete d->postponed_autorepeat; 01100 d->postponed_autorepeat = 0; 01101 } 01102 01103 if( !_ke->isAutoRepeat()) { 01104 return dispatchKeyEventHelper( _ke, false ); // keyup 01105 } 01106 else 01107 { 01108 d->postponed_autorepeat = new QKeyEvent( _ke->type(), _ke->key(), _ke->ascii(), _ke->state(), 01109 _ke->text(), _ke->isAutoRepeat(), _ke->count()); 01110 if( _ke->isAccepted()) 01111 d->postponed_autorepeat->accept(); 01112 else 01113 d->postponed_autorepeat->ignore(); 01114 return true; 01115 } 01116 } 01117 } 01118 01119 // returns true if event should be swallowed 01120 bool KHTMLView::dispatchKeyEventHelper( QKeyEvent *_ke, bool keypress ) 01121 { 01122 DOM::NodeImpl* keyNode = m_part->xmlDocImpl()->focusNode(); 01123 if (keyNode) { 01124 return keyNode->dispatchKeyEvent(_ke, keypress); 01125 } else { // no focused node, send to document 01126 return m_part->xmlDocImpl()->dispatchKeyEvent(_ke, keypress); 01127 } 01128 } 01129 01130 void KHTMLView::keyPressEvent( QKeyEvent *_ke ) 01131 { 01132 01133 #ifndef KHTML_NO_CARET 01134 if (m_part->isEditable() || m_part->isCaretMode() 01135 || (m_part->xmlDocImpl() && m_part->xmlDocImpl()->focusNode() 01136 && m_part->xmlDocImpl()->focusNode()->contentEditable())) { 01137 d->caretViewContext()->keyReleasePending = true; 01138 caretKeyPressEvent(_ke); 01139 return; 01140 } 01141 #endif // KHTML_NO_CARET 01142 01143 // If CTRL was hit, be prepared for access keys 01144 if (_ke->key() == Key_Control && _ke->state()==0 && !d->accessKeysActivated) d->accessKeysPreActivate=true; 01145 01146 if (_ke->key() == Key_Shift && _ke->state()==0) 01147 d->scrollSuspendPreActivate=true; 01148 01149 // accesskey handling needs to be done before dispatching, otherwise e.g. lineedits 01150 // may eat the event 01151 01152 if (d->accessKeysActivated) 01153 { 01154 if (_ke->state()==0 || _ke->state()==ShiftButton) { 01155 if (_ke->key() != Key_Shift) accessKeysTimeout(); 01156 handleAccessKey( _ke ); 01157 _ke->accept(); 01158 return; 01159 } 01160 accessKeysTimeout(); 01161 } 01162 01163 if ( dispatchKeyEvent( _ke )) { 01164 // If either keydown or keypress was accepted by a widget, or canceled by JS, stop here. 01165 _ke->accept(); 01166 return; 01167 } 01168 01169 #ifndef KHTML_NO_TYPE_AHEAD_FIND 01170 if(d->typeAheadActivated) 01171 { 01172 // type-ahead find aka find-as-you-type 01173 if(_ke->key() == Key_BackSpace) 01174 { 01175 d->findString = d->findString.left(d->findString.length() - 1); 01176 01177 if(!d->findString.isEmpty()) 01178 { 01179 findAhead(false); 01180 } 01181 else 01182 { 01183 findTimeout(); 01184 } 01185 01186 d->timer.start(3000, true); 01187 _ke->accept(); 01188 return; 01189 } 01190 else if(_ke->key() == KStdAccel::findNext()) 01191 { // part doesn't get this key event because of the keyboard grab 01192 m_part->findTextNext(); 01193 d->timer.start(3000, true); 01194 _ke->accept(); 01195 return; 01196 } 01197 else if(_ke->key() == Key_Escape) 01198 { 01199 findTimeout(); 01200 01201 _ke->accept(); 01202 return; 01203 } 01204 else if(_ke->text().isEmpty() == false) 01205 { 01206 d->findString += _ke->text(); 01207 01208 findAhead(true); 01209 01210 d->timer.start(3000, true); 01211 _ke->accept(); 01212 return; 01213 } 01214 } 01215 else if(_ke->key() == '\'' || _ke->key() == '/') 01216 { 01217 if(_ke->key() == '\'') 01218 { 01219 d->findLinksOnly = true; 01220 m_part->setStatusBarText(i18n("Starting -- find links as you type"), 01221 KHTMLPart::BarDefaultText); 01222 } 01223 else if(_ke->key() == '/') 01224 { 01225 d->findLinksOnly = false; 01226 m_part->setStatusBarText(i18n("Starting -- find text as you type"), 01227 KHTMLPart::BarDefaultText); 01228 } 01229 01230 m_part->findTextBegin(); 01231 d->typeAheadActivated = true; 01232 d->timer.start(3000, true); 01233 grabKeyboard(); 01234 _ke->accept(); 01235 return; 01236 } 01237 #endif // KHTML_NO_TYPE_AHEAD_FIND 01238 01239 int offs = (clipper()->height() < 30) ? clipper()->height() : 30; 01240 if (_ke->state() & Qt::ShiftButton) 01241 switch(_ke->key()) 01242 { 01243 case Key_Space: 01244 if ( d->vmode == QScrollView::AlwaysOff ) 01245 _ke->accept(); 01246 else { 01247 scrollBy( 0, -clipper()->height() - offs ); 01248 if(d->scrollSuspended) 01249 d->newScrollTimer(this, 0); 01250 } 01251 break; 01252 01253 case Key_Down: 01254 case Key_J: 01255 d->adjustScroller(this, KHTMLViewPrivate::ScrollDown, KHTMLViewPrivate::ScrollUp); 01256 break; 01257 01258 case Key_Up: 01259 case Key_K: 01260 d->adjustScroller(this, KHTMLViewPrivate::ScrollUp, KHTMLViewPrivate::ScrollDown); 01261 break; 01262 01263 case Key_Left: 01264 case Key_H: 01265 d->adjustScroller(this, KHTMLViewPrivate::ScrollLeft, KHTMLViewPrivate::ScrollRight); 01266 break; 01267 01268 case Key_Right: 01269 case Key_L: 01270 d->adjustScroller(this, KHTMLViewPrivate::ScrollRight, KHTMLViewPrivate::ScrollLeft); 01271 break; 01272 } 01273 else 01274 switch ( _ke->key() ) 01275 { 01276 case Key_Down: 01277 case Key_J: 01278 if ( d->vmode == QScrollView::AlwaysOff ) 01279 _ke->accept(); 01280 else { 01281 if (!d->scrollTimerId || d->scrollSuspended) 01282 scrollBy( 0, 10 ); 01283 if (d->scrollTimerId) 01284 d->newScrollTimer(this, 0); 01285 } 01286 break; 01287 01288 case Key_Space: 01289 case Key_Next: 01290 if ( d->vmode == QScrollView::AlwaysOff ) 01291 _ke->accept(); 01292 else { 01293 scrollBy( 0, clipper()->height() - offs ); 01294 if(d->scrollSuspended) 01295 d->newScrollTimer(this, 0); 01296 } 01297 break; 01298 01299 case Key_Up: 01300 case Key_K: 01301 if ( d->vmode == QScrollView::AlwaysOff ) 01302 _ke->accept(); 01303 else { 01304 if (!d->scrollTimerId || d->scrollSuspended) 01305 scrollBy( 0, -10 ); 01306 if (d->scrollTimerId) 01307 d->newScrollTimer(this, 0); 01308 } 01309 break; 01310 01311 case Key_Prior: 01312 if ( d->vmode == QScrollView::AlwaysOff ) 01313 _ke->accept(); 01314 else { 01315 scrollBy( 0, -clipper()->height() + offs ); 01316 if(d->scrollSuspended) 01317 d->newScrollTimer(this, 0); 01318 } 01319 break; 01320 case Key_Right: 01321 case Key_L: 01322 if ( d->hmode == QScrollView::AlwaysOff ) 01323 _ke->accept(); 01324 else { 01325 if (!d->scrollTimerId || d->scrollSuspended) 01326 scrollBy( 10, 0 ); 01327 if (d->scrollTimerId) 01328 d->newScrollTimer(this, 0); 01329 } 01330 break; 01331 case Key_Left: 01332 case Key_H: 01333 if ( d->hmode == QScrollView::AlwaysOff ) 01334 _ke->accept(); 01335 else { 01336 if (!d->scrollTimerId || d->scrollSuspended) 01337 scrollBy( -10, 0 ); 01338 if (d->scrollTimerId) 01339 d->newScrollTimer(this, 0); 01340 } 01341 break; 01342 case Key_Enter: 01343 case Key_Return: 01344 // ### FIXME: 01345 // or even better to HTMLAnchorElementImpl::event() 01346 if (m_part->xmlDocImpl()) { 01347 NodeImpl *n = m_part->xmlDocImpl()->focusNode(); 01348 if (n) 01349 n->setActive(); 01350 } 01351 break; 01352 case Key_Home: 01353 if ( d->vmode == QScrollView::AlwaysOff ) 01354 _ke->accept(); 01355 else { 01356 setContentsPos( 0, 0 ); 01357 if(d->scrollSuspended) 01358 d->newScrollTimer(this, 0); 01359 } 01360 break; 01361 case Key_End: 01362 if ( d->vmode == QScrollView::AlwaysOff ) 01363 _ke->accept(); 01364 else { 01365 setContentsPos( 0, contentsHeight() - visibleHeight() ); 01366 if(d->scrollSuspended) 01367 d->newScrollTimer(this, 0); 01368 } 01369 break; 01370 case Key_Shift: 01371 // what are you doing here? 01372 _ke->ignore(); 01373 return; 01374 default: 01375 if (d->scrollTimerId) 01376 d->newScrollTimer(this, 0); 01377 _ke->ignore(); 01378 return; 01379 } 01380 01381 _ke->accept(); 01382 } 01383 01384 #ifndef KHTML_NO_TYPE_AHEAD_FIND 01385 01386 void KHTMLView::findTimeout() 01387 { 01388 d->typeAheadActivated = false; 01389 d->findString = ""; 01390 releaseKeyboard(); 01391 m_part->setStatusBarText(i18n("Find stopped."), KHTMLPart::BarDefaultText); 01392 } 01393 01394 void KHTMLView::findAhead(bool increase) 01395 { 01396 QString status; 01397 01398 if(d->findLinksOnly) 01399 { 01400 m_part->findText(d->findString, KHTMLPart::FindNoPopups | 01401 KHTMLPart::FindLinksOnly, this); 01402 if(m_part->findTextNext()) 01403 { 01404 status = i18n("Link found: \"%1\"."); 01405 } 01406 else 01407 { 01408 if(increase) KNotifyClient::beep(); 01409 status = i18n("Link not found: \"%1\"."); 01410 } 01411 } 01412 else 01413 { 01414 m_part->findText(d->findString, KHTMLPart::FindNoPopups, this); 01415 if(m_part->findTextNext()) 01416 { 01417 status = i18n("Text found: \"%1\"."); 01418 } 01419 else 01420 { 01421 if(increase) KNotifyClient::beep(); 01422 status = i18n("Text not found: \"%1\"."); 01423 } 01424 } 01425 01426 m_part->setStatusBarText(status.arg(d->findString.lower()), 01427 KHTMLPart::BarDefaultText); 01428 } 01429 01430 #endif // KHTML_NO_TYPE_AHEAD_FIND 01431 01432 void KHTMLView::keyReleaseEvent(QKeyEvent *_ke) 01433 { 01434 if (d->m_caretViewContext && d->m_caretViewContext->keyReleasePending) { 01435 //caretKeyReleaseEvent(_ke); 01436 d->m_caretViewContext->keyReleasePending = false; 01437 return; 01438 } 01439 01440 if (d->accessKeysPreActivate && _ke->key() != Key_Control) d->accessKeysPreActivate=false; 01441 if (_ke->key() == Key_Control && d->accessKeysPreActivate && _ke->state() == Qt::ControlButton && !(KApplication::keyboardModifiers() & KApplication::ControlModifier)) 01442 { 01443 displayAccessKeys(); 01444 m_part->setStatusBarText(i18n("Access Keys activated"),KHTMLPart::BarOverrideText); 01445 d->accessKeysActivated = true; 01446 d->accessKeysPreActivate = false; 01447 } 01448 else if (d->accessKeysActivated) accessKeysTimeout(); 01449 01450 if( d->scrollSuspendPreActivate && _ke->key() != Key_Shift ) 01451 d->scrollSuspendPreActivate = false; 01452 if( _ke->key() == Key_Shift && d->scrollSuspendPreActivate && _ke->state() == Qt::ShiftButton 01453 && !(KApplication::keyboardModifiers() & KApplication::ShiftModifier)) 01454 if (d->scrollTimerId) 01455 d->scrollSuspended = !d->scrollSuspended; 01456 01457 // Send keyup event 01458 if ( dispatchKeyEvent( _ke ) ) 01459 { 01460 _ke->accept(); 01461 return; 01462 } 01463 01464 QScrollView::keyReleaseEvent(_ke); 01465 } 01466 01467 void KHTMLView::contentsContextMenuEvent ( QContextMenuEvent * /*ce*/ ) 01468 { 01469 // ### what kind of c*** is that ? 01470 #if 0 01471 if (!m_part->xmlDocImpl()) return; 01472 int xm = _ce->x(); 01473 int ym = _ce->y(); 01474 01475 DOM::NodeImpl::MouseEvent mev( _ce->state(), DOM::NodeImpl::MouseMove ); // ### not a mouse event! 01476 m_part->xmlDocImpl()->prepareMouseEvent( xm, ym, &mev ); 01477 01478 NodeImpl *targetNode = mev.innerNode.handle(); 01479 if (targetNode && targetNode->renderer() && targetNode->renderer()->isWidget()) { 01480 int absx = 0; 01481 int absy = 0; 01482 targetNode->renderer()->absolutePosition(absx,absy); 01483 QPoint pos(xm-absx,ym-absy); 01484 01485 QWidget *w = static_cast<RenderWidget*>(targetNode->renderer())->widget(); 01486 QContextMenuEvent cme(_ce->reason(),pos,_ce->globalPos(),_ce->state()); 01487 setIgnoreEvents(true); 01488 QApplication::sendEvent(w,&cme); 01489 setIgnoreEvents(false); 01490 } 01491 #endif 01492 } 01493 01494 bool KHTMLView::focusNextPrevChild( bool next ) 01495 { 01496 // Now try to find the next child 01497 if (m_part->xmlDocImpl() && focusNextPrevNode(next)) 01498 { 01499 if (m_part->xmlDocImpl()->focusNode()) 01500 kdDebug() << "focusNode.name: " 01501 << m_part->xmlDocImpl()->focusNode()->nodeName().string() << endl; 01502 return true; // focus node found 01503 } 01504 01505 // If we get here, pass tabbing control up to the next/previous child in our parent 01506 d->pseudoFocusNode = KHTMLViewPrivate::PFNone; 01507 if (m_part->parentPart() && m_part->parentPart()->view()) 01508 return m_part->parentPart()->view()->focusNextPrevChild(next); 01509 01510 return QWidget::focusNextPrevChild(next); 01511 } 01512 01513 void KHTMLView::doAutoScroll() 01514 { 01515 QPoint pos = QCursor::pos(); 01516 pos = viewport()->mapFromGlobal( pos ); 01517 01518 int xm, ym; 01519 viewportToContents(pos.x(), pos.y(), xm, ym); 01520 01521 pos = QPoint(pos.x() - viewport()->x(), pos.y() - viewport()->y()); 01522 if ( (pos.y() < 0) || (pos.y() > visibleHeight()) || 01523 (pos.x() < 0) || (pos.x() > visibleWidth()) ) 01524 { 01525 ensureVisible( xm, ym, 0, 5 ); 01526 01527 #ifndef KHTML_NO_SELECTION 01528 // extend the selection while scrolling 01529 DOM::Node innerNode; 01530 if (m_part->isExtendingSelection()) { 01531 RenderObject::NodeInfo renderInfo(true/*readonly*/, false/*active*/); 01532 m_part->xmlDocImpl()->renderer()->layer() 01533 ->nodeAtPoint(renderInfo, xm, ym); 01534 innerNode = renderInfo.innerNode(); 01535 }/*end if*/ 01536 01537 if (innerNode.handle() && innerNode.handle()->renderer()) { 01538 int absX, absY; 01539 innerNode.handle()->renderer()->absolutePosition(absX, absY); 01540 01541 m_part->extendSelectionTo(xm, ym, absX, absY, innerNode); 01542 }/*end if*/ 01543 #endif // KHTML_NO_SELECTION 01544 } 01545 } 01546 01547 01548 class HackWidget : public QWidget 01549 { 01550 public: 01551 inline void setNoErase() { setWFlags(getWFlags()|WRepaintNoErase); } 01552 }; 01553 01554 bool KHTMLView::eventFilter(QObject *o, QEvent *e) 01555 { 01556 if ( e->type() == QEvent::AccelOverride ) { 01557 QKeyEvent* ke = (QKeyEvent*) e; 01558 //kdDebug(6200) << "QEvent::AccelOverride" << endl; 01559 if (m_part->isEditable() || m_part->isCaretMode() 01560 || (m_part->xmlDocImpl() && m_part->xmlDocImpl()->focusNode() 01561 && m_part->xmlDocImpl()->focusNode()->contentEditable())) { 01562 //kdDebug(6200) << "editable/navigable" << endl; 01563 if ( (ke->state() & ControlButton) || (ke->state() & ShiftButton) ) { 01564 switch ( ke->key() ) { 01565 case Key_Left: 01566 case Key_Right: 01567 case Key_Up: 01568 case Key_Down: 01569 case Key_Home: 01570 case Key_End: 01571 ke->accept(); 01572 //kdDebug(6200) << "eaten" << endl; 01573 return true; 01574 default: 01575 break; 01576 } 01577 } 01578 } 01579 } 01580 01581 QWidget *view = viewport(); 01582 01583 if (o == view) { 01584 // we need to install an event filter on all children of the viewport to 01585 // be able to get correct stacking of children within the document. 01586 if(e->type() == QEvent::ChildInserted) { 01587 QObject *c = static_cast<QChildEvent *>(e)->child(); 01588 if (c->isWidgetType()) { 01589 QWidget *w = static_cast<QWidget *>(c); 01590 // don't install the event filter on toplevels 01591 if (w->parentWidget(true) == view) { 01592 if (!strcmp(w->name(), "__khtml")) { 01593 w->installEventFilter(this); 01594 w->unsetCursor(); 01595 w->setBackgroundMode( QWidget::NoBackground ); 01596 static_cast<HackWidget *>(w)->setNoErase(); 01597 if (w->children()) { 01598 QObjectListIterator it(*w->children()); 01599 for (; it.current(); ++it) { 01600 QWidget *widget = ::qt_cast<QWidget *>(it.current()); 01601 if (widget && !widget->isTopLevel() 01602 && !::qt_cast<QScrollView *>(widget)) { 01603 widget->setBackgroundMode( QWidget::NoBackground ); 01604 static_cast<HackWidget *>(widget)->setNoErase(); 01605 widget->installEventFilter(this); 01606 } 01607 } 01608 } 01609 } 01610 } 01611 } 01612 } 01613 } else if (o->isWidgetType()) { 01614 QWidget *v = static_cast<QWidget *>(o); 01615 QWidget *c = v; 01616 while (v && v != view) { 01617 c = v; 01618 v = v->parentWidget(true); 01619 } 01620 01621 if (v && !strcmp(c->name(), "__khtml")) { 01622 bool block = false; 01623 QWidget *w = static_cast<QWidget *>(o); 01624 switch(e->type()) { 01625 case QEvent::Paint: 01626 if (!allowWidgetPaintEvents) { 01627 // eat the event. Like this we can control exactly when the widget 01628 // get's repainted. 01629 block = true; 01630 int x = 0, y = 0; 01631 QWidget *v = w; 01632 while (v && v != view) { 01633 x += v->x(); 01634 y += v->y(); 01635 v = v->parentWidget(); 01636 } 01637 viewportToContents( x, y, x, y ); 01638 QPaintEvent *pe = static_cast<QPaintEvent *>(e); 01639 scheduleRepaint(x + pe->rect().x(), y + pe->rect().y(), 01640 pe->rect().width(), pe->rect().height()); 01641 } 01642 break; 01643 case QEvent::MouseMove: 01644 case QEvent::MouseButtonPress: 01645 case QEvent::MouseButtonRelease: 01646 case QEvent::MouseButtonDblClick: { 01647 if (w->parentWidget() == view && !::qt_cast<QScrollBar *>(w)) { 01648 QMouseEvent *me = static_cast<QMouseEvent *>(e); 01649 QPoint pt = (me->pos() + w->pos()); 01650 QMouseEvent me2(me->type(), pt, me->button(), me->state()); 01651 01652 if (e->type() == QEvent::MouseMove) 01653 viewportMouseMoveEvent(&me2); 01654 else if(e->type() == QEvent::MouseButtonPress) 01655 viewportMousePressEvent(&me2); 01656 else if(e->type() == QEvent::MouseButtonRelease) 01657 viewportMouseReleaseEvent(&me2); 01658 else 01659 viewportMouseDoubleClickEvent(&me2); 01660 block = true; 01661 } 01662 break; 01663 } 01664 case QEvent::KeyPress: 01665 case QEvent::KeyRelease: 01666 if (w->parentWidget() == view && !::qt_cast<QScrollBar *>(w)) { 01667 QKeyEvent *ke = static_cast<QKeyEvent *>(e); 01668 if (e->type() == QEvent::KeyPress) 01669 keyPressEvent(ke); 01670 else 01671 keyReleaseEvent(ke); 01672 block = true; 01673 } 01674 default: 01675 break; 01676 } 01677 if (block) { 01678 //qDebug("eating event"); 01679 return true; 01680 } 01681 } 01682 } 01683 01684 // kdDebug(6000) <<"passing event on to sv event filter object=" << o->className() << " event=" << e->type() << endl; 01685 return QScrollView::eventFilter(o, e); 01686 } 01687 01688 01689 DOM::NodeImpl *KHTMLView::nodeUnderMouse() const 01690 { 01691 return d->underMouse; 01692 } 01693 01694 DOM::NodeImpl *KHTMLView::nonSharedNodeUnderMouse() const 01695 { 01696 return d->underMouseNonShared; 01697 } 01698 01699 bool KHTMLView::scrollTo(const QRect &bounds) 01700 { 01701 d->scrollingSelf = true; // so scroll events get ignored 01702 01703 int x, y, xe, ye; 01704 x = bounds.left(); 01705 y = bounds.top(); 01706 xe = bounds.right(); 01707 ye = bounds.bottom(); 01708 01709 //kdDebug(6000)<<"scrolling coords: x="<<x<<" y="<<y<<" width="<<xe-x<<" height="<<ye-y<<endl; 01710 01711 int deltax; 01712 int deltay; 01713 01714 int curHeight = visibleHeight(); 01715 int curWidth = visibleWidth(); 01716 01717 if (ye-y>curHeight-d->borderY) 01718 ye = y + curHeight - d->borderY; 01719 01720 if (xe-x>curWidth-d->borderX) 01721 xe = x + curWidth - d->borderX; 01722 01723 // is xpos of target left of the view's border? 01724 if (x < contentsX() + d->borderX ) 01725 deltax = x - contentsX() - d->borderX; 01726 // is xpos of target right of the view's right border? 01727 else if (xe + d->borderX > contentsX() + curWidth) 01728 deltax = xe + d->borderX - ( contentsX() + curWidth ); 01729 else 01730 deltax = 0; 01731 01732 // is ypos of target above upper border? 01733 if (y < contentsY() + d->borderY) 01734 deltay = y - contentsY() - d->borderY; 01735 // is ypos of target below lower border? 01736 else if (ye + d->borderY > contentsY() + curHeight) 01737 deltay = ye + d->borderY - ( contentsY() + curHeight ); 01738 else 01739 deltay = 0; 01740 01741 int maxx = curWidth-d->borderX; 01742 int maxy = curHeight-d->borderY; 01743 01744 int scrollX,scrollY; 01745 01746 scrollX = deltax > 0 ? (deltax > maxx ? maxx : deltax) : deltax == 0 ? 0 : (deltax>-maxx ? deltax : -maxx); 01747 scrollY = deltay > 0 ? (deltay > maxy ? maxy : deltay) : deltay == 0 ? 0 : (deltay>-maxy ? deltay : -maxy); 01748 01749 if (contentsX() + scrollX < 0) 01750 scrollX = -contentsX(); 01751 else if (contentsWidth() - visibleWidth() - contentsX() < scrollX) 01752 scrollX = contentsWidth() - visibleWidth() - contentsX(); 01753 01754 if (contentsY() + scrollY < 0) 01755 scrollY = -contentsY(); 01756 else if (contentsHeight() - visibleHeight() - contentsY() < scrollY) 01757 scrollY = contentsHeight() - visibleHeight() - contentsY(); 01758 01759 scrollBy(scrollX, scrollY); 01760 01761 d->scrollingSelf = false; 01762 01763 if ( (abs(deltax)<=maxx) && (abs(deltay)<=maxy) ) 01764 return true; 01765 else return false; 01766 01767 } 01768 01769 bool KHTMLView::focusNextPrevNode(bool next) 01770 { 01771 // Sets the focus node of the document to be the node after (or if 01772 // next is false, before) the current focus node. Only nodes that 01773 // are selectable (i.e. for which isSelectable() returns true) are 01774 // taken into account, and the order used is that specified in the 01775 // HTML spec (see DocumentImpl::nextFocusNode() and 01776 // DocumentImpl::previousFocusNode() for details). 01777 01778 DocumentImpl *doc = m_part->xmlDocImpl(); 01779 NodeImpl *oldFocusNode = doc->focusNode(); 01780 01781 #if 1 01782 // If the user has scrolled the document, then instead of picking 01783 // the next focusable node in the document, use the first one that 01784 // is within the visible area (if possible). 01785 if (d->scrollBarMoved) 01786 { 01787 NodeImpl *toFocus; 01788 if (next) 01789 toFocus = doc->nextFocusNode(oldFocusNode); 01790 else 01791 toFocus = doc->previousFocusNode(oldFocusNode); 01792 01793 if (!toFocus && oldFocusNode) 01794 if (next) 01795 toFocus = doc->nextFocusNode(NULL); 01796 else 01797 toFocus = doc->previousFocusNode(NULL); 01798 01799 while (toFocus && toFocus != oldFocusNode) 01800 { 01801 01802 QRect focusNodeRect = toFocus->getRect(); 01803 if ((focusNodeRect.left() > contentsX()) && (focusNodeRect.right() < contentsX() + visibleWidth()) && 01804 (focusNodeRect.top() > contentsY()) && (focusNodeRect.bottom() < contentsY() + visibleHeight())) { 01805 { 01806 QRect r = toFocus->getRect(); 01807 ensureVisible( r.right(), r.bottom()); 01808 ensureVisible( r.left(), r.top()); 01809 d->scrollBarMoved = false; 01810 d->tabMovePending = false; 01811 d->lastTabbingDirection = next; 01812 d->pseudoFocusNode = KHTMLViewPrivate::PFNone; 01813 m_part->xmlDocImpl()->setFocusNode(toFocus); 01814 Node guard(toFocus); 01815 if (!toFocus->hasOneRef() ) 01816 { 01817 emit m_part->nodeActivated(Node(toFocus)); 01818 } 01819 return true; 01820 } 01821 } 01822 if (next) 01823 toFocus = doc->nextFocusNode(toFocus); 01824 else 01825 toFocus = doc->previousFocusNode(toFocus); 01826 01827 if (!toFocus && oldFocusNode) 01828 if (next) 01829 toFocus = doc->nextFocusNode(NULL); 01830 else 01831 toFocus = doc->previousFocusNode(NULL); 01832 } 01833 01834 d->scrollBarMoved = false; 01835 } 01836 #endif 01837 01838 if (!oldFocusNode && d->pseudoFocusNode == KHTMLViewPrivate::PFNone) 01839 { 01840 ensureVisible(contentsX(), next?0:contentsHeight()); 01841 d->scrollBarMoved = false; 01842 d->pseudoFocusNode = next?KHTMLViewPrivate::PFTop:KHTMLViewPrivate::PFBottom; 01843 return true; 01844 } 01845 01846 NodeImpl *newFocusNode = NULL; 01847 01848 if (d->tabMovePending && next != d->lastTabbingDirection) 01849 { 01850 //kdDebug ( 6000 ) << " tab move pending and tabbing direction changed!\n"; 01851 newFocusNode = oldFocusNode; 01852 } 01853 else if (next) 01854 { 01855 if (oldFocusNode || d->pseudoFocusNode == KHTMLViewPrivate::PFTop ) 01856 newFocusNode = doc->nextFocusNode(oldFocusNode); 01857 } 01858 else 01859 { 01860 if (oldFocusNode || d->pseudoFocusNode == KHTMLViewPrivate::PFBottom ) 01861 newFocusNode = doc->previousFocusNode(oldFocusNode); 01862 } 01863 01864 bool targetVisible = false; 01865 if (!newFocusNode) 01866 { 01867 if ( next ) 01868 { 01869 targetVisible = scrollTo(QRect(contentsX()+visibleWidth()/2,contentsHeight()-d->borderY,0,0)); 01870 } 01871 else 01872 { 01873 targetVisible = scrollTo(QRect(contentsX()+visibleWidth()/2,d->borderY,0,0)); 01874 } 01875 } 01876 else 01877 { 01878 #ifndef KHTML_NO_CARET 01879 // if it's an editable element, activate the caret 01880 if (!m_part->isCaretMode() && !m_part->isEditable() 01881 && newFocusNode->contentEditable()) { 01882 d->caretViewContext(); 01883 moveCaretTo(newFocusNode, 0L, true); 01884 } else { 01885 caretOff(); 01886 } 01887 #endif // KHTML_NO_CARET 01888 01889 targetVisible = scrollTo(newFocusNode->getRect()); 01890 } 01891 01892 if (targetVisible) 01893 { 01894 //kdDebug ( 6000 ) << " target reached.\n"; 01895 d->tabMovePending = false; 01896 01897 m_part->xmlDocImpl()->setFocusNode(newFocusNode); 01898 if (newFocusNode) 01899 { 01900 Node guard(newFocusNode); 01901 if (!newFocusNode->hasOneRef() ) 01902 { 01903 emit m_part->nodeActivated(Node(newFocusNode)); 01904 } 01905 return true; 01906 } 01907 else 01908 { 01909 d->pseudoFocusNode = next?KHTMLViewPrivate::PFBottom:KHTMLViewPrivate::PFTop; 01910 return false; 01911 } 01912 } 01913 else 01914 { 01915 if (!d->tabMovePending) 01916 d->lastTabbingDirection = next; 01917 d->tabMovePending = true; 01918 return true; 01919 } 01920 } 01921 01922 void KHTMLView::displayAccessKeys() 01923 { 01924 for( NodeImpl* n = m_part->xmlDocImpl(); n != NULL; n = n->traverseNextNode()) { 01925 if( n->isElementNode()) { 01926 ElementImpl* en = static_cast< ElementImpl* >( n ); 01927 DOMString s = en->getAttribute( ATTR_ACCESSKEY ); 01928 if( s.length() == 1) { 01929 QRect rec=en->getRect(); 01930 QLabel *lab=new QLabel(s.string(),viewport(),0,Qt::WDestructiveClose); 01931 connect( this, SIGNAL(hideAccessKeys()), lab, SLOT(close()) ); 01932 connect( this, SIGNAL(repaintAccessKeys()), lab, SLOT(repaint())); 01933 lab->setPalette(QToolTip::palette()); 01934 lab->setLineWidth(2); 01935 lab->setFrameStyle(QFrame::Box | QFrame::Plain); 01936 lab->setMargin(3); 01937 lab->adjustSize(); 01938 addChild(lab,rec.left()+rec.width()/2,rec.top()+rec.height()/2); 01939 showChild(lab); 01940 } 01941 } 01942 } 01943 } 01944 01945 void KHTMLView::accessKeysTimeout() 01946 { 01947 d->accessKeysActivated=false; 01948 d->accessKeysPreActivate = false; 01949 m_part->setStatusBarText(QString::null, KHTMLPart::BarOverrideText); 01950 emit hideAccessKeys(); 01951 } 01952 01953 // Handling of the HTML accesskey attribute. 01954 bool KHTMLView::handleAccessKey( const QKeyEvent* ev ) 01955 { 01956 // Qt interprets the keyevent also with the modifiers, and ev->text() matches that, 01957 // but this code must act as if the modifiers weren't pressed 01958 QChar c; 01959 if( ev->key() >= Key_A && ev->key() <= Key_Z ) 01960 c = 'A' + ev->key() - Key_A; 01961 else if( ev->key() >= Key_0 && ev->key() <= Key_9 ) 01962 c = '0' + ev->key() - Key_0; 01963 else { 01964 // TODO fake XKeyEvent and XLookupString ? 01965 // This below seems to work e.g. for eacute though. 01966 if( ev->text().length() == 1 ) 01967 c = ev->text()[ 0 ]; 01968 } 01969 if( c.isNull()) 01970 return false; 01971 return focusNodeWithAccessKey( c ); 01972 } 01973 01974 bool KHTMLView::focusNodeWithAccessKey( QChar c, KHTMLView* caller ) 01975 { 01976 DocumentImpl *doc = m_part->xmlDocImpl(); 01977 if( !doc ) 01978 return false; 01979 ElementImpl* node = doc->findAccessKeyElement( c ); 01980 if( !node ) { 01981 QPtrList<KParts::ReadOnlyPart> frames = m_part->frames(); 01982 for( QPtrListIterator<KParts::ReadOnlyPart> it( frames ); 01983 it != NULL; 01984 ++it ) { 01985 if( !(*it)->inherits( "KHTMLPart" )) 01986 continue; 01987 KHTMLPart* part = static_cast< KHTMLPart* >( *it ); 01988 if( part->view() && part->view() != caller 01989 && part->view()->focusNodeWithAccessKey( c, this )) 01990 return true; 01991 } 01992 // pass up to the parent 01993 if (m_part->parentPart() && m_part->parentPart()->view() 01994 && m_part->parentPart()->view() != caller ) 01995 return m_part->parentPart()->view()->focusNodeWithAccessKey( c, this ); 01996 return false; 01997 } 01998 01999 // Scroll the view as necessary to ensure that the new focus node is visible 02000 #ifndef KHTML_NO_CARET 02001 // if it's an editable element, activate the caret 02002 if (!m_part->isCaretMode() && !m_part->isEditable() 02003 && node->contentEditable()) { 02004 d->caretViewContext(); 02005 moveCaretTo(node, 0L, true); 02006 } else { 02007 caretOff(); 02008 } 02009 #endif // KHTML_NO_CARET 02010 02011 QRect r = node->getRect(); 02012 ensureVisible( r.right(), r.bottom()); 02013 ensureVisible( r.left(), r.top()); 02014 02015 Node guard( node ); 02016 if( node->isSelectable()) { 02017 if (node->id()==ID_LABEL) { 02018 // if Accesskey is a label, give focus to the label's referrer. 02019 node=static_cast<ElementImpl *>(static_cast< HTMLLabelElementImpl* >( node )->getFormElement()); 02020 if (!node) return true; 02021 guard = node; 02022 } 02023 // Set focus node on the document 02024 m_part->xmlDocImpl()->setFocusNode(node); 02025 if( node != NULL && node->hasOneRef()) // deleted, only held by guard 02026 return true; 02027 emit m_part->nodeActivated(Node(node)); 02028 if( node != NULL && node->hasOneRef()) 02029 return true; 02030 } 02031 02032 switch( node->id()) { 02033 case ID_A: 02034 static_cast< HTMLAnchorElementImpl* >( node )->click(); 02035 break; 02036 case ID_INPUT: 02037 static_cast< HTMLInputElementImpl* >( node )->click(); 02038 break; 02039 case ID_BUTTON: 02040 static_cast< HTMLButtonElementImpl* >( node )->click(); 02041 break; 02042 case ID_AREA: 02043 static_cast< HTMLAreaElementImpl* >( node )->click(); 02044 break; 02045 case ID_TEXTAREA: 02046 break; // just focusing it is enough 02047 case ID_LEGEND: 02048 // TODO 02049 break; 02050 } 02051 return true; 02052 } 02053 02054 void KHTMLView::setMediaType( const QString &medium ) 02055 { 02056 m_medium = medium; 02057 } 02058 02059 QString KHTMLView::mediaType() const 02060 { 02061 return m_medium; 02062 } 02063 02064 void KHTMLView::setWidgetVisible(RenderWidget* w, bool vis) 02065 { 02066 if (vis) { 02067 d->visibleWidgets.replace(w, w->widget()); 02068 } 02069 else 02070 d->visibleWidgets.remove(w); 02071 } 02072 02073 void KHTMLView::print() 02074 { 02075 print( false ); 02076 } 02077 02078 void KHTMLView::print(bool quick) 02079 { 02080 if(!m_part->xmlDocImpl()) return; 02081 khtml::RenderCanvas *root = static_cast<khtml::RenderCanvas *>(m_part->xmlDocImpl()->renderer()); 02082 if(!root) return; 02083 02084 // this only works on Unix - we assume 72dpi 02085 KPrinter *printer = new KPrinter(true, QPrinter::PrinterResolution); 02086 printer->addDialogPage(new KHTMLPrintSettings()); 02087 QString docname = m_part->xmlDocImpl()->URL().prettyURL(); 02088 if ( !docname.isEmpty() ) 02089 docname = KStringHandler::csqueeze(docname, 80); 02090 if(quick || printer->setup(this, i18n("Print %1").arg(docname))) { 02091 viewport()->setCursor( waitCursor ); // only viewport(), no QApplication::, otherwise we get the busy cursor in kdeprint's dialogs 02092 // set up KPrinter 02093 printer->setFullPage(false); 02094 printer->setCreator(QString("KDE %1.%2.%3 HTML Library").arg(KDE_VERSION_MAJOR).arg(KDE_VERSION_MINOR).arg(KDE_VERSION_RELEASE)); 02095 printer->setDocName(docname); 02096 02097 QPainter *p = new QPainter; 02098 p->begin( printer ); 02099 khtml::setPrintPainter( p ); 02100 02101 m_part->xmlDocImpl()->setPaintDevice( printer ); 02102 QString oldMediaType = mediaType(); 02103 setMediaType( "print" ); 02104 // We ignore margin settings for html and body when printing 02105 // and use the default margins from the print-system 02106 // (In Qt 3.0.x the default margins are hardcoded in Qt) 02107 m_part->xmlDocImpl()->setPrintStyleSheet( printer->option("app-khtml-printfriendly") == "true" ? 02108 "* { background-image: none !important;" 02109 " background-color: white !important;" 02110 " color: black !important; }" 02111 "body { margin: 0px !important; }" 02112 "html { margin: 0px !important; }" : 02113 "body { margin: 0px !important; }" 02114 "html { margin: 0px !important; }" 02115 ); 02116 02117 QPaintDeviceMetrics metrics( printer ); 02118 02119 // this is a simple approximation... we layout the document 02120 // according to the width of the page, then just cut 02121 // pages without caring about the content. We should do better 02122 // in the future, but for the moment this is better than no 02123 // printing support 02124 kdDebug(6000) << "printing: physical page width = " << metrics.width() 02125 << " height = " << metrics.height() << endl; 02126 root->setPrintingMode(true); 02127 root->setWidth(metrics.width()); 02128 02129 m_part->xmlDocImpl()->styleSelector()->computeFontSizes(&metrics, 100); 02130 m_part->xmlDocImpl()->updateStyleSelector(); 02131 root->setPrintImages( printer->option("app-khtml-printimages") == "true"); 02132 root->setMinMaxKnown( false ); 02133 root->setLayouted( false ); 02134 root->layout(); 02135 khtml::RenderWidget::flushWidgetResizes(); // make sure widgets have their final size 02136 02137 bool printHeader = (printer->option("app-khtml-printheader") == "true"); 02138 02139 int headerHeight = 0; 02140 QFont headerFont("helvetica", 8); 02141 02142 QString headerLeft = KGlobal::locale()->formatDate(QDate::currentDate(),true); 02143 QString headerMid = docname; 02144 QString headerRight; 02145 02146 if (printHeader) 02147 { 02148 p->setFont(headerFont); 02149 headerHeight = (p->fontMetrics().lineSpacing() * 3) / 2; 02150 } 02151 02152 // ok. now print the pages. 02153 kdDebug(6000) << "printing: html page width = " << root->docWidth() 02154 << " height = " << root->docHeight() << endl; 02155 kdDebug(6000) << "printing: margins left = " << printer->margins().width() 02156 << " top = " << printer->margins().height() << endl; 02157 kdDebug(6000) << "printing: paper width = " << metrics.width() 02158 << " height = " << metrics.height() << endl; 02159 // if the width is too large to fit on the paper we just scale 02160 // the whole thing. 02161 int pageHeight = metrics.height(); 02162 int pageWidth = metrics.width(); 02163 p->setClipRect(0,0, pageWidth, pageHeight); 02164 02165 pageHeight -= headerHeight; 02166 02167 bool scalePage = false; 02168 double scale = 0.0; 02169 #ifndef QT_NO_TRANSFORMATIONS 02170 if(root->docWidth() > metrics.width()) { 02171 scalePage = true; 02172 scale = ((double) metrics.width())/((double) root->docWidth()); 02173 pageHeight = (int) (pageHeight/scale); 02174 pageWidth = (int) (pageWidth/scale); 02175 headerHeight = (int) (headerHeight/scale); 02176 } 02177 #endif 02178 kdDebug(6000) << "printing: scaled html width = " << pageWidth 02179 << " height = " << pageHeight << endl; 02180 02181 // Squeeze header to make it it on the page. 02182 if (printHeader) 02183 { 02184 int available_width = metrics.width() - 10 - 02185 2 * kMax(p->boundingRect(0, 0, metrics.width(), p->fontMetrics().lineSpacing(), Qt::AlignLeft, headerLeft).width(), 02186 p->boundingRect(0, 0, metrics.width(), p->fontMetrics().lineSpacing(), Qt::AlignLeft, headerRight).width()); 02187 if (available_width < 150) 02188 available_width = 150; 02189 int mid_width; 02190 int squeeze = 120; 02191 do { 02192 headerMid = KStringHandler::csqueeze(docname, squeeze); 02193 mid_width = p->boundingRect(0, 0, metrics.width(), p->fontMetrics().lineSpacing(), Qt::AlignLeft, headerMid).width(); 02194 squeeze -= 10; 02195 } while (mid_width > available_width); 02196 } 02197 02198 int top = 0; 02199 int page = 1; 02200 while(top < root->docHeight()) { 02201 if(top > 0) printer->newPage(); 02202 if (printHeader) 02203 { 02204 int dy = p->fontMetrics().lineSpacing(); 02205 p->setPen(Qt::black); 02206 p->setFont(headerFont); 02207 02208 headerRight = QString("#%1").arg(page); 02209 02210 p->drawText(0, 0, metrics.width(), dy, Qt::AlignLeft, headerLeft); 02211 p->drawText(0, 0, metrics.width(), dy, Qt::AlignHCenter, headerMid); 02212 p->drawText(0, 0, metrics.width(), dy, Qt::AlignRight, headerRight); 02213 } 02214 02215 #ifndef QT_NO_TRANSFORMATIONS 02216 if (scalePage) 02217 p->scale(scale, scale); 02218 #endif 02219 p->translate(0, headerHeight-top); 02220 02221 root->setTruncatedAt(top+pageHeight); 02222 02223 root->layer()->paint(p, QRect(0, top, pageWidth, pageHeight)); 02224 if (top + pageHeight >= root->docHeight()) 02225 break; // Stop if we have printed everything 02226 02227 top = root->truncatedAt(); 02228 p->resetXForm(); 02229 page++; 02230 } 02231 02232 p->end(); 02233 delete p; 02234 02235 // and now reset the layout to the usual one... 02236 root->setPrintingMode(false); 02237 khtml::setPrintPainter( 0 ); 02238 setMediaType( oldMediaType ); 02239 m_part->xmlDocImpl()->setPaintDevice( this ); 02240 m_part->xmlDocImpl()->styleSelector()->computeFontSizes(m_part->xmlDocImpl()->paintDeviceMetrics(), m_part->zoomFactor()); 02241 m_part->xmlDocImpl()->updateStyleSelector(); 02242 viewport()->unsetCursor(); 02243 } 02244 delete printer; 02245 } 02246 02247 void KHTMLView::slotPaletteChanged() 02248 { 02249 if(!m_part->xmlDocImpl()) return; 02250 DOM::DocumentImpl *document = m_part->xmlDocImpl(); 02251 if (!document->isHTMLDocument()) return; 02252 khtml::RenderCanvas *root = static_cast<khtml::RenderCanvas *>(document->renderer()); 02253 if(!root) return; 02254 root->style()->resetPalette(); 02255 NodeImpl *body = static_cast<HTMLDocumentImpl*>(document)->body(); 02256 if(!body) return; 02257 body->setChanged(true); 02258 body->recalcStyle( NodeImpl::Force ); 02259 } 02260 02261 void KHTMLView::paint(QPainter *p, const QRect &rc, int yOff, bool *more) 02262 { 02263 if(!m_part->xmlDocImpl()) return; 02264 khtml::RenderCanvas *root = static_cast<khtml::RenderCanvas *>(m_part->xmlDocImpl()->renderer()); 02265 if(!root) return; 02266 02267 m_part->xmlDocImpl()->setPaintDevice(p->device()); 02268 root->setPrintingMode(true); 02269 root->setWidth(rc.width()); 02270 02271 p->save(); 02272 p->setClipRect(rc); 02273 p->translate(rc.left(), rc.top()); 02274 double scale = ((double) rc.width()/(double) root->docWidth()); 02275 int height = (int) ((double) rc.height() / scale); 02276 #ifndef QT_NO_TRANSFORMATIONS 02277 p->scale(scale, scale); 02278 #endif 02279 02280 root->layer()->paint(p, QRect(0, yOff, root->docWidth(), height)); 02281 if (more) 02282 *more = yOff + height < root->docHeight(); 02283 p->restore(); 02284 02285 root->setPrintingMode(false); 02286 m_part->xmlDocImpl()->setPaintDevice( this ); 02287 } 02288 02289 02290 void KHTMLView::useSlowRepaints() 02291 { 02292 d->useSlowRepaints = true; 02293 setStaticBackground(true); 02294 } 02295 02296 02297 void KHTMLView::setVScrollBarMode ( ScrollBarMode mode ) 02298 { 02299 #ifndef KHTML_NO_SCROLLBARS 02300 d->vmode = mode; 02301 QScrollView::setVScrollBarMode(mode); 02302 #else 02303 Q_UNUSED( mode ); 02304 #endif 02305 } 02306 02307 void KHTMLView::setHScrollBarMode ( ScrollBarMode mode ) 02308 { 02309 #ifndef KHTML_NO_SCROLLBARS 02310 d->hmode = mode; 02311 QScrollView::setHScrollBarMode(mode); 02312 #else 02313 Q_UNUSED( mode ); 02314 #endif 02315 } 02316 02317 void KHTMLView::restoreScrollBar() 02318 { 02319 int ow = visibleWidth(); 02320 QScrollView::setVScrollBarMode(d->vmode); 02321 if (visibleWidth() != ow) 02322 layout(); 02323 d->prevScrollbarVisible = verticalScrollBar()->isVisible(); 02324 } 02325 02326 QStringList KHTMLView::formCompletionItems(const QString &name) const 02327 { 02328 if (!m_part->settings()->isFormCompletionEnabled()) 02329 return QStringList(); 02330 if (!d->formCompletions) 02331 d->formCompletions = new KSimpleConfig(locateLocal("data", "khtml/formcompletions")); 02332 return d->formCompletions->readListEntry(name); 02333 } 02334 02335 void KHTMLView::clearCompletionHistory(const QString& name) 02336 { 02337 if (!d->formCompletions) 02338 { 02339 d->formCompletions = new KSimpleConfig(locateLocal("data", "khtml/formcompletions")); 02340 } 02341 d->formCompletions->writeEntry(name, ""); 02342 d->formCompletions->sync(); 02343 } 02344 02345 void KHTMLView::addFormCompletionItem(const QString &name, const QString &value) 02346 { 02347 if (!m_part->settings()->isFormCompletionEnabled()) 02348 return; 02349 // don't store values that are all numbers or just numbers with 02350 // dashes or spaces as those are likely credit card numbers or 02351 // something similar 02352 bool cc_number(true); 02353 for (unsigned int i = 0; i < value.length(); ++i) 02354 { 02355 QChar c(value[i]); 02356 if (!c.isNumber() && c != '-' && !c.isSpace()) 02357 { 02358 cc_number = false; 02359 break; 02360 } 02361 } 02362 if (cc_number) 02363 return; 02364 QStringList items = formCompletionItems(name); 02365 if (!items.contains(value)) 02366 items.prepend(value); 02367 while ((int)items.count() > m_part->settings()->maxFormCompletionItems()) 02368 items.remove(items.fromLast()); 02369 d->formCompletions->writeEntry(name, items); 02370 } 02371 02372 void KHTMLView::addNonPasswordStorableSite(const QString& host) 02373 { 02374 if (!d->formCompletions) { 02375 d->formCompletions = new KSimpleConfig(locateLocal("data", "khtml/formcompletions")); 02376 } 02377 02378 d->formCompletions->setGroup("NonPasswordStorableSites"); 02379 QStringList sites = d->formCompletions->readListEntry("Sites"); 02380 sites.append(host); 02381 d->formCompletions->writeEntry("Sites", sites); 02382 d->formCompletions->sync(); 02383 d->formCompletions->setGroup(QString::null);//reset 02384 } 02385 02386 bool KHTMLView::nonPasswordStorableSite(const QString& host) const 02387 { 02388 if (!d->formCompletions) { 02389 d->formCompletions = new KSimpleConfig(locateLocal("data", "khtml/formcompletions")); 02390 } 02391 d->formCompletions->setGroup("NonPasswordStorableSites"); 02392 QStringList sites = d->formCompletions->readListEntry("Sites"); 02393 d->formCompletions->setGroup(QString::null);//reset 02394 02395 return (sites.find(host) != sites.end()); 02396 } 02397 02398 // returns true if event should be swallowed 02399 bool KHTMLView::dispatchMouseEvent(int eventId, DOM::NodeImpl *targetNode, 02400 DOM::NodeImpl *targetNodeNonShared, bool cancelable, 02401 int detail,QMouseEvent *_mouse, bool setUnder, 02402 int mouseEventType) 02403 { 02404 if (d->underMouse) 02405 d->underMouse->deref(); 02406 d->underMouse = targetNode; 02407 if (d->underMouse) 02408 d->underMouse->ref(); 02409 02410 if (d->underMouseNonShared) 02411 d->underMouseNonShared->deref(); 02412 d->underMouseNonShared = targetNodeNonShared; 02413 if (d->underMouseNonShared) 02414 d->underMouseNonShared->ref(); 02415 02416 int exceptioncode = 0; 02417 int pageX = 0; 02418 int pageY = 0; 02419 viewportToContents(_mouse->x(), _mouse->y(), pageX, pageY); 02420 int clientX = pageX - contentsX(); 02421 int clientY = pageY - contentsY(); 02422 int screenX = _mouse->globalX(); 02423 int screenY = _mouse->globalY(); 02424 int button = -1; 02425 switch (_mouse->button()) { 02426 case LeftButton: 02427 button = 0; 02428 break; 02429 case MidButton: 02430 button = 1; 02431 break; 02432 case RightButton: 02433 button = 2; 02434 break; 02435 default: 02436 break; 02437 } 02438 if (d->accessKeysPreActivate && button!=-1) 02439 d->accessKeysPreActivate=false; 02440 02441 bool ctrlKey = (_mouse->state() & ControlButton); 02442 bool altKey = (_mouse->state() & AltButton); 02443 bool shiftKey = (_mouse->state() & ShiftButton); 02444 bool metaKey = (_mouse->state() & MetaButton); 02445 02446 // mouseout/mouseover 02447 if (setUnder && (d->prevMouseX != pageX || d->prevMouseY != pageY)) { 02448 02449 // ### this code sucks. we should save the oldUnder instead of calculating 02450 // it again. calculating is expensive! (Dirk) 02451 NodeImpl *oldUnder = 0; 02452 if (d->prevMouseX >= 0 && d->prevMouseY >= 0) { 02453 NodeImpl::MouseEvent mev( _mouse->stateAfter(), static_cast<NodeImpl::MouseEventType>(mouseEventType)); 02454 m_part->xmlDocImpl()->prepareMouseEvent( true, d->prevMouseX, d->prevMouseY, &mev ); 02455 oldUnder = mev.innerNode.handle(); 02456 } 02457 // 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()); 02458 if (oldUnder != targetNode) { 02459 // send mouseout event to the old node 02460 if (oldUnder){ 02461 oldUnder->ref(); 02462 MouseEventImpl *me = new MouseEventImpl(EventImpl::MOUSEOUT_EVENT, 02463 true,true,m_part->xmlDocImpl()->defaultView(), 02464 0,screenX,screenY,clientX,clientY,pageX, pageY, 02465 ctrlKey,altKey,shiftKey,metaKey, 02466 button,targetNode); 02467 me->ref(); 02468 oldUnder->dispatchEvent(me,exceptioncode,true); 02469 me->deref(); 02470 } 02471 02472 // send mouseover event to the new node 02473 if (targetNode) { 02474 MouseEventImpl *me = new MouseEventImpl(EventImpl::MOUSEOVER_EVENT, 02475 true,true,m_part->xmlDocImpl()->defaultView(), 02476 0,screenX,screenY,clientX,clientY,pageX, pageY, 02477 ctrlKey,altKey,shiftKey,metaKey, 02478 button,oldUnder); 02479 02480 me->ref(); 02481 targetNode->dispatchEvent(me,exceptioncode,true); 02482 me->deref(); 02483 } 02484 02485 if (oldUnder) 02486 oldUnder->deref(); 02487 } 02488 } 02489 02490 bool swallowEvent = false; 02491 02492 if (targetNode) { 02493 // send the actual event 02494 bool dblclick = ( eventId == EventImpl::CLICK_EVENT && 02495 _mouse->type() == QEvent::MouseButtonDblClick ); 02496 MouseEventImpl *me = new MouseEventImpl(static_cast<EventImpl::EventId>(eventId), 02497 true,cancelable,m_part->xmlDocImpl()->defaultView(), 02498 detail,screenX,screenY,clientX,clientY,pageX, pageY, 02499 ctrlKey,altKey,shiftKey,metaKey, 02500 button,0, _mouse, dblclick ); 02501 me->ref(); 02502 targetNode->dispatchEvent(me,exceptioncode,true); 02503 if (me->defaultHandled() || me->defaultPrevented()) 02504 swallowEvent = true; 02505 me->deref(); 02506 02507 if (eventId == EventImpl::MOUSEDOWN_EVENT) { 02508 if (targetNode->isSelectable()) 02509 m_part->xmlDocImpl()->setFocusNode(targetNode); 02510 else 02511 m_part->xmlDocImpl()->setFocusNode(0); 02512 } 02513 } 02514 02515 return swallowEvent; 02516 } 02517 02518 void KHTMLView::setIgnoreWheelEvents( bool e ) 02519 { 02520 d->ignoreWheelEvents = e; 02521 } 02522 02523 #ifndef QT_NO_WHEELEVENT 02524 02525 void KHTMLView::viewportWheelEvent(QWheelEvent* e) 02526 { 02527 if (d->accessKeysPreActivate) d->accessKeysPreActivate=false; 02528 02529 if ( ( e->state() & ControlButton) == ControlButton ) 02530 { 02531 emit zoomView( - e->delta() ); 02532 e->accept(); 02533 } 02534 else if ( ( (d->ignoreWheelEvents && !verticalScrollBar()->isVisible()) 02535 || e->delta() > 0 && contentsY() <= 0 02536 || e->delta() < 0 && contentsY() >= contentsHeight() - visibleHeight()) 02537 && m_part->parentPart() ) { 02538 kdDebug(6000) << this << " cz " << contentsY() << " ch " << contentsHeight() << " vh " << visibleHeight() << endl; 02539 if ( m_part->parentPart()->view() ) 02540 m_part->parentPart()->view()->wheelEvent( e ); 02541 kdDebug(6000) << "sent" << endl; 02542 e->ignore(); 02543 } 02544 else if ( d->vmode == QScrollView::AlwaysOff ) { 02545 e->accept(); 02546 } 02547 else { 02548 d->scrollBarMoved = true; 02549 QScrollView::viewportWheelEvent( e ); 02550 02551 QMouseEvent *tempEvent = new QMouseEvent( QEvent::MouseMove, QPoint(-1,-1), QPoint(-1,-1), Qt::NoButton, e->state() ); 02552 emit viewportMouseMoveEvent ( tempEvent ); 02553 delete tempEvent; 02554 } 02555 02556 } 02557 #endif 02558 02559 void KHTMLView::dragEnterEvent( QDragEnterEvent* ev ) 02560 { 02561 // Handle drops onto frames (#16820) 02562 // Drops on the main html part is handled by Konqueror (and shouldn't do anything 02563 // in e.g. kmail, so not handled here). 02564 if ( m_part->parentPart() ) 02565 { 02566 QApplication::sendEvent(m_part->parentPart()->widget(), ev); 02567 return; 02568 } 02569 QScrollView::dragEnterEvent( ev ); 02570 } 02571 02572 void KHTMLView::dropEvent( QDropEvent *ev ) 02573 { 02574 // Handle drops onto frames (#16820) 02575 // Drops on the main html part is handled by Konqueror (and shouldn't do anything 02576 // in e.g. kmail, so not handled here). 02577 if ( m_part->parentPart() ) 02578 { 02579 QApplication::sendEvent(m_part->parentPart()->widget(), ev); 02580 return; 02581 } 02582 QScrollView::dropEvent( ev ); 02583 } 02584 02585 void KHTMLView::focusInEvent( QFocusEvent *e ) 02586 { 02587 #ifndef KHTML_NO_CARET 02588 // Restart blink frequency timer if it has been killed, but only on 02589 // editable nodes 02590 if (d->m_caretViewContext && 02591 d->m_caretViewContext->freqTimerId == -1 && 02592 m_part->xmlDocImpl()) { 02593 NodeImpl *caretNode = m_part->xmlDocImpl()->focusNode(); 02594 if (m_part->isCaretMode() 02595 || m_part->isEditable() 02596 || (caretNode && caretNode->renderer() 02597 && caretNode->renderer()->style()->userInput() 02598 == UI_ENABLED)) { 02599 d->m_caretViewContext->freqTimerId = startTimer(500); 02600 d->m_caretViewContext->visible = true; 02601 }/*end if*/ 02602 }/*end if*/ 02603 showCaret(); 02604 #endif // KHTML_NO_CARET 02605 QScrollView::focusInEvent( e ); 02606 } 02607 02608 void KHTMLView::focusOutEvent( QFocusEvent *e ) 02609 { 02610 if(m_part) m_part->stopAutoScroll(); 02611 02612 #ifndef KHTML_NO_TYPE_AHEAD_FIND 02613 if(d->typeAheadActivated) 02614 { 02615 findTimeout(); 02616 } 02617 #endif // KHTML_NO_TYPE_AHEAD_FIND 02618 02619 #ifndef KHTML_NO_CARET 02620 if (d->m_caretViewContext) { 02621 switch (d->m_caretViewContext->displayNonFocused) { 02622 case KHTMLPart::CaretInvisible: 02623 hideCaret(); 02624 break; 02625 case KHTMLPart::CaretVisible: { 02626 killTimer(d->m_caretViewContext->freqTimerId); 02627 d->m_caretViewContext->freqTimerId = -1; 02628 NodeImpl *caretNode = m_part->xmlDocImpl()->focusNode(); 02629 if (!d->m_caretViewContext->visible && (m_part->isCaretMode() 02630 || m_part->isEditable() 02631 || (caretNode && caretNode->renderer() 02632 && caretNode->renderer()->style()->userInput() 02633 == UI_ENABLED))) { 02634 d->m_caretViewContext->visible = true; 02635 showCaret(true); 02636 }/*end if*/ 02637 break; 02638 } 02639 case KHTMLPart::CaretBlink: 02640 // simply leave as is 02641 break; 02642 }/*end switch*/ 02643 }/*end if*/ 02644 #endif // KHTML_NO_CARET 02645 QScrollView::focusOutEvent( e ); 02646 } 02647 02648 void KHTMLView::slotScrollBarMoved() 02649 { 02650 if (!d->scrollingSelf) 02651 d->scrollBarMoved = true; 02652 } 02653 02654 void KHTMLView::timerEvent ( QTimerEvent *e ) 02655 { 02656 // kdDebug() << "timer event " << e->timerId() << endl; 02657 if ( e->timerId() == d->scrollTimerId ) { 02658 if( d->scrollSuspended ) 02659 return; 02660 switch (d->scrollDirection) { 02661 case KHTMLViewPrivate::ScrollDown: 02662 if (contentsY() + visibleHeight () >= contentsHeight()) 02663 d->newScrollTimer(this, 0); 02664 else 02665 scrollBy( 0, d->scrollBy ); 02666 break; 02667 case KHTMLViewPrivate::ScrollUp: 02668 if (contentsY() <= 0) 02669 d->newScrollTimer(this, 0); 02670 else 02671 scrollBy( 0, -d->scrollBy ); 02672 break; 02673 case KHTMLViewPrivate::ScrollRight: 02674 if (contentsX() + visibleWidth () >= contentsWidth()) 02675 d->newScrollTimer(this, 0); 02676 else 02677 scrollBy( d->scrollBy, 0 ); 02678 break; 02679 case KHTMLViewPrivate::ScrollLeft: 02680 if (contentsX() <= 0) 02681 d->newScrollTimer(this, 0); 02682 else 02683 scrollBy( -d->scrollBy, 0 ); 02684 break; 02685 } 02686 return; 02687 } 02688 else if ( e->timerId() == d->layoutTimerId ) { 02689 d->firstRelayout = false; 02690 d->dirtyLayout = true; 02691 layout(); 02692 } 02693 #ifndef KHTML_NO_CARET 02694 else if (d->m_caretViewContext 02695 && e->timerId() == d->m_caretViewContext->freqTimerId) { 02696 d->m_caretViewContext->visible = !d->m_caretViewContext->visible; 02697 if (d->m_caretViewContext->displayed) { 02698 updateContents(d->m_caretViewContext->x, d->m_caretViewContext->y, 02699 d->m_caretViewContext->width, 02700 d->m_caretViewContext->height); 02701 }/*end if*/ 02702 // if (d->m_caretViewContext->visible) cout << "|" << flush; 02703 // else cout << "" << flush; 02704 return; 02705 } 02706 #endif 02707 02708 if( m_part->xmlDocImpl() ) { 02709 DOM::DocumentImpl *document = m_part->xmlDocImpl(); 02710 khtml::RenderCanvas* root = static_cast<khtml::RenderCanvas *>(document->renderer()); 02711 02712 if ( root && !root->layouted() ) { 02713 killTimer(d->repaintTimerId); 02714 d->repaintTimerId = 0; 02715 scheduleRelayout(); 02716 return; 02717 } 02718 } 02719 02720 setStaticBackground(d->useSlowRepaints); 02721 02722 // kdDebug() << "scheduled repaint "<< d->repaintTimerId << endl; 02723 killTimer(d->repaintTimerId); 02724 d->repaintTimerId = 0; 02725 02726 QRegion updateRegion; 02727 QMemArray<QRect> rects = d->updateRegion.rects(); 02728 02729 d->updateRegion = QRegion(); 02730 02731 if ( rects.size() ) 02732 updateRegion = rects[0]; 02733 02734 for ( unsigned i = 1; i < rects.size(); ++i ) { 02735 QRect obR = updateRegion.boundingRect(); 02736 QRegion newRegion = updateRegion.unite(rects[i]); 02737 if (2*newRegion.boundingRect().height() > 3*obR.height() ) 02738 { 02739 repaintContents( obR ); 02740 updateRegion = rects[i]; 02741 } 02742 else 02743 updateRegion = newRegion; 02744 } 02745 02746 if ( !updateRegion.isNull() ) 02747 repaintContents( updateRegion.boundingRect() ); 02748 02749 if (d->dirtyLayout && !d->visibleWidgets.isEmpty()) { 02750 QWidget* w; 02751 d->dirtyLayout = false; 02752 02753 QRect visibleRect(contentsX(), contentsY(), visibleWidth(), visibleHeight()); 02754 QPtrList<RenderWidget> toRemove; 02755 for (QPtrDictIterator<QWidget> it(d->visibleWidgets); it.current(); ++it) { 02756 int xp = 0, yp = 0; 02757 w = it.current(); 02758 RenderWidget* rw = static_cast<RenderWidget*>( it.currentKey() ); 02759 if (!rw->absolutePosition(xp, yp) || 02760 !visibleRect.intersects(QRect(xp, yp, w->width(), w->height()))) 02761 toRemove.append(rw); 02762 } 02763 for (RenderWidget* r = toRemove.first(); r; r = toRemove.next()) 02764 if ( (w = d->visibleWidgets.take(r) ) ) 02765 addChild(w, 0, -500000); 02766 } 02767 if (d->accessKeysActivated) emit repaintAccessKeys(); 02768 } 02769 02770 void KHTMLView::scheduleRelayout(khtml::RenderObject * /*clippedObj*/) 02771 { 02772 if (!d->layoutSchedulingEnabled || d->layoutTimerId) 02773 return; 02774 02775 d->layoutTimerId = startTimer( m_part->xmlDocImpl() && m_part->xmlDocImpl()->parsing() 02776 ? 1000 : 0 ); 02777 } 02778 02779 void KHTMLView::unscheduleRelayout() 02780 { 02781 if (!d->layoutTimerId) 02782 return; 02783 02784 killTimer(d->layoutTimerId); 02785 d->layoutTimerId = 0; 02786 } 02787 02788 void KHTMLView::unscheduleRepaint() 02789 { 02790 if (!d->repaintTimerId) 02791 return; 02792 02793 killTimer(d->repaintTimerId); 02794 d->repaintTimerId = 0; 02795 } 02796 02797 void KHTMLView::scheduleRepaint(int x, int y, int w, int h) 02798 { 02799 bool parsing = !m_part->xmlDocImpl() || m_part->xmlDocImpl()->parsing(); 02800 02801 // kdDebug() << "parsing " << parsing << endl; 02802 // kdDebug() << "complete " << d->complete << endl; 02803 02804 int time = parsing ? 300 : ( !d->complete ? 100 : 20 ); 02805 02806 #ifdef DEBUG_FLICKER 02807 QPainter p; 02808 p.begin( viewport() ); 02809 02810 int vx, vy; 02811 contentsToViewport( x, y, vx, vy ); 02812 p.fillRect( vx, vy, w, h, Qt::red ); 02813 p.end(); 02814 #endif 02815 02816 d->updateRegion = d->updateRegion.unite(QRect(x,y,w,h)); 02817 02818 if ( !d->repaintTimerId ) 02819 d->repaintTimerId = startTimer( time ); 02820 02821 // kdDebug() << "starting timer " << time << endl; 02822 } 02823 02824 void KHTMLView::complete() 02825 { 02826 // kdDebug() << "KHTMLView::complete()" << endl; 02827 02828 d->complete = true; 02829 02830 // is there a relayout pending? 02831 if (d->layoutTimerId) 02832 { 02833 // kdDebug() << "requesting relayout now" << endl; 02834 // do it now 02835 killTimer(d->layoutTimerId); 02836 d->layoutTimerId = startTimer( 0 ); 02837 } 02838 02839 // is there a repaint pending? 02840 if (d->repaintTimerId) 02841 { 02842 // kdDebug() << "requesting repaint now" << endl; 02843 // do it now 02844 killTimer(d->repaintTimerId); 02845 d->repaintTimerId = startTimer( 20 ); 02846 } 02847 } 02848 02849 #ifndef KHTML_NO_CARET 02850 02851 // ### the dependencies on static functions are a nightmare. just be 02852 // hacky and include the implementation here. Clean me up, please. 02853 02854 #include "khtml_caret.cpp" 02855 02856 void KHTMLView::initCaret(bool keepSelection) 02857 { 02858 #if DEBUG_CARETMODE > 0 02859 kdDebug(6200) << "begin initCaret" << endl; 02860 #endif 02861 // save caretMoved state as moveCaretTo changes it 02862 if (m_part->xmlDocImpl()) { 02863 #if 0 02864 ElementImpl *listitem = m_part->xmlDocImpl()->getElementById("__test_element__"); 02865 if (listitem) dumpLineBoxes(static_cast<RenderFlow *>(listitem->renderer())); 02866 #endif 02867 d->caretViewContext(); 02868 bool cmoved = d->m_caretViewContext->caretMoved; 02869 if (m_part->d->caretNode().isNull()) { 02870 // set to document, position will be sanitized anyway 02871 m_part->d->caretNode() = m_part->document(); 02872 m_part->d->caretOffset() = 0L; 02873 // This sanity check is necessary for the not so unlikely case that 02874 // setEditable or setCaretMode is called before any render objects have 02875 // been created. 02876 if (!m_part->d->caretNode().handle()->renderer()) return; 02877 }/*end if*/ 02878 // kdDebug(6200) << "d->m_selectionStart " << m_part->d->m_selectionStart.handle() 02879 // << " d->m_selectionEnd " << m_part->d->m_selectionEnd.handle() << endl; 02880 // ### does not repaint the selection on keepSelection!=false 02881 moveCaretTo(m_part->d->caretNode().handle(), m_part->d->caretOffset(), !keepSelection); 02882 // kdDebug(6200) << "d->m_selectionStart " << m_part->d->m_selectionStart.handle() 02883 // << " d->m_selectionEnd " << m_part->d->m_selectionEnd.handle() << endl; 02884 d->m_caretViewContext->caretMoved = cmoved; 02885 }/*end if*/ 02886 #if DEBUG_CARETMODE > 0 02887 kdDebug(6200) << "end initCaret" << endl; 02888 #endif 02889 } 02890 02891 bool KHTMLView::caretOverrides() const 02892 { 02893 bool cm = m_part->isCaretMode(); 02894 bool dm = m_part->isEditable(); 02895 return cm && !dm ? false 02896 : (dm || m_part->d->caretNode().handle()->contentEditable()) 02897 && d->editorContext()->override; 02898 } 02899 02900 void KHTMLView::ensureNodeHasFocus(NodeImpl *node) 02901 { 02902 if (m_part->isCaretMode() || m_part->isEditable()) return; 02903 if (node->focused()) return; 02904 02905 // Find first ancestor whose "user-input" is "enabled" 02906 NodeImpl *firstAncestor = 0; 02907 while (node) { 02908 if (node->renderer() 02909 && node->renderer()->style()->userInput() != UI_ENABLED) 02910 break; 02911 firstAncestor = node; 02912 node = node->parentNode(); 02913 }/*wend*/ 02914 02915 if (!node) firstAncestor = 0; 02916 02917 DocumentImpl *doc = m_part->xmlDocImpl(); 02918 // ensure that embedded widgets don't lose their focus 02919 if (!firstAncestor && doc->focusNode() && doc->focusNode()->renderer() 02920 && doc->focusNode()->renderer()->isWidget()) 02921 return; 02922 02923 // Set focus node on the document 02924 #if DEBUG_CARETMODE > 1 02925 kdDebug(6200) << k_funcinfo << "firstAncestor " << firstAncestor << ": " 02926 << (firstAncestor ? firstAncestor->nodeName().string() : QString::null) << endl; 02927 #endif 02928 doc->setFocusNode(firstAncestor); 02929 emit m_part->nodeActivated(Node(firstAncestor)); 02930 } 02931 02932 void KHTMLView::recalcAndStoreCaretPos(CaretBox *hintBox) 02933 { 02934 if (!m_part || m_part->d->caretNode().isNull()) return; 02935 d->caretViewContext(); 02936 NodeImpl *caretNode = m_part->d->caretNode().handle(); 02937 #if DEBUG_CARETMODE > 0 02938 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; 02939 #endif 02940 caretNode->getCaret(m_part->d->caretOffset(), caretOverrides(), 02941 d->m_caretViewContext->x, d->m_caretViewContext->y, 02942 d->m_caretViewContext->width, 02943 d->m_caretViewContext->height); 02944 02945 if (hintBox && d->m_caretViewContext->x == -1) { 02946 #if DEBUG_CARETMODE > 1 02947 kdDebug(6200) << "using hint inline box coordinates" << endl; 02948 #endif 02949 RenderObject *r = caretNode->renderer(); 02950 const QFontMetrics &fm = r->style()->fontMetrics(); 02951 int absx, absy; 02952 r->containingBlock()->absolutePosition(absx, absy, 02953 false); // ### what about fixed? 02954 d->m_caretViewContext->x = absx + hintBox->xPos(); 02955 d->m_caretViewContext->y = absy + hintBox->yPos(); 02956 // + hintBox->baseline() - fm.ascent(); 02957 d->m_caretViewContext->width = 1; 02958 // ### firstline not regarded. But I think it can be safely neglected 02959 // as hint boxes are only used for empty lines. 02960 d->m_caretViewContext->height = fm.height(); 02961 }/*end if*/ 02962 02963 #if DEBUG_CARETMODE > 4 02964 // kdDebug(6200) << "freqTimerId: "<<d->m_caretViewContext->freqTimerId<<endl; 02965 #endif 02966 #if DEBUG_CARETMODE > 0 02967 kdDebug(6200) << "caret: ofs="<<m_part->d->caretOffset()<<" " 02968 <<" x="<<d->m_caretViewContext->x<<" y="<<d->m_caretViewContext->y 02969 <<" h="<<d->m_caretViewContext->height<<endl; 02970 #endif 02971 } 02972 02973 void KHTMLView::caretOn() 02974 { 02975 if (d->m_caretViewContext) { 02976 killTimer(d->m_caretViewContext->freqTimerId); 02977 02978 if (hasFocus() || d->m_caretViewContext->displayNonFocused 02979 == KHTMLPart::CaretBlink) { 02980 d->m_caretViewContext->freqTimerId = startTimer(500); 02981 } else { 02982 d->m_caretViewContext->freqTimerId = -1; 02983 }/*end if*/ 02984 02985 d->m_caretViewContext->visible = true; 02986 if ((d->m_caretViewContext->displayed = (hasFocus() 02987 || d->m_caretViewContext->displayNonFocused 02988 != KHTMLPart::CaretInvisible))) { 02989 updateContents(d->m_caretViewContext->x, d->m_caretViewContext->y, 02990 d->m_caretViewContext->width, 02991 d->m_caretViewContext->height); 02992 }/*end if*/ 02993 // kdDebug(6200) << "caret on" << endl; 02994 }/*end if*/ 02995 } 02996 02997 void KHTMLView::caretOff() 02998 { 02999 if (d->m_caretViewContext) { 03000 killTimer(d->m_caretViewContext->freqTimerId); 03001 d->m_caretViewContext->freqTimerId = -1; 03002 d->m_caretViewContext->displayed = false; 03003 if (d->m_caretViewContext->visible) { 03004 d->m_caretViewContext->visible = false; 03005 updateContents(d->m_caretViewContext->x, d->m_caretViewContext->y, 03006 d->m_caretViewContext->width, 03007 d->m_caretViewContext->height); 03008 }/*end if*/ 03009 // kdDebug(6200) << "caret off" << endl; 03010 }/*end if*/ 03011 } 03012 03013 void KHTMLView::showCaret(bool forceRepaint) 03014 { 03015 if (d->m_caretViewContext) { 03016 d->m_caretViewContext->displayed = true; 03017 if (d->m_caretViewContext->visible) { 03018 if (!forceRepaint) { 03019 updateContents(d->m_caretViewContext->x, d->m_caretViewContext->y, 03020 d->m_caretViewContext->width, 03021 d->m_caretViewContext->height); 03022 } else { 03023 repaintContents(d->m_caretViewContext->x, d->m_caretViewContext->y, 03024 d->m_caretViewContext->width, 03025 d->m_caretViewContext->height); 03026 }/*end if*/ 03027 }/*end if*/ 03028 // kdDebug(6200) << "caret shown" << endl; 03029 }/*end if*/ 03030 } 03031 03032 bool KHTMLView::foldSelectionToCaret(NodeImpl *startNode, long startOffset, 03033 NodeImpl *endNode, long endOffset) 03034 { 03035 m_part->d->m_selectionStart = m_part->d->m_selectionEnd = m_part->d->caretNode(); 03036 m_part->d->m_startOffset = m_part->d->m_endOffset = m_part->d->caretOffset(); 03037 m_part->d->m_extendAtEnd = true; 03038 03039 bool folded = startNode != endNode || startOffset != endOffset; 03040 03041 // Only clear the selection if there has been one. 03042 if (folded) { 03043 m_part->xmlDocImpl()->clearSelection(); 03044 }/*end if*/ 03045 03046 return folded; 03047 } 03048 03049 void KHTMLView::hideCaret() 03050 { 03051 if (d->m_caretViewContext) { 03052 if (d->m_caretViewContext->visible) { 03053 // kdDebug(6200) << "redraw caret hidden" << endl; 03054 d->m_caretViewContext->visible = false; 03055 // force repaint, otherwise the event won't be handled 03056 // before the focus leaves the window 03057 repaintContents(d->m_caretViewContext->x, d->m_caretViewContext->y, 03058 d->m_caretViewContext->width, 03059 d->m_caretViewContext->height); 03060 d->m_caretViewContext->visible = true; 03061 }/*end if*/ 03062 d->m_caretViewContext->displayed = false; 03063 // kdDebug(6200) << "caret hidden" << endl; 03064 }/*end if*/ 03065 } 03066 03067 int KHTMLView::caretDisplayPolicyNonFocused() const 03068 { 03069 if (d->m_caretViewContext) 03070 return d->m_caretViewContext->displayNonFocused; 03071 else 03072 return KHTMLPart::CaretInvisible; 03073 } 03074 03075 void KHTMLView::setCaretDisplayPolicyNonFocused(int policy) 03076 { 03077 d->caretViewContext(); 03078 // int old = d->m_caretViewContext->displayNonFocused; 03079 d->m_caretViewContext->displayNonFocused = (KHTMLPart::CaretDisplayPolicy)policy; 03080 03081 // make change immediately take effect if not focused 03082 if (!hasFocus()) { 03083 switch (d->m_caretViewContext->displayNonFocused) { 03084 case KHTMLPart::CaretInvisible: 03085 hideCaret(); 03086 break; 03087 case KHTMLPart::CaretBlink: 03088 if (d->m_caretViewContext->freqTimerId != -1) break; 03089 d->m_caretViewContext->freqTimerId = startTimer(500); 03090 // fall through 03091 case KHTMLPart::CaretVisible: 03092 d->m_caretViewContext->displayed = true; 03093 showCaret(); 03094 break; 03095 }/*end switch*/ 03096 }/*end if*/ 03097 } 03098 03099 bool KHTMLView::placeCaret(CaretBox *hintBox) 03100 { 03101 CaretViewContext *cv = d->caretViewContext(); 03102 caretOff(); 03103 NodeImpl *caretNode = m_part->d->caretNode().handle(); 03104 // ### why is it sometimes null? 03105 if (!caretNode || !caretNode->renderer()) return false; 03106 ensureNodeHasFocus(caretNode); 03107 if (m_part->isCaretMode() || m_part->isEditable() 03108 || caretNode->renderer()->style()->userInput() == UI_ENABLED) { 03109 recalcAndStoreCaretPos(hintBox); 03110 03111 cv->origX = cv->x; 03112 03113 caretOn(); 03114 return true; 03115 }/*end if*/ 03116 return false; 03117 } 03118 03119 void KHTMLView::ensureCaretVisible() 03120 { 03121 CaretViewContext *cv = d->m_caretViewContext; 03122 if (!cv) return; 03123 ensureVisible(cv->x, cv->y, cv->width, cv->height); 03124 d->scrollBarMoved = false; 03125 } 03126 03127 bool KHTMLView::extendSelection(NodeImpl *oldStartSel, long oldStartOfs, 03128 NodeImpl *oldEndSel, long oldEndOfs) 03129 { 03130 bool changed = false; 03131 if (m_part->d->m_selectionStart == m_part->d->m_selectionEnd 03132 && m_part->d->m_startOffset == m_part->d->m_endOffset) { 03133 changed = foldSelectionToCaret(oldStartSel, oldStartOfs, oldEndSel, oldEndOfs); 03134 m_part->d->m_extendAtEnd = true; 03135 } else do { 03136 changed = m_part->d->m_selectionStart.handle() != oldStartSel 03137 || m_part->d->m_startOffset != oldStartOfs 03138 || m_part->d->m_selectionEnd.handle() != oldEndSel 03139 || m_part->d->m_endOffset != oldEndOfs; 03140 if (!changed) break; 03141 03142 // determine start position -- caret position is always at end. 03143 NodeImpl *startNode; 03144 long startOffset; 03145 if (m_part->d->m_extendAtEnd) { 03146 startNode = m_part->d->m_selectionStart.handle(); 03147 startOffset = m_part->d->m_startOffset; 03148 } else { 03149 startNode = m_part->d->m_selectionEnd.handle(); 03150 startOffset = m_part->d->m_endOffset; 03151 m_part->d->m_selectionEnd = m_part->d->m_selectionStart; 03152 m_part->d->m_endOffset = m_part->d->m_startOffset; 03153 m_part->d->m_extendAtEnd = true; 03154 }/*end if*/ 03155 03156 bool swapNeeded = false; 03157 if (!m_part->d->m_selectionEnd.isNull() && startNode) { 03158 swapNeeded = RangeImpl::compareBoundaryPoints(startNode, startOffset, 03159 m_part->d->m_selectionEnd.handle(), 03160 m_part->d->m_endOffset) >= 0; 03161 }/*end if*/ 03162 03163 m_part->d->m_selectionStart = startNode; 03164 m_part->d->m_startOffset = startOffset; 03165 03166 if (swapNeeded) { 03167 m_part->xmlDocImpl()->setSelection(m_part->d->m_selectionEnd.handle(), 03168 m_part->d->m_endOffset, m_part->d->m_selectionStart.handle(), 03169 m_part->d->m_startOffset); 03170 } else { 03171 m_part->xmlDocImpl()->setSelection(m_part->d->m_selectionStart.handle(), 03172 m_part->d->m_startOffset, m_part->d->m_selectionEnd.handle(), 03173 m_part->d->m_endOffset); 03174 }/*end if*/ 03175 } while(false);/*end if*/ 03176 return changed; 03177 } 03178 03179 void KHTMLView::updateSelection(NodeImpl *oldStartSel, long oldStartOfs, 03180 NodeImpl *oldEndSel, long oldEndOfs) 03181 { 03182 if (m_part->d->m_selectionStart == m_part->d->m_selectionEnd 03183 && m_part->d->m_startOffset == m_part->d->m_endOffset) { 03184 if (foldSelectionToCaret(oldStartSel, oldStartOfs, oldEndSel, oldEndOfs)) { 03185 m_part->emitSelectionChanged(); 03186 }/*end if*/ 03187 m_part->d->m_extendAtEnd = true; 03188 } else { 03189 // check if the extending end has passed the immobile end 03190 if (!m_part->d->m_selectionEnd.isNull() && !m_part->d->m_selectionEnd.isNull()) { 03191 bool swapNeeded = RangeImpl::compareBoundaryPoints( 03192 m_part->d->m_selectionStart.handle(), m_part->d->m_startOffset, 03193 m_part->d->m_selectionEnd.handle(), m_part->d->m_endOffset) >= 0; 03194 if (swapNeeded) { 03195 DOM::Node tmpNode = m_part->d->m_selectionStart; 03196 long tmpOffset = m_part->d->m_startOffset; 03197 m_part->d->m_selectionStart = m_part->d->m_selectionEnd; 03198 m_part->d->m_startOffset = m_part->d->m_endOffset; 03199 m_part->d->m_selectionEnd = tmpNode; 03200 m_part->d->m_endOffset = tmpOffset; 03201 m_part->d->m_startBeforeEnd = true; 03202 m_part->d->m_extendAtEnd = !m_part->d->m_extendAtEnd; 03203 }/*end if*/ 03204 }/*end if*/ 03205 03206 m_part->xmlDocImpl()->setSelection(m_part->d->m_selectionStart.handle(), 03207 m_part->d->m_startOffset, m_part->d->m_selectionEnd.handle(), 03208 m_part->d->m_endOffset); 03209 m_part->emitSelectionChanged(); 03210 }/*end if*/ 03211 } 03212 03213 void KHTMLView::caretKeyPressEvent(QKeyEvent *_ke) 03214 { 03215 NodeImpl *oldStartSel = m_part->d->m_selectionStart.handle(); 03216 long oldStartOfs = m_part->d->m_startOffset; 03217 NodeImpl *oldEndSel = m_part->d->m_selectionEnd.handle(); 03218 long oldEndOfs = m_part->d->m_endOffset; 03219 03220 NodeImpl *oldCaretNode = m_part->d->caretNode().handle(); 03221 long oldOffset = m_part->d->caretOffset(); 03222 03223 bool ctrl = _ke->state() & ControlButton; 03224 03225 // FIXME: this is that widely indented because I will write ifs around it. 03226 switch(_ke->key()) { 03227 case Key_Space: 03228 break; 03229 03230 case Key_Down: 03231 moveCaretNextLine(1); 03232 break; 03233 03234 case Key_Up: 03235 moveCaretPrevLine(1); 03236 break; 03237 03238 case Key_Left: 03239 moveCaretBy(false, ctrl ? CaretByWord : CaretByCharacter, 1); 03240 break; 03241 03242 case Key_Right: 03243 moveCaretBy(true, ctrl ? CaretByWord : CaretByCharacter, 1); 03244 break; 03245 03246 case Key_Next: 03247 moveCaretNextPage(); 03248 break; 03249 03250 case Key_Prior: 03251 moveCaretPrevPage(); 03252 break; 03253 03254 case Key_Home: 03255 if (ctrl) 03256 moveCaretToDocumentBoundary(false); 03257 else 03258 moveCaretToLineBegin(); 03259 break; 03260 03261 case Key_End: 03262 if (ctrl) 03263 moveCaretToDocumentBoundary(true); 03264 else 03265 moveCaretToLineEnd(); 03266 break; 03267 03268 }/*end switch*/ 03269 03270 if ((m_part->d->caretNode().handle() != oldCaretNode 03271 || m_part->d->caretOffset() != oldOffset) 03272 // node should never be null, but faulty conditions may cause it to be 03273 && !m_part->d->caretNode().isNull()) { 03274 03275 d->m_caretViewContext->caretMoved = true; 03276 03277 if (_ke->state() & ShiftButton) { // extend selection 03278 updateSelection(oldStartSel, oldStartOfs, oldEndSel, oldEndOfs); 03279 } else { // clear any selection 03280 if (foldSelectionToCaret(oldStartSel, oldStartOfs, oldEndSel, oldEndOfs)) 03281 m_part->emitSelectionChanged(); 03282 }/*end if*/ 03283 03284 m_part->emitCaretPositionChanged(m_part->d->caretNode(), m_part->d->caretOffset()); 03285 }/*end if*/ 03286 03287 _ke->accept(); 03288 } 03289 03290 bool KHTMLView::moveCaretTo(NodeImpl *node, long offset, bool clearSel) 03291 { 03292 if (!node) return false; 03293 ElementImpl *baseElem = determineBaseElement(node); 03294 RenderFlow *base = static_cast<RenderFlow *>(baseElem ? baseElem->renderer() : 0); 03295 if (!node) return false; 03296 03297 // need to find out the node's inline box. If there is none, this function 03298 // will snap to the next node that has one. This is necessary to make the 03299 // caret visible in any case. 03300 CaretBoxLineDeleter cblDeleter; 03301 // RenderBlock *cb; 03302 long r_ofs; 03303 CaretBoxIterator cbit; 03304 CaretBoxLine *cbl = findCaretBoxLine(node, offset, &cblDeleter, base, r_ofs, cbit); 03305 if(!cbl) { 03306 kdWarning() << "KHTMLView::moveCaretTo - findCaretBoxLine() returns NULL" << endl; 03307 return false; 03308 } 03309 03310 #if DEBUG_CARETMODE > 3 03311 if (cbl) kdDebug(6200) << cbl->information() << endl; 03312 #endif 03313 CaretBox *box = *cbit; 03314 if (cbit != cbl->end() && box->object() != node->renderer()) { 03315 if (box->object()->element()) { 03316 mapRenderPosToDOMPos(box->object(), r_ofs, box->isOutside(), 03317 box->isOutsideEnd(), node, offset); 03318 //if (!outside) offset = node->minOffset(); 03319 #if DEBUG_CARETMODE > 1 03320 kdDebug(6200) << "set new node " << node->nodeName().string() << "@" << node << endl; 03321 #endif 03322 } else { // box has no associated element -> do not use 03323 // this case should actually never happen. 03324 box = 0; 03325 kdError(6200) << "Box contains no node! Crash imminent" << endl; 03326 }/*end if*/ 03327 } 03328 03329 NodeImpl *oldStartSel = m_part->d->m_selectionStart.handle(); 03330 long oldStartOfs = m_part->d->m_startOffset; 03331 NodeImpl *oldEndSel = m_part->d->m_selectionEnd.handle(); 03332 long oldEndOfs = m_part->d->m_endOffset; 03333 03334 // test for position change 03335 bool posChanged = m_part->d->caretNode().handle() != node 03336 || m_part->d->caretOffset() != offset; 03337 bool selChanged = false; 03338 03339 m_part->d->caretNode() = node; 03340 m_part->d->caretOffset() = offset; 03341 if (clearSel || !oldStartSel || !oldEndSel) { 03342 selChanged = foldSelectionToCaret(oldStartSel, oldStartOfs, oldEndSel, oldEndOfs); 03343 } else { 03344 //kdDebug(6200) << "moveToCaret: extendSelection: m_extendAtEnd " << m_part->d->m_extendAtEnd << endl; 03345 //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; 03346 selChanged = extendSelection(oldStartSel, oldStartOfs, oldEndSel, oldEndOfs); 03347 //kdDebug(6200) << "after extendSelection: m_extendAtEnd " << m_part->d->m_extendAtEnd << endl; 03348 //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; 03349 }/*end if*/ 03350 03351 d->caretViewContext()->caretMoved = true; 03352 03353 bool visible_caret = placeCaret(box); 03354 03355 // FIXME: if the old position was !visible_caret, and the new position is 03356 // also, then two caretPositionChanged signals with a null Node are 03357 // emitted in series. 03358 if (posChanged) { 03359 m_part->emitCaretPositionChanged(visible_caret ? node : 0, offset); 03360 }/*end if*/ 03361 03362 return selChanged; 03363 } 03364 03365 void KHTMLView::moveCaretByLine(bool next, int count) 03366 { 03367 Node &caretNodeRef = m_part->d->caretNode(); 03368 if (caretNodeRef.isNull()) return; 03369 03370 NodeImpl *caretNode = caretNodeRef.handle(); 03371 // kdDebug(6200) << ": caretNode=" << caretNode << endl; 03372 long offset = m_part->d->caretOffset(); 03373 03374 CaretViewContext *cv = d->caretViewContext(); 03375 03376 ElementImpl *baseElem = determineBaseElement(caretNode); 03377 LinearDocument ld(m_part, caretNode, offset, LeafsOnly, baseElem); 03378 03379 ErgonomicEditableLineIterator it(ld.current(), cv->origX); 03380 03381 // move count lines vertically 03382 while (count > 0 && it != ld.end() && it != ld.preBegin()) { 03383 count--; 03384 if (next) ++it; else --it; 03385 }/*wend*/ 03386 03387 // Nothing? Then leave everything as is. 03388 if (it == ld.end() || it == ld.preBegin()) return; 03389 03390 int x, absx, absy; 03391 CaretBox *caretBox = nearestCaretBox(it, d->m_caretViewContext, x, absx, absy); 03392 03393 placeCaretOnLine(caretBox, x, absx, absy); 03394 } 03395 03396 void KHTMLView::placeCaretOnLine(CaretBox *caretBox, int x, int absx, int absy) 03397 { 03398 // paranoia sanity check 03399 if (!caretBox) return; 03400 03401 RenderObject *caretRender = caretBox->object(); 03402 03403 #if DEBUG_CARETMODE > 0 03404 kdDebug(6200) << "got valid caretBox " << caretBox << endl; 03405 kdDebug(6200) << "xPos: " << caretBox->xPos() << " yPos: " << caretBox->yPos() 03406 << " width: " << caretBox->width() << " height: " << caretBox->height() << endl; 03407 InlineTextBox *tb = static_cast<InlineTextBox *>(caretBox->inlineBox()); 03408 if (caretBox->isInlineTextBox()) { kdDebug(6200) << "contains \"" << QString(static_cast<RenderText *>(tb->object())->str->s + tb->m_start, tb->m_len) << "\"" << endl;} 03409 #endif 03410 // inquire height of caret 03411 int caretHeight = caretBox->height(); 03412 bool isText = caretBox->isInlineTextBox(); 03413 int yOfs = 0; // y-offset for text nodes 03414 if (isText) { 03415 // text boxes need extrawurst 03416 RenderText *t = static_cast<RenderText *>(caretRender); 03417 const QFontMetrics &fm = t->metrics(caretBox->inlineBox()->m_firstLine); 03418 caretHeight = fm.height(); 03419 yOfs = caretBox->inlineBox()->baseline() - fm.ascent(); 03420 }/*end if*/ 03421 03422 caretOff(); 03423 03424 // set new caret node 03425 NodeImpl *caretNode; 03426 long &offset = m_part->d->caretOffset(); 03427 mapRenderPosToDOMPos(caretRender, offset, caretBox->isOutside(), 03428 caretBox->isOutsideEnd(), caretNode, offset); 03429 03430 // set all variables not needing special treatment 03431 d->m_caretViewContext->y = caretBox->yPos() + yOfs; 03432 d->m_caretViewContext->height = caretHeight; 03433 d->m_caretViewContext->width = 1; // FIXME: regard override 03434 03435 int xPos = caretBox->xPos(); 03436 int caretBoxWidth = caretBox->width(); 03437 d->m_caretViewContext->x = xPos; 03438 03439 if (!caretBox->isOutside()) { 03440 // before or at beginning of inline box -> place at beginning 03441 long r_ofs = 0; 03442 if (x <= xPos) { 03443 r_ofs = caretBox->minOffset(); 03444 // somewhere within this block 03445 } else if (x > xPos && x <= xPos + caretBoxWidth) { 03446 if (isText) { // find out where exactly 03447 r_ofs = static_cast<InlineTextBox *>(caretBox->inlineBox()) 03448 ->offsetForPoint(x, d->m_caretViewContext->x); 03449 #if DEBUG_CARETMODE > 2 03450 kdDebug(6200) << "deviation from origX " << d->m_caretViewContext->x - x << endl; 03451 #endif 03452 #if 0 03453 } else { // snap to nearest end 03454 if (xPos + caretBoxWidth - x < x - xPos) { 03455 d->m_caretViewContext->x = xPos + caretBoxWidth; 03456 r_ofs = caretNode ? caretNode->maxOffset() : 1; 03457 } else { 03458 d->m_caretViewContext->x = xPos; 03459 r_ofs = caretNode ? caretNode->minOffset() : 0; 03460 }/*end if*/ 03461 #endif 03462 }/*end if*/ 03463 } else { // after the inline box -> place at end 03464 d->m_caretViewContext->x = xPos + caretBoxWidth; 03465 r_ofs = caretBox->maxOffset(); 03466 }/*end if*/ 03467 offset = r_ofs; 03468 }/*end if*/ 03469 #if DEBUG_CARETMODE > 0 03470 kdDebug(6200) << "new offset: " << offset << endl; 03471 #endif 03472 03473 m_part->d->caretNode() = caretNode; 03474 m_part->d->caretOffset() = offset; 03475 03476 d->m_caretViewContext->x += absx; 03477 d->m_caretViewContext->y += absy; 03478 03479 #if DEBUG_CARETMODE > 1 03480 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; 03481 #endif 03482 03483 ensureVisible(d->m_caretViewContext->x, d->m_caretViewContext->y, 03484 d->m_caretViewContext->width, d->m_caretViewContext->height); 03485 d->scrollBarMoved = false; 03486 03487 ensureNodeHasFocus(caretNode); 03488 caretOn(); 03489 } 03490 03491 void KHTMLView::moveCaretToLineBoundary(bool end) 03492 { 03493 Node &caretNodeRef = m_part->d->caretNode(); 03494 if (caretNodeRef.isNull()) return; 03495 03496 NodeImpl *caretNode = caretNodeRef.handle(); 03497 // kdDebug(6200) << ": caretNode=" << caretNode << endl; 03498 long offset = m_part->d->caretOffset(); 03499 03500 ElementImpl *baseElem = determineBaseElement(caretNode); 03501 LinearDocument ld(m_part, caretNode, offset, LeafsOnly, baseElem); 03502 03503 EditableLineIterator it = ld.current(); 03504 if (it == ld.end()) return; // should not happen, but who knows 03505 03506 EditableCaretBoxIterator fbit(it, end); 03507 Q_ASSERT(fbit != (*it)->end() && fbit != (*it)->preBegin()); 03508 CaretBox *b = *fbit; 03509 03510 RenderObject *cb = b->containingBlock(); 03511 int absx, absy; 03512 03513 if (cb) cb->absolutePosition(absx,absy); 03514 else absx = absy = 0; 03515 03516 int x = b->xPos() + (end && !b->isOutside() ? b->width() : 0); 03517 d->m_caretViewContext->origX = absx + x; 03518 placeCaretOnLine(b, x, absx, absy); 03519 } 03520 03521 void KHTMLView::moveCaretToDocumentBoundary(bool end) 03522 { 03523 Node &caretNodeRef = m_part->d->caretNode(); 03524 if (caretNodeRef.isNull()) return; 03525 03526 NodeImpl *caretNode = caretNodeRef.handle(); 03527 // kdDebug(6200) << ": caretNode=" << caretNode << endl; 03528 long offset = m_part->d->caretOffset(); 03529 03530 ElementImpl *baseElem = determineBaseElement(caretNode); 03531 LinearDocument ld(m_part, caretNode, offset, IndicatedFlows, baseElem); 03532 03533 EditableLineIterator it(end ? ld.preEnd() : ld.begin(), end); 03534 if (it == ld.end() || it == ld.preBegin()) return; // should not happen, but who knows 03535 03536 EditableCaretBoxIterator fbit = it; 03537 Q_ASSERT(fbit != (*it)->end() && fbit != (*it)->preBegin()); 03538 CaretBox *b = *fbit; 03539 03540 RenderObject *cb = (*it)->containingBlock(); 03541 int absx, absy; 03542 03543 if (cb) cb->absolutePosition(absx, absy); 03544 else absx = absy = 0; 03545 03546 int x = b->xPos()/* + (end ? b->width() : 0) reactivate for rtl*/; 03547 d->m_caretViewContext->origX = absx + x; 03548 placeCaretOnLine(b, x, absx, absy); 03549 } 03550 03551 void KHTMLView::moveCaretBy(bool next, CaretMovement cmv, int count) 03552 { 03553 if (!m_part) return; 03554 Node &caretNodeRef = m_part->d->caretNode(); 03555 if (caretNodeRef.isNull()) return; 03556 03557 NodeImpl *caretNode = caretNodeRef.handle(); 03558 // kdDebug(6200) << ": caretNode=" << caretNode << endl; 03559 long &offset = m_part->d->caretOffset(); 03560 03561 ElementImpl *baseElem = determineBaseElement(caretNode); 03562 CaretAdvancePolicy advpol = cmv != CaretByWord ? IndicatedFlows : LeafsOnly; 03563 LinearDocument ld(m_part, caretNode, offset, advpol, baseElem); 03564 03565 EditableCharacterIterator it(&ld); 03566 while (!it.isEnd() && count > 0) { 03567 count--; 03568 if (cmv == CaretByCharacter) { 03569 if (next) ++it; 03570 else --it; 03571 } else if (cmv == CaretByWord) { 03572 if (next) moveItToNextWord(it); 03573 else moveItToPrevWord(it); 03574 }/*end if*/ 03575 //kdDebug(6200) << "movecaret" << endl; 03576 }/*wend*/ 03577 CaretBox *hintBox = 0; // make gcc uninit warning disappear 03578 if (!it.isEnd()) { 03579 NodeImpl *node = caretNodeRef.handle(); 03580 hintBox = it.caretBox(); 03581 //kdDebug(6200) << "hintBox = " << hintBox << endl; 03582 //kdDebug(6200) << " outside " << hintBox->isOutside() << " outsideEnd " << hintBox->isOutsideEnd() << " r " << it.renderer() << " ofs " << it.offset() << " cb " << hintBox->containingBlock() << endl; 03583 mapRenderPosToDOMPos(it.renderer(), it.offset(), hintBox->isOutside(), 03584 hintBox->isOutsideEnd(), node, offset); 03585 //kdDebug(6200) << "mapRTD" << endl; 03586 caretNodeRef = node; 03587 #if DEBUG_CARETMODE > 2 03588 kdDebug(6200) << "set by valid node " << node << " " << (node?node->nodeName().string():QString::null) << " offset: " << offset << endl; 03589 #endif 03590 } else { 03591 offset = next ? caretNode->maxOffset() : caretNode->minOffset(); 03592 #if DEBUG_CARETMODE > 0 03593 kdDebug(6200) << "set by INvalid node. offset: " << offset << endl; 03594 #endif 03595 }/*end if*/ 03596 placeCaretOnChar(hintBox); 03597 } 03598 03599 void KHTMLView::placeCaretOnChar(CaretBox *hintBox) 03600 { 03601 caretOff(); 03602 recalcAndStoreCaretPos(hintBox); 03603 ensureVisible(d->m_caretViewContext->x, d->m_caretViewContext->y, 03604 d->m_caretViewContext->width, d->m_caretViewContext->height); 03605 d->m_caretViewContext->origX = d->m_caretViewContext->x; 03606 d->scrollBarMoved = false; 03607 #if DEBUG_CARETMODE > 3 03608 //if (caretNode->isTextNode()) kdDebug(6200) << "text[0] = " << (int)*((TextImpl *)caretNode)->data().unicode() << " text :\"" << ((TextImpl *)caretNode)->data().string() << "\"" << endl; 03609 #endif 03610 ensureNodeHasFocus(m_part->d->caretNode().handle()); 03611 caretOn(); 03612 } 03613 03614 void KHTMLView::moveCaretByPage(bool next) 03615 { 03616 Node &caretNodeRef = m_part->d->caretNode(); 03617 03618 NodeImpl *caretNode = caretNodeRef.handle(); 03619 // kdDebug(6200) << ": caretNode=" << caretNode << endl; 03620 long offset = m_part->d->caretOffset(); 03621 03622 int offs = (clipper()->height() < 30) ? clipper()->height() : 30; 03623 // Minimum distance the caret must be moved 03624 int mindist = clipper()->height() - offs; 03625 03626 CaretViewContext *cv = d->caretViewContext(); 03627 // int y = cv->y; // we always measure the top border 03628 03629 ElementImpl *baseElem = determineBaseElement(caretNode); 03630 LinearDocument ld(m_part, caretNode, offset, LeafsOnly, baseElem); 03631 03632 ErgonomicEditableLineIterator it(ld.current(), cv->origX); 03633 03634 moveIteratorByPage(ld, it, mindist, next); 03635 03636 int x, absx, absy; 03637 CaretBox *caretBox = nearestCaretBox(it, d->m_caretViewContext, x, absx, absy); 03638 03639 placeCaretOnLine(caretBox, x, absx, absy); 03640 } 03641 03642 void KHTMLView::moveCaretPrevWord() 03643 { 03644 moveCaretBy(false, CaretByWord, 1); 03645 } 03646 03647 void KHTMLView::moveCaretNextWord() 03648 { 03649 moveCaretBy(true, CaretByWord, 1); 03650 } 03651 03652 void KHTMLView::moveCaretPrevLine(int n) 03653 { 03654 moveCaretByLine(false, n); 03655 } 03656 03657 void KHTMLView::moveCaretNextLine(int n) 03658 { 03659 moveCaretByLine(true, n); 03660 } 03661 03662 void KHTMLView::moveCaretPrevPage() 03663 { 03664 moveCaretByPage(false); 03665 } 03666 03667 void KHTMLView::moveCaretNextPage() 03668 { 03669 moveCaretByPage(true); 03670 } 03671 03672 void KHTMLView::moveCaretToLineBegin() 03673 { 03674 moveCaretToLineBoundary(false); 03675 } 03676 03677 void KHTMLView::moveCaretToLineEnd() 03678 { 03679 moveCaretToLineBoundary(true); 03680 } 03681 03682 #endif // KHTML_NO_CARET 03683 03684 #undef DEBUG_CARETMODE
KDE Logo
This file is part of the documentation for khtml Library Version 3.3.0.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Wed Sep 29 09:42:28 2004 by doxygen 1.3.8 written by Dimitri van Heesch, © 1997-2003