1 // Copyright (C) 2000-2007, Luca Padovani <padovani@sti.uniurb.it>.
2 //
3 // This file is part of GtkMathView, a flexible, high-quality rendering
4 // engine for MathML documents.
5 //
6 // GtkMathView is free software; you can redistribute it and/or modify it
7 // under the terms of the GNU Lesser General Public License as published
8 // by the Free Software Foundation; either version 3 of the License, or
9 // (at your option) any later version.
10 //
11 // GtkMathView is distributed in the hope that it will be useful, but
12 // WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 // Lesser General Public License for more details.
15 //
16 // You should have received a copy of the GNU Lesser General Public License
17 // along with this program. If not, see <http://www.gnu.org/licenses/>.
18
19 #include <config.h>
20
21 #include "defs.h"
22 #include "config.dirs"
23
24 #include "Clock.hh"
25 #include "View.hh"
26 #include "Element.hh"
27 #include "Builder.hh"
28 #include "MathMLNamespaceContext.hh"
29 #include "MathMLOperatorDictionary.hh"
30 #if GMV_ENABLE_BOXML
31 #include "BoxMLNamespaceContext.hh"
32 #endif // GMV_ENABLE_BOXML
33 #include "AreaId.hh"
34 #include "AbstractLogger.hh"
35 #include "FormattingContext.hh"
36 #include "MathGraphicDevice.hh"
37 #include "BoxGraphicDevice.hh"
38 #ifdef ENABLE_BINRELOC
39 #include "prefix.h"
40 #endif // ENABLE_BINRELOC
41
View(const SmartPtr<AbstractLogger> & l)42 View::View(const SmartPtr<AbstractLogger>& l)
43 : logger(l), defaultFontSize(DEFAULT_FONT_SIZE), freezeCounter(0)
44 { }
45
~View()46 View::~View()
47 {
48 // When the view is destroyed the formatting tree must have
49 // been destroyed already, because elements need a healthy
50 // view for de-registering themselves from the linker
51 assert(!rootElement);
52 }
53
54 bool
freeze()55 View::freeze()
56 {
57 return freezeCounter++ == 0;
58 }
59
60 bool
thaw()61 View::thaw()
62 {
63 assert(freezeCounter > 0);
64 return --freezeCounter == 0;
65 }
66
67 String
getDefaultConfigurationPath()68 View::getDefaultConfigurationPath()
69 {
70 #ifdef ENABLE_BINRELOC
71 return BR_SYSCONFDIR("/gtkmathview.conf.xml");
72 #else
73 return PKGSYSCONFDIR"/gtkmathview.conf.xml";
74 #endif
75 }
76
77 String
getDefaultOperatorDictionaryPath()78 View::getDefaultOperatorDictionaryPath()
79 {
80 #ifdef ENABLE_BINRELOC
81 return BR_DATADIR("/gtkmathview/dictionary.xml");
82 #else
83 return PKGDATADIR"/dictionary.xml";
84 #endif
85 }
86
87 SmartPtr<AbstractLogger>
getLogger() const88 View::getLogger() const
89 { return logger; }
90
91 void
setOperatorDictionary(const SmartPtr<MathMLOperatorDictionary> & d)92 View::setOperatorDictionary(const SmartPtr<MathMLOperatorDictionary>& d)
93 { dictionary = d; }
94
95 SmartPtr<MathMLOperatorDictionary>
getOperatorDictionary() const96 View::getOperatorDictionary() const
97 { return dictionary; }
98
99 void
setBuilder(const SmartPtr<Builder> & b)100 View::setBuilder(const SmartPtr<Builder>& b)
101 {
102 resetRootElement();
103 builder = b;
104 if (builder)
105 {
106 builder->setMathMLNamespaceContext(mathmlContext);
107 #if GMV_ENABLE_BOXML
108 builder->setBoxMLNamespaceContext(boxmlContext);
109 #endif // GMV_ENABLE_BOXML
110 builder->setLogger(logger);
111 }
112 }
113
114 SmartPtr<Builder>
getBuilder() const115 View::getBuilder() const
116 { return builder; }
117
118 AreaRef
formatElement(const SmartPtr<Element> & elem) const119 View::formatElement(const SmartPtr<Element>& elem) const
120 {
121 if (!elem) return 0;
122
123 if (elem->dirtyLayout())
124 {
125 const SmartPtr<MathGraphicDevice> mgd = mathmlContext ? mathmlContext->getGraphicDevice() : 0;
126 assert(mgd != 0);
127 #if GMV_ENABLE_BOXML
128 const SmartPtr<BoxGraphicDevice> bgd = boxmlContext ? boxmlContext->getGraphicDevice() : 0;
129 FormattingContext ctxt(mgd, bgd);
130 #else
131 FormattingContext ctxt(mgd);
132 #endif // GMV_ENABLE_BOXML
133 Length defaultSize(getDefaultFontSize(), Length::PT_UNIT);
134 scaled l = mgd->evaluate(ctxt, defaultSize, scaled::zero());
135 ctxt.setSize(l);
136 ctxt.setActualSize(ctxt.getSize());
137 ctxt.setAvailableWidth(getAvailableWidth());
138 Clock perf;
139 perf.Start();
140 elem->format(ctxt);
141 perf.Stop();
142 getLogger()->out(LOG_INFO, "formatting time: %dms", perf());
143 }
144
145 return elem->getArea();
146 }
147
148 SmartPtr<Element>
getRootElement() const149 View::getRootElement() const
150 {
151 bool rootDirty = !rootElement ||
152 rootElement->dirtyStructure() || rootElement->dirtyAttribute() ||
153 rootElement->dirtyAttributeP();
154
155 if (rootDirty)
156 {
157 Clock perf;
158
159 perf.Start();
160 rootElement = builder->getRootElement();
161 perf.Stop();
162
163 getLogger()->out(LOG_INFO, "build time: %dms", perf());
164 }
165
166 return rootElement;
167 }
168
169 void
resetRootElement()170 View::resetRootElement()
171 {
172 rootElement = 0;
173 }
174
175 AreaRef
getRootArea() const176 View::getRootArea() const
177 { return formatElement(getRootElement()); }
178
179 BoundingBox
getBoundingBox() const180 View::getBoundingBox() const
181 {
182 if (AreaRef rootArea = getRootArea())
183 return rootArea->box();
184 else
185 return BoundingBox();
186 }
187
188 SmartPtr<Element>
getElementAt(const scaled & x,const scaled & y,Point * elemOrigin,BoundingBox * elemBox) const189 View::getElementAt(const scaled& x, const scaled& y, Point* elemOrigin, BoundingBox* elemBox) const
190 {
191 if (AreaRef rootArea = getRootArea())
192 {
193 AreaId deepId(rootArea);
194 if (rootArea->searchByCoords(deepId, x, y))
195 for (int i = deepId.size(); i >= 0; i--)
196 {
197 AreaRef area = deepId.getArea(i);
198 if (SmartPtr<Element> elem = area->getElement())
199 {
200 if (elemOrigin) deepId.getOrigin(*elemOrigin, 0, i);
201 if (elemBox) *elemBox = area->box();
202 return elem;
203 }
204 }
205 }
206
207 return 0;
208 }
209
210 bool
getElementExtents(const SmartPtr<Element> & refElem,const SmartPtr<Element> & elem,Point * elemOrigin,BoundingBox * elemBox) const211 View::getElementExtents(const SmartPtr<Element>& refElem, const SmartPtr<Element>& elem,
212 Point* elemOrigin, BoundingBox* elemBox) const
213 {
214 assert(refElem);
215 assert(elem);
216 if (getRootArea())
217 if (AreaRef elemArea = elem->getArea())
218 {
219 if (elemOrigin)
220 {
221 if (AreaRef refArea = refElem->getArea())
222 {
223 AreaId elemId(refArea);
224 if (refArea->searchByArea(elemId, elemArea))
225 elemId.getOrigin(*elemOrigin);
226 else
227 return false;
228 }
229 else
230 return false;
231 }
232
233 if (elemBox) *elemBox = elemArea->box();
234
235 return true;
236 }
237
238 return false;
239 }
240
241 bool
getElementExtents(const SmartPtr<Element> & elem,Point * elemOrigin,BoundingBox * elemBox) const242 View::getElementExtents(const SmartPtr<Element>& elem, Point* elemOrigin, BoundingBox* elemBox) const
243 {
244 return getElementExtents(getRootElement(), elem, elemOrigin, elemBox);
245 }
246
247 bool
getElementLength(const SmartPtr<Element> & elem,CharIndex & length) const248 View::getElementLength(const SmartPtr<Element>& elem, CharIndex& length) const
249 {
250 assert(elem);
251 if (getRootArea())
252 if (AreaRef elemArea = elem->getArea())
253 {
254 length = elemArea->length();
255 return true;
256 }
257
258 return false;
259 }
260
261 SmartPtr<Element>
getCharAt(const scaled & x,const scaled & y,CharIndex & index,Point * charOrig,BoundingBox * charBox) const262 View::getCharAt(const scaled& x, const scaled& y, CharIndex& index, Point* charOrig, BoundingBox* charBox) const
263 {
264 if (AreaRef rootArea = getRootArea())
265 {
266 AreaId deepId(rootArea);
267 if (rootArea->searchByCoords(deepId, x, y))
268 for (int i = deepId.size(); i >= 0; i--)
269 {
270 AreaRef area = deepId.getArea(i);
271 if (SmartPtr<Element> elem = area->getElement())
272 {
273 Point deepOrigin;
274 deepId.accumulateOrigin(deepOrigin);
275
276 AreaRef deepArea = deepId.getArea();
277 CharIndex deepIndex;
278 if (!deepArea->indexOfPosition(x - deepOrigin.x, y - deepOrigin.y, deepIndex))
279 deepIndex = 0;
280
281 index = deepId.getLength(i, -1) + deepIndex;
282
283 if (charOrig || charBox)
284 {
285 if (!deepArea->positionOfIndex(deepIndex, charOrig, charBox))
286 return 0;
287 }
288
289 return elem;
290 }
291 }
292 }
293
294 return 0;
295 }
296
297 bool
getCharExtents(const SmartPtr<Element> & refElem,const SmartPtr<Element> & elem,CharIndex index,Point * charOrig,BoundingBox * charBox) const298 View::getCharExtents(const SmartPtr<Element>& refElem, const SmartPtr<Element>& elem, CharIndex index,
299 Point* charOrig, BoundingBox* charBox) const
300 {
301 assert(refElem);
302 assert(elem);
303
304 Point elemOrig;
305 if (getElementOrigin(refElem, elem, elemOrig))
306 if (AreaRef elemArea = elem->getArea())
307 {
308 AreaId deepId(elemArea);
309 if (elemArea->searchByIndex(deepId, index))
310 {
311 AreaRef deepArea = deepId.getArea();
312 Point deepOrig;
313 deepId.getOrigin(deepOrig);
314
315 if (deepArea->positionOfIndex(index - deepId.getLength(), charOrig, charBox))
316 {
317 if (charOrig)
318 {
319 charOrig->x += elemOrig.x + deepOrig.x;
320 charOrig->y += elemOrig.y + deepOrig.y;
321 }
322
323 return true;
324 }
325 }
326 }
327
328 return false;
329 }
330
331 bool
getCharExtents(const SmartPtr<Element> & elem,CharIndex index,Point * charOrig,BoundingBox * charBox) const332 View::getCharExtents(const SmartPtr<Element>& elem, CharIndex index,
333 Point* charOrig, BoundingBox* charBox) const
334 {
335 return getCharExtents(getRootElement(), elem, index, charOrig, charBox);
336 }
337
338 void
setMathMLNamespaceContext(const SmartPtr<MathMLNamespaceContext> & ctxt)339 View::setMathMLNamespaceContext(const SmartPtr<MathMLNamespaceContext>& ctxt)
340 {
341 mathmlContext = ctxt;
342 if (builder) builder->setMathMLNamespaceContext(mathmlContext);
343 }
344
345 SmartPtr<MathMLNamespaceContext>
getMathMLNamespaceContext(void) const346 View::getMathMLNamespaceContext(void) const
347 { return mathmlContext; }
348
349 #if GMV_ENABLE_BOXML
350 void
setBoxMLNamespaceContext(const SmartPtr<BoxMLNamespaceContext> & ctxt)351 View::setBoxMLNamespaceContext(const SmartPtr<BoxMLNamespaceContext>& ctxt)
352 {
353 boxmlContext = ctxt;
354 if (builder) builder->setBoxMLNamespaceContext(boxmlContext);
355 }
356
357 SmartPtr<BoxMLNamespaceContext>
getBoxMLNamespaceContext(void) const358 View::getBoxMLNamespaceContext(void) const
359 { return boxmlContext; }
360 #endif // GMV_ENABLE_BOXML
361
362 void
render(RenderingContext & ctxt,const scaled & x,const scaled & y) const363 View::render(RenderingContext& ctxt, const scaled& x, const scaled& y) const
364 {
365 //std::cerr << "View::render " << &ctxt << std::endl;
366 if (AreaRef rootArea = getRootArea())
367 {
368 Clock perf;
369 perf.Start();
370
371 // Basically (x, y) are the coordinates of the origin
372 rootArea->render(ctxt, x, y);
373
374 perf.Stop();
375 getLogger()->out(LOG_INFO, "rendering time: %dms", perf());
376 }
377 }
378
379 void
setDirtyLayout() const380 View::setDirtyLayout() const
381 {
382 if (SmartPtr<Element> elem = getRootElement())
383 {
384 //elem->setDirtyAttributeD();
385 elem->setDirtyLayoutD();
386 }
387 }
388
389 void
setDefaultFontSize(unsigned size)390 View::setDefaultFontSize(unsigned size)
391 {
392 assert(size > 0);
393 if (defaultFontSize != size)
394 {
395 defaultFontSize = size;
396 setDirtyLayout();
397 }
398 }
399
400 void
setAvailableWidth(const scaled & width)401 View::setAvailableWidth(const scaled& width)
402 {
403 if (width != availableWidth)
404 {
405 availableWidth = width;
406 setDirtyLayout();
407 }
408 }
409