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