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