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