1 /*
2  * Part of WCM Commander
3  * https://github.com/corporateshark/WCMCommander
4  * wcm@linderdaum.com
5  */
6 
7 #include <swl.h>
8 #include "help.h"
9 #include "ncfonts.h"
10 #include "wcm-config.h"
11 #include "ltext.h"
12 #include "globals.h"
13 #include "string-util.h"
14 #ifdef _WIN32
15 #  include "w32util.h"
16 #endif
17 
18 using namespace wal;
19 
20 // GCC
21 #ifdef __GNUC__
22 #  define PLATFORM_GCC
23 #  ifdef __clang__
24 #     define __COMPILER_VER__ "Clang " __VERSION__
25 #  else
26 #     define __COMPILER_VER__ "GCC " __VERSION__
27 #  endif
28 #endif
29 
30 // Microsoft Visual C++
31 #ifdef _MSC_VER
32 #  define PLATFORM_MSVC
33 #define __COMPILER_VER__ "Microsoft Visual C++"
34 #endif
35 
36 #if defined(_WIN64)
37 #  define BUILD_OS "Win64"
38 #elif defined(_WIN32)
39 #  define BUILD_OS "Win32"
40 #elif defined(__APPLE__)
41 #  if __x86_64__ || __ppc64__
42 #     define BUILD_OS "OS X 64"
43 #  else
44 #     define BUILD_OS "OS X 32"
45 #  endif
46 #elif defined(__FreeBSD__)
47 #  if __x86_64__ || __ppc64__
48 #     define BUILD_OS "FreeBSD 64"
49 #  else
50 #     define BUILD_OS "FreeBSD 32"
51 #  endif
52 #else
53 #  if __x86_64__ || __ppc64__
54 #     define BUILD_OS "Linux64"
55 #  else
56 #     define BUILD_OS "Linux32"
57 #  endif
58 #endif
59 
60 #include "wcm-version.h"
61 const char* verString = "WCM Commander v " WCM_VERSION " (" __DATE__ "  " __TIME__ " via " __COMPILER_VER__ " for " BUILD_OS ")";
62 
63 struct HelpStyle
64 {
65 	cfont* font;
66 	unsigned fg;
67 	unsigned bg;
HelpStyleHelpStyle68 	HelpStyle(): font( 0 ), fg( 0 ), bg( 0xFFFFFF ) {}
HelpStyleHelpStyle69 	HelpStyle( cfont* f, unsigned c, unsigned b ) : font( f ), fg( c ), bg( b ) {}
FontHelpStyle70 	cfont* Font() { return this ? font : ( cfont* )0; }
FgHelpStyle71 	unsigned Fg() { return this ? fg : 0; }
BgHelpStyle72 	unsigned Bg() { return this ? bg : 0xFFFFFF; }
73 };
74 
75 //HelpGC сделан для кэширования операций GetTextExtents а то в X11 (со стандартными X11 фонтами) это пиздец как медленно
76 
77 class HelpGC
78 {
79 	struct Node
80 	{
81 		cfont* font;
82 		clPtr<cstrhash<cpoint, unicode_t> > pHash;
NodeHelpGC::Node83 		Node( cfont* f = 0 ): font( f ) { pHash = new cstrhash<cpoint, unicode_t>; }
84 	};
85 	ccollect<Node> _list;
86 	wal::GC* _gc;
87 	cfont* _font;
HelpGC()88 	HelpGC() {};
89 public:
HelpGC(wal::GC * gc)90 	HelpGC( wal::GC* gc ): _gc( gc ), _font( 0 ) {}
Set(cfont * f)91 	void Set( cfont* f ) { if ( _font != f ) { _gc->Set( f ); _font = f; } }
SetFillColor(unsigned c)92 	void SetFillColor( unsigned c ) { _gc->SetFillColor( c ); }
SetTextColor(unsigned c)93 	void SetTextColor( unsigned c ) { _gc->SetTextColor( c ); }
TextOut(int x,int y,const unicode_t * s)94 	void TextOut( int x, int y, const unicode_t* s ) { _gc->TextOut( x, y, s ); }
TextOutF(int x,int y,const unicode_t * s)95 	void TextOutF( int x, int y, const unicode_t* s ) { _gc->TextOutF( x, y, s ); }
96 	cpoint GetTextExtents( const unicode_t* s );
FillRect(crect & r)97 	void FillRect( crect& r ) { _gc->FillRect( r ); }
98 	~HelpGC();
99 };
100 
GetTextExtents(const unicode_t * s)101 cpoint HelpGC::GetTextExtents( const unicode_t* s )
102 {
103 	cstrhash<cpoint, unicode_t>* h = 0;
104 
105 	for ( int i = 0; i < _list.count(); i++ )
106 		if ( _list[i].font == _font )
107 		{
108 			h = _list[i].pHash.ptr();
109 			break;
110 		}
111 
112 	if ( !h )
113 	{
114 		Node node( _font );
115 		h = node.pHash.ptr();
116 		_list.append( node );
117 	}
118 
119 	cpoint* pp = h->exist( s );
120 
121 	if ( pp )
122 	{
123 		return *pp;
124 	}
125 
126 	cpoint p = _gc->GetTextExtents( s );
127 	h->put( ( unicode_t* )s, p );
128 	return p;
129 }
130 
~HelpGC()131 HelpGC::~HelpGC() {};
132 
133 
134 
135 struct HelpNode: public iIntrusiveCounter
136 {
137 	HelpStyle* _style;
138 	//минимальная и максимальная ширина
139 	int _min; //если 0 то считается пробелом и может быть проигнорирован в начале или конце строки
140 	int _max;
141 
142 	cpoint _pos; //относительно хозяина (хозяином и выставляется после prepare)
143 	cpoint _size; //текущая ширина и высота
144 
HelpNodeHelpNode145 	HelpNode( HelpStyle* style, int min = 0, int max = 10000 ): _style( style ), _min( min ), _max( max ), _pos( 0, 0 ), _size( 0, 0 ) {}
146 	virtual void Init( HelpGC& gc ); //инициализируется по данным и определяет min и max
147 	virtual void Prepare( int width ); //подготовка к размеру, выставляет _size
148 	virtual void Paint( HelpGC& gc, int x, int y, bool selected, crect visibleRect ); //нарисовать в нужном месте (обязан зарисовать весь размер _size)
149 	virtual ~HelpNode();
150 private:
HelpNodeHelpNode151 	HelpNode() {}
152 };
153 
154 struct HelpNodeSpace: public HelpNode
155 {
156 	int _count;
157 
HelpNodeSpaceHelpNodeSpace158 	HelpNodeSpace( HelpStyle* s = 0, int n = 1 ): HelpNode( s ), _count( n ) {}
159 	virtual void Init( HelpGC& gc );
160 	virtual void Prepare( int width );
161 	virtual void Paint( HelpGC& gc, int x, int y, bool selected, crect visibleRect );
162 	virtual ~HelpNodeSpace();
163 };
164 
165 
166 struct HelpNodeH: public HelpNode
167 {
168 	int _count;
169 
HelpNodeHHelpNodeH170 	HelpNodeH( HelpStyle* s = 0, int n = 1 ): HelpNode( s ), _count( n ) {}
171 	virtual void Init( HelpGC& gc );
172 	virtual void Prepare( int width );
173 	virtual ~HelpNodeH();
174 };
175 
176 struct HelpNodeV: public HelpNode
177 {
178 	int _count;
179 
HelpNodeVHelpNodeV180 	HelpNodeV( HelpStyle* s = 0, int n = 1 ): HelpNode( s ), _count( n ) {}
181 	virtual void Init( HelpGC& gc );
182 	virtual void Prepare( int width );
183 	virtual ~HelpNodeV();
184 };
185 
186 //текст фиксированной длины и ширины
187 struct HelpNodeWord: public HelpNode
188 {
189 	std::vector<unicode_t> _txt;
190 
191 	HelpNodeWord( HelpStyle* style, const char* utf8, const char* addr = 0 );
192 	virtual void Init( HelpGC& gc );
193 	virtual void Prepare( int w );
194 	virtual void Paint( HelpGC& gc, int x, int y, bool selected, crect visibleRect );
195 	virtual ~HelpNodeWord();
196 };
197 
198 //список элементов (по умолчанию расположенных вертикально)
199 struct HelpNodeList: public HelpNode
200 {
201 
202 	struct Node
203 	{
204 		clPtr<HelpNode> item;
205 		bool paint; //расчитывается ProbeWidth
NodeHelpNodeList::Node206 		Node() {}
NodeHelpNodeList::Node207 		Node( clPtr<HelpNode> w ): item( w ) {}
208 	};
209 
210 	ccollect<Node> _list;
211 	//int currentSelected;
212 
213 	HelpNodeList( HelpStyle* style );
CountHelpNodeList214 	int Count() const { return _list.count(); }
215 	void Append( clPtr<HelpNode> item );
216 	virtual void Init( HelpGC& gc );
217 	virtual void Prepare( int width );
218 	virtual void Paint( HelpGC& gc, int x, int y, bool selected, crect visibleRect );
219 	virtual ~HelpNodeList();
220 };
221 
222 //абзац из элементов, элементы могут занать несколько строк
223 struct HelpNodeParagraph: public HelpNodeList
224 {
225 
226 
227 	enum {ALIGN_LEFT = 0, ALIGN_RIGHT, ALIGN_CENTER, ALIGN_WIDTH};
228 	int _align;
229 
HelpNodeParagraphHelpNodeParagraph230 	HelpNodeParagraph( HelpStyle* style ): HelpNodeList( style ), _align( ALIGN_LEFT ) {};
231 
SetAlignHelpNodeParagraph232 	void SetAlign( int a ) { _align = a; }
233 
234 	virtual void Init( HelpGC& gc );
235 	virtual void Prepare( int w );
236 	virtual ~HelpNodeParagraph();
237 };
238 
239 
240 struct HelpNodeTable: public HelpNode
241 {
242 	struct Pair
243 	{
244 		int minV, maxV;
PairHelpNodeTable::Pair245 		Pair(): minV( 0 ), maxV( 0 ) {}
PairHelpNodeTable::Pair246 		Pair( int _min, int _max ): minV( _min ), maxV( _max ) {}
247 	};
248 
249 	ccollect<clPtr<ccollect<clPtr<HelpNode> > > > _tab;
250 
251 	int _cols; //заполняется Init
252 	std::vector<Pair> _colPair; //создается Init
253 
AppendHelpNodeTable254 	void Append( clPtr<HelpNode> item ) { _tab[_tab.count() - 1]->append( item ); }
NLHelpNodeTable255 	void NL() { _tab.append( new ccollect< clPtr<HelpNode> > ); }
256 
HelpNodeTableHelpNodeTable257 	HelpNodeTable( HelpStyle* s ): HelpNode( s )
258 	{
259 		NL(); //добавляем первую строку
260 	};
261 
262 	virtual void Init( HelpGC& gc );
263 	virtual void Prepare( int width );
264 	virtual void Paint( HelpGC& gc, int x, int y, bool selected, crect visibleRect );
265 	virtual ~HelpNodeTable();
266 };
267 
268 
269 ///// HelpNode
270 
Init(HelpGC & gc)271 void HelpNode::Init( HelpGC& gc ) {}
272 
Prepare(int width)273 void HelpNode::Prepare( int width )
274 {
275 	if ( width > _max ) { width = _max; }
276 
277 	if ( width < _min ) { width = _min; }
278 
279 	_size.x = width;
280 }
Paint(HelpGC & gc,int x,int y,bool selected,crect visibleRect)281 void HelpNode::Paint( HelpGC& gc, int x, int y, bool selected, crect visibleRect ) {}
282 
~HelpNode()283 HelpNode::~HelpNode() {}
284 
285 
286 ///// HelpNodeSpace
287 
Paint(HelpGC & gc,int x,int y,bool selected,crect visibleRect)288 void HelpNodeSpace::Paint( HelpGC& gc, int x, int y, bool selected, crect visibleRect )
289 {
290 	gc.SetFillColor( _style->Bg() );
291 	crect r( x, y, x + _size.x, y + _size.y );
292 	gc.FillRect( r );
293 }
294 
Init(HelpGC & gc)295 void HelpNodeSpace::Init( HelpGC& gc )
296 {
297 	gc.Set( _style->Font() );
298 	static unicode_t sp[] = {' ', 0};
299 	_size = gc.GetTextExtents( sp );
300 	_size.x *= _count;
301 	_min = _max = _size.x;
302 }
303 
Prepare(int w)304 void HelpNodeSpace::Prepare( int w ) {}
305 
~HelpNodeSpace()306 HelpNodeSpace::~HelpNodeSpace() {}
307 
308 
309 
310 //регион (прямоугольный) из которого можно удалить прямоугольники и потом заполнить (не трогая удаленную площадь)
311 class HelpRgn
312 {
313 	struct Node
314 	{
315 		crect rect;
316 		Node* next;
NodeHelpRgn::Node317 		Node(): next( 0 ) {}
NodeHelpRgn::Node318 		Node( crect& r ): rect( r ), next( 0 ) {}
319 	};
320 	Node* list;
321 public:
HelpRgn(crect rect)322 	HelpRgn( crect rect ): list( 0 ) { if ( !rect.IsEmpty() ) { list = new Node( rect ); } }
Clear()323 	void Clear() { while ( list ) { Node* p = list; list = list->next; delete p; } }
324 
325 	void Minus( crect rect );
326 	int Fill( HelpGC& gc );
327 	int Fill( wal::GC& gc );
328 
329 	~HelpRgn();
330 };
331 
~HelpRgn()332 HelpRgn::~HelpRgn() { Clear(); }
333 
334 
Fill(HelpGC & gc)335 int HelpRgn::Fill( HelpGC& gc )
336 {
337 	int n = 0;
338 
339 	for ( Node* p = list; p; p = p->next, n++ ) { gc.FillRect( p->rect ); }
340 
341 	return n;
342 }
343 
344 
Fill(wal::GC & gc)345 int HelpRgn::Fill( wal::GC& gc )
346 {
347 	int n = 0;
348 
349 	for ( Node* p = list; p; p = p->next, n++ ) { gc.FillRect( p->rect ); }
350 
351 	return n;
352 }
353 
Minus(crect rect)354 void HelpRgn::Minus( crect rect )
355 {
356 	if ( rect.IsEmpty() ) { return; }
357 
358 	Node** pp = &list;
359 
360 	while ( *pp )
361 	{
362 		{
363 			//проверяем пересечение
364 			crect& pr = pp[0]->rect;
365 
366 			if ( pr.top >= rect.bottom || pr.bottom <= rect.top ||
367 			     pr.left >= rect.right || pr.right <= rect.left )
368 			{
369 				pp = &( pp[0]->next );
370 				continue;
371 			}
372 		}
373 
374 		crect r = pp[0]->rect;
375 
376 		{
377 			//free old
378 			Node* p = pp[0];
379 			pp[0] = p->next;
380 			delete p;
381 		}
382 
383 		int top = r.top;
384 
385 		if ( r.top < rect.top )
386 		{
387 			crect n( r.left, r.top, r.right, rect.top );
388 			ASSERT( !n.IsEmpty() );
389 			Node* p = new Node( n );
390 			p->next = pp[0];
391 			pp[0] = p;
392 			pp = &( p->next );
393 			top = rect.top;
394 		}
395 
396 		int bottom = r.bottom;
397 
398 		if ( r.bottom > rect.bottom )
399 		{
400 			crect n( r.left, rect.bottom, r.right, r.bottom );
401 			ASSERT( !n.IsEmpty() );
402 			Node* p = new Node( n );
403 			p->next = pp[0];
404 			pp[0] = p;
405 			pp = &( p->next );
406 			bottom = rect.bottom;
407 		}
408 
409 		if ( r.left < rect.left )
410 		{
411 			crect n( r.left, top, rect.left, bottom );
412 			ASSERT( !n.IsEmpty() );
413 			Node* p = new Node( n );
414 			p->next = pp[0];
415 			pp[0] = p;
416 			pp = &( p->next );
417 		}
418 
419 		if ( r.right > rect.right )
420 		{
421 			crect n( rect.right, top, r.right, bottom );
422 			ASSERT( !n.IsEmpty() );
423 			Node* p = new Node( n );
424 			p->next = pp[0];
425 			pp[0] = p;
426 			pp = &( p->next );
427 		}
428 	}
429 }
430 
431 
432 
433 ///// HelpNodeTable
434 
Init(HelpGC & gc)435 void HelpNodeTable::Init( HelpGC& gc )
436 {
437 	_cols = 0;
438 	int i;
439 
440 	for ( i = 0; i < _tab.count(); i++ )
441 		if ( _cols < _tab[i]->count() )
442 		{
443 			_cols = _tab[i]->count();
444 		}
445 
446 	if ( _cols <= 0 )
447 	{
448 		_size.Set( 0, 0 );
449 		_min = _max = 0;
450 		return;
451 	}
452 
453 	_colPair.resize( _cols );
454 
455 	for ( i = 0; i < _tab.count(); i++ )
456 	{
457 		for ( int j = 0; j < _tab[i]->count(); j++ )
458 		{
459 			HelpNode& a = *( _tab[i]->get( j ).ptr() );
460 			a.Init( gc );
461 
462 			if ( _colPair[j].minV < a._min ) { _colPair[j].minV = a._min; }
463 
464 			if ( _colPair[j].maxV < a._max ) { _colPair[j].maxV = a._max; }
465 		}
466 	}
467 
468 	_min = 0;
469 	_max = 0;
470 
471 	for ( i = 0; i < _cols; i++ )
472 	{
473 		_min += _colPair[i].minV;
474 		_max += _colPair[i].maxV;
475 	}
476 }
477 
Prepare(int width)478 void HelpNodeTable::Prepare( int width )
479 {
480 	if ( _cols < 0 )
481 	{
482 		_size.Set( 0, 0 );
483 		return;
484 	}
485 
486 	int w = _min;
487 	int n = _cols;
488 	int i;
489 
490 	std::vector<int> cw( _cols );
491 
492 	for ( i = 0; i < _cols; i++ ) { cw[i] = _colPair[i].minV; }
493 
494 	//распределяем добавочную длину
495 	while ( w < width && n > 0 )
496 	{
497 		int plus = ( width - w ) / n;
498 
499 		if ( !plus ) { break; }
500 
501 		n = 0;
502 
503 		for ( i = 0; i < _cols; i++ )
504 		{
505 			int t = _colPair[i].maxV - cw[i];
506 
507 			if ( t > plus ) { t = plus; }
508 
509 			if ( t > 0 )
510 			{
511 				cw[i] += t;
512 				w += t;
513 
514 				if ( cw[i] < _colPair[i].maxV )
515 				{
516 					n++;
517 				}
518 			}
519 		}
520 	}
521 
522 	int H = 0;
523 
524 	for ( i = 0; i < _tab.count(); i++ )
525 	{
526 		int lineH = 0;
527 		int x = 0;
528 
529 		for ( int j = 0; j < _tab[i]->count(); j++ )
530 		{
531 			HelpNode* p = _tab[i]->get( j ).ptr();
532 			p->Prepare( cw[j] );
533 
534 			if ( lineH < p->_size.y ) { lineH = p->_size.y; }
535 
536 			p->_pos.Set( x, H );
537 			x += cw[j];
538 		}
539 
540 		H += lineH;
541 	}
542 
543 	_size.Set( w, H );
544 }
545 
Paint(HelpGC & gc,int x,int y,bool selected,crect visibleRect)546 void HelpNodeTable::Paint( HelpGC& gc, int x, int y, bool selected, crect visibleRect )
547 {
548 	crect rect( x, y, x + _size.x, y + _size.y );
549 
550 	if ( rect.left < visibleRect.left ) { rect.left = visibleRect.left; }
551 
552 	if ( rect.right > visibleRect.right ) { rect.right = visibleRect.right; }
553 
554 	if ( rect.top < visibleRect.top ) { rect.top = visibleRect.top; }
555 
556 	if ( rect.bottom > visibleRect.bottom ) { rect.bottom = visibleRect.bottom; }
557 
558 	HelpRgn rgn( rect );
559 
560 	for ( int i = 0; i < _tab.count(); i++ )
561 	{
562 		for ( int j = 0; j < _tab[i]->count(); j++ )
563 		{
564 			HelpNode* p = _tab[i]->get( j ).ptr();
565 			int itemX = x + p->_pos.x;
566 			int itemY = y + p->_pos.y;
567 			cpoint iSize = p->_size;
568 
569 			crect r( itemX, itemY, itemX + iSize.x, itemY + iSize.y );
570 
571 			if ( r.Cross( visibleRect ) )
572 			{
573 				p->Paint( gc, itemX, itemY, selected, visibleRect );
574 				rgn.Minus( r );
575 			}
576 		}
577 	}
578 
579 	gc.SetFillColor( _style->Bg() );
580 	rgn.Fill( gc );
581 }
582 
~HelpNodeTable()583 HelpNodeTable::~HelpNodeTable() {}
584 
585 
586 ///// HelpNodeV
Init(HelpGC & gc)587 void HelpNodeV::Init( HelpGC& gc )
588 {
589 	gc.Set( _style->Font() );
590 	int V = gc.GetTextExtents( ABCString ).y;
591 	_min = -1;
592 	_max = 0;
593 	_size.x = 0;
594 	_size.y = ( V * _count ) / 10;
595 }
596 
Prepare(int w)597 void  HelpNodeV::Prepare( int w ) {}
598 
~HelpNodeV()599 HelpNodeV::~HelpNodeV() {}
600 
601 
602 ///// HelpNodeH
603 
Init(HelpGC & gc)604 void HelpNodeH::Init( HelpGC& gc )
605 {
606 	gc.Set( _style->Font() );
607 	int H = gc.GetTextExtents( ABCString ).x / ABCStringLen;
608 	_min = _max = _size.x = ( H * _count ) / 10;
609 	_size.y = 0;
610 }
611 
612 
Prepare(int w)613 void HelpNodeH::Prepare( int w ) {}
614 
~HelpNodeH()615 HelpNodeH::~HelpNodeH() {}
616 
617 ///// HelpNodeWord
HelpNodeWord(HelpStyle * style,const char * utf8,const char * addr)618 HelpNodeWord::HelpNodeWord( HelpStyle* style, const char* utf8, const char* addr )
619 	:  HelpNode( style ),
620 	   _txt( utf8_to_unicode( utf8 ) )
621 {
622 }
623 
Init(HelpGC & gc)624 void HelpNodeWord::Init( HelpGC& gc )
625 {
626 	gc.Set( _style->Font() );
627 	_size = gc.GetTextExtents( _txt.data() );
628 	_min = _max = _size.x;
629 }
630 
Prepare(int w)631 void HelpNodeWord::Prepare( int w ) {}
632 
Paint(HelpGC & gc,int x,int y,bool selected,crect visibleRect)633 void HelpNodeWord::Paint( HelpGC& gc, int x, int y, bool selected, crect visibleRect )
634 {
635 	gc.Set( _style->Font() );
636 	gc.SetFillColor( _style->Bg() );
637 	gc.SetTextColor( _style->Fg() );
638 	gc.TextOutF( x, y, _txt.data() );
639 }
640 
641 
~HelpNodeWord()642 HelpNodeWord::~HelpNodeWord() {}
643 
644 ///// HelpNodeList
645 
HelpNodeList(HelpStyle * style)646 HelpNodeList::HelpNodeList( HelpStyle* style )
647 	: HelpNode( style, 0, 0 )
648 {}
649 
Append(clPtr<HelpNode> word)650 void HelpNodeList::Append( clPtr<HelpNode> word )
651 {
652 	_list.append( Node( word ) );
653 }
654 
Init(HelpGC & gc)655 void HelpNodeList::Init( HelpGC& gc )
656 {
657 	_min = 0;
658 	_max = 0;
659 
660 	for ( int i = 0; i < _list.count(); i++ )
661 	{
662 		HelpNode* p = _list[i].item.ptr();
663 		p->Init( gc );
664 
665 		if ( _min < p->_min ) { _min = p->_min; }
666 
667 		if ( _max < p->_max ) { _max = p->_max; }
668 	}
669 }
670 
Prepare(int width)671 void HelpNodeList::Prepare( int width )
672 {
673 	//вертикальный расчет
674 	int count = _list.count();
675 
676 	int w = 0;
677 	int h = 0;
678 
679 	for ( int i = 0; i < count; i++ )
680 	{
681 		HelpNode* p = _list[i].item.ptr();
682 		p->Prepare( width );
683 		cpoint size = p->_size;
684 		p->_pos.x = 0;
685 		p->_pos.y = h;
686 		_list[i].paint = true;
687 		h += size.y;
688 
689 		if ( w < size.x ) { w = size.x; }
690 	}
691 
692 	_size.Set( w, h );
693 }
694 
695 
Paint(HelpGC & gc,int x,int y,bool selected,crect visibleRect)696 void HelpNodeList::Paint( HelpGC& gc, int x, int y, bool selected, crect visibleRect )
697 {
698 	crect rect( x, y, x + _size.x, y + _size.y );
699 
700 	if ( rect.left < visibleRect.left ) { rect.left = visibleRect.left; }
701 
702 	if ( rect.right > visibleRect.right ) { rect.right = visibleRect.right; }
703 
704 	if ( rect.top < visibleRect.top ) { rect.top = visibleRect.top; }
705 
706 	if ( rect.bottom > visibleRect.bottom ) { rect.bottom = visibleRect.bottom; }
707 
708 	HelpRgn rgn( rect );
709 
710 	int count = _list.count();
711 
712 	for ( int i = 0; i < count; i++ )
713 		if ( _list[i].paint )
714 		{
715 			int itemX = x + _list[i].item->_pos.x;
716 			int itemY = y + _list[i].item->_pos.y;
717 			cpoint iSize = _list[i].item->_size;
718 			crect r( itemX, itemY, itemX + iSize.x, itemY + iSize.y );
719 
720 			if ( r.Cross( visibleRect ) )
721 			{
722 				_list[i].item->Paint( gc, itemX, itemY, selected, visibleRect );
723 				rgn.Minus( r );
724 			}
725 		}
726 
727 	gc.SetFillColor( _style->Bg() );
728 	rgn.Fill( gc );
729 }
730 
731 
~HelpNodeList()732 HelpNodeList::~HelpNodeList() {}
733 
734 ///// HelpNodeParagraph
735 
Init(HelpGC & gc)736 void HelpNodeParagraph::Init( HelpGC& gc )
737 {
738 	int minW = 0, maxW = 0;
739 
740 	for ( int i = 0; i < _list.count(); i++ )
741 	{
742 		_list[i].item->Init( gc );
743 
744 		if ( minW < _list[i].item->_min ) { minW = _list[i].item->_min; }
745 
746 		maxW += _list[i].item->_max;
747 	}
748 
749 	_min = minW;
750 	_max = maxW;
751 }
752 
Prepare(int width)753 void HelpNodeParagraph::Prepare( int width )
754 {
755 	int w = _min, h = 0;
756 	int itemH = 0;
757 	int pos = 0;
758 	int i;
759 
760 	if ( w < width ) { w = width; }
761 
762 	//if (w>_max) w = _max;
763 
764 	ccollect<Node*> curLine;
765 
766 	for ( i = 0; i < _list.count(); i++ )
767 	{
768 		_list[i].item->Prepare( ( i == _list.count() - 1 ) ? width : 0 ); //для непоследних элементовпо минимальному размеру
769 		int n = _list[i].item->_size.x;
770 
771 		if ( pos > 0 && pos + n > w )
772 		{
773 			{
774 				//выравниваем
775 				if ( _align == ALIGN_RIGHT )
776 				{
777 					for ( int t = 0; t < curLine.count(); t++ ) { curLine[t]->item->_pos.x += w - pos; }
778 				}
779 				else if ( _align == ALIGN_CENTER )
780 				{
781 					for ( int t = 0; t < curLine.count(); t++ ) { curLine[t]->item->_pos.x += ( w - pos ) / 2; }
782 				}
783 				else if ( _align == ALIGN_WIDTH && curLine.count() > 1 )
784 				{
785 					int add = ( w - pos ) / ( curLine.count() - 1 );
786 					int n = 0;
787 
788 					for ( int t = 0; t < curLine.count(); t++, n += add )
789 					{
790 						curLine[t]->item->_pos.x += n;
791 					}
792 				}
793 			}
794 
795 			pos = 0;
796 			h += itemH;
797 			itemH = 0;
798 			curLine.clear();
799 		}
800 
801 		_list[i].item->_pos.x = pos;
802 		_list[i].item->_pos.y = h;
803 
804 		_list[i].paint = pos > 0 || _list[i].item->_min != 0; //игнорируем пробелы в начале строки
805 
806 		if ( _list[i].paint )
807 		{
808 			pos += n;
809 
810 			if ( itemH < _list[i].item->_size.y )
811 			{
812 				itemH = _list[i].item->_size.y;
813 			}
814 
815 			curLine.append( &( _list[i] ) );
816 		}
817 	}
818 
819 	if ( curLine.count() > 0 ) //выравниваем
820 	{
821 		if ( _align == ALIGN_RIGHT )
822 			for ( int t = 0; t < curLine.count(); t++ ) { curLine[t]->item->_pos.x += w - pos; }
823 		else if ( _align == ALIGN_CENTER )
824 			for ( int t = 0; t < curLine.count(); t++ ) { curLine[t]->item->_pos.x += ( w - pos ) / 2; }
825 	}
826 
827 
828 	h += itemH;
829 	_size.Set( w, h );
830 }
831 
~HelpNodeParagraph()832 HelpNodeParagraph::~HelpNodeParagraph() {};
833 
834 
835 
836 
837 
838 
839 
840 ///// HelpParzer
841 
842 class HelpParzerStream
843 {
844 public:
845 	virtual int GetChar(); //eof is 0
846 	virtual ~HelpParzerStream();
847 };
848 
GetChar()849 int HelpParzerStream::GetChar() { return 0; }
~HelpParzerStream()850 HelpParzerStream::~HelpParzerStream() {}
851 
852 class HelpParzerMem: public HelpParzerStream
853 {
854 	const char* str;
855 public:
HelpParzerMem(const char * s)856 	HelpParzerMem( const char* s ): str( s ) {}
857 	virtual int GetChar(); //eof is 0
858 	virtual ~HelpParzerMem();
859 };
860 
GetChar()861 int HelpParzerMem::GetChar() { return *str ? *( str++ ) : 0; }
~HelpParzerMem()862 HelpParzerMem::~HelpParzerMem() {}
863 
864 /*
865 
866    / - экранирующий символ, т.е. // - /
867 
868    /l /r /c /w -выравнивание абзаца (побеждает последний в абзаце) по умолчанию /l
869 
870    // %+ %- включить, выключить автоперенос (в пределах блока) по умолчанию включен
871 
872    <s [id]> смена текущего стиля (в пределах блока)
873    /n переход на новый абзац
874    <v [N]> -вертикальная вставка
875    <h [N]> -горизонтальная вставка (размар 1/10 от ширины символа текущего фонта (средней ширины A B и С))
876 
877    @t - таблица
878    @n - конец строки таблицы
879    @c - конец колонки в строке таблицы
880    @e - конец таблицы
881 
882    блок ограничивается символами { и }
883 
884 */
885 
886 struct HelpParzerBlockData
887 {
888 	HelpStyle* style;
HelpParzerBlockDataHelpParzerBlockData889 	HelpParzerBlockData(): style( 0 ) {}
890 };
891 
892 class HelpParzer
893 {
894 	HelpParzerStream* stream;
895 	cstrhash<HelpStyle>* styles;
896 
897 	int cchar;
898 	int backChar;
NextChar()899 	int NextChar() { if ( backChar ) { cchar = backChar; backChar = 0; } else { cchar = stream->GetChar(); } return cchar; }
PutCharBack(int c)900 	void PutCharBack( int c ) { backChar = cchar; cchar = c; }
901 
902 	enum Tokens { TOK_EOF = 0, TOK_SPACE = -1000, TOK_WORD, TOK_NL, TOK_ALEFT, TOK_ARIGHT, TOK_ACENTER, TOK_AWIDTH,
903 	              TOK_STYLE,
904 	              TOK_H, TOK_V,
905 	              TOK_TAB, TOK_TAB_END,
906 	              TOK_TAB_NL, TOK_TAB_NF
907 	            };
908 	int tok; //current token
909 	int num;
910 	ccollect<char, 0x100> word; //or style name
911 	int NextToken();
912 
913 	struct BlockNode
914 	{
915 		HelpParzerBlockData data;
916 		BlockNode* next;
BlockNodeHelpParzer::BlockNode917 		BlockNode( HelpParzerBlockData& d, BlockNode* nx ): data( d ), next( nx ) {}
918 	};
919 
920 	BlockNode* blockStack;
PopBlockData(HelpParzerBlockData & d)921 	void PopBlockData( HelpParzerBlockData& d ) { if ( blockStack ) { BlockNode* p = blockStack; blockStack = blockStack->next; currentBData = p->data; delete p; } }
PushBlockData(HelpParzerBlockData & d)922 	void PushBlockData( HelpParzerBlockData& d ) { blockStack = new BlockNode( d, blockStack ); }
923 
924 	HelpParzerBlockData currentBData;
925 
926 public:
HelpParzer(HelpParzerStream * _stream,cstrhash<HelpStyle> * _styles)927 	HelpParzer( HelpParzerStream* _stream, cstrhash<HelpStyle>* _styles )
928 		: stream( _stream )
929 		, styles( _styles )
930 		, cchar( 'a' )
931 		, backChar( 0 )
932 		, tok( 'a' )
933 		, blockStack( 0 )
934 	{
935 		if ( styles ) { currentBData.style = styles->exist( "def" ); }
936 
937 		NextChar();
938 		NextToken();
939 	}
940 
941 	clPtr<HelpNode> Parze();
942 	clPtr<HelpNode> ParzeTable();
943 
944 	~HelpParzer();
945 };
946 
NextToken()947 int HelpParzer::NextToken()
948 {
949 begin:
950 
951 	if ( tok == TOK_EOF ) { return tok; }
952 
953 	if ( IsSpace( cchar ) )
954 	{
955 		tok = TOK_SPACE;
956 
957 		while ( IsSpace( cchar ) ) { NextChar(); }
958 
959 		return tok;
960 	}
961 
962 	word.clear();
963 
964 	while ( true )
965 	{
966 		if ( IsSpace( cchar ) )
967 		{
968 			if ( word.count() > 0 ) { tok = TOK_WORD; word.append( 0 ); return tok; }
969 
970 			goto begin;
971 		}
972 
973 		if ( !cchar )
974 		{
975 			if ( word.count() > 0 ) { tok = TOK_WORD; word.append( 0 ); return tok; }
976 
977 			tok = TOK_EOF;
978 			return tok;
979 		}
980 
981 		if ( cchar == '{' || cchar == '}' )
982 		{
983 			if ( word.count() > 0 ) { tok = TOK_WORD; word.append( 0 ); return tok; }
984 
985 			tok = cchar;
986 			NextChar();
987 			return tok;
988 		}
989 
990 		if ( cchar == '/' )
991 		{
992 			NextChar();
993 
994 			if ( word.count() > 0 ) { PutCharBack( '/' ); tok = TOK_WORD; word.append( 0 ); return tok; }
995 
996 			switch ( cchar )
997 			{
998 				case 'n':
999 					tok = TOK_NL;
1000 					NextChar();
1001 					return tok;
1002 					break;
1003 
1004 				case 'l':
1005 					tok = TOK_ALEFT;
1006 					NextChar();
1007 					return tok;
1008 
1009 				case 'r':
1010 					tok = TOK_ARIGHT;
1011 					NextChar();
1012 					return tok;
1013 
1014 				case 'c':
1015 					tok = TOK_ACENTER;
1016 					NextChar();
1017 					return tok;
1018 
1019 				case 'w':
1020 					tok = TOK_AWIDTH;
1021 					NextChar();
1022 					return tok;
1023 
1024 				default:
1025 					word.append( cchar );
1026 					NextChar();
1027 					continue;
1028 			}
1029 
1030 			word.append( cchar );
1031 			NextChar();
1032 			continue;
1033 		}
1034 
1035 
1036 		if ( cchar == '@' )
1037 		{
1038 			NextChar();
1039 
1040 			if ( word.count() > 0 ) { PutCharBack( '@' ); tok = TOK_WORD; word.append( 0 ); return tok; }
1041 
1042 			switch ( cchar )
1043 			{
1044 				case 't':
1045 					tok = TOK_TAB;
1046 					NextChar();
1047 					return tok;
1048 
1049 				case 'e':
1050 					tok = TOK_TAB_END;
1051 					NextChar();
1052 					return tok;
1053 
1054 				case 'n':
1055 					tok = TOK_TAB_NL;
1056 					NextChar();
1057 					return tok;
1058 
1059 				case 'c':
1060 					tok = TOK_TAB_NF;
1061 					NextChar();
1062 					return tok;
1063 
1064 				default:
1065 					word.append( cchar );
1066 					NextChar();
1067 					continue;
1068 			}
1069 
1070 			word.append( cchar );
1071 			NextChar();
1072 			continue;
1073 		}
1074 
1075 
1076 		if ( cchar == '<' )
1077 		{
1078 			NextChar();
1079 
1080 			if ( word.count() > 0 ) { PutCharBack( '<' ); tok = TOK_WORD; word.append( 0 ); return tok; }
1081 
1082 			switch ( cchar )
1083 			{
1084 				case 's':
1085 					NextChar();
1086 
1087 					while ( IsSpace( cchar ) ) { NextChar(); }
1088 
1089 					while ( cchar && !IsSpace( cchar ) && cchar != '>' )
1090 					{
1091 						word.append( cchar );
1092 						NextChar();
1093 					}
1094 
1095 					word.append( 0 );
1096 
1097 					while ( IsSpace( cchar ) ) { NextChar(); }
1098 
1099 					if ( cchar == '>' ) { NextChar(); }
1100 
1101 					tok = TOK_STYLE;
1102 					return tok;
1103 
1104 				case 'v':
1105 				{
1106 					NextChar();
1107 
1108 					while ( IsSpace( cchar ) ) { NextChar(); }
1109 
1110 					int n = 0;
1111 
1112 					for ( ; cchar && cchar >= '0' && cchar <= '9'; NextChar() )
1113 					{
1114 						n = n * 10 + cchar - '0';
1115 					}
1116 
1117 					num = n ? n : 10;
1118 
1119 					while ( IsSpace( cchar ) ) { NextChar(); }
1120 
1121 					if ( cchar == '>' ) { NextChar(); }
1122 
1123 					tok = TOK_V;
1124 					return tok;
1125 				}
1126 
1127 				case 'h':
1128 				{
1129 					NextChar();
1130 
1131 					while ( IsSpace( cchar ) ) { NextChar(); }
1132 
1133 					int n = 0;
1134 
1135 					for ( ; cchar && cchar >= '0' && cchar <= '9'; NextChar() )
1136 					{
1137 						n = n * 10 + cchar - '0';
1138 					}
1139 
1140 					num = n ? n : 10;
1141 
1142 					while ( IsSpace( cchar ) ) { NextChar(); }
1143 
1144 					if ( cchar == '>' ) { NextChar(); }
1145 
1146 					tok = TOK_H;
1147 					return tok;
1148 				}
1149 
1150 
1151 				default:
1152 					word.append( cchar );
1153 					NextChar();
1154 					continue;
1155 			}
1156 
1157 			word.append( cchar );
1158 			NextChar();
1159 			continue;
1160 		}
1161 
1162 		word.append( cchar );
1163 		NextChar();
1164 	}
1165 
1166 	return tok;
1167 }
1168 
ParzeTable()1169 clPtr<HelpNode> HelpParzer::ParzeTable()
1170 {
1171 	HelpNodeTable* pTable;
1172 	clPtr<HelpNode> ret = pTable = new HelpNodeTable( currentBData.style );
1173 
1174 	while ( true )
1175 	{
1176 		if ( tok == TOK_EOF ) { break; }
1177 
1178 		if ( tok == TOK_TAB_END ) { NextToken(); break; }
1179 
1180 		switch ( tok )
1181 		{
1182 
1183 			case TOK_TAB_NL:
1184 				pTable->NL();
1185 				NextToken();
1186 				pTable->Append( Parze() );
1187 				continue;
1188 
1189 			case TOK_TAB_NF:
1190 				NextToken();
1191 				pTable->Append( Parze() );
1192 				continue;
1193 
1194 			default:
1195 				pTable->Append( Parze() );
1196 				continue;
1197 		};
1198 
1199 		NextToken();
1200 	}
1201 
1202 	return ret;
1203 }
1204 
1205 
Parze()1206 clPtr<HelpNode> HelpParzer::Parze()
1207 {
1208 	HelpNodeList* pList;
1209 	clPtr<HelpNode> ret = pList = new HelpNodeList( currentBData.style );
1210 
1211 	HelpNodeParagraph* pPar;
1212 	clPtr<HelpNode> paragraph = pPar = new HelpNodeParagraph( currentBData.style );
1213 
1214 	while ( true )
1215 	{
1216 		if ( tok == TOK_EOF ) { break; }
1217 
1218 		if ( tok == TOK_TAB_END ) { break; }
1219 
1220 		if ( tok == TOK_TAB_NL ) { break; }
1221 
1222 		if ( tok == TOK_TAB_NF ) { break; }
1223 
1224 		switch ( tok )
1225 		{
1226 			case '{':
1227 				this->PushBlockData( currentBData );
1228 				break;
1229 
1230 			case '}':
1231 				this->PopBlockData( currentBData );
1232 				break;
1233 
1234 			case TOK_SPACE:
1235 				pPar->Append( new HelpNodeSpace( this->currentBData.style, 1 ) );
1236 				break;
1237 
1238 			case TOK_V:
1239 				pPar->Append( new HelpNodeV( this->currentBData.style, num ) );
1240 				break;
1241 
1242 			case TOK_H:
1243 				pPar->Append( new HelpNodeH( this->currentBData.style, num ) );
1244 				break;
1245 
1246 			case TOK_WORD:
1247 				pPar->Append( new HelpNodeWord( this->currentBData.style, word.ptr(), 0 ) );
1248 				break;
1249 
1250 			case TOK_NL:
1251 				if ( pPar->Count() > 0 ) { pList->Append( paragraph ); }
1252 
1253 				paragraph = pPar = new HelpNodeParagraph( currentBData.style );
1254 				break;
1255 
1256 			case TOK_ALEFT:
1257 				pPar->SetAlign( HelpNodeParagraph::ALIGN_LEFT );
1258 				break;
1259 
1260 			case TOK_ARIGHT:
1261 				pPar->SetAlign( HelpNodeParagraph::ALIGN_RIGHT );
1262 				break;
1263 
1264 			case TOK_ACENTER:
1265 				pPar->SetAlign( HelpNodeParagraph::ALIGN_CENTER );
1266 				break;
1267 
1268 			case TOK_AWIDTH:
1269 				pPar->SetAlign( HelpNodeParagraph::ALIGN_WIDTH );
1270 				break;
1271 
1272 			case TOK_STYLE:
1273 			{
1274 				HelpStyle* s = styles->exist( word.ptr() );
1275 
1276 				if ( s ) { currentBData.style = s; }
1277 			}
1278 			break;
1279 
1280 			case TOK_TAB:
1281 				NextToken();
1282 				pPar->Append( ParzeTable() );
1283 				//ParzeTable забирает токен окончания таблицы
1284 				continue;
1285 
1286 			default:
1287 				break;
1288 		};
1289 
1290 		NextToken();
1291 	};
1292 
1293 	//if (pPar->Count()>0)
1294 	pList->Append( paragraph );
1295 
1296 	return ret;
1297 }
1298 
~HelpParzer()1299 HelpParzer::~HelpParzer() {}
1300 
1301 
1302 
1303 class HelpFile
1304 {
1305 	cstrhash< std::vector<char> > hash;
1306 	bool loaded;
1307 	bool LoadFile( sys_char_t* name );
1308 	void Load();
1309 public:
HelpFile()1310 	HelpFile(): loaded( false ) {}
1311 	const char* GetTheme( const char* theme );
1312 	~HelpFile();
1313 };
1314 
LoadFile(sys_char_t * name)1315 bool HelpFile::LoadFile( sys_char_t* name )
1316 {
1317 	try
1318 	{
1319 		BFile f;
1320 		std::vector<char> collector;
1321 //printf("file-'%s'\n", name);
1322 		f.Open( name );
1323 
1324 		std::vector<char> thName;
1325 
1326 		char buf[4096];
1327 
1328 		while ( f.GetStr( buf, sizeof( buf ) ) )
1329 		{
1330 			char* s = buf;
1331 
1332 			if ( s[0] == '[' )
1333 			{
1334 				s++;
1335 
1336 				while ( IsSpace( *s ) ) { s++; }
1337 
1338 				std::vector<char> str;
1339 
1340 				while ( *s && *s != ']' && !IsSpace( *s ) ) { str.push_back( *s ); s++; }
1341 
1342 				while ( IsSpace( *s ) ) { s++; }
1343 
1344 				if ( *s == ']' && str.size() > 0 ) //ok
1345 				{
1346 
1347 					str.push_back( 0 );
1348 
1349 					if ( thName.data() )
1350 					{
1351 						collector.push_back( 0 );
1352 						hash[ thName.data() ] = collector;
1353 					}
1354 
1355 					thName = str;
1356 					continue;
1357 				}
1358 			}
1359 
1360 			if ( thName.data() )
1361 			{
1362 				int n = strlen( buf );
1363 
1364 				std::copy( buf, buf + n, std::back_inserter( collector ) );
1365 			}
1366 
1367 		}
1368 
1369 		if ( thName.data() )
1370 		{
1371 			collector.push_back( 0 );
1372 			hash[ thName.data() ] = collector;
1373 		}
1374 
1375 		f.Close();
1376 
1377 	}
1378 	catch ( cexception* ex )
1379 	{
1380 		ex->destroy();
1381 		return false;
1382 	}
1383 
1384 	return true;
1385 }
1386 
1387 
Load()1388 void HelpFile::Load()
1389 {
1390 	if ( loaded ) { return; }
1391 
1392 	loaded = true;
1393 
1394 	const char* langId = g_WcmConfig.systemLang.data() ? g_WcmConfig.systemLang.data() : "+";
1395 
1396 	if ( langId[0] == '-' ) { return; }
1397 
1398 	if ( langId[0] != '+' )
1399 	{
1400 #ifdef _WIN32
1401 		LoadFile( carray_cat<sys_char_t>( GetAppPath().data(),
1402 		                                  utf8_to_sys( carray_cat<char>( "\\lang\\help.", langId ).data() ).data() ).data() );
1403 #else
1404 
1405 		if ( !LoadFile( utf8_to_sys( carray_cat<char>( "install-files/share/wcm/lang/help.", langId ).data() ).data() ) )
1406 		{
1407 			LoadFile( utf8_to_sys( carray_cat<char>( UNIX_CONFIG_DIR_PATH "/lang/help.", langId ).data() ).data() );
1408 		}
1409 
1410 #endif
1411 		return;
1412 	};
1413 
1414 #ifdef _WIN32
1415 	if ( !LoadFile( carray_cat<sys_char_t>( GetAppPath().data(),
1416 	                                        utf8_to_sys( carray_cat<char>( "\\lang\\help.", sys_locale_lang_ter() ).data() ).data() ).data() )
1417 	   )
1418 		LoadFile( carray_cat<sys_char_t>( GetAppPath().data(),
1419 		                                  utf8_to_sys( carray_cat<char>( "\\lang\\help.", sys_locale_lang() ).data() ).data() ).data() );
1420 
1421 #else
1422 
1423 	if (
1424 	   !LoadFile( utf8_to_sys( carray_cat<char>( "install-files/share/wcm/lang/help.", sys_locale_lang_ter() ).data() ).data() ) &&
1425 	   !LoadFile( utf8_to_sys( carray_cat<char>( "install-files/share/wcm/lang/help.", sys_locale_lang() ).data() ).data() ) &&
1426 	   !LoadFile( utf8_to_sys( carray_cat<char>( UNIX_CONFIG_DIR_PATH "/lang/help.", sys_locale_lang_ter() ).data() ).data() )
1427 	)
1428 	{
1429 		LoadFile( utf8_to_sys( carray_cat<char>( UNIX_CONFIG_DIR_PATH "/lang/help.", sys_locale_lang() ).data() ).data() );
1430 	}
1431 
1432 #endif
1433 }
1434 
GetTheme(const char * theme)1435 const char* HelpFile::GetTheme( const char* theme )
1436 {
1437 	Load();
1438 	std::vector<char>* p = hash.exist( theme );
1439 	return p ? p->data() : 0;
1440 }
1441 
1442 
~HelpFile()1443 HelpFile::~HelpFile() {}
1444 
1445 
1446 
1447 /////
1448 
1449 
1450 #include "nc.h"
1451 
1452 extern const char* helpData_main;
1453 extern const char* helpData_edit;
1454 extern const char* helpData_view;
1455 
1456 static HelpFile helpFile;
1457 
GetHelpNode(const char * theme,cstrhash<HelpStyle> * pStyles)1458 static clPtr<HelpNode> GetHelpNode( const char* theme, cstrhash<HelpStyle>* pStyles )
1459 {
1460 	const char* ptext = helpFile.GetTheme( theme );
1461 
1462 	if ( !ptext )
1463 	{
1464 		if ( !strcmp( theme, "main" ) ) { ptext = helpData_main; }
1465 		else if ( !strcmp( theme, "edit" ) ) { ptext = helpData_edit; }
1466 		else if ( !strcmp( theme, "view" ) ) { ptext = helpData_view; }
1467 	}
1468 
1469 	if ( !ptext ) { return 0; }
1470 
1471 	HelpParzerMem mstream( ptext );
1472 	HelpParzer parzer( &mstream, pStyles );
1473 	return parzer.Parze();
1474 }
1475 
1476 int UiClassHelpWin  = GetUiID( "HelpWin" );
1477 
1478 class HelpWin: public Win
1479 {
1480 	clPtr<HelpNode> data;
1481 	int dataWidth;
1482 	int dataHeight;
1483 
1484 	int xOffset;
1485 	int yOffset;
1486 
1487 	int captureDelta;
1488 	ScrollBar vScroll;
1489 	ScrollBar hScroll;
1490 	Layout layout;
1491 	crect helpRect;
1492 	crect scrollRect;
1493 	cstrhash<HelpStyle> styles;
1494 
1495 	HelpStyle style_head;
1496 	HelpStyle style_def;
1497 	HelpStyle style_red;
1498 	HelpStyle style_bold;
1499 
1500 protected:
1501 	void CalcScroll();
1502 public:
1503 	HelpWin( const char* theme, Win* _parent, crect* rect );
Ok()1504 	bool Ok() { return data.ptr() != 0; }
1505 	void MoveXOffset( int n );
1506 	void MoveYOffset( int n );
1507 	virtual void Paint( wal::GC& gc, const crect& paintRect );
1508 	virtual void EventSize( cevent_size* pEvent );
1509 	virtual bool EventKey( cevent_key* pEvent );
1510 	virtual bool EventMouse( cevent_mouse* pEvent );
1511 	virtual bool Command( int id, int subId, Win* win, void* data );
1512 	virtual void EventTimer( int tid );
1513 	virtual int UiGetClassId();
1514 	virtual ~HelpWin();
1515 };
1516 
UiGetClassId()1517 int HelpWin::UiGetClassId() { return UiClassHelpWin; }
1518 
1519 static int uiClassHelpWin  = GetUiID( "HelpWin" );
1520 static int uiStyleHead = GetUiID( "style-head" );
1521 static int uiStyleDef = GetUiID( "style-def" );
1522 static int uiStyleRed = GetUiID( "style-red" );
1523 static int uiStyleBold = GetUiID( "style-bold" );
1524 
HelpWin(const char * theme,Win * parent,crect * rect)1525 HelpWin::HelpWin( const char* theme, Win* parent, crect* rect )
1526 	:  Win( Win::WT_CHILD, WH_TABFOCUS | WH_CLICKFOCUS, parent, rect, uiClassHelpWin ),
1527 	   data( 0 ),
1528 	   xOffset( 0 ), yOffset( 0 ),
1529 	   captureDelta( 0 ),
1530 	   vScroll( 0, this, true, true ),
1531 	   hScroll( 0, this, false ),
1532 	   layout( 4, 4 ),
1533 	   style_head( g_HelpHeadFont.ptr(), UiGetColor( uiColor, uiStyleHead, 0, 0 ), UiGetColor( uiBackground, uiStyleHead, 0, 0xFFFFFF ) ),
1534 	   style_def ( g_HelpTextFont.ptr(), UiGetColor( uiColor, uiStyleDef, 0, 0 ), UiGetColor( uiBackground, uiStyleDef, 0, 0xFFFFFF ) ),
1535 	   style_red ( g_HelpTextFont.ptr(), UiGetColor( uiColor, uiStyleRed, 0, 0 ), UiGetColor( uiBackground, uiStyleRed, 0, 0xFFFFFF ) ),
1536 	   style_bold( g_HelpBoldFont.ptr(), UiGetColor( uiColor, uiStyleBold, 0, 0 ), UiGetColor( uiBackground, uiStyleBold, 0, 0xFFFFFF ) )
1537 {
1538 	styles["def"] = style_def;
1539 	styles["red"] = style_red;
1540 	styles["head"] = style_head;
1541 	styles["bold"] = style_bold;
1542 
1543 	vScroll.Show();
1544 	vScroll.Enable();
1545 	hScroll.Show();
1546 	hScroll.Enable();
1547 
1548 	vScroll.SetManagedWin( this );
1549 	hScroll.SetManagedWin( this );
1550 
1551 	layout.AddWin( &vScroll, 1, 2 );
1552 	layout.AddWin( &hScroll, 2, 1 );
1553 	layout.AddRect( &helpRect, 1, 1 );
1554 	layout.AddRect( &scrollRect, 2, 2 );
1555 	layout.LineSet( 0, 2 );
1556 	layout.LineSet( 3, 2 );
1557 	layout.ColSet( 0, 2 );
1558 	layout.ColSet( 3, 2 );
1559 	layout.SetColGrowth( 1 );
1560 	layout.SetLineGrowth( 1 );
1561 	this->SetLayout( &layout );
1562 	this->RecalcLayouts();
1563 
1564 	data = GetHelpNode( theme, &styles );
1565 
1566 	if ( data.ptr() )
1567 	{
1568 		wal::GC gc( this );
1569 		HelpGC hgc( &gc );
1570 		data->Init( hgc );
1571 		data->Prepare( 600 );
1572 	}
1573 }
1574 
CalcScroll()1575 void HelpWin::CalcScroll()
1576 {
1577 	if ( dataHeight <= 0 ) { dataHeight = 1; }
1578 
1579 	if ( dataWidth <= 0 ) { dataWidth = 1; }
1580 
1581 	ScrollInfo vsi, hsi;
1582 	vsi.m_PageSize = helpRect.Height();
1583 	vsi.m_Size = dataHeight;
1584 	vsi.m_Pos = yOffset;
1585 
1586 	hsi.m_PageSize = helpRect.Width();
1587 	hsi.m_Size = dataWidth;
1588 	hsi.m_Pos = xOffset;
1589 
1590 	bool vVisible = vScroll.IsVisible();
1591 	vScroll.Command( CMD_SCROLL_INFO, SCMD_SCROLL_VCHANGE, this, &vsi );
1592 
1593 	bool hVisible = hScroll.IsVisible();
1594 	hScroll.Command( CMD_SCROLL_INFO, SCMD_SCROLL_HCHANGE, this, &hsi );
1595 
1596 	if ( vVisible != vScroll.IsVisible() || hVisible != hScroll.IsVisible() )
1597 	{
1598 		this->RecalcLayouts();
1599 	}
1600 
1601 }
1602 
MoveXOffset(int n)1603 void HelpWin::MoveXOffset( int n )
1604 {
1605 	if ( n + helpRect.Width() >= dataWidth ) { n = dataWidth - helpRect.Width(); }
1606 
1607 	if ( n < 0 ) { n = 0; }
1608 
1609 	if ( xOffset != n )
1610 	{
1611 		xOffset = n;
1612 		CalcScroll();
1613 		Invalidate();
1614 	}
1615 }
1616 
MoveYOffset(int n)1617 void HelpWin::MoveYOffset( int n )
1618 {
1619 	if ( n + helpRect.Height() >= dataHeight ) { n = dataHeight - helpRect.Height(); }
1620 
1621 	if ( n < 0 ) { n = 0; }
1622 
1623 	if ( yOffset != n )
1624 	{
1625 		yOffset = n;
1626 		CalcScroll();
1627 		Invalidate();
1628 	}
1629 }
1630 
1631 
Command(int id,int subId,Win * win,void * data)1632 bool HelpWin::Command( int id, int subId, Win* win, void* data )
1633 {
1634 	if ( id == CMD_SCROLL_INFO )
1635 	{
1636 		switch ( subId )
1637 		{
1638 			case SCMD_SCROLL_LINE_UP:
1639 				MoveYOffset( yOffset - 10 );
1640 				break;
1641 
1642 			case SCMD_SCROLL_LINE_DOWN:
1643 				MoveYOffset( yOffset + 10 );
1644 				break;
1645 
1646 			case SCMD_SCROLL_PAGE_UP:
1647 				MoveYOffset( yOffset - helpRect.Height() );
1648 				break;
1649 
1650 			case SCMD_SCROLL_PAGE_DOWN:
1651 				MoveYOffset( yOffset + helpRect.Height() );
1652 				break;
1653 
1654 			case SCMD_SCROLL_LINE_LEFT:
1655 				MoveXOffset( xOffset - 10 );
1656 				break;
1657 
1658 			case SCMD_SCROLL_LINE_RIGHT:
1659 				MoveXOffset( xOffset + 10 );
1660 				break;
1661 
1662 			case SCMD_SCROLL_PAGE_LEFT:
1663 				MoveXOffset( xOffset - helpRect.Width() );
1664 				break;
1665 
1666 			case SCMD_SCROLL_PAGE_RIGHT:
1667 				MoveXOffset( xOffset + helpRect.Width() );
1668 				break;
1669 
1670 			case SCMD_SCROLL_TRACK:
1671 				if ( win == &vScroll )
1672 				{
1673 					MoveYOffset( ( ( int* )data )[0] );
1674 				}
1675 				else
1676 				{
1677 					MoveXOffset( ( ( int* )data )[0] );
1678 				}
1679 
1680 				break;
1681 		}
1682 
1683 		return true;
1684 	}
1685 
1686 	return Win::Command( id, subId, win, data );
1687 }
1688 
Paint(wal::GC & gc,const crect & paintRect)1689 void HelpWin::Paint( wal::GC& gc, const crect& paintRect )
1690 {
1691 	crect rect = ClientRect();
1692 	Draw3DButtonW2( gc, rect, 0x808080, false );
1693 
1694 	if ( !scrollRect.IsEmpty() )
1695 	{
1696 		gc.SetFillColor( 0xD0D0D0 );
1697 		gc.FillRect( scrollRect ); //CCC
1698 	}
1699 
1700 	rect.Dec();
1701 
1702 	if ( data.ptr() )
1703 	{
1704 		HelpRgn rgn( rect );
1705 		rgn.Minus( crect( helpRect.left - xOffset, helpRect.top - yOffset,
1706 		                  helpRect.left + data->_size.x - xOffset, helpRect.top + data->_size.y - yOffset ) );
1707 		gc.SetFillColor( data->_style->Bg() );
1708 		rgn.Fill( gc );
1709 
1710 		gc.SetClipRgn( &helpRect );
1711 		HelpGC hgc( &gc );
1712 		data->Paint( hgc, helpRect.left - xOffset, helpRect.top - yOffset, false, rect );
1713 	}
1714 	else
1715 	{
1716 		gc.SetFillColor( 0xFFFFFF ); //0x808080);
1717 		gc.FillRect( rect );
1718 	}
1719 }
1720 
EventSize(cevent_size * pEvent)1721 void HelpWin::EventSize( cevent_size* pEvent )
1722 {
1723 	if ( data.ptr() )
1724 	{
1725 		data->Prepare( helpRect.Width() < 500 ? 500 : helpRect.Width() );
1726 		dataWidth = data->_size.x;
1727 		dataHeight = data->_size.y;
1728 	}
1729 	else
1730 	{
1731 		dataWidth = 1;
1732 		dataHeight = 1;
1733 	}
1734 
1735 	this->CalcScroll();
1736 }
1737 
EventMouse(cevent_mouse * pEvent)1738 bool HelpWin::EventMouse( cevent_mouse* pEvent )
1739 {
1740 	if ( !IsEnabled() ) { return false; }
1741 
1742 	if ( pEvent->Type() == EV_MOUSE_PRESS )
1743 	{
1744 
1745 		if ( pEvent->Button() == MB_X1 )
1746 		{
1747 			MoveYOffset( yOffset - helpRect.Height() / 3 );
1748 			return true;
1749 		}
1750 
1751 		if ( pEvent->Button() == MB_X2 )
1752 		{
1753 			MoveYOffset( yOffset + helpRect.Height() / 3 );
1754 			return true;
1755 		}
1756 	}
1757 
1758 
1759 //	int n = (pEvent->Point().y - helpRect.top)/this->itemHeight + first;
1760 
1761 	/*
1762 	   if (pEvent->Type() == EV_MOUSE_PRESS && pEvent->Button() == MB_L && listRect.In(pEvent->Point()))
1763 	   {
1764 	      captureDelta=0;
1765 	      MoveCurrent(n);
1766 	      this->SetCapture();
1767 	      this->SetTimer(0,100);
1768 	      return true;
1769 	   }
1770 
1771 	   if (pEvent->Type() == EV_MOUSE_DOUBLE)
1772 	   {
1773 	      MoveCurrent(n);
1774 	      Command(CMD_ITEM_CLICK, GetCurrent(), this, 0);
1775 	      return true;
1776 	   }
1777 	*/
1778 	if ( pEvent->Type() == EV_MOUSE_MOVE && IsCaptured() )
1779 	{
1780 		if ( pEvent->Point().y >= helpRect.top && pEvent->Point().y <= helpRect.bottom )
1781 		{
1782 			captureDelta = 0;
1783 		}
1784 		else
1785 		{
1786 			captureDelta = ( pEvent->Point().y > helpRect.bottom ) ? +1 : ( pEvent->Point().y < helpRect.top ) ? -1 : 0;
1787 		}
1788 
1789 		return true;
1790 	}
1791 
1792 	if ( pEvent->Type() == EV_MOUSE_RELEASE && pEvent->Button() == MB_L )
1793 	{
1794 		this->ReleaseCapture();
1795 		this->DelTimer( 0 );
1796 		return true;
1797 	}
1798 
1799 	return false;
1800 }
1801 
EventTimer(int tid)1802 void HelpWin::EventTimer( int tid )
1803 {
1804 	if ( tid == 0 && captureDelta )
1805 	{
1806 //		int n = current + captureDelta;
1807 //		if (n>=0 && n<this->count)
1808 //			MoveCurrent(n);
1809 	}
1810 }
1811 
EventKey(cevent_key * pEvent)1812 bool HelpWin::EventKey( cevent_key* pEvent )
1813 {
1814 	if ( pEvent->Type() == EV_KEYDOWN )
1815 	{
1816 		switch ( pEvent->Key() )
1817 		{
1818 			case VK_DOWN:
1819 				MoveYOffset( yOffset + 10 );
1820 				break;
1821 
1822 			case VK_UP:
1823 				MoveYOffset( yOffset - 10 );
1824 				break;
1825 
1826 			case VK_END:
1827 				MoveYOffset( dataHeight - helpRect.Height() );
1828 				break;
1829 
1830 			case VK_HOME:
1831 				MoveYOffset( 0 );
1832 				break;
1833 
1834 			case VK_PRIOR:
1835 				MoveYOffset( yOffset - helpRect.Height() );
1836 				break;
1837 
1838 			case VK_NEXT:
1839 				MoveYOffset( yOffset + helpRect.Height() );
1840 				break;
1841 
1842 			case VK_LEFT:
1843 				MoveXOffset( xOffset - 10 );
1844 				break;
1845 
1846 			case VK_RIGHT:
1847 				MoveXOffset( xOffset + 10 );
1848 				break;
1849 
1850 			default:
1851 				return false;
1852 		}
1853 
1854 		Invalidate();
1855 		return true;
1856 	}
1857 
1858 	return false;
1859 }
1860 
~HelpWin()1861 HelpWin::~HelpWin() {}
1862 
1863 
1864 
1865 
1866 /////
1867 class HelpDlg: public NCDialog
1868 {
1869 	Layout lo;
1870 	StaticLine ver;
1871 	HelpWin helpWin;
1872 //	cstrhash<HelpStyle> styles;
1873 public:
HelpDlg(NCDialogParent * parent,const char * name,const char * theme)1874 	HelpDlg( NCDialogParent* parent, const char* name, const char* theme )
1875 		:  NCDialog( ::createDialogAsChild, 0, parent, utf8_to_unicode( name ).data(), bListCancel ),
1876 		   lo( 10, 10 ),
1877 		   ver( 0, this, utf8_to_unicode( verString ).data() ),
1878 		   helpWin( theme, this,  0 )
1879 	{
1880 		lo.SetColGrowth( 0 );
1881 		lo.SetColGrowth( 2 );
1882 
1883 		ver.Show();
1884 		ver.Enable();
1885 		lo.AddWin( &ver, 0, 1 );
1886 		helpWin.Show();
1887 		helpWin.Enable();
1888 		lo.LineSet( 1, 5 );
1889 		lo.AddWin( &helpWin, 2, 0, 2, 2 );
1890 
1891 		this->AddLayout( &lo );
1892 		this->SetEnterCmd( CMD_OK );
1893 
1894 		MaximizeIfChild();
1895 
1896 		if ( !createDialogAsChild )
1897 		{
1898 			LSize ls = helpWin.GetLSize();
1899 
1900 			if ( ls.x.minimal < 500 ) { ls.x.minimal = 500; }
1901 
1902 			if ( ls.y.minimal < 300 ) { ls.y.minimal = 300; }
1903 
1904 			helpWin.SetLSize( ls );
1905 			SetPosition();
1906 		}
1907 
1908 		helpWin.SetFocus();
1909 	}
Ok()1910 	bool Ok() { return helpWin.Ok(); }
1911 	virtual bool Command( int id, int subId, Win* win, void* data );
1912 	virtual ~HelpDlg();
1913 };
1914 
Command(int id,int subId,Win * win,void * data)1915 bool HelpDlg::Command( int id, int subId, Win* win, void* data )
1916 {
1917 	return NCDialog::Command( id, subId, win, data );
1918 }
1919 
~HelpDlg()1920 HelpDlg::~HelpDlg() {}
1921 
1922 
Help(NCDialogParent * parent,const char * theme)1923 void Help( NCDialogParent* parent, const char* theme )
1924 {
1925 	HelpDlg dlg( parent, _LT( "Help" ), theme );
1926 
1927 	if ( !dlg.Ok() ) { return; } //не нашел тему
1928 
1929 	dlg.DoModal();
1930 }
1931