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, 01939 KMIN(rec.left()+rec.width()/2, contentsWidth() - lab->width()), 01940 KMIN(rec.top()+rec.height()/2, contentsHeight() - lab->height())); 01941 showChild(lab); 01942 } 01943 } 01944 } 01945 } 01946 01947 void KHTMLView::accessKeysTimeout() 01948 { 01949 d->accessKeysActivated=false; 01950 d->accessKeysPreActivate = false; 01951 m_part->setStatusBarText(QString::null, KHTMLPart::BarOverrideText); 01952 emit hideAccessKeys(); 01953 } 01954 01955 // Handling of the HTML accesskey attribute. 01956 bool KHTMLView::handleAccessKey( const QKeyEvent* ev ) 01957 { 01958 // Qt interprets the keyevent also with the modifiers, and ev->text() matches that, 01959 // but this code must act as if the modifiers weren't pressed 01960 QChar c; 01961 if( ev->key() >= Key_A && ev->key() <= Key_Z ) 01962 c = 'A' + ev->key() - Key_A; 01963 else if( ev->key() >= Key_0 && ev->key() <= Key_9 ) 01964 c = '0' + ev->key() - Key_0; 01965 else { 01966 // TODO fake XKeyEvent and XLookupString ? 01967 // This below seems to work e.g. for eacute though. 01968 if( ev->text().length() == 1 ) 01969 c = ev->text()[ 0 ]; 01970 } 01971 if( c.isNull()) 01972 return false; 01973 return focusNodeWithAccessKey( c ); 01974 } 01975 01976 bool KHTMLView::focusNodeWithAccessKey( QChar c, KHTMLView* caller ) 01977 { 01978 DocumentImpl *doc = m_part->xmlDocImpl(); 01979 if( !doc ) 01980 return false; 01981 ElementImpl* node = doc->findAccessKeyElement( c ); 01982 if( !node ) { 01983 QPtrList<KParts::ReadOnlyPart> frames = m_part->frames(); 01984 for( QPtrListIterator<KParts::ReadOnlyPart> it( frames ); 01985 it != NULL; 01986 ++it ) { 01987 if( !(*it)->inherits( "KHTMLPart" )) 01988 continue; 01989 KHTMLPart* part = static_cast< KHTMLPart* >( *it ); 01990 if( part->view() && part->view() != caller 01991 && part->view()->focusNodeWithAccessKey( c, this )) 01992 return true; 01993 } 01994 // pass up to the parent 01995 if (m_part->parentPart() && m_part->parentPart()->view() 01996 && m_part->parentPart()->view() != caller ) 01997 return m_part->parentPart()->view()->focusNodeWithAccessKey( c, this ); 01998 return false; 01999 } 02000 02001 // Scroll the view as necessary to ensure that the new focus node is visible 02002 #ifndef KHTML_NO_CARET 02003 // if it's an editable element, activate the caret 02004 if (!m_part->isCaretMode() && !m_part->isEditable() 02005 && node->contentEditable()) { 02006 d->caretViewContext(); 02007 moveCaretTo(node, 0L, true); 02008 } else { 02009 caretOff(); 02010 } 02011 #endif // KHTML_NO_CARET 02012 02013 QRect r = node->getRect(); 02014 ensureVisible( r.right(), r.bottom()); 02015 ensureVisible( r.left(), r.top()); 02016 02017 Node guard( node ); 02018 if( node->isSelectable()) { 02019 if (node->id()==ID_LABEL) { 02020 // if Accesskey is a label, give focus to the label's referrer. 02021 node=static_cast<ElementImpl *>(static_cast< HTMLLabelElementImpl* >( node )->getFormElement()); 02022 if (!node) return true; 02023 guard = node; 02024 } 02025 // Set focus node on the document 02026 m_part->xmlDocImpl()->setFocusNode(node); 02027 if( node != NULL && node->hasOneRef()) // deleted, only held by guard 02028 return true; 02029 emit m_part->nodeActivated(Node(node)); 02030 if( node != NULL && node->hasOneRef()) 02031 return true; 02032 } 02033 02034 switch( node->id()) { 02035 case ID_A: 02036 static_cast< HTMLAnchorElementImpl* >( node )->click(); 02037 break; 02038 case ID_INPUT: 02039 static_cast< HTMLInputElementImpl* >( node )->click(); 02040 break; 02041 case ID_BUTTON: 02042 static_cast< HTMLButtonElementImpl* >( node )->click(); 02043 break; 02044 case ID_AREA: 02045 static_cast< HTMLAreaElementImpl* >( node )->click(); 02046 break; 02047 case ID_TEXTAREA: 02048 break; // just focusing it is enough 02049 case ID_LEGEND: 02050 // TODO 02051 break; 02052 } 02053 return true; 02054 } 02055 02056 void KHTMLView::setMediaType( const QString &medium ) 02057 { 02058 m_medium = medium; 02059 } 02060 02061 QString KHTMLView::mediaType() const 02062 { 02063 return m_medium; 02064 } 02065 02066 void KHTMLView::setWidgetVisible(RenderWidget* w, bool vis) 02067 { 02068 if (vis) { 02069 d->visibleWidgets.replace(w, w->widget()); 02070 } 02071 else 02072 d->visibleWidgets.remove(w); 02073 } 02074 02075 void KHTMLView::print() 02076 { 02077 print( false ); 02078 } 02079 02080 void KHTMLView::print(bool quick) 02081 { 02082 if(!m_part->xmlDocImpl()) return; 02083 khtml::RenderCanvas *root = static_cast<khtml::RenderCanvas *>(m_part->xmlDocImpl()->renderer()); 02084 if(!root) return; 02085 02086 // this only works on Unix - we assume 72dpi 02087 KPrinter *printer = new KPrinter(true, QPrinter::PrinterResolution); 02088 printer->addDialogPage(new KHTMLPrintSettings()); 02089 QString docname = m_part->xmlDocImpl()->URL().prettyURL(); 02090 if ( !docname.isEmpty() ) 02091 docname = KStringHandler::csqueeze(docname, 80); 02092 if(quick || printer->setup(this, i18n("Print %1").arg(docname))) { 02093 viewport()->setCursor( waitCursor ); // only viewport(), no QApplication::, otherwise we get the busy cursor in kdeprint's dialogs 02094 // set up KPrinter 02095 printer->setFullPage(false); 02096 printer->setCreator(QString("KDE %1.%2.%3 HTML Library").arg(KDE_VERSION_MAJOR).arg(KDE_VERSION_MINOR).arg(KDE_VERSION_RELEASE)); 02097 printer->setDocName(docname); 02098 02099 QPainter *p = new QPainter; 02100 p->begin( printer ); 02101 khtml::setPrintPainter( p ); 02102 02103 m_part->xmlDocImpl()->setPaintDevice( printer ); 02104 QString oldMediaType = mediaType(); 02105 setMediaType( "print" ); 02106 // We ignore margin settings for html and body when printing 02107 // and use the default margins from the print-system 02108 // (In Qt 3.0.x the default margins are hardcoded in Qt) 02109 m_part->xmlDocImpl()->setPrintStyleSheet( printer->option("app-khtml-printfriendly") == "true" ? 02110 "* { background-image: none !important;" 02111 " background-color: white !important;" 02112 " color: black !important; }" 02113 "body { margin: 0px !important; }" 02114 "html { margin: 0px !important; }" : 02115 "body { margin: 0px !important; }" 02116 "html { margin: 0px !important; }" 02117 ); 02118 02119 QPaintDeviceMetrics metrics( printer ); 02120 02121 // this is a simple approximation... we layout the document 02122 // according to the width of the page, then just cut 02123 // pages without caring about the content. We should do better 02124 // in the future, but for the moment this is better than no 02125 // printing support 02126 kdDebug(6000) << "printing: physical page width = " << metrics.width() 02127 << " height = " << metrics.height() << endl; 02128 root->setPrintingMode(true); 02129 root->setWidth(metrics.width()); 02130 02131 m_part->xmlDocImpl()->styleSelector()->computeFontSizes(&metrics, 100); 02132 m_part->xmlDocImpl()->updateStyleSelector(); 02133 root->setPrintImages( printer->option("app-khtml-printimages") == "true"); 02134 root->setMinMaxKnown( false ); 02135 root->setLayouted( false ); 02136 root->layout(); 02137 khtml::RenderWidget::flushWidgetResizes(); // make sure widgets have their final size 02138 02139 bool printHeader = (printer->option("app-khtml-printheader") == "true"); 02140 02141 int headerHeight = 0; 02142 QFont headerFont("Sans", 8); 02143 02144 QString headerLeft = KGlobal::locale()->formatDate(QDate::currentDate(),true); 02145 QString headerMid = docname; 02146 QString headerRight; 02147 02148 if (printHeader) 02149 { 02150 p->setFont(headerFont); 02151 headerHeight = (p->fontMetrics().lineSpacing() * 3) / 2; 02152 } 02153 02154 // ok. now print the pages. 02155 kdDebug(6000) << "printing: html page width = " << root->docWidth() 02156 << " height = " << root->docHeight() << endl; 02157 kdDebug(6000) << "printing: margins left = " << printer->margins().width() 02158 << " top = " << printer->margins().height() << endl; 02159 kdDebug(6000) << "printing: paper width = " << metrics.width() 02160 << " height = " << metrics.height() << endl; 02161 // if the width is too large to fit on the paper we just scale 02162 // the whole thing. 02163 int pageHeight = metrics.height(); 02164 int pageWidth = metrics.width(); 02165 p->setClipRect(0,0, pageWidth, pageHeight); 02166 02167 pageHeight -= headerHeight; 02168 02169 bool scalePage = false; 02170 double scale = 0.0; 02171 #ifndef QT_NO_TRANSFORMATIONS 02172 if(root->docWidth() > metrics.width()) { 02173 scalePage = true; 02174 scale = ((double) metrics.width())/((double) root->docWidth()); 02175 pageHeight = (int) (pageHeight/scale); 02176 pageWidth = (int) (pageWidth/scale); 02177 headerHeight = (int) (headerHeight/scale); 02178 } 02179 #endif 02180 kdDebug(6000) << "printing: scaled html width = " << pageWidth 02181 << " height = " << pageHeight << endl; 02182 02183 // Squeeze header to make it it on the page. 02184 if (printHeader) 02185 { 02186 int available_width = metrics.width() - 10 - 02187 2 * kMax(p->boundingRect(0, 0, metrics.width(), p->fontMetrics().lineSpacing(), Qt::AlignLeft, headerLeft).width(), 02188 p->boundingRect(0, 0, metrics.width(), p->fontMetrics().lineSpacing(), Qt::AlignLeft, headerRight).width()); 02189 if (available_width < 150) 02190 available_width = 150; 02191 int mid_width; 02192 int squeeze = 120; 02193 do { 02194 headerMid = KStringHandler::csqueeze(docname, squeeze); 02195 mid_width = p->boundingRect(0, 0, metrics.width(), p->fontMetrics().lineSpacing(), Qt::AlignLeft, headerMid).width(); 02196 squeeze -= 10; 02197 } while (mid_width > available_width); 02198 } 02199 02200 int top = 0; 02201 int page = 1; 02202 while(top < root->docHeight()) { 02203 if(top > 0) printer->newPage(); 02204 if (printHeader) 02205 { 02206 int dy = p->fontMetrics().lineSpacing(); 02207 p->setPen(Qt::black); 02208 p->setFont(headerFont); 02209 02210 headerRight = QString("#%1").arg(page); 02211 02212 p->drawText(0, 0, metrics.width(), dy, Qt::AlignLeft, headerLeft); 02213 p->drawText(0, 0, metrics.width(), dy, Qt::AlignHCenter, headerMid); 02214 p->drawText(0, 0, metrics.width(), dy, Qt::AlignRight, headerRight); 02215 } 02216 02217 #ifndef QT_NO_TRANSFORMATIONS 02218 if (scalePage) 02219 p->scale(scale, scale); 02220 #endif 02221 p->translate(0, headerHeight-top); 02222 02223 root->setTruncatedAt(top+pageHeight); 02224 02225 root->layer()->paint(p, QRect(0, top, pageWidth, pageHeight)); 02226 if (top + pageHeight >= root->docHeight()) 02227 break; // Stop if we have printed everything 02228 02229 top = root->truncatedAt(); 02230 p->resetXForm(); 02231 page++; 02232 } 02233 02234 p->end(); 02235 delete p; 02236 02237 // and now reset the layout to the usual one... 02238 root->setPrintingMode(false); 02239 khtml::setPrintPainter( 0 ); 02240 setMediaType( oldMediaType ); 02241 m_part->xmlDocImpl()->setPaintDevice( this ); 02242 m_part->xmlDocImpl()->styleSelector()->computeFontSizes(m_part->xmlDocImpl()->paintDeviceMetrics(), m_part->zoomFactor()); 02243 m_part->xmlDocImpl()->updateStyleSelector(); 02244 viewport()->unsetCursor(); 02245 } 02246 delete printer; 02247 } 02248 02249 void KHTMLView::slotPaletteChanged() 02250 { 02251 if(!m_part->xmlDocImpl()) return; 02252 DOM::DocumentImpl *document = m_part->xmlDocImpl(); 02253 if (!document->isHTMLDocument()) return; 02254 khtml::RenderCanvas *root = static_cast<khtml::RenderCanvas *>(document->renderer()); 02255 if(!root) return; 02256 root->style()->resetPalette(); 02257 NodeImpl *body = static_cast<HTMLDocumentImpl*>(document)->body(); 02258 if(!body) return; 02259 body->setChanged(true); 02260 body->recalcStyle( NodeImpl::Force ); 02261 } 02262 02263 void KHTMLView::paint(QPainter *p, const QRect &rc, int yOff, bool *more) 02264 { 02265 if(!m_part->xmlDocImpl()) return; 02266 khtml::RenderCanvas *root = static_cast<khtml::RenderCanvas *>(m_part->xmlDocImpl()->renderer()); 02267 if(!root) return; 02268 02269 m_part->xmlDocImpl()->setPaintDevice(p->device()); 02270 root->setPrintingMode(true); 02271 root->setWidth(rc.width()); 02272 02273 p->save(); 02274 p->setClipRect(rc); 02275 p->translate(rc.left(), rc.top()); 02276 double scale = ((double) rc.width()/(double) root->docWidth()); 02277 int height = (int) ((double) rc.height() / scale); 02278 #ifndef QT_NO_TRANSFORMATIONS 02279 p->scale(scale, scale); 02280 #endif 02281 02282 root->layer()->paint(p, QRect(0, yOff, root->docWidth(), height)); 02283 if (more) 02284 *more = yOff + height < root->docHeight(); 02285 p->restore(); 02286 02287 root->setPrintingMode(false); 02288 m_part->xmlDocImpl()->setPaintDevice( this ); 02289 } 02290 02291 02292 void KHTMLView::useSlowRepaints() 02293 { 02294 d->useSlowRepaints = true; 02295 setStaticBackground(true); 02296 } 02297 02298 02299 void KHTMLView::setVScrollBarMode ( ScrollBarMode mode ) 02300 { 02301 #ifndef KHTML_NO_SCROLLBARS 02302 d->vmode = mode; 02303 QScrollView::setVScrollBarMode(mode); 02304 #else 02305 Q_UNUSED( mode ); 02306 #endif 02307 } 02308 02309 void KHTMLView::setHScrollBarMode ( ScrollBarMode mode ) 02310 { 02311 #ifndef KHTML_NO_SCROLLBARS 02312 d->hmode = mode; 02313 QScrollView::setHScrollBarMode(mode); 02314 #else 02315 Q_UNUSED( mode ); 02316 #endif 02317 } 02318 02319 void KHTMLView::restoreScrollBar() 02320 { 02321 int ow = visibleWidth(); 02322 QScrollView::setVScrollBarMode(d->vmode); 02323 if (visibleWidth() != ow) 02324 layout(); 02325 d->prevScrollbarVisible = verticalScrollBar()->isVisible(); 02326 } 02327 02328 QStringList KHTMLView::formCompletionItems(const QString &name) const 02329 { 02330 if (!m_part->settings()->isFormCompletionEnabled()) 02331 return QStringList(); 02332 if (!d->formCompletions) 02333 d->formCompletions = new KSimpleConfig(locateLocal("data", "khtml/formcompletions")); 02334 return d->formCompletions->readListEntry(name); 02335 } 02336 02337 void KHTMLView::clearCompletionHistory(const QString& name) 02338 { 02339 if (!d->formCompletions) 02340 { 02341 d->formCompletions = new KSimpleConfig(locateLocal("data", "khtml/formcompletions")); 02342 } 02343 d->formCompletions->writeEntry(name, ""); 02344 d->formCompletions->sync(); 02345 } 02346 02347 void KHTMLView::addFormCompletionItem(const QString &name, const QString &value) 02348 { 02349 if (!m_part->settings()->isFormCompletionEnabled()) 02350 return; 02351 // don't store values that are all numbers or just numbers with 02352 // dashes or spaces as those are likely credit card numbers or 02353 // something similar 02354 bool cc_number(true); 02355 for (unsigned int i = 0; i < value.length(); ++i) 02356 { 02357 QChar c(value[i]); 02358 if (!c.isNumber() && c != '-' && !c.isSpace()) 02359 { 02360 cc_number = false; 02361 break; 02362 } 02363 } 02364 if (cc_number) 02365 return; 02366 QStringList items = formCompletionItems(name); 02367 if (!items.contains(value)) 02368 items.prepend(value); 02369 while ((int)items.count() > m_part->settings()->maxFormCompletionItems()) 02370 items.remove(items.fromLast()); 02371 d->formCompletions->writeEntry(name, items); 02372 } 02373 02374 void KHTMLView::addNonPasswordStorableSite(const QString& host) 02375 { 02376 if (!d->formCompletions) { 02377 d->formCompletions = new KSimpleConfig(locateLocal("data", "khtml/formcompletions")); 02378 } 02379 02380 d->formCompletions->setGroup("NonPasswordStorableSites"); 02381 QStringList sites = d->formCompletions->readListEntry("Sites"); 02382 sites.append(host); 02383 d->formCompletions->writeEntry("Sites", sites); 02384 d->formCompletions->sync(); 02385 d->formCompletions->setGroup(QString::null);//reset 02386 } 02387 02388 bool KHTMLView::nonPasswordStorableSite(const QString& host) const 02389 { 02390 if (!d->formCompletions) { 02391 d->formCompletions = new KSimpleConfig(locateLocal("data", "khtml/formcompletions")); 02392 } 02393 d->formCompletions->setGroup("NonPasswordStorableSites"); 02394 QStringList sites = d->formCompletions->readListEntry("Sites"); 02395 d->formCompletions->setGroup(QString::null);//reset 02396 02397 return (sites.find(host) != sites.end()); 02398 } 02399 02400 // returns true if event should be swallowed 02401 bool KHTMLView::dispatchMouseEvent(int eventId, DOM::NodeImpl *targetNode, 02402 DOM::NodeImpl *targetNodeNonShared, bool cancelable, 02403 int detail,QMouseEvent *_mouse, bool setUnder, 02404 int mouseEventType) 02405 { 02406 if (d->underMouse) 02407 d->underMouse->deref(); 02408 d->underMouse = targetNode; 02409 if (d->underMouse) 02410 d->underMouse->ref(); 02411 02412 if (d->underMouseNonShared) 02413 d->underMouseNonShared->deref(); 02414 d->underMouseNonShared = targetNodeNonShared; 02415 if (d->underMouseNonShared) 02416 d->underMouseNonShared->ref(); 02417 02418 int exceptioncode = 0; 02419 int pageX = 0; 02420 int pageY = 0; 02421 viewportToContents(_mouse->x(), _mouse->y(), pageX, pageY); 02422 int clientX = pageX - contentsX(); 02423 int clientY = pageY - contentsY(); 02424 int screenX = _mouse->globalX(); 02425 int screenY = _mouse->globalY(); 02426 int button = -1; 02427 switch (_mouse->button()) { 02428 case LeftButton: 02429 button = 0; 02430 break; 02431 case MidButton: 02432 button = 1; 02433 break; 02434 case RightButton: 02435 button = 2; 02436 break; 02437 default: 02438 break; 02439 } 02440 if (d->accessKeysPreActivate && button!=-1) 02441 d->accessKeysPreActivate=false; 02442 02443 bool ctrlKey = (_mouse->state() & ControlButton); 02444 bool altKey = (_mouse->state() & AltButton); 02445 bool shiftKey = (_mouse->state() & ShiftButton); 02446 bool metaKey = (_mouse->state() & MetaButton); 02447 02448 // mouseout/mouseover 02449 if (setUnder && (d->prevMouseX != pageX || d->prevMouseY != pageY)) { 02450 02451 // ### this code sucks. we should save the oldUnder instead of calculating 02452 // it again. calculating is expensive! (Dirk) 02453 NodeImpl *oldUnder = 0; 02454 if (d->prevMouseX >= 0 && d->prevMouseY >= 0) { 02455 NodeImpl::MouseEvent mev( _mouse->stateAfter(), static_cast<NodeImpl::MouseEventType>(mouseEventType)); 02456 m_part->xmlDocImpl()->prepareMouseEvent( true, d->prevMouseX, d->prevMouseY, &mev ); 02457 oldUnder = mev.innerNode.handle(); 02458 } 02459 // 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()); 02460 if (oldUnder != targetNode) { 02461 // send mouseout event to the old node 02462 if (oldUnder){ 02463 oldUnder->ref(); 02464 MouseEventImpl *me = new MouseEventImpl(EventImpl::MOUSEOUT_EVENT, 02465 true,true,m_part->xmlDocImpl()->defaultView(), 02466 0,screenX,screenY,clientX,clientY,pageX, pageY, 02467 ctrlKey,altKey,shiftKey,metaKey, 02468 button,targetNode); 02469 me->ref(); 02470 oldUnder->dispatchEvent(me,exceptioncode,true); 02471 me->deref(); 02472 } 02473 02474 // send mouseover event to the new node 02475 if (targetNode) { 02476 MouseEventImpl *me = new MouseEventImpl(EventImpl::MOUSEOVER_EVENT, 02477 true,true,m_part->xmlDocImpl()->defaultView(), 02478 0,screenX,screenY,clientX,clientY,pageX, pageY, 02479 ctrlKey,altKey,shiftKey,metaKey, 02480 button,oldUnder); 02481 02482 me->ref(); 02483 targetNode->dispatchEvent(me,exceptioncode,true); 02484 me->deref(); 02485 } 02486 02487 if (oldUnder) 02488 oldUnder->deref(); 02489 } 02490 } 02491 02492 bool swallowEvent = false; 02493 02494 if (targetNode) { 02495 // send the actual event 02496 bool dblclick = ( eventId == EventImpl::CLICK_EVENT && 02497 _mouse->type() == QEvent::MouseButtonDblClick ); 02498 MouseEventImpl *me = new MouseEventImpl(static_cast<EventImpl::EventId>(eventId), 02499 true,cancelable,m_part->xmlDocImpl()->defaultView(), 02500 detail,screenX,screenY,clientX,clientY,pageX, pageY, 02501 ctrlKey,altKey,shiftKey,metaKey, 02502 button,0, _mouse, dblclick ); 02503 me->ref(); 02504 targetNode->dispatchEvent(me,exceptioncode,true); 02505 if (me->defaultHandled() || me->defaultPrevented()) 02506 swallowEvent = true; 02507 me->deref(); 02508 02509 if (eventId == EventImpl::MOUSEDOWN_EVENT) { 02510 if (targetNode->isSelectable()) 02511 m_part->xmlDocImpl()->setFocusNode(targetNode); 02512 else 02513 m_part->xmlDocImpl()->setFocusNode(0); 02514 } 02515 } 02516 02517 return swallowEvent; 02518 } 02519 02520 void KHTMLView::setIgnoreWheelEvents( bool e ) 02521 { 02522 d->ignoreWheelEvents = e; 02523 } 02524 02525 #ifndef QT_NO_WHEELEVENT 02526 02527 void KHTMLView::viewportWheelEvent(QWheelEvent* e) 02528 { 02529 if (d->accessKeysPreActivate) d->accessKeysPreActivate=false; 02530 02531 if ( ( e->state() & ControlButton) == ControlButton ) 02532 { 02533 emit zoomView( - e->delta() ); 02534 e->accept(); 02535 } 02536 else if ( ( (d->ignoreWheelEvents && !verticalScrollBar()->isVisible()) 02537 || e->delta() > 0 && contentsY() <= 0 02538 || e->delta() < 0 && contentsY() >= contentsHeight() - visibleHeight()) 02539 && m_part->parentPart() ) { 02540 kdDebug(6000) << this << " cz " << contentsY() << " ch " << contentsHeight() << " vh " << visibleHeight() << endl; 02541 if ( m_part->parentPart()->view() ) 02542 m_part->parentPart()->view()->wheelEvent( e ); 02543 kdDebug(6000) << "sent" << endl; 02544 e->ignore(); 02545 } 02546 else if ( d->vmode == QScrollView::AlwaysOff ) { 02547 e->accept(); 02548 } 02549 else { 02550 d->scrollBarMoved = true; 02551 QScrollView::viewportWheelEvent( e ); 02552 02553 QMouseEvent *tempEvent = new QMouseEvent( QEvent::MouseMove, QPoint(-1,-1), QPoint(-1,-1), Qt::NoButton, e->state() ); 02554 emit viewportMouseMoveEvent ( tempEvent ); 02555 delete tempEvent; 02556 } 02557 02558 } 02559 #endif 02560 02561 void KHTMLView::dragEnterEvent( QDragEnterEvent* ev ) 02562 { 02563 // Handle drops onto frames (#16820) 02564 // Drops on the main html part is handled by Konqueror (and shouldn't do anything 02565 // in e.g. kmail, so not handled here). 02566 if ( m_part->parentPart() ) 02567 { 02568 QApplication::sendEvent(m_part->parentPart()->widget(), ev); 02569 return; 02570 } 02571 QScrollView::dragEnterEvent( ev ); 02572 } 02573 02574 void KHTMLView::dropEvent( QDropEvent *ev ) 02575 { 02576 // Handle drops onto frames (#16820) 02577 // Drops on the main html part is handled by Konqueror (and shouldn't do anything 02578 // in e.g. kmail, so not handled here). 02579 if ( m_part->parentPart() ) 02580 { 02581 QApplication::sendEvent(m_part->parentPart()->widget(), ev); 02582 return; 02583 } 02584 QScrollView::dropEvent( ev ); 02585 } 02586 02587 void KHTMLView::focusInEvent( QFocusEvent *e ) 02588 { 02589 #ifndef KHTML_NO_CARET 02590 // Restart blink frequency timer if it has been killed, but only on 02591 // editable nodes 02592 if (d->m_caretViewContext && 02593 d->m_caretViewContext->freqTimerId == -1 && 02594 m_part->xmlDocImpl()) { 02595 NodeImpl *caretNode = m_part->xmlDocImpl()->focusNode(); 02596 if (m_part->isCaretMode() 02597 || m_part->isEditable() 02598 || (caretNode && caretNode->renderer() 02599 && caretNode->renderer()->style()->userInput() 02600 == UI_ENABLED)) { 02601 d->m_caretViewContext->freqTimerId = startTimer(500); 02602 d->m_caretViewContext->visible = true; 02603 }/*end if*/ 02604 }/*end if*/ 02605 showCaret(); 02606 #endif // KHTML_NO_CARET 02607 QScrollView::focusInEvent( e ); 02608 } 02609 02610 void KHTMLView::focusOutEvent( QFocusEvent *e ) 02611 { 02612 if(m_part) m_part->stopAutoScroll(); 02613 02614 #ifndef KHTML_NO_TYPE_AHEAD_FIND 02615 if(d->typeAheadActivated) 02616 { 02617 findTimeout(); 02618 } 02619 #endif // KHTML_NO_TYPE_AHEAD_FIND 02620 02621 #ifndef KHTML_NO_CARET 02622 if (d->m_caretViewContext) { 02623 switch (d->m_caretViewContext->displayNonFocused) { 02624 case KHTMLPart::CaretInvisible: 02625 hideCaret(); 02626 break; 02627 case KHTMLPart::CaretVisible: { 02628 killTimer(d->m_caretViewContext->freqTimerId); 02629 d->m_caretViewContext->freqTimerId = -1; 02630 NodeImpl *caretNode = m_part->xmlDocImpl()->focusNode(); 02631 if (!d->m_caretViewContext->visible && (m_part->isCaretMode() 02632 || m_part->isEditable() 02633 || (caretNode && caretNode->renderer() 02634 && caretNode->renderer()->style()->userInput() 02635 == UI_ENABLED))) { 02636 d->m_caretViewContext->visible = true; 02637 showCaret(true); 02638 }/*end if*/ 02639 break; 02640 } 02641 case KHTMLPart::CaretBlink: 02642 // simply leave as is 02643 break; 02644 }/*end switch*/ 02645 }/*end if*/ 02646 #endif // KHTML_NO_CARET 02647 QScrollView::focusOutEvent( e ); 02648 } 02649 02650 void KHTMLView::slotScrollBarMoved() 02651 { 02652 if (!d->scrollingSelf) 02653 d->scrollBarMoved = true; 02654 } 02655 02656 void KHTMLView::timerEvent ( QTimerEvent *e ) 02657 { 02658 // kdDebug() << "timer event " << e->timerId() << endl; 02659 if ( e->timerId() == d->scrollTimerId ) { 02660 if( d->scrollSuspended ) 02661 return; 02662 switch (d->scrollDirection) { 02663 case KHTMLViewPrivate::ScrollDown: 02664 if (contentsY() + visibleHeight () >= contentsHeight()) 02665 d->newScrollTimer(this, 0); 02666 else 02667 scrollBy( 0, d->scrollBy ); 02668 break; 02669 case KHTMLViewPrivate::ScrollUp: 02670 if (contentsY() <= 0) 02671 d->newScrollTimer(this, 0); 02672 else 02673 scrollBy( 0, -d->scrollBy ); 02674 break; 02675 case KHTMLViewPrivate::ScrollRight: 02676 if (contentsX() + visibleWidth () >= contentsWidth()) 02677 d->newScrollTimer(this, 0); 02678 else 02679 scrollBy( d->scrollBy, 0 ); 02680 break; 02681 case KHTMLViewPrivate::ScrollLeft: 02682 if (contentsX() <= 0) 02683 d->newScrollTimer(this, 0); 02684 else 02685 scrollBy( -d->scrollBy, 0 ); 02686 break; 02687 } 02688 return; 02689 } 02690 else if ( e->timerId() == d->layoutTimerId ) { 02691 d->firstRelayout = false; 02692 d->dirtyLayout = true; 02693 layout(); 02694 } 02695 #ifndef KHTML_NO_CARET 02696 else if (d->m_caretViewContext 02697 && e->timerId() == d->m_caretViewContext->freqTimerId) { 02698 d->m_caretViewContext->visible = !d->m_caretViewContext->visible; 02699 if (d->m_caretViewContext->displayed) { 02700 updateContents(d->m_caretViewContext->x, d->m_caretViewContext->y, 02701 d->m_caretViewContext->width, 02702 d->m_caretViewContext->height); 02703 }/*end if*/ 02704 // if (d->m_caretViewContext->visible) cout << "|" << flush; 02705 // else cout << "" << flush; 02706 return; 02707 } 02708 #endif 02709 02710 if( m_part->xmlDocImpl() ) { 02711 DOM::DocumentImpl *document = m_part->xmlDocImpl(); 02712 khtml::RenderCanvas* root = static_cast<khtml::RenderCanvas *>(document->renderer()); 02713 02714 if ( root && !root->layouted() ) { 02715 killTimer(d->repaintTimerId); 02716 d->repaintTimerId = 0; 02717 scheduleRelayout(); 02718 return; 02719 } 02720 } 02721 02722 setStaticBackground(d->useSlowRepaints); 02723 02724 // kdDebug() << "scheduled repaint "<< d->repaintTimerId << endl; 02725 killTimer(d->repaintTimerId); 02726 d->repaintTimerId = 0; 02727 02728 QRegion updateRegion; 02729 QMemArray<QRect> rects = d->updateRegion.rects(); 02730 02731 d->updateRegion = QRegion(); 02732 02733 if ( rects.size() ) 02734 updateRegion = rects[0]; 02735 02736 for ( unsigned i = 1; i < rects.size(); ++i ) { 02737 QRect obR = updateRegion.boundingRect(); 02738 QRegion newRegion = updateRegion.unite(rects[i]); 02739 if (2*newRegion.boundingRect().height() > 3*obR.height() ) 02740 { 02741 repaintContents( obR ); 02742 updateRegion = rects[i]; 02743 } 02744 else 02745 updateRegion = newRegion; 02746 } 02747 02748 if ( !updateRegion.isNull() ) 02749 repaintContents( updateRegion.boundingRect() ); 02750 02751 if (d->dirtyLayout && !d->visibleWidgets.isEmpty()) { 02752 QWidget* w; 02753 d->dirtyLayout = false; 02754 02755 QRect visibleRect(contentsX(), contentsY(), visibleWidth(), visibleHeight()); 02756 QPtrList<RenderWidget> toRemove; 02757 for (QPtrDictIterator<QWidget> it(d->visibleWidgets); it.current(); ++it) { 02758 int xp = 0, yp = 0; 02759 w = it.current(); 02760 RenderWidget* rw = static_cast<RenderWidget*>( it.currentKey() ); 02761 if (!rw->absolutePosition(xp, yp) || 02762 !visibleRect.intersects(QRect(xp, yp, w->width(), w->height()))) 02763 toRemove.append(rw); 02764 } 02765 for (RenderWidget* r = toRemove.first(); r; r = toRemove.next()) 02766 if ( (w = d->visibleWidgets.take(r) ) ) 02767 addChild(w, 0, -500000); 02768 } 02769 if (d->accessKeysActivated) emit repaintAccessKeys(); 02770 } 02771 02772 void KHTMLView::scheduleRelayout(khtml::RenderObject * /*clippedObj*/) 02773 { 02774 if (!d->layoutSchedulingEnabled || d->layoutTimerId) 02775 return; 02776 02777 d->layoutTimerId = startTimer( m_part->xmlDocImpl() && m_part->xmlDocImpl()->parsing() 02778 ? 1000 : 0 ); 02779 } 02780 02781 void KHTMLView::unscheduleRelayout() 02782 { 02783 if (!d->layoutTimerId) 02784 return; 02785 02786 killTimer(d->layoutTimerId); 02787 d->layoutTimerId = 0; 02788 } 02789 02790 void KHTMLView::unscheduleRepaint() 02791 { 02792 if (!d->repaintTimerId) 02793 return; 02794 02795 killTimer(d->repaintTimerId); 02796 d->repaintTimerId = 0; 02797 } 02798 02799 void KHTMLView::scheduleRepaint(int x, int y, int w, int h) 02800 { 02801 bool parsing = !m_part->xmlDocImpl() || m_part->xmlDocImpl()->parsing(); 02802 02803 // kdDebug() << "parsing " << parsing << endl; 02804 // kdDebug() << "complete " << d->complete << endl; 02805 02806 int time = parsing ? 300 : ( !d->complete ? 100 : 20 ); 02807 02808 #ifdef DEBUG_FLICKER 02809 QPainter p; 02810 p.begin( viewport() ); 02811 02812 int vx, vy; 02813 contentsToViewport( x, y, vx, vy ); 02814 p.fillRect( vx, vy, w, h, Qt::red ); 02815 p.end(); 02816 #endif 02817 02818 d->updateRegion = d->updateRegion.unite(QRect(x,y,w,h)); 02819 02820 if ( !d->repaintTimerId ) 02821 d->repaintTimerId = startTimer( time ); 02822 02823 // kdDebug() << "starting timer " << time << endl; 02824 } 02825 02826 void KHTMLView::complete() 02827 { 02828 // kdDebug() << "KHTMLView::complete()" << endl; 02829 02830 d->complete = true; 02831 02832 // is there a relayout pending? 02833 if (d->layoutTimerId) 02834 { 02835 // kdDebug() << "requesting relayout now" << endl; 02836 // do it now 02837 killTimer(d->layoutTimerId); 02838 d->layoutTimerId = startTimer( 0 ); 02839 } 02840 02841 // is there a repaint pending? 02842 if (d->repaintTimerId) 02843 { 02844 // kdDebug() << "requesting repaint now" << endl; 02845 // do it now 02846 killTimer(d->repaintTimerId); 02847 d->repaintTimerId = startTimer( 20 ); 02848 } 02849 } 02850 02851 #ifndef KHTML_NO_CARET 02852 02853 // ### the dependencies on static functions are a nightmare. just be 02854 // hacky and include the implementation here. Clean me up, please. 02855 02856 #include "khtml_caret.cpp" 02857 02858 void KHTMLView::initCaret(bool keepSelection) 02859 { 02860 #if DEBUG_CARETMODE > 0 02861 kdDebug(6200) << "begin initCaret" << endl; 02862 #endif 02863 // save caretMoved state as moveCaretTo changes it 02864 if (m_part->xmlDocImpl()) { 02865 #if 0 02866 ElementImpl *listitem = m_part->xmlDocImpl()->getElementById("__test_element__"); 02867 if (listitem) dumpLineBoxes(static_cast<RenderFlow *>(listitem->renderer())); 02868 #endif 02869 d->caretViewContext(); 02870 bool cmoved = d->m_caretViewContext->caretMoved; 02871 if (m_part->d->caretNode().isNull()) { 02872 // set to document, position will be sanitized anyway 02873 m_part->d->caretNode() = m_part->document(); 02874 m_part->d->caretOffset() = 0L; 02875 // This sanity check is necessary for the not so unlikely case that 02876 // setEditable or setCaretMode is called before any render objects have 02877 // been created. 02878 if (!m_part->d->caretNode().handle()->renderer()) return; 02879 }/*end if*/ 02880 // kdDebug(6200) << "d->m_selectionStart " << m_part->d->m_selectionStart.handle() 02881 // << " d->m_selectionEnd " << m_part->d->m_selectionEnd.handle() << endl; 02882 // ### does not repaint the selection on keepSelection!=false 02883 moveCaretTo(m_part->d->caretNode().handle(), m_part->d->caretOffset(), !keepSelection); 02884 // kdDebug(6200) << "d->m_selectionStart " << m_part->d->m_selectionStart.handle() 02885 // << " d->m_selectionEnd " << m_part->d->m_selectionEnd.handle() << endl; 02886 d->m_caretViewContext->caretMoved = cmoved; 02887 }/*end if*/ 02888 #if DEBUG_CARETMODE > 0 02889 kdDebug(6200) << "end initCaret" << endl; 02890 #endif 02891 } 02892 02893 bool KHTMLView::caretOverrides() const 02894 { 02895 bool cm = m_part->isCaretMode(); 02896 bool dm = m_part->isEditable(); 02897 return cm && !dm ? false 02898 : (dm || m_part->d->caretNode().handle()->contentEditable()) 02899 && d->editorContext()->override; 02900 } 02901 02902 void KHTMLView::ensureNodeHasFocus(NodeImpl *node) 02903 { 02904 if (m_part->isCaretMode() || m_part->isEditable()) return; 02905 if (node->focused()) return; 02906 02907 // Find first ancestor whose "user-input" is "enabled" 02908 NodeImpl *firstAncestor = 0; 02909 while (node) { 02910 if (node->renderer() 02911 && node->renderer()->style()->userInput() != UI_ENABLED) 02912 break; 02913 firstAncestor = node; 02914 node = node->parentNode(); 02915 }/*wend*/ 02916 02917 if (!node) firstAncestor = 0; 02918 02919 DocumentImpl *doc = m_part->xmlDocImpl(); 02920 // ensure that embedded widgets don't lose their focus 02921 if (!firstAncestor && doc->focusNode() && doc->focusNode()->renderer() 02922 && doc->focusNode()->renderer()->isWidget()) 02923 return; 02924 02925 // Set focus node on the document 02926 #if DEBUG_CARETMODE > 1 02927 kdDebug(6200) << k_funcinfo << "firstAncestor " << firstAncestor << ": " 02928 << (firstAncestor ? firstAncestor->nodeName().string() : QString::null) << endl; 02929 #endif 02930 doc->setFocusNode(firstAncestor); 02931 emit m_part->nodeActivated(Node(firstAncestor)); 02932 } 02933 02934 void KHTMLView::recalcAndStoreCaretPos(CaretBox *hintBox) 02935 { 02936 if (!m_part || m_part->d->caretNode().isNull()) return; 02937 d->caretViewContext(); 02938 NodeImpl *caretNode = m_part->d->caretNode().handle(); 02939 #if DEBUG_CARETMODE > 0 02940 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; 02941 #endif 02942 caretNode->getCaret(m_part->d->caretOffset(), caretOverrides(), 02943 d->m_caretViewContext->x, d->m_caretViewContext->y, 02944 d->m_caretViewContext->width, 02945 d->m_caretViewContext->height); 02946 02947 if (hintBox && d->m_caretViewContext->x == -1) { 02948 #if DEBUG_CARETMODE > 1 02949 kdDebug(6200) << "using hint inline box coordinates" << endl; 02950 #endif 02951 RenderObject *r = caretNode->renderer(); 02952 const QFontMetrics &fm = r->style()->fontMetrics(); 02953 int absx, absy; 02954 r->containingBlock()->absolutePosition(absx, absy, 02955 false); // ### what about fixed? 02956 d->m_caretViewContext->x = absx + hintBox->xPos(); 02957 d->m_caretViewContext->y = absy + hintBox->yPos(); 02958 // + hintBox->baseline() - fm.ascent(); 02959 d->m_caretViewContext->width = 1; 02960 // ### firstline not regarded. But I think it can be safely neglected 02961 // as hint boxes are only used for empty lines. 02962 d->m_caretViewContext->height = fm.height(); 02963 }/*end if*/ 02964 02965 #if DEBUG_CARETMODE > 4 02966 // kdDebug(6200) << "freqTimerId: "<<d->m_caretViewContext->freqTimerId<<endl; 02967 #endif 02968 #if DEBUG_CARETMODE > 0 02969 kdDebug(6200) << "caret: ofs="<<m_part->d->caretOffset()<<" " 02970 <<" x="<<d->m_caretViewContext->x<<" y="<<d->m_caretViewContext->y 02971 <<" h="<<d->m_caretViewContext->height<<endl; 02972 #endif 02973 } 02974 02975 void KHTMLView::caretOn() 02976 { 02977 if (d->m_caretViewContext) { 02978 killTimer(d->m_caretViewContext->freqTimerId); 02979 02980 if (hasFocus() || d->m_caretViewContext->displayNonFocused 02981 == KHTMLPart::CaretBlink) { 02982 d->m_caretViewContext->freqTimerId = startTimer(500); 02983 } else { 02984 d->m_caretViewContext->freqTimerId = -1; 02985 }/*end if*/ 02986 02987 d->m_caretViewContext->visible = true; 02988 if ((d->m_caretViewContext->displayed = (hasFocus() 02989 || d->m_caretViewContext->displayNonFocused 02990 != KHTMLPart::CaretInvisible))) { 02991 updateContents(d->m_caretViewContext->x, d->m_caretViewContext->y, 02992 d->m_caretViewContext->width, 02993 d->m_caretViewContext->height); 02994 }/*end if*/ 02995 // kdDebug(6200) << "caret on" << endl; 02996 }/*end if*/ 02997 } 02998 02999 void KHTMLView::caretOff() 03000 { 03001 if (d->m_caretViewContext) { 03002 killTimer(d->m_caretViewContext->freqTimerId); 03003 d->m_caretViewContext->freqTimerId = -1; 03004 d->m_caretViewContext->displayed = false; 03005 if (d->m_caretViewContext->visible) { 03006 d->m_caretViewContext->visible = false; 03007 updateContents(d->m_caretViewContext->x, d->m_caretViewContext->y, 03008 d->m_caretViewContext->width, 03009 d->m_caretViewContext->height); 03010 }/*end if*/ 03011 // kdDebug(6200) << "caret off" << endl; 03012 }/*end if*/ 03013 } 03014 03015 void KHTMLView::showCaret(bool forceRepaint) 03016 { 03017 if (d->m_caretViewContext) { 03018 d->m_caretViewContext->displayed = true; 03019 if (d->m_caretViewContext->visible) { 03020 if (!forceRepaint) { 03021 updateContents(d->m_caretViewContext->x, d->m_caretViewContext->y, 03022 d->m_caretViewContext->width, 03023 d->m_caretViewContext->height); 03024 } else { 03025 repaintContents(d->m_caretViewContext->x, d->m_caretViewContext->y, 03026 d->m_caretViewContext->width, 03027 d->m_caretViewContext->height); 03028 }/*end if*/ 03029 }/*end if*/ 03030 // kdDebug(6200) << "caret shown" << endl; 03031 }/*end if*/ 03032 } 03033 03034 bool KHTMLView::foldSelectionToCaret(NodeImpl *startNode, long startOffset, 03035 NodeImpl *endNode, long endOffset) 03036 { 03037 m_part->d->m_selectionStart = m_part->d->m_selectionEnd = m_part->d->caretNode(); 03038 m_part->d->m_startOffset = m_part->d->m_endOffset = m_part->d->caretOffset(); 03039 m_part->d->m_extendAtEnd = true; 03040 03041 bool folded = startNode != endNode || startOffset != endOffset; 03042 03043 // Only clear the selection if there has been one. 03044 if (folded) { 03045 m_part->xmlDocImpl()->clearSelection(); 03046 }/*end if*/ 03047 03048 return folded; 03049 } 03050 03051 void KHTMLView::hideCaret() 03052 { 03053 if (d->m_caretViewContext) { 03054 if (d->m_caretViewContext->visible) { 03055 // kdDebug(6200) << "redraw caret hidden" << endl; 03056 d->m_caretViewContext->visible = false; 03057 // force repaint, otherwise the event won't be handled 03058 // before the focus leaves the window 03059 repaintContents(d->m_caretViewContext->x, d->m_caretViewContext->y, 03060 d->m_caretViewContext->width, 03061 d->m_caretViewContext->height); 03062 d->m_caretViewContext->visible = true; 03063 }/*end if*/ 03064 d->m_caretViewContext->displayed = false; 03065 // kdDebug(6200) << "caret hidden" << endl; 03066 }/*end if*/ 03067 } 03068 03069 int KHTMLView::caretDisplayPolicyNonFocused() const 03070 { 03071 if (d->m_caretViewContext) 03072 return d->m_caretViewContext->displayNonFocused; 03073 else 03074 return KHTMLPart::CaretInvisible; 03075 } 03076 03077 void KHTMLView::setCaretDisplayPolicyNonFocused(int policy) 03078 { 03079 d->caretViewContext(); 03080 // int old = d->m_caretViewContext->displayNonFocused; 03081 d->m_caretViewContext->displayNonFocused = (KHTMLPart::CaretDisplayPolicy)policy; 03082 03083 // make change immediately take effect if not focused 03084 if (!hasFocus()) { 03085 switch (d->m_caretViewContext->displayNonFocused) { 03086 case KHTMLPart::CaretInvisible: 03087 hideCaret(); 03088 break; 03089 case KHTMLPart::CaretBlink: 03090 if (d->m_caretViewContext->freqTimerId != -1) break; 03091 d->m_caretViewContext->freqTimerId = startTimer(500); 03092 // fall through 03093 case KHTMLPart::CaretVisible: 03094 d->m_caretViewContext->displayed = true; 03095 showCaret(); 03096 break; 03097 }/*end switch*/ 03098 }/*end if*/ 03099 } 03100 03101 bool KHTMLView::placeCaret(CaretBox *hintBox) 03102 { 03103 CaretViewContext *cv = d->caretViewContext(); 03104 caretOff(); 03105 NodeImpl *caretNode = m_part->d->caretNode().handle(); 03106 // ### why is it sometimes null? 03107 if (!caretNode || !caretNode->renderer()) return false; 03108 ensureNodeHasFocus(caretNode); 03109 if (m_part->isCaretMode() || m_part->isEditable() 03110 || caretNode->renderer()->style()->userInput() == UI_ENABLED) { 03111 recalcAndStoreCaretPos(hintBox); 03112 03113 cv->origX = cv->x; 03114 03115 caretOn(); 03116 return true; 03117 }/*end if*/ 03118 return false; 03119 } 03120 03121 void KHTMLView::ensureCaretVisible() 03122 { 03123 CaretViewContext *cv = d->m_caretViewContext; 03124 if (!cv) return; 03125 ensureVisible(cv->x, cv->y, cv->width, cv->height); 03126 d->scrollBarMoved = false; 03127 } 03128 03129 bool KHTMLView::extendSelection(NodeImpl *oldStartSel, long oldStartOfs, 03130 NodeImpl *oldEndSel, long oldEndOfs) 03131 { 03132 bool changed = false; 03133 if (m_part->d->m_selectionStart == m_part->d->m_selectionEnd 03134 && m_part->d->m_startOffset == m_part->d->m_endOffset) { 03135 changed = foldSelectionToCaret(oldStartSel, oldStartOfs, oldEndSel, oldEndOfs); 03136 m_part->d->m_extendAtEnd = true; 03137 } else do { 03138 changed = m_part->d->m_selectionStart.handle() != oldStartSel 03139 || m_part->d->m_startOffset != oldStartOfs 03140 || m_part->d->m_selectionEnd.handle() != oldEndSel 03141 || m_part->d->m_endOffset != oldEndOfs; 03142 if (!changed) break; 03143 03144 // determine start position -- caret position is always at end. 03145 NodeImpl *startNode; 03146 long startOffset; 03147 if (m_part->d->m_extendAtEnd) { 03148 startNode = m_part->d->m_selectionStart.handle(); 03149 startOffset = m_part->d->m_startOffset; 03150 } else { 03151 startNode = m_part->d->m_selectionEnd.handle(); 03152 startOffset = m_part->d->m_endOffset; 03153 m_part->d->m_selectionEnd = m_part->d->m_selectionStart; 03154 m_part->d->m_endOffset = m_part->d->m_startOffset; 03155 m_part->d->m_extendAtEnd = true; 03156 }/*end if*/ 03157 03158 bool swapNeeded = false; 03159 if (!m_part->d->m_selectionEnd.isNull() && startNode) { 03160 swapNeeded = RangeImpl::compareBoundaryPoints(startNode, startOffset, 03161 m_part->d->m_selectionEnd.handle(), 03162 m_part->d->m_endOffset) >= 0; 03163 }/*end if*/ 03164 03165 m_part->d->m_selectionStart = startNode; 03166 m_part->d->m_startOffset = startOffset; 03167 03168 if (swapNeeded) { 03169 m_part->xmlDocImpl()->setSelection(m_part->d->m_selectionEnd.handle(), 03170 m_part->d->m_endOffset, m_part->d->m_selectionStart.handle(), 03171 m_part->d->m_startOffset); 03172 } else { 03173 m_part->xmlDocImpl()->setSelection(m_part->d->m_selectionStart.handle(), 03174 m_part->d->m_startOffset, m_part->d->m_selectionEnd.handle(), 03175 m_part->d->m_endOffset); 03176 }/*end if*/ 03177 } while(false);/*end if*/ 03178 return changed; 03179 } 03180 03181 void KHTMLView::updateSelection(NodeImpl *oldStartSel, long oldStartOfs, 03182 NodeImpl *oldEndSel, long oldEndOfs) 03183 { 03184 if (m_part->d->m_selectionStart == m_part->d->m_selectionEnd 03185 && m_part->d->m_startOffset == m_part->d->m_endOffset) { 03186 if (foldSelectionToCaret(oldStartSel, oldStartOfs, oldEndSel, oldEndOfs)) { 03187 m_part->emitSelectionChanged(); 03188 }/*end if*/ 03189 m_part->d->m_extendAtEnd = true; 03190 } else { 03191 // check if the extending end has passed the immobile end 03192 if (!m_part->d->m_selectionEnd.isNull() && !m_part->d->m_selectionEnd.isNull()) { 03193 bool swapNeeded = RangeImpl::compareBoundaryPoints( 03194 m_part->d->m_selectionStart.handle(), m_part->d->m_startOffset, 03195 m_part->d->m_selectionEnd.handle(), m_part->d->m_endOffset) >= 0; 03196 if (swapNeeded) { 03197 DOM::Node tmpNode = m_part->d->m_selectionStart; 03198 long tmpOffset = m_part->d->m_startOffset; 03199 m_part->d->m_selectionStart = m_part->d->m_selectionEnd; 03200 m_part->d->m_startOffset = m_part->d->m_endOffset; 03201 m_part->d->m_selectionEnd = tmpNode; 03202 m_part->d->m_endOffset = tmpOffset; 03203 m_part->d->m_startBeforeEnd = true; 03204 m_part->d->m_extendAtEnd = !m_part->d->m_extendAtEnd; 03205 }/*end if*/ 03206 }/*end if*/ 03207 03208 m_part->xmlDocImpl()->setSelection(m_part->d->m_selectionStart.handle(), 03209 m_part->d->m_startOffset, m_part->d->m_selectionEnd.handle(), 03210 m_part->d->m_endOffset); 03211 m_part->emitSelectionChanged(); 03212 }/*end if*/ 03213 } 03214 03215 void KHTMLView::caretKeyPressEvent(QKeyEvent *_ke) 03216 { 03217 NodeImpl *oldStartSel = m_part->d->m_selectionStart.handle(); 03218 long oldStartOfs = m_part->d->m_startOffset; 03219 NodeImpl *oldEndSel = m_part->d->m_selectionEnd.handle(); 03220 long oldEndOfs = m_part->d->m_endOffset; 03221 03222 NodeImpl *oldCaretNode = m_part->d->caretNode().handle(); 03223 long oldOffset = m_part->d->caretOffset(); 03224 03225 bool ctrl = _ke->state() & ControlButton; 03226 03227 // FIXME: this is that widely indented because I will write ifs around it. 03228 switch(_ke->key()) { 03229 case Key_Space: 03230 break; 03231 03232 case Key_Down: 03233 moveCaretNextLine(1); 03234 break; 03235 03236 case Key_Up: 03237 moveCaretPrevLine(1); 03238 break; 03239 03240 case Key_Left: 03241 moveCaretBy(false, ctrl ? CaretByWord : CaretByCharacter, 1); 03242 break; 03243 03244 case Key_Right: 03245 moveCaretBy(true, ctrl ? CaretByWord : CaretByCharacter, 1); 03246 break; 03247 03248 case Key_Next: 03249 moveCaretNextPage(); 03250 break; 03251 03252 case Key_Prior: 03253 moveCaretPrevPage(); 03254 break; 03255 03256 case Key_Home: 03257 if (ctrl) 03258 moveCaretToDocumentBoundary(false); 03259 else 03260 moveCaretToLineBegin(); 03261 break; 03262 03263 case Key_End: 03264 if (ctrl) 03265 moveCaretToDocumentBoundary(true); 03266 else 03267 moveCaretToLineEnd(); 03268 break; 03269 03270 }/*end switch*/ 03271 03272 if ((m_part->d->caretNode().handle() != oldCaretNode 03273 || m_part->d->caretOffset() != oldOffset) 03274 // node should never be null, but faulty conditions may cause it to be 03275 && !m_part->d->caretNode().isNull()) { 03276 03277 d->m_caretViewContext->caretMoved = true; 03278 03279 if (_ke->state() & ShiftButton) { // extend selection 03280 updateSelection(oldStartSel, oldStartOfs, oldEndSel, oldEndOfs); 03281 } else { // clear any selection 03282 if (foldSelectionToCaret(oldStartSel, oldStartOfs, oldEndSel, oldEndOfs)) 03283 m_part->emitSelectionChanged(); 03284 }/*end if*/ 03285 03286 m_part->emitCaretPositionChanged(m_part->d->caretNode(), m_part->d->caretOffset()); 03287 }/*end if*/ 03288 03289 _ke->accept(); 03290 } 03291 03292 bool KHTMLView::moveCaretTo(NodeImpl *node, long offset, bool clearSel) 03293 { 03294 if (!node) return false; 03295 ElementImpl *baseElem = determineBaseElement(node); 03296 RenderFlow *base = static_cast<RenderFlow *>(baseElem ? baseElem->renderer() : 0); 03297 if (!node) return false; 03298 03299 // need to find out the node's inline box. If there is none, this function 03300 // will snap to the next node that has one. This is necessary to make the 03301 // caret visible in any case. 03302 CaretBoxLineDeleter cblDeleter; 03303 // RenderBlock *cb; 03304 long r_ofs; 03305 CaretBoxIterator cbit; 03306 CaretBoxLine *cbl = findCaretBoxLine(node, offset, &cblDeleter, base, r_ofs, cbit); 03307 if(!cbl) { 03308 kdWarning() << "KHTMLView::moveCaretTo - findCaretBoxLine() returns NULL" << endl; 03309 return false; 03310 } 03311 03312 #if DEBUG_CARETMODE > 3 03313 if (cbl) kdDebug(6200) << cbl->information() << endl; 03314 #endif 03315 CaretBox *box = *cbit; 03316 if (cbit != cbl->end() && box->object() != node->renderer()) { 03317 if (box->object()->element()) { 03318 mapRenderPosToDOMPos(box->object(), r_ofs, box->isOutside(), 03319 box->isOutsideEnd(), node, offset); 03320 //if (!outside) offset = node->minOffset(); 03321 #if DEBUG_CARETMODE > 1 03322 kdDebug(6200) << "set new node " << node->nodeName().string() << "@" << node << endl; 03323 #endif 03324 } else { // box has no associated element -> do not use 03325 // this case should actually never happen. 03326 box = 0; 03327 kdError(6200) << "Box contains no node! Crash imminent" << endl; 03328 }/*end if*/ 03329 } 03330 03331 NodeImpl *oldStartSel = m_part->d->m_selectionStart.handle(); 03332 long oldStartOfs = m_part->d->m_startOffset; 03333 NodeImpl *oldEndSel = m_part->d->m_selectionEnd.handle(); 03334 long oldEndOfs = m_part->d->m_endOffset; 03335 03336 // test for position change 03337 bool posChanged = m_part->d->caretNode().handle() != node 03338 || m_part->d->caretOffset() != offset; 03339 bool selChanged = false; 03340 03341 m_part->d->caretNode() = node; 03342 m_part->d->caretOffset() = offset; 03343 if (clearSel || !oldStartSel || !oldEndSel) { 03344 selChanged = foldSelectionToCaret(oldStartSel, oldStartOfs, oldEndSel, oldEndOfs); 03345 } else { 03346 //kdDebug(6200) << "moveToCaret: extendSelection: m_extendAtEnd " << m_part->d->m_extendAtEnd << endl; 03347 //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; 03348 selChanged = extendSelection(oldStartSel, oldStartOfs, oldEndSel, oldEndOfs); 03349 //kdDebug(6200) << "after extendSelection: m_extendAtEnd " << m_part->d->m_extendAtEnd << endl; 03350 //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; 03351 }/*end if*/ 03352 03353 d->caretViewContext()->caretMoved = true; 03354 03355 bool visible_caret = placeCaret(box); 03356 03357 // FIXME: if the old position was !visible_caret, and the new position is 03358 // also, then two caretPositionChanged signals with a null Node are 03359 // emitted in series. 03360 if (posChanged) { 03361 m_part->emitCaretPositionChanged(visible_caret ? node : 0, offset); 03362 }/*end if*/ 03363 03364 return selChanged; 03365 } 03366 03367 void KHTMLView::moveCaretByLine(bool next, int count) 03368 { 03369 Node &caretNodeRef = m_part->d->caretNode(); 03370 if (caretNodeRef.isNull()) return; 03371 03372 NodeImpl *caretNode = caretNodeRef.handle(); 03373 // kdDebug(6200) << ": caretNode=" << caretNode << endl; 03374 long offset = m_part->d->caretOffset(); 03375 03376 CaretViewContext *cv = d->caretViewContext(); 03377 03378 ElementImpl *baseElem = determineBaseElement(caretNode); 03379 LinearDocument ld(m_part, caretNode, offset, LeafsOnly, baseElem); 03380 03381 ErgonomicEditableLineIterator it(ld.current(), cv->origX); 03382 03383 // move count lines vertically 03384 while (count > 0 && it != ld.end() && it != ld.preBegin()) { 03385 count--; 03386 if (next) ++it; else --it; 03387 }/*wend*/ 03388 03389 // Nothing? Then leave everything as is. 03390 if (it == ld.end() || it == ld.preBegin()) return; 03391 03392 int x, absx, absy; 03393 CaretBox *caretBox = nearestCaretBox(it, d->m_caretViewContext, x, absx, absy); 03394 03395 placeCaretOnLine(caretBox, x, absx, absy); 03396 } 03397 03398 void KHTMLView::placeCaretOnLine(CaretBox *caretBox, int x, int absx, int absy) 03399 { 03400 // paranoia sanity check 03401 if (!caretBox) return; 03402 03403 RenderObject *caretRender = caretBox->object(); 03404 03405 #if DEBUG_CARETMODE > 0 03406 kdDebug(6200) << "got valid caretBox " << caretBox << endl; 03407 kdDebug(6200) << "xPos: " << caretBox->xPos() << " yPos: " << caretBox->yPos() 03408 << " width: " << caretBox->width() << " height: " << caretBox->height() << endl; 03409 InlineTextBox *tb = static_cast<InlineTextBox *>(caretBox->inlineBox()); 03410 if (caretBox->isInlineTextBox()) { kdDebug(6200) << "contains \"" << QString(static_cast<RenderText *>(tb->object())->str->s + tb->m_start, tb->m_len) << "\"" << endl;} 03411 #endif 03412 // inquire height of caret 03413 int caretHeight = caretBox->height(); 03414 bool isText = caretBox->isInlineTextBox(); 03415 int yOfs = 0; // y-offset for text nodes 03416 if (isText) { 03417 // text boxes need extrawurst 03418 RenderText *t = static_cast<RenderText *>(caretRender); 03419 const QFontMetrics &fm = t->metrics(caretBox->inlineBox()->m_firstLine); 03420 caretHeight = fm.height(); 03421 yOfs = caretBox->inlineBox()->baseline() - fm.ascent(); 03422 }/*end if*/ 03423 03424 caretOff(); 03425 03426 // set new caret node 03427 NodeImpl *caretNode; 03428 long &offset = m_part->d->caretOffset(); 03429 mapRenderPosToDOMPos(caretRender, offset, caretBox->isOutside(), 03430 caretBox->isOutsideEnd(), caretNode, offset); 03431 03432 // set all variables not needing special treatment 03433 d->m_caretViewContext->y = caretBox->yPos() + yOfs; 03434 d->m_caretViewContext->height = caretHeight; 03435 d->m_caretViewContext->width = 1; // FIXME: regard override 03436 03437 int xPos = caretBox->xPos(); 03438 int caretBoxWidth = caretBox->width(); 03439 d->m_caretViewContext->x = xPos; 03440 03441 if (!caretBox->isOutside()) { 03442 // before or at beginning of inline box -> place at beginning 03443 long r_ofs = 0; 03444 if (x <= xPos) { 03445 r_ofs = caretBox->minOffset(); 03446 // somewhere within this block 03447 } else if (x > xPos && x <= xPos + caretBoxWidth) { 03448 if (isText) { // find out where exactly 03449 r_ofs = static_cast<InlineTextBox *>(caretBox->inlineBox()) 03450 ->offsetForPoint(x, d->m_caretViewContext->x); 03451 #if DEBUG_CARETMODE > 2 03452 kdDebug(6200) << "deviation from origX " << d->m_caretViewContext->x - x << endl; 03453 #endif 03454 #if 0 03455 } else { // snap to nearest end 03456 if (xPos + caretBoxWidth - x < x - xPos) { 03457 d->m_caretViewContext->x = xPos + caretBoxWidth; 03458 r_ofs = caretNode ? caretNode->maxOffset() : 1; 03459 } else { 03460 d->m_caretViewContext->x = xPos; 03461 r_ofs = caretNode ? caretNode->minOffset() : 0; 03462 }/*end if*/ 03463 #endif 03464 }/*end if*/ 03465 } else { // after the inline box -> place at end 03466 d->m_caretViewContext->x = xPos + caretBoxWidth; 03467 r_ofs = caretBox->maxOffset(); 03468 }/*end if*/ 03469 offset = r_ofs; 03470 }/*end if*/ 03471 #if DEBUG_CARETMODE > 0 03472 kdDebug(6200) << "new offset: " << offset << endl; 03473 #endif 03474 03475 m_part->d->caretNode() = caretNode; 03476 m_part->d->caretOffset() = offset; 03477 03478 d->m_caretViewContext->x += absx; 03479 d->m_caretViewContext->y += absy; 03480 03481 #if DEBUG_CARETMODE > 1 03482 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; 03483 #endif 03484 03485 ensureVisible(d->m_caretViewContext->x, d->m_caretViewContext->y, 03486 d->m_caretViewContext->width, d->m_caretViewContext->height); 03487 d->scrollBarMoved = false; 03488 03489 ensureNodeHasFocus(caretNode); 03490 caretOn(); 03491 } 03492 03493 void KHTMLView::moveCaretToLineBoundary(bool end) 03494 { 03495 Node &caretNodeRef = m_part->d->caretNode(); 03496 if (caretNodeRef.isNull()) return; 03497 03498 NodeImpl *caretNode = caretNodeRef.handle(); 03499 // kdDebug(6200) << ": caretNode=" << caretNode << endl; 03500 long offset = m_part->d->caretOffset(); 03501 03502 ElementImpl *baseElem = determineBaseElement(caretNode); 03503 LinearDocument ld(m_part, caretNode, offset, LeafsOnly, baseElem); 03504 03505 EditableLineIterator it = ld.current(); 03506 if (it == ld.end()) return; // should not happen, but who knows 03507 03508 EditableCaretBoxIterator fbit(it, end); 03509 Q_ASSERT(fbit != (*it)->end() && fbit != (*it)->preBegin()); 03510 CaretBox *b = *fbit; 03511 03512 RenderObject *cb = b->containingBlock(); 03513 int absx, absy; 03514 03515 if (cb) cb->absolutePosition(absx,absy); 03516 else absx = absy = 0; 03517 03518 int x = b->xPos() + (end && !b->isOutside() ? b->width() : 0); 03519 d->m_caretViewContext->origX = absx + x; 03520 placeCaretOnLine(b, x, absx, absy); 03521 } 03522 03523 void KHTMLView::moveCaretToDocumentBoundary(bool end) 03524 { 03525 Node &caretNodeRef = m_part->d->caretNode(); 03526 if (caretNodeRef.isNull()) return; 03527 03528 NodeImpl *caretNode = caretNodeRef.handle(); 03529 // kdDebug(6200) << ": caretNode=" << caretNode << endl; 03530 long offset = m_part->d->caretOffset(); 03531 03532 ElementImpl *baseElem = determineBaseElement(caretNode); 03533 LinearDocument ld(m_part, caretNode, offset, IndicatedFlows, baseElem); 03534 03535 EditableLineIterator it(end ? ld.preEnd() : ld.begin(), end); 03536 if (it == ld.end() || it == ld.preBegin()) return; // should not happen, but who knows 03537 03538 EditableCaretBoxIterator fbit = it; 03539 Q_ASSERT(fbit != (*it)->end() && fbit != (*it)->preBegin()); 03540 CaretBox *b = *fbit; 03541 03542 RenderObject *cb = (*it)->containingBlock(); 03543 int absx, absy; 03544 03545 if (cb) cb->absolutePosition(absx, absy); 03546 else absx = absy = 0; 03547 03548 int x = b->xPos()/* + (end ? b->width() : 0) reactivate for rtl*/; 03549 d->m_caretViewContext->origX = absx + x; 03550 placeCaretOnLine(b, x, absx, absy); 03551 } 03552 03553 void KHTMLView::moveCaretBy(bool next, CaretMovement cmv, int count) 03554 { 03555 if (!m_part) return; 03556 Node &caretNodeRef = m_part->d->caretNode(); 03557 if (caretNodeRef.isNull()) return; 03558 03559 NodeImpl *caretNode = caretNodeRef.handle(); 03560 // kdDebug(6200) << ": caretNode=" << caretNode << endl; 03561 long &offset = m_part->d->caretOffset(); 03562 03563 ElementImpl *baseElem = determineBaseElement(caretNode); 03564 CaretAdvancePolicy advpol = cmv != CaretByWord ? IndicatedFlows : LeafsOnly; 03565 LinearDocument ld(m_part, caretNode, offset, advpol, baseElem); 03566 03567 EditableCharacterIterator it(&ld); 03568 while (!it.isEnd() && count > 0) { 03569 count--; 03570 if (cmv == CaretByCharacter) { 03571 if (next) ++it; 03572 else --it; 03573 } else if (cmv == CaretByWord) { 03574 if (next) moveItToNextWord(it); 03575 else moveItToPrevWord(it); 03576 }/*end if*/ 03577 //kdDebug(6200) << "movecaret" << endl; 03578 }/*wend*/ 03579 CaretBox *hintBox = 0; // make gcc uninit warning disappear 03580 if (!it.isEnd()) { 03581 NodeImpl *node = caretNodeRef.handle(); 03582 hintBox = it.caretBox(); 03583 //kdDebug(6200) << "hintBox = " << hintBox << endl; 03584 //kdDebug(6200) << " outside " << hintBox->isOutside() << " outsideEnd " << hintBox->isOutsideEnd() << " r " << it.renderer() << " ofs " << it.offset() << " cb " << hintBox->containingBlock() << endl; 03585 mapRenderPosToDOMPos(it.renderer(), it.offset(), hintBox->isOutside(), 03586 hintBox->isOutsideEnd(), node, offset); 03587 //kdDebug(6200) << "mapRTD" << endl; 03588 caretNodeRef = node; 03589 #if DEBUG_CARETMODE > 2 03590 kdDebug(6200) << "set by valid node " << node << " " << (node?node->nodeName().string():QString::null) << " offset: " << offset << endl; 03591 #endif 03592 } else { 03593 offset = next ? caretNode->maxOffset() : caretNode->minOffset(); 03594 #if DEBUG_CARETMODE > 0 03595 kdDebug(6200) << "set by INvalid node. offset: " << offset << endl; 03596 #endif 03597 }/*end if*/ 03598 placeCaretOnChar(hintBox); 03599 } 03600 03601 void KHTMLView::placeCaretOnChar(CaretBox *hintBox) 03602 { 03603 caretOff(); 03604 recalcAndStoreCaretPos(hintBox); 03605 ensureVisible(d->m_caretViewContext->x, d->m_caretViewContext->y, 03606 d->m_caretViewContext->width, d->m_caretViewContext->height); 03607 d->m_caretViewContext->origX = d->m_caretViewContext->x; 03608 d->scrollBarMoved = false; 03609 #if DEBUG_CARETMODE > 3 03610 //if (caretNode->isTextNode()) kdDebug(6200) << "text[0] = " << (int)*((TextImpl *)caretNode)->data().unicode() << " text :\"" << ((TextImpl *)caretNode)->data().string() << "\"" << endl; 03611 #endif 03612 ensureNodeHasFocus(m_part->d->caretNode().handle()); 03613 caretOn(); 03614 } 03615 03616 void KHTMLView::moveCaretByPage(bool next) 03617 { 03618 Node &caretNodeRef = m_part->d->caretNode(); 03619 03620 NodeImpl *caretNode = caretNodeRef.handle(); 03621 // kdDebug(6200) << ": caretNode=" << caretNode << endl; 03622 long offset = m_part->d->caretOffset(); 03623 03624 int offs = (clipper()->height() < 30) ? clipper()->height() : 30; 03625 // Minimum distance the caret must be moved 03626 int mindist = clipper()->height() - offs; 03627 03628 CaretViewContext *cv = d->caretViewContext(); 03629 // int y = cv->y; // we always measure the top border 03630 03631 ElementImpl *baseElem = determineBaseElement(caretNode); 03632 LinearDocument ld(m_part, caretNode, offset, LeafsOnly, baseElem); 03633 03634 ErgonomicEditableLineIterator it(ld.current(), cv->origX); 03635 03636 moveIteratorByPage(ld, it, mindist, next); 03637 03638 int x, absx, absy; 03639 CaretBox *caretBox = nearestCaretBox(it, d->m_caretViewContext, x, absx, absy); 03640 03641 placeCaretOnLine(caretBox, x, absx, absy); 03642 } 03643 03644 void KHTMLView::moveCaretPrevWord() 03645 { 03646 moveCaretBy(false, CaretByWord, 1); 03647 } 03648 03649 void KHTMLView::moveCaretNextWord() 03650 { 03651 moveCaretBy(true, CaretByWord, 1); 03652 } 03653 03654 void KHTMLView::moveCaretPrevLine(int n) 03655 { 03656 moveCaretByLine(false, n); 03657 } 03658 03659 void KHTMLView::moveCaretNextLine(int n) 03660 { 03661 moveCaretByLine(true, n); 03662 } 03663 03664 void KHTMLView::moveCaretPrevPage() 03665 { 03666 moveCaretByPage(false); 03667 } 03668 03669 void KHTMLView::moveCaretNextPage() 03670 { 03671 moveCaretByPage(true); 03672 } 03673 03674 void KHTMLView::moveCaretToLineBegin() 03675 { 03676 moveCaretToLineBoundary(false); 03677 } 03678 03679 void KHTMLView::moveCaretToLineEnd() 03680 { 03681 moveCaretToLineBoundary(true); 03682 } 03683 03684 #endif // KHTML_NO_CARET 03685 03686 #undef DEBUG_CARETMODE
KDE Logo
This file is part of the documentation for khtml Library Version 3.3.1.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Sun Oct 17 11:33:57 2004 by doxygen 1.3.8 written by Dimitri van Heesch, © 1997-2003