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 MathML Project.
16 : *
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.
21 : *
22 : * Contributor(s):
23 : * Roger B. Sidje <rbs@maths.uq.edu.au>
24 : *
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.
36 : *
37 : * ***** END LICENSE BLOCK ***** */
38 :
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 :
46 : #include "nsCSSRendering.h"
47 : #include "prprf.h" // For PR_snprintf()
48 :
49 : #include "nsIDocShellTreeItem.h"
50 : #include "nsIDocShellTreeOwner.h"
51 : #include "nsIWebBrowserChrome.h"
52 : #include "nsIInterfaceRequestor.h"
53 : #include "nsIInterfaceRequestorUtils.h"
54 : #include "nsIDOMElement.h"
55 :
56 : #include "nsIDOMEventTarget.h"
57 :
58 : #include "nsMathMLmactionFrame.h"
59 : #include "nsAutoPtr.h"
60 : #include "nsStyleSet.h"
61 : #include "nsDisplayList.h"
62 : #include "nsContentUtils.h"
63 :
64 : //
65 : // <maction> -- bind actions to a subexpression - implementation
66 : //
67 :
68 : #define NS_MATHML_ACTION_TYPE_NONE 0
69 : #define NS_MATHML_ACTION_TYPE_TOGGLE 1
70 : #define NS_MATHML_ACTION_TYPE_STATUSLINE 2
71 : #define NS_MATHML_ACTION_TYPE_TOOLTIP 3 // unsupported
72 :
73 :
74 : nsIFrame*
75 0 : NS_NewMathMLmactionFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
76 : {
77 0 : return new (aPresShell) nsMathMLmactionFrame(aContext);
78 : }
79 :
80 0 : NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmactionFrame)
81 :
82 0 : nsMathMLmactionFrame::~nsMathMLmactionFrame()
83 : {
84 : // unregister us as a mouse event listener ...
85 : // printf("maction:%p unregistering as mouse event listener ...\n", this);
86 0 : if (mListener) {
87 0 : mContent->RemoveSystemEventListener(NS_LITERAL_STRING("click"), mListener,
88 0 : false);
89 0 : mContent->RemoveSystemEventListener(NS_LITERAL_STRING("mouseover"), mListener,
90 0 : false);
91 0 : mContent->RemoveSystemEventListener(NS_LITERAL_STRING("mouseout"), mListener,
92 0 : false);
93 : }
94 0 : }
95 :
96 : NS_IMETHODIMP
97 0 : nsMathMLmactionFrame::Init(nsIContent* aContent,
98 : nsIFrame* aParent,
99 : nsIFrame* aPrevInFlow)
100 : {
101 0 : nsAutoString value, prefix;
102 :
103 : // Init our local attributes
104 :
105 0 : mChildCount = -1; // these will be updated in GetSelectedFrame()
106 0 : mSelection = 0;
107 0 : mSelectedFrame = nsnull;
108 0 : nsRefPtr<nsStyleContext> newStyleContext;
109 :
110 0 : mActionType = NS_MATHML_ACTION_TYPE_NONE;
111 0 : aContent->GetAttr(kNameSpaceID_None, nsGkAtoms::actiontype_, value);
112 0 : if (!value.IsEmpty()) {
113 0 : if (value.EqualsLiteral("toggle"))
114 0 : mActionType = NS_MATHML_ACTION_TYPE_TOGGLE;
115 :
116 : // XXX use goto to jump out of these if?
117 :
118 0 : if (NS_MATHML_ACTION_TYPE_NONE == mActionType) {
119 : // expected tooltip prefix (8ch)...
120 0 : if (8 < value.Length() && 0 == value.Find("tooltip#"))
121 0 : mActionType = NS_MATHML_ACTION_TYPE_TOOLTIP;
122 : }
123 :
124 0 : if (NS_MATHML_ACTION_TYPE_NONE == mActionType) {
125 : // expected statusline prefix (11ch)...
126 0 : if (11 < value.Length() && 0 == value.Find("statusline#"))
127 0 : mActionType = NS_MATHML_ACTION_TYPE_STATUSLINE;
128 : }
129 :
130 : }
131 :
132 : // Let the base class do the rest
133 0 : return nsMathMLContainerFrame::Init(aContent, aParent, aPrevInFlow);
134 : }
135 :
136 : NS_IMETHODIMP
137 0 : nsMathMLmactionFrame::TransmitAutomaticData() {
138 : // The REC defines the following element to be space-like:
139 : // * an maction element whose selected sub-expression exists and is
140 : // space-like;
141 0 : nsIMathMLFrame* mathMLFrame = do_QueryFrame(mSelectedFrame);
142 0 : if (mathMLFrame && mathMLFrame->IsSpaceLike()) {
143 0 : mPresentationData.flags |= NS_MATHML_SPACE_LIKE;
144 : } else {
145 0 : mPresentationData.flags &= ~NS_MATHML_SPACE_LIKE;
146 : }
147 :
148 : // The REC defines the following element to be an embellished operator:
149 : // * an maction element whose selected sub-expression exists and is an
150 : // embellished operator;
151 0 : mPresentationData.baseFrame = mSelectedFrame;
152 0 : GetEmbellishDataFrom(mSelectedFrame, mEmbellishData);
153 :
154 0 : return NS_OK;
155 : }
156 :
157 : nsresult
158 0 : nsMathMLmactionFrame::ChildListChanged(PRInt32 aModType)
159 : {
160 : // update cached values
161 0 : mChildCount = -1;
162 0 : mSelection = 0;
163 0 : mSelectedFrame = nsnull;
164 0 : GetSelectedFrame();
165 :
166 0 : return nsMathMLContainerFrame::ChildListChanged(aModType);
167 : }
168 :
169 : // return the frame whose number is given by the attribute selection="number"
170 : nsIFrame*
171 0 : nsMathMLmactionFrame::GetSelectedFrame()
172 : {
173 0 : nsAutoString value;
174 : PRInt32 selection;
175 :
176 : GetAttribute(mContent, mPresentationData.mstyle, nsGkAtoms::selection_,
177 0 : value);
178 0 : if (!value.IsEmpty()) {
179 : PRInt32 errorCode;
180 0 : selection = value.ToInteger(&errorCode);
181 0 : if (NS_FAILED(errorCode))
182 0 : selection = 1;
183 : }
184 0 : else selection = 1; // default is first frame
185 :
186 0 : if (-1 != mChildCount) { // we have been in this function before...
187 : // cater for invalid user-supplied selection
188 0 : if (selection > mChildCount || selection < 1)
189 0 : selection = 1;
190 : // quick return if it is identical with our cache
191 0 : if (selection == mSelection)
192 0 : return mSelectedFrame;
193 : }
194 :
195 : // get the selected child and cache new values...
196 0 : PRInt32 count = 0;
197 0 : nsIFrame* childFrame = mFrames.FirstChild();
198 0 : while (childFrame) {
199 0 : if (!mSelectedFrame)
200 0 : mSelectedFrame = childFrame; // default is first child
201 0 : if (++count == selection)
202 0 : mSelectedFrame = childFrame;
203 :
204 0 : childFrame = childFrame->GetNextSibling();
205 : }
206 : // cater for invalid user-supplied selection
207 0 : if (selection > count || selection < 1)
208 0 : selection = 1;
209 :
210 0 : mChildCount = count;
211 0 : mSelection = selection;
212 :
213 0 : return mSelectedFrame;
214 : }
215 :
216 : NS_IMETHODIMP
217 0 : nsMathMLmactionFrame::SetInitialChildList(ChildListID aListID,
218 : nsFrameList& aChildList)
219 : {
220 0 : nsresult rv = nsMathMLContainerFrame::SetInitialChildList(aListID, aChildList);
221 :
222 : // This very first call to GetSelectedFrame() will cause us to be marked as an
223 : // embellished operator if the selected child is an embellished operator
224 0 : if (!GetSelectedFrame()) {
225 0 : mActionType = NS_MATHML_ACTION_TYPE_NONE;
226 : }
227 : else {
228 : // create mouse event listener and register it
229 0 : mListener = new nsMathMLmactionFrame::MouseListener(this);
230 : // printf("maction:%p registering as mouse event listener ...\n", this);
231 0 : mContent->AddSystemEventListener(NS_LITERAL_STRING("click"), mListener,
232 0 : false, false);
233 0 : mContent->AddSystemEventListener(NS_LITERAL_STRING("mouseover"), mListener,
234 0 : false, false);
235 0 : mContent->AddSystemEventListener(NS_LITERAL_STRING("mouseout"), mListener,
236 0 : false, false);
237 : }
238 0 : return rv;
239 : }
240 :
241 : // Only paint the selected child...
242 : NS_IMETHODIMP
243 0 : nsMathMLmactionFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
244 : const nsRect& aDirtyRect,
245 : const nsDisplayListSet& aLists)
246 : {
247 0 : nsresult rv = DisplayBorderBackgroundOutline(aBuilder, aLists);
248 0 : NS_ENSURE_SUCCESS(rv, rv);
249 :
250 0 : nsIFrame* childFrame = GetSelectedFrame();
251 0 : if (childFrame) {
252 : // Put the child's background directly onto the content list
253 0 : nsDisplayListSet set(aLists, aLists.Content());
254 : // The children should be in content order
255 0 : rv = BuildDisplayListForChild(aBuilder, childFrame, aDirtyRect, set);
256 0 : NS_ENSURE_SUCCESS(rv, rv);
257 : }
258 :
259 : #if defined(NS_DEBUG) && defined(SHOW_BOUNDING_BOX)
260 : // visual debug
261 : rv = DisplayBoundingMetrics(aBuilder, this, mReference, mBoundingMetrics, aLists);
262 : #endif
263 0 : return rv;
264 : }
265 :
266 : // Only reflow the selected child ...
267 : NS_IMETHODIMP
268 0 : nsMathMLmactionFrame::Reflow(nsPresContext* aPresContext,
269 : nsHTMLReflowMetrics& aDesiredSize,
270 : const nsHTMLReflowState& aReflowState,
271 : nsReflowStatus& aStatus)
272 : {
273 0 : nsresult rv = NS_OK;
274 0 : aStatus = NS_FRAME_COMPLETE;
275 0 : aDesiredSize.width = aDesiredSize.height = 0;
276 0 : aDesiredSize.ascent = 0;
277 0 : mBoundingMetrics = nsBoundingMetrics();
278 0 : nsIFrame* childFrame = GetSelectedFrame();
279 0 : if (childFrame) {
280 0 : nsSize availSize(aReflowState.ComputedWidth(), NS_UNCONSTRAINEDSIZE);
281 : nsHTMLReflowState childReflowState(aPresContext, aReflowState,
282 0 : childFrame, availSize);
283 : rv = ReflowChild(childFrame, aPresContext, aDesiredSize,
284 0 : childReflowState, aStatus);
285 : SaveReflowAndBoundingMetricsFor(childFrame, aDesiredSize,
286 0 : aDesiredSize.mBoundingMetrics);
287 0 : mBoundingMetrics = aDesiredSize.mBoundingMetrics;
288 : }
289 0 : FinalizeReflow(*aReflowState.rendContext, aDesiredSize);
290 0 : NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize);
291 0 : return rv;
292 : }
293 :
294 : // Only place the selected child ...
295 : /* virtual */ nsresult
296 0 : nsMathMLmactionFrame::Place(nsRenderingContext& aRenderingContext,
297 : bool aPlaceOrigin,
298 : nsHTMLReflowMetrics& aDesiredSize)
299 : {
300 0 : aDesiredSize.width = aDesiredSize.height = 0;
301 0 : aDesiredSize.ascent = 0;
302 0 : mBoundingMetrics = nsBoundingMetrics();
303 0 : nsIFrame* childFrame = GetSelectedFrame();
304 0 : if (childFrame) {
305 0 : GetReflowAndBoundingMetricsFor(childFrame, aDesiredSize, mBoundingMetrics);
306 0 : if (aPlaceOrigin) {
307 0 : FinishReflowChild(childFrame, PresContext(), nsnull, aDesiredSize, 0, 0, 0);
308 : }
309 0 : mReference.x = 0;
310 0 : mReference.y = aDesiredSize.ascent;
311 : }
312 0 : aDesiredSize.mBoundingMetrics = mBoundingMetrics;
313 0 : return NS_OK;
314 : }
315 :
316 : // ################################################################
317 : // Event handlers
318 : // ################################################################
319 :
320 0 : NS_IMPL_ISUPPORTS1(nsMathMLmactionFrame::MouseListener,
321 : nsIDOMEventListener)
322 :
323 :
324 : // helper to show a msg on the status bar
325 : // curled from nsObjectFrame.cpp ...
326 : void
327 0 : ShowStatus(nsPresContext* aPresContext, nsString& aStatusMsg)
328 : {
329 0 : nsCOMPtr<nsISupports> cont = aPresContext->GetContainer();
330 0 : if (cont) {
331 0 : nsCOMPtr<nsIDocShellTreeItem> docShellItem(do_QueryInterface(cont));
332 0 : if (docShellItem) {
333 0 : nsCOMPtr<nsIDocShellTreeOwner> treeOwner;
334 0 : docShellItem->GetTreeOwner(getter_AddRefs(treeOwner));
335 0 : if (treeOwner) {
336 0 : nsCOMPtr<nsIWebBrowserChrome> browserChrome(do_GetInterface(treeOwner));
337 0 : if (browserChrome) {
338 0 : browserChrome->SetStatus(nsIWebBrowserChrome::STATUS_LINK, aStatusMsg.get());
339 : }
340 : }
341 : }
342 : }
343 0 : }
344 :
345 : NS_IMETHODIMP
346 0 : nsMathMLmactionFrame::MouseListener::HandleEvent(nsIDOMEvent* aEvent)
347 : {
348 0 : nsAutoString eventType;
349 0 : aEvent->GetType(eventType);
350 0 : if (eventType.EqualsLiteral("mouseover")) {
351 0 : mOwner->MouseOver();
352 : }
353 0 : else if (eventType.EqualsLiteral("click")) {
354 0 : mOwner->MouseClick();
355 : }
356 0 : else if (eventType.EqualsLiteral("mouseout")) {
357 0 : mOwner->MouseOut();
358 : }
359 : else {
360 0 : NS_ABORT();
361 : }
362 :
363 0 : return NS_OK;
364 : }
365 :
366 : void
367 0 : nsMathMLmactionFrame::MouseOver()
368 : {
369 : // see if we should display a status message
370 0 : if (NS_MATHML_ACTION_TYPE_STATUSLINE == mActionType) {
371 0 : nsAutoString value;
372 0 : mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::actiontype_, value);
373 : // expected statusline prefix (11ch)...
374 0 : if (11 < value.Length() && 0 == value.Find("statusline#")) {
375 0 : value.Cut(0, 11);
376 0 : ShowStatus(PresContext(), value);
377 : }
378 : }
379 0 : }
380 :
381 : void
382 0 : nsMathMLmactionFrame::MouseOut()
383 : {
384 : // see if we should remove the status message
385 0 : if (NS_MATHML_ACTION_TYPE_STATUSLINE == mActionType) {
386 0 : nsAutoString value;
387 0 : value.SetLength(0);
388 0 : ShowStatus(PresContext(), value);
389 : }
390 0 : }
391 :
392 : void
393 0 : nsMathMLmactionFrame::MouseClick()
394 : {
395 0 : if (NS_MATHML_ACTION_TYPE_TOGGLE == mActionType) {
396 0 : if (mChildCount > 1) {
397 0 : PRInt32 selection = (mSelection == mChildCount)? 1 : mSelection + 1;
398 0 : nsAutoString value;
399 : char cbuf[10];
400 0 : PR_snprintf(cbuf, sizeof(cbuf), "%d", selection);
401 0 : value.AssignASCII(cbuf);
402 0 : bool notify = false; // don't yet notify the document
403 0 : mContent->SetAttr(kNameSpaceID_None, nsGkAtoms::selection_, value, notify);
404 :
405 : // Now trigger a content-changed reflow...
406 0 : PresContext()->PresShell()->
407 : FrameNeedsReflow(mSelectedFrame, nsIPresShell::eTreeChange,
408 0 : NS_FRAME_IS_DIRTY);
409 : }
410 : }
411 0 : }
|