|
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
|
2 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
3 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
5 |
|
6 // |
|
7 // Eric Vaughan |
|
8 // Netscape Communications |
|
9 // |
|
10 // See documentation in associated header file |
|
11 // |
|
12 |
|
13 #include "nsScrollbarButtonFrame.h" |
|
14 #include "nsPresContext.h" |
|
15 #include "nsIContent.h" |
|
16 #include "nsCOMPtr.h" |
|
17 #include "nsNameSpaceManager.h" |
|
18 #include "nsGkAtoms.h" |
|
19 #include "nsSliderFrame.h" |
|
20 #include "nsScrollbarFrame.h" |
|
21 #include "nsIScrollbarMediator.h" |
|
22 #include "nsRepeatService.h" |
|
23 #include "mozilla/LookAndFeel.h" |
|
24 #include "mozilla/MouseEvents.h" |
|
25 |
|
26 using namespace mozilla; |
|
27 |
|
28 // |
|
29 // NS_NewToolbarFrame |
|
30 // |
|
31 // Creates a new Toolbar frame and returns it |
|
32 // |
|
33 nsIFrame* |
|
34 NS_NewScrollbarButtonFrame (nsIPresShell* aPresShell, nsStyleContext* aContext) |
|
35 { |
|
36 return new (aPresShell) nsScrollbarButtonFrame(aPresShell, aContext); |
|
37 } |
|
38 |
|
39 NS_IMPL_FRAMEARENA_HELPERS(nsScrollbarButtonFrame) |
|
40 |
|
41 nsresult |
|
42 nsScrollbarButtonFrame::HandleEvent(nsPresContext* aPresContext, |
|
43 WidgetGUIEvent* aEvent, |
|
44 nsEventStatus* aEventStatus) |
|
45 { |
|
46 NS_ENSURE_ARG_POINTER(aEventStatus); |
|
47 |
|
48 // If a web page calls event.preventDefault() we still want to |
|
49 // scroll when scroll arrow is clicked. See bug 511075. |
|
50 if (!mContent->IsInNativeAnonymousSubtree() && |
|
51 nsEventStatus_eConsumeNoDefault == *aEventStatus) { |
|
52 return NS_OK; |
|
53 } |
|
54 |
|
55 switch (aEvent->message) { |
|
56 case NS_MOUSE_BUTTON_DOWN: |
|
57 mCursorOnThis = true; |
|
58 // if we didn't handle the press ourselves, pass it on to the superclass |
|
59 if (HandleButtonPress(aPresContext, aEvent, aEventStatus)) { |
|
60 return NS_OK; |
|
61 } |
|
62 break; |
|
63 case NS_MOUSE_BUTTON_UP: |
|
64 HandleRelease(aPresContext, aEvent, aEventStatus); |
|
65 break; |
|
66 case NS_MOUSE_EXIT_SYNTH: |
|
67 mCursorOnThis = false; |
|
68 break; |
|
69 case NS_MOUSE_MOVE: { |
|
70 nsPoint cursor = |
|
71 nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, this); |
|
72 nsRect frameRect(nsPoint(0, 0), GetSize()); |
|
73 mCursorOnThis = frameRect.Contains(cursor); |
|
74 break; |
|
75 } |
|
76 } |
|
77 |
|
78 return nsButtonBoxFrame::HandleEvent(aPresContext, aEvent, aEventStatus); |
|
79 } |
|
80 |
|
81 |
|
82 bool |
|
83 nsScrollbarButtonFrame::HandleButtonPress(nsPresContext* aPresContext, |
|
84 WidgetGUIEvent* aEvent, |
|
85 nsEventStatus* aEventStatus) |
|
86 { |
|
87 // Get the desired action for the scrollbar button. |
|
88 LookAndFeel::IntID tmpAction; |
|
89 uint16_t button = aEvent->AsMouseEvent()->button; |
|
90 if (button == WidgetMouseEvent::eLeftButton) { |
|
91 tmpAction = LookAndFeel::eIntID_ScrollButtonLeftMouseButtonAction; |
|
92 } else if (button == WidgetMouseEvent::eMiddleButton) { |
|
93 tmpAction = LookAndFeel::eIntID_ScrollButtonMiddleMouseButtonAction; |
|
94 } else if (button == WidgetMouseEvent::eRightButton) { |
|
95 tmpAction = LookAndFeel::eIntID_ScrollButtonRightMouseButtonAction; |
|
96 } else { |
|
97 return false; |
|
98 } |
|
99 |
|
100 // Get the button action metric from the pres. shell. |
|
101 int32_t pressedButtonAction; |
|
102 if (NS_FAILED(LookAndFeel::GetInt(tmpAction, &pressedButtonAction))) { |
|
103 return false; |
|
104 } |
|
105 |
|
106 // get the scrollbar control |
|
107 nsIFrame* scrollbar; |
|
108 GetParentWithTag(nsGkAtoms::scrollbar, this, scrollbar); |
|
109 |
|
110 if (scrollbar == nullptr) |
|
111 return false; |
|
112 |
|
113 // get the scrollbars content node |
|
114 nsIContent* content = scrollbar->GetContent(); |
|
115 |
|
116 static nsIContent::AttrValuesArray strings[] = { &nsGkAtoms::increment, |
|
117 &nsGkAtoms::decrement, |
|
118 nullptr }; |
|
119 int32_t index = mContent->FindAttrValueIn(kNameSpaceID_None, |
|
120 nsGkAtoms::type, |
|
121 strings, eCaseMatters); |
|
122 int32_t direction; |
|
123 if (index == 0) |
|
124 direction = 1; |
|
125 else if (index == 1) |
|
126 direction = -1; |
|
127 else |
|
128 return false; |
|
129 |
|
130 // Whether or not to repeat the click action. |
|
131 bool repeat = true; |
|
132 // Use smooth scrolling by default. |
|
133 bool smoothScroll = true; |
|
134 switch (pressedButtonAction) { |
|
135 case 0: |
|
136 mIncrement = direction * nsSliderFrame::GetIncrement(content); |
|
137 break; |
|
138 case 1: |
|
139 mIncrement = direction * nsSliderFrame::GetPageIncrement(content); |
|
140 break; |
|
141 case 2: |
|
142 if (direction == -1) |
|
143 mIncrement = -nsSliderFrame::GetCurrentPosition(content); |
|
144 else |
|
145 mIncrement = nsSliderFrame::GetMaxPosition(content) - |
|
146 nsSliderFrame::GetCurrentPosition(content); |
|
147 // Don't repeat or use smooth scrolling if scrolling to beginning or end |
|
148 // of a page. |
|
149 repeat = smoothScroll = false; |
|
150 break; |
|
151 case 3: |
|
152 default: |
|
153 // We were told to ignore this click, or someone assigned a non-standard |
|
154 // value to the button's action. |
|
155 return false; |
|
156 } |
|
157 // set this attribute so we can style it later |
|
158 nsWeakFrame weakFrame(this); |
|
159 mContent->SetAttr(kNameSpaceID_None, nsGkAtoms::active, NS_LITERAL_STRING("true"), true); |
|
160 |
|
161 nsIPresShell::SetCapturingContent(mContent, CAPTURE_IGNOREALLOWED); |
|
162 |
|
163 if (weakFrame.IsAlive()) { |
|
164 DoButtonAction(smoothScroll); |
|
165 } |
|
166 if (repeat) |
|
167 StartRepeat(); |
|
168 return true; |
|
169 } |
|
170 |
|
171 NS_IMETHODIMP |
|
172 nsScrollbarButtonFrame::HandleRelease(nsPresContext* aPresContext, |
|
173 WidgetGUIEvent* aEvent, |
|
174 nsEventStatus* aEventStatus) |
|
175 { |
|
176 nsIPresShell::SetCapturingContent(nullptr, 0); |
|
177 // we're not active anymore |
|
178 mContent->UnsetAttr(kNameSpaceID_None, nsGkAtoms::active, true); |
|
179 StopRepeat(); |
|
180 return NS_OK; |
|
181 } |
|
182 |
|
183 void nsScrollbarButtonFrame::Notify() |
|
184 { |
|
185 // Since this is only going to get called if we're scrolling a page length |
|
186 // or a line increment, we will always use smooth scrolling. |
|
187 if (mCursorOnThis || |
|
188 LookAndFeel::GetInt( |
|
189 LookAndFeel::eIntID_ScrollbarButtonAutoRepeatBehavior, 0)) { |
|
190 DoButtonAction(true); |
|
191 } |
|
192 } |
|
193 |
|
194 void |
|
195 nsScrollbarButtonFrame::MouseClicked(nsPresContext* aPresContext, |
|
196 WidgetGUIEvent* aEvent) |
|
197 { |
|
198 nsButtonBoxFrame::MouseClicked(aPresContext, aEvent); |
|
199 //MouseClicked(); |
|
200 } |
|
201 |
|
202 void |
|
203 nsScrollbarButtonFrame::DoButtonAction(bool aSmoothScroll) |
|
204 { |
|
205 // get the scrollbar control |
|
206 nsIFrame* scrollbar; |
|
207 GetParentWithTag(nsGkAtoms::scrollbar, this, scrollbar); |
|
208 |
|
209 if (scrollbar == nullptr) |
|
210 return; |
|
211 |
|
212 // get the scrollbars content node |
|
213 nsCOMPtr<nsIContent> content = scrollbar->GetContent(); |
|
214 |
|
215 // get the current pos |
|
216 int32_t curpos = nsSliderFrame::GetCurrentPosition(content); |
|
217 int32_t oldpos = curpos; |
|
218 |
|
219 // get the max pos |
|
220 int32_t maxpos = nsSliderFrame::GetMaxPosition(content); |
|
221 |
|
222 // increment the given amount |
|
223 if (mIncrement) |
|
224 curpos += mIncrement; |
|
225 |
|
226 // make sure the current position is between the current and max positions |
|
227 if (curpos < 0) |
|
228 curpos = 0; |
|
229 else if (curpos > maxpos) |
|
230 curpos = maxpos; |
|
231 |
|
232 nsScrollbarFrame* sb = do_QueryFrame(scrollbar); |
|
233 if (sb) { |
|
234 nsIScrollbarMediator* m = sb->GetScrollbarMediator(); |
|
235 if (m) { |
|
236 m->ScrollbarButtonPressed(sb, oldpos, curpos); |
|
237 return; |
|
238 } |
|
239 } |
|
240 |
|
241 // set the current position of the slider. |
|
242 nsAutoString curposStr; |
|
243 curposStr.AppendInt(curpos); |
|
244 |
|
245 if (aSmoothScroll) |
|
246 content->SetAttr(kNameSpaceID_None, nsGkAtoms::smooth, NS_LITERAL_STRING("true"), false); |
|
247 content->SetAttr(kNameSpaceID_None, nsGkAtoms::curpos, curposStr, true); |
|
248 if (aSmoothScroll) |
|
249 content->UnsetAttr(kNameSpaceID_None, nsGkAtoms::smooth, false); |
|
250 } |
|
251 |
|
252 nsresult |
|
253 nsScrollbarButtonFrame::GetChildWithTag(nsPresContext* aPresContext, |
|
254 nsIAtom* atom, nsIFrame* start, |
|
255 nsIFrame*& result) |
|
256 { |
|
257 // recursively search our children |
|
258 nsIFrame* childFrame = start->GetFirstPrincipalChild(); |
|
259 while (nullptr != childFrame) |
|
260 { |
|
261 // get the content node |
|
262 nsIContent* child = childFrame->GetContent(); |
|
263 |
|
264 if (child) { |
|
265 // see if it is the child |
|
266 if (child->Tag() == atom) |
|
267 { |
|
268 result = childFrame; |
|
269 |
|
270 return NS_OK; |
|
271 } |
|
272 } |
|
273 |
|
274 // recursive search the child |
|
275 GetChildWithTag(aPresContext, atom, childFrame, result); |
|
276 if (result != nullptr) |
|
277 return NS_OK; |
|
278 |
|
279 childFrame = childFrame->GetNextSibling(); |
|
280 } |
|
281 |
|
282 result = nullptr; |
|
283 return NS_OK; |
|
284 } |
|
285 |
|
286 nsresult |
|
287 nsScrollbarButtonFrame::GetParentWithTag(nsIAtom* toFind, nsIFrame* start, |
|
288 nsIFrame*& result) |
|
289 { |
|
290 while (start) |
|
291 { |
|
292 start = start->GetParent(); |
|
293 |
|
294 if (start) { |
|
295 // get the content node |
|
296 nsIContent* child = start->GetContent(); |
|
297 |
|
298 if (child && child->Tag() == toFind) { |
|
299 result = start; |
|
300 return NS_OK; |
|
301 } |
|
302 } |
|
303 } |
|
304 |
|
305 result = nullptr; |
|
306 return NS_OK; |
|
307 } |
|
308 |
|
309 void |
|
310 nsScrollbarButtonFrame::DestroyFrom(nsIFrame* aDestructRoot) |
|
311 { |
|
312 // Ensure our repeat service isn't going... it's possible that a scrollbar can disappear out |
|
313 // from under you while you're in the process of scrolling. |
|
314 StopRepeat(); |
|
315 nsButtonBoxFrame::DestroyFrom(aDestructRoot); |
|
316 } |