1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* ***** BEGIN LICENSE BLOCK *****
3 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 : *
5 : * The contents of this file are subject to the Mozilla Public License Version
6 : * 1.1 (the "License"); you may not use this file except in compliance with
7 : * the License. You may obtain a copy of the License at
8 : * http://www.mozilla.org/MPL/
9 : *
10 : * Software distributed under the License is distributed on an "AS IS" basis,
11 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 : * for the specific language governing rights and limitations under the
13 : * License.
14 : *
15 : * The Original Code is Mozilla Communicator client code.
16 : *
17 : * The Initial Developer of the Original Code is
18 : * Netscape Communications Corporation.
19 : * Portions created by the Initial Developer are Copyright (C) 1998
20 : * the Initial Developer. All Rights Reserved.
21 : *
22 : * Contributor(s):
23 : *
24 : * Alternatively, the contents of this file may be used under the terms of
25 : * either of the GNU General Public License Version 2 or later (the "GPL"),
26 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27 : * in which case the provisions of the GPL or the LGPL are applicable instead
28 : * of those above. If you wish to allow use of your version of this file only
29 : * under the terms of either the GPL or the LGPL, and not to allow others to
30 : * use your version of this file under the terms of the MPL, indicate your
31 : * decision by deleting the provisions above and replace them with the notice
32 : * and other provisions required by the GPL or the LGPL. If you do not delete
33 : * the provisions above, a recipient may use your version of this file under
34 : * the terms of any one of the MPL, the GPL or the LGPL.
35 : *
36 : * ***** END LICENSE BLOCK ***** */
37 : #include "nsButtonFrameRenderer.h"
38 : #include "nsCSSRendering.h"
39 : #include "nsPresContext.h"
40 : #include "nsGkAtoms.h"
41 : #include "nsCSSPseudoElements.h"
42 : #include "nsINameSpaceManager.h"
43 : #include "nsStyleSet.h"
44 : #include "nsDisplayList.h"
45 : #include "nsITheme.h"
46 : #include "nsThemeConstants.h"
47 : #include "nsEventStates.h"
48 : #include "mozilla/dom/Element.h"
49 :
50 : #define ACTIVE "active"
51 : #define HOVER "hover"
52 : #define FOCUS "focus"
53 :
54 0 : nsButtonFrameRenderer::nsButtonFrameRenderer()
55 : {
56 0 : MOZ_COUNT_CTOR(nsButtonFrameRenderer);
57 0 : }
58 :
59 0 : nsButtonFrameRenderer::~nsButtonFrameRenderer()
60 : {
61 0 : MOZ_COUNT_DTOR(nsButtonFrameRenderer);
62 0 : }
63 :
64 : void
65 0 : nsButtonFrameRenderer::SetFrame(nsFrame* aFrame, nsPresContext* aPresContext)
66 : {
67 0 : mFrame = aFrame;
68 0 : ReResolveStyles(aPresContext);
69 0 : }
70 :
71 : nsIFrame*
72 0 : nsButtonFrameRenderer::GetFrame()
73 : {
74 0 : return mFrame;
75 : }
76 :
77 : void
78 0 : nsButtonFrameRenderer::SetDisabled(bool aDisabled, bool notify)
79 : {
80 0 : if (aDisabled)
81 0 : mFrame->GetContent()->SetAttr(kNameSpaceID_None, nsGkAtoms::disabled, EmptyString(),
82 0 : notify);
83 : else
84 0 : mFrame->GetContent()->UnsetAttr(kNameSpaceID_None, nsGkAtoms::disabled, notify);
85 0 : }
86 :
87 : bool
88 0 : nsButtonFrameRenderer::isDisabled()
89 : {
90 0 : return mFrame->GetContent()->AsElement()->
91 0 : State().HasState(NS_EVENT_STATE_DISABLED);
92 : }
93 :
94 : class nsDisplayButtonBoxShadowOuter : public nsDisplayItem {
95 : public:
96 0 : nsDisplayButtonBoxShadowOuter(nsDisplayListBuilder* aBuilder,
97 : nsButtonFrameRenderer* aRenderer)
98 0 : : nsDisplayItem(aBuilder, aRenderer->GetFrame()), mBFR(aRenderer) {
99 0 : MOZ_COUNT_CTOR(nsDisplayButtonBoxShadowOuter);
100 0 : }
101 : #ifdef NS_BUILD_REFCNT_LOGGING
102 0 : virtual ~nsDisplayButtonBoxShadowOuter() {
103 0 : MOZ_COUNT_DTOR(nsDisplayButtonBoxShadowOuter);
104 0 : }
105 : #endif
106 :
107 : virtual void Paint(nsDisplayListBuilder* aBuilder,
108 : nsRenderingContext* aCtx);
109 : virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder);
110 0 : NS_DISPLAY_DECL_NAME("ButtonBoxShadowOuter", TYPE_BUTTON_BOX_SHADOW_OUTER)
111 : private:
112 : nsButtonFrameRenderer* mBFR;
113 : };
114 :
115 : nsRect
116 0 : nsDisplayButtonBoxShadowOuter::GetBounds(nsDisplayListBuilder* aBuilder) {
117 0 : return mFrame->GetVisualOverflowRectRelativeToSelf() + ToReferenceFrame();
118 : }
119 :
120 : void
121 0 : nsDisplayButtonBoxShadowOuter::Paint(nsDisplayListBuilder* aBuilder,
122 : nsRenderingContext* aCtx) {
123 0 : nsRect frameRect = nsRect(ToReferenceFrame(), mFrame->GetSize());
124 :
125 0 : nsRect buttonRect;
126 0 : mBFR->GetButtonRect(frameRect, buttonRect);
127 :
128 : nsCSSRendering::PaintBoxShadowOuter(mFrame->PresContext(), *aCtx, mFrame,
129 0 : buttonRect, mVisibleRect);
130 0 : }
131 :
132 : class nsDisplayButtonBorderBackground : public nsDisplayItem {
133 : public:
134 0 : nsDisplayButtonBorderBackground(nsDisplayListBuilder* aBuilder,
135 : nsButtonFrameRenderer* aRenderer)
136 0 : : nsDisplayItem(aBuilder, aRenderer->GetFrame()), mBFR(aRenderer) {
137 0 : MOZ_COUNT_CTOR(nsDisplayButtonBorderBackground);
138 0 : }
139 : #ifdef NS_BUILD_REFCNT_LOGGING
140 0 : virtual ~nsDisplayButtonBorderBackground() {
141 0 : MOZ_COUNT_DTOR(nsDisplayButtonBorderBackground);
142 0 : }
143 : #endif
144 :
145 0 : virtual void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
146 : HitTestState* aState, nsTArray<nsIFrame*> *aOutFrames) {
147 0 : aOutFrames->AppendElement(mFrame);
148 0 : }
149 : virtual void Paint(nsDisplayListBuilder* aBuilder,
150 : nsRenderingContext* aCtx);
151 0 : NS_DISPLAY_DECL_NAME("ButtonBorderBackground", TYPE_BUTTON_BORDER_BACKGROUND)
152 : private:
153 : nsButtonFrameRenderer* mBFR;
154 : };
155 :
156 : class nsDisplayButtonForeground : public nsDisplayItem {
157 : public:
158 0 : nsDisplayButtonForeground(nsDisplayListBuilder* aBuilder,
159 : nsButtonFrameRenderer* aRenderer)
160 0 : : nsDisplayItem(aBuilder, aRenderer->GetFrame()), mBFR(aRenderer) {
161 0 : MOZ_COUNT_CTOR(nsDisplayButtonForeground);
162 0 : }
163 : #ifdef NS_BUILD_REFCNT_LOGGING
164 0 : virtual ~nsDisplayButtonForeground() {
165 0 : MOZ_COUNT_DTOR(nsDisplayButtonForeground);
166 0 : }
167 : #endif
168 :
169 : virtual void Paint(nsDisplayListBuilder* aBuilder,
170 : nsRenderingContext* aCtx);
171 0 : NS_DISPLAY_DECL_NAME("ButtonForeground", TYPE_BUTTON_FOREGROUND)
172 : private:
173 : nsButtonFrameRenderer* mBFR;
174 : };
175 :
176 0 : void nsDisplayButtonBorderBackground::Paint(nsDisplayListBuilder* aBuilder,
177 : nsRenderingContext* aCtx)
178 : {
179 0 : NS_ASSERTION(mFrame, "No frame?");
180 0 : nsPresContext* pc = mFrame->PresContext();
181 0 : nsRect r = nsRect(ToReferenceFrame(), mFrame->GetSize());
182 :
183 : // draw the border and background inside the focus and outline borders
184 : mBFR->PaintBorderAndBackground(pc, *aCtx, mVisibleRect, r,
185 0 : aBuilder->GetBackgroundPaintFlags());
186 0 : }
187 :
188 0 : void nsDisplayButtonForeground::Paint(nsDisplayListBuilder* aBuilder,
189 : nsRenderingContext* aCtx)
190 : {
191 0 : nsPresContext *presContext = mFrame->PresContext();
192 0 : const nsStyleDisplay *disp = mFrame->GetStyleDisplay();
193 0 : if (!mFrame->IsThemed(disp) ||
194 0 : !presContext->GetTheme()->ThemeDrawsFocusForWidget(presContext, mFrame, disp->mAppearance)) {
195 : // draw the focus and outline borders
196 0 : nsRect r = nsRect(ToReferenceFrame(), mFrame->GetSize());
197 0 : mBFR->PaintOutlineAndFocusBorders(presContext, *aCtx, mVisibleRect, r);
198 : }
199 0 : }
200 :
201 : nsresult
202 0 : nsButtonFrameRenderer::DisplayButton(nsDisplayListBuilder* aBuilder,
203 : nsDisplayList* aBackground,
204 : nsDisplayList* aForeground)
205 : {
206 0 : if (mFrame->GetStyleBorder()->mBoxShadow) {
207 : nsresult rv = aBackground->AppendNewToTop(new (aBuilder)
208 0 : nsDisplayButtonBoxShadowOuter(aBuilder, this));
209 0 : NS_ENSURE_SUCCESS(rv, rv);
210 : }
211 :
212 : nsresult rv = aBackground->AppendNewToTop(new (aBuilder)
213 0 : nsDisplayButtonBorderBackground(aBuilder, this));
214 0 : NS_ENSURE_SUCCESS(rv, rv);
215 :
216 : return aForeground->AppendNewToTop(new (aBuilder)
217 0 : nsDisplayButtonForeground(aBuilder, this));
218 : }
219 :
220 : void
221 0 : nsButtonFrameRenderer::PaintOutlineAndFocusBorders(nsPresContext* aPresContext,
222 : nsRenderingContext& aRenderingContext,
223 : const nsRect& aDirtyRect,
224 : const nsRect& aRect)
225 : {
226 : // once we have all that we'll draw the focus if we have it. We will
227 : // need to draw 2 focuses, the inner and the outer. This is so we
228 : // can do any kind of look and feel. Some buttons have focus on the
229 : // outside like mac and motif. While others like windows have it
230 : // inside (dotted line). Usually only one will be specifed. But I
231 : // guess you could have both if you wanted to.
232 :
233 0 : nsRect rect;
234 :
235 0 : if (mOuterFocusStyle) {
236 : // ---------- paint the outer focus border -------------
237 :
238 0 : GetButtonOuterFocusRect(aRect, rect);
239 :
240 : nsCSSRendering::PaintBorder(aPresContext, aRenderingContext, mFrame,
241 0 : aDirtyRect, rect, mOuterFocusStyle);
242 : }
243 :
244 0 : if (mInnerFocusStyle) {
245 : // ---------- paint the inner focus border -------------
246 :
247 0 : GetButtonInnerFocusRect(aRect, rect);
248 :
249 : nsCSSRendering::PaintBorder(aPresContext, aRenderingContext, mFrame,
250 0 : aDirtyRect, rect, mInnerFocusStyle);
251 : }
252 0 : }
253 :
254 :
255 : void
256 0 : nsButtonFrameRenderer::PaintBorderAndBackground(nsPresContext* aPresContext,
257 : nsRenderingContext& aRenderingContext,
258 : const nsRect& aDirtyRect,
259 : const nsRect& aRect,
260 : PRUint32 aBGFlags)
261 :
262 : {
263 : // get the button rect this is inside the focus and outline rects
264 0 : nsRect buttonRect;
265 0 : GetButtonRect(aRect, buttonRect);
266 :
267 0 : nsStyleContext* context = mFrame->GetStyleContext();
268 :
269 : nsCSSRendering::PaintBackground(aPresContext, aRenderingContext, mFrame,
270 0 : aDirtyRect, buttonRect, aBGFlags);
271 : nsCSSRendering::PaintBoxShadowInner(aPresContext, aRenderingContext,
272 0 : mFrame, buttonRect, aDirtyRect);
273 : nsCSSRendering::PaintBorder(aPresContext, aRenderingContext, mFrame,
274 0 : aDirtyRect, buttonRect, context);
275 0 : }
276 :
277 :
278 : void
279 0 : nsButtonFrameRenderer::GetButtonOuterFocusRect(const nsRect& aRect, nsRect& focusRect)
280 : {
281 0 : focusRect = aRect;
282 0 : }
283 :
284 : void
285 0 : nsButtonFrameRenderer::GetButtonRect(const nsRect& aRect, nsRect& r)
286 : {
287 0 : r = aRect;
288 0 : r.Deflate(GetButtonOuterFocusBorderAndPadding());
289 0 : }
290 :
291 :
292 : void
293 0 : nsButtonFrameRenderer::GetButtonInnerFocusRect(const nsRect& aRect, nsRect& focusRect)
294 : {
295 0 : GetButtonRect(aRect, focusRect);
296 0 : focusRect.Deflate(GetButtonBorderAndPadding());
297 0 : focusRect.Deflate(GetButtonInnerFocusMargin());
298 0 : }
299 :
300 :
301 : nsMargin
302 0 : nsButtonFrameRenderer::GetButtonOuterFocusBorderAndPadding()
303 : {
304 0 : nsMargin result(0,0,0,0);
305 :
306 0 : if (mOuterFocusStyle) {
307 0 : if (!mOuterFocusStyle->GetStylePadding()->GetPadding(result)) {
308 0 : NS_NOTYETIMPLEMENTED("percentage padding");
309 : }
310 0 : result += mOuterFocusStyle->GetStyleBorder()->GetActualBorder();
311 : }
312 :
313 : return result;
314 : }
315 :
316 : nsMargin
317 0 : nsButtonFrameRenderer::GetButtonBorderAndPadding()
318 : {
319 0 : return mFrame->GetUsedBorderAndPadding();
320 : }
321 :
322 : /**
323 : * Gets the size of the buttons border this is the union of the normal and disabled borders.
324 : */
325 : nsMargin
326 0 : nsButtonFrameRenderer::GetButtonInnerFocusMargin()
327 : {
328 0 : nsMargin innerFocusMargin(0,0,0,0);
329 :
330 0 : if (mInnerFocusStyle) {
331 0 : const nsStyleMargin* margin = mInnerFocusStyle->GetStyleMargin();
332 0 : if (!margin->GetMargin(innerFocusMargin)) {
333 0 : NS_NOTYETIMPLEMENTED("percentage margin");
334 : }
335 : }
336 :
337 : return innerFocusMargin;
338 : }
339 :
340 : nsMargin
341 0 : nsButtonFrameRenderer::GetButtonInnerFocusBorderAndPadding()
342 : {
343 0 : nsMargin result(0,0,0,0);
344 :
345 0 : if (mInnerFocusStyle) {
346 0 : if (!mInnerFocusStyle->GetStylePadding()->GetPadding(result)) {
347 0 : NS_NOTYETIMPLEMENTED("percentage padding");
348 : }
349 0 : result += mInnerFocusStyle->GetStyleBorder()->GetActualBorder();
350 : }
351 :
352 : return result;
353 : }
354 :
355 : // gets all the focus borders and padding that will be added to the regular border
356 : nsMargin
357 0 : nsButtonFrameRenderer::GetAddedButtonBorderAndPadding()
358 : {
359 0 : return GetButtonOuterFocusBorderAndPadding() + GetButtonInnerFocusMargin() + GetButtonInnerFocusBorderAndPadding();
360 : }
361 :
362 : /**
363 : * Call this when styles change
364 : */
365 : void
366 0 : nsButtonFrameRenderer::ReResolveStyles(nsPresContext* aPresContext)
367 : {
368 : // get all the styles
369 0 : nsStyleContext* context = mFrame->GetStyleContext();
370 0 : nsStyleSet *styleSet = aPresContext->StyleSet();
371 :
372 : // style for the inner such as a dotted line (Windows)
373 : mInnerFocusStyle =
374 0 : styleSet->ProbePseudoElementStyle(mFrame->GetContent()->AsElement(),
375 : nsCSSPseudoElements::ePseudo_mozFocusInner,
376 0 : context);
377 :
378 : // style for outer focus like a ridged border (MAC).
379 : mOuterFocusStyle =
380 0 : styleSet->ProbePseudoElementStyle(mFrame->GetContent()->AsElement(),
381 : nsCSSPseudoElements::ePseudo_mozFocusOuter,
382 0 : context);
383 0 : }
384 :
385 : nsStyleContext*
386 0 : nsButtonFrameRenderer::GetStyleContext(PRInt32 aIndex) const
387 : {
388 0 : switch (aIndex) {
389 : case NS_BUTTON_RENDERER_FOCUS_INNER_CONTEXT_INDEX:
390 0 : return mInnerFocusStyle;
391 : case NS_BUTTON_RENDERER_FOCUS_OUTER_CONTEXT_INDEX:
392 0 : return mOuterFocusStyle;
393 : default:
394 0 : return nsnull;
395 : }
396 : }
397 :
398 : void
399 0 : nsButtonFrameRenderer::SetStyleContext(PRInt32 aIndex, nsStyleContext* aStyleContext)
400 : {
401 0 : switch (aIndex) {
402 : case NS_BUTTON_RENDERER_FOCUS_INNER_CONTEXT_INDEX:
403 0 : mInnerFocusStyle = aStyleContext;
404 0 : break;
405 : case NS_BUTTON_RENDERER_FOCUS_OUTER_CONTEXT_INDEX:
406 0 : mOuterFocusStyle = aStyleContext;
407 0 : break;
408 : }
409 0 : }
|