2010-08-09 Fran├žois Sausset <sausset@gmail.com>
[WebKit.git] / WebCore / mathml / RenderMathMLFenced.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 "RenderMathMLFenced.h"
31
32 #include "FontSelector.h"
33 #include "MathMLNames.h"
34 #include "RenderInline.h"
35 #include "RenderMathMLOperator.h"
36 #include "RenderText.h"
37
38 namespace WebCore {
39     
40 using namespace MathMLNames;
41     
42 enum Braces { OpeningBraceChar = 0x28, ClosingBraceChar = 0x29 };
43
44 RenderMathMLFenced::RenderMathMLFenced(Node* fenced) 
45     : RenderMathMLRow(fenced)
46     , m_open(OpeningBraceChar)
47     , m_close(ClosingBraceChar)
48 {
49 }
50
51 void RenderMathMLFenced::updateFromElement()
52 {
53     Element* fenced = static_cast<Element*>(node());
54  
55     // FIXME: Handle open/close values with more than one character (they should be treated like text).
56     AtomicString openValue = fenced->getAttribute(MathMLNames::openAttr);
57     if (openValue.length() > 0)
58         m_open = openValue[0];
59     AtomicString closeValue = fenced->getAttribute(MathMLNames::closeAttr);
60     if (closeValue.length() > 0)
61         m_close = closeValue[0];
62     
63     AtomicString separators = static_cast<Element*>(fenced)->getAttribute(MathMLNames::separatorsAttr);
64     if (!separators.isNull()) {
65         Vector<UChar> characters;
66         for (unsigned int i = 0; i < separators.length(); i++) {
67             if (!isSpaceOrNewline(separators[i]))
68                 characters.append(separators[i]);
69         }
70         m_separators = !separators.length() ? 0 : StringImpl::create(characters.data() , characters.size());
71     } else {
72         // The separator defaults to a single comma.
73         m_separators = StringImpl::create(",");
74     }
75     
76     if (isEmpty())
77         makeFences();
78 }
79
80 RefPtr<RenderStyle> RenderMathMLFenced::makeOperatorStyle() 
81 {
82     RefPtr<RenderStyle> newStyle = RenderStyle::create();
83     newStyle->inheritFrom(style());
84     newStyle->setDisplay(INLINE_BLOCK);
85     newStyle->setHeight(Length(100.0, Percent));
86     newStyle->setVerticalAlign(MIDDLE);
87     return newStyle;
88 }
89
90 void RenderMathMLFenced::makeFences()
91 {
92     RenderObject* openFence = new (renderArena()) RenderMathMLOperator(node(), m_open);
93     openFence->setStyle(makeOperatorStyle().release());
94     RenderBlock::addChild(openFence, firstChild());
95     RenderObject* closeFence = new (renderArena()) RenderMathMLOperator(node(), m_close);
96     closeFence->setStyle(makeOperatorStyle().release());
97     RenderBlock::addChild(closeFence);
98 }
99
100 void RenderMathMLFenced::addChild(RenderObject* child, RenderObject*)
101 {
102     // make the fences if the render object is empty
103     if (isEmpty())
104         updateFromElement();
105     
106     if (m_separators.get()) {
107         unsigned int count = 0;
108         for (Node* position = child->node(); position; position = position->previousSibling()) {
109             if (position->nodeType() == Node::ELEMENT_NODE)
110                 count++;
111         }
112                 
113         if (count > 1) {
114             UChar separator;
115             
116             // Use the last separator if we've run out of specified separators.
117             if ((count - 1) >= m_separators.get()->length())
118                 separator = (*m_separators.get())[m_separators.get()->length() - 1];
119             else
120                 separator = (*m_separators.get())[count - 1];
121                 
122             RenderObject* separatorObj = new (renderArena()) RenderMathMLOperator(node(), separator);
123             separatorObj->setStyle(makeOperatorStyle().release());
124             RenderBlock::addChild(separatorObj, lastChild());
125         }
126     }
127     
128     // If we have a block, we'll wrap it in an inline-block.
129     if (child->isBlockFlow() && child->style()->display() != INLINE_BLOCK) {
130         // Block objects wrapper.
131
132         RenderBlock* block = new (renderArena()) RenderBlock(node());
133         RefPtr<RenderStyle> newStyle = RenderStyle::create();
134         newStyle->inheritFrom(style());
135         newStyle->setDisplay(INLINE_BLOCK);
136         block->setStyle(newStyle.release());
137         
138         RenderBlock::addChild(block, lastChild());
139         block->addChild(child);    
140     } else
141         RenderBlock::addChild(child, lastChild());
142 }
143
144 void RenderMathMLFenced::layout() 
145 {
146     RenderMathMLRow::layout();
147     
148     int width = 0;
149     for (RenderObject* current = firstChild(); current; current = current->nextSibling()) {
150         if (current->isBoxModelObject()) {
151             RenderBoxModelObject* box = toRenderBoxModelObject(current);
152             width += box->offsetWidth();
153         }
154     }
155     width++;
156     style()->setWidth(Length(width, Fixed));
157
158     setNeedsLayoutAndPrefWidthsRecalc();
159     markContainingBlocksForLayout();
160     RenderBlock::layout();
161 }
162 }    
163
164 #endif