- fixed <rdar://problem/3524359>: REGRESSSION (119-120):
[WebKit-https.git] / WebCore / khtml / khtmlview.cpp
1 /* This file is part of the KDE project
2  *
3  * Copyright (C) 1998, 1999 Torben Weis <weis@kde.org>
4  *                     1999 Lars Knoll <knoll@kde.org>
5  *                     1999 Antti Koivisto <koivisto@kde.org>
6  *                     2000 Dirk Mueller <mueller@kde.org>
7  * Copyright (C) 2003 Apple Computer, Inc.
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Library General Public
11  * License as published by the Free Software Foundation; either
12  * version 2 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Library General Public License for more details.
18  *
19  * You should have received a copy of the GNU Library General Public License
20  * along with this library; see the file COPYING.LIB.  If not, write to
21  * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
22  * Boston, MA 02111-1307, USA.
23  */
24 #include "khtmlview.moc"
25
26 #include "khtmlview.h"
27
28 #include "khtml_part.h"
29 #include "khtml_events.h"
30
31 #include "html/html_documentimpl.h"
32 #include "html/html_inlineimpl.h"
33 #include "html/html_formimpl.h"
34 #include "rendering/render_object.h"
35 #include "rendering/render_canvas.h"
36 #include "rendering/render_style.h"
37 #include "rendering/render_replaced.h"
38 #include "xml/dom2_eventsimpl.h"
39 #include "css/cssstyleselector.h"
40 #include "misc/htmlhashes.h"
41 #include "misc/helper.h"
42 #include "khtml_settings.h"
43 #include "khtml_printsettings.h"
44
45 #include <kcursor.h>
46 #include <ksimpleconfig.h>
47 #include <kstandarddirs.h>
48 #include <kprinter.h>
49
50 #include <qtooltip.h>
51 #include <qpainter.h>
52 #include <qpaintdevicemetrics.h>
53 #include <kapplication.h>
54
55 #include <kimageio.h>
56 #include <assert.h>
57 #include <kdebug.h>
58 #include <kurldrag.h>
59 #include <qobjectlist.h>
60
61 #define PAINT_BUFFER_HEIGHT 128
62
63 using namespace DOM;
64 using namespace khtml;
65 class KHTMLToolTip;
66
67 #ifndef QT_NO_TOOLTIP
68
69 class KHTMLToolTip : public QToolTip
70 {
71 public:
72     KHTMLToolTip(KHTMLView *view,  KHTMLViewPrivate* vp) : QToolTip(view->viewport())
73     {
74         m_view = view;
75         m_viewprivate = vp;
76     };
77
78 protected:
79     virtual void maybeTip(const QPoint &);
80
81 private:
82     KHTMLView *m_view;
83     KHTMLViewPrivate* m_viewprivate;
84 };
85
86 #endif
87
88 class KHTMLViewPrivate {
89     friend class KHTMLToolTip;
90 public:
91     KHTMLViewPrivate()
92     {
93         underMouse = 0;
94         reset();
95         tp=0;
96         paintBuffer=0;
97         formCompletions=0;
98         layoutTimerId = 0;
99         complete = false;
100         mousePressed = false;
101         tooltip = 0;
102 #ifdef INCREMENTAL_REPAINTING
103         doFullRepaint = true;
104 #endif
105 #if APPLE_CHANGES
106         vmode = hmode = QScrollView::Auto;
107         firstLayout = true;
108         needToInitScrollBars = true;
109 #else
110         prevScrollbarVisible = true;
111 #endif
112     }
113     ~KHTMLViewPrivate()
114     {
115         delete formCompletions;
116         delete tp; tp = 0;
117         delete paintBuffer; paintBuffer =0;
118         
119         if (underMouse)
120             underMouse->deref();
121         delete tooltip;
122     }
123     void reset()
124     {
125         if (underMouse)
126             underMouse->deref();
127         underMouse = 0;
128         linkPressed = false;
129         useSlowRepaints = false;
130         originalNode = 0;
131         borderTouched = false;
132 #if !APPLE_CHANGES
133 #ifndef KHTML_NO_SCROLLBARS
134         vmode = QScrollView::Auto;
135         hmode = QScrollView::Auto;
136 #else
137         vmode = QScrollView::AlwaysOff;
138         hmode = QScrollView::AlwaysOff;
139 #endif
140 #endif
141         scrollBarMoved = false;
142         ignoreWheelEvents = false;
143         borderX = 30;
144         borderY = 30;
145         clickX = -1;
146         clickY = -1;
147         prevMouseX = -1;
148         prevMouseY = -1;
149         clickCount = 0;
150         isDoubleClick = false;
151         scrollingSelf = false;
152         layoutTimerId = 0;
153         complete = false;
154         mousePressed = false;
155 #ifdef INCREMENTAL_REPAINTING
156         doFullRepaint = true;
157 #endif        
158         layoutSchedulingEnabled = true;
159         layoutSuppressed = false;
160 #if APPLE_CHANGES
161         firstLayout = true;
162 #endif
163     }
164
165     QPainter *tp;
166     QPixmap  *paintBuffer;
167     NodeImpl *underMouse;
168
169     // the node that was selected when enter was pressed
170     NodeImpl *originalNode;
171
172     bool borderTouched:1;
173     bool borderStart:1;
174     bool scrollBarMoved:1;
175 #ifdef INCREMENTAL_REPAINTING
176     bool doFullRepaint:1;
177 #endif
178     
179     QScrollView::ScrollBarMode vmode;
180     QScrollView::ScrollBarMode hmode;
181 #if !APPLE_CHANGES
182     bool prevScrollbarVisible;
183 #endif
184     bool linkPressed;
185     bool useSlowRepaints;
186     bool ignoreWheelEvents;
187
188     int borderX, borderY;
189     KSimpleConfig *formCompletions;
190
191     int clickX, clickY, clickCount;
192     bool isDoubleClick;
193
194     int prevMouseX, prevMouseY;
195     bool scrollingSelf;
196     int layoutTimerId;
197
198     bool complete;
199     bool layoutSchedulingEnabled;
200     bool layoutSuppressed;
201 #if APPLE_CHANGES
202     bool firstLayout;
203     bool needToInitScrollBars;
204 #endif
205     bool mousePressed;
206     KHTMLToolTip *tooltip;
207 };
208
209 #ifndef QT_NO_TOOLTIP
210
211 void KHTMLToolTip::maybeTip(const QPoint& /*p*/)
212 {
213     DOM::NodeImpl *node = m_viewprivate->underMouse;
214     while ( node ) {
215         if ( node->isElementNode() ) {
216             QString s = static_cast<DOM::ElementImpl*>( node )->getAttribute( ATTR_TITLE ).string();
217             if ( !s.isEmpty() ) {
218                 QRect r( m_view->contentsToViewport( node->getRect().topLeft() ), node->getRect().size() );
219                 tip( r,  s );
220             }
221             break;
222         }
223         node = node->parentNode();
224     }
225 }
226 #endif
227
228 KHTMLView::KHTMLView( KHTMLPart *part, QWidget *parent, const char *name)
229     : QScrollView( parent, name, WResizeNoErase | WRepaintNoErase | WPaintUnclipped ),
230       _refCount(1)
231 {
232     m_medium = "screen";
233
234 #ifndef INCREMENTAL_REPAINTING
235     m_layoutObject = 0;
236 #endif
237     
238     m_part = part;
239 #if APPLE_CHANGES
240     m_part->ref();
241 #endif
242     d = new KHTMLViewPrivate;
243
244 #if !APPLE_CHANGES
245     QScrollView::setVScrollBarMode(d->vmode);
246     QScrollView::setHScrollBarMode(d->hmode);
247 #endif
248     
249     connect(kapp, SIGNAL(kdisplayPaletteChanged()), this, SLOT(slotPaletteChanged()));
250     connect(this, SIGNAL(contentsMoving(int, int)), this, SLOT(slotScrollBarMoved()));
251
252     // initialize QScrollview
253     enableClipper(true);
254     viewport()->setMouseTracking(true);
255     viewport()->setBackgroundMode(NoBackground);
256
257     KImageIO::registerFormats();
258
259 #ifndef QT_NO_TOOLTIP
260     d->tooltip = new KHTMLToolTip( this, d );
261 #endif
262
263     init();
264
265     viewport()->show();
266 }
267
268 KHTMLView::~KHTMLView()
269 {
270 #if APPLE_CHANGES
271     resetScrollBars();
272 #endif
273
274     assert(_refCount == 0);
275
276     if (m_part)
277     {
278         //WABA: Is this Ok? Do I need to deref it as well?
279         //Does this need to be done somewhere else?
280         DOM::DocumentImpl *doc = m_part->xmlDocImpl();
281         if (doc)
282             doc->detach();
283
284 #if APPLE_CHANGES
285         m_part->deref();
286 #endif
287     }
288
289     delete d; d = 0;
290 }
291
292 void KHTMLView::clearPart()
293 {
294     if (m_part){
295         m_part->deref();
296         m_part = 0;
297     }
298 }
299
300 #if APPLE_CHANGES
301 void KHTMLView::resetScrollBars()
302 {
303     // Reset the document's scrollbars back to our defaults before we yield the floor.
304     d->firstLayout = true;
305     suppressScrollBars(true);
306     QScrollView::setVScrollBarMode(d->vmode);
307     QScrollView::setHScrollBarMode(d->hmode);
308     suppressScrollBars(false);
309 }
310 #endif
311
312 void KHTMLView::init()
313 {
314 #if !APPLE_CHANGES
315     if(!d->paintBuffer) d->paintBuffer = new QPixmap(PAINT_BUFFER_HEIGHT, PAINT_BUFFER_HEIGHT);
316     if(!d->tp) d->tp = new QPainter();
317 #endif
318
319     setFocusPolicy(QWidget::StrongFocus);
320     viewport()->setFocusPolicy( QWidget::WheelFocus );
321     viewport()->setFocusProxy(this);
322
323     _marginWidth = -1; // undefined
324     _marginHeight = -1;
325     _width = 0;
326     _height = 0;
327
328     setAcceptDrops(true);
329     
330     resizeContents(visibleWidth(), visibleHeight());
331 }
332
333 void KHTMLView::clear()
334 {
335
336
337 //    viewport()->erase();
338
339     setStaticBackground(false);
340
341     d->reset();
342     killTimers();
343     emit cleared();
344
345 #if APPLE_CHANGES
346     suppressScrollBars(true);
347 #else
348     QScrollView::setHScrollBarMode(d->hmode);
349     if (d->vmode==Auto)
350         QScrollView::setVScrollBarMode(d->prevScrollbarVisible?AlwaysOn:Auto);
351     else
352         QScrollView::setVScrollBarMode(d->vmode);
353     resizeContents(visibleWidth(), visibleHeight());
354 #endif
355 }
356
357 void KHTMLView::hideEvent(QHideEvent* e)
358 {
359 //    viewport()->setBackgroundMode(PaletteBase);
360     QScrollView::hideEvent(e);
361 }
362
363 void KHTMLView::showEvent(QShowEvent* e)
364 {
365 //    viewport()->setBackgroundMode(NoBackground);
366     QScrollView::showEvent(e);
367 }
368
369 void KHTMLView::resizeEvent (QResizeEvent* e)
370 {
371     QScrollView::resizeEvent(e);
372
373 #if !APPLE_CHANGES
374     int w = visibleWidth();
375     int h = visibleHeight();
376
377     layout();
378
379     //  this is to make sure we get the right width even if the scrolbar has dissappeared
380     // due to the size change.
381     if(visibleHeight() != h || visibleWidth() != w)
382         layout();
383 #endif
384     if ( m_part && m_part->xmlDocImpl() )
385         m_part->xmlDocImpl()->dispatchWindowEvent( EventImpl::RESIZE_EVENT, false, false );
386     KApplication::sendPostedEvents(viewport(), QEvent::Paint);
387 }
388
389 #if APPLE_CHANGES
390 void KHTMLView::initScrollBars()
391 {
392     if (!d->needToInitScrollBars)
393         return;
394     d->needToInitScrollBars = false;
395     setScrollBarsMode(hScrollBarMode());
396 }
397 #endif
398
399 #if !APPLE_CHANGES
400
401 // this is to get rid of a compiler virtual overload mismatch warning. do not remove
402 void KHTMLView::drawContents( QPainter*)
403 {
404 }
405
406 void KHTMLView::drawContents( QPainter *p, int ex, int ey, int ew, int eh )
407 {
408     //kdDebug( 6000 ) << "drawContents x=" << ex << ",y=" << ey << ",w=" << ew << ",h=" << eh << endl;
409     if(!m_part->xmlDocImpl() || !m_part->xmlDocImpl()->renderer()) {
410         p->fillRect(ex, ey, ew, eh, palette().normal().brush(QColorGroup::Base));
411         return;
412     }
413     if ( d->paintBuffer->width() < visibleWidth() )
414         d->paintBuffer->resize(visibleWidth(),PAINT_BUFFER_HEIGHT);
415
416     int py=0;
417     while (py < eh) {
418         int ph = eh-py < PAINT_BUFFER_HEIGHT ? eh-py : PAINT_BUFFER_HEIGHT;
419         d->tp->begin(d->paintBuffer);
420         d->tp->translate(-ex, -ey-py);
421         d->tp->fillRect(ex, ey+py, ew, ph, palette().normal().brush(QColorGroup::Base));
422         m_part->xmlDocImpl()->renderer()->print(d->tp, ex, ey+py, ew, ph, 0, 0);
423 #ifdef BOX_DEBUG
424         if (m_part->xmlDocImpl()->focusNode())
425         {
426             d->tp->setBrush(Qt::NoBrush);
427             d->tp->drawRect(m_part->xmlDocImpl()->focusNode()->getRect());
428         }
429 #endif
430         d->tp->end();
431
432         p->drawPixmap(ex, ey+py, *d->paintBuffer, 0, 0, ew, ph);
433         py += PAINT_BUFFER_HEIGHT;
434     }
435
436     khtml::DrawContentsEvent event( p, ex, ey, ew, eh );
437     QApplication::sendEvent( m_part, &event );
438
439 }
440
441 #endif // !APPLE_CHANGES
442
443 void KHTMLView::setMarginWidth(int w)
444 {
445     // make it update the rendering area when set
446     _marginWidth = w;
447 }
448
449 void KHTMLView::setMarginHeight(int h)
450 {
451     // make it update the rendering area when set
452     _marginHeight = h;
453 }
454
455
456 void KHTMLView::adjustViewSize()
457 {
458     if( m_part->xmlDocImpl() ) {
459         DOM::DocumentImpl *document = m_part->xmlDocImpl();
460
461         khtml::RenderCanvas* root = static_cast<khtml::RenderCanvas *>(document->renderer());
462         if ( !root )
463             return;
464         
465         int docw = root->docWidth();
466         int doch = root->docHeight();
467     
468         resizeContents(docw, doch);
469     }
470 }
471
472 void KHTMLView::applyBodyScrollQuirk(khtml::RenderObject* o, ScrollBarMode& hMode, ScrollBarMode& vMode)
473 {
474     // Handle the overflow:hidden/scroll quirk for the body elements.  WinIE treats
475     // overflow:hidden and overflow:scroll on <body> as applying to the document's
476     // scrollbars.  The CSS2.1 draft has even added a sentence, "HTML UAs may apply overflow
477     // specified on the body or HTML elements to the viewport."  Since WinIE and Mozilla both
478     // do it, we will do it too for <body> elements.
479     switch(o->style()->overflow()) {
480         case OHIDDEN:
481             hMode = vMode = AlwaysOff;
482             break;
483         case OSCROLL:
484             hMode = vMode = AlwaysOn;
485             break;
486         case OAUTO:
487             hMode = vMode = Auto;
488             break;
489         default:
490             // Don't set it at all.
491             ;
492     }
493 }
494
495 bool KHTMLView::inLayout() const
496 {
497     return d->layoutSuppressed;
498 }
499
500 #ifdef INCREMENTAL_REPAINTING
501 bool KHTMLView::needsFullRepaint() const
502 {
503     return d->doFullRepaint;
504 }
505 #endif
506
507 void KHTMLView::layout()
508 {
509     if (d->layoutSuppressed)
510         return;
511     
512     d->layoutSchedulingEnabled=false;
513     killTimer(d->layoutTimerId);
514     d->layoutTimerId = 0;
515
516     if (!m_part) {
517         // FIXME: Do we need to set _width here?
518         // FIXME: Should we set _height here too?
519         _width = visibleWidth();
520         return;
521     }
522
523     DOM::DocumentImpl* document = m_part->xmlDocImpl();
524     if (!document) {
525         // FIXME: Should we set _height here too?
526         _width = visibleWidth();
527         return;
528     }
529
530     khtml::RenderCanvas* root = static_cast<khtml::RenderCanvas*>(document->renderer());
531     if (!root) {
532         // FIXME: Do we need to set _width or _height here?
533         return;
534     }
535
536     ScrollBarMode hMode = d->hmode;
537     ScrollBarMode vMode = d->vmode;
538     
539     if (document->isHTMLDocument()) {
540         NodeImpl *body = static_cast<HTMLDocumentImpl*>(document)->body();
541         if (body && body->renderer()) {
542             if (body->id() == ID_FRAMESET) {
543                 body->renderer()->setNeedsLayout(true);
544                 vMode = AlwaysOff;
545                 hMode = AlwaysOff;
546             }
547             else if (body->id() == ID_BODY)
548                 applyBodyScrollQuirk(body->renderer(), hMode, vMode); // Only applies to HTML UAs, not to XML/XHTML UAs
549         }
550     }
551
552 #ifdef INCREMENTAL_REPAINTING
553     d->doFullRepaint = d->firstLayout || root->printingMode();
554 #endif
555
556 #if APPLE_CHANGES
557     // Now set our scrollbar state for the layout.
558     ScrollBarMode currentHMode = hScrollBarMode();
559     ScrollBarMode currentVMode = vScrollBarMode();
560
561     if (d->firstLayout || (hMode != currentHMode || vMode != currentVMode)) {
562         suppressScrollBars(true);
563         if (d->firstLayout) {
564             d->firstLayout = false;
565             
566             // Set the initial vMode to AlwaysOn if we're auto.
567             if (vMode == Auto)
568                 QScrollView::setVScrollBarMode(AlwaysOn); // This causes a vertical scrollbar to appear.
569             // Set the initial hMode to AlwaysOff if we're auto.
570             if (hMode == Auto)
571                 QScrollView::setHScrollBarMode(AlwaysOff); // This causes a horizontal scrollbar to disappear.
572         }
573         
574         if (hMode == vMode)
575             QScrollView::setScrollBarsMode(hMode);
576         else {
577             QScrollView::setHScrollBarMode(hMode);
578             QScrollView::setVScrollBarMode(vMode);
579         }
580
581         suppressScrollBars(false, true);
582     }
583 #else
584     QScrollView::setHScrollBarMode(hMode);
585     QScrollView::setVScrollBarMode(vMode);
586 #endif
587
588 #ifdef INCREMENTAL_REPAINTING
589     int oldHeight = _height;
590     int oldWidth = _width;
591 #endif
592     
593     _height = visibleHeight();
594     _width = visibleWidth();
595
596 #ifdef INCREMENTAL_REPAINTING
597     if (oldHeight != _height || oldWidth != _width)
598         d->doFullRepaint = true;
599 #endif
600     
601     RenderLayer* layer = root->layer();
602      
603 #ifdef INCREMENTAL_REPAINTING
604     if (!d->doFullRepaint) {
605         layer->computeRepaintRects();
606         root->repaintObjectsBeforeLayout();
607     }
608 #endif
609
610     root->layout();
611
612     //kdDebug( 6000 ) << "TIME: layout() dt=" << qt.elapsed() << endl;
613    
614     d->layoutSchedulingEnabled=true;
615     d->layoutSuppressed = false;
616
617     resizeContents(layer->width(), layer->height());
618
619     // Now update the positions of all layers.
620 #ifdef INCREMENTAL_REPAINTING
621     layer->updateLayerPositions(d->doFullRepaint);
622 #else
623     layer->updateLayerPositions();
624 #endif
625
626 #if APPLE_CHANGES
627     // We update our widget positions right after doing a layout.
628     root->updateWidgetPositions();
629 #endif
630     
631 #ifdef INCREMENTAL_REPAINTING
632     if (root->needsLayout())
633 #else
634     // Do not allow a full layout if we had a clip object set.
635     if ( root->needsLayout() && !m_layoutObject)
636 #endif
637     {
638         //qDebug("needs layout, delaying repaint");
639         scheduleRelayout();
640         return;
641     }
642     setStaticBackground(d->useSlowRepaints);
643
644 #ifndef INCREMENTAL_REPAINTING
645     if (m_layoutObject) {
646         m_layoutObject->repaint();
647         m_layoutObject = 0;
648     }
649     else
650         root->repaint();
651 #endif    
652 }
653
654 //
655 // Event Handling
656 //
657 /////////////////
658
659 void KHTMLView::viewportMousePressEvent( QMouseEvent *_mouse )
660 {
661     if(!m_part->xmlDocImpl()) return;
662
663     int xm, ym;
664     viewportToContents(_mouse->x(), _mouse->y(), xm, ym);
665
666     //kdDebug( 6000 ) << "\nmousePressEvent: x=" << xm << ", y=" << ym << endl;
667
668     d->isDoubleClick = false;
669     d->mousePressed = true;
670
671     DOM::NodeImpl::MouseEvent mev( _mouse->stateAfter(), DOM::NodeImpl::MousePress );
672     m_part->xmlDocImpl()->prepareMouseEvent( false, xm, ym, &mev );
673
674 #if !APPLE_CHANGES
675     if (d->clickCount > 0 &&
676         QPoint(d->clickX-xm,d->clickY-ym).manhattanLength() <= QApplication::startDragDistance())
677         d->clickCount++;
678     else {
679         d->clickCount = 1;
680         d->clickX = xm;
681         d->clickY = ym;
682     }
683 #else
684     if (KWQ(m_part)->passSubframeEventToSubframe(mev))
685         return;
686
687     d->clickX = xm;
688     d->clickY = ym;
689     d->clickCount = _mouse->clickCount();
690 #endif    
691
692     bool swallowEvent = dispatchMouseEvent(EventImpl::MOUSEDOWN_EVENT,mev.innerNode.handle(),true,
693                                            d->clickCount,_mouse,true,DOM::NodeImpl::MousePress);
694
695     if (!swallowEvent) {
696         khtml::MousePressEvent event( _mouse, xm, ym, mev.url, mev.target, mev.innerNode );
697         QApplication::sendEvent( m_part, &event );
698 #if APPLE_CHANGES
699         // Many AK widgets run their own event loops and consume events while the mouse is down.
700         // When they finish, currentEvent is the mouseUp that they exited on.  We need to update
701         // the khtml state with this mouseUp, which khtml never saw.
702         // If this event isn't a mouseUp, we assume that the mouseUp will be coming later.  There
703         // is a hole here if the widget consumes the mouseUp and subsequent events.
704         if (KWQ(m_part)->lastEventIsMouseUp()) {
705             d->mousePressed = false;
706         }
707 #endif        
708         emit m_part->nodeActivated(mev.innerNode);
709     }
710 }
711
712 void KHTMLView::viewportMouseDoubleClickEvent( QMouseEvent *_mouse )
713 {
714     if(!m_part->xmlDocImpl()) return;
715
716     int xm, ym;
717     viewportToContents(_mouse->x(), _mouse->y(), xm, ym);
718
719     //kdDebug( 6000 ) << "mouseDblClickEvent: x=" << xm << ", y=" << ym << endl;
720
721     d->isDoubleClick = true;
722 #if APPLE_CHANGES
723     // We get this instead of a second mouse-up 
724     d->mousePressed = false;
725 #endif
726
727     DOM::NodeImpl::MouseEvent mev( _mouse->stateAfter(), DOM::NodeImpl::MouseDblClick );
728     m_part->xmlDocImpl()->prepareMouseEvent( false, xm, ym, &mev );
729
730 #if APPLE_CHANGES
731     if (KWQ(m_part)->passSubframeEventToSubframe(mev))
732         return;
733
734     d->clickCount = _mouse->clickCount();
735     bool swallowEvent = dispatchMouseEvent(EventImpl::MOUSEUP_EVENT,mev.innerNode.handle(),true,
736                                            d->clickCount,_mouse,false,DOM::NodeImpl::MouseRelease);
737     
738     dispatchMouseEvent(EventImpl::CLICK_EVENT,mev.innerNode.handle(),true,
739                            d->clickCount,_mouse,true,DOM::NodeImpl::MouseRelease);
740
741     // Qt delivers a release event AND a double click event.
742     if (!swallowEvent) {
743         khtml::MouseReleaseEvent event1( _mouse, xm, ym, mev.url, mev.target, mev.innerNode );
744         QApplication::sendEvent( m_part, &event1 );
745
746         khtml::MouseDoubleClickEvent event2( _mouse, xm, ym, mev.url, mev.target, mev.innerNode );
747         QApplication::sendEvent( m_part, &event2 );
748     }
749
750 #else
751     // We do the same thing as viewportMousePressEvent() here, since the DOM does not treat
752     // single and double-click events as separate (only the detail, i.e. number of clicks differs)
753     // In other words an even detail value for a mouse click event means a double click, and an
754     // odd detail value means a single click
755     bool swallowEvent = dispatchMouseEvent(EventImpl::MOUSEDOWN_EVENT,mev.innerNode.handle(),true,
756                                            d->clickCount,_mouse,true,DOM::NodeImpl::MouseDblClick);
757
758     if (!swallowEvent) {
759         khtml::MouseDoubleClickEvent event( _mouse, xm, ym, mev.url, mev.target, mev.innerNode );
760         QApplication::sendEvent( m_part, &event );
761
762         // ###
763         //if ( url.length() )
764         //emit doubleClick( url.string(), _mouse->button() );
765     }
766 #endif    
767 }
768
769 static bool isSubmitImage(DOM::NodeImpl *node)
770 {
771     return node
772         && node->isHTMLElement()
773         && node->id() == ID_INPUT
774         && static_cast<HTMLInputElementImpl*>(node)->inputType() == HTMLInputElementImpl::IMAGE;
775 }
776
777 void KHTMLView::viewportMouseMoveEvent( QMouseEvent * _mouse )
778 {
779     if(!m_part->xmlDocImpl()) return;
780
781     int xm, ym;
782     viewportToContents(_mouse->x(), _mouse->y(), xm, ym);
783
784     // Treat mouse move events while the mouse is pressed as "read-only" in prepareMouseEvent.
785     // This means that :hover and :active freeze in the state they were in when the mouse
786     // was pressed, rather than updating for nodes the mouse moves over as you hold the mouse down.
787     DOM::NodeImpl::MouseEvent mev( _mouse->stateAfter(), DOM::NodeImpl::MouseMove );
788     m_part->xmlDocImpl()->prepareMouseEvent( d->mousePressed, xm, ym, &mev );
789 #if APPLE_CHANGES
790     if (KWQ(m_part)->passSubframeEventToSubframe(mev))
791         return;
792 #endif
793
794     bool swallowEvent = dispatchMouseEvent(EventImpl::MOUSEMOVE_EVENT,mev.innerNode.handle(),false,
795                                            0,_mouse,true,DOM::NodeImpl::MouseMove);
796
797     if (d->clickCount > 0 &&
798         QPoint(d->clickX-xm,d->clickY-ym).manhattanLength() > QApplication::startDragDistance()) {
799         d->clickCount = 0;  // moving the mouse outside the threshold invalidates the click
800     }
801
802     // execute the scheduled script. This is to make sure the mouseover events come after the mouseout events
803     m_part->executeScheduledScript();
804
805     khtml::RenderStyle* style = (mev.innerNode.handle() && mev.innerNode.handle()->renderer() &&
806                                  mev.innerNode.handle()->renderer()->style()) ? mev.innerNode.handle()->renderer()->style() : 0;
807     QCursor c;
808     if (style && style->cursor() == CURSOR_AUTO && style->cursorImage()
809         && !(style->cursorImage()->pixmap().isNull())) {
810         /* First of all it works: Check out http://www.iam.unibe.ch/~schlpbch/cursor.html
811          *
812          * But, I don't know what exactly we have to do here: rescale to 32*32, change to monochrome..
813          */
814         //kdDebug( 6000 ) << "using custom cursor" << endl;
815         const QPixmap p = style->cursorImage()->pixmap();
816         // ### fix
817         c = QCursor(p);
818     }
819
820     switch ( style ? style->cursor() : CURSOR_AUTO) {
821     case CURSOR_AUTO:
822         if ( d->mousePressed && m_part->hasSelection() )
823             // during selection, use an IBeam no matter what we're over
824             c = KCursor::ibeamCursor();
825         else if ( (!mev.url.isNull() || isSubmitImage(mev.innerNode.handle()))
826                   && m_part->settings()->changeCursor() )
827             c = m_part->urlCursor();
828         else if ( !mev.innerNode.isNull()
829                   && (mev.innerNode.nodeType() == Node::TEXT_NODE
830                       || mev.innerNode.nodeType() == Node::CDATA_SECTION_NODE) )
831             c = KCursor::ibeamCursor();
832         break;
833     case CURSOR_CROSS:
834         c = KCursor::crossCursor();
835         break;
836     case CURSOR_POINTER:
837         c = m_part->urlCursor();
838         break;
839     case CURSOR_MOVE:
840         c = KCursor::sizeAllCursor();
841         break;
842     case CURSOR_E_RESIZE:
843 #if APPLE_CHANGES
844         c = KCursor::eastResizeCursor();
845         break;
846 #endif
847     case CURSOR_W_RESIZE:
848 #if APPLE_CHANGES
849         c = KCursor::westResizeCursor();
850 #else
851         c = KCursor::sizeHorCursor();
852 #endif
853         break;
854     case CURSOR_N_RESIZE:
855 #if APPLE_CHANGES
856         c = KCursor::northResizeCursor();
857         break;
858 #endif
859     case CURSOR_S_RESIZE:
860 #if APPLE_CHANGES
861         c = KCursor::southResizeCursor();
862 #else
863         c = KCursor::sizeVerCursor();
864 #endif
865         break;
866     case CURSOR_NE_RESIZE:
867 #if APPLE_CHANGES
868         c = KCursor::northEastResizeCursor();
869         break;
870 #endif
871     case CURSOR_SW_RESIZE:
872 #if APPLE_CHANGES
873         c = KCursor::southWestResizeCursor();
874 #else
875         c = KCursor::sizeBDiagCursor();
876 #endif
877         break;
878     case CURSOR_NW_RESIZE:
879 #if APPLE_CHANGES
880         c = KCursor::northWestResizeCursor();
881         break;
882 #endif
883     case CURSOR_SE_RESIZE:
884 #if APPLE_CHANGES
885         c = KCursor::southEastResizeCursor();
886 #else
887         c = KCursor::sizeFDiagCursor();
888 #endif
889         break;
890     case CURSOR_TEXT:
891         c = KCursor::ibeamCursor();
892         break;
893     case CURSOR_WAIT:
894         c = KCursor::waitCursor();
895         break;
896     case CURSOR_HELP:
897         c = KCursor::whatsThisCursor();
898         break;
899     case CURSOR_DEFAULT:
900         break;
901     }
902
903     QWidget *vp = viewport();
904     if ( vp->cursor().handle() != c.handle() ) {
905         if( c.handle() == KCursor::arrowCursor().handle())
906             vp->unsetCursor();
907         else
908             vp->setCursor( c );
909     }
910     d->prevMouseX = xm;
911     d->prevMouseY = ym;
912
913     if (!swallowEvent) {
914         khtml::MouseMoveEvent event( _mouse, xm, ym, mev.url, mev.target, mev.innerNode );
915         QApplication::sendEvent( m_part, &event );
916     }
917 }
918
919 void KHTMLView::resetCursor()
920 {
921     viewport()->unsetCursor();
922 }
923
924 void KHTMLView::viewportMouseReleaseEvent( QMouseEvent * _mouse )
925 {
926     if ( !m_part->xmlDocImpl() ) return;
927
928     int xm, ym;
929     viewportToContents(_mouse->x(), _mouse->y(), xm, ym);
930
931     d->mousePressed = false;
932
933     //kdDebug( 6000 ) << "\nmouseReleaseEvent: x=" << xm << ", y=" << ym << endl;
934
935     DOM::NodeImpl::MouseEvent mev( _mouse->stateAfter(), DOM::NodeImpl::MouseRelease );
936     m_part->xmlDocImpl()->prepareMouseEvent( false, xm, ym, &mev );
937 #if APPLE_CHANGES
938     if (KWQ(m_part)->passSubframeEventToSubframe(mev))
939         return;
940 #endif
941
942     bool swallowEvent = dispatchMouseEvent(EventImpl::MOUSEUP_EVENT,mev.innerNode.handle(),true,
943                                            d->clickCount,_mouse,false,DOM::NodeImpl::MouseRelease);
944
945     if (d->clickCount > 0 &&
946         QPoint(d->clickX-xm,d->clickY-ym).manhattanLength() <= QApplication::startDragDistance())
947         dispatchMouseEvent(EventImpl::CLICK_EVENT,mev.innerNode.handle(),true,
948                            d->clickCount,_mouse,true,DOM::NodeImpl::MouseRelease);
949
950     if (!swallowEvent) {
951         khtml::MouseReleaseEvent event( _mouse, xm, ym, mev.url, mev.target, mev.innerNode );
952         QApplication::sendEvent( m_part, &event );
953     }
954 }
955
956 void KHTMLView::keyPressEvent( QKeyEvent *_ke )
957 {
958
959     if (m_part->xmlDocImpl() && m_part->xmlDocImpl()->focusNode()) {
960         if (m_part->xmlDocImpl()->focusNode()->dispatchKeyEvent(_ke))
961         {
962             _ke->accept();
963             return;
964         }
965     }
966
967 #if !APPLE_CHANGES
968     int offs = (clipper()->height() < 30) ? clipper()->height() : 30;
969     if (_ke->state()&ShiftButton)
970       switch(_ke->key())
971         {
972         case Key_Space:
973             if ( d->vmode == QScrollView::AlwaysOff )
974                 _ke->accept();
975             else
976                 scrollBy( 0, -clipper()->height() - offs );
977             break;
978         }
979     else
980         switch ( _ke->key() )
981         {
982         case Key_Down:
983         case Key_J:
984             if ( d->vmode == QScrollView::AlwaysOff )
985                 _ke->accept();
986             else
987                 scrollBy( 0, 10 );
988             break;
989
990         case Key_Space:
991         case Key_Next:
992             if ( d->vmode == QScrollView::AlwaysOff )
993                 _ke->accept();
994             else
995                 scrollBy( 0, clipper()->height() - offs );
996             break;
997
998         case Key_Up:
999         case Key_K:
1000             if ( d->vmode == QScrollView::AlwaysOff )
1001                 _ke->accept();
1002             else
1003                 scrollBy( 0, -10 );
1004             break;
1005
1006         case Key_Prior:
1007             if ( d->vmode == QScrollView::AlwaysOff )
1008                 _ke->accept();
1009             else
1010                 scrollBy( 0, -clipper()->height() + offs );
1011             break;
1012         case Key_Right:
1013         case Key_L:
1014             if ( d->hmode == QScrollView::AlwaysOff )
1015                 _ke->accept();
1016             else
1017                 scrollBy( 10, 0 );
1018             break;
1019         case Key_Left:
1020         case Key_H:
1021             if ( d->hmode == QScrollView::AlwaysOff )
1022                 _ke->accept();
1023             else
1024                 scrollBy( -10, 0 );
1025             break;
1026         case Key_Enter:
1027         case Key_Return:
1028             // ### FIXME:
1029             // or even better to HTMLAnchorElementImpl::event()
1030             if (m_part->xmlDocImpl()) {
1031                 NodeImpl *n = m_part->xmlDocImpl()->focusNode();
1032                 if (n)
1033                     n->setActive();
1034                 d->originalNode = n;
1035             }
1036             break;
1037         case Key_Home:
1038             if ( d->vmode == QScrollView::AlwaysOff )
1039                 _ke->accept();
1040             else
1041                 setContentsPos( 0, 0 );
1042             break;
1043         case Key_End:
1044             if ( d->vmode == QScrollView::AlwaysOff )
1045                 _ke->accept();
1046             else
1047                 setContentsPos( 0, contentsHeight() - visibleHeight() );
1048             break;
1049         default:
1050             _ke->ignore();
1051             return;
1052         }
1053     _ke->accept();
1054 #endif
1055 }
1056
1057 void KHTMLView::keyReleaseEvent(QKeyEvent *_ke)
1058 {
1059     if(m_part->xmlDocImpl() && m_part->xmlDocImpl()->focusNode()) {
1060         // Qt is damn buggy. we receive release events from our child
1061         // widgets. therefore, do not support keyrelease event on generic
1062         // nodes for now until we found  a workaround for the Qt bugs. (Dirk)
1063 //         if (m_part->xmlDocImpl()->focusNode()->dispatchKeyEvent(_ke)) {
1064 //             _ke->accept();
1065 //             return;
1066 //         }
1067 //        QScrollView::keyReleaseEvent(_ke);
1068         Q_UNUSED(_ke);
1069     }
1070 }
1071
1072 void KHTMLView::contentsContextMenuEvent ( QContextMenuEvent *_ce )
1073 {
1074 // ### what kind of c*** is that ?
1075 #if 0
1076     if (!m_part->xmlDocImpl()) return;
1077     int xm = _ce->x();
1078     int ym = _ce->y();
1079
1080     DOM::NodeImpl::MouseEvent mev( _ce->state(), DOM::NodeImpl::MouseMove ); // ### not a mouse event!
1081     m_part->xmlDocImpl()->prepareMouseEvent( xm, ym, &mev );
1082
1083     NodeImpl *targetNode = mev.innerNode.handle();
1084     if (targetNode && targetNode->renderer() && targetNode->renderer()->isWidget()) {
1085         int absx = 0;
1086         int absy = 0;
1087         targetNode->renderer()->absolutePosition(absx,absy);
1088         QPoint pos(xm-absx,ym-absy);
1089
1090         QWidget *w = static_cast<RenderWidget*>(targetNode->renderer())->widget();
1091         QContextMenuEvent cme(_ce->reason(),pos,_ce->globalPos(),_ce->state());
1092         setIgnoreEvents(true);
1093         QApplication::sendEvent(w,&cme);
1094         setIgnoreEvents(false);
1095     }
1096 #endif
1097 }
1098
1099 bool KHTMLView::focusNextPrevChild( bool next )
1100 {
1101     // Now try to find the next child
1102     if (m_part->xmlDocImpl()) {
1103         focusNextPrevNode(next);
1104         if (m_part->xmlDocImpl()->focusNode() != 0)
1105             return true; // focus node found
1106     }
1107
1108     // If we get here, there is no next/previous child to go to, so pass up to the next/previous child in our parent
1109     if (m_part->parentPart() && m_part->parentPart()->view()) {
1110         return m_part->parentPart()->view()->focusNextPrevChild(next);
1111     }
1112
1113     return QWidget::focusNextPrevChild(next);
1114 }
1115
1116 void KHTMLView::doAutoScroll()
1117 {
1118     QPoint pos = QCursor::pos();
1119     pos = viewport()->mapFromGlobal( pos );
1120
1121     int xm, ym;
1122     viewportToContents(pos.x(), pos.y(), xm, ym);
1123
1124     pos = QPoint(pos.x() - viewport()->x(), pos.y() - viewport()->y());
1125     if ( (pos.y() < 0) || (pos.y() > visibleHeight()) ||
1126          (pos.x() < 0) || (pos.x() > visibleWidth()) )
1127     {
1128         ensureVisible( xm, ym, 0, 5 );
1129     }
1130 }
1131
1132 DOM::NodeImpl *KHTMLView::nodeUnderMouse() const
1133 {
1134     return d->underMouse;
1135 }
1136
1137 bool KHTMLView::scrollTo(const QRect &bounds)
1138 {
1139     d->scrollingSelf = true; // so scroll events get ignored
1140
1141     int x, y, xe, ye;
1142     x = bounds.left();
1143     y = bounds.top();
1144     xe = bounds.right();
1145     ye = bounds.bottom();
1146
1147     //kdDebug(6000)<<"scrolling coords: x="<<x<<" y="<<y<<" width="<<xe-x<<" height="<<ye-y<<endl;
1148
1149     int deltax;
1150     int deltay;
1151
1152     int curHeight = visibleHeight();
1153     int curWidth = visibleWidth();
1154
1155     if (ye-y>curHeight-d->borderY)
1156         ye  = y + curHeight - d->borderY;
1157
1158     if (xe-x>curWidth-d->borderX)
1159         xe = x + curWidth - d->borderX;
1160
1161     // is xpos of target left of the view's border?
1162     if (x < contentsX() + d->borderX )
1163             deltax = x - contentsX() - d->borderX;
1164     // is xpos of target right of the view's right border?
1165     else if (xe + d->borderX > contentsX() + curWidth)
1166             deltax = xe + d->borderX - ( contentsX() + curWidth );
1167     else
1168         deltax = 0;
1169
1170     // is ypos of target above upper border?
1171     if (y < contentsY() + d->borderY)
1172             deltay = y - contentsY() - d->borderY;
1173     // is ypos of target below lower border?
1174     else if (ye + d->borderY > contentsY() + curHeight)
1175             deltay = ye + d->borderY - ( contentsY() + curHeight );
1176     else
1177         deltay = 0;
1178
1179     int maxx = curWidth-d->borderX;
1180     int maxy = curHeight-d->borderY;
1181
1182     int scrollX,scrollY;
1183
1184     scrollX = deltax > 0 ? (deltax > maxx ? maxx : deltax) : deltax == 0 ? 0 : (deltax>-maxx ? deltax : -maxx);
1185     scrollY = deltay > 0 ? (deltay > maxy ? maxy : deltay) : deltay == 0 ? 0 : (deltay>-maxy ? deltay : -maxy);
1186
1187     if (contentsX() + scrollX < 0)
1188         scrollX = -contentsX();
1189     else if (contentsWidth() - visibleWidth() - contentsX() < scrollX)
1190         scrollX = contentsWidth() - visibleWidth() - contentsX();
1191
1192     if (contentsY() + scrollY < 0)
1193         scrollY = -contentsY();
1194     else if (contentsHeight() - visibleHeight() - contentsY() < scrollY)
1195         scrollY = contentsHeight() - visibleHeight() - contentsY();
1196
1197     scrollBy(scrollX, scrollY);
1198
1199
1200
1201     // generate abs(scroll.)
1202     if (scrollX<0)
1203         scrollX=-scrollX;
1204     if (scrollY<0)
1205         scrollY=-scrollY;
1206
1207     d->scrollingSelf = false;
1208
1209     if ( (scrollX!=maxx) && (scrollY!=maxy) )
1210         return true;
1211     else return false;
1212
1213 }
1214
1215 void KHTMLView::focusNextPrevNode(bool next)
1216 {
1217     // Sets the focus node of the document to be the node after (or if next is false, before) the current focus node.
1218     // Only nodes that are selectable (i.e. for which isSelectable() returns true) are taken into account, and the order
1219     // used is that specified in the HTML spec (see DocumentImpl::nextFocusNode() and DocumentImpl::previousFocusNode()
1220     // for details).
1221
1222     DocumentImpl *doc = m_part->xmlDocImpl();
1223     NodeImpl *oldFocusNode = doc->focusNode();
1224     NodeImpl *newFocusNode;
1225
1226     // Find the next/previous node from the current one
1227     if (next)
1228         newFocusNode = doc->nextFocusNode(oldFocusNode);
1229     else
1230         newFocusNode = doc->previousFocusNode(oldFocusNode);
1231
1232     // If there was previously no focus node and the user has scrolled the document, then instead of picking the first
1233     // focusable node in the document, use the first one that lies within the visible area (if possible).
1234     if (!oldFocusNode && newFocusNode && d->scrollBarMoved) {
1235
1236       kdDebug(6000) << " searching for visible link" << endl;
1237
1238         bool visible = false;
1239         NodeImpl *toFocus = newFocusNode;
1240         while (!visible && toFocus) {
1241             QRect focusNodeRect = toFocus->getRect();
1242             if ((focusNodeRect.left() > contentsX()) && (focusNodeRect.right() < contentsX() + visibleWidth()) &&
1243                 (focusNodeRect.top() > contentsY()) && (focusNodeRect.bottom() < contentsY() + visibleHeight())) {
1244                 // toFocus is visible in the contents area
1245                 visible = true;
1246             }
1247             else {
1248                 // toFocus is _not_ visible in the contents area, pick the next node
1249                 if (next)
1250                     toFocus = doc->nextFocusNode(toFocus);
1251                 else
1252                     toFocus = doc->previousFocusNode(toFocus);
1253             }
1254         }
1255
1256         if (toFocus)
1257             newFocusNode = toFocus;
1258     }
1259
1260     d->scrollBarMoved = false;
1261
1262     if (!newFocusNode)
1263       {
1264         // No new focus node, scroll to bottom or top depending on next
1265         if (next)
1266             scrollTo(QRect(contentsX()+visibleWidth()/2,contentsHeight(),0,0));
1267         else
1268             scrollTo(QRect(contentsX()+visibleWidth()/2,0,0,0));
1269     }
1270     else
1271     // Scroll the view as necessary to ensure that the new focus node is visible
1272     {
1273       if (oldFocusNode)
1274         {
1275           if (!scrollTo(newFocusNode->getRect()))
1276             return;
1277         }
1278       else
1279         {
1280           ensureVisible(contentsX(), next?0:contentsHeight());
1281           //return;
1282         }
1283     }
1284     // Set focus node on the document
1285     m_part->xmlDocImpl()->setFocusNode(newFocusNode);
1286     emit m_part->nodeActivated(Node(newFocusNode));
1287
1288 #if 0
1289     if (newFocusNode) {
1290
1291         // this does not belong here. it should run a query on the tree (Dirk)
1292         // I'll fix this very particular part of the code soon when I cleaned
1293         // up the positioning code
1294         // If the newly focussed node is a link, notify the part
1295
1296         HTMLAnchorElementImpl *anchor = 0;
1297         if ((newFocusNode->id() == ID_A || newFocusNode->id() == ID_AREA))
1298             anchor = static_cast<HTMLAnchorElementImpl *>(newFocusNode);
1299
1300         if (anchor && !anchor->areaHref().isNull())
1301             m_part->overURL(anchor->areaHref().string(), 0);
1302         else
1303             m_part->overURL(QString(), 0);
1304     }
1305 #endif
1306 }
1307
1308 void KHTMLView::setMediaType( const QString &medium )
1309 {
1310     m_medium = medium;
1311 }
1312
1313 QString KHTMLView::mediaType() const
1314 {
1315     return m_medium;
1316 }
1317
1318 #if !APPLE_CHANGES
1319
1320 void KHTMLView::print()
1321 {
1322     if(!m_part->xmlDocImpl()) return;
1323     khtml::RenderCanvas *root = static_cast<khtml::RenderCanvas *>(m_part->xmlDocImpl()->renderer());
1324     if(!root) return;
1325
1326     // this only works on Unix - we assume 72dpi
1327     KPrinter *printer = new KPrinter(QPrinter::PrinterResolution);
1328     printer->addDialogPage(new KHTMLPrintSettings());
1329     if(printer->setup(this)) {
1330         viewport()->setCursor( waitCursor ); // only viewport(), no QApplication::, otherwise we get the busy cursor in kdeprint's dialogs
1331         // set up KPrinter
1332         printer->setFullPage(false);
1333         printer->setCreator("KDE 3.0 HTML Library");
1334         QString docname = m_part->xmlDocImpl()->URL();
1335         if ( !docname.isEmpty() )
1336             printer->setDocName(docname);
1337
1338         QPainter *p = new QPainter;
1339         p->begin( printer );
1340         khtml::setPrintPainter( p );
1341
1342         m_part->xmlDocImpl()->setPaintDevice( printer );
1343         QString oldMediaType = mediaType();
1344         setMediaType( "print" );
1345         // We ignore margin settings for html and body when printing
1346         // and use the default margins from the print-system
1347         // (In Qt 3.0.x the default margins are hardcoded in Qt)
1348         m_part->xmlDocImpl()->setPrintStyleSheet( printer->option("kde-khtml-printfriendly") == "true" ?
1349                                                   "* { background-image: none !important;"
1350                                                   "    background-color: white !important;"
1351                                                   "    color: black !important; }"
1352                                                   "body { margin: 0px !important; }"
1353                                                   "html { margin: 0px !important; }" :
1354                                                   "body { margin: 0px !important; }"
1355                                                   "html { margin: 0px !important; }"
1356                                                   );
1357
1358
1359         QPaintDeviceMetrics metrics( printer );
1360
1361         // this is a simple approximation... we layout the document
1362         // according to the width of the page, then just cut
1363         // pages without caring about the content. We should do better
1364         // in the future, but for the moment this is better than no
1365         // printing support
1366         kdDebug(6000) << "printing: physical page width = " << metrics.width()
1367                       << " height = " << metrics.height() << endl;
1368         root->setPrintingMode(true);
1369         root->setWidth(metrics.width());
1370
1371         m_part->xmlDocImpl()->styleSelector()->computeFontSizes(&metrics);
1372         m_part->xmlDocImpl()->updateStyleSelector();
1373         root->setPrintImages( printer->option("kde-khtml-printimages") == "true");
1374         root->setNeedsLayoutAndMinMaxRecalc();
1375         root->layout();
1376
1377         // ok. now print the pages.
1378         kdDebug(6000) << "printing: html page width = " << root->docWidth()
1379                       << " height = " << root->docHeight() << endl;
1380         kdDebug(6000) << "printing: margins left = " << printer->margins().width()
1381                       << " top = " << printer->margins().height() << endl;
1382         kdDebug(6000) << "printing: paper width = " << metrics.width()
1383                       << " height = " << metrics.height() << endl;
1384         // if the width is too large to fit on the paper we just scale
1385         // the whole thing.
1386         int pageHeight = metrics.height();
1387         int pageWidth = metrics.width();
1388         p->setClipRect(0,0, pageWidth, pageHeight);
1389         if(root->docWidth() > metrics.width()) {
1390             double scale = ((double) metrics.width())/((double) root->docWidth());
1391 #ifndef QT_NO_TRANSFORMATIONS
1392             p->scale(scale, scale);
1393 #endif
1394             pageHeight = (int) (pageHeight/scale);
1395             pageWidth = (int) (pageWidth/scale);
1396         }
1397         kdDebug(6000) << "printing: scaled html width = " << pageWidth
1398                       << " height = " << pageHeight << endl;
1399         int top = 0;
1400         while(top < root->docHeight()) {
1401             if(top > 0) printer->newPage();
1402             root->setTruncatedAt(top+pageHeight);
1403
1404             root->print(p, 0, top, pageWidth, pageHeight, 0, 0);
1405             if (top + pageHeight >= root->docHeight())
1406                 break; // Stop if we have printed everything
1407
1408             p->translate(0, top - root->truncatedAt());
1409             top = root->truncatedAt();
1410         }
1411
1412         p->end();
1413         delete p;
1414
1415         // and now reset the layout to the usual one...
1416         root->setPrintingMode(false);
1417         khtml::setPrintPainter( 0 );
1418         setMediaType( oldMediaType );
1419         m_part->xmlDocImpl()->setPaintDevice( this );
1420         m_part->xmlDocImpl()->styleSelector()->computeFontSizes(m_part->xmlDocImpl()->paintDeviceMetrics());
1421         m_part->xmlDocImpl()->updateStyleSelector();
1422         viewport()->unsetCursor();
1423     }
1424     delete printer;
1425 }
1426
1427 void KHTMLView::slotPaletteChanged()
1428 {
1429     if(!m_part->xmlDocImpl()) return;
1430     DOM::DocumentImpl *document = m_part->xmlDocImpl();
1431     if (!document->isHTMLDocument()) return;
1432     khtml::RenderCanvas *root = static_cast<khtml::RenderCanvas *>(document->renderer());
1433     if(!root) return;
1434     root->style()->resetPalette();
1435     NodeImpl *body = static_cast<HTMLDocumentImpl*>(document)->body();
1436     if(!body) return;
1437     body->setChanged(true);
1438     body->recalcStyle( NodeImpl::Force );
1439 }
1440
1441 void KHTMLView::paint(QPainter *p, const QRect &rc, int yOff, bool *more)
1442 {
1443     if(!m_part->xmlDocImpl()) return;
1444     khtml::RenderCanvas *root = static_cast<khtml::RenderCanvas *>(m_part->xmlDocImpl()->renderer());
1445     if(!root) return;
1446
1447     m_part->xmlDocImpl()->setPaintDevice(p->device());
1448     root->setPrintingMode(true);
1449     root->setWidth(rc.width());
1450
1451     p->save();
1452     p->setClipRect(rc);
1453     p->translate(rc.left(), rc.top());
1454     double scale = ((double) rc.width()/(double) root->docWidth());
1455     int height = (int) ((double) rc.height() / scale);
1456 #ifndef QT_NO_TRANSFORMATIONS
1457     p->scale(scale, scale);
1458 #endif
1459
1460     root->print(p, 0, yOff, root->docWidth(), height, 0, 0);
1461     if (more)
1462         *more = yOff + height < root->docHeight();
1463     p->restore();
1464
1465     root->setPrintingMode(false);
1466     m_part->xmlDocImpl()->setPaintDevice( this );
1467 }
1468
1469 #endif // !APPLE_CHANGES
1470
1471 void KHTMLView::useSlowRepaints()
1472 {
1473     kdDebug(0) << "slow repaints requested" << endl;
1474     d->useSlowRepaints = true;
1475     setStaticBackground(true);
1476 }
1477
1478 void KHTMLView::setScrollBarsMode ( ScrollBarMode mode )
1479 {
1480 #ifndef KHTML_NO_SCROLLBARS
1481     d->vmode = mode;
1482     d->hmode = mode;
1483     
1484 #if APPLE_CHANGES
1485     QScrollView::setScrollBarsMode(mode);
1486 #else
1487     QScrollView::setVScrollBarMode(mode);
1488     QScrollView::setHScrollBarMode(mode);
1489 #endif
1490 #else
1491     Q_UNUSED( mode );
1492 #endif
1493 }
1494
1495 void KHTMLView::setVScrollBarMode ( ScrollBarMode mode )
1496 {
1497 #ifndef KHTML_NO_SCROLLBARS
1498     d->vmode = mode;
1499     QScrollView::setVScrollBarMode(mode);
1500 #else
1501     Q_UNUSED( mode );
1502 #endif
1503 }
1504
1505 void KHTMLView::setHScrollBarMode ( ScrollBarMode mode )
1506 {
1507 #ifndef KHTML_NO_SCROLLBARS
1508     d->hmode = mode;
1509     QScrollView::setHScrollBarMode(mode);
1510 #else
1511     Q_UNUSED( mode );
1512 #endif
1513 }
1514
1515 void KHTMLView::restoreScrollBar ( )
1516 {
1517 #if APPLE_CHANGES
1518     suppressScrollBars(false);
1519 #else
1520     int ow = visibleWidth();
1521     QScrollView::setVScrollBarMode(d->vmode);
1522     if (visibleWidth() != ow)
1523         layout();
1524     d->prevScrollbarVisible = verticalScrollBar()->isVisible();
1525 #endif
1526 }
1527
1528 QStringList KHTMLView::formCompletionItems(const QString &name) const
1529 {
1530     if (!m_part->settings()->isFormCompletionEnabled())
1531         return QStringList();
1532     if (!d->formCompletions)
1533         d->formCompletions = new KSimpleConfig(locateLocal("data", "khtml/formcompletions"));
1534     return d->formCompletions->readListEntry(name);
1535 }
1536
1537 void KHTMLView::addFormCompletionItem(const QString &name, const QString &value)
1538 {
1539     if (!m_part->settings()->isFormCompletionEnabled())
1540         return;
1541     // don't store values that are all numbers or just numbers with
1542     // dashes or spaces as those are likely credit card numbers or
1543     // something similar
1544     bool cc_number(true);
1545     for (unsigned int i = 0; i < value.length(); ++i)
1546     {
1547       QChar c(value[i]);
1548       if (!c.isNumber() && c != '-' && !c.isSpace())
1549       {
1550         cc_number = false;
1551         break;
1552       }
1553     }
1554     if (cc_number)
1555       return;
1556     QStringList items = formCompletionItems(name);
1557     if (!items.contains(value))
1558         items.prepend(value);
1559     while ((int)items.count() > m_part->settings()->maxFormCompletionItems())
1560         items.remove(items.fromLast());
1561     d->formCompletions->writeEntry(name, items);
1562 }
1563
1564 bool KHTMLView::dispatchMouseEvent(int eventId, DOM::NodeImpl *targetNode, bool cancelable,
1565                                    int detail,QMouseEvent *_mouse, bool setUnder,
1566                                    int mouseEventType)
1567 {
1568     if (d->underMouse)
1569         d->underMouse->deref();
1570     d->underMouse = targetNode;
1571     if (d->underMouse)
1572         d->underMouse->ref();
1573
1574     int exceptioncode = 0;
1575     int clientX, clientY;
1576     viewportToContents(_mouse->x(), _mouse->y(), clientX, clientY);
1577     int screenX = _mouse->globalX();
1578     int screenY = _mouse->globalY();
1579     int button = -1;
1580     switch (_mouse->button()) {
1581         case LeftButton:
1582             button = 0;
1583             break;
1584         case MidButton:
1585             button = 1;
1586             break;
1587         case RightButton:
1588             button = 2;
1589             break;
1590         default:
1591             break;
1592     }
1593     bool ctrlKey = (_mouse->state() & ControlButton);
1594     bool altKey = (_mouse->state() & AltButton);
1595     bool shiftKey = (_mouse->state() & ShiftButton);
1596     bool metaKey = (_mouse->state() & MetaButton);
1597
1598     // mouseout/mouseover
1599     if (setUnder && (d->prevMouseX != clientX || d->prevMouseY != clientY)) {
1600
1601         // ### this code sucks. we should save the oldUnder instead of calculating
1602         // it again. calculating is expensive! (Dirk)
1603         NodeImpl *oldUnder = 0;
1604         if (d->prevMouseX >= 0 && d->prevMouseY >= 0) {
1605             NodeImpl::MouseEvent mev( _mouse->stateAfter(), static_cast<NodeImpl::MouseEventType>(mouseEventType));
1606             m_part->xmlDocImpl()->prepareMouseEvent( true, d->prevMouseX, d->prevMouseY, &mev );
1607             oldUnder = mev.innerNode.handle();
1608         }
1609         if (oldUnder != targetNode) {
1610             // send mouseout event to the old node
1611             if (oldUnder){
1612                 oldUnder->ref();
1613                 MouseEventImpl *me = new MouseEventImpl(EventImpl::MOUSEOUT_EVENT,
1614                                                         true,true,m_part->xmlDocImpl()->defaultView(),
1615                                                         0,screenX,screenY,clientX,clientY,
1616                                                         ctrlKey,altKey,shiftKey,metaKey,
1617                                                         button,targetNode);
1618                 me->ref();
1619                 oldUnder->dispatchEvent(me,exceptioncode,true);
1620                 me->deref();
1621             }
1622
1623             // send mouseover event to the new node
1624             if (targetNode) {
1625                 MouseEventImpl *me = new MouseEventImpl(EventImpl::MOUSEOVER_EVENT,
1626                                                         true,true,m_part->xmlDocImpl()->defaultView(),
1627                                                         0,screenX,screenY,clientX,clientY,
1628                                                         ctrlKey,altKey,shiftKey,metaKey,
1629                                                         button,oldUnder);
1630
1631                 me->ref();
1632                 targetNode->dispatchEvent(me,exceptioncode,true);
1633                 me->deref();
1634             }
1635
1636             if (oldUnder)
1637                 oldUnder->deref();
1638         }
1639     }
1640
1641     bool swallowEvent = false;
1642
1643     if (targetNode) {
1644         // send the actual event
1645         MouseEventImpl *me = new MouseEventImpl(static_cast<EventImpl::EventId>(eventId),
1646                                                 true,cancelable,m_part->xmlDocImpl()->defaultView(),
1647                                                 detail,screenX,screenY,clientX,clientY,
1648                                                 ctrlKey,altKey,shiftKey,metaKey,
1649                                                 button,0);
1650         me->ref();
1651         targetNode->dispatchEvent(me,exceptioncode,true);
1652         bool defaultHandled = me->defaultHandled();
1653         if (me->defaultHandled() || me->defaultPrevented())
1654             swallowEvent = true;
1655         me->deref();
1656
1657         // Special case: If it's a click event, we also send the KHTML_CLICK or KHTML_DBLCLICK event. This is not part
1658         // of the DOM specs, but is used for compatibility with the traditional onclick="" and ondblclick="" attributes,
1659         // as there is no way to tell the difference between single & double clicks using DOM (only the click count is
1660         // stored, which is not necessarily the same)
1661         if (eventId == EventImpl::CLICK_EVENT) {
1662             me = new MouseEventImpl(d->isDoubleClick ? EventImpl::KHTML_DBLCLICK_EVENT : EventImpl::KHTML_CLICK_EVENT,
1663                                     true,cancelable,m_part->xmlDocImpl()->defaultView(),
1664                                     detail,screenX,screenY,clientX,clientY,
1665                                     ctrlKey,altKey,shiftKey,metaKey,
1666                                     button,0);
1667
1668             me->ref();
1669             if (defaultHandled)
1670                 me->setDefaultHandled();
1671             targetNode->dispatchEvent(me,exceptioncode,true);
1672             if (me->defaultHandled() || me->defaultPrevented())
1673                 swallowEvent = true;
1674             me->deref();
1675         }
1676         else if (eventId == EventImpl::MOUSEDOWN_EVENT) {
1677             // Focus should be shifted on mouse down, not on a click.  -dwh
1678             // Blur current focus node when a link/button is clicked; this
1679             // is expected by some sites that rely on onChange handlers running
1680             // from form fields before the button click is processed.
1681             DOM::NodeImpl* nodeImpl = targetNode;
1682             for ( ; nodeImpl && !nodeImpl->isFocusable(); nodeImpl = nodeImpl->parentNode());
1683             if (nodeImpl && nodeImpl->isMouseFocusable())
1684                 m_part->xmlDocImpl()->setFocusNode(nodeImpl);
1685             else if (!nodeImpl || !nodeImpl->focused())
1686                 m_part->xmlDocImpl()->setFocusNode(0);
1687         }
1688     }
1689
1690     return swallowEvent;
1691 }
1692
1693 void KHTMLView::setIgnoreWheelEvents( bool e )
1694 {
1695     d->ignoreWheelEvents = e;
1696 }
1697
1698 #ifndef QT_NO_WHEELEVENT
1699
1700 void KHTMLView::viewportWheelEvent(QWheelEvent* e)
1701 {
1702 #if !APPLE_CHANGES
1703     if ( d->ignoreWheelEvents && !verticalScrollBar()->isVisible() && m_part->parentPart() ) {
1704         if ( m_part->parentPart()->view() )
1705             m_part->parentPart()->view()->wheelEvent( e );
1706         e->ignore();
1707     }
1708     else if ( d->vmode == QScrollView::AlwaysOff ) {
1709         e->accept();
1710     }
1711     else {
1712         d->scrollBarMoved = true;
1713         QScrollView::viewportWheelEvent( e );
1714     }
1715 #endif
1716 }
1717 #endif
1718
1719 #if !APPLE_CHANGES
1720 void KHTMLView::dragEnterEvent( QDragEnterEvent* ev )
1721 {
1722     // Handle drops onto frames (#16820)
1723     // Drops on the main html part is handled by Konqueror (and shouldn't do anything
1724     // in e.g. kmail, so not handled here).
1725     if ( m_part->parentPart() )
1726     {
1727         // Duplicated from KonqView::eventFilter
1728         if ( QUriDrag::canDecode( ev ) )
1729         {
1730             KURL::List lstDragURLs;
1731             bool ok = KURLDrag::decode( ev, lstDragURLs );
1732             QObjectList *children = this->queryList( "QWidget" );
1733
1734             if ( ok &&
1735                  !lstDragURLs.first().url().contains( "javascript:", false ) && // ### this looks like a hack to me
1736                  ev->source() != this &&
1737                  children &&
1738                  children->findRef( ev->source() ) == -1 )
1739                 ev->acceptAction();
1740
1741             delete children;
1742         }
1743     }
1744     QScrollView::dragEnterEvent( ev );
1745 }
1746
1747 void KHTMLView::dropEvent( QDropEvent *ev )
1748 {
1749     // Handle drops onto frames (#16820)
1750     // Drops on the main html part is handled by Konqueror (and shouldn't do anything
1751     // in e.g. kmail, so not handled here).
1752     if ( m_part->parentPart() )
1753     {
1754         KURL::List lstDragURLs;
1755         bool ok = KURLDrag::decode( ev, lstDragURLs );
1756
1757         KHTMLPart* part = m_part->parentPart();
1758         while ( part && part->parentPart() )
1759             part = part->parentPart();
1760         KParts::BrowserExtension *ext = part->browserExtension();
1761         if ( ok && ext && lstDragURLs.first().isValid() )
1762             emit ext->openURLRequest( lstDragURLs.first() );
1763     }
1764     QScrollView::dropEvent( ev );
1765 }
1766 #endif // !APPLE_CHANGES
1767
1768 void KHTMLView::focusOutEvent( QFocusEvent *e )
1769 {
1770     m_part->stopAutoScroll();
1771     QScrollView::focusOutEvent( e );
1772 }
1773
1774 void KHTMLView::slotScrollBarMoved()
1775 {
1776     if (!d->scrollingSelf)
1777         d->scrollBarMoved = true;
1778 }
1779
1780 void KHTMLView::repaintRectangle(const QRect& r, bool immediate)
1781 {
1782 #ifndef INCREMENTAL_REPAINTING
1783     if (d->layoutTimerId) // Don't bother scheduling a repaint when a layout is pending.
1784         return;
1785 #endif
1786
1787     updateContents(r, immediate);
1788 }
1789
1790 void KHTMLView::timerEvent ( QTimerEvent *e )
1791 {
1792 //    kdDebug() << "timer event " << e->timerId() << endl;
1793     if (e->timerId()==d->layoutTimerId)
1794         layout();
1795 }
1796
1797 #ifdef INCREMENTAL_REPAINTING
1798 void KHTMLView::scheduleRelayout()
1799 #else
1800 void KHTMLView::scheduleRelayout(khtml::RenderObject* clippedObj)
1801 #endif
1802 {
1803     if (!d->layoutSchedulingEnabled)
1804         return;
1805
1806 #ifndef INCREMENTAL_REPAINTING
1807     if (m_layoutObject != clippedObj)
1808       m_layoutObject = 0;
1809 #endif
1810     
1811     if (d->layoutTimerId)
1812         return;
1813
1814 #ifndef INCREMENTAL_REPAINTING
1815     m_layoutObject = clippedObj;
1816 #endif
1817
1818     bool parsing = false;
1819     if( m_part->xmlDocImpl() ) {
1820         parsing = m_part->xmlDocImpl()->parsing();
1821     }
1822
1823     d->layoutTimerId = startTimer( parsing ? 1000 : 0 );
1824 }
1825
1826 void KHTMLView::unscheduleRelayout()
1827 {
1828 #ifndef INCREMENTAL_REPAINTING
1829     m_layoutObject = 0;
1830 #endif
1831
1832     if (!d->layoutTimerId)
1833         return;
1834
1835     killTimer(d->layoutTimerId);
1836     d->layoutTimerId = 0;
1837 }
1838
1839 void KHTMLView::complete()
1840 {
1841 //     kdDebug() << "KHTMLView::complete()" << endl;
1842
1843     d->complete = true;
1844
1845     // is there a relayout pending?
1846     if (d->layoutTimerId)
1847     {
1848 //         kdDebug() << "requesting relayout now" << endl;
1849         // do it now
1850         killTimer(d->layoutTimerId);
1851         d->layoutTimerId = startTimer( 0 );
1852     }
1853 }