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