1 /*
2    Copyright (c) by Valery Goryachev (Wal)
3 */
4 
5 #include <string>
6 #include <unordered_map>
7 
8 #include "string-util.h"
9 #include "swl.h"
10 #include "swl_wincore_internal.h"
11 
12 namespace wal
13 {
14 
15 	struct WINHASHNODE
16 	{
17 		WinID id;
18 		Win* win;
19 
WINHASHNODEwal::WINHASHNODE20 		WINHASHNODE( WinID h, Win* w ): id( h ), win( w ) {}
WINHASHNODEwal::WINHASHNODE21 		WINHASHNODE(): id( 0 ), win ( nullptr ) {}
22 
keywal::WINHASHNODE23 		const WinID& key() const { return id; };
24 	};
25 
26 	static std::unordered_map<WinID, WINHASHNODE> winhash;
27 	WinID Win::focusWinId = 0;
28 
29 
GetWinByID(WinID hWnd)30 	Win* GetWinByID( WinID hWnd )
31 	{
32 		WinID id( hWnd );
33 		auto i = winhash.find(hWnd);
34 		const WINHASHNODE* node = ( i == winhash.end() ) ? nullptr : &(i->second);
35 		return node ? node->win : 0;
36 	}
37 
DelWinFromHash(WinID w)38 	int DelWinFromHash( WinID w )
39 	{
40 		winhash.erase(w);
41 		return winhash.size();
42 	}
43 
AddWinToHash(WinID handle,Win * w)44 	void AddWinToHash( WinID handle, Win* w )
45 	{
46 		WINHASHNODE node( handle, w );
47 		winhash[handle] = node;
48 	}
49 
50 
ForeachBlock(const WINHASHNODE * t,void * data)51 	static void ForeachBlock( const WINHASHNODE* t, void* data ) { t->win->Block( *( ( WinID* )data ) );}
ForeachUnblock(const WINHASHNODE * t,void * data)52 	static void ForeachUnblock( const WINHASHNODE* t, void* data ) {  t->win->Unblock( *( ( WinID* )data ) );}
AppBlock(WinID w)53 	void AppBlock( WinID w ) { for ( const auto& i : winhash ) ForeachBlock( &(i.second), &w ); }
AppUnblock(WinID w)54 	void AppUnblock( WinID w ) { for ( const auto& i : winhash ) ForeachUnblock( &(i.second), &w ); }
55 
56 
57 	struct TimerStruct
58 	{
59 		WinID winId;
60 		int timerId;
61 		unsigned period;
62 		unsigned last;
63 	};
64 
65 	static ccollect<TimerStruct> timerList;
66 	unsigned GetTickMiliseconds();
67 
__TimerAppend(WinID wid,int tid,unsigned period)68 	static void __TimerAppend( WinID wid, int tid, unsigned period )
69 	{
70 		if ( !period ) { period = 0; }
71 
72 		TimerStruct ts;
73 		ts.winId = wid;
74 		ts.timerId = tid;
75 		ts.period = period;
76 		ts.last = GetTickMiliseconds();
77 		timerList.append( ts );
78 	}
79 
TimerDel(WinID wid,int tid)80 	static void TimerDel( WinID wid, int tid )
81 	{
82 		for ( int i = 0, n = timerList.count(); i < n; )
83 		{
84 			TimerStruct* ts = timerList.ptr() + i;
85 
86 			if ( ts->winId == wid && ts->timerId == tid )
87 			{
88 				timerList.del( i );
89 				n--;
90 			}
91 			else { i++; }
92 		}
93 	}
94 
TimerReset(WinID wid,int tid,unsigned period)95 	static void TimerReset( WinID wid, int tid, unsigned period )
96 	{
97 		if ( !period ) { period = 0; }
98 
99 		for ( int i = 0, n = timerList.count(); i < n; i++ )
100 		{
101 			TimerStruct* ts = timerList.ptr() + i;
102 
103 			if ( ts->winId == wid && ts->timerId == tid )
104 			{
105 				timerList[i].period = period;
106 				return;
107 			}
108 		}
109 
110 		__TimerAppend( wid, tid, period );
111 	}
112 
TimerDel(WinID wid)113 	void TimerDel( WinID wid )
114 	{
115 		for ( int i = 0; i < timerList.count(); )
116 		{
117 			if ( timerList[i].winId == wid )
118 			{
119 				timerList.del( i );
120 			}
121 			else
122 			{
123 				i++;
124 			}
125 		}
126 	}
127 
128 	struct RunTimersNode
129 	{
130 		WinID wid;
131 		int tid;
132 	};
133 
134 
RunTimers()135 	unsigned RunTimers()
136 	{
137 
138 		ccollect<RunTimersNode> runList;
139 		unsigned tim = GetTickMiliseconds();
140 		int i, n;
141 
142 //printf("count timers %i\n", timerList.count());
143 		for ( i = 0, n = timerList.count(); i < n; i++ )
144 		{
145 			TimerStruct* ts = timerList.ptr() + i;
146 
147 //printf("tim%i (%i,%i)\n",1, tim, ts->last);
148 
149 			if ( tim - ts->last >= ts->period ) //! условие должно соответствовать условию *
150 			{
151 				ts->last = tim;
152 				RunTimersNode node;
153 				node.wid = ts->winId;
154 				node.tid = ts->timerId;
155 				runList.append( node );
156 			}
157 		}
158 
159 //printf("running timers %i\n", runList.count());
160 		for ( i = 0; i < runList.count(); )
161 		{
162 			Win* w = GetWinByID( runList[i].wid );
163 
164 			if ( w )
165 			{
166 				w->EventTimer( runList[i].tid ); //...
167 				i++;
168 			}
169 			else { runList.del( i ); }
170 		}
171 
172 		//DWORD
173 		unsigned minTim = 0xFFFF;
174 		tim = GetTickMiliseconds();
175 
176 		for ( i = 0, n = timerList.count(); i < n; i++ )
177 		{
178 			TimerStruct* ts = timerList.ptr() + i;
179 			//DWORD
180 			unsigned t = tim - ts->last;
181 
182 			if ( t >= ts->period ) //! условие *
183 			{
184 				minTim = 0;
185 			}
186 			else
187 			{
188 				//DWORD
189 				unsigned m = ts->period - t;
190 
191 				if ( minTim > m )
192 				{
193 					minTim = m;
194 				}
195 			}
196 		}
197 
198 		return minTim;
199 	}
200 
201 
202 ////////////////////////////////////////////////// Win
203 
204 
EndModal(int id)205 	void Win::EndModal( int id )
206 	{
207 		if ( modal && !blockedBy )
208 		{
209 			( ( ModalStruct* )modal )->EndModal( id );
210 		}
211 	}
212 
213 
Paint(GC & gc,const crect & paintRect)214 	void Win::Paint( GC& gc, const crect& paintRect )
215 	{
216 		gc.SetFillColor( 0xA0A0A0 );
217 		gc.FillRect( paintRect ); //CCC
218 	}
219 
220 
IsEnabled()221 	bool Win::IsEnabled()
222 	{
223 		for ( Win* p = this; p; p = p->parent )
224 		{
225 			if ( !( p->state & S_ENABLED ) ) { return false; }
226 
227 			if ( p->IsModal() || p->Type() == Win::WT_MAIN ) { break; }
228 		}
229 
230 		return true;
231 	}
232 
IsVisible()233 	bool Win::IsVisible()
234 	{
235 		return ( state & S_VISIBLE ) != 0;
236 	}
237 
Enable(bool en)238 	void Win::Enable( bool en )
239 	{
240 		if ( en )
241 		{
242 			SetState( S_ENABLED );
243 		}
244 		else
245 		{
246 			ClearState( S_ENABLED );
247 		}
248 
249 		if ( IsVisible() )
250 		{
251 			Invalidate();
252 		}
253 	}
254 
SetTimer(int id,unsigned period)255 	void Win::SetTimer( int id, unsigned period )
256 	{
257 		TimerReset/*Append*/( GetID(), id, period );
258 	}
259 
ResetTimer(int id,unsigned period)260 	void Win::ResetTimer( int id, unsigned period )
261 	{
262 		TimerReset( GetID(), id, period );
263 	}
264 
DelTimer(int id)265 	void Win::DelTimer( int id )
266 	{
267 		TimerDel( GetID(), id );
268 	}
269 
DelAllTimers()270 	void Win::DelAllTimers()
271 	{
272 		TimerDel( GetID() );
273 	}
274 
Event(cevent * pEvent)275 	bool Win::Event( cevent* pEvent )
276 	{
277 		switch ( pEvent->Type() )
278 		{
279 			case EV_KEYUP:
280 			case EV_KEYDOWN:
281 				return EventKey( ( cevent_key* )pEvent );
282 
283 			case EV_CLOSE:
284 				return EventClose();
285 
286 			case EV_SETFOCUS:
287 			{
288 //			Invalidate();
289 				return EventFocus( true );
290 			}
291 
292 			case EV_KILLFOCUS:
293 //			Invalidate();
294 				return EventFocus( false );
295 
296 			case EV_SHOW:
297 				return EventShow( ( ( cevent_show* )pEvent )->Show() );
298 
299 			case EV_MOUSE_PRESS:
300 				if ( ( whint & WH_CLICKFOCUS ) != 0 && !InFocus() )
301 				{
302 					SetFocus();
303 				}
304 
305 			case EV_MOUSE_MOVE:
306 			case EV_MOUSE_RELEASE:
307 			case EV_MOUSE_DOUBLE:
308 				return EventMouse( ( cevent_mouse* )pEvent );
309 
310 			case EV_ENTER:
311 			case EV_LEAVE:
312 				EventEnterLeave( pEvent );
313 				return true;
314 
315 			case EV_SIZE:
316 				RecalcLayouts();
317 				EventSize( ( cevent_size* )pEvent );
318 				return true;
319 
320 			case EV_MOVE:
321 				EventMove( ( cevent_move* )pEvent );
322 				return true;
323 
324 			case EV_ACTIVATE:
325 				return EventActivate( ( ( cevent_activate* )pEvent )->Activated(), ( ( cevent_activate* )pEvent )->Who() );
326 
327 			default:
328 				return false;
329 		}
330 	}
331 
332 
333 
EventMouse(cevent_mouse * pEvent)334 	bool Win::EventMouse( cevent_mouse* pEvent ) {  return false; }
EventKey(cevent_key * pEvent)335 	bool Win::EventKey( cevent_key* pEvent ) {   return false;}
336 
EventChildKey(Win * child,cevent_key * pEvent)337 	bool Win::EventChildKey( Win* child, cevent_key* pEvent )
338 	{
339 		//dbg_printf("Win::EventChildKey key=0x%x mod=%d\n", pEvent->Key(), pEvent->Mod());
340 		// check if any child recognizes the key as its hot key
341 		if ( ( pEvent->Mod() & KM_ALT ) != 0 )
342 		{
343 			for ( int i = 0; i < childList.count(); i++ )
344 			{
345 				Win* tchild = childList[i];
346 				Win* tHotkeyWin;
347 
348 				if ( ( tHotkeyWin = tchild->IsHisHotKey( pEvent ) ) != 0 )
349 				{
350 					tHotkeyWin->SetFocus();
351 					return tHotkeyWin->EventKey( pEvent );
352 				}
353 			}
354 		}
355 
356 		return false;
357 	}
358 
EventFocus(bool recv)359 	bool Win::EventFocus( bool recv )
360 	{
361 		if ( recv && lastFocusChild )
362 		{
363 			Win* w = GetWinByID( lastFocusChild );
364 
365 //printf("SET FOCUS TO LAST FOCUS CHILD (%p)\n",w);
366 			if ( w ) { w->SetFocus(); }
367 		}
368 
369 		return false;
370 	}
371 
EventClose()372 	bool Win::EventClose() { if ( this->IsModal() ) { EndModal( CMD_CANCEL ); } return true;}
EventShow(bool)373 	bool Win::EventShow( bool ) { return true; }
EventTimer(int tid)374 	void Win::EventTimer( int tid ) {};
EventActivate(bool activated,Win * w)375 	bool Win::EventActivate( bool activated, Win* w ) { return false; }
EventEnterLeave(cevent * pEvent)376 	void Win::EventEnterLeave( cevent* pEvent ) {}
EventSize(cevent_size * pEvent)377 	void Win::EventSize( cevent_size* pEvent ) {}
EventMove(cevent_move * pEvent)378 	void Win::EventMove( cevent_move* pEvent ) {}
ThreadSignal(int id,int data)379 	void Win::ThreadSignal( int id, int data ) {}
ThreadStopped(int id,void * data)380 	void Win::ThreadStopped( int id, void* data ) {}
381 
Command(int id,int subId,Win * win,void * data)382 	bool Win::Command( int id, int subId, Win* win, void* data )
383 	{
384 		return parent ? parent->Command( id, subId,  win, data ) : false;
385 	}
386 
Broadcast(int id,int subId,Win * win,void * data)387 	bool Win::Broadcast( int id, int subId, Win* win, void* data )
388 	{
389 		return false;
390 	}
391 
392 
SendBroadcast(int id,int subId,Win * win,void * data,int level)393 	int Win::SendBroadcast( int id, int subId, Win* win, void* data, int level )
394 	{
395 		if ( level <= 0 ) { return 0; }
396 
397 		level--;
398 		ccollect<WinID> list;
399 		int n = 0;
400 		int i;
401 
402 		for ( i = 0; i < childList.count(); i++ )
403 		{
404 			list.append( childList[i]->GetID() );
405 		}
406 
407 		for ( i = 0; i < list.count(); i++ )
408 		{
409 			Win* w = GetWinByID( list[i] );
410 
411 			if ( w ) { n += w->SendBroadcast( id, subId, win, data, level ); }
412 		}
413 
414 		if ( Broadcast( id, subId, win, data ) ) { n++; }
415 
416 		return n;
417 	}
418 
SetIdealSize()419 	void Win::SetIdealSize()
420 	{
421 		if ( lSize.x.ideal > 0 && lSize.y.ideal > 0 )
422 		{
423 			crect r = ScreenRect();
424 			crect cr = ClientRect();
425 
426 			int xplus = ( r.right - r.left + 1 ) - ( cr.right - cr.left + 1 );
427 			int yplus = ( r.bottom - r.top + 1 ) - ( cr.bottom - cr.top + 1 );
428 			Move( crect( r.left, r.top, r.left + lSize.x.ideal + xplus, r.top + lSize.y.ideal + yplus ) ); //???
429 		}
430 	}
431 
432 
UnblockTree(WinID id)433 	bool Win::UnblockTree( WinID id )
434 	{
435 		if ( blockedBy != id )
436 		{
437 			return false;
438 		}
439 
440 		blockedBy = 0;
441 
442 		for ( int i = 0; i < childList.count(); i++ )
443 		{
444 			childList[i]->UnblockTree( id );
445 		}
446 
447 		return true;
448 	}
449 
450 
IsOneParentWith(WinID h)451 	bool Win::IsOneParentWith( WinID h )
452 	{
453 		Win* w = GetWinByID( h );
454 
455 		if ( !w ) { return false; }
456 
457 		while ( w->Parent() ) { w = w->Parent(); }
458 
459 		Win* t = this;
460 
461 		while ( t->Parent() ) { t = t->Parent(); }
462 
463 		return t == w;
464 	}
465 
PopupTreeList(ccollect<WinID> & list)466 	void Win::PopupTreeList( ccollect<WinID>& list )
467 	{
468 		if (type & Win::WT_POPUP)
469 			list.append(handle);
470 		for (int i = 0; i < childList.count(); i++)
471 			childList[i]->PopupTreeList(list);
472 	}
473 
FocusNPChild(bool next)474 	Win* Win::FocusNPChild( bool next )
475 	{
476 		WinID c = lastFocusChild;
477 		Win** list = childList.ptr();
478 		int count = childList.count();
479 		int i = 0;
480 		Win* p = 0;
481 
482 		for ( i = 0; i < count; i++ )
483 			if ( list[i]->handle == c )
484 			{
485 				p = list[i];
486 				break;
487 			};
488 
489 		if ( p )
490 		{
491 			int n = i;
492 
493 			if ( next )
494 			{
495 				for ( i = n + 1; i < count; i++ )
496 					if ( ( list[i]->whint &  WH_TABFOCUS ) && list[i]->IsVisible() )
497 					{
498 						list[i]->SetFocus();
499 						return list[i];
500 					}
501 
502 				for ( i = 0; i < n; i++ )
503 					if ( ( list[i]->whint &  WH_TABFOCUS ) && list[i]->IsVisible() )
504 					{
505 						list[i]->SetFocus();
506 						return list[i];
507 					}
508 			}
509 			else
510 			{
511 				for ( i = n - 1; i >= 0; i-- )
512 					if ( ( list[i]->whint &  WH_TABFOCUS ) && list[i]->IsVisible() )
513 					{
514 						list[i]->SetFocus();
515 						return list[i];
516 					}
517 
518 				for ( i = count - 1; i > n; i-- )
519 					if ( ( list[i]->whint &  WH_TABFOCUS ) && list[i]->IsVisible() )
520 					{
521 						list[i]->SetFocus();
522 						return list[i];
523 					}
524 			}
525 
526 			if ( !p->InFocus() )
527 			{
528 				p->SetFocus();
529 			}
530 
531 			return p;
532 
533 		}
534 		else
535 		{
536 			for ( i = 0; i < count; i++ )
537 				if ( ( list[i]->whint &  WH_TABFOCUS ) && list[i]->IsVisible() )
538 				{
539 					list[i]->SetFocus();
540 					return list[i];
541 				}
542 		}
543 
544 		return 0;
545 	}
546 
547 
OnChangeStyles()548 	void Win::OnChangeStyles()
549 	{
550 	}
551 
StylesChanged(Win * w)552 	void Win::StylesChanged( Win* w )
553 	{
554 		if ( !w ) { return; }
555 
556 		int count = w->ChildCount();
557 
558 		for ( int i = 0; i < count; i++ )
559 		{
560 			Win* ch = w->GetChild( i );
561 
562 			if ( !ch ) { break; }
563 
564 			StylesChanged( ch );
565 		};
566 
567 		w->OnChangeStyles();
568 
569 		w->RecalcLayouts();
570 
571 		w->Invalidate();
572 	}
573 
GetChildFont(Win * w,int fontId)574 	cfont* Win::GetChildFont( Win* w, int fontId )
575 	{
576 		return parent ? parent->GetChildFont( w, fontId ) : SysGetFont( w, fontId );
577 	}
578 
ThreadCreate(int id,void * (* f)(void *),void * d)579 	void Win::ThreadCreate( int id, void* ( *f )( void* ), void* d )
580 	{
581 		wth_CreateThread( this, id, f, d );
582 	}
583 
584 
585 
ColorTone(unsigned color,int tone)586 	unsigned ColorTone( unsigned color, int tone /*0-255*/ )
587 	{
588 		unsigned char R = ( unsigned char )( color % 0x100 ), G = ( unsigned char )( ( color / 0x100 ) % 0x100 ), B = ( unsigned char )( ( color / 0x10000 ) % 0x100 );
589 
590 		if ( tone >= 0 )
591 		{
592 			unsigned char t = tone % 0x100;
593 			R += ( ( 0x100 - R ) * t ) / 0x100;
594 			G += ( ( 0x100 - G ) * t ) / 0x100;
595 			B += ( ( 0x100 - B ) * t ) / 0x100;
596 		}
597 		else
598 		{
599 			unsigned char t = ( -tone ) % 0x100;
600 			R -= ( ( R * t ) / 0x100 );
601 			G -= ( ( G * t ) / 0x100 );
602 			B -= ( ( B * t ) / 0x100 );
603 		}
604 
605 		return ( ( B % 0x100 ) << 16 ) + ( ( G % 0x100 ) << 8 ) + ( R % 0x100 );
606 	}
607 
DrawPixelList(GC & gc,unsigned short * s,int x,int y,unsigned color)608 	void DrawPixelList( GC& gc, unsigned short* s, int x, int y, unsigned color )
609 	{
610 		int n = *( s++ );
611 
612 		for ( int i = 0; i < n; i++, s++, y++ )
613 		{
614 			unsigned c = *s;
615 
616 			for ( int j = 0; j < 16; j++, c >>= 1 )
617 				if ( c & 1 ) { gc.SetPixel( x + j, y, color ); }
618 		}
619 	}
620 
621 
622 
623 
624 /////////////////////////////////////// cevent's
625 
~cevent()626 	cevent::~cevent() {}
~cevent_show()627 	cevent_show::~cevent_show() {}
~cevent_input()628 	cevent_input::~cevent_input() {}
~cevent_key()629 	cevent_key::~cevent_key() {}
~cevent_mouse()630 	cevent_mouse::~cevent_mouse() {}
~cevent_activate()631 	cevent_activate::~cevent_activate() {}
~cevent_size()632 	cevent_size::~cevent_size() {}
~cevent_move()633 	cevent_move::~cevent_move() {}
634 
635 ///////////////////////////////////////   Win Threads
636 
637 	WthInternalEvent wthInternalEvent;
638 
639 	enum WTHCMD
640 	{
641 		WTH_CMD_NONE = 0,
642 		WTH_CMD_EVENT = 1,
643 		WTH_CMD_END = 2
644 	};
645 
646 	struct WTHNode
647 	{
648 		volatile thread_t th;
649 		volatile int id;
650 		Win* volatile w;
651 		volatile int data;
652 		void* ( *volatile func )( void* );
653 		void* volatile fData;
654 		volatile int cmd;
655 		WTHNode* volatile thNext;
656 		WTHNode* volatile winNext;
657 		WTHNode* volatile cmdNext;
WTHNodewal::WTHNode658 		WTHNode(): id( 0 ), w( 0 ), data( 0 ), func( 0 ), fData( 0 ), cmd( 0 ), thNext( 0 ), winNext( 0 ), cmdNext( 0 ) {}
659 	};
660 
th_key(thread_t th)661 	inline unsigned th_key( thread_t th )
662 	{
663 		unsigned key = 0;
664 
665 		for ( size_t i = 0; i < sizeof( th ); i++ ) { key += ( ( unsigned char* )&th )[i]; }
666 
667 		return key;
668 	}
669 
win_key(Win * w)670 	inline unsigned win_key( Win* w )
671 	{
672 		return ( unsigned )( w - ( Win* )0 );
673 	}
674 
675 	class WTHash
676 	{
677 		friend void wth_DropWindow( Win* w );
678 		friend void wth_CreateThread( Win* w, int id, void* ( *f )( void* ), void* d );
679 		friend bool WinThreadSignal( int data );
680 		friend void* _swl_win_thread_func( void* data );
681 		friend void wth_DoEvents();
682 
683 #define WTH_HASH_SIZE (331)
684 		WTHNode* volatile thList[WTH_HASH_SIZE];
685 		WTHNode* volatile winList[WTH_HASH_SIZE];
686 		WTHNode* volatile cmdFirst;
687 		WTHNode* volatile cmdLast;
688 
689 		Mutex mutex;
690 	public:
WTHash()691 		WTHash()
692 			: cmdFirst( 0 ), cmdLast( 0 )
693 		{
694 			int i;
695 
696 			for ( i = 0; i < WTH_HASH_SIZE; i++ ) { thList[i] = 0; }
697 
698 			for ( i = 0; i < WTH_HASH_SIZE; i++ ) { winList[i] = 0; }
699 		}
~WTHash()700 		~WTHash() {}
701 	} wtHash;
702 
703 
_swl_win_thread_func(void * data)704 	void* _swl_win_thread_func( void* data )
705 	{
706 		WTHNode* p = ( ( WTHNode* )data );
707 		void* ret = p->func( p->fData );
708 
709 		MutexLock lock( &wtHash.mutex );
710 		unsigned tN = th_key( p->th ) % WTH_HASH_SIZE;
711 
712 		WTHNode* volatile* t = &( wtHash.thList[tN] );
713 
714 		while ( *t )
715 		{
716 			if ( t[0] == p )
717 			{
718 				t[0] = p->thNext;
719 				p->thNext = 0;
720 				break;
721 			}
722 
723 			t = &( t[0]->thNext );
724 		}
725 
726 		if ( !p->cmd )
727 		{
728 			p->cmdNext = 0;
729 
730 			if ( wtHash.cmdLast )
731 			{
732 				wtHash.cmdLast->cmdNext = p;
733 			}
734 			else
735 			{
736 				wtHash.cmdFirst = p;
737 			}
738 
739 			wtHash.cmdLast = p;
740 		}
741 
742 		p->cmd = WTH_CMD_END;
743 
744 		try
745 		{
746 			wthInternalEvent.SetSignal();
747 		}
748 		catch ( cexception* e )   //???
749 		{
750 			e->destroy();
751 		}
752 
753 		return ret;
754 	}
755 
wth_CreateThread(Win * w,int id,void * (* f)(void *),void * d)756 	void wth_CreateThread( Win* w, int id, void* ( *f )( void* ), void* d )
757 	{
758 		WTHNode* p = new WTHNode;
759 		p->w = w;
760 		p->id = id;
761 		p->func = f;
762 		p->fData = d;
763 		MutexLock lock( &wtHash.mutex );
764 
765 		int err = thread_create( const_cast<thread_t*>( &( p->th ) ), _swl_win_thread_func, p );
766 
767 		if ( err ) { throw_syserr( err, "can`t create window thread" ); }
768 
769 		unsigned tN = th_key( p->th ) % WTH_HASH_SIZE;
770 		unsigned wN = win_key( w ) % WTH_HASH_SIZE;
771 		p->winNext = wtHash.winList[wN];
772 		wtHash.winList[wN] = p;
773 		p->thNext =  wtHash.thList[tN];
774 		wtHash.thList[tN] = p;
775 	}
776 
WinThreadSignal(int data)777 	bool WinThreadSignal( int data )
778 	{
779 		thread_t th = thread_self();
780 		unsigned n = th_key( th ) % WTH_HASH_SIZE;
781 //printf("Find tn=%i (%p)\n", n, th);
782 		MutexLock lock( &wtHash.mutex );
783 
784 		wthInternalEvent.SetSignal();
785 
786 		for ( WTHNode* p = wtHash.thList[n]; p; p = p->thNext )
787 			if ( thread_equal( th, p->th ) )
788 			{
789 				if ( !p->cmd )
790 				{
791 					p->cmdNext = 0;
792 
793 					if ( wtHash.cmdLast )
794 					{
795 						wtHash.cmdLast->cmdNext = p;
796 					}
797 					else
798 					{
799 						wtHash.cmdFirst = p;
800 					}
801 
802 					wtHash.cmdLast = p;
803 				}
804 
805 				p->cmd = WTH_CMD_EVENT;
806 				p->data = data;
807 
808 				return p->w != 0;
809 			}
810 
811 //printf("thread not found %p\n", th);
812 		return false;
813 	}
814 
wth_DropWindow(Win * w)815 	void wth_DropWindow( Win* w )
816 	{
817 		MutexLock lock( &wtHash.mutex );
818 		unsigned n = win_key( w ) % WTH_HASH_SIZE;
819 		WTHNode* volatile* p = &( wtHash.winList[n] );
820 
821 		while ( *p )
822 		{
823 			if ( p[0]->w == w )
824 			{
825 				p[0]->w = 0;
826 				WTHNode* t = p[0];
827 				p[0] = p[0]->winNext;
828 				t->winNext = 0;
829 			}
830 			else { p = &( p[0]->winNext ); }
831 		}
832 	}
833 
834 
wth_DoEvents()835 	void wth_DoEvents()
836 	{
837 		WTHNode* last = 0;
838 
839 		{
840 			MutexLock lock( &wtHash.mutex );
841 			last = wtHash.cmdLast;
842 			wthInternalEvent.ClearSignal();
843 		}
844 
845 		if ( !last ) { return; }
846 
847 		while ( true )
848 		{
849 			MutexLock lock( &wtHash.mutex );
850 			WTHNode* p = wtHash.cmdFirst;
851 
852 			if ( !p ) { break; } //botva?
853 
854 			if ( p->cmd == WTH_CMD_END )
855 			{
856 				if ( p->w )
857 				{
858 					unsigned n = win_key( p->w ) % WTH_HASH_SIZE;
859 
860 					for ( WTHNode * volatile* pp = &( wtHash.winList[n] ); pp[0]; pp = &( pp[0]->winNext ) )
861 						if ( pp[0] == p )
862 						{
863 							pp[0] = p->winNext;
864 							break;
865 						}
866 				}
867 
868 				wtHash.cmdFirst = p->cmdNext;
869 
870 				if ( !wtHash.cmdFirst ) { wtHash.cmdLast = 0; }
871 
872 				lock.Unlock();
873 
874 				try
875 				{
876 					void* ret = 0;
877 					thread_join( p->th, &ret );
878 
879 					if ( p->w != 0 )
880 					{
881 						p->w->ThreadStopped( p->id, ret );
882 					}
883 
884 //printf("End of Thread Win=%p, id=%i, ret = %p\n", p->w, p->id, ret);
885 
886 				}
887 				catch ( ... )
888 				{
889 					delete p;
890 				};
891 
892 				delete p;
893 
894 			}
895 			else   if ( p->cmd == WTH_CMD_EVENT )
896 			{
897 				Win* w = p->w;
898 				int id = p->id;
899 				int data = p->data;
900 				p->cmd = 0;
901 				wtHash.cmdFirst   = p->cmdNext;
902 
903 				if ( !wtHash.cmdFirst ) { wtHash.cmdLast = 0; }
904 
905 				lock.Unlock();
906 
907 				if ( w )
908 				{
909 					w->ThreadSignal( id, data );
910 				}
911 
912 //printf("Thread Evant Win=%p, id=%i, data=%i\n",w,id,data);
913 
914 			}
915 			else
916 			{
917 				fprintf( stderr, "bad state in void wth_DoEvents()\n" );
918 			}
919 
920 			if ( p == last ) { break; }
921 		}
922 	}
923 
924 
925 ///////////////////////////////////////////// ClipboardText
926 
CopyFrom(const ClipboardText & a)927 	void ClipboardText::CopyFrom( const ClipboardText& a )
928 	{
929 		Clear();
930 		int i;
931 
932 		for ( i = 0; i < a.list.count(); i++ )
933 		{
934 			std::vector<unicode_t> p( BUF_SIZE );
935 			memcpy( p.data(), a.list.const_item( i ).data(), BUF_SIZE * sizeof( unicode_t ) );
936 			list.append( p );
937 		}
938 
939 		count = a.count;
940 	}
941 
ClipboardText()942 	ClipboardText::ClipboardText()
943 		:  count( 0 )
944 	{
945 	}
946 
ClipboardText(const ClipboardText & a)947 	ClipboardText::ClipboardText( const ClipboardText& a )
948 		:  count( 0 )
949 	{
950 		CopyFrom( a );
951 	}
952 
operator =(const ClipboardText & a)953 	ClipboardText& ClipboardText::operator = ( const ClipboardText& a )
954 	{
955 		CopyFrom( a );
956 		return *this;
957 	}
958 
959 
Clear()960 	void ClipboardText::Clear()
961 	{
962 		list.clear();
963 		count = 0;
964 	}
965 
Append(unicode_t c)966 	void ClipboardText::Append( unicode_t c )
967 	{
968 		int n = count + 1;
969 		int bc = ( n + BUF_SIZE - 1 ) / BUF_SIZE;
970 
971 		while ( list.count() < bc )
972 		{
973 			std::vector<unicode_t> p( BUF_SIZE );
974 			list.append( p );
975 		}
976 
977 		count++;
978 		n = count - 1;
979 
980 		list[n / BUF_SIZE][n % BUF_SIZE] = c;
981 	}
982 
AppendUnicodeStr(const unicode_t * c)983 	void ClipboardText::AppendUnicodeStr( const unicode_t* c )
984 	{
985 		while ( c && *c )
986 		{
987 			this->Append( *c );
988 			c++;
989 		}
990 	}
991 
992 /////////////////////////////////    Image32 /////////////////////////////
993 
alloc(int w,int h)994 	void Image32::alloc( int w, int h )
995 	{
996 		std::vector<uint32_t> d( w * h );
997 
998 		_data = d;
999 		_width = w;
1000 		_height = h;
1001 	}
1002 
clear()1003 	void Image32::clear() { _width = _height = 0; _data.clear(); }
1004 
fill(int left,int top,int right,int bottom,unsigned c)1005 	void Image32::fill( int left, int top, int right, int bottom,  unsigned c )
1006 	{
1007 		if ( left < 0 ) { left = 0; }
1008 
1009 		if ( right > _width ) { right = _width; }
1010 
1011 		if ( top < 0 ) { top = 0; }
1012 
1013 		if ( bottom > _height ) { bottom = _height; }
1014 
1015 		if ( left >= right || top >= bottom ) { return; }
1016 
1017 		for ( int i = top; i < bottom; i++ )
1018 		{
1019 			uint32_t* p = &_data[i * _width];
1020 			uint32_t* end = p + right;
1021 			p += left;
1022 
1023 			for ( ; p < end; p++ ) { *p = c; }
1024 		}
1025 	}
1026 
copy(const Image32 & a)1027 	void Image32::copy( const Image32& a )
1028 	{
1029 		if ( a._width != _width || a._height != _height ) { alloc( a._width, a._height ); }
1030 
1031 		if ( _width > 0 && _height > 0 )
1032 		{
1033 			memcpy( _data.data(), a._data.data(), _width * _height * sizeof( uint32_t ) );
1034 		}
1035 	}
1036 
1037 
1038 #define FROM_RGBA32(src,R,G,B,A) { A = ((src)>>24)&0xFF; B=((src)>>16)&0xFF; G=((src)>>8)&0xFF; R=(src)&0xFF; }
1039 #define FROM_RGBA32_PLUS(src,R,G,B,A) { A += ((src)>>24)&0xFF; B+=((src)>>16)&0xFF; G+=((src)>>8)&0xFF; R+=(src)&0xFF; }
1040 #define TO_RGBA32(R,G,B,A,n) ((((A)/(n))&0xFF)<<24) | ((((B)/(n))&0xFF)<<16) | ((((G)/(n))&0xFF)<<8) | ((((R)/(n))&0xFF))
1041 
copy(const Image32 & a,int w,int h)1042 	void Image32::copy( const Image32& a, int w, int h )
1043 	{
1044 		if ( a._width == w && a._height == h )
1045 		{
1046 			copy( a );
1047 			return;
1048 		}
1049 
1050 		if ( !w || !h )
1051 		{
1052 			clear();
1053 			return;
1054 		}
1055 
1056 		alloc( w, h );
1057 
1058 		if ( a._width <= 0 || a._height <= 0 ) { return; }
1059 
1060 		//упрощенная схема, не очень качественно, зато быстро
1061 
1062 		std::vector< std::pair<int, int> > p( w );
1063 
1064 		int x;
1065 		int wN = a._width / w;
1066 		int wN2 = wN / 2;
1067 
1068 		for ( x = 0; x < w; x++ )
1069 		{
1070 			int t = ( x * a._width ) / w;
1071 			int t1 = t - wN2;
1072 			int t2 = t1 + wN;
1073 
1074 			if ( t2 == t1 ) { t2 = t1 + 1; }
1075 
1076 			p[x].first  = ( t1 >= 0 ) ? t1 : 0;
1077 			p[x].second = ( t2 <= a._width ) ? t2 : a._width;
1078 		}
1079 
1080 		int hN = a._height / h;
1081 
1082 		int y;
1083 
1084 		for ( y = 0; y < h; y++ )
1085 		{
1086 
1087 			uint32_t* line =  &_data[y * _width];
1088 			int hN2 = hN / 2;
1089 
1090 			if ( !hN )
1091 			{
1092 				const uint32_t* a_line =  &a._data[ ( ( y * a._height ) / h ) * a._width ];
1093 
1094 				if ( !wN )
1095 					for ( x = 0; x < w; x++ ) { line[x] = a_line[p[x].first]; }
1096 				else
1097 				{
1098 					for ( x = 0; x < w; x++ )
1099 					{
1100 						unsigned R, G, B, A;
1101 						int n1 = p[x].first;
1102 						int n2 = p[x].second;
1103 						FROM_RGBA32( a_line[n1], R, G, B, A );
1104 
1105 						for ( int i = n1 + 1; i < n2; i++ ) { FROM_RGBA32_PLUS( a_line[i], R, G, B, A ); }
1106 
1107 						n2 -= n1;
1108 						line[x] = n2 ? TO_RGBA32( R, G, B, A, n2 ) : 0xFF;
1109 					}
1110 				}
1111 			}
1112 			else
1113 			{
1114 
1115 				int t = ( y * a._height ) / h;
1116 				int t1 = t - hN2;
1117 				int t2 = t1 + hN;
1118 
1119 				if ( t1 < 0 ) { t1 = 0; }
1120 
1121 				if ( t2 > a._height ) { t2 = a._height; }
1122 
1123 				for ( x = 0; x < w; x++ )
1124 				{
1125 
1126 					unsigned R = 0, G = 0, B = 0, A = 0;
1127 					int n1 = p[x].first;
1128 					int n2 = p[x].second;
1129 
1130 					for ( int iy = t1; iy < t2; iy++ )
1131 					{
1132 						const uint32_t* a_line =  &a._data[ iy * a._width ];
1133 
1134 						for ( int i = n1; i < n2; i++ ) { FROM_RGBA32_PLUS( a_line[i], R, G, B, A ); }
1135 					}
1136 
1137 					n2 -= n1;
1138 					n2 *= ( t2 - t1 );
1139 					line[x] = n2 ? TO_RGBA32( R, G, B, A, n2 ) : 0xFF00;
1140 				}
1141 			}
1142 		}
1143 	}
1144 
copy(const XPMImage & xpm)1145 	void Image32::copy( const XPMImage& xpm )
1146 	{
1147 		const unsigned* colors = xpm.Colors();
1148 		const int* data = xpm.Data();
1149 		clear();
1150 		int w = xpm.Width();
1151 		int h = xpm.Height();
1152 
1153 		if ( w <= 0 || h <= 0 ) { return; }
1154 
1155 		alloc( w, h );
1156 
1157 		for ( size_t i = 0; i != _data.size( ); i++, data++ )
1158 		{
1159 			_data[i] = ( *data >= 0 ) ? colors[*data] : 0xFF000000;
1160 		}
1161 	}
1162 
1163 
1164 //////////// XPMImage
1165 
Clear()1166 	void XPMImage::Clear()
1167 	{
1168 		colors.clear();
1169 		colorCount = 0;
1170 		none = -1;
1171 		width = height = 0;
1172 		data.clear();
1173 	}
1174 
1175 	struct color_tree_node
1176 	{
1177 		bool isColor;
1178 		union
1179 		{
1180 			color_tree_node* p[0x100];
1181 			int c[0x100];
1182 		};
color_tree_nodewal::color_tree_node1183 		color_tree_node( bool _is_color )
1184 			:  isColor( _is_color )
1185 		{
1186 			int i;
1187 
1188 			if ( _is_color ) { for ( i = 0; i < 0x100; i++ ) { c[i] = -1; } }
1189 			else { for ( i = 0; i < 0x100; i++ ) { p[i] = 0; } }
1190 		}
1191 		~color_tree_node();
1192 	};
1193 
~color_tree_node()1194 	color_tree_node::~color_tree_node()
1195 	{
1196 		if ( !isColor )
1197 		{
1198 			for ( int i = 0; i < 0x100; i++ ) if ( p[i] ) { delete p[i]; }
1199 		}
1200 	}
1201 
InsertColorId(color_tree_node * p,const char * s,int count,int colorId)1202 	static void InsertColorId( color_tree_node* p, const char* s, int count, int colorId )
1203 	{
1204 		if ( !p ) { return; }
1205 
1206 		while ( count > 0 )
1207 		{
1208 			unsigned char n = *( s++ );
1209 			count--;
1210 
1211 
1212 			if ( p->isColor )
1213 			{
1214 
1215 				if ( !count )
1216 				{
1217 					p->c[n] = colorId;
1218 				}
1219 
1220 				//else ignore
1221 
1222 				return;
1223 			}
1224 			else
1225 			{
1226 				if ( count )
1227 				{
1228 					if ( !p->p[n] )
1229 					{
1230 						p->p[n] = new color_tree_node( count == 1 );
1231 					}
1232 
1233 					p = p->p[n];
1234 				}
1235 				else { return; }   //else ignore
1236 			}
1237 		}
1238 	}
1239 
GetColorId(color_tree_node * p,const char * s,int count)1240 	static int GetColorId( color_tree_node* p, const char* s, int count )
1241 	{
1242 		while ( p && count > 0 )
1243 		{
1244 			unsigned char n = *( s++ );
1245 			count--;
1246 
1247 			if ( p->isColor )
1248 			{
1249 				return p->c[n];
1250 			}
1251 			else
1252 			{
1253 				p = p->p[n];
1254 			}
1255 		}
1256 
1257 		return -1;
1258 	}
1259 
SS(const char * & s)1260 	inline void SS( const char*& s ) { while ( *s > 0 && *s <= 32 ) { s++; } }
1261 
SInt(const char * & s)1262 	static int SInt( const char*& s )
1263 	{
1264 		int n = 0;
1265 
1266 		for ( ; *s && *s >= '0' && *s <= '9'; s++ )
1267 		{
1268 			n = n * 10 + ( *s - '0' );
1269 		}
1270 
1271 		return n;
1272 	}
1273 
SHex(const char * & s)1274 	static unsigned SHex( const char*& s )
1275 	{
1276 		unsigned n = 0;
1277 
1278 		for ( ; *s; s++ )
1279 		{
1280 			if ( *s >= '0' && *s <= '9' )
1281 			{
1282 				n = ( n << 4 ) + ( *s - '0' );
1283 			}
1284 			else if ( *s >= 'a' && *s <= 'f' )
1285 			{
1286 				n = ( n << 4 ) + ( *s - 'a' ) + 10;
1287 			}
1288 			else if ( *s >= 'A' && *s <= 'F' )
1289 			{
1290 				n = ( n << 4 ) + ( *s - 'A' ) + 10;
1291 			}
1292 			else { return n; }
1293 		}
1294 
1295 		return n;
1296 	}
1297 
HSVtoRGB(unsigned hsv)1298 	static unsigned HSVtoRGB( unsigned hsv )
1299 	{
1300 		return hsv; //!!!
1301 	}
1302 
1303 
Load(const char ** list,int count)1304 	bool XPMImage::Load( const char** list, int count )
1305 	{
1306 		Clear();
1307 
1308 		if ( count < 1 )
1309 		{
1310 //printf("err 0\n");
1311 			return false;
1312 		}
1313 
1314 		const char* s = list[0];
1315 
1316 		list++;
1317 		count--;
1318 
1319 		SS( s );
1320 		int w = SInt( s );
1321 		SS( s );
1322 		int h = SInt( s );
1323 		SS( s );
1324 		int nc = SInt( s );
1325 		SS( s );
1326 		int cpp = SInt( s );
1327 
1328 		if ( !( w > 0 && h > 0 && nc > 0 && cpp > 0 ) || w > 16000 || h > 16000 || nc > 0xFFFFF || cpp > 4 )
1329 		{
1330 //printf("err 1\n");
1331 			return false;
1332 		}
1333 
1334 		colors.resize( nc );
1335 		{
1336 			for ( int i = 0; i < nc; i++ ) { colors[i] = 0; }
1337 		}
1338 		data.resize( w * h );
1339 		{
1340 			int* p = data.data();
1341 
1342 			for ( int n = w * h; n > 0; n--, p++ ) { *p = -1; }
1343 		}
1344 
1345 		colorCount = nc;
1346 		width = w;
1347 		height = h;
1348 
1349 
1350 		int i;
1351 
1352 		color_tree_node tree( cpp == 1 );
1353 
1354 		for ( i = 0; i < nc && count > 0; i++, count--, list++ )
1355 		{
1356 			const char* s = list[0];
1357 			int l = strlen( s );
1358 
1359 			if ( l <= cpp )
1360 			{
1361 				return false;
1362 //printf("err 2\n");
1363 			}
1364 
1365 			s += cpp;
1366 
1367 
1368 			while ( *s )
1369 			{
1370 				SS( s );
1371 
1372 				if ( *s == 'c' )
1373 				{
1374 					s++;
1375 					SS( s );
1376 
1377 					if ( *s == '#' )
1378 					{
1379 						s++;
1380 						unsigned c = SHex( s );
1381 						colors[i] = ( c & 0xFF00FF00 ) | ( ( c >> 16 ) & 0xFF ) | ( ( c << 16 ) & 0xFF0000 );
1382 
1383 					}
1384 					else if ( *s == '%' )
1385 					{
1386 						s++;
1387 						colors[i] = HSVtoRGB( SHex( s ) );
1388 					}
1389 					else if (    ( s[0] == 'N' || s[0] == 'n' ) &&
1390 					             ( s[1] == 'O' || s[1] == 'o' ) &&
1391 					             ( s[2] == 'N' || s[2] == 'n' ) &&
1392 					             ( s[3] == 'E' || s[3] == 'e' )
1393 					        )
1394 					{
1395 						s += 4;
1396 						break;
1397 					}
1398 					else
1399 					{
1400 						//ignore
1401 						while ( *s < 0 || *s > 32 ) { s++; }
1402 					}
1403 
1404 					InsertColorId( &tree, list[0], cpp, i );
1405 
1406 					break;
1407 				}
1408 				else
1409 				{
1410 					break;   //ignore
1411 				}
1412 			};
1413 
1414 
1415 		}
1416 
1417 		int* dataPtr = data.data();
1418 
1419 		for ( i = 0; i < h && count > 0; i++, count--, list++, dataPtr += width )
1420 		{
1421 			const char* s = list[0];
1422 			int l = strlen( s );
1423 			int n = l / cpp;
1424 
1425 			for ( int j = 0; j < n; j++, s += cpp )
1426 			{
1427 				int id = GetColorId( &tree, s, cpp );
1428 //printf("%i,", id);
1429 				dataPtr[j] = id;
1430 			}
1431 		}
1432 
1433 		return true;
1434 	}
1435 
1436 /////////////////////////// cicon //////////////////////////////////
1437 
1438 	static std::unordered_map< int, ccollect< clPtr< cicon > > > cmdIconList;
1439 
SetCmdIcon(int cmd,const Image32 & image,int w,int h)1440 	void cicon::SetCmdIcon( int cmd, const Image32& image, int w, int h )
1441 	{
1442 		MutexLock lock( &iconListMutex );
1443 		ccollect< clPtr<cicon> >& p = cmdIconList[cmd];
1444 		int n = p.count();
1445 
1446 		clPtr<cicon> pIcon = new cicon();
1447 		pIcon->Load( image, w, h );
1448 
1449 		for ( int i = 0; i < n; i++ )
1450 		{
1451 			if ( p[i]->Width() == w && p[i]->Height() == h )
1452 			{
1453 				p[i] = pIcon;
1454 				return;
1455 			}
1456 		}
1457 
1458 		cmdIconList[cmd].append( pIcon );
1459 	}
1460 
SetCmdIcon(int cmd,const char ** s,int w,int h)1461 	void cicon::SetCmdIcon( int cmd, const char** s, int w, int h )
1462 	{
1463 		XPMImage im;
1464 		im.Load( s, 200000 );
1465 		Image32 image32;
1466 		image32.copy( im );
1467 		cicon::SetCmdIcon( cmd, image32, w, h );
1468 	}
1469 
1470 
ClearCmdIcons(int cmd)1471 	void cicon::ClearCmdIcons( int cmd )
1472 	{
1473 		MutexLock lock( &iconListMutex );
1474 		cmdIconList.erase( cmd );
1475 	}
1476 
IconDist(const cicon & a,int w,int h)1477 	inline int IconDist( const cicon& a, int w, int h )
1478 	{
1479 		w -= a.Width();
1480 
1481 		if ( w < 0 ) { w = -w; }
1482 
1483 		h -= a.Height();
1484 
1485 		if ( h < 0 ) { h = -h; }
1486 
1487 		return w + h;
1488 	}
1489 
1490 
Load(int cmd,int w,int h)1491 	void cicon::Load( int cmd, int w, int h )
1492 	{
1493 		if ( data ) { Clear(); }
1494 
1495 		MutexLock lock( &iconListMutex );
1496 
1497 		auto i = cmdIconList.find( cmd );
1498 
1499 		if ( i == cmdIconList.end() ) { return; }
1500 
1501 		ccollect< clPtr<cicon> >* p = &( i->second );
1502 
1503 		if ( !p ) { return; }
1504 
1505 		int n = p->count();
1506 
1507 		if ( n <= 0 ) { return; }
1508 
1509 		clPtr<cicon>* t = p->ptr();
1510 
1511 		cicon* minIcon = t[0].ptr();
1512 		t++;
1513 
1514 		int minDist = IconDist( *minIcon, w, h );
1515 
1516 
1517 		if ( !minDist )
1518 		{
1519 			Copy( *minIcon );
1520 			return;
1521 		}
1522 
1523 		for ( int i = 1; i < n; i++, t++ )
1524 		{
1525 			int dist = IconDist( t[i].ptr()[0], w, h );
1526 
1527 			if ( !dist )
1528 			{
1529 				Copy( t[i].ptr()[0] );
1530 				return;
1531 			}
1532 
1533 			if ( minDist > dist )
1534 			{
1535 				minIcon = t[i].ptr();
1536 				minDist = dist;
1537 			}
1538 		}
1539 
1540 		Load( minIcon->data->image, w, h );
1541 	}
1542 
Load(const char ** list,int w,int h)1543 	void cicon::Load( const char** list, int w, int h )
1544 	{
1545 		XPMImage xpm;
1546 		xpm.Load( list, 100000 );
1547 		Image32 im;
1548 		im.copy( xpm );
1549 		Load( im, w, h );
1550 	}
1551 
Copy(const cicon & a)1552 	void cicon::Copy( const cicon& a )
1553 	{
1554 		if ( data ) { Clear(); }
1555 
1556 		if ( !a.data ) { return; }
1557 
1558 		MutexLock lock( &iconCopyMutex );
1559 		data = a.data;
1560 		data->counter++;
1561 	}
1562 
Load(const Image32 & image,int w,int h)1563 	void cicon::Load( const Image32& image, int w, int h )
1564 	{
1565 		if ( data ) { Clear(); }
1566 
1567 		IconData* p = new IconData;
1568 
1569 		try
1570 		{
1571 			p->counter = 1;
1572 //printf("load cicon %i, %i\n", w, h);
1573 			p->image.copy( image, w, h );
1574 		}
1575 		catch ( ... )
1576 		{
1577 			delete p;
1578 			throw;
1579 		}
1580 
1581 		data = p;
1582 	}
1583 
1584 
Clear()1585 	void cicon::Clear()
1586 	{
1587 		if ( data )
1588 		{
1589 			MutexLock lock( &iconCopyMutex );
1590 			data->counter--;
1591 
1592 			if ( data->counter <= 0 ) { delete data; }
1593 
1594 			data = 0;
1595 		}
1596 	}
1597 
1598 
Dis(unsigned a)1599 	inline unsigned Dis( unsigned a )
1600 	{
1601 		unsigned b = ( ( a >> 16 ) & 0xFF );
1602 		unsigned g = ( ( a >> 8 ) & 0xFF );
1603 		unsigned r = ( ( a ) & 0xFF );
1604 		unsigned m =  ( r + g + b ) / 2 + 30;
1605 
1606 		if ( m > 230 ) { m = 230; }
1607 
1608 		return ( a & 0xFF000000 ) + ( m << 16 ) + ( m << 8 ) + m;
1609 	}
1610 
MakeDisabledImage32(Image32 * dest,const Image32 & src)1611 	void MakeDisabledImage32( Image32* dest, const Image32& src )
1612 	{
1613 		if ( !dest ) { return; }
1614 
1615 		dest->copy( src );
1616 		int n = dest->height() * dest->width();
1617 
1618 		if ( n <= 0 ) { return; }
1619 
1620 		uint32_t* p = dest->line( 0 );
1621 
1622 		for ( ; n > 0; n--, p++ )
1623 		{
1624 			*p = Dis( *p );
1625 		}
1626 	}
1627 }; //namespace wal
1628 
1629 namespace wal
1630 {
1631 
1632 ////////////////////////////  Ui
1633 
UiCache()1634 	UiCache::UiCache(): updated( false ) {}
~UiCache()1635 	UiCache::~UiCache() {}
1636 
GetUiID(const char * name)1637 	int GetUiID( const char* name )
1638 	{
1639 		static std::unordered_map< std::string, int> hash;
1640 		static int id = 0;
1641 
1642 		auto i = hash.find( name );
1643 
1644 		if ( i != hash.end() ) { return i->second; }
1645 
1646 		id++;
1647 		hash[name] = id;
1648 
1649 //printf("%3i '%s'\n", id, name);
1650 
1651 		return id;
1652 	}
1653 
1654 	class UiStream
1655 	{
1656 	public:
UiStream()1657 		UiStream() {}
1658 		enum { EOFCHAR = EOF };
1659 		virtual const char* Name();
1660 		virtual int Next();
1661 		virtual ~UiStream();
1662 	};
1663 
1664 	class UiStreamFile: public UiStream
1665 	{
1666 		std::vector<sys_char_t> _name;
1667 		std::string _name_utf8;
1668 		BFile _f;
1669 	public:
UiStreamFile(const sys_char_t * s)1670 		UiStreamFile( const sys_char_t* s ): _name( new_sys_str( s ) ), _name_utf8( sys_to_utf8( s ) ) { _f.Open( s ); }
1671 		virtual const char* Name();
1672 		virtual int Next();
1673 		virtual ~UiStreamFile();
1674 	};
1675 
1676 	class UiStreamMem: public UiStream
1677 	{
1678 		const char* s;
1679 	public:
UiStreamMem(const char * data)1680 		UiStreamMem( const char* data ): s( data ) {}
1681 		virtual int Next();
1682 		virtual ~UiStreamMem();
1683 	};
1684 
1685 
Name()1686 	const char* UiStream::Name() { return ""; }
Next()1687 	int UiStream::Next() { return EOFCHAR; }
~UiStream()1688 	UiStream::~UiStream() {}
1689 
Name()1690 	const char* UiStreamFile::Name() { return _name_utf8.data(); }
Next()1691 	int UiStreamFile::Next() { return _f.GetC(); }
~UiStreamFile()1692 	UiStreamFile::~UiStreamFile() {}
1693 
Next()1694 	int UiStreamMem::Next() { return *s ? *( s++ ) : EOF; }
~UiStreamMem()1695 	UiStreamMem::~UiStreamMem() {}
1696 
1697 
1698 	struct UiParzerBuf
1699 	{
1700 		std::vector<char> data;
1701 		int size;
1702 		int pos;
UiParzerBufwal::UiParzerBuf1703 		UiParzerBuf(): size( 0 ), pos( 0 ) {}
Clearwal::UiParzerBuf1704 		void Clear() { pos = 0; }
1705 		void Append( int c );
1706 	};
1707 
Append(int c)1708 	void UiParzerBuf::Append( int c )
1709 	{
1710 		if ( pos >= size )
1711 		{
1712 			int n = ( pos + 1 ) * 2;
1713 			std::vector<char> p( n );
1714 
1715 			if ( pos > 0 )
1716 			{
1717 				memcpy( p.data(), data.data(), pos );
1718 			}
1719 
1720 			data = p;
1721 			size = n;
1722 		}
1723 
1724 		data[pos++] = c;
1725 	}
1726 
1727 
1728 ///////////////////////////////// UiParzer
1729 	class UiParzer
1730 	{
1731 		UiStream* _stream;
1732 		int _cc;
1733 		int _tok;
1734 		int NextChar();
SS()1735 		void SS() { while ( _cc >= 0 && _cc <= ' ' ) { NextChar(); } }
1736 		UiParzerBuf _buf;
1737 		int64_t _i;
1738 		int _cline;
1739 	public:
1740 		enum TOKENS
1741 		{
1742 			TOK_ID = -500,   // /[_\a][_\a\d]+/
1743 			TOK_STR, // "...\?..."
1744 			TOK_INT,
1745 			TOK_EOF
1746 		};
1747 
1748 		void Syntax( const char* s = "" );
1749 		UiParzer( UiStream* stream );
Tok() const1750 		int Tok() const { return _tok; }
Int() const1751 		int64_t Int() const { return _i; }
Str()1752 		const char* Str() { return _buf.data.data(); }
1753 		int Next();
1754 		void Skip( int tok );
1755 		void SkipAll( int tok, int mincount = 1 );
1756 		~UiParzer();
1757 	};
1758 
Skip(int t)1759 	void UiParzer::Skip( int t )
1760 	{
1761 		if ( _tok != t )
1762 		{
1763 			Syntax( GetFormattedString( "symbol not found '%c'", t ).c_str() );
1764 		}
1765 
1766 		Next();
1767 	}
1768 
SkipAll(int t,int mincount)1769 	void UiParzer::SkipAll( int t, int mincount )
1770 	{
1771 		int n;
1772 
1773 		for ( n = 0; _tok == t; n++ ) { Next(); }
1774 
1775 		if ( n < mincount )
1776 		{
1777 			Syntax( GetFormattedString( "symbol not found '%c'", t ).c_str() );
1778 		}
1779 	}
1780 
Syntax(const char * s)1781 	void UiParzer::Syntax( const char* s )
1782 	{
1783 		throw_msg( "Syntax error in '%s' line %i: %s", _stream->Name(), _cline + 1, s );
1784 	}
1785 
UiParzer(UiStream * stream)1786 	UiParzer::UiParzer( UiStream* stream )
1787 		:  _stream( stream ),
1788 		   _cc( 0 ),
1789 		   _tok( 0 ),
1790 		   _cline( 0 )
1791 	{
1792 		NextChar();
1793 		Next();
1794 	}
1795 
~UiParzer()1796 	UiParzer::~UiParzer() {}
1797 
NextChar()1798 	int UiParzer::NextChar()
1799 	{
1800 		if ( _cc == UiStream::EOFCHAR )
1801 		{
1802 			return _cc;
1803 		}
1804 
1805 		_cc = _stream->Next();
1806 
1807 		if ( _cc == '\n' ) { _cline++; }
1808 
1809 		return _cc;
1810 	}
1811 
1812 
1813 
Next()1814 	int UiParzer::Next()
1815 	{
1816 begin:
1817 		SS();
1818 
1819 		if ( _cc == '_' || IsAlpha( _cc ) )
1820 		{
1821 			_buf.Clear();
1822 			_buf.Append( _cc );
1823 			NextChar();
1824 
1825 			while ( IsAlpha( _cc ) || IsDigit( _cc ) || _cc == '_' || _cc == '-' )
1826 			{
1827 				_buf.Append( _cc );
1828 				NextChar();
1829 			};
1830 
1831 			_buf.Append( 0 );
1832 
1833 			_tok = TOK_ID;
1834 
1835 			return _tok;
1836 		}
1837 
1838 		if ( _cc == '/' )
1839 		{
1840 			NextChar();
1841 
1842 			if ( _cc != '/' )
1843 			{
1844 				_tok = '/';
1845 				return _tok;
1846 			}
1847 
1848 			//comment
1849 			while ( _cc != UiStream::EOFCHAR && _cc != '\n' )
1850 			{
1851 				NextChar();
1852 			}
1853 
1854 			goto begin;
1855 		}
1856 
1857 		if ( _cc == '"' || _cc == '\'' )
1858 		{
1859 			_buf.Clear();
1860 			int bc = _cc;
1861 			NextChar();
1862 
1863 			while ( _cc != bc )
1864 			{
1865 				if ( _cc == UiStream::EOFCHAR )
1866 				{
1867 					Syntax( "invalid string constant" );
1868 				}
1869 
1870 				if ( _cc == '\\' )
1871 				{
1872 					NextChar();
1873 
1874 					if ( _cc == UiStream::EOFCHAR )
1875 					{
1876 						Syntax( "invalid string constant" );
1877 					}
1878 				}
1879 
1880 				_buf.Append( _cc );
1881 				NextChar();
1882 			}
1883 
1884 			_buf.Append( 0 );
1885 			_tok = TOK_STR;
1886 			NextChar();
1887 			return _tok;
1888 		}
1889 
1890 		if ( IsDigit( _cc ) )
1891 		{
1892 			int64_t n = 0;
1893 
1894 			if ( _cc == '0' )
1895 			{
1896 				NextChar();
1897 
1898 
1899 				if ( _cc == 'x' || _cc == 'X' )
1900 				{
1901 					NextChar();
1902 
1903 					while ( true )
1904 					{
1905 						if ( IsDigit( _cc ) )
1906 						{
1907 							n = n * 16 + ( _cc - '0' );
1908 						}
1909 						else if ( _cc >= 'a' && _cc <= 'f' )
1910 						{
1911 							n = n * 16 + ( _cc - 'a' ) + 10;
1912 						}
1913 						else if ( _cc >= 'A' && _cc <= 'F' )
1914 						{
1915 							n = n * 16 + ( _cc - 'A' ) + 10;
1916 						}
1917 						else
1918 						{
1919 							break;
1920 						}
1921 
1922 						NextChar();
1923 					};
1924 
1925 					_i = n;
1926 
1927 					_tok = TOK_INT;
1928 
1929 					return _tok;
1930 				}
1931 			}
1932 
1933 			for ( ; IsDigit( _cc ); NextChar() )
1934 			{
1935 				n = n * 10 + ( _cc - '0' );
1936 			}
1937 
1938 			_i = n;
1939 			_tok = TOK_INT;
1940 			return _tok;
1941 		};
1942 
1943 		if ( _cc == UiStream::EOFCHAR )
1944 		{
1945 			_tok = TOK_EOF;
1946 			return _tok;
1947 		}
1948 
1949 		_tok = _cc;
1950 		NextChar();
1951 		return _tok;
1952 	}
1953 
Int()1954 	int64_t UiValueNode::Int()
1955 	{
1956 		if ( !( flags & INT ) )
1957 		{
1958 			if ( !( flags & STR ) ) { return 0; }
1959 
1960 			int64_t n = 0;
1961 			const char* t = s.data();
1962 
1963 			if ( !t || !*t ) { return 0; }
1964 
1965 //			bool minus = false;
1966 
1967 			if ( *t == '-' )
1968 			{
1969 //				minus = true;
1970 				t++;
1971 			}
1972 
1973 			if ( *t == '+' ) { t++; }
1974 
1975 			for ( ; *t >= '0' && *t <= '9'; t++ ) { n = n * 10 + ( *t - '0' ); }
1976 
1977 			i = n;
1978 			flags |= INT;
1979 		}
1980 
1981 		return i;
1982 	}
1983 
Str()1984 	const char* UiValueNode::Str()
1985 	{
1986 		if ( !( flags & STR ) )
1987 		{
1988 			if ( !( flags & INT ) ) { return ""; }
1989 
1990 			s = ToString(i);
1991 			flags |= STR;
1992 		}
1993 
1994 		return s.data();
1995 	}
1996 
1997 	class UiValue;
1998 
1999 	struct UiSelector: public iIntrusiveCounter
2000 	{
2001 		struct Node
2002 		{
2003 			int idC;
2004 			int idN;
2005 			bool oneStep;
Nodewal::UiSelector::Node2006 			Node(): idC( 0 ), idN( 0 ), oneStep( true ) {}
Nodewal::UiSelector::Node2007 			Node( int c, int n, bool o ): idC( c ), idN( n ), oneStep( o ) {}
2008 		};
2009 
2010 		struct CondNode
2011 		{
2012 			bool no;
2013 			int id;
CondNodewal::UiSelector::CondNode2014 			CondNode(): no( false ), id( 0 ) {}
CondNodewal::UiSelector::CondNode2015 			CondNode( bool n, int i ): no( n ), id( i ) {}
2016 		};
2017 
2018 		struct ValueNode
2019 		{
2020 			int id;
2021 			UiValue* data;
ValueNodewal::UiSelector::ValueNode2022 			ValueNode(): id( 0 ), data( 0 ) {}
ValueNodewal::UiSelector::ValueNode2023 			ValueNode( int i, UiValue* v ): id( i ), data( v ) {}
2024 		};
2025 
2026 		ccollect<Node> stack;
2027 		int item;
2028 		ccollect<CondNode> cond;
2029 		ccollect<ValueNode> val;
2030 
2031 		enum {CMPN = 4};
2032 		unsigned char cmpLev[CMPN];
2033 
Cmpwal::UiSelector2034 		int Cmp( const clPtr<UiSelector>& a )
2035 		{
2036 			int i = 0;
2037 
2038 			while ( i < CMPN && cmpLev[i] == a->cmpLev[i] ) { i++; }
2039 
2040 			return ( i >= CMPN ) ? 0 : cmpLev[i] < a->cmpLev[i] ? -1 : 1;
2041 		};
2042 
AppendObjwal::UiSelector2043 		void AppendObj( int c, int n, bool o )
2044 		{
2045 			stack.append( Node( c, n, o ) );
2046 
2047 			if ( n ) { cmpLev[0]++; }
2048 
2049 			if ( c ) { cmpLev[1]++; }
2050 		}
2051 
AppendCondwal::UiSelector2052 		void AppendCond( bool no, int id ) { cond.append( CondNode( no, id ) ); cmpLev[3]++; }
AppendValwal::UiSelector2053 		void AppendVal( int id, UiValue* v ) { val.append( ValueNode( id, v ) ); }
SetItemwal::UiSelector2054 		void SetItem( int id ) {item = id; cmpLev[2] = id ? 1 : 0; }
2055 
2056 
UiSelectorwal::UiSelector2057 		UiSelector(): item( 0 )   { for ( int i = 0; i < CMPN; i++ ) { cmpLev[i] = 0; } }
2058 
2059 		void Parze( UiParzer& parzer );
2060 	};
2061 
Parze(UiParzer & parzer)2062 	void UiSelector::Parze( UiParzer& parzer )
2063 	{
2064 		while ( true )
2065 		{
2066 			bool oneStep = false;
2067 
2068 			if ( parzer.Tok() == '>' )
2069 			{
2070 				oneStep = true;
2071 				parzer.Next();
2072 			}
2073 
2074 			if ( parzer.Tok() == '*' )
2075 			{
2076 				parzer.Next();
2077 				AppendObj( 0, 0, oneStep );
2078 				continue;
2079 			}
2080 
2081 			int classId = 0;
2082 
2083 			if ( parzer.Tok() == UiParzer::TOK_ID )
2084 			{
2085 				classId = GetUiID( parzer.Str() );
2086 				parzer.Next();
2087 
2088 			}
2089 
2090 			int nameId = 0;
2091 
2092 			if ( parzer.Tok() == '#' )
2093 			{
2094 				parzer.Next();
2095 
2096 				if ( parzer.Tok() != UiParzer::TOK_ID )
2097 				{
2098 					parzer.Syntax( "object name not found" );
2099 				}
2100 
2101 				nameId = GetUiID( parzer.Str() );
2102 				parzer.Next();
2103 			}
2104 
2105 			if ( !classId && !nameId ) { break; }
2106 
2107 			AppendObj( classId, nameId, oneStep );
2108 		}
2109 
2110 		if ( parzer.Tok() == '@' ) //item
2111 		{
2112 			parzer.Next();
2113 
2114 			if ( parzer.Tok() != UiParzer::TOK_ID ) { parzer.Syntax( "item name not found" ); }
2115 
2116 			SetItem( item = GetUiID( parzer.Str() ) );
2117 			parzer.Next();
2118 		}
2119 
2120 		while ( parzer.Tok() == ':' )
2121 		{
2122 			parzer.Next();
2123 			bool no = false;
2124 
2125 			if ( parzer.Tok() == '!' )
2126 			{
2127 				parzer.Next();
2128 				no = true;
2129 			}
2130 
2131 			if ( parzer.Tok() != UiParzer::TOK_ID ) { parzer.Syntax( "condition name not found" ); }
2132 
2133 			AppendCond( no, GetUiID( parzer.Str() ) );
2134 			parzer.Next();
2135 		}
2136 	}
2137 
ParzeNode(UiParzer & parzer)2138 	bool UiValue::ParzeNode( UiParzer& parzer )
2139 	{
2140 		if ( parzer.Tok() == UiParzer::TOK_INT )
2141 		{
2142 			list.append( new UiValueNode( parzer.Int() ) );
2143 		}
2144 		else   if ( parzer.Tok() == UiParzer::TOK_STR )
2145 		{
2146 			list.append( new UiValueNode( parzer.Str() ) );
2147 		}
2148 		else
2149 		{
2150 			return false;
2151 		}
2152 
2153 		parzer.Next();
2154 		return true;
2155 	}
2156 
Parze(UiParzer & parzer)2157 	void UiValue::Parze( UiParzer& parzer )
2158 	{
2159 		if ( parzer.Tok() == '(' )
2160 		{
2161 			parzer.Next();
2162 
2163 			while ( true )
2164 			{
2165 				if ( !ParzeNode( parzer ) ) { break; }
2166 
2167 				if ( parzer.Tok() != ',' ) { break; }
2168 
2169 				parzer.Next();
2170 			};
2171 
2172 			parzer.Skip( ')' );
2173 		}
2174 		else
2175 		{
2176 			ParzeNode( parzer );
2177 		}
2178 	}
2179 
UiValue()2180 	UiValue::UiValue() {}
~UiValue()2181 	UiValue::~UiValue() {}
2182 
2183 	class UiRules: public iIntrusiveCounter
2184 	{
2185 		friend class UiCache;
2186 		ccollect< clPtr<UiSelector>, 0x100>  selectors;
2187 		UiValue* values;
2188 	public:
UiRules()2189 		UiRules(): values( 0 ) {}
2190 		void Parze( UiParzer& parzer );
2191 		~UiRules();
2192 	};
2193 
2194 
2195 
~UiRules()2196 	UiRules::~UiRules()
2197 	{
2198 		UiValue* v = values;
2199 
2200 		while ( v )
2201 		{
2202 			UiValue* t = v;
2203 			v = v->next;
2204 			delete t;
2205 		}
2206 	}
2207 
Parze(UiParzer & parzer)2208 	void UiRules::Parze( UiParzer& parzer )
2209 	{
2210 		while ( true )
2211 		{
2212 			parzer.SkipAll( ';', 0 );
2213 
2214 			if ( parzer.Tok() == UiParzer::TOK_EOF ) { break; }
2215 
2216 			ccollect< clPtr<UiSelector> > slist;
2217 
2218 			while ( true )
2219 			{
2220 				if ( parzer.Tok() == '{' ) { break; }
2221 
2222 				clPtr<UiSelector> sel = new UiSelector();
2223 				slist.append( sel );
2224 				sel->Parze( parzer );
2225 
2226 				int i = 0;
2227 
2228 				while ( i < selectors.count() )
2229 				{
2230 					clPtr<UiSelector> other = selectors[i];
2231 
2232 					if ( sel->Cmp( other ) >= 0 ) { break; }
2233 
2234 					i++;
2235 				}
2236 
2237 				if ( i < selectors.count() )
2238 				{
2239 					selectors.insert( i, sel );
2240 				}
2241 				else
2242 				{
2243 					selectors.append( sel );
2244 				}
2245 
2246 				if ( parzer.Tok() != ',' ) { break; }
2247 
2248 				parzer.Next();
2249 			}
2250 
2251 			parzer.Skip( '{' );
2252 
2253 			if ( !slist.count() ) { parzer.Syntax( "empty list of selectors" ); }
2254 
2255 			while ( true )
2256 			{
2257 				if ( parzer.Tok() != UiParzer::TOK_ID ) { break; }
2258 
2259 				int id = GetUiID( parzer.Str() );
2260 				parzer.Next();
2261 
2262 				parzer.Skip( ':' );
2263 
2264 				values = new UiValue( values );
2265 				values->Parze( parzer );
2266 
2267 				for ( int i = 0; i < slist.count(); i++ )
2268 				{
2269 					slist[i]->AppendVal( id, values );
2270 				}
2271 
2272 				if ( parzer.Tok() == '}' ) { break; }
2273 
2274 				parzer.SkipAll( ';' );
2275 			}
2276 
2277 			parzer.Skip( '}' );
2278 		}
2279 	}
2280 
Update(UiRules & rules,ObjNode * orderList,int orderlistCount)2281 	void UiCache::Update( UiRules& rules, ObjNode* orderList, int orderlistCount )
2282 	{
2283 //printf("UiCache::Update 1: %i\n", orderlistCount);
2284 		hash.clear();
2285 		clPtr<UiSelector>* psl = rules.selectors.ptr();
2286 		int count = rules.selectors.count();
2287 
2288 		for ( int i = 0; count > 0; count--, psl++, i++ )
2289 		{
2290 			clPtr<UiSelector> s = rules.selectors.get( i );
2291 			int scount = s->stack.count();
2292 
2293 			if ( scount > 0 )
2294 			{
2295 				if ( orderlistCount > 0 )
2296 				{
2297 					UiSelector::Node* sp = s->stack.ptr() + scount - 1;
2298 					ObjNode* op = orderList;
2299 					int n = orderlistCount;
2300 
2301 					bool oneStep = true;
2302 
2303 					while ( scount > 0 && n > 0 )
2304 					{
2305 						if ( ( !sp->idC || sp->idC == op->classId ) && ( !sp->idN || sp->idN == op->nameId ) )
2306 						{
2307 							oneStep = sp->oneStep;
2308 							sp--;
2309 							scount--;
2310 							op++;
2311 							n--;
2312 						}
2313 						else
2314 						{
2315 							if ( oneStep ) { break; }
2316 
2317 							op++;
2318 							n--;
2319 						}
2320 					}
2321 
2322 					if ( scount <= 0 && s->val.count() )
2323 					{
2324 						int count = s->val.count();
2325 
2326 //printf("'");
2327 						for ( int i = 0; i < count; i++ )
2328 						{
2329 							hash[s->val[i].id].append( Node( s.ptr(), s->val[i].data ) );
2330 //printf("*");
2331 						}
2332 
2333 //printf("'\n");
2334 					}
2335 				}
2336 			}
2337 		}
2338 
2339 //printf("cache hash count=%i\n", hash.count());
2340 		updated = true;
2341 	}
2342 
2343 
Get(int id,int item,int * condList)2344 	UiValue* UiCache::Get( int id, int item, int* condList )
2345 	{
2346 		auto i = hash.find( id );
2347 
2348 		if ( i == hash.end() ) { return nullptr; }
2349 
2350 		ccollect<Node>* ids = &( i->second );
2351 
2352 		if ( !ids ) { return nullptr; }
2353 
2354 		Node* p = ids->ptr();
2355 		int count = ids->count();
2356 
2357 		for ( ; count > 0; count--, p++ )
2358 		{
2359 			clPtr<UiSelector> s = p->s;
2360 
2361 			if ( !item && s->item ) { continue; }
2362 
2363 			if ( item && s->item && s->item != item ) { continue; }
2364 
2365 			int i;
2366 			int n = s->cond.count();
2367 
2368 			for ( i = 0; i < n; i++ )
2369 			{
2370 				int cid = s->cond[i].id;
2371 				bool no = s->cond[i].no;
2372 
2373 				bool found = false;
2374 
2375 				if ( condList )
2376 				{
2377 					for ( int* t = condList; *t; t++ )
2378 						if ( *t == cid )
2379 						{
2380 							found = true;
2381 							break;
2382 						}
2383 				}
2384 
2385 				if ( found == no ) { break; }
2386 			}
2387 
2388 			if ( i >= n ) { return p->v; }
2389 		}
2390 
2391 		return 0;
2392 	}
2393 
2394 
2395 	static clPtr<UiRules> globalUiRules;
2396 
ClearWinCache(const WINHASHNODE * pnode,void *)2397 	static void ClearWinCache( const WINHASHNODE* pnode, void* )
2398 	{
2399 		if ( pnode->win )
2400 		{
2401 			pnode->win->UiCacheClear();
2402 		}
2403 	}
2404 
UiReadFile(const sys_char_t * fileName)2405 	void UiReadFile( const sys_char_t* fileName )
2406 	{
2407 		UiStreamFile stream( fileName );
2408 		UiParzer parzer( &stream );
2409 
2410 		clPtr<UiRules> rules  = new UiRules();
2411 		rules->Parze( parzer );
2412 		globalUiRules = rules;
2413 		for ( const auto& i : winhash ) ClearWinCache( &(i.second), nullptr );
2414 	}
2415 
UiReadMem(const char * s)2416 	void UiReadMem( const char* s )
2417 	{
2418 		UiStreamMem stream( s );
2419 		UiParzer parzer( &stream );
2420 
2421 		clPtr<UiRules> rules  = new UiRules();
2422 		rules->Parze( parzer );
2423 		globalUiRules = rules;
2424 		for ( const auto& i : winhash ) ClearWinCache( &(i.second), nullptr );
2425 	}
2426 
Set(int id,bool yes)2427 	void UiCondList::Set( int id, bool yes )
2428 	{
2429 		int i;
2430 
2431 		for ( i = 0; i < N; i++ ) if ( buf[i] == id ) { buf[i] = 0; }
2432 
2433 		if ( yes )
2434 		{
2435 			for ( i = 0; i < N; i++ )
2436 				if ( !buf[i] )
2437 				{
2438 					buf[i] = id;
2439 					return;
2440 				}
2441 		}
2442 	}
2443 
2444 	int uiColor = GetUiID( "color" );
2445 	int uiHotkeyColor = GetUiID( "hotkey-color" );
2446 	int uiBackground = GetUiID( "background" );
2447 	int uiFocusFrameColor = GetUiID( "focus-frame-color" );
2448 	int uiFrameColor = GetUiID( "frame-color" );
2449 	int uiButtonColor = GetUiID( "button-color" );
2450 	int uiMarkColor = GetUiID( "mark-color" );
2451 	int uiMarkBackground = GetUiID( "mark-background" );
2452 	int uiCurrentItem = GetUiID( "current-item" );
2453 	int uiCurrentItemFrame = GetUiID( "current-item-frame-color" );
2454 	int uiLineColor = GetUiID( "line-color" );
2455 	int uiPointerColor = GetUiID( "pointer-color" );
2456 	int uiOdd = GetUiID( "odd" );
2457 
2458 	int uiVariable = GetUiID( "variable" );
2459 	int uiValue = GetUiID( "value" );
2460 
2461 	int uiEnabled = GetUiID( "enabled" );
2462 	int uiFocus = GetUiID( "focus" );
2463 	int uiItem = GetUiID( "item" );
2464 	int uiClassWin = GetUiID( "Win" );
2465 
UiGetColor(int id,int itemId,UiCondList * cl,unsigned def)2466 	unsigned Win::UiGetColor( int id, int itemId, UiCondList* cl, unsigned def )
2467 	{
2468 		if ( !globalUiRules.ptr() ) { return def; }
2469 
2470 		int buf[0x100];
2471 		int pos = 0;
2472 
2473 		if ( cl )
2474 		{
2475 			for ( int i = 0; i < UiCondList::N && i < 0x100 - 10; i++ )
2476 				if ( cl->buf[i] )
2477 				{
2478 					buf[pos++] = cl->buf[i];
2479 				}
2480 		}
2481 
2482 		if ( IsEnabled() )
2483 		{
2484 			buf[pos++] = uiEnabled;
2485 		}
2486 
2487 		if ( InFocus() )
2488 		{
2489 			buf[pos++] = uiFocus;
2490 		}
2491 
2492 		buf[pos++] = 0;
2493 
2494 //printf("1\n");
2495 		if ( !uiCache.Updated() )
2496 		{
2497 //printf("2\n");
2498 			//Update(UiRules &rules, ObjNode *orderList, int orderlistCount)
2499 			ccollect<UiCache::ObjNode> wlist;
2500 
2501 			for ( Win* w = this; w; w = w->Parent() )
2502 			{
2503 				wlist.append( UiCache::ObjNode( w->UiGetClassId(), w->UiGetNameId() ) );
2504 			}
2505 
2506 			uiCache.Update( *globalUiRules.ptr(), wlist.ptr(), wlist.count() );
2507 		}
2508 
2509 //printf("3\n");
2510 		UiValue* v = uiCache.Get( id, itemId, buf );
2511 //printf("4 %p\n", v);
2512 //if (v) printf("5 %i\n", v->Int());
2513 		return v ? unsigned( v->Int() ) : def;
2514 	};
2515 
UiGetClassId()2516 	int Win::UiGetClassId()
2517 	{
2518 		return uiClassWin;
2519 	}
2520 
2521 } //namespace wal
2522