Backed out changeset b88172246b66 due to Win32 debug failures.
[mozilla-central.git] / layout / mathml / nsMathMLmactionFrame.cpp
blob62eeb099b1fa9bbd9ac609a328a9d977d9dbe454
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
13 * License.
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.
22 * Contributor(s):
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 ***** */
39 #include "nsCOMPtr.h"
40 #include "nsFrame.h"
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
78 nsIFrame*
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);
90 if (mListener)
91 mContent->RemoveEventListenerByIID(mListener, NS_GET_IID(nsIDOMMouseListener));
94 NS_IMETHODIMP
95 nsMathMLmactionFrame::Init(nsIContent* aContent,
96 nsIFrame* aParent,
97 nsIFrame* aPrevInFlow)
99 nsAutoString value, prefix;
101 // Init our local attributes
103 mChildCount = -1; // these will be updated in GetSelectedFrame()
104 mSelection = 0;
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;
132 mRestyle = value;
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)
150 mRestyle.Truncate();
151 else {
152 if (newStyleContext != GetStyleContext())
153 SetStyleContextWithoutNotification(newStyleContext);
154 else
155 mRestyle.Truncate();
161 // Let the base class do the rest
162 return nsMathMLContainerFrame::Init(aContent, aParent, aPrevInFlow);
165 nsresult
166 nsMathMLmactionFrame::ChildListChanged(PRInt32 aModType)
168 // update cached values
169 mChildCount = -1;
170 mSelection = 0;
171 mSelectedFrame = nsnull;
172 GetSelectedFrame();
174 return nsMathMLContainerFrame::ChildListChanged(aModType);
177 // return the frame whose number is given by the attribute selection="number"
178 nsIFrame*
179 nsMathMLmactionFrame::GetSelectedFrame()
181 nsAutoString value;
182 PRInt32 selection;
184 mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::selection_, value);
185 if (!value.IsEmpty()) {
186 PRInt32 errorCode;
187 selection = value.ToInteger(&errorCode);
188 if (NS_FAILED(errorCode))
189 selection = 1;
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)
196 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...
203 PRInt32 count = 0;
204 nsIFrame* childFrame = mFrames.FirstChild();
205 while (childFrame) {
206 if (!mSelectedFrame)
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)
215 selection = 1;
217 mChildCount = count;
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;
228 NS_IMETHODIMP
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;
239 else {
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));
245 return rv;
248 // Only paint the selected child...
249 NS_IMETHODIMP
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();
258 if (childFrame) {
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)
267 // visual debug
268 rv = DisplayBoundingMetrics(aBuilder, this, mReference, mBoundingMetrics, aLists);
269 #endif
270 return rv;
273 // Only reflow the selected child ...
274 NS_IMETHODIMP
275 nsMathMLmactionFrame::Reflow(nsPresContext* aPresContext,
276 nsHTMLReflowMetrics& aDesiredSize,
277 const nsHTMLReflowState& aReflowState,
278 nsReflowStatus& aStatus)
280 nsresult rv = NS_OK;
281 aStatus = NS_FRAME_COMPLETE;
282 aDesiredSize.width = aDesiredSize.height = 0;
283 aDesiredSize.ascent = 0;
284 mBoundingMetrics.Clear();
285 nsIFrame* childFrame = GetSelectedFrame();
286 if (childFrame) {
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);
298 return rv;
301 // Only place the selected child ...
302 /* virtual */ nsresult
303 nsMathMLmactionFrame::Place(nsIRenderingContext& aRenderingContext,
304 PRBool aPlaceOrigin,
305 nsHTMLReflowMetrics& aDesiredSize)
307 aDesiredSize.width = aDesiredSize.height = 0;
308 aDesiredSize.ascent = 0;
309 mBoundingMetrics.Clear();
310 nsIFrame* childFrame = GetSelectedFrame();
311 if (childFrame) {
312 GetReflowAndBoundingMetricsFor(childFrame, aDesiredSize, mBoundingMetrics);
313 if (aPlaceOrigin) {
314 FinishReflowChild(childFrame, PresContext(), nsnull, aDesiredSize, 0, 0, 0);
316 mReference.x = 0;
317 mReference.y = aDesiredSize.ascent;
319 aDesiredSize.mBoundingMetrics = mBoundingMetrics;
320 return NS_OK;
323 // ################################################################
324 // Event handlers
325 // ################################################################
327 NS_IMPL_ISUPPORTS2(nsMathMLmactionFrame::MouseListener,
328 nsIDOMEventListener,
329 nsIDOMMouseListener)
332 // helper to show a msg on the status bar
333 // curled from nsObjectFrame.cpp ...
334 void
335 ShowStatus(nsPresContext* aPresContext, nsString& aStatusMsg)
337 nsCOMPtr<nsISupports> cont = aPresContext->GetContainer();
338 if (cont) {
339 nsCOMPtr<nsIDocShellTreeItem> docShellItem(do_QueryInterface(cont));
340 if (docShellItem) {
341 nsCOMPtr<nsIDocShellTreeOwner> treeOwner;
342 docShellItem->GetTreeOwner(getter_AddRefs(treeOwner));
343 if (treeOwner) {
344 nsCOMPtr<nsIWebBrowserChrome> browserChrome(do_GetInterface(treeOwner));
345 if (browserChrome) {
346 browserChrome->SetStatus(nsIWebBrowserChrome::STATUS_LINK, aStatusMsg.get());
353 NS_IMETHODIMP
354 nsMathMLmactionFrame::MouseListener::MouseOver(nsIDOMEvent* aMouseEvent)
356 mOwner->MouseOver();
357 return NS_OK;
360 void
361 nsMathMLmactionFrame::MouseOver()
363 // see if we should display a status message
364 if (NS_MATHML_ACTION_TYPE_STATUSLINE == mActionType) {
365 nsAutoString value;
366 mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::actiontype_, value);
367 // expected statusline prefix (11ch)...
368 if (11 < value.Length() && 0 == value.Find("statusline#")) {
369 value.Cut(0, 11);
370 ShowStatus(PresContext(), value);
375 NS_IMETHODIMP
376 nsMathMLmactionFrame::MouseListener::MouseOut(nsIDOMEvent* aMouseEvent)
378 mOwner->MouseOut();
379 return NS_OK;
382 void
383 nsMathMLmactionFrame::MouseOut()
385 // see if we should remove the status message
386 if (NS_MATHML_ACTION_TYPE_STATUSLINE == mActionType) {
387 nsAutoString value;
388 value.SetLength(0);
389 ShowStatus(PresContext(), value);
393 NS_IMETHODIMP
394 nsMathMLmactionFrame::MouseListener::MouseClick(nsIDOMEvent* aMouseEvent)
396 mOwner->MouseClick();
397 return NS_OK;
400 void
401 nsMathMLmactionFrame::MouseClick()
403 if (NS_MATHML_ACTION_TYPE_TOGGLE == mActionType) {
404 if (mChildCount > 1) {
405 PRInt32 selection = (mSelection == mChildCount)? 1 : mSelection + 1;
406 nsAutoString value;
407 char cbuf[10];
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,
416 NS_FRAME_IS_DIRTY);
419 else if (NS_MATHML_ACTION_TYPE_RESTYLE == mActionType) {
420 if (!mRestyle.IsEmpty()) {
421 nsCOMPtr<nsIDOMElement> node( do_QueryInterface(mContent) );
422 if (node.get()) {
423 if (nsContentUtils::HasNonEmptyAttr(mContent, kNameSpaceID_None,
424 nsGkAtoms::actiontype_))
425 node->RemoveAttribute(NS_LITERAL_STRING("actiontype"));
426 else
427 node->SetAttribute(NS_LITERAL_STRING("actiontype"), mRestyle);
429 // Trigger a style change reflow
430 PresContext()->PresShell()->
431 FrameNeedsReflow(mSelectedFrame, nsIPresShell::eStyleChange,
432 NS_FRAME_IS_DIRTY);