7d6d16b8bf2e5f37a7387680ccfe285e896b687c
[WebKit-https.git] / WebCore / kwq / KWQWidget.mm
1 /*
2  * Copyright (C) 2004 Apple Computer, Inc.  All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
24  */
25
26 #import "KWQWidget.h"
27
28 #import "KWQExceptions.h"
29 #import "KWQFoundationExtras.h"
30 #import "KWQKHTMLPart.h"
31 #import "KWQLogging.h"
32 #import "KWQView.h"
33 #import "KWQWindowWidget.h"
34 #import "WebCoreBridge.h"
35 #import "WebCoreFrameView.h"
36 #import "WebCoreView.h"
37 #import "khtmlview.h"
38 #import "render_canvas.h"
39 #import "render_replaced.h"
40 #import "render_style.h"
41
42 using khtml::RenderWidget;
43
44 /*
45     A QWidget roughly corresponds to an NSView.  In Qt a QFrame and QMainWindow inherit
46     from a QWidget.  In Cocoa a NSWindow does not inherit from NSView.  We
47     emulate most QWidgets using NSViews.
48 */
49
50 class KWQWidgetPrivate
51 {
52 public:
53     QStyle *style;
54     QFont font;
55     QPalette pal;
56     NSView *view;
57     bool visible;
58     bool mustStayInWindow;
59     bool removeFromSuperviewSoon;
60 };
61
62 QWidget::QWidget() : data(new KWQWidgetPrivate)
63 {
64     static QStyle defaultStyle;
65     data->style = &defaultStyle;
66     data->view = nil;
67     data->visible = true;
68     data->mustStayInWindow = false;
69     data->removeFromSuperviewSoon = false;
70 }
71
72 QWidget::QWidget(NSView *view) : data(new KWQWidgetPrivate)
73 {
74     static QStyle defaultStyle;
75     data->style = &defaultStyle;
76     data->view = KWQRetain(view);
77     data->visible = true;
78     data->mustStayInWindow = false;
79     data->removeFromSuperviewSoon = false;
80 }
81
82 QWidget::~QWidget() 
83 {
84     KWQ_BLOCK_EXCEPTIONS;
85     KWQRelease(data->view);
86     KWQ_UNBLOCK_EXCEPTIONS;
87
88     delete data;
89 }
90
91 QSize QWidget::sizeHint() const 
92 {
93     // May be overriden by subclasses.
94     return QSize(0,0);
95 }
96
97 void QWidget::resize(int w, int h) 
98 {
99     setFrameGeometry(QRect(pos().x(), pos().y(), w, h));
100 }
101
102 void QWidget::setActiveWindow() 
103 {
104     KWQ_BLOCK_EXCEPTIONS;
105     [KWQKHTMLPart::bridgeForWidget(this) focusWindow];
106     KWQ_UNBLOCK_EXCEPTIONS;
107 }
108
109 void QWidget::setEnabled(bool enabled)
110 {
111     id view = data->view;
112     KWQ_BLOCK_EXCEPTIONS;
113     if ([view respondsToSelector:@selector(setEnabled:)]) {
114         [view setEnabled:enabled];
115     }
116     KWQ_UNBLOCK_EXCEPTIONS;
117 }
118
119 bool QWidget::isEnabled() const
120 {
121     id view = data->view;
122
123     KWQ_BLOCK_EXCEPTIONS;
124     if ([view respondsToSelector:@selector(isEnabled)]) {
125         return [view isEnabled];
126     }
127     KWQ_UNBLOCK_EXCEPTIONS;
128
129     return true;
130 }
131
132 long QWidget::winId() const
133 {
134     return (long)this;
135 }
136
137 int QWidget::x() const
138 {
139     return frameGeometry().topLeft().x();
140 }
141
142 int QWidget::y() const 
143 {
144     return frameGeometry().topLeft().y();
145 }
146
147 int QWidget::width() const 
148
149     return frameGeometry().size().width();
150 }
151
152 int QWidget::height() const 
153 {
154     return frameGeometry().size().height();
155 }
156
157 QSize QWidget::size() const 
158 {
159     return frameGeometry().size();
160 }
161
162 void QWidget::resize(const QSize &s) 
163 {
164     resize(s.width(), s.height());
165 }
166
167 QPoint QWidget::pos() const 
168 {
169     return frameGeometry().topLeft();
170 }
171
172 void QWidget::move(int x, int y) 
173 {
174     setFrameGeometry(QRect(x, y, width(), height()));
175 }
176
177 void QWidget::move(const QPoint &p) 
178 {
179     move(p.x(), p.y());
180 }
181
182 QRect QWidget::frameGeometry() const
183 {
184     QRect rect;
185
186     KWQ_BLOCK_EXCEPTIONS;
187     rect = QRect([getOuterView() frame]);
188     KWQ_UNBLOCK_EXCEPTIONS;
189
190     return rect;
191 }
192
193 int QWidget::baselinePosition(int height) const
194 {
195     return height;
196 }
197
198 bool QWidget::hasFocus() const
199 {
200     NSView *view = [getView() _webcore_effectiveFirstResponder];
201
202     KWQ_BLOCK_EXCEPTIONS;
203     NSView *firstResponder = [KWQKHTMLPart::bridgeForWidget(this) firstResponder];
204
205     if (!firstResponder) {
206         return false;
207     }
208     if (firstResponder == view) {
209         return true;
210     }
211
212     // Some widgets, like text fields, secure text fields, text areas, and selects
213     // (when displayed using a list box) may have a descendent widget that is
214     // first responder. This checksDescendantsForFocus() check, turned on for the 
215     // four widget types listed, enables the additional check which makes this 
216     // function work correctly for the above-mentioned widget types.
217     if (checksDescendantsForFocus() && 
218         [firstResponder isKindOfClass:[NSView class]] && 
219         [(NSView *)firstResponder isDescendantOf:view]) {
220         // Return true when the first responder is a subview of this widget's view
221         return true;
222     }
223     KWQ_UNBLOCK_EXCEPTIONS;
224
225     return false;
226 }
227
228 void QWidget::setFocus()
229 {
230     if (hasFocus()) {
231         return;
232     }
233     
234     NSView *view = [getView() _webcore_effectiveFirstResponder];
235
236     KWQ_BLOCK_EXCEPTIONS;
237     if ([view acceptsFirstResponder]) {
238         [KWQKHTMLPart::bridgeForWidget(this) makeFirstResponder:view];
239     }
240     KWQ_UNBLOCK_EXCEPTIONS;
241 }
242
243 void QWidget::clearFocus()
244 {
245     if (!hasFocus()) {
246         return;
247     }
248     
249     KWQKHTMLPart::clearDocumentFocus(this);
250 }
251
252 bool QWidget::checksDescendantsForFocus() const
253 {
254     return false;
255 }
256
257 QWidget::FocusPolicy QWidget::focusPolicy() const
258 {
259     // This provides support for controlling the widgets that take 
260     // part in tab navigation. Widgets must:
261     // 1. not be hidden by css
262     // 2. be enabled
263     // 3. accept first responder
264
265     RenderWidget *widget = const_cast<RenderWidget *>
266         (static_cast<const RenderWidget *>(eventFilterObject()));
267     if (widget->style()->visibility() != khtml::VISIBLE)
268         return NoFocus;
269
270     if (!isEnabled())
271         return NoFocus;
272     
273     KWQ_BLOCK_EXCEPTIONS;
274     if (![getView() acceptsFirstResponder])
275         return NoFocus;
276     KWQ_UNBLOCK_EXCEPTIONS;
277     
278     return TabFocus;
279 }
280
281 const QPalette& QWidget::palette() const
282 {
283     return data->pal;
284 }
285
286 void QWidget::setPalette(const QPalette &palette)
287 {
288     data->pal = palette;
289 }
290
291 QStyle &QWidget::style() const
292 {
293     return *data->style;
294 }
295
296 void QWidget::setStyle(QStyle *style)
297 {
298     // According to the Qt implementation 
299     /*
300     Sets the widget's GUI style to \a style. Ownership of the style
301     object is not transferred.
302     */
303     data->style = style;
304 }
305
306 QFont QWidget::font() const
307 {
308     return data->font;
309 }
310
311 void QWidget::setFont(const QFont &font)
312 {
313     data->font = font;
314 }
315
316 void QWidget::constPolish() const
317 {
318 }
319
320 bool QWidget::isVisible() const
321 {
322     // FIXME - rewrite interms of top level widget?
323     
324     KWQ_BLOCK_EXCEPTIONS;
325     return [[KWQKHTMLPart::bridgeForWidget(this) window] isVisible];
326     KWQ_UNBLOCK_EXCEPTIONS;
327
328     return false;
329 }
330
331 void QWidget::setCursor(const QCursor &cur)
332 {
333     KWQ_BLOCK_EXCEPTIONS;
334     id view = data->view;
335     while (view) {
336         if ([view respondsToSelector:@selector(setDocumentCursor:)]) {
337             [view setDocumentCursor:cur.handle()];
338             break;
339         }
340         view = [view superview];
341     }
342     KWQ_UNBLOCK_EXCEPTIONS;
343 }
344
345 QCursor QWidget::cursor()
346 {
347     KWQ_BLOCK_EXCEPTIONS;
348     QCursor cursor;
349
350     id view = data->view;
351     while (view) {
352         if ([view respondsToSelector:@selector(documentCursor)]) { 
353             cursor = QCursor([view documentCursor]);
354             break;
355         }
356         view = [view superview];
357     }
358
359     return cursor;
360     KWQ_UNBLOCK_EXCEPTIONS;
361
362     return QCursor();
363 }
364
365 void QWidget::unsetCursor()
366 {
367     setCursor(QCursor());
368 }
369
370 bool QWidget::event(QEvent *)
371 {
372     return false;
373 }
374
375 void QWidget::show()
376 {
377     if (!data || data->visible)
378         return;
379
380     data->visible = true;
381
382     KWQ_BLOCK_EXCEPTIONS;
383     [getOuterView() setHidden: NO];
384     KWQ_UNBLOCK_EXCEPTIONS;
385 }
386
387 void QWidget::hide()
388 {
389     if (!data || !data->visible)
390         return;
391
392     data->visible = false;
393
394     KWQ_BLOCK_EXCEPTIONS;
395     [getOuterView() setHidden: YES];
396     KWQ_UNBLOCK_EXCEPTIONS;
397 }
398
399 void QWidget::setFrameGeometry(const QRect &rect)
400 {
401     KWQ_BLOCK_EXCEPTIONS;
402     NSView *v = getOuterView();
403     NSRect f = rect;
404     if (!NSEqualRects(f, [v frame])) {
405         [v setFrame:f];
406         [v setNeedsDisplay: NO];
407     }
408     KWQ_UNBLOCK_EXCEPTIONS;
409 }
410
411 QPoint QWidget::mapFromGlobal(const QPoint &p) const
412 {
413     NSPoint bp = {0,0};
414
415     KWQ_BLOCK_EXCEPTIONS;
416     bp = [[KWQKHTMLPart::bridgeForWidget(this) window] convertScreenToBase:[data->view convertPoint:p toView:nil]];
417     KWQ_UNBLOCK_EXCEPTIONS;
418
419     return QPoint(bp);
420 }
421
422 NSView *QWidget::getView() const
423 {
424     return data->view;
425 }
426
427 void QWidget::setView(NSView *view)
428 {
429     KWQ_BLOCK_EXCEPTIONS;
430     KWQRetain(view);
431     KWQRelease(data->view);
432     data->view = view;
433     KWQ_UNBLOCK_EXCEPTIONS;
434 }
435
436 NSView *QWidget::getOuterView() const
437 {
438     // A QScrollView is a widget normally used to represent a frame.
439     // If this widget's view is a WebCoreFrameView the we resize its containing view, a WebFrameView.
440     // The scroll view contained by the WebFrameView will be autosized.
441
442     NSView *view = data->view;
443     ASSERT(view);
444
445     if ([view conformsToProtocol:@protocol(WebCoreFrameView)]) {
446         view = [view superview];
447         ASSERT(view);
448     }
449
450     return view;
451 }
452
453 void QWidget::lockDrawingFocus()
454 {
455     KWQ_BLOCK_EXCEPTIONS;
456     [getView() lockFocus];
457     KWQ_UNBLOCK_EXCEPTIONS;
458 }
459
460 void QWidget::unlockDrawingFocus()
461 {
462     KWQ_BLOCK_EXCEPTIONS;
463     [getView() unlockFocus];
464     KWQ_UNBLOCK_EXCEPTIONS;
465 }
466
467 void QWidget::disableFlushDrawing()
468 {
469     // It's OK to use the real window here, because if the view's not
470     // in the view hierarchy, then we don't actually want to affect
471     // flushing.
472     KWQ_BLOCK_EXCEPTIONS;
473     [[getView() window] disableFlushWindow];
474     KWQ_UNBLOCK_EXCEPTIONS;
475 }
476
477 void QWidget::enableFlushDrawing()
478 {
479     // It's OK to use the real window here, because if the view's not
480     // in the view hierarchy, then we don't actually want to affect
481     // flushing.
482     KWQ_BLOCK_EXCEPTIONS;
483     NSWindow *window = [getView() window];
484     [window enableFlushWindow];
485     [window flushWindow];
486     KWQ_UNBLOCK_EXCEPTIONS;
487 }
488
489 void QWidget::setDrawingAlpha(float alpha)
490 {
491     KWQ_BLOCK_EXCEPTIONS;
492     CGContextSetAlpha((CGContextRef)[[NSGraphicsContext currentContext] graphicsPort], alpha);
493     KWQ_UNBLOCK_EXCEPTIONS;
494 }
495
496 void QWidget::paint(QPainter *p, const QRect &r)
497 {
498     if (p->paintingDisabled()) {
499         return;
500     }
501     NSView *view = getOuterView();
502     // KWQTextArea and KWQTextField both rely on the fact that we use this particular
503     // NSView display method. If you change this, be sure to update them as well.
504     KWQ_BLOCK_EXCEPTIONS;
505     [view displayRectIgnoringOpacity:[view convertRect:r fromView:[view superview]]];
506     KWQ_UNBLOCK_EXCEPTIONS;
507 }
508
509 void QWidget::sendConsumedMouseUp()
510 {
511     khtml::RenderWidget *widget = const_cast<khtml::RenderWidget *>
512         (static_cast<const khtml::RenderWidget *>(eventFilterObject()));
513
514     KWQ_BLOCK_EXCEPTIONS;
515     widget->sendConsumedMouseUp(QPoint([[NSApp currentEvent] locationInWindow]),
516                               // FIXME: should send real state and button
517                               0, 0);
518     KWQ_UNBLOCK_EXCEPTIONS;
519 }
520
521 void QWidget::setIsSelected(bool isSelected)
522 {
523     [KWQKHTMLPart::bridgeForWidget(this) setIsSelected:isSelected forView:getView()];
524 }
525
526 void QWidget::addToSuperview(NSView *superview)
527 {
528     KWQ_BLOCK_EXCEPTIONS;
529
530     ASSERT(superview);
531     NSView *subview = getOuterView();
532     ASSERT(![superview isDescendantOf:subview]);
533     if ([subview superview] != superview)
534         [superview addSubview:subview];
535     data->removeFromSuperviewSoon = false;
536
537     KWQ_UNBLOCK_EXCEPTIONS;
538 }
539
540 void QWidget::removeFromSuperview()
541 {
542     if (data->mustStayInWindow)
543         data->removeFromSuperviewSoon = true;
544     else {
545         KWQ_BLOCK_EXCEPTIONS;
546         [getOuterView() removeFromSuperview];
547         KWQ_UNBLOCK_EXCEPTIONS;
548         data->removeFromSuperviewSoon = false;
549     }
550 }
551
552 void QWidget::beforeMouseDown(NSView *view)
553 {
554     ASSERT([view conformsToProtocol:@protocol(KWQWidgetHolder)]);
555     QWidget *widget = [(NSView <KWQWidgetHolder> *)view widget];
556     if (widget) {
557         ASSERT(view == widget->getOuterView());
558         ASSERT(!widget->data->mustStayInWindow);
559         widget->data->mustStayInWindow = true;
560     }
561 }
562
563 void QWidget::afterMouseDown(NSView *view)
564 {
565     ASSERT([view conformsToProtocol:@protocol(KWQWidgetHolder)]);
566     QWidget *widget = [(NSView <KWQWidgetHolder> *)view widget];
567     if (!widget) {
568         KWQ_BLOCK_EXCEPTIONS;
569         [view removeFromSuperview];
570         KWQ_UNBLOCK_EXCEPTIONS;
571     } else {
572         ASSERT(widget->data->mustStayInWindow);
573         widget->data->mustStayInWindow = false;
574         if (widget->data->removeFromSuperviewSoon)
575             widget->removeFromSuperview();
576     }
577 }