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
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/
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
15 * The Original Code is Mozilla MathML Project.
17 * The Initial Developer of the Original Code is
18 * The University Of Queensland.
19 * Portions created by the Initial Developer are Copyright (C) 1999
20 * the Initial Developer. All Rights Reserved.
23 * Roger B. Sidje <rbs@maths.uq.edu.au>
25 * Alternatively, the contents of this file may be used under the terms of
26 * either of the GNU General Public License Version 2 or later (the "GPL"),
27 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 * in which case the provisions of the GPL or the LGPL are applicable instead
29 * of those above. If you wish to allow use of your version of this file only
30 * under the terms of either the GPL or the LGPL, and not to allow others to
31 * use your version of this file under the terms of the MPL, indicate your
32 * decision by deleting the provisions above and replace them with the notice
33 * and other provisions required by the GPL or the LGPL. If you do not delete
34 * the provisions above, a recipient may use your version of this file under
35 * the terms of any one of the MPL, the GPL or the LGPL.
37 * ***** END LICENSE BLOCK ***** */
41 #include "nsPresContext.h"
42 #include "nsStyleContext.h"
43 #include "nsStyleConsts.h"
44 #include "nsINameSpaceManager.h"
45 #include "nsIRenderingContext.h"
46 #include "nsIFontMetrics.h"
48 #include "nsCSSRendering.h"
49 #include "prprf.h" // For PR_snprintf()
51 #include "nsIDocShellTreeItem.h"
52 #include "nsIDocShellTreeOwner.h"
53 #include "nsIWebBrowserChrome.h"
54 #include "nsIInterfaceRequestor.h"
55 #include "nsIInterfaceRequestorUtils.h"
56 #include "nsIDOMElement.h"
58 #include "nsIDOMEventTarget.h"
59 #include "nsIDOMMouseListener.h"
61 #include "nsMathMLmactionFrame.h"
62 #include "nsAutoPtr.h"
63 #include "nsStyleSet.h"
64 #include "nsDisplayList.h"
65 #include "nsContentUtils.h"
68 // <maction> -- bind actions to a subexpression - implementation
71 #define NS_MATHML_ACTION_TYPE_NONE 0
72 #define NS_MATHML_ACTION_TYPE_TOGGLE 1
73 #define NS_MATHML_ACTION_TYPE_STATUSLINE 2
74 #define NS_MATHML_ACTION_TYPE_TOOLTIP 3 // unsupported
75 #define NS_MATHML_ACTION_TYPE_RESTYLE 4
79 NS_NewMathMLmactionFrame(nsIPresShell
* aPresShell
, nsStyleContext
* aContext
)
81 return new (aPresShell
) nsMathMLmactionFrame(aContext
);
84 NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmactionFrame
)
86 nsMathMLmactionFrame::~nsMathMLmactionFrame()
88 // unregister us as a mouse event listener ...
89 // printf("maction:%p unregistering as mouse event listener ...\n", this);
91 mContent
->RemoveEventListenerByIID(mListener
, NS_GET_IID(nsIDOMMouseListener
));
95 nsMathMLmactionFrame::Init(nsIContent
* aContent
,
97 nsIFrame
* aPrevInFlow
)
99 nsAutoString value
, prefix
;
101 // Init our local attributes
103 mChildCount
= -1; // these will be updated in GetSelectedFrame()
105 mSelectedFrame
= nsnull
;
106 nsRefPtr
<nsStyleContext
> newStyleContext
;
108 mActionType
= NS_MATHML_ACTION_TYPE_NONE
;
109 aContent
->GetAttr(kNameSpaceID_None
, nsGkAtoms::actiontype_
, value
);
110 if (!value
.IsEmpty()) {
111 if (value
.EqualsLiteral("toggle"))
112 mActionType
= NS_MATHML_ACTION_TYPE_TOGGLE
;
114 // XXX use goto to jump out of these if?
116 if (NS_MATHML_ACTION_TYPE_NONE
== mActionType
) {
117 // expected tooltip prefix (8ch)...
118 if (8 < value
.Length() && 0 == value
.Find("tooltip#"))
119 mActionType
= NS_MATHML_ACTION_TYPE_TOOLTIP
;
122 if (NS_MATHML_ACTION_TYPE_NONE
== mActionType
) {
123 // expected statusline prefix (11ch)...
124 if (11 < value
.Length() && 0 == value
.Find("statusline#"))
125 mActionType
= NS_MATHML_ACTION_TYPE_STATUSLINE
;
128 if (NS_MATHML_ACTION_TYPE_NONE
== mActionType
) {
129 // expected restyle prefix (8ch)...
130 if (8 < value
.Length() && 0 == value
.Find("restyle#")) {
131 mActionType
= NS_MATHML_ACTION_TYPE_RESTYLE
;
134 // Here is the situation:
135 // When the attribute [actiontype="restyle#id"] is set, the Style System has
136 // given us the associated style. But we want to start with our default style.
138 // So... first, remove the attribute actiontype="restyle#id"
139 // XXXbz this is pretty messed up, since this can change whether we
140 // should have a frame at all. This really needs a better solution.
141 PRBool notify
= PR_FALSE
; // don't trigger a reflow yet!
142 aContent
->UnsetAttr(kNameSpaceID_None
, nsGkAtoms::actiontype_
, notify
);
144 // then, re-resolve our style
145 nsStyleContext
* parentStyleContext
= GetStyleContext()->GetParent();
146 newStyleContext
= PresContext()->StyleSet()->
147 ResolveStyleFor(aContent
->AsElement(), parentStyleContext
);
149 if (!newStyleContext
)
152 if (newStyleContext
!= GetStyleContext())
153 SetStyleContextWithoutNotification(newStyleContext
);
161 // Let the base class do the rest
162 return nsMathMLContainerFrame::Init(aContent
, aParent
, aPrevInFlow
);
166 nsMathMLmactionFrame::ChildListChanged(PRInt32 aModType
)
168 // update cached values
171 mSelectedFrame
= nsnull
;
174 return nsMathMLContainerFrame::ChildListChanged(aModType
);
177 // return the frame whose number is given by the attribute selection="number"
179 nsMathMLmactionFrame::GetSelectedFrame()
184 mContent
->GetAttr(kNameSpaceID_None
, nsGkAtoms::selection_
, value
);
185 if (!value
.IsEmpty()) {
187 selection
= value
.ToInteger(&errorCode
);
188 if (NS_FAILED(errorCode
))
191 else selection
= 1; // default is first frame
193 if (-1 != mChildCount
) { // we have been in this function before...
194 // cater for invalid user-supplied selection
195 if (selection
> mChildCount
|| selection
< 1)
197 // quick return if it is identical with our cache
198 if (selection
== mSelection
)
199 return mSelectedFrame
;
202 // get the selected child and cache new values...
204 nsIFrame
* childFrame
= mFrames
.FirstChild();
207 mSelectedFrame
= childFrame
; // default is first child
208 if (++count
== selection
)
209 mSelectedFrame
= childFrame
;
211 childFrame
= childFrame
->GetNextSibling();
213 // cater for invalid user-supplied selection
214 if (selection
> count
|| selection
< 1)
218 mSelection
= selection
;
220 // if the selected child is an embellished operator,
221 // we become embellished as well
222 mPresentationData
.baseFrame
= mSelectedFrame
;
223 GetEmbellishDataFrom(mSelectedFrame
, mEmbellishData
);
225 return mSelectedFrame
;
229 nsMathMLmactionFrame::SetInitialChildList(nsIAtom
* aListName
,
230 nsFrameList
& aChildList
)
232 nsresult rv
= nsMathMLContainerFrame::SetInitialChildList(aListName
, aChildList
);
234 // This very first call to GetSelectedFrame() will cause us to be marked as an
235 // embellished operator if the selected child is an embellished operator
236 if (!GetSelectedFrame()) {
237 mActionType
= NS_MATHML_ACTION_TYPE_NONE
;
240 // create mouse event listener and register it
241 mListener
= new nsMathMLmactionFrame::MouseListener(this);
242 // printf("maction:%p registering as mouse event listener ...\n", this);
243 mContent
->AddEventListenerByIID(mListener
, NS_GET_IID(nsIDOMMouseListener
));
248 // Only paint the selected child...
250 nsMathMLmactionFrame::BuildDisplayList(nsDisplayListBuilder
* aBuilder
,
251 const nsRect
& aDirtyRect
,
252 const nsDisplayListSet
& aLists
)
254 nsresult rv
= DisplayBorderBackgroundOutline(aBuilder
, aLists
);
255 NS_ENSURE_SUCCESS(rv
, rv
);
257 nsIFrame
* childFrame
= GetSelectedFrame();
259 // Put the child's background directly onto the content list
260 nsDisplayListSet
set(aLists
, aLists
.Content());
261 // The children should be in content order
262 rv
= BuildDisplayListForChild(aBuilder
, childFrame
, aDirtyRect
, set
);
263 NS_ENSURE_SUCCESS(rv
, rv
);
266 #if defined(NS_DEBUG) && defined(SHOW_BOUNDING_BOX)
268 rv
= DisplayBoundingMetrics(aBuilder
, this, mReference
, mBoundingMetrics
, aLists
);
273 // Only reflow the selected child ...
275 nsMathMLmactionFrame::Reflow(nsPresContext
* aPresContext
,
276 nsHTMLReflowMetrics
& aDesiredSize
,
277 const nsHTMLReflowState
& aReflowState
,
278 nsReflowStatus
& aStatus
)
281 aStatus
= NS_FRAME_COMPLETE
;
282 aDesiredSize
.width
= aDesiredSize
.height
= 0;
283 aDesiredSize
.ascent
= 0;
284 mBoundingMetrics
.Clear();
285 nsIFrame
* childFrame
= GetSelectedFrame();
287 nsSize
availSize(aReflowState
.ComputedWidth(), NS_UNCONSTRAINEDSIZE
);
288 nsHTMLReflowState
childReflowState(aPresContext
, aReflowState
,
289 childFrame
, availSize
);
290 rv
= ReflowChild(childFrame
, aPresContext
, aDesiredSize
,
291 childReflowState
, aStatus
);
292 SaveReflowAndBoundingMetricsFor(childFrame
, aDesiredSize
,
293 aDesiredSize
.mBoundingMetrics
);
294 mBoundingMetrics
= aDesiredSize
.mBoundingMetrics
;
296 FinalizeReflow(*aReflowState
.rendContext
, aDesiredSize
);
297 NS_FRAME_SET_TRUNCATION(aStatus
, aReflowState
, aDesiredSize
);
301 // Only place the selected child ...
302 /* virtual */ nsresult
303 nsMathMLmactionFrame::Place(nsIRenderingContext
& aRenderingContext
,
305 nsHTMLReflowMetrics
& aDesiredSize
)
307 aDesiredSize
.width
= aDesiredSize
.height
= 0;
308 aDesiredSize
.ascent
= 0;
309 mBoundingMetrics
.Clear();
310 nsIFrame
* childFrame
= GetSelectedFrame();
312 GetReflowAndBoundingMetricsFor(childFrame
, aDesiredSize
, mBoundingMetrics
);
314 FinishReflowChild(childFrame
, PresContext(), nsnull
, aDesiredSize
, 0, 0, 0);
317 mReference
.y
= aDesiredSize
.ascent
;
319 aDesiredSize
.mBoundingMetrics
= mBoundingMetrics
;
323 // ################################################################
325 // ################################################################
327 NS_IMPL_ISUPPORTS2(nsMathMLmactionFrame::MouseListener
,
332 // helper to show a msg on the status bar
333 // curled from nsObjectFrame.cpp ...
335 ShowStatus(nsPresContext
* aPresContext
, nsString
& aStatusMsg
)
337 nsCOMPtr
<nsISupports
> cont
= aPresContext
->GetContainer();
339 nsCOMPtr
<nsIDocShellTreeItem
> docShellItem(do_QueryInterface(cont
));
341 nsCOMPtr
<nsIDocShellTreeOwner
> treeOwner
;
342 docShellItem
->GetTreeOwner(getter_AddRefs(treeOwner
));
344 nsCOMPtr
<nsIWebBrowserChrome
> browserChrome(do_GetInterface(treeOwner
));
346 browserChrome
->SetStatus(nsIWebBrowserChrome::STATUS_LINK
, aStatusMsg
.get());
354 nsMathMLmactionFrame::MouseListener::MouseOver(nsIDOMEvent
* aMouseEvent
)
361 nsMathMLmactionFrame::MouseOver()
363 // see if we should display a status message
364 if (NS_MATHML_ACTION_TYPE_STATUSLINE
== mActionType
) {
366 mContent
->GetAttr(kNameSpaceID_None
, nsGkAtoms::actiontype_
, value
);
367 // expected statusline prefix (11ch)...
368 if (11 < value
.Length() && 0 == value
.Find("statusline#")) {
370 ShowStatus(PresContext(), value
);
376 nsMathMLmactionFrame::MouseListener::MouseOut(nsIDOMEvent
* aMouseEvent
)
383 nsMathMLmactionFrame::MouseOut()
385 // see if we should remove the status message
386 if (NS_MATHML_ACTION_TYPE_STATUSLINE
== mActionType
) {
389 ShowStatus(PresContext(), value
);
394 nsMathMLmactionFrame::MouseListener::MouseClick(nsIDOMEvent
* aMouseEvent
)
396 mOwner
->MouseClick();
401 nsMathMLmactionFrame::MouseClick()
403 if (NS_MATHML_ACTION_TYPE_TOGGLE
== mActionType
) {
404 if (mChildCount
> 1) {
405 PRInt32 selection
= (mSelection
== mChildCount
)? 1 : mSelection
+ 1;
408 PR_snprintf(cbuf
, sizeof(cbuf
), "%d", selection
);
409 value
.AssignASCII(cbuf
);
410 PRBool notify
= PR_FALSE
; // don't yet notify the document
411 mContent
->SetAttr(kNameSpaceID_None
, nsGkAtoms::selection_
, value
, notify
);
413 // Now trigger a content-changed reflow...
414 PresContext()->PresShell()->
415 FrameNeedsReflow(mSelectedFrame
, nsIPresShell::eTreeChange
,
419 else if (NS_MATHML_ACTION_TYPE_RESTYLE
== mActionType
) {
420 if (!mRestyle
.IsEmpty()) {
421 nsCOMPtr
<nsIDOMElement
> node( do_QueryInterface(mContent
) );
423 if (nsContentUtils::HasNonEmptyAttr(mContent
, kNameSpaceID_None
,
424 nsGkAtoms::actiontype_
))
425 node
->RemoveAttribute(NS_LITERAL_STRING("actiontype"));
427 node
->SetAttribute(NS_LITERAL_STRING("actiontype"), mRestyle
);
429 // Trigger a style change reflow
430 PresContext()->PresShell()->
431 FrameNeedsReflow(mSelectedFrame
, nsIPresShell::eStyleChange
,