1 /*
2 * Copyright (C) 2007 Kevin Ollivier <kevino@theolliviers.com>
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 #include "config.h"
27 #include "ScrollView.h"
28
29 #include "FloatRect.h"
30 #include "IntRect.h"
31 #include "NotImplemented.h"
32 #include "PlatformWheelEvent.h"
33 #include "Scrollbar.h"
34
35 #include <algorithm>
36 #include <stdio.h>
37
38 #include <wx/defs.h>
39 #include <wx/scrolbar.h>
40 #include <wx/scrolwin.h>
41 #include <wx/event.h>
42
43 using namespace std;
44
45 namespace WebCore {
46
47 class ScrollView::ScrollViewPrivate : public wxEvtHandler {
48
49 public:
ScrollViewPrivate(ScrollView * scrollView)50 ScrollViewPrivate(ScrollView* scrollView)
51 : wxEvtHandler()
52 , m_scrollView(scrollView)
53 , vScrollbarMode(ScrollbarAuto)
54 , hScrollbarMode(ScrollbarAuto)
55 , viewStart(0, 0)
56 {
57 }
58
bindEvents(wxWindow * win)59 void bindEvents(wxWindow* win)
60 {
61 // TODO: is there an easier way to Connect to a range of events? these are contiguous.
62 win->Connect(wxEVT_SCROLLWIN_TOP, wxScrollWinEventHandler(ScrollViewPrivate::OnScrollWinEvents), NULL, this);
63 win->Connect(wxEVT_SCROLLWIN_BOTTOM, wxScrollWinEventHandler(ScrollViewPrivate::OnScrollWinEvents), NULL, this);
64 win->Connect(wxEVT_SCROLLWIN_LINEUP, wxScrollWinEventHandler(ScrollViewPrivate::OnScrollWinEvents), NULL, this);
65 win->Connect(wxEVT_SCROLLWIN_LINEDOWN, wxScrollWinEventHandler(ScrollViewPrivate::OnScrollWinEvents), NULL, this);
66 win->Connect(wxEVT_SCROLLWIN_PAGEUP, wxScrollWinEventHandler(ScrollViewPrivate::OnScrollWinEvents), NULL, this);
67 win->Connect(wxEVT_SCROLLWIN_PAGEDOWN, wxScrollWinEventHandler(ScrollViewPrivate::OnScrollWinEvents), NULL, this);
68 win->Connect(wxEVT_SCROLLWIN_THUMBTRACK, wxScrollWinEventHandler(ScrollViewPrivate::OnScrollWinEvents), NULL, this);
69 win->Connect(wxEVT_SCROLLWIN_THUMBRELEASE, wxScrollWinEventHandler(ScrollViewPrivate::OnScrollWinEvents), NULL, this);
70 }
71
OnScrollWinEvents(wxScrollWinEvent & e)72 void OnScrollWinEvents(wxScrollWinEvent& e)
73 {
74 wxEventType scrollType(e.GetEventType());
75 bool horiz = e.GetOrientation() == wxHORIZONTAL;
76
77 wxPoint pos(viewStart);
78
79 if (scrollType == wxEVT_SCROLLWIN_THUMBTRACK || scrollType == wxEVT_SCROLLWIN_THUMBRELEASE) {
80 if (horiz)
81 pos.x = e.GetPosition();
82 else
83 pos.y = e.GetPosition();
84 }
85 else if (scrollType == wxEVT_SCROLLWIN_LINEDOWN) {
86 if (horiz)
87 pos.x += Scrollbar::pixelsPerLineStep();
88 else
89 pos.y += Scrollbar::pixelsPerLineStep();
90 }
91 else if (scrollType == wxEVT_SCROLLWIN_LINEUP) {
92 if (horiz)
93 pos.x -= Scrollbar::pixelsPerLineStep();
94 else
95 pos.y -= Scrollbar::pixelsPerLineStep();
96 }
97 else if (scrollType == wxEVT_SCROLLWIN_PAGEUP) {
98 if (horiz)
99 pos.x -= max<int>(m_scrollView->visibleWidth() * Scrollbar::minFractionToStepWhenPaging(), m_scrollView->visibleWidth() - Scrollbar::maxOverlapBetweenPages());
100 else
101 pos.y -= max<int>(m_scrollView->visibleHeight() * Scrollbar::minFractionToStepWhenPaging(), m_scrollView->visibleHeight() - Scrollbar::maxOverlapBetweenPages());
102 }
103 else if (scrollType == wxEVT_SCROLLWIN_PAGEDOWN) {
104 if (horiz)
105 pos.x += max<int>(m_scrollView->visibleWidth() * Scrollbar::minFractionToStepWhenPaging(), m_scrollView->visibleWidth() - Scrollbar::maxOverlapBetweenPages());
106 else
107 pos.y += max<int>(m_scrollView->visibleHeight() * Scrollbar::minFractionToStepWhenPaging(), m_scrollView->visibleHeight() - Scrollbar::maxOverlapBetweenPages());
108 }
109 else
110 return e.Skip();
111
112 m_scrollView->setScrollPosition(IntPoint(pos.x, pos.y));
113 }
114
115 ScrollView* m_scrollView;
116
117 ScrollbarMode vScrollbarMode;
118 ScrollbarMode hScrollbarMode;
119 wxPoint viewStart;
120 };
121
platformInit()122 void ScrollView::platformInit()
123 {
124 m_data = new ScrollViewPrivate(this);
125 }
126
127
platformDestroy()128 void ScrollView::platformDestroy()
129 {
130 delete m_data;
131 }
132
setPlatformWidget(wxWindow * win)133 void ScrollView::setPlatformWidget(wxWindow* win)
134 {
135 Widget::setPlatformWidget(win);
136 m_data->bindEvents(win);
137 }
138
platformRepaintContentRectangle(const IntRect & updateRect,bool now)139 void ScrollView::platformRepaintContentRectangle(const IntRect& updateRect, bool now)
140 {
141 // we need to convert coordinates to scrolled position
142 wxRect contentsRect = updateRect;
143 contentsRect.Offset(-scrollX(), -scrollY());
144 wxWindow* win = platformWidget();
145 if (win) {
146 win->RefreshRect(contentsRect, true);
147 if (now)
148 win->Update();
149 }
150 }
151
platformVisibleContentRect(bool includeScrollbars) const152 IntRect ScrollView::platformVisibleContentRect(bool includeScrollbars) const
153 {
154 wxWindow* win = platformWidget();
155 if (!win)
156 return IntRect();
157
158 int width, height;
159
160 if (includeScrollbars)
161 win->GetSize(&width, &height);
162 else
163 win->GetClientSize(&width, &height);
164
165 return IntRect(m_data->viewStart.x, m_data->viewStart.y, width, height);
166 }
167
platformContentsSize() const168 IntSize ScrollView::platformContentsSize() const
169 {
170 int width = 0;
171 int height = 0;
172 if (platformWidget()) {
173 platformWidget()->GetVirtualSize(&width, &height);
174 ASSERT(width >= 0 && height >= 0);
175 }
176 return IntSize(width, height);
177 }
178
platformSetScrollPosition(const IntPoint & scrollPoint)179 void ScrollView::platformSetScrollPosition(const IntPoint& scrollPoint)
180 {
181 wxWindow* win = platformWidget();
182
183 wxPoint scrollOffset = m_data->viewStart;
184 wxPoint orig(scrollOffset);
185 wxPoint newScrollOffset(scrollPoint);
186
187 wxRect vRect(win->GetVirtualSize());
188 wxRect cRect(win->GetClientSize());
189
190 // clamp to scroll area
191 if (newScrollOffset.x < 0)
192 newScrollOffset.x = 0;
193 else if (newScrollOffset.x + cRect.width > vRect.width)
194 newScrollOffset.x = max(0, vRect.width - cRect.width);
195
196 if (newScrollOffset.y < 0)
197 newScrollOffset.y = 0;
198 else if (newScrollOffset.y + cRect.height > vRect.height)
199 newScrollOffset.y = max(0, vRect.height - cRect.height);
200
201 if (newScrollOffset == scrollOffset)
202 return;
203
204 m_data->viewStart = newScrollOffset;
205
206 wxPoint delta(orig - newScrollOffset);
207
208 if (canBlitOnScroll())
209 win->ScrollWindow(delta.x, delta.y);
210 else
211 win->Refresh();
212
213 adjustScrollbars();
214 }
215
platformScroll(ScrollDirection,ScrollGranularity)216 bool ScrollView::platformScroll(ScrollDirection, ScrollGranularity)
217 {
218 notImplemented();
219 return true;
220 }
221
platformSetContentsSize()222 void ScrollView::platformSetContentsSize()
223 {
224 wxWindow* win = platformWidget();
225 if (!win)
226 return;
227
228 win->SetVirtualSize(m_contentsSize.width(), m_contentsSize.height());
229 adjustScrollbars();
230 }
231
adjustScrollbars(int x,int y,bool refresh)232 void ScrollView::adjustScrollbars(int x, int y, bool refresh)
233 {
234 wxWindow* win = platformWidget();
235 if (!win)
236 return;
237
238 wxRect crect(win->GetClientRect()), vrect(win->GetVirtualSize());
239
240 if (x == -1) x = m_data->viewStart.x;
241 if (y == -1) y = m_data->viewStart.y;
242
243 long style = win->GetWindowStyle();
244
245 // by setting the wxALWAYS_SHOW_SB wxWindow flag before
246 // each SetScrollbar call, we can control the scrollbars
247 // visibility individually.
248
249 // horizontal scrollbar
250 switch (m_data->hScrollbarMode) {
251 case ScrollbarAlwaysOff:
252 win->SetWindowStyleFlag(style & ~wxALWAYS_SHOW_SB);
253 win->SetScrollbar(wxHORIZONTAL, 0, 0, 0, refresh);
254 break;
255
256 case ScrollbarAuto:
257 win->SetWindowStyleFlag(style & ~wxALWAYS_SHOW_SB);
258 win->SetScrollbar(wxHORIZONTAL, x, crect.width, vrect.width, refresh);
259 break;
260
261 default: // ScrollbarAlwaysOn
262 win->SetWindowStyleFlag(style | wxALWAYS_SHOW_SB);
263 win->SetScrollbar(wxHORIZONTAL, x, crect.width, vrect.width, refresh);
264 break;
265 }
266
267 // vertical scrollbar
268 switch (m_data->vScrollbarMode) {
269 case ScrollbarAlwaysOff:
270 win->SetWindowStyleFlag(style & ~wxALWAYS_SHOW_SB);
271 win->SetScrollbar(wxVERTICAL, 0, 0, 0, refresh);
272 break;
273
274 case ScrollbarAlwaysOn:
275 win->SetWindowStyleFlag(style | wxALWAYS_SHOW_SB);
276 win->SetScrollbar(wxVERTICAL, y, crect.height, vrect.height, refresh);
277 break;
278
279 default: // case ScrollbarAuto:
280 win->SetWindowStyleFlag(style & ~wxALWAYS_SHOW_SB);
281 win->SetScrollbar(wxVERTICAL, y, crect.height, vrect.height, refresh);
282 }
283 }
284
platformSetScrollbarModes()285 void ScrollView::platformSetScrollbarModes()
286 {
287 bool needsAdjust = false;
288
289 if (m_data->hScrollbarMode != horizontalScrollbarMode() ) {
290 m_data->hScrollbarMode = horizontalScrollbarMode();
291 needsAdjust = true;
292 }
293
294 if (m_data->vScrollbarMode != verticalScrollbarMode() ) {
295 m_data->vScrollbarMode = verticalScrollbarMode();
296 needsAdjust = true;
297 }
298
299 if (needsAdjust)
300 adjustScrollbars();
301 }
302
platformScrollbarModes(ScrollbarMode & horizontal,ScrollbarMode & vertical) const303 void ScrollView::platformScrollbarModes(ScrollbarMode& horizontal, ScrollbarMode& vertical) const
304 {
305 horizontal = m_data->hScrollbarMode;
306 vertical = m_data->vScrollbarMode;
307 }
308
platformSetCanBlitOnScroll(bool canBlitOnScroll)309 void ScrollView::platformSetCanBlitOnScroll(bool canBlitOnScroll)
310 {
311 m_canBlitOnScroll = canBlitOnScroll;
312 }
313
platformCanBlitOnScroll() const314 bool ScrollView::platformCanBlitOnScroll() const
315 {
316 return m_canBlitOnScroll;
317 }
318
319 // used for subframes support
platformAddChild(Widget * widget)320 void ScrollView::platformAddChild(Widget* widget)
321 {
322 // NB: In all cases I'm aware of,
323 // by the time this is called the ScrollView is already a child
324 // of its parent Widget by wx port APIs, so I don't think
325 // we need to do anything here.
326 }
327
platformRemoveChild(Widget * widget)328 void ScrollView::platformRemoveChild(Widget* widget)
329 {
330 if (platformWidget()) {
331 platformWidget()->RemoveChild(widget->platformWidget());
332 // FIXME: Is this the right place to do deletion? I see
333 // detachFromParent2/3/4, initiated by FrameLoader::detachFromParent,
334 // but I'm not sure if it's better to handle there or not.
335 widget->platformWidget()->Destroy();
336 }
337 }
338
platformContentsToScreen(const IntRect & rect) const339 IntRect ScrollView::platformContentsToScreen(const IntRect& rect) const
340 {
341 if (platformWidget()) {
342 wxRect wxrect = rect;
343 platformWidget()->ClientToScreen(&wxrect.x, &wxrect.y);
344 return wxrect;
345 }
346 return IntRect();
347 }
348
platformScreenToContents(const IntPoint & point) const349 IntPoint ScrollView::platformScreenToContents(const IntPoint& point) const
350 {
351 if (platformWidget()) {
352 return platformWidget()->ScreenToClient(point);
353 }
354 return IntPoint();
355 }
356
platformIsOffscreen() const357 bool ScrollView::platformIsOffscreen() const
358 {
359 return !platformWidget() || !platformWidget()->IsShownOnScreen();
360 }
361
362 }
363