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