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