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