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