Bug 461269 - Remove TOK_RP nodes from the parse tree. r=brendan.
[mozilla-central.git] / layout / mathml / nsMathMLmactionFrame.cpp
blob9bf15c0098d9be8e5adafe9a31febb3415996bf6
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
77 NS_IMETHODIMP_(nsrefcnt)
78 nsMathMLmactionFrame::AddRef()
80 return 2;
83 NS_IMETHODIMP_(nsrefcnt)
84 nsMathMLmactionFrame::Release()
86 return 1;
89 NS_IMPL_QUERY_INTERFACE2(nsMathMLmactionFrame,
90 nsIDOMMouseListener,
91 nsIDOMEventListener)
93 nsIFrame*
94 NS_NewMathMLmactionFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
96 return new (aPresShell) nsMathMLmactionFrame(aContext);
99 nsMathMLmactionFrame::~nsMathMLmactionFrame()
101 // unregister us as a mouse event listener ...
102 // printf("maction:%p unregistering as mouse event listener ...\n", this);
103 mContent->RemoveEventListenerByIID(this, NS_GET_IID(nsIDOMMouseListener));
106 NS_IMETHODIMP
107 nsMathMLmactionFrame::Init(nsIContent* aContent,
108 nsIFrame* aParent,
109 nsIFrame* aPrevInFlow)
111 nsAutoString value, prefix;
113 // Init our local attributes
115 mChildCount = -1; // these will be updated in GetSelectedFrame()
116 mSelection = 0;
117 mSelectedFrame = nsnull;
118 nsRefPtr<nsStyleContext> newStyleContext;
120 mActionType = NS_MATHML_ACTION_TYPE_NONE;
121 aContent->GetAttr(kNameSpaceID_None, nsGkAtoms::actiontype_, value);
122 if (!value.IsEmpty()) {
123 if (value.EqualsLiteral("toggle"))
124 mActionType = NS_MATHML_ACTION_TYPE_TOGGLE;
126 // XXX use goto to jump out of these if?
128 if (NS_MATHML_ACTION_TYPE_NONE == mActionType) {
129 // expected tooltip prefix (8ch)...
130 if (8 < value.Length() && 0 == value.Find("tooltip#"))
131 mActionType = NS_MATHML_ACTION_TYPE_TOOLTIP;
134 if (NS_MATHML_ACTION_TYPE_NONE == mActionType) {
135 // expected statusline prefix (11ch)...
136 if (11 < value.Length() && 0 == value.Find("statusline#"))
137 mActionType = NS_MATHML_ACTION_TYPE_STATUSLINE;
140 if (NS_MATHML_ACTION_TYPE_NONE == mActionType) {
141 // expected restyle prefix (8ch)...
142 if (8 < value.Length() && 0 == value.Find("restyle#")) {
143 mActionType = NS_MATHML_ACTION_TYPE_RESTYLE;
144 mRestyle = value;
146 // Here is the situation:
147 // When the attribute [actiontype="restyle#id"] is set, the Style System has
148 // given us the associated style. But we want to start with our default style.
150 // So... first, remove the attribute actiontype="restyle#id"
151 // XXXbz this is pretty messed up, since this can change whether we
152 // should have a frame at all. This really needs a better solution.
153 PRBool notify = PR_FALSE; // don't trigger a reflow yet!
154 aContent->UnsetAttr(kNameSpaceID_None, nsGkAtoms::actiontype_, notify);
156 // then, re-resolve our style
157 nsStyleContext* parentStyleContext = GetStyleContext()->GetParent();
158 newStyleContext = PresContext()->StyleSet()->
159 ResolveStyleFor(aContent, parentStyleContext);
161 if (!newStyleContext)
162 mRestyle.Truncate();
163 else {
164 if (newStyleContext != GetStyleContext())
165 SetStyleContextWithoutNotification(newStyleContext);
166 else
167 mRestyle.Truncate();
173 // Let the base class do the rest
174 return nsMathMLContainerFrame::Init(aContent, aParent, aPrevInFlow);
177 nsresult
178 nsMathMLmactionFrame::ChildListChanged(PRInt32 aModType)
180 // update cached values
181 mChildCount = -1;
182 mSelection = 0;
183 mSelectedFrame = nsnull;
184 GetSelectedFrame();
186 return nsMathMLContainerFrame::ChildListChanged(aModType);
189 // return the frame whose number is given by the attribute selection="number"
190 nsIFrame*
191 nsMathMLmactionFrame::GetSelectedFrame()
193 nsAutoString value;
194 PRInt32 selection;
196 mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::selection_, value);
197 if (!value.IsEmpty()) {
198 PRInt32 errorCode;
199 selection = value.ToInteger(&errorCode);
200 if (NS_FAILED(errorCode))
201 selection = 1;
203 else selection = 1; // default is first frame
205 if (-1 != mChildCount) { // we have been in this function before...
206 // cater for invalid user-supplied selection
207 if (selection > mChildCount || selection < 1)
208 selection = 1;
209 // quick return if it is identical with our cache
210 if (selection == mSelection)
211 return mSelectedFrame;
214 // get the selected child and cache new values...
215 PRInt32 count = 0;
216 nsIFrame* childFrame = mFrames.FirstChild();
217 while (childFrame) {
218 if (!mSelectedFrame)
219 mSelectedFrame = childFrame; // default is first child
220 if (++count == selection)
221 mSelectedFrame = childFrame;
223 childFrame = childFrame->GetNextSibling();
225 // cater for invalid user-supplied selection
226 if (selection > count || selection < 1)
227 selection = 1;
229 mChildCount = count;
230 mSelection = selection;
232 // if the selected child is an embellished operator,
233 // we become embellished as well
234 mPresentationData.baseFrame = mSelectedFrame;
235 GetEmbellishDataFrom(mSelectedFrame, mEmbellishData);
237 return mSelectedFrame;
240 NS_IMETHODIMP
241 nsMathMLmactionFrame::SetInitialChildList(nsIAtom* aListName,
242 nsFrameList& aChildList)
244 nsresult rv = nsMathMLContainerFrame::SetInitialChildList(aListName, aChildList);
246 // This very first call to GetSelectedFrame() will cause us to be marked as an
247 // embellished operator if the selected child is an embellished operator
248 if (!GetSelectedFrame()) {
249 mActionType = NS_MATHML_ACTION_TYPE_NONE;
251 else {
252 // register us as a mouse event listener ...
253 // printf("maction:%p registering as mouse event listener ...\n", this);
254 mContent->AddEventListenerByIID(this, NS_GET_IID(nsIDOMMouseListener));
256 return rv;
259 // Only paint the selected child...
260 NS_IMETHODIMP
261 nsMathMLmactionFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
262 const nsRect& aDirtyRect,
263 const nsDisplayListSet& aLists)
265 nsresult rv = DisplayBorderBackgroundOutline(aBuilder, aLists);
266 NS_ENSURE_SUCCESS(rv, rv);
268 nsIFrame* childFrame = GetSelectedFrame();
269 if (childFrame) {
270 // Put the child's background directly onto the content list
271 nsDisplayListSet set(aLists, aLists.Content());
272 // The children should be in content order
273 rv = BuildDisplayListForChild(aBuilder, childFrame, aDirtyRect, set);
274 NS_ENSURE_SUCCESS(rv, rv);
277 #if defined(NS_DEBUG) && defined(SHOW_BOUNDING_BOX)
278 // visual debug
279 rv = DisplayBoundingMetrics(aBuilder, this, mReference, mBoundingMetrics, aLists);
280 #endif
281 return rv;
284 // Only reflow the selected child ...
285 NS_IMETHODIMP
286 nsMathMLmactionFrame::Reflow(nsPresContext* aPresContext,
287 nsHTMLReflowMetrics& aDesiredSize,
288 const nsHTMLReflowState& aReflowState,
289 nsReflowStatus& aStatus)
291 nsresult rv = NS_OK;
292 aStatus = NS_FRAME_COMPLETE;
293 aDesiredSize.width = aDesiredSize.height = 0;
294 aDesiredSize.ascent = 0;
295 mBoundingMetrics.Clear();
296 nsIFrame* childFrame = GetSelectedFrame();
297 if (childFrame) {
298 nsSize availSize(aReflowState.ComputedWidth(), NS_UNCONSTRAINEDSIZE);
299 nsHTMLReflowState childReflowState(aPresContext, aReflowState,
300 childFrame, availSize);
301 rv = ReflowChild(childFrame, aPresContext, aDesiredSize,
302 childReflowState, aStatus);
303 SaveReflowAndBoundingMetricsFor(childFrame, aDesiredSize,
304 aDesiredSize.mBoundingMetrics);
305 mBoundingMetrics = aDesiredSize.mBoundingMetrics;
307 FinalizeReflow(*aReflowState.rendContext, aDesiredSize);
308 NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize);
309 return rv;
312 // Only place the selected child ...
313 /* virtual */ nsresult
314 nsMathMLmactionFrame::Place(nsIRenderingContext& aRenderingContext,
315 PRBool aPlaceOrigin,
316 nsHTMLReflowMetrics& aDesiredSize)
318 aDesiredSize.width = aDesiredSize.height = 0;
319 aDesiredSize.ascent = 0;
320 mBoundingMetrics.Clear();
321 nsIFrame* childFrame = GetSelectedFrame();
322 if (childFrame) {
323 GetReflowAndBoundingMetricsFor(childFrame, aDesiredSize, mBoundingMetrics);
324 if (aPlaceOrigin) {
325 FinishReflowChild(childFrame, PresContext(), nsnull, aDesiredSize, 0, 0, 0);
327 mReference.x = 0;
328 mReference.y = aDesiredSize.ascent;
330 aDesiredSize.mBoundingMetrics = mBoundingMetrics;
331 return NS_OK;
334 // ################################################################
335 // Event handlers
336 // ################################################################
338 // helper to show a msg on the status bar
339 // curled from nsObjectFrame.cpp ...
340 nsresult
341 nsMathMLmactionFrame::ShowStatus(nsPresContext* aPresContext,
342 nsString& aStatusMsg)
344 nsCOMPtr<nsISupports> cont = aPresContext->GetContainer();
345 if (cont) {
346 nsCOMPtr<nsIDocShellTreeItem> docShellItem(do_QueryInterface(cont));
347 if (docShellItem) {
348 nsCOMPtr<nsIDocShellTreeOwner> treeOwner;
349 docShellItem->GetTreeOwner(getter_AddRefs(treeOwner));
350 if (treeOwner) {
351 nsCOMPtr<nsIWebBrowserChrome> browserChrome(do_GetInterface(treeOwner));
352 if (browserChrome) {
353 browserChrome->SetStatus(nsIWebBrowserChrome::STATUS_LINK, aStatusMsg.get());
358 return NS_OK;
361 NS_IMETHODIMP
362 nsMathMLmactionFrame::MouseOver(nsIDOMEvent* aMouseEvent)
364 // see if we should display a status message
365 if (NS_MATHML_ACTION_TYPE_STATUSLINE == mActionType) {
366 nsAutoString value;
367 mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::actiontype_, value);
368 // expected statusline prefix (11ch)...
369 if (11 < value.Length() && 0 == value.Find("statusline#")) {
370 value.Cut(0, 11);
371 ShowStatus(PresContext(), value);
374 return NS_OK;
377 NS_IMETHODIMP
378 nsMathMLmactionFrame::MouseOut(nsIDOMEvent* aMouseEvent)
380 // see if we should remove the status message
381 if (NS_MATHML_ACTION_TYPE_STATUSLINE == mActionType) {
382 nsAutoString value;
383 value.SetLength(0);
384 ShowStatus(PresContext(), value);
386 return NS_OK;
389 NS_IMETHODIMP
390 nsMathMLmactionFrame::MouseClick(nsIDOMEvent* aMouseEvent)
392 if (NS_MATHML_ACTION_TYPE_TOGGLE == mActionType) {
393 if (mChildCount > 1) {
394 PRInt32 selection = (mSelection == mChildCount)? 1 : mSelection + 1;
395 nsAutoString value;
396 char cbuf[10];
397 PR_snprintf(cbuf, sizeof(cbuf), "%d", selection);
398 value.AssignASCII(cbuf);
399 PRBool notify = PR_FALSE; // don't yet notify the document
400 mContent->SetAttr(kNameSpaceID_None, nsGkAtoms::selection_, value, notify);
402 // Now trigger a content-changed reflow...
403 PresContext()->PresShell()->
404 FrameNeedsReflow(mSelectedFrame, nsIPresShell::eTreeChange,
405 NS_FRAME_IS_DIRTY);
408 else if (NS_MATHML_ACTION_TYPE_RESTYLE == mActionType) {
409 if (!mRestyle.IsEmpty()) {
410 nsCOMPtr<nsIDOMElement> node( do_QueryInterface(mContent) );
411 if (node.get()) {
412 if (nsContentUtils::HasNonEmptyAttr(mContent, kNameSpaceID_None,
413 nsGkAtoms::actiontype_))
414 node->RemoveAttribute(NS_LITERAL_STRING("actiontype"));
415 else
416 node->SetAttribute(NS_LITERAL_STRING("actiontype"), mRestyle);
418 // Trigger a style change reflow
419 PresContext()->PresShell()->
420 FrameNeedsReflow(mSelectedFrame, nsIPresShell::eStyleChange,
421 NS_FRAME_IS_DIRTY);
425 return NS_OK;