1 /*******************************************************
2 
3    CoolReader Engine
4 
5    crskin.cpp: skinning file support
6 
7    (c) Vadim Lopatin, 2000-2008
8    This source code is distributed under the terms of
9    GNU General Public License
10    See LICENSE file for details
11 
12 *******************************************************/
13 
14 #include <stdlib.h>
15 #include "../include/crskin.h"
16 #include "../include/lvstsheet.h"
17 #include "../include/crtrace.h"
18 
19 // uncomment to trace skin XML access errors / not found elements
20 //#define TRACE_SKIN_ERRORS
21 
22 
23 class RecursionLimit
24 {
25 static int counter;
26 public:
test(int limit=15)27     bool test( int limit = 15 ) { return counter < limit; }
RecursionLimit()28     RecursionLimit() { counter++; }
~RecursionLimit()29     ~RecursionLimit() { counter--; }
30 };
31 int RecursionLimit::counter = 0;
32 
33 
34 /// decodes skin percent
fromSkinPercent(int x,int fullx)35 int fromSkinPercent( int x, int fullx )
36 {
37     if ( x>0 && (x & SKIN_COORD_PERCENT_FLAG) ) {
38         x ^= SKIN_COORD_PERCENT_FLAG;
39         return x * fullx / 10000;
40     } else {
41         if ( x<0 ) {
42             if ( !(x & SKIN_COORD_PERCENT_FLAG) ) {
43                 x ^= SKIN_COORD_PERCENT_FLAG;
44                 x = 10000-x;
45                 return x * fullx / 10000;
46             }
47             return fullx + x;
48         }
49         return x;
50     }
51 }
52 
53 /// decodes skin percent point to pixels (fullx is value corresponding to 100%)
fromSkinPercent(lvPoint pt,lvPoint fullpt)54 lvPoint fromSkinPercent( lvPoint pt, lvPoint fullpt )
55 {
56     lvPoint res;
57     res.x = fromSkinPercent( pt.x, fullpt.x );
58     res.y = fromSkinPercent( pt.y, fullpt.y );
59     return res;
60 }
61 
62 /// encodes percent value*100 (0..10000), to store in skin, from string like "75%" or "10"
toSkinPercent(const lString32 & value,int defValue,bool * res)63 int toSkinPercent( const lString32 & value, int defValue, bool * res )
64 {
65     // "75%" format - in percent
66     int p = value.pos("%");
67     int pvalue;
68     if ( p>0 ) {
69         if ( value.substr(0, p).atoi(pvalue) ) {
70             if ( res )
71                 *res = true;
72             return toSkinPercent(pvalue*100);
73         }
74     }
75     // "75px" format - in pixels
76     p = value.pos("px");
77     if ( p>0 ) {
78         if ( value.substr(0, p).atoi(pvalue) ) {
79             if ( res )
80                 *res = true;
81             return pvalue;
82         }
83     }
84     // simple "75" format
85     if ( value.atoi(pvalue) ) {
86         if ( res )
87             *res = true;
88         return pvalue;
89     }
90     return defValue;
91 }
92 
findByName(const lString32 & name)93 CRPageSkinRef CRPageSkinList::findByName( const lString32 & name )
94 {
95     for ( int i=0; i<length(); i++ ) {
96         if ( get(i)->getName()==name )
97             return get(i);
98     }
99     return CRPageSkinRef();
100 }
101 
CRPageSkin()102 CRPageSkin::CRPageSkin()
103 : _scrollSkin(new CRRectSkin())
104 , _leftPageSkin(new CRRectSkin())
105 , _rightPageSkin(new CRRectSkin())
106 , _singlePageSkin(new CRRectSkin())
107 , _name("Default")
108 {
109 
110 }
111 
getSkin(page_skin_type_t type)112 CRRectSkinRef CRPageSkin::getSkin( page_skin_type_t type )
113 {
114     switch ( type ) {
115     case PAGE_SKIN_SCROLL:
116         return _scrollSkin;
117     case PAGE_SKIN_LEFT_PAGE:
118         return _leftPageSkin;
119     case PAGE_SKIN_RIGHT_PAGE:
120         return _rightPageSkin;
121     case PAGE_SKIN_SINGLE_PAGE:
122         return _singlePageSkin;
123     default:
124         return _scrollSkin;
125     }
126 }
127 
128 
CRIconSkin()129 CRIconSkin::CRIconSkin()
130 : _bgcolor(0xFF000000) // transparent
131 , _hTransform(IMG_TRANSFORM_SPLIT)
132 , _vTransform(IMG_TRANSFORM_SPLIT)
133 , _splitPoint(-1, -1)
134 , _pos(0, 0)
135 , _size(toSkinPercent( 10000 ), toSkinPercent( 10000 )) // 100% x 100%
136 , _align(SKIN_VALIGN_TOP|SKIN_HALIGN_LEFT) // relative to top left
137 {
138 }
139 
getRect(lvRect & rc,const lvRect & baseRect)140 bool CRRectSkin::getRect( lvRect & rc, const lvRect & baseRect )
141 {
142     rc = baseRect;
143     lvPoint pos( fromSkinPercent( _pos.x, rc.width()),
144                  fromSkinPercent( _pos.y, rc.height()) );
145     lvPoint sz( fromSkinPercent( _size.x, rc.width()),
146                 fromSkinPercent( _size.y, rc.height()) );
147 
148     // left top corner -> origin point
149     if ( getHAlign()==SKIN_HALIGN_RIGHT )
150         pos.x = pos.x + sz.x;
151     else if ( getHAlign()==SKIN_HALIGN_CENTER ) {
152         pos.x = pos.x + sz.x / 2;
153     }
154     if ( getVAlign()==SKIN_VALIGN_BOTTOM )
155         pos.y = pos.y + sz.y;
156     else if ( getVAlign()==SKIN_VALIGN_CENTER ) {
157         pos.y = pos.y + sz.y/2;
158     }
159 
160     // apply size constraints
161     if ( _minsize.x>0 && sz.x < _minsize.x )
162         sz.x = _minsize.x;
163     if ( _minsize.y>0 && sz.y < _minsize.y )
164         sz.y = _minsize.y;
165     if ( _maxsize.x>0 && sz.x > _maxsize.x )
166         sz.x = _maxsize.x;
167     if ( _maxsize.y>0 && sz.y > _maxsize.y )
168         sz.y = _maxsize.y;
169 
170     // origin -> left top corner
171     if ( getHAlign()==SKIN_HALIGN_RIGHT )
172         pos.x = pos.x - sz.x;
173     else if ( getHAlign()==SKIN_HALIGN_CENTER ) {
174         pos.x = pos.x - sz.x / 2;
175     }
176     if ( getVAlign()==SKIN_VALIGN_BOTTOM )
177         pos.y = pos.y - sz.y;
178     else if ( getVAlign()==SKIN_VALIGN_CENTER ) {
179         pos.y = pos.y - sz.y/2;
180     }
181 
182     pos.x += baseRect.left;
183     pos.y += baseRect.top;
184     rc.left = pos.x;
185     rc.top = pos.y;
186     rc.right = pos.x + sz.x;
187     rc.bottom = pos.y + sz.y;
188     return true;
189 }
190 
draw(LVDrawBuf & buf,const lvRect & rc)191 void CRIconSkin::draw( LVDrawBuf & buf, const lvRect & rc )
192 {
193     int dx = _image.isNull() ? 0 : _image->GetWidth();
194     int dy = _image.isNull() ? 0 : _image->GetHeight();
195     lvRect rc2(rc);
196     rc2.left = rc.left + fromSkinPercent( _pos.x, rc.width() );
197     rc2.top = rc.top + fromSkinPercent( _pos.y, rc.height() );
198     rc2.right = rc2.left + fromSkinPercent( _size.x, rc.width() );
199     rc2.bottom = rc2.top + fromSkinPercent( _size.y, rc.height() );
200     if ( _hTransform==IMG_TRANSFORM_NONE ) {
201         int ddx = rc2.width()-dx;
202         if ( getHAlign()==SKIN_HALIGN_RIGHT )
203             rc2.left = rc2.right - dx;
204         else if ( getHAlign()==SKIN_HALIGN_CENTER ) {
205             rc2.left += ddx/2;
206             rc2.right = rc2.left + dx;
207         } else
208             rc2.right = rc2.left + dx;
209     }
210     if ( _vTransform==IMG_TRANSFORM_NONE ) {
211         int ddy = rc2.height()-dy;
212         if ( getVAlign()==SKIN_VALIGN_BOTTOM )
213             rc2.top = rc2.bottom - dy;
214         else if ( getVAlign()==SKIN_VALIGN_CENTER ) {
215             rc2.top += ddy/2;
216             rc2.bottom = rc2.top + dy;
217         } else
218             rc2.bottom = rc2.top + dy;
219     }
220     if ( _image.isNull() ) {
221         if ( ((_bgcolor>>24)&255) != 255 )
222             buf.FillRect( rc2, _bgcolor );
223     } else {
224         LVImageSourceRef img = LVCreateStretchFilledTransform( _image,
225             rc2.width(), rc2.height(), _hTransform, _vTransform, _splitPoint.x, _splitPoint.y );
226         LVDrawStateSaver saver(buf);
227 		lvRect oldClip;
228 		buf.GetClipRect(&oldClip);
229 		if (oldClip.isEmpty())
230 			buf.SetClipRect(&rc);
231 		else if (oldClip.intersect(rc))
232 			buf.SetClipRect(&oldClip);
233 		else
234 			return;
235         buf.Draw( img, rc2.left, rc2.top, rc2.width(), rc2.height(), false );
236     }
237 }
238 
draw(LVDrawBuf & buf,const lvRect & rc)239 void CRIconList::draw( LVDrawBuf & buf, const lvRect & rc )
240 {
241     //CRLog::trace("enter CRIconList::draw(%d images)", _list.length());
242     for ( int i=0; i<_list.length(); i++ )
243         _list[i]->draw( buf, rc );
244     //CRLog::trace("exit CRIconList::draw()");
245 }
246 
247 /// retuns path to base definition, if attribute base="#nodeid" is specified for element of path
getBasePath(const lChar32 * path)248 lString32 CRSkinContainer::getBasePath( const lChar32 * path )
249 {
250     lString32 res;
251     ldomXPointer p = getXPointer( lString32( path ) );
252     if ( !p )
253         return res;
254     if ( !p.getNode()->isElement() )
255         return res;
256     lString32 value = p.getNode()->getAttributeValue("base");
257     if (value.empty() || value[0] != '#')
258         return res;
259     res = pathById( value.c_str() + 1 );
260     crtrace log;
261     log << "CRSkinContainer::getBasePath( " << lString32( path ) << " ) = " << res;
262     return res;
263 }
264 
265 /// skin file support
266 class CRSkinImpl : public CRSkinContainer
267 {
268 protected:
269     LVContainerRef _container;
270     LVAutoPtr<ldomDocument> _doc;
271     LVCacheMap<lString32,LVImageSourceRef> _imageCache;
272     LVCacheMap<lString32,CRRectSkinRef> _rectCache;
273     LVCacheMap<lString32,CRScrollSkinRef> _scrollCache;
274     LVCacheMap<lString32,CRWindowSkinRef> _windowCache;
275     LVCacheMap<lString32,CRMenuSkinRef> _menuCache;
276     LVCacheMap<lString32,CRPageSkinRef> _pageCache;
277     LVCacheMap<lString32,CRToolBarSkinRef> _toolbarCache;
278     CRPageSkinListRef _pageSkinList;
279 public:
280     /// returns scroll skin by path or #id
281     virtual CRScrollSkinRef getScrollSkin( const lChar32 * path );
282     /// returns rect skin by path or #id
283     virtual CRRectSkinRef getRectSkin( const lChar32 * path );
284     /// returns window skin by path or #id
285     virtual CRWindowSkinRef getWindowSkin( const lChar32 * path );
286     /// returns menu skin by path or #id
287     virtual CRMenuSkinRef getMenuSkin( const lChar32 * path );
288     /// returns book page skin by path or #id
289     virtual CRPageSkinRef getPageSkin( const lChar32 * path );
290     /// returns book page skin list
291     virtual CRPageSkinListRef getPageSkinList();
292     /// return ToolBar skin by path or #id
293 	virtual CRToolBarSkinRef getToolBarSkin( const lChar32 * path );
294     /// get DOM path by id
295     virtual lString32 pathById( const lChar32 * id );
296     /// gets image from container
297     virtual LVImageSourceRef getImage( const lChar32 * filename );
298     /// gets doc pointer by asolute path
getXPointer(const lString32 & xPointerStr)299     virtual ldomXPointer getXPointer( const lString32 & xPointerStr ) { return _doc->createXPointer( xPointerStr ); }
300     /// garbage collection
gc()301     virtual void gc()
302     {
303         _imageCache.clear();
304     }
305     /// constructor does nothing
CRSkinImpl()306     CRSkinImpl()  : _imageCache(8), _rectCache(8), _scrollCache(1), _windowCache(8), _menuCache(8), _pageCache(8), _toolbarCache(2) { }
~CRSkinImpl()307     virtual ~CRSkinImpl(){ }
308     // open from container
309     virtual bool open( LVContainerRef container );
310     virtual bool open( lString8 simpleXml );
311 };
312 
313 
314 /* XPM */
315 static const char *menu_item_background[] = {
316 /* width height num_colors chars_per_pixel */
317 "44 48 5 1",
318 /* colors */
319 "  c None",
320 ". c #000000",
321 "o c #555555",
322 "0 c #AAAAAA",
323 "# c #ffffff",
324 /* pixels               ..                       */
325 "                                            ",
326 "                                            ",
327 "                                            ",
328 "                                            ",
329 "oooooooooooooooooooooooooooooooooooooooooooo",
330 "oooooooooooooooooooooooooooooooooooooooooooo",
331 "oooooooooooooooooooooooooooooooooooooooooooo",
332 "ooo######################################ooo",
333 "ooo######################################ooo",
334 "ooo######################################ooo",
335 "ooo######################################ooo",
336 "ooo######################################ooo",
337 "ooo######################################ooo",
338 "ooo######################################ooo",
339 "ooo######################################ooo",
340 "ooo######################################ooo",
341 "ooo######################################ooo",
342 "ooo######################################ooo",
343 "ooo######################################ooo",
344 "ooo######################################ooo",
345 "ooo######################################ooo",
346 "ooo######################################ooo",
347 "ooo######################################ooo",
348 "ooo######################################ooo",
349 "ooo######################################ooo",
350 "ooo######################################ooo",
351 "ooo######################################ooo",
352 "ooo######################################ooo",
353 "ooo######################################ooo",
354 "ooo######################################ooo",
355 "ooo######################################ooo",
356 "ooo######################################ooo",
357 "ooo######################################ooo",
358 "ooo######################################ooo",
359 "ooo######################################ooo",
360 "ooo######################################ooo",
361 "ooo######################################ooo",
362 "ooo######################################ooo",
363 "ooo######################################ooo",
364 "ooo######################################ooo",
365 "ooo######################################ooo",
366 "oooooooooooooooooooooooooooooooooooooooooooo",
367 "oooooooooooooooooooooooooooooooooooooooooooo",
368 "oooooooooooooooooooooooooooooooooooooooooooo",
369 "                                            ",
370 "                                            ",
371 "                                            ",
372 "                                            ",
373 };
374 
375 /* XPM */
376 static const char *menu_shortcut_background[] = {
377 /* width height num_colors chars_per_pixel */
378 "36 48 5 1",
379 /* colors */
380 "  c None",
381 ". c #000000",
382 "o c #555555",
383 "0 c #AAAAAA",
384 "# c #ffffff",
385 /* pixels               ..                       */
386 "                                    ",
387 "                                    ",
388 "                                    ",
389 "                                    ",
390 "                oooooooooooooooooooo",
391 "             ooooooooooooooooooooooo",
392 "          oooooooooooooooooooooooooo",
393 "        oooooooo####################",
394 "      oooooo########################",
395 "     oooo###########################",
396 "    oooo############################",
397 "   ooo##############################",
398 "   ooo##############################",
399 "  ooo###############################",
400 "  ooo###############################",
401 "  ooo###############################",
402 " ooo################################",
403 " ooo################################",
404 " ooo################################",
405 "ooo#################################",
406 "ooo#################################",
407 "ooo#################################",
408 "ooo#################################",
409 "ooo#################################",//==
410 "ooo#################################",//==
411 "ooo#################################",
412 "ooo#################################",
413 "ooo#################################",
414 "ooo#################################",
415 " ooo################################",
416 " ooo################################",
417 " ooo################################",
418 "  ooo###############################",
419 "  ooo###############################",
420 "  ooo###############################",
421 "   ooo##############################",
422 "   ooo##############################",
423 "    ooo#############################",
424 "     oooo###########################",
425 "      oooooo########################",
426 "       oooooooo#####################",
427 "         ooooooooooooooooooooooooooo",
428 "            oooooooooooooooooooooooo",
429 "               ooooooooooooooooooooo",
430 "                                    ",
431 "                                    ",
432 "                                    ",
433 "                                    ",
434 };
435 
436 
437 typedef struct {
438     const lChar32 * filename;
439     const char * * xpm;
440 } standard_image_item_t;
441 
442 static standard_image_item_t standard_images [] = {
443     { U"std_menu_shortcut_background.xpm", menu_shortcut_background },
444     { U"std_menu_item_background.xpm", menu_item_background },
445     { NULL, NULL }
446 };
447 
448 /// gets image from container
getImage(const lChar32 * filename)449 LVImageSourceRef CRSkinImpl::getImage(  const lChar32 * filename  )
450 {
451     LVImageSourceRef res;
452     lString32 fn( filename );
453     if ( _imageCache.get( fn, res ) )
454         return res; // found in cache
455 
456     bool standard = false;
457     for ( int i=0; standard_images[i].filename; i++ )
458         if ( !lStr_cmp( filename, standard_images[i].filename ) ) {
459             res = LVCreateXPMImageSource( standard_images[i].xpm );
460             standard = true;
461         }
462     if ( !standard && !!_container ) {
463         LVStreamRef stream = _container->OpenStream( filename, LVOM_READ );
464         if ( !!stream ) {
465             if ( stream->GetSize() < MAX_SKIN_IMAGE_CACHE_ITEM_RAM_COPY_PACKED_SIZE )
466                 res = LVCreateStreamCopyImageSource( stream );
467             else
468                 res = LVCreateStreamImageSource( stream );
469             // try to hold unpacked image, if small enough
470             res = LVCreateUnpackedImageSource( res, MAX_SKIN_IMAGE_CACHE_ITEM_UNPACKED_SIZE, COLOR_BACKBUFFER==0 );
471         }
472     }
473     // add found image to cache
474     _imageCache.set( fn, res );
475     return res;
476 }
477 
478 // open from container
open(LVContainerRef container)479 bool CRSkinImpl::open( LVContainerRef container )
480 {
481     if ( container.isNull() )
482         return false;
483     LVStreamRef stream = container->OpenStream( U"cr3skin.xml", LVOM_READ );
484     if ( stream.isNull() ) {
485         CRLog::error("cannot open skin: cr3skin.xml not found");
486         return false;
487     }
488     ldomDocument * doc = LVParseXMLStream( stream );
489     if ( !doc ) {
490         CRLog::error("cannot open skin: error while parsing cr3skin.xml");
491         return false;
492     }
493     _doc = doc;
494     _container = container;
495     return true;
496 }
497 
open(lString8 simpleXml)498 bool CRSkinImpl::open( lString8 simpleXml )
499 {
500     LVStreamRef stream = LVCreateStringStream( simpleXml );
501     ldomDocument * doc = LVParseXMLStream( stream );
502     if ( !doc ) {
503         CRLog::error("cannot open skin: error while parsing skin xml");
504         return false;
505     }
506     _doc = doc;
507     return true;
508 }
509 
510 /// reads string value from attrname attribute of element specified by path, returns empty string if not found
readString(const lChar32 * path,const lChar32 * attrname,bool * res)511 lString32 CRSkinContainer::readString( const lChar32 * path, const lChar32 * attrname, bool * res )
512 {
513     ldomXPointer ptr = getXPointer( path );
514     if ( !ptr )
515         return lString32::empty_str;
516     if ( !ptr.getNode()->isElement() )
517         return lString32::empty_str;
518 	//lString32 pnname = ptr.getNode()->getParentNode()->getNodeName();
519 	//lString32 nname = ptr.getNode()->getNodeName();
520     lString32 value = ptr.getNode()->getAttributeValue(attrname);
521 	if ( res )
522 		*res = true;
523     return value;
524 }
525 
526 /// reads string value from attrname attribute of element specified by path, returns defValue if not found
readString(const lChar32 * path,const lChar32 * attrname,const lString32 & defValue,bool * res)527 lString32 CRSkinContainer::readString( const lChar32 * path, const lChar32 * attrname, const lString32 & defValue, bool * res )
528 {
529     lString32 value = readString( path, attrname );
530     if ( value.empty() )
531         return defValue;
532 	if ( res )
533 		*res = true;
534     return value;
535 }
536 
537 /// reads color value from attrname attribute of element specified by path, returns defValue if not found
readColor(const lChar32 * path,const lChar32 * attrname,lUInt32 defValue,bool * res)538 lUInt32 CRSkinContainer::readColor( const lChar32 * path, const lChar32 * attrname, lUInt32 defValue, bool * res  )
539 {
540     lString32 value = readString( path, attrname );
541     if ( value.empty() )
542         return defValue;
543     css_length_t cv;
544     lString8 buf = UnicodeToUtf8(value);
545     const char * bufptr = buf.modify();
546     if ( !parse_color_value( bufptr, cv ) )
547         return defValue;
548 	if ( res )
549 		*res = true;
550     return cv.value;
551 }
552 
553 /// reads rect value from attrname attribute of element specified by path, returns defValue if not found
readRect(const lChar32 * path,const lChar32 * attrname,lvRect defValue,bool * res)554 lvRect CRSkinContainer::readRect( const lChar32 * path, const lChar32 * attrname, lvRect defValue, bool * res )
555 {
556     lString32 value = readString( path, attrname );
557     if ( value.empty() )
558         return defValue;
559     lvRect p = defValue;
560     lString32 s1, s2, s3, s4, s;
561     s = value;
562     if ( !s.split2(",", s1, s2) )
563         return p;
564     s1.trim();
565     s2.trim();
566     s = s2;
567     if ( !s.split2(",", s2, s3) )
568         return p;
569     s2.trim();
570     s3.trim();
571     s = s3;
572     if ( !s.split2(",", s3, s4) )
573         return p;
574     s3.trim();
575     s4.trim();
576 
577     bool b1=false;
578     bool b2=false;
579     bool b3=false;
580     bool b4=false;
581     p.left = toSkinPercent( s1, defValue.left, &b1 );
582     p.top = toSkinPercent( s2, defValue.top, &b2 );
583     p.right = toSkinPercent( s3, defValue.right, &b3 );
584     p.bottom = toSkinPercent( s4, defValue.bottom, &b4 );
585     if ( b1 && b2 && b3 && b4) {
586         if ( res )
587             *res = true;
588         return p;
589     }
590     return defValue;
591 }
592 
593 /// reads boolean value from attrname attribute of element specified by path, returns defValue if not found
readBool(const lChar32 * path,const lChar32 * attrname,bool defValue,bool * res)594 bool CRSkinContainer::readBool( const lChar32 * path, const lChar32 * attrname, bool defValue, bool * res )
595 {
596     lString32 value = readString( path, attrname );
597     if (value.empty())
598         return defValue;
599     if (value == "true" || value == "yes")
600         return true;
601     if (value == "false" || value == "no")
602         return false;
603 	if ( res )
604 		*res = true;
605     return defValue;
606 }
607 
608 /// reads image transform value from attrname attribute of element specified by path, returns defValue if not found
readTransform(const lChar32 * path,const lChar32 * attrname,ImageTransform defValue,bool * res)609 ImageTransform CRSkinContainer::readTransform( const lChar32 * path, const lChar32 * attrname, ImageTransform defValue, bool * res )
610 {
611     lString32 value = readString( path, attrname );
612     if ( value.empty() )
613         return defValue;
614     value.lowercase();
615     if (value == "none") {
616         if ( res )
617             *res = true;
618         return IMG_TRANSFORM_NONE;
619     }
620     if (value == "split") {
621         if ( res )
622             *res = true;
623         return IMG_TRANSFORM_SPLIT;
624     }
625     if (value == "stretch") {
626         if ( res )
627             *res = true;
628         return IMG_TRANSFORM_STRETCH;
629     }
630     if (value == "tile") {
631         if ( res )
632             *res = true;
633         return IMG_TRANSFORM_TILE;
634     }
635     // invalid value
636     return defValue;
637 }
638 
639 /// reads h align value from attrname attribute of element specified by path, returns defValue if not found
readHAlign(const lChar32 * path,const lChar32 * attrname,int defValue,bool * res)640 int CRSkinContainer::readHAlign( const lChar32 * path, const lChar32 * attrname, int defValue, bool * res )
641 {
642     lString32 value = readString( path, attrname );
643     if (value.empty())
644         return defValue;
645     if (value == "left") {
646 		if ( res )
647 			*res = true;
648         return SKIN_HALIGN_LEFT;
649 	}
650     if (value == "center") {
651 		if ( res )
652 			*res = true;
653         return SKIN_HALIGN_CENTER;
654 	}
655     if (value == "right") {
656 		if ( res )
657 			*res = true;
658         return SKIN_HALIGN_RIGHT;
659 	}
660     // invalid value
661     return defValue;
662 }
663 
664 /// reads h align value from attrname attribute of element specified by path, returns defValue if not found
readVAlign(const lChar32 * path,const lChar32 * attrname,int defValue,bool * res)665 int CRSkinContainer::readVAlign( const lChar32 * path, const lChar32 * attrname, int defValue, bool * res )
666 {
667     lString32 value = readString( path, attrname );
668     if (value.empty())
669         return defValue;
670     if (value == "top") {
671 		if ( res )
672 			*res = true;
673         return SKIN_VALIGN_TOP;
674 	}
675     if (value == "center") {
676 		if ( res )
677 			*res = true;
678         return SKIN_VALIGN_CENTER;
679 	}
680     if (value == "bottom") {
681 		if ( res )
682 			*res = true;
683         return SKIN_VALIGN_BOTTOM;
684 	}
685     // invalid value
686     return defValue;
687 }
688 
689 /// reads int value from attrname attribute of element specified by path, returns defValue if not found
readInt(const lChar32 * path,const lChar32 * attrname,int defValue,bool * res)690 int CRSkinContainer::readInt( const lChar32 * path, const lChar32 * attrname, int defValue, bool * res )
691 {
692     lString32 value = readString( path, attrname );
693     if ( value.empty() )
694         return defValue;
695     value.trim();
696     return toSkinPercent( value, defValue, res);
697 }
698 
699 /// reads point(size) value from attrname attribute of element specified by path, returns defValue if not found
readSize(const lChar32 * path,const lChar32 * attrname,lvPoint defValue,bool * res)700 lvPoint CRSkinContainer::readSize( const lChar32 * path, const lChar32 * attrname, lvPoint defValue, bool * res )
701 {
702     lString32 value = readString( path, attrname );
703     if ( value.empty() )
704         return defValue;
705     lvPoint p = defValue;
706     lString32 s1, s2;
707     if ( !value.split2(",", s1, s2) )
708         return p;
709     s1.trim();
710     s2.trim();
711     bool b1=false;
712     bool b2=false;
713     p.x = toSkinPercent( s1, defValue.x, &b1 );
714     p.y = toSkinPercent( s2, defValue.y, &b2 );
715     if ( b1 && b2 ) {
716         if ( res )
717             *res = true;
718         return p;
719     }
720     return defValue;
721 }
722 
723 /// reads rect value from attrname attribute of element specified by path, returns null ref if not found
readImage(const lChar32 * path,const lChar32 * attrname,bool * r)724 LVImageSourceRef CRSkinContainer::readImage( const lChar32 * path, const lChar32 * attrname, bool * r )
725 {
726     lString32 value = readString( path, attrname );
727     if ( value.empty() ) {
728 #ifdef TRACE_SKIN_ERRORS
729         crtrace log;
730         log << "CRSkinContainer::readImage( " << path << ", " << attrname << ") - attribute or element not found";
731 #endif
732         return LVImageSourceRef();
733     }
734     LVImageSourceRef res = getImage( value );
735     if ( res.isNull() ) {
736 #ifdef TRACE_SKIN_ERRORS
737         crtrace log;
738         log << "Image " << value << " cannot be read";
739 #endif
740 	} else {
741 		if ( r )
742 			*r = true;
743 	}
744     return res;
745 }
746 
747 /// reads rect value from attrname attribute of element specified by path, returns null ref if not found
readIcons(const lChar32 * path,bool * r)748 CRIconListRef CRSkinContainer::readIcons( const lChar32 * path, bool * r )
749 {
750     CRIconListRef list = CRIconListRef(new CRIconList() );
751     for ( int i=1; i<16; i++ ) {
752         lString32 p = lString32(path) << "[" << fmt::decimal(i) << "]";
753         CRIconSkin * icon = new CRIconSkin();
754         if ( readIconSkin(p.c_str(), icon ) )
755             list->add( CRIconSkinRef(icon) );
756         else {
757             delete icon;
758             break;
759         }
760     }
761     if ( list->length()==0 ) {
762 #ifdef TRACE_SKIN_ERRORS
763         crtrace log;
764         log << "CRSkinContainer::readIcons( " << path << ") - cannot read icon from specified path";
765 #endif
766         return CRIconListRef();
767     }
768     if ( r )
769         *r = true;
770     return list;
771 }
772 
773 /// open simple skin, without image files, from string
LVOpenSimpleSkin(const lString8 & xml)774 CRSkinRef LVOpenSimpleSkin( const lString8 & xml )
775 {
776     CRSkinImpl * skin = new CRSkinImpl();
777     CRSkinRef res( skin );
778     if ( !skin->open( xml ) )
779         return CRSkinRef();
780     //CRLog::trace("skin xml opened ok");
781     return res;
782 }
783 
784 /// opens skin from directory or .zip file
LVOpenSkin(const lString32 & pathname)785 CRSkinRef LVOpenSkin( const lString32 & pathname )
786 {
787     LVContainerRef container = LVOpenDirectory( pathname.c_str() );
788     if ( !container ) {
789         LVStreamRef stream = LVOpenFileStream( pathname.c_str(), LVOM_READ );
790         if ( stream.isNull() ) {
791             CRLog::error("cannot open skin: specified archive or directory not found");
792             return CRSkinRef();
793         }
794         container = LVOpenArchieve( stream );
795         if ( !container ) {
796             CRLog::error("cannot open skin: specified archive or directory not found");
797             return CRSkinRef();
798         }
799     }
800     CRSkinImpl * skin = new CRSkinImpl();
801     CRSkinRef res( skin );
802     if ( !skin->open( container ) )
803         return CRSkinRef();
804     CRLog::trace("skin container %s opened ok", LCSTR(pathname) );
805     return res;
806 }
807 
808 // default parameters
809 //LVFontRef CRSkinnedItem::getFont() { return fontMan->GetFont( 24, 300, false, css_ff_sans_serif, cs8("Arial")) }
810 
draw(LVDrawBuf & buf,const lvRect & rc)811 void CRSkinnedItem::draw( LVDrawBuf & buf, const lvRect & rc )
812 {
813     SAVE_DRAW_STATE( buf );
814 	buf.SetBackgroundColor( getBackgroundColor() );
815 	buf.SetTextColor( getTextColor() );
816     //CRLog::trace("CRSkinnedItem::draw before getBgIcons()");
817     CRIconListRef bgimg = getBgIcons();
818 	if ( bgimg.isNull() ) {
819         //buf.FillRect( rc, getBackgroundColor() );
820 	} else {
821         bgimg->draw( buf, rc );
822 	}
823 }
824 
825 
getClientRect(const lvRect & windowRect)826 lvRect CRRectSkin::getClientRect( const lvRect &windowRect )
827 {
828     lvRect rc = windowRect;
829     lvRect border = getBorderWidths();
830     rc.left += border.left;
831     rc.top += border.top;
832     rc.right -= border.right;
833     rc.bottom -= border.bottom;
834     return rc;
835 }
836 
getTitleRect(const lvRect & windowRect)837 lvRect CRWindowSkin::getTitleRect( const lvRect &windowRect )
838 {
839     lvRect rc = CRRectSkin::getClientRect( windowRect );
840     lvPoint tsz = getTitleSize();
841     rc.bottom = rc.top + tsz.y;
842     rc.left = rc.left + tsz.x;
843     return rc;
844 }
845 
getClientRect(const lvRect & windowRect)846 lvRect CRWindowSkin::getClientRect( const lvRect &windowRect )
847 {
848 	lvRect rc = CRRectSkin::getClientRect( windowRect );
849     lvPoint tsz = getTitleSize();
850 	rc.top += tsz.y;
851 	rc.left += tsz.x;
852 	return rc;
853 }
854 
855 /// returns necessary window size for specified client size
getWindowSize(const lvPoint & clientSize)856 lvPoint CRWindowSkin::getWindowSize( const lvPoint & clientSize )
857 {
858     lvRect borders = getBorderWidths();
859     lvPoint tsz = getTitleSize();
860     return lvPoint( clientSize.x + borders.left + borders.right + tsz.x, clientSize.y + borders.top + borders.bottom + tsz.y );
861 }
862 
CRSkinnedItem()863 CRSkinnedItem::CRSkinnedItem()
864 :   _textcolor( 0x000000 )
865 ,   _fontFace("Arial")
866 ,   _fontSize( 24 )
867 ,   _fontBold( false )
868 ,   _fontItalic( false )
869 ,   _textAlign( 0 )
870 {
871 }
872 
setFontFace(lString32 face)873 void CRSkinnedItem::setFontFace( lString32 face )
874 {
875     if ( _fontFace != face ) {
876         _fontFace = face;
877         _font.Clear();
878     }
879 }
880 
setFontSize(int size)881 void CRSkinnedItem::setFontSize( int size )
882 {
883     if ( _fontSize != size ) {
884         _fontSize = size;
885         _font.Clear();
886     }
887 }
888 
setFontBold(bool bold)889 void CRSkinnedItem::setFontBold( bool bold )
890 {
891     if ( _fontBold != bold ) {
892         _fontBold = bold;
893         _font.Clear();
894     }
895 }
896 
setFontItalic(bool italic)897 void CRSkinnedItem::setFontItalic( bool italic )
898 {
899     if ( _fontItalic != italic ) {
900         _fontItalic = italic;
901         _font.Clear();
902     }
903 }
904 
getFont()905 LVFontRef CRSkinnedItem::getFont()
906 {
907     if ( _font.isNull() ) {
908         _font = fontMan->GetFont( _fontSize, _fontBold ? 700 : 400, _fontItalic, css_ff_sans_serif, UnicodeToUtf8(_fontFace) );
909     }
910     return _font;
911 }
912 
measureText(lString32 text)913 lvPoint CRSkinnedItem::measureText( lString32 text )
914 {
915     int th = getFont()->getHeight();
916     int tw = getFont()->getTextWidth( text.c_str(), text.length() );
917     return lvPoint( tw, th );
918 }
919 
wrapLine(lString32Collection & dst,lString32 stringToSplit,int maxWidth,LVFontRef font)920 static void wrapLine( lString32Collection & dst, lString32 stringToSplit, int maxWidth, LVFontRef font )
921 {
922     lString32 str = stringToSplit;
923     int w = font->getTextWidth( str.c_str(), str.length() );
924     if ( w<=maxWidth ) {
925         dst.add( str );
926         return;
927     }
928     for ( ;!str.empty(); ) {
929         int wpos = 1;
930         int wquality = 0;
931         for ( int i=str.length(); i>=0; i-- ) {
932             lChar32 ch = str[i];
933             if ( ch!=' ' && ch!=0 && wpos>1 )
934                 continue;
935             lChar32 prevChar = i>0 ? str[i-1] : 0;
936             w = font->getTextWidth( str.c_str(), i );
937             int q = 0;
938             if ( ch!=' ' && ch!=0 )
939                 q = 1;
940             else
941                 q = (prevChar=='.' || prevChar==',' || prevChar==';'
942                      || prevChar=='!'  || prevChar=='?'
943                      ) ? (w<maxWidth*2/3 ? 3 : 2) : 2;
944             if ( q>wquality && w<maxWidth ) {
945                 wquality = q;
946                 wpos = i;
947             }
948             if ( wquality>1 && w<=maxWidth*2/3 )
949                 break;
950         }
951         lString32 s = str.substr(0, wpos);
952         s.trim();
953         if ( s.length()>0 )
954             dst.add( s );
955         str = str.substr( wpos );
956         str.trim();
957     }
958 }
959 
measureTextItem(lString32 text)960 lvPoint CRRectSkin::measureTextItem( lString32 text )
961 {
962     lvPoint sz = CRSkinnedItem::measureText( text );
963     sz.x += _margins.left + _margins.right;
964     sz.y += _margins.top + _margins.bottom;
965     if ( _minsize.x > 0 && sz.x < _minsize.x )
966         sz.x = _minsize.x;
967     if ( _minsize.y > 0 && sz.y < _minsize.y )
968         sz.y = _minsize.y;
969     return sz;
970 }
971 
drawText(LVDrawBuf & buf,const lvRect & rc,lString32 text,LVFontRef font,lUInt32 textColor,lUInt32 bgColor,int flags)972 void CRSkinnedItem::drawText( LVDrawBuf & buf, const lvRect & rc, lString32 text, LVFontRef font, lUInt32 textColor, lUInt32 bgColor, int flags )
973 {
974     SAVE_DRAW_STATE( buf );
975     if ( font.isNull() )
976         font = getFont();
977     if ( font.isNull() )
978         return;
979     lString32Collection lines;
980     lString32 tabText;
981     int tabPos = text.pos("\t");
982     if ( tabPos>=0 ) {
983         if ( flags & SKIN_EXTEND_TAB ) {
984             tabText = text.substr( tabPos+1 );
985             text = text.substr( 0, tabPos );
986         } else {
987             text[tabPos] = U' ';
988         }
989     }
990     lString32 cr("\n");
991     if ( flags & SKIN_WORD_WRAP ) {
992         lString32Collection crlines;
993         lString32 s1, s2;
994         while ( text.split2( cr, s1, s2 ) ) {
995             crlines.add(s1);
996             text = s2;
997         }
998         crlines.add( text );
999         for ( int i=0; i<crlines.length(); i++ ) {
1000             wrapLine( lines, crlines[i], rc.width(), font );
1001         }
1002     } else {
1003         lString32 s = text;
1004         while ( s.replace( cr, cs32(" ") ) )
1005             ;
1006         lines.add( s );
1007     }
1008     buf.SetTextColor( textColor );
1009     buf.SetBackgroundColor( bgColor );
1010     lvRect oldRc;
1011     buf.GetClipRect( &oldRc );
1012     buf.SetClipRect( &rc );
1013     int lh = font->getHeight();
1014     int th = lh * lines.length();
1015     int ttw = tabText.empty() ? 0 : font->getTextWidth( tabText.c_str(), tabText.length() );
1016 
1017     int halign = tabText.empty() ? (flags & SKIN_HALIGN_MASK) : SKIN_HALIGN_LEFT;
1018     int valign = flags & SKIN_VALIGN_MASK;
1019 
1020     lvRect txtrc = rc;
1021     int y = txtrc.top;
1022     int dy = txtrc.height() - th;
1023     if ( valign == SKIN_VALIGN_CENTER )
1024         y += dy / 2;
1025     else if ( valign == SKIN_VALIGN_BOTTOM )
1026         y += dy;
1027 
1028     for ( int i=0; i<lines.length(); i++ ) {
1029         lString32 s = lines[i];
1030         int tw = font->getTextWidth( s.c_str(), s.length() );
1031         int x = txtrc.left;
1032         int dx = txtrc.width() - tw;
1033         if ( halign == SKIN_HALIGN_CENTER )
1034             x += dx / 2;
1035         else if ( halign == SKIN_HALIGN_RIGHT )
1036             x += dx;
1037 
1038 
1039         font->DrawTextString( &buf, x, y, s.c_str(), s.length(), U'?', NULL, false, 0 );
1040         if ( !tabText.empty() ) {
1041             font->DrawTextString( &buf, txtrc.right-ttw, y, tabText.c_str(), tabText.length(), U'?', NULL, false, 0 );
1042             tabText.clear();
1043         }
1044         y = y + lh;
1045     }
1046     buf.SetClipRect( &oldRc );
1047 }
1048 
drawText(LVDrawBuf & buf,const lvRect & rc,lString32 text,LVFontRef font)1049 void CRRectSkin::drawText( LVDrawBuf & buf, const lvRect & rc, lString32 text, LVFontRef font )
1050 {
1051     lvRect rect = getClientRect( rc );
1052     CRSkinnedItem::drawText( buf, rect, text, font );
1053 }
drawText(LVDrawBuf & buf,const lvRect & rc,lString32 text)1054 void CRRectSkin::drawText( LVDrawBuf & buf, const lvRect & rc, lString32 text )
1055 {
1056     lvRect rect = CRRectSkin::getClientRect( rc );
1057     CRSkinnedItem::drawText( buf, rect, text );
1058 }
1059 
drawButton(LVDrawBuf & buf,const lvRect & rect,int flags)1060 void CRButtonSkin::drawButton( LVDrawBuf & buf, const lvRect & rect, int flags )
1061 {
1062     lvRect rc = rect;
1063     rc.shrinkBy( _margins );
1064     LVImageSourceRef btnImage = getImage(flags);
1065     if ( !btnImage.isNull() ) {
1066         LVImageSourceRef img = LVCreateStretchFilledTransform( btnImage,
1067             rc.width(), rc.height() );
1068         buf.Draw( btnImage, rc.left, rc.top, rc.width(), rc.height(), false );
1069     }
1070 }
1071 
getImage(int flags)1072 LVImageSourceRef CRButtonSkin::getImage(int flags)
1073 {
1074     LVImageSourceRef btnImage;
1075 
1076     if ( flags & ENABLED ) {
1077         if ( flags & PRESSED )
1078             btnImage = _pressedimage;
1079         else if ( flags & SELECTED )
1080             btnImage = _selectedimage;
1081         else
1082             btnImage = _normalimage;
1083     } else
1084         btnImage = _disabledimage;
1085     if ( btnImage.isNull() )
1086         btnImage = _normalimage;
1087 	return btnImage;
1088 }
1089 
drawScroll(LVDrawBuf & buf,const lvRect & rect,bool vertical,int pos,int maxpos,int pagesize)1090 void CRScrollSkin::drawScroll( LVDrawBuf & buf, const lvRect & rect, bool vertical, int pos, int maxpos, int pagesize )
1091 {
1092     lvRect rc = rect;
1093 
1094     draw( buf, rc );
1095 
1096     int pages = pagesize>0 ? (maxpos+pagesize-1)/pagesize : 0;
1097     int page = pages>0 ? pos/pagesize+1 : 0;
1098 
1099     if ( !_bottomTabSkin.isNull() && !_bottomPageBoundSkin.isNull() &&
1100          !_bottomActiveTabSkin.isNull() ) {
1101         // tabs
1102         if ( pages<=1 )
1103             return; // don't draw tabs if no other pages
1104         int tabwidth = _bottomTabSkin->getMinSize().x;
1105         if ( tabwidth<40 )
1106             tabwidth = 40;
1107         if ( tabwidth>_bottomTabSkin->getMaxSize().x && _bottomTabSkin->getMaxSize().x>0)
1108             tabwidth = _bottomTabSkin->getMaxSize().x;
1109         int maxtabs = rc.width()-_margins.left-_margins.right / tabwidth;
1110         if ( pages <= maxtabs ) {
1111             // can draw tabs
1112             lvRect r(rc);
1113             r.left += _margins.left;
1114             for ( int i=0; i<pages; i++ ) {
1115                 r.right = r.left + tabwidth;
1116                 if ( i+1!=page ) {
1117                     _bottomTabSkin->draw(buf, r);
1118                     lString32 label = lString32::itoa(i+1);
1119                     _bottomTabSkin->drawText(buf, r, label);
1120                 }
1121                 r.left += tabwidth - r.height()/6;
1122             }
1123             _bottomPageBoundSkin->draw(buf, rc);
1124             r = rc;
1125             r.left += _margins.left;
1126             for ( int i=0; i<pages; i++ ) {
1127                 r.right = r.left + tabwidth;
1128                 if ( i+1==page ) {
1129                     _bottomActiveTabSkin->draw(buf, r);
1130                     lString32 label = lString32::itoa(i+1);
1131                     _bottomActiveTabSkin->drawText(buf, r, label);
1132                 }
1133                 r.left += tabwidth - r.height()/6;
1134             }
1135             return;
1136         }
1137     }
1138 
1139 
1140     rc.shrinkBy( _margins );
1141 
1142     int btn1State = CRButtonSkin::ENABLED;
1143     int btn2State = CRButtonSkin::ENABLED;
1144     if ( pos <= 0 )
1145         btn1State = 0;
1146     if ( pos >= maxpos-pagesize )
1147         btn2State = 0;
1148     CRButtonSkinRef btn1Skin;
1149     CRButtonSkinRef btn2Skin;
1150     lvRect btn1Rect = rc;
1151     lvRect btn2Rect = rc;
1152     lvRect bodyRect = rc;
1153     lvRect sliderRect = rc;
1154     LVImageSourceRef bodyImg;
1155     LVImageSourceRef sliderImg;
1156 
1157     if ( _hBody.isNull() ) {
1158         // text label with optional arrows
1159         lString32 label;
1160         label << fmt::decimal(page) << " / " << fmt::decimal(pages);
1161         // calc label width
1162         int w = getFont()->getTextWidth( label.c_str(), label.length() );
1163         int margin = 4;
1164         btn1Skin = _leftButton;
1165         btn2Skin = _rightButton;
1166         // calc button widths
1167         int bw1 = btn1Skin.isNull() ? 0 : btn1Skin->getMinSize().x;
1168         int bw2 = btn1Skin.isNull() ? 0 : btn2Skin->getMinSize().x;
1169         // total width
1170         int ww = w + margin + margin + bw1 + bw2;
1171         int dw = rc.width() - ww;
1172         rc.left += dw*3/4;
1173         rc.right = rc.left + ww;
1174         // adjust rectangle size
1175         btn1Rect = rc;
1176         btn2Rect = rc;
1177         btn1Rect.right = btn1Rect.left + bw1;
1178         btn2Rect.left = btn2Rect.right - bw2;
1179         bodyRect.left = btn1Rect.right;
1180         bodyRect.right = btn2Rect.left;
1181         int dy = bodyRect.height() - btn1Skin->getMinSize().y;
1182         btn1Rect.top += dy/2;
1183         btn1Rect.bottom = btn1Rect.top + btn1Skin->getMinSize().y;
1184         dy = bodyRect.height() - btn2Skin->getMinSize().y;
1185         btn2Rect.top += dy/2;
1186         btn2Rect.bottom = btn2Rect.top + btn2Skin->getMinSize().y;
1187         btn1Skin->drawButton( buf, btn1Rect, btn1State );
1188         btn2Skin->drawButton( buf, btn2Rect, btn2State );
1189         drawText( buf, bodyRect, label );
1190         return;
1191     }
1192 
1193     if ( vertical ) {
1194         // draw vertical
1195         btn1Skin = _upButton;
1196         btn2Skin = _downButton;
1197         btn1Rect.bottom = btn1Rect.top + btn1Skin->getMinSize().y;
1198         btn2Rect.top = btn2Rect.bottom - btn2Skin->getMinSize().y;
1199         bodyRect.top = btn1Rect.bottom;
1200         bodyRect.bottom = btn2Rect.top;
1201         int sz = bodyRect.height();
1202         if ( pagesize < maxpos ) {
1203             sliderRect.top = bodyRect.top + sz * pos / maxpos;
1204             sliderRect.bottom = bodyRect.top + sz * (pos + pagesize) / maxpos;
1205         } else
1206             sliderRect = bodyRect;
1207         bodyImg = _vBody;
1208         sliderImg = _vSlider;
1209     } else {
1210         // draw horz
1211         btn1Skin = _leftButton;
1212         btn2Skin = _rightButton;
1213         btn1Rect.right = btn1Rect.left + btn1Skin->getMinSize().x;
1214         btn2Rect.left = btn2Rect.right - btn2Skin->getMinSize().x;
1215         bodyRect.left = btn1Rect.right;
1216         bodyRect.right = btn2Rect.left;
1217         int sz = bodyRect.width();
1218         if ( pagesize < maxpos ) {
1219             sliderRect.left = bodyRect.left + sz * pos / maxpos;
1220             sliderRect.right = bodyRect.left + sz * (pos + pagesize) / maxpos;
1221         } else
1222             sliderRect = bodyRect;
1223         bodyImg = _hBody;
1224         sliderImg = _hSlider;
1225     }
1226     btn1Skin->drawButton( buf, btn1Rect, btn1State );
1227     btn2Skin->drawButton( buf, btn2Rect, btn2State );
1228     if ( !bodyImg.isNull() ) {
1229         LVImageSourceRef img = LVCreateStretchFilledTransform( bodyImg,
1230             bodyRect.width(), bodyRect.height() );
1231         buf.Draw( img, bodyRect.left, bodyRect.top, bodyRect.width(), bodyRect.height(), false );
1232     }
1233     if ( !sliderImg.isNull() ) {
1234         LVImageSourceRef img = LVCreateStretchFilledTransform( sliderImg,
1235             sliderRect.width(), sliderRect.height() );
1236         buf.Draw( img, sliderRect.left, sliderRect.top, sliderRect.width(), sliderRect.height(), false );
1237         if ( this->getShowPageNumbers() ) {
1238             lString32 label;
1239             label << fmt::decimal(page) << " / " << fmt::decimal(pages);
1240             drawText( buf, sliderRect, label );
1241         }
1242     }
1243 }
1244 
drawGauge(LVDrawBuf & buf,const lvRect & rect,int percent)1245 void CRScrollSkin::drawGauge( LVDrawBuf & buf, const lvRect & rect, int percent )
1246 {
1247     lvRect rc = rect;
1248     rc.shrinkBy( _margins );
1249     bool vertical = rect.width()<rect.height();
1250     lvRect bodyRect = rc;
1251     lvRect sliderRect = rc;
1252     LVImageSourceRef bodyImg;
1253     LVImageSourceRef sliderImg;
1254     if ( vertical ) {
1255         // draw vertical
1256         int sz = bodyRect.height();
1257         sliderRect.bottom = bodyRect.top + sz * percent / 100;
1258         bodyImg = _vBody;
1259         sliderImg = _vSlider;
1260     } else {
1261         // draw horz
1262         int sz = bodyRect.width();
1263         sliderRect.right = bodyRect.left + sz * percent / 100;
1264         bodyImg = _hBody;
1265         sliderImg = _hSlider;
1266     }
1267     if ( !bodyImg.isNull() ) {
1268         LVImageSourceRef img = LVCreateStretchFilledTransform( bodyImg,
1269             bodyRect.width(), bodyRect.height() );
1270         buf.Draw( img, bodyRect.left, bodyRect.top, bodyRect.width(), bodyRect.height(), false );
1271     }
1272     if ( !sliderImg.isNull() ) {
1273         LVImageSourceRef img = LVCreateStretchFilledTransform( sliderImg,
1274             sliderRect.width(), sliderRect.height() );
1275         buf.Draw( img, sliderRect.left, sliderRect.top, sliderRect.width(), sliderRect.height(), false );
1276     }
1277 }
1278 
drawToolBar(LVDrawBuf & buf,const lvRect & rect,bool enabled,int selectedButton)1279 void CRToolBarSkin::drawToolBar( LVDrawBuf & buf, const lvRect & rect, bool enabled, int selectedButton )
1280 {
1281 	draw(buf, rect);
1282     lvRect rc = rect;
1283     rc.shrinkBy( _margins );
1284     int width = 0;
1285 	for ( int i=0; i<_buttons->length(); i++ ) {
1286 		// int flags = enabled ? CRButtonSkin::ENABLED : 0;
1287 		// if (i == selectedButton && enabled)
1288 		// 	flags |= CRButtonSkin::SELECTED;
1289 		LVRef<CRButtonSkin> button = _buttons->get(i);
1290 		if (!button.isNull()) {
1291 			width += button->getMinSize().x;
1292 			int h = button->getMinSize().y;
1293 			if (h > rc.height())
1294 				return;
1295 		}
1296 	}
1297 	if (width > rc.width())
1298 		return; // That's all for now
1299 	int offsetX = 0;
1300 	if (getHAlign() == SKIN_HALIGN_RIGHT)
1301 		offsetX = rc.width() - width;
1302 	else if (getHAlign() == SKIN_HALIGN_CENTER )
1303 		offsetX = rc.width() - width/2;
1304 	int h = rc.height();
1305     for ( int i=0; i<_buttons->length(); i++ ) {
1306 		lvRect rc2 = rc;
1307 		int flags = enabled ? CRButtonSkin::ENABLED : 0;
1308 		if (i == selectedButton && enabled)
1309 			flags |= CRButtonSkin::SELECTED;
1310 		LVRef<CRButtonSkin> button = _buttons->get(i);
1311 		if (!button.isNull()) {
1312 			LVImageSourceRef img = button->getImage(flags);
1313 			rc2.left += offsetX;
1314 			rc2.right = rc2.left + button->getMinSize().x;
1315 			if ( getVAlign()==SKIN_VALIGN_BOTTOM )
1316 				rc2.top = rc2.bottom - button->getMinSize().y;
1317 			else if ( getVAlign()==SKIN_VALIGN_CENTER ) {
1318 				int imgh = button->getMinSize().y;
1319 				rc2.top += (h - imgh/2);
1320 				rc2.bottom = rc2.top + imgh;
1321 			} else
1322 				rc2.bottom = rc2.top + button->getMinSize().y;
1323 			button->drawButton( buf, rc2, flags );
1324 			offsetX = rc2.right - rc.left;
1325 		}
1326 	}
1327 }
1328 
drawButton(LVDrawBuf & buf,const lvRect & rc,int index,int flags)1329 void CRToolBarSkin::drawButton(LVDrawBuf & buf, const lvRect & rc, int index, int flags)
1330 {
1331 
1332 }
1333 
CRRectSkin()1334 CRRectSkin::CRRectSkin()
1335 : _margins( 0, 0, 0, 0 )
1336 , _size(toSkinPercent( 10000 ), toSkinPercent( 10000 )) // 100% x 100%
1337 , _pos(0, 0)
1338 , _align(SKIN_VALIGN_TOP|SKIN_HALIGN_LEFT) // relative to top left
1339 {
1340 }
1341 
CRWindowSkin()1342 CRWindowSkin::CRWindowSkin()
1343 {
1344     _fullscreen = false;
1345 }
1346 
CRMenuSkin()1347 CRMenuSkin::CRMenuSkin()
1348 : _minItemCount(-1)
1349 , _maxItemCount(-1)
1350 , _showShortcuts(true)
1351 {
1352 }
1353 
1354 
1355 // WINDOW skin stub
1356 class CRSimpleWindowSkin : public CRWindowSkin
1357 {
1358 public:
CRSimpleWindowSkin(CRSkinImpl *)1359         CRSimpleWindowSkin( CRSkinImpl * )
1360 	{
1361         //setBackgroundColor( 0xAAAAAA );
1362 	}
1363 };
1364 
1365 class CRSimpleFrameSkin : public CRRectSkin
1366 {
1367 public:
CRSimpleFrameSkin(CRSkinImpl *)1368         CRSimpleFrameSkin( CRSkinImpl * )
1369 	{
1370         //setBackgroundColor( 0xAAAAAA );
1371 	}
1372 };
1373 
1374 /*
1375     <item>
1376         <text color="" face="" size="" bold="" italic="" valign="" halign=""/>
1377         <background image="filename" color=""/>
1378         <border widths="left,top,right,bottom"/>
1379         <icon image="filename" valign="" halign=""/>
1380         <title>
1381             <size minvalue="x,y" maxvalue=""/>
1382             <text color="" face="" size="" bold="" italic="" valign="" halign=""/>
1383             <background image="filename" color=""/>
1384             <border widths="left,top,right,bottom"/>
1385             <icon image="filename" valign="" halign="">
1386         </title>
1387         <item>
1388             <size minvalue="x,y" maxvalue=""/>
1389             <text color="" face="" size="" bold="" italic="" valign="" halign=""/>
1390             <background image="filename" color=""/>
1391             <border widths="left,top,right,bottom"/>
1392             <icon image="filename" valign="" halign="">
1393         </item>
1394         <shortcut>
1395             <size minvalue="x,y" maxvalue=""/>
1396             <text color="" face="" size="" bold="" italic="" valign="" halign=""/>
1397             <background image="filename" color=""/>
1398             <border widths="left,top,right,bottom"/>
1399             <icon image="filename" valign="" halign="">
1400         </shortcut>
1401     </item>
1402 */
1403 class CRSimpleMenuSkin : public CRMenuSkin
1404 {
1405 public:
CRSimpleMenuSkin(CRSkinImpl * skin)1406     CRSimpleMenuSkin( CRSkinImpl * skin )
1407     {
1408         //setBackgroundColor( 0xAAAAAA );
1409         //setTitleSize( lvPoint( 0, 48 ) );
1410         setBorderWidths( lvRect( 8, 8, 8, 8 ) );
1411         _titleSkin = CRRectSkinRef( new CRRectSkin() );
1412         //_titleSkin->setBackgroundColor(0xAAAAAA);
1413         _titleSkin->setTextColor(0x000000);
1414         _titleSkin->setFontBold( true );
1415         _titleSkin->setFontSize( 28 );
1416         _itemSkin = CRRectSkinRef( new CRRectSkin() );
1417         _itemSkin->setBackgroundImage( skin->getImage( U"std_menu_item_background.xpm" ) );
1418         _itemSkin->setBorderWidths( lvRect( 8, 8, 8, 8 ) );
1419         _itemShortcutSkin = CRRectSkinRef( new CRRectSkin() );
1420         _itemShortcutSkin->setBackgroundImage( skin->getImage( U"std_menu_shortcut_background.xpm" ) );
1421         _itemShortcutSkin->setBorderWidths( lvRect( 12, 8, 8, 8 ) );
1422         _itemShortcutSkin->setTextColor( 0x555555 );
1423         _itemShortcutSkin->setTextHAlign( SKIN_HALIGN_CENTER );
1424         _itemShortcutSkin->setTextVAlign( SKIN_VALIGN_CENTER );
1425 	}
1426 };
1427 
CRButtonSkin()1428 CRButtonSkin::CRButtonSkin() { }
1429 
readButtonSkin(const lChar32 * path,CRButtonSkin * res)1430 bool CRSkinContainer::readButtonSkin(  const lChar32 * path, CRButtonSkin * res )
1431 {
1432     bool flg = false;
1433     lString32 base = getBasePath( path );
1434     RecursionLimit limit;
1435     if ( !base.empty() && limit.test() ) {
1436         // read base skin first
1437         flg = readButtonSkin( base.c_str(), res ) || flg;
1438     }
1439 
1440     lString32 p( path );
1441     ldomXPointer ptr = getXPointer( path );
1442     if ( !ptr ) {
1443 #ifdef TRACE_SKIN_ERRORS
1444         crtrace log;
1445         log << "Button skin by path " << p << " was not found";
1446 #endif
1447         return false;
1448     }
1449 
1450     flg = readRectSkin( path, res ) || flg;
1451     res->setNormalImage( readImage( path, U"normal", &flg ) );
1452     res->setDisabledImage( readImage( path, U"disabled", &flg ) );
1453     res->setPressedImage( readImage( path, U"pressed", &flg ) );
1454     res->setSelectedImage( readImage( path, U"selected", &flg ) );
1455 
1456     LVImageSourceRef img = res->getNormalImage();
1457     lvRect margins = res->getBorderWidths();
1458     if ( !img.isNull() ) {
1459         flg = true;
1460         res->setMinSize( lvPoint( margins.left + margins.right + img->GetWidth(), margins.top + margins.bottom + img->GetHeight() ) );
1461     }
1462 
1463     if ( !flg ) {
1464 #ifdef TRACE_SKIN_ERRORS
1465         crtrace log;
1466         log << "Button skin reading failed: " << path;
1467 #endif
1468     }
1469 
1470     return flg;
1471 }
1472 
CRScrollSkin()1473 CRScrollSkin::CRScrollSkin() : _autohide(false), _showPageNumbers(true), _location(CRScrollSkin::Status) { }
1474 
readScrollSkin(const lChar32 * path,CRScrollSkin * res)1475 bool CRSkinContainer::readScrollSkin(  const lChar32 * path, CRScrollSkin * res )
1476 {
1477     bool flg = false;
1478     lString32 base = getBasePath( path );
1479     RecursionLimit limit;
1480     if ( !base.empty() && limit.test() ) {
1481         // read base skin first
1482         flg = readScrollSkin( base.c_str(), res ) || flg;
1483     }
1484 
1485     lString32 p( path );
1486     ldomXPointer ptr = getXPointer( path );
1487     if ( !ptr ) {
1488 #ifdef TRACE_SKIN_ERRORS
1489         crtrace log;
1490         log << "ScrollBar skin by path " << p << " was not found";
1491 #endif
1492         return false;
1493     }
1494 
1495 
1496 
1497     flg = readRectSkin( path, res ) || flg;
1498 
1499     res->setAutohide( readBool( (p).c_str(), U"autohide", res->getAutohide()) );
1500     res->setShowPageNumbers( readBool( (p).c_str(), U"show-page-numbers", res->getShowPageNumbers()) );
1501     lString32 l = readString( (p).c_str(), U"location", lString32::empty_str );
1502     if ( !l.empty() ) {
1503         l.lowercase();
1504         if (l == "title")
1505             res->setLocation( CRScrollSkin::Title );
1506     }
1507     CRButtonSkinRef upButton( new CRButtonSkin() );
1508     if ( readButtonSkin(  (p + "/upbutton").c_str(), upButton.get() ) ) {
1509         res->setUpButton( upButton );
1510         flg = true;
1511     }
1512 
1513     CRButtonSkinRef downButton( new CRButtonSkin() );
1514     if ( readButtonSkin(  (p + "/downbutton").c_str(), downButton.get() ) ) {
1515         res->setDownButton( downButton );
1516         flg = true;
1517     }
1518 
1519     CRButtonSkinRef leftButton( new CRButtonSkin() );
1520     if ( readButtonSkin(  (p + "/leftbutton").c_str(), leftButton.get() ) ) {
1521         res->setLeftButton( leftButton );
1522         flg = true;
1523     }
1524 
1525     CRButtonSkinRef rightButton( new CRButtonSkin() );
1526     if ( readButtonSkin(  (p + "/rightbutton").c_str(), rightButton.get() ) ) {
1527         res->setRightButton( rightButton );
1528         flg = true;
1529     }
1530 
1531     CRRectSkinRef tabSkin( new CRRectSkin() );
1532     if ( readRectSkin(  (p + "/tab-bottom").c_str(), tabSkin.get() ) ) {
1533         res->setBottomTabSkin( tabSkin );
1534         flg = true;
1535     }
1536 
1537     CRRectSkinRef tabActiveSkin( new CRRectSkin() );
1538     if ( readRectSkin(  (p + "/tab-bottom-active").c_str(), tabActiveSkin.get() ) ) {
1539         res->setBottomActiveTabSkin( tabActiveSkin );
1540         flg = true;
1541     }
1542 
1543     CRRectSkinRef pageBoundSkin( new CRRectSkin() );
1544     if ( readRectSkin(  (p + "/page-bound-bottom").c_str(), pageBoundSkin.get() ) ) {
1545         res->setBottomPageBoundSkin( pageBoundSkin );
1546         flg = true;
1547     }
1548 
1549     LVImageSourceRef hf = readImage( (p + "/hbody").c_str(), U"frame", &flg );
1550     if ( !hf.isNull() )
1551         res->setHBody( hf );
1552     LVImageSourceRef hs = readImage( (p + "/hbody").c_str(), U"slider", &flg );
1553     if ( !hs.isNull() )
1554         res->setHSlider( hs );
1555     LVImageSourceRef vf = readImage( (p + "/vbody").c_str(), U"frame", &flg );
1556     if ( !vf.isNull() )
1557         res->setVBody( vf );
1558     LVImageSourceRef vs = readImage( (p + "/vbody").c_str(), U"slider", &flg );
1559     if ( !vs.isNull() )
1560         res->setVSlider(vs );
1561 
1562     if ( !flg ) {
1563         crtrace log;
1564         log << "Scroll skin reading failed: " << path;
1565     }
1566 
1567     return flg;
1568 }
1569 
readIconSkin(const lChar32 * path,CRIconSkin * res)1570 bool CRSkinContainer::readIconSkin(  const lChar32 * path, CRIconSkin * res )
1571 {
1572     bool flg = false;
1573     lString32 base = getBasePath( path );
1574     RecursionLimit limit;
1575     if ( !base.empty() && limit.test() ) {
1576         // read base skin first
1577         flg = readIconSkin( base.c_str(), res ) || flg;
1578     }
1579     lString32 p( path );
1580     ldomXPointer ptr = getXPointer( path );
1581     if ( !ptr ) {
1582 #ifdef TRACE_SKIN_ERRORS
1583         crtrace log;
1584         log << "Image skin by path " << p << " was not found";
1585 #endif
1586         return false;
1587     }
1588     LVImageSourceRef image = readImage( path, U"image", &flg );
1589     if ( !image.isNull() )
1590         res->setImage( image );
1591     res->setHAlign( readHAlign( path, U"halign", res->getHAlign(), &flg) );
1592     res->setVAlign( readVAlign( path, U"valign", res->getVAlign(), &flg) );
1593     res->setBgColor( readColor( path, U"color", res->getBgColor(), &flg) );
1594     res->setHTransform( readTransform( path, U"htransform", res->getHTransform(), &flg) );
1595     res->setVTransform( readTransform( path, U"vtransform", res->getVTransform(), &flg) );
1596     res->setSplitPoint( readSize( path, U"split", res->getSplitPoint(), &flg) );
1597     res->setPos( readSize( path, U"pos", res->getPos(), &flg) );
1598     res->setSize( readSize( path, U"size", res->getSize(), &flg) );
1599     return flg;
1600 }
1601 
readRectSkin(const lChar32 * path,CRRectSkin * res)1602 bool CRSkinContainer::readRectSkin(  const lChar32 * path, CRRectSkin * res )
1603 {
1604     bool flg = false;
1605 
1606     lString32 base = getBasePath( path );
1607     RecursionLimit limit;
1608     if ( !base.empty() && limit.test() ) {
1609         // read base skin first
1610         flg = readRectSkin( base.c_str(), res ) || flg;
1611     }
1612 
1613     lString32 p( path );
1614     ldomXPointer ptr = getXPointer( path );
1615     if ( !ptr ) {
1616 #ifdef TRACE_SKIN_ERRORS
1617         crtrace log;
1618         log << "Rect skin by path " << p << " was not found";
1619 #endif
1620         return false;
1621     }
1622 
1623     lString32 bgpath = p + "/background";
1624     lString32 borderpath = p + "/border";
1625     lString32 textpath = p + "/text";
1626     lString32 sizepath = p + "/size";
1627 
1628     CRIconListRef icons;
1629     bool bgIconsFlag = false;
1630     icons = readIcons( bgpath.c_str(), &bgIconsFlag);
1631     if ( bgIconsFlag ) {
1632         res->setBgIcons( icons );
1633         flg = true;
1634     }
1635     //res->setBackgroundColor( readColor( bgpath.c_str(), U"color", res->getBackgroundColor(), &flg ) );
1636     res->setBorderWidths( readRect( borderpath.c_str(), U"widths", res->getBorderWidths(), &flg ) );
1637     res->setMinSize( readSize( sizepath.c_str(), U"minvalue", res->getMinSize(), &flg ) );
1638     res->setMaxSize( readSize( sizepath.c_str(), U"maxvalue", res->getMaxSize(), &flg ) );
1639     res->setFontFace( readString( textpath.c_str(), U"face", res->getFontFace(), &flg ) );
1640     res->setTextColor( readColor( textpath.c_str(), U"color", res->getTextColor(), &flg ) );
1641     res->setFontBold( readBool( textpath.c_str(), U"bold", res->getFontBold(), &flg ) );
1642     res->setWordWrap( readBool( textpath.c_str(), U"wordwrap", res->getWordWrap(), &flg ) );
1643     res->setFontItalic( readBool( textpath.c_str(), U"italic", res->getFontItalic(), &flg ) );
1644     res->setFontSize( readInt( textpath.c_str(), U"size", res->getFontSize(), &flg ) );
1645     res->setTextHAlign( readHAlign( textpath.c_str(), U"halign", res->getTextHAlign(), &flg) );
1646     res->setTextVAlign( readVAlign( textpath.c_str(), U"valign", res->getTextVAlign(), &flg) );
1647 
1648     res->setHAlign( readHAlign( path, U"halign", res->getHAlign(), &flg) );
1649     res->setVAlign( readVAlign( path, U"valign", res->getVAlign(), &flg) );
1650     res->setPos( readSize( path, U"pos", res->getPos(), &flg) );
1651     res->setSize( readSize( path, U"size", res->getSize(), &flg) );
1652 
1653     if ( !flg ) {
1654         crtrace log;
1655         log << "Rect skin reading failed: " << path;
1656     }
1657 
1658     return flg;
1659 }
1660 
getTitleSize()1661 lvPoint CRWindowSkin::getTitleSize()
1662 {
1663     lvPoint minsize = _titleSkin.isNull() ? lvPoint(0, 0) : _titleSkin->getMinSize();
1664     return minsize;
1665 }
1666 
readPageSkin(const lChar32 * path,CRPageSkin * res)1667 bool CRSkinContainer::readPageSkin(  const lChar32 * path, CRPageSkin * res )
1668 {
1669     bool flg = false;
1670 
1671     lString32 base = getBasePath( path );
1672     RecursionLimit limit;
1673     if ( !base.empty() && limit.test() ) {
1674         // read base skin first
1675         flg = readPageSkin( base.c_str(), res ) || flg;
1676     }
1677 
1678     lString32 p( path );
1679     ldomXPointer ptr = getXPointer( path );
1680     if ( !ptr ) {
1681 #ifdef TRACE_SKIN_ERRORS
1682         crtrace log;
1683         log << "Book page skin by path " << p << " was not found";
1684 #endif
1685         return false;
1686     }
1687 
1688     lString32 name = ptr.getNode()->getAttributeValue(ptr.getNode()->getDocument()->getAttrNameIndex("name"));
1689     if ( !name.empty() )
1690         res->setName(name);
1691 
1692     flg = readRectSkin( (p + "scroll-skin").c_str(),  res->getSkin( PAGE_SKIN_SCROLL ).get() ) || flg;
1693     flg = readRectSkin( (p + "left-page-skin").c_str(),  res->getSkin( PAGE_SKIN_LEFT_PAGE ).get() ) || flg;
1694     flg = readRectSkin( (p + "right-page-skin").c_str(),  res->getSkin( PAGE_SKIN_RIGHT_PAGE ).get() ) || flg;
1695     flg = readRectSkin( (p + "single-page-skin").c_str(),  res->getSkin( PAGE_SKIN_SINGLE_PAGE ).get() ) || flg;
1696 
1697     if ( !flg ) {
1698         crtrace log;
1699         log << "Book page skin reading failed: " << path;
1700     }
1701 
1702     return flg;
1703 }
1704 
readWindowSkin(const lChar32 * path,CRWindowSkin * res)1705 bool CRSkinContainer::readWindowSkin(  const lChar32 * path, CRWindowSkin * res )
1706 {
1707     bool flg = false;
1708 
1709     lString32 base = getBasePath( path );
1710     RecursionLimit limit;
1711     if ( !base.empty() && limit.test() ) {
1712         // read base skin first
1713         flg = readWindowSkin( base.c_str(), res ) || flg;
1714     }
1715 
1716     lString32 p( path );
1717     ldomXPointer ptr = getXPointer( path );
1718     if ( !ptr ) {
1719 #ifdef TRACE_SKIN_ERRORS
1720         crtrace log;
1721         log << "Window skin by path " << p << " was not found";
1722 #endif
1723         return false;
1724     }
1725 
1726     res->setFullScreen(readBool(path, U"fullscreen", res->getFullScreen(), &flg));
1727 
1728     flg = readRectSkin(  path, res ) || flg;
1729     CRRectSkinRef titleSkin( new CRRectSkin() );
1730     if ( readRectSkin(  (p + "/title").c_str(), titleSkin.get() ) ) {
1731         res->setTitleSkin( titleSkin );
1732         flg = true;
1733     }
1734 
1735     CRRectSkinRef clientSkin( new CRRectSkin() );
1736     if ( readRectSkin(  (p + "/client").c_str(), clientSkin.get() ) ) {
1737         res->setClientSkin( clientSkin );
1738         flg = true;
1739     }
1740 
1741     CRRectSkinRef inputSkin( new CRRectSkin() );
1742     if ( readRectSkin(  (p + "/input").c_str(), inputSkin.get() ) ) {
1743         res->setInputSkin( inputSkin );
1744         flg = true;
1745     }
1746 
1747     CRRectSkinRef statusSkin( new CRRectSkin() );
1748     if ( readRectSkin(  (p + "/status").c_str(), statusSkin.get() ) ) {
1749         res->setStatusSkin( statusSkin );
1750         flg = true;
1751     }
1752 
1753     CRScrollSkinRef scrollSkin( new CRScrollSkin() );
1754     if ( readScrollSkin(  (p + "/scroll").c_str(), scrollSkin.get() ) ) {
1755         res->setScrollSkin( scrollSkin );
1756         flg = true;
1757     }
1758 
1759     if ( !flg ) {
1760         crtrace log;
1761         log << "Window skin reading failed: " << path;
1762     }
1763 
1764     return flg;
1765 }
1766 
readMenuSkin(const lChar32 * path,CRMenuSkin * res)1767 bool CRSkinContainer::readMenuSkin(  const lChar32 * path, CRMenuSkin * res )
1768 {
1769     bool flg = false;
1770 
1771     lString32 base = getBasePath( path );
1772     RecursionLimit limit;
1773     if ( !base.empty() && limit.test() ) {
1774         // read base skin first
1775         flg = readMenuSkin( base.c_str(), res ) || flg;
1776     }
1777 
1778     lString32 p( path );
1779     ldomXPointer ptr = getXPointer( path );
1780     if ( !ptr ) {
1781 #ifdef TRACE_SKIN_ERRORS
1782         crtrace log;
1783         log << "Menu skin by path " << p << " was not found";
1784 #endif
1785         return false;
1786     }
1787 
1788     flg = readWindowSkin( path, res ) || flg;
1789 
1790     bool b;
1791     CRRectSkinRef separatorSkin( new CRRectSkin() );
1792     b = readRectSkin(  (p + "/separator").c_str(), separatorSkin.get() );
1793     flg = flg || b;
1794     if ( b || res->getSeparatorSkin().isNull() )
1795         res->setSeparatorSkin( separatorSkin );
1796 
1797     CRRectSkinRef valueSkin( new CRRectSkin() );
1798     b = readRectSkin(  (p + "/value").c_str(), valueSkin.get() );
1799     flg = flg || b;
1800     if ( b || res->getValueSkin().isNull() )
1801         res->setValueSkin( valueSkin );
1802 
1803     CRRectSkinRef itemSkin( new CRRectSkin() );
1804     b = readRectSkin(  (p + "/item").c_str(), itemSkin.get() );
1805     flg = flg || b;
1806     if ( b || res->getItemSkin().isNull() )
1807         res->setItemSkin( itemSkin );
1808     CRRectSkinRef shortcutSkin( new CRRectSkin() );
1809     b = readRectSkin(  (p + "/shortcut").c_str(), shortcutSkin.get() );
1810     flg = flg || b;
1811     if ( b || res->getItemShortcutSkin().isNull() )
1812         res->setItemShortcutSkin( shortcutSkin );
1813 
1814     CRRectSkinRef itemSelSkin( new CRRectSkin() );
1815     b = readRectSkin(  (p + "/selitem").c_str(), itemSelSkin.get() );
1816     flg = flg || b;
1817     if ( b || res->getSelItemSkin().isNull() )
1818         res->setSelItemSkin( itemSelSkin );
1819     CRRectSkinRef shortcutSelSkin( new CRRectSkin() );
1820     b = readRectSkin(  (p + "/selshortcut").c_str(), shortcutSelSkin.get() );
1821     flg = flg || b;
1822     if ( b || res->getSelItemShortcutSkin().isNull() )
1823         res->setSelItemShortcutSkin( shortcutSelSkin );
1824 
1825     CRRectSkinRef evenitemSkin( new CRRectSkin() );
1826     b = readRectSkin(  (p + "/item-even").c_str(), evenitemSkin.get() );
1827     flg = flg || b;
1828     if ( b )
1829         res->setEvenItemSkin( evenitemSkin );
1830     CRRectSkinRef evenshortcutSkin( new CRRectSkin() );
1831     b = readRectSkin(  (p + "/shortcut-even").c_str(), evenshortcutSkin.get() );
1832     flg = flg || b;
1833     if ( b )
1834         res->setEvenItemShortcutSkin( evenshortcutSkin );
1835 
1836     CRRectSkinRef evenitemSelSkin( new CRRectSkin() );
1837     b = readRectSkin(  (p + "/selitem-even").c_str(), evenitemSelSkin.get() );
1838     flg = flg || b;
1839     if ( b )
1840         res->setEvenSelItemSkin( evenitemSelSkin );
1841     CRRectSkinRef evenshortcutSelSkin( new CRRectSkin() );
1842     b = readRectSkin(  (p + "/selshortcut-even").c_str(), evenshortcutSelSkin.get() );
1843     flg = flg || b;
1844     if ( b )
1845         res->setEvenSelItemShortcutSkin( evenshortcutSelSkin );
1846 
1847     res->setMinItemCount( readInt( path, U"min-item-count", res->getMinItemCount()) );
1848     res->setMaxItemCount( readInt( path, U"max-item-count", res->getMaxItemCount()) );
1849     res->setShowShortcuts( readBool( path, U"show-shortcuts", res->getShowShortcuts() ) );
1850 
1851     return flg;
1852 }
1853 
readButtons(const lChar32 * path,bool * res)1854 CRButtonListRef CRSkinContainer::readButtons( const lChar32 * path, bool * res )
1855 {
1856     CRButtonListRef list = CRButtonListRef(new CRButtonList() );
1857     for ( int i=1; i<64; i++ ) {
1858         lString32 p = lString32(path) << "[" << fmt::decimal(i) << "]";
1859         CRButtonSkin * button = new CRButtonSkin();
1860         if ( readButtonSkin(p.c_str(), button ) )
1861             list->add( LVRef<CRButtonSkin>(button) );
1862         else {
1863             delete button;
1864             break;
1865         }
1866     }
1867     if ( list->length()==0 ) {
1868 #ifdef TRACE_SKIN_ERRORS
1869         crtrace log;
1870         log << "CRSkinContainer::readButtons( " << path << ") - cannot read button from specified path";
1871 #endif
1872 		if ( res )
1873 			*res = false;
1874         return CRButtonListRef();
1875     }
1876     if ( res )
1877         *res = true;
1878     return list;
1879 }
1880 
readToolBarSkin(const lChar32 * path,CRToolBarSkin * res)1881 bool CRSkinContainer::readToolBarSkin(  const lChar32 * path, CRToolBarSkin * res )
1882 {
1883     bool flg = false;
1884     lString32 base = getBasePath( path );
1885     RecursionLimit limit;
1886     if ( !base.empty() && limit.test() ) {
1887         // read base skin first
1888         flg = readToolBarSkin( base.c_str(), res ) || flg;
1889     }
1890 
1891     lString32 p( path );
1892     ldomXPointer ptr = getXPointer( path );
1893     if ( !ptr ) {
1894 #ifdef TRACE_SKIN_ERRORS
1895         crtrace log;
1896         log << "ToolBar skin by path " << p << " was not found";
1897 #endif
1898         return false;
1899     }
1900     flg = readRectSkin( path, res ) || flg;
1901 
1902     lString32 buttonspath = p + "/button";
1903     bool buttonsFlag = false;
1904     CRButtonListRef buttons = readButtons( buttonspath.c_str(), &buttonsFlag);
1905     if ( buttonsFlag ) {
1906         res->setButtons( buttons );
1907         flg = true;
1908     }
1909 	return flg;
1910 }
1911 
pathById(const lChar32 * id)1912 lString32 CRSkinImpl::pathById( const lChar32 * id )
1913 {
1914     ldomNode * elem = _doc->getElementById( id );
1915     if ( !elem )
1916         return lString32::empty_str;
1917     return ldomXPointer(elem, -1).toString();
1918 }
1919 
1920 /// returns rect skin
getRectSkin(const lChar32 * path)1921 CRRectSkinRef CRSkinImpl::getRectSkin( const lChar32 * path )
1922 {
1923     lString32 p(path);
1924     CRRectSkinRef res;
1925     if ( _rectCache.get( p, res ) )
1926         return res; // found in cache
1927     if ( *path == '#' ) {
1928         // find by id
1929         p = pathById( path+1 );
1930     }
1931     // create new one
1932     res = CRRectSkinRef( new CRRectSkin() );
1933     readRectSkin( p.c_str(), res.get() );
1934     _rectCache.set( lString32(path), res );
1935     return res;
1936 }
1937 
1938 /// returns scroll skin by path or #id
getScrollSkin(const lChar32 * path)1939 CRScrollSkinRef CRSkinImpl::getScrollSkin( const lChar32 * path )
1940 {
1941     lString32 p(path);
1942     CRScrollSkinRef res;
1943     if ( _scrollCache.get( p, res ) )
1944         return res; // found in cache
1945     if ( *path == '#' ) {
1946         // find by id
1947         p = pathById( path+1 );
1948     }
1949     // create new one
1950     res = CRScrollSkinRef( new CRScrollSkin() );
1951     readScrollSkin( p.c_str(), res.get() );
1952     _scrollCache.set( lString32(path), res );
1953     return res;
1954 }
1955 
1956 /// returns book page skin list
getPageSkinList()1957 CRPageSkinListRef CRSkinImpl::getPageSkinList()
1958 {
1959     if ( _pageSkinList.isNull() ) {
1960         _pageSkinList = CRPageSkinListRef( new CRPageSkinList() );
1961         for ( int i=0; i<32; i++ ) {
1962             lString32 path("/CR3Skin/page-skins/page-skin[");
1963             path << (i+1) << U"]";
1964             CRPageSkinRef skin = CRPageSkinRef( new CRPageSkin() );
1965             if ( readPageSkin(path.c_str(), skin.get() ) ) {
1966                 _pageSkinList->add( skin );
1967             } else {
1968                 break;
1969             }
1970         }
1971     }
1972     return _pageSkinList;
1973 }
1974 
1975 /// returns book page skin by path or #id
getPageSkin(const lChar32 * path)1976 CRPageSkinRef CRSkinImpl::getPageSkin( const lChar32 * path )
1977 {
1978     lString32 p(path);
1979     CRPageSkinRef res;
1980     if ( _pageCache.get( p, res ) )
1981         return res; // found in cache
1982     if ( *path == '#' ) {
1983         // find by id
1984         p = pathById( path+1 );
1985     }
1986     // create new one
1987     res = CRPageSkinRef( new CRPageSkin() );
1988     readPageSkin( p.c_str(), res.get() );
1989     _pageCache.set( lString32(path), res );
1990     return res;
1991 }
1992 
1993 /// returns window skin
getWindowSkin(const lChar32 * path)1994 CRWindowSkinRef CRSkinImpl::getWindowSkin( const lChar32 * path )
1995 {
1996     lString32 p(path);
1997     CRWindowSkinRef res;
1998     if ( _windowCache.get( p, res ) )
1999         return res; // found in cache
2000     if ( *path == '#' ) {
2001         // find by id
2002         p = pathById( path+1 );
2003     }
2004     // create new one
2005     res = CRWindowSkinRef( new CRWindowSkin() );
2006     readWindowSkin( p.c_str(), res.get() );
2007     _windowCache.set( lString32(path), res );
2008     return res;
2009 }
2010 
2011 /// returns menu skin
getMenuSkin(const lChar32 * path)2012 CRMenuSkinRef CRSkinImpl::getMenuSkin( const lChar32 * path )
2013 {
2014     lString32 p(path);
2015     CRMenuSkinRef res;
2016     if ( _menuCache.get( p, res ) )
2017         return res; // found in cache
2018     if ( *path == '#' ) {
2019         // find by id
2020         p = pathById( path+1 );
2021     }
2022     // create new one
2023     res = CRMenuSkinRef( new CRMenuSkin() );
2024     readMenuSkin( p.c_str(), res.get() );
2025     _menuCache.set( lString32(path), res );
2026     return res;
2027 }
2028 
getToolBarSkin(const lChar32 * path)2029 CRToolBarSkinRef CRSkinImpl::getToolBarSkin( const lChar32 * path )
2030 {
2031     lString32 p(path);
2032     CRToolBarSkinRef res;
2033     if ( _toolbarCache.get( p, res ) )
2034         return res; // found in cache
2035     if ( *path == '#' ) {
2036         // find by id
2037         p = pathById( path+1 );
2038     }
2039     res = CRToolBarSkinRef( new CRToolBarSkin() );
2040     readToolBarSkin( p.c_str(), res.get() );
2041     _toolbarCache.set( lString32(path), res );
2042     return res;
2043 }
2044 
findByName(const lString32 & name)2045 CRSkinListItem * CRSkinList::findByName( const lString32 & name )
2046 {
2047     for ( int i=0; i<length(); i++ )
2048         if ( get(i)->getName()==name )
2049             return get(i);
2050     return NULL;
2051 }
2052 
init(lString32 baseDir,lString32 fileName)2053 CRSkinListItem * CRSkinListItem::init( lString32 baseDir, lString32 fileName )
2054 {
2055     CRSkinRef skin = LVOpenSkin( baseDir + fileName );
2056     if ( skin.isNull() )
2057         return NULL;
2058     CRSkinListItem * item = new CRSkinListItem();
2059     item->_baseDir = baseDir;
2060     item->_fileName = fileName;
2061     //item->_name = skin->get
2062     return item;
2063 }
2064 
getSkin()2065 CRSkinRef CRSkinListItem::getSkin()
2066 {
2067     return LVOpenSkin( getDirName() + getFileName() );
2068 }
2069 
CRLoadSkinList(lString32 baseDir,CRSkinList & list)2070 bool CRLoadSkinList( lString32 baseDir, CRSkinList & list )
2071 {
2072     return false;
2073 }
2074