MathML internals - remove nonOperatorHeight(), hasBase()
[WebKit-https.git] / Source / WebCore / rendering / mathml / RenderMathMLUnderOver.cpp
1 /*
2  * Copyright (C) 2009 Alex Milowski (alex@milowski.com). All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
14  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
15  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
16  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
17  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
18  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
19  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27
28 #if ENABLE(MATHML)
29
30 #include "RenderMathMLUnderOver.h"
31
32 #include "FontSelector.h"
33 #include "MathMLNames.h"
34
35 namespace WebCore {
36
37 using namespace MathMLNames;
38     
39 static const double gOverSpacingAdjustment = 0.5;
40     
41 RenderMathMLUnderOver::RenderMathMLUnderOver(Element* element)
42     : RenderMathMLBlock(element)
43 {
44     // Determine what kind of under/over expression we have by element name
45     if (element->hasLocalName(MathMLNames::munderTag))
46         m_kind = Under;
47     else if (element->hasLocalName(MathMLNames::moverTag))
48         m_kind = Over;
49     else {
50         ASSERT(element->hasLocalName(MathMLNames::munderoverTag));
51         m_kind = UnderOver;
52     }
53 }
54
55 RenderBoxModelObject* RenderMathMLUnderOver::base() const
56 {
57     RenderObject* baseWrapper = firstChild();
58     if ((m_kind == Over || m_kind == UnderOver) && baseWrapper)
59         baseWrapper = baseWrapper->nextSibling();
60     if (!baseWrapper)
61         return 0;
62     RenderObject* base = baseWrapper->firstChild();
63     if (!base || !base->isBoxModelObject())
64         return 0;
65     return toRenderBoxModelObject(base);
66 }
67
68 void RenderMathMLUnderOver::addChild(RenderObject* child, RenderObject* beforeChild)
69 {    
70     RenderMathMLBlock* row = new (renderArena()) RenderMathMLBlock(node());
71     RefPtr<RenderStyle> rowStyle = createBlockStyle();
72     row->setStyle(rowStyle.release());
73     row->setIsAnonymous(true);
74     
75     // look through the children for rendered elements counting the blocks so we know what child
76     // we are adding
77     int blocks = 0;
78     RenderObject* current = this->firstChild();
79     while (current) {
80         blocks++;
81         current = current->nextSibling();
82     }
83     
84     switch (blocks) {
85     case 0:
86         // this is the base so just append it
87         RenderBlock::addChild(row, beforeChild);
88         break;
89     case 1:
90         // the under or over
91         // FIXME: text-align: center does not work
92         row->style()->setTextAlign(CENTER);
93         if (m_kind == Over) {
94             // add the over as first
95             RenderBlock::addChild(row, firstChild());
96         } else {
97             // add the under as last
98             RenderBlock::addChild(row, beforeChild);
99         }
100         break;
101     case 2:
102         // the under or over
103         // FIXME: text-align: center does not work
104         row->style()->setTextAlign(CENTER);
105         if (m_kind == UnderOver) {
106             // add the over as first
107             RenderBlock::addChild(row, firstChild());
108         } else {
109             // we really shouldn't get here as only munderover should have three children
110             RenderBlock::addChild(row, beforeChild);
111         }
112         break;
113     default:
114         // munderover shouldn't have more than three children. In theory we shouldn't 
115         // get here if the MathML is correctly formed, but that isn't a guarantee.
116         // We will treat this as another under element and they'll get something funky.
117         RenderBlock::addChild(row, beforeChild);
118     }
119     row->addChild(child);    
120 }
121
122 RenderMathMLOperator* RenderMathMLUnderOver::unembellishedOperator()
123 {
124     RenderBoxModelObject* base = this->base();
125     if (!base || !base->isRenderMathMLBlock())
126         return 0;
127     return toRenderMathMLBlock(base)->unembellishedOperator();
128 }
129
130 inline int getOffsetHeight(RenderObject* obj) 
131 {
132     if (obj->isBoxModelObject()) {
133         RenderBoxModelObject* box = toRenderBoxModelObject(obj);
134         return box->offsetHeight();
135     }
136    
137     return 0;
138 }
139
140 void RenderMathMLUnderOver::stretchToHeight(int height)
141 {
142     RenderBoxModelObject* base = this->base();
143     if (base && base->isRenderMathMLBlock()) {
144         RenderMathMLBlock* block = toRenderMathMLBlock(base);
145         block->stretchToHeight(height);
146         setNeedsLayout(true);
147     }
148 }
149
150 void RenderMathMLUnderOver::layout() 
151 {
152     RenderBlock::layout();
153     RenderObject* over = 0;
154     RenderObject* base = 0;
155     switch (m_kind) {
156     case Over:
157         // We need to calculate the baseline over the over versus the start of the base and 
158         // adjust the placement of the base.
159         over = firstChild();
160         if (over) {
161             // FIXME: descending glyphs intrude into base (e.g. lowercase y over base)
162             // FIXME: bases that ascend higher than the line box intrude into the over
163             if (!over->firstChild() || !over->firstChild()->isBoxModelObject())
164                 break;
165             
166             LayoutUnit overSpacing = static_cast<LayoutUnit>(gOverSpacingAdjustment * (getOffsetHeight(over) - toRenderBoxModelObject(over->firstChild())->baselinePosition(AlphabeticBaseline, true, HorizontalLine)));
167             
168             // base row wrapper
169             base = over->nextSibling();
170             if (base) {
171                 if (overSpacing > 0) 
172                     base->style()->setMarginTop(Length(-overSpacing, Fixed));
173                 else 
174                     base->style()->setMarginTop(Length(0, Fixed));
175             }
176             
177         }
178         break;
179     case Under:
180         // FIXME: Non-ascending glyphs in the under should be moved closer to the base
181
182         // We need to calculate the baseline of the base versus the start of the under block and
183         // adjust the placement of the under block.
184         
185         // base row wrapper
186         base = firstChild();
187         if (base) {
188             LayoutUnit baseHeight = getOffsetHeight(base);
189             // actual base
190             base = base->firstChild();
191             if (!base || !base->isBoxModelObject())
192                 break;
193             
194             // FIXME: We need to look at the space between a single maximum height of
195             //        the line boxes and the baseline and squeeze them together
196             LayoutUnit underSpacing = baseHeight - toRenderBoxModelObject(base)->baselinePosition(AlphabeticBaseline, true, HorizontalLine);
197             
198             // adjust the base's intrusion into the under
199             RenderObject* under = lastChild();
200             if (under && underSpacing > 0)
201                 under->style()->setMarginTop(Length(-underSpacing, Fixed));
202         }
203         break;
204     case UnderOver:
205         // FIXME: Non-descending glyphs in the over should be moved closer to the base
206         // FIXME: Non-ascending glyphs in the under should be moved closer to the base
207         
208         // We need to calculate the baseline of the over versus the start of the base and 
209         // adjust the placement of the base.
210         
211         over = firstChild();
212         if (over) {
213             // FIXME: descending glyphs intrude into base (e.g. lowercase y over base)
214             // FIXME: bases that ascend higher than the line box intrude into the over
215             if (!over->firstChild() || !over->firstChild()->isBoxModelObject())
216                 break;
217             LayoutUnit overSpacing = static_cast<LayoutUnit>(gOverSpacingAdjustment * (getOffsetHeight(over) - toRenderBoxModelObject(over->firstChild())->baselinePosition(AlphabeticBaseline, true, HorizontalLine)));
218             
219             // base row wrapper
220             base = over->nextSibling();
221             
222             if (base) {
223                 if (overSpacing > 0)
224                     base->style()->setMarginTop(Length(-overSpacing, Fixed));
225                 
226                 // We need to calculate the baseline of the base versus the start of the under block and
227                 // adjust the placement of the under block.
228                 
229                 LayoutUnit baseHeight = getOffsetHeight(base);
230                 // actual base
231                 base = base->firstChild();
232                 if (!base || !base->isBoxModelObject())
233                     break;
234
235                 // FIXME: We need to look at the space between a single maximum height of
236                 //        the line boxes and the baseline and squeeze them together
237                 LayoutUnit underSpacing = baseHeight - toRenderBoxModelObject(base)->baselinePosition(AlphabeticBaseline, true, HorizontalLine);
238                 
239                 RenderObject* under = lastChild();
240                 if (under && under->firstChild() && under->firstChild()->isRenderInline() && underSpacing > 0)
241                     under->style()->setMarginTop(Length(-underSpacing, Fixed));
242                 
243             }
244         }
245         break;
246     }
247     setNeedsLayout(true);
248     RenderBlock::layout();
249 }
250
251 LayoutUnit RenderMathMLUnderOver::baselinePosition(FontBaseline, bool firstLine, LineDirectionMode direction, LinePositionMode linePositionMode) const
252 {
253     RenderObject* current = firstChild();
254     if (!current || linePositionMode == PositionOfInteriorLineBoxes)
255         return RenderBlock::baselinePosition(AlphabeticBaseline, firstLine, direction, linePositionMode);
256
257     LayoutUnit baseline = 0;
258     switch (m_kind) {
259     case UnderOver:
260     case Over:
261         baseline += getOffsetHeight(current);
262         current = current->nextSibling();
263         if (current) {
264             // actual base
265             RenderObject* base = current->firstChild();
266             if (!base || !base->isBoxModelObject())
267                 break;
268             baseline += toRenderBoxModelObject(base)->baselinePosition(AlphabeticBaseline, firstLine, HorizontalLine, linePositionMode);
269             // added the negative top margin
270             baseline += current->style()->marginTop().value();
271         }
272         break;
273     case Under:
274         RenderObject* base = current->firstChild();
275         if (base && base->isBoxModelObject())
276             baseline += toRenderBoxModelObject(base)->baselinePosition(AlphabeticBaseline, true, HorizontalLine);
277     }
278
279     return baseline;
280 }
281
282 }
283
284 #endif // ENABLE(MATHML)