1 //------------------------------------------------------------------------------
2 // emPanel.cpp
3 //
4 // Copyright (C) 2004-2008,2011,2014-2017 Oliver Hamann.
5 //
6 // Homepage: http://eaglemode.sourceforge.net/
7 //
8 // This program is free software: you can redistribute it and/or modify it under
9 // the terms of the GNU General Public License version 3 as published by the
10 // Free Software Foundation.
11 //
12 // This program is distributed in the hope that it will be useful, but WITHOUT
13 // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14 // FOR A PARTICULAR PURPOSE. See the GNU General Public License version 3 for
15 // more details.
16 //
17 // You should have received a copy of the GNU General Public License version 3
18 // along with this program. If not, see <http://www.gnu.org/licenses/>.
19 //------------------------------------------------------------------------------
20 
21 #include <emCore/emList.h>
22 #include <emCore/emPanel.h>
23 
24 
emPanel(ParentArg parent,const emString & name)25 emPanel::emPanel(ParentArg parent, const emString & name)
26 	: emEngine(parent.GetView().GetScheduler()),
27 	View(parent.GetView()),
28 	Name(name)
29 {
30 
31 #	define EM_PANEL_DEFAULT_AE_THRESHOLD 100.0 //???
32 
33 	if (parent.GetPanel()) {
34 		AvlTree=NULL;
35 		Parent=parent.GetPanel();
36 		FirstChild=NULL;
37 		LastChild=NULL;
38 		Prev=Parent->LastChild;
39 		Next=NULL;
40 		if (Prev) Prev->Next=this; else Parent->FirstChild=this;
41 		Parent->LastChild=this;
42 		NoticeNode.Prev=NULL;
43 		NoticeNode.Next=NULL;
44 		LayoutX=-2;
45 		LayoutY=-2;
46 		LayoutWidth=1;
47 		LayoutHeight=1;
48 		ViewedX=-1;
49 		ViewedY=-1;
50 		ViewedWidth=1;
51 		ViewedHeight=1;
52 		ClipX1=0;
53 		ClipY1=0;
54 		ClipX2=0;
55 		ClipY2=0;
56 		AEThresholdValue=EM_PANEL_DEFAULT_AE_THRESHOLD;
57 		CanvasColor=0;
58 		PendingNoticeFlags=0;
59 		Viewed=0;
60 		InViewedPath=0;
61 		EnableSwitch=1;
62 		Enabled=Parent->Enabled;
63 		Focusable=1;
64 		Active=0;
65 		InActivePath=0;
66 		PendingInput=0;
67 		ChildrenLayoutInvalid=0;
68 		AEInvalid=0;
69 		AEDecisionInvalid=0;
70 		AECalling=0;
71 		AEExpanded=0;
72 		CreatedByAE=Parent->AECalling;
73 		AEThresholdType=VCT_AREA;
74 		AutoplayHandling=APH_ITEM;
75 		Parent->AvlInsertChild(this);
76 		Parent->AddPendingNotice(NF_CHILD_LIST_CHANGED);
77 		AddPendingNotice(
78 			NF_CHILD_LIST_CHANGED |
79 			NF_LAYOUT_CHANGED |
80 			NF_VIEWING_CHANGED |
81 			NF_ENABLE_CHANGED |
82 			NF_ACTIVE_CHANGED |
83 			NF_FOCUS_CHANGED |
84 			NF_VIEW_FOCUS_CHANGED |
85 			NF_UPDATE_PRIORITY_CHANGED |
86 			NF_MEMORY_LIMIT_CHANGED |
87 			NF_SOUGHT_NAME_CHANGED
88 		);
89 	}
90 	else {
91 		if (View.RootPanel) {
92 			emFatalError(
93 				"Root panel created for an emView which has already a root panel."
94 			);
95 		}
96 		View.RootPanel=this;
97 		View.SupremeViewedPanel=this;
98 		View.MinSVP=this;
99 		View.MaxSVP=this;
100 		View.ActivePanel=this;
101 		AvlTree=NULL;
102 		Parent=NULL;
103 		FirstChild=NULL;
104 		LastChild=NULL;
105 		Prev=NULL;
106 		Next=NULL;
107 		NoticeNode.Prev=NULL;
108 		NoticeNode.Next=NULL;
109 		LayoutX=0;
110 		LayoutY=0;
111 		LayoutWidth=1.0;
112 		LayoutHeight=View.GetHomeTallness();
113 		ViewedX=View.CurrentX;
114 		ViewedY=View.CurrentY;
115 		ViewedWidth=View.CurrentWidth;
116 		ViewedHeight=View.CurrentHeight;
117 		ClipX1=ViewedX;
118 		ClipY1=ViewedY;
119 		ClipX2=ViewedX+ViewedWidth;
120 		ClipY2=ViewedY+ViewedHeight;
121 		AEThresholdValue=EM_PANEL_DEFAULT_AE_THRESHOLD;
122 		CanvasColor=0;
123 		PendingNoticeFlags=0;
124 		Viewed=1;
125 		InViewedPath=1;
126 		EnableSwitch=1;
127 		Enabled=1;
128 		Focusable=1;
129 		Active=1;
130 		InActivePath=1;
131 		PendingInput=0;
132 		ChildrenLayoutInvalid=0;
133 		AEInvalid=0;
134 		AEDecisionInvalid=0;
135 		AECalling=0;
136 		AEExpanded=0;
137 		CreatedByAE=0;
138 		AEThresholdType=VCT_AREA;
139 		AutoplayHandling=APH_ITEM;
140 		InvalidatePainting();
141 		AddPendingNotice(
142 			NF_CHILD_LIST_CHANGED |
143 			NF_LAYOUT_CHANGED |
144 			NF_VIEWING_CHANGED |
145 			NF_ENABLE_CHANGED |
146 			NF_ACTIVE_CHANGED |
147 			NF_FOCUS_CHANGED |
148 			NF_VIEW_FOCUS_CHANGED |
149 			NF_UPDATE_PRIORITY_CHANGED |
150 			NF_MEMORY_LIMIT_CHANGED |
151 			NF_SOUGHT_NAME_CHANGED
152 		);
153 		View.TitleInvalid=true;
154 		View.CursorInvalid=true;
155 		View.UpdateEngine->WakeUp();
156 	}
157 }
158 
159 
~emPanel()160 emPanel::~emPanel()
161 {
162 	InvalidatePainting();
163 	if (View.SeekPosPanel==this) View.SetSeekPos(NULL,NULL);
164 	DeleteAllChildren();
165 	if (!Parent) {
166 		if (View.IsPoppedUp()) View.RawZoomOut();
167 		View.RootPanel=NULL;
168 		View.SupremeViewedPanel=NULL;
169 		View.MinSVP=NULL;
170 		View.MaxSVP=NULL;
171 		View.ActivePanel=NULL;
172 		View.ActivationAdherent=false;
173 		View.TitleInvalid=true;
174 		View.CursorInvalid=true;
175 		View.UpdateEngine->WakeUp();
176 	}
177 	else {
178 		if (InActivePath || View.SupremeViewedPanel==this) {
179 			SetFocusable(false);
180 			if (View.SupremeViewedPanel==this) {
181 				LayoutX=-2.0;
182 				LayoutY=-2.0;
183 				LayoutWidth=1.0;
184 				LayoutHeight=1.0;
185 				CanvasColor=0;
186 				if ((View.GetViewFlags()&emView::VF_POPUP_ZOOM)!=0 && !View.IsPoppedUp()) {
187 					View.RawZoomOut();
188 				}
189 				else {
190 					View.RawVisitFullsized(Parent);
191 				}
192 			}
193 			if (InActivePath || View.SupremeViewedPanel==this) {
194 				emFatalError("emPanel::~emPanel: Could not to get rid of activation or SVP status.");
195 			}
196 		}
197 		if (View.MinSVP==this) {
198 			View.MinSVP=Parent;
199 		}
200 		View.RestartInputRecursion=true;
201 		if (InViewedPath) {
202 			View.SVPChoiceInvalid=true;
203 			View.TitleInvalid=true;
204 			View.CursorInvalid=true;
205 			View.UpdateEngine->WakeUp();
206 		}
207 		Parent->AvlRemoveChild(this);
208 		Parent->AddPendingNotice(NF_CHILD_LIST_CHANGED);
209 		if (Next) Next->Prev=Prev;
210 		else Parent->LastChild=Prev;
211 		if (Prev) Prev->Next=Next;
212 		else Parent->FirstChild=Next;
213 		Next=NULL;
214 		Prev=NULL;
215 	}
216 	if (NoticeNode.Next) {
217 		NoticeNode.Next->Prev=NoticeNode.Prev;
218 		NoticeNode.Prev->Next=NoticeNode.Next;
219 		NoticeNode.Next=NULL;
220 		NoticeNode.Prev=NULL;
221 	}
222 }
223 
224 
GetIdentity() const225 emString emPanel::GetIdentity() const
226 {
227 	const emPanel * p;
228 	emArray<emString> a;
229 	int i;
230 
231 	p=this;
232 	i=0;
233 	do {
234 		i++;
235 		p=p->Parent;
236 	} while (p);
237 	a.SetTuningLevel(1);
238 	a.SetCount(i);
239 	p=this;
240 	do {
241 		i--;
242 		a.Set(i,p->Name);
243 		p=p->Parent;
244 	} while (p);
245 	return EncodeIdentity(a);
246 }
247 
248 
EncodeIdentity(const emArray<emString> & names)249 emString emPanel::EncodeIdentity(const emArray<emString> & names)
250 {
251 	emString res;
252 	const char * r, * s;
253 	char * t;
254 	int i,cnt,len;
255 	char c;
256 
257 	cnt=names.GetCount();
258 	len=cnt-1;
259 	for (i=0; i<cnt; i++) {
260 		r=s=names[i].Get();
261 		c=*s;
262 		if (c) {
263 			do {
264 				if (c==':' || c=='\\') len++;
265 				c=*++s;
266 			} while (c);
267 			len+=s-r;
268 		}
269 	}
270 	t=res.SetLenGetWritable(len);
271 	for (i=0; i<cnt; i++) {
272 		if (i>0) *t++=':';
273 		s=names[i].Get();
274 		c=*s;
275 		while (c) {
276 			if (c==':' || c=='\\') *t++='\\';
277 			*t++=c;
278 			c=*++s;
279 		}
280 	}
281 	return res;
282 }
283 
284 
DecodeIdentity(const char * identity)285 emArray<emString> emPanel::DecodeIdentity(const char * identity)
286 {
287 	emArray<emString> a;
288 	const char * p, * t;
289 	char * s;
290 	int i,q;
291 
292 	a.SetTuningLevel(1);
293 	p=identity;
294 	for (i=0; ; i++, p++) {
295 		a.SetCount(i+1);
296 		if (!*p) break;
297 		if (*p==':') continue;
298 		t=p;
299 		q=0;
300 		do {
301 			if (*t=='\\') {
302 				t++;
303 				q++;
304 				if (!*t) break;
305 			}
306 			t++;
307 		} while (*t && *t!=':');
308 		s=a.GetWritable(i).SetLenGetWritable(t-p-q);
309 		do {
310 			if (*p=='\\') {
311 				p++;
312 				if (!*p) break;
313 			}
314 			*s++=*p++;
315 		} while (*p && *p!=':');
316 		if (!*p) break;
317 	}
318 	return a;
319 }
320 
321 
GetTitle() const322 emString emPanel::GetTitle() const
323 {
324 	if (Parent) return Parent->GetTitle();
325 	else return "untitled";
326 }
327 
328 
GetIconFileName() const329 emString emPanel::GetIconFileName() const
330 {
331 	if (Parent) return Parent->GetIconFileName();
332 	else return emString();
333 }
334 
335 
GetChild(const char * name) const336 emPanel * emPanel::GetChild(const char * name) const
337 {
338 	EM_AVL_SEARCH_VARS(emPanel)
339 	int d;
340 
341 	EM_AVL_SEARCH_BEGIN(emPanel,AvlNode,AvlTree)
342 		d=strcmp(name,element->Name.Get());
343 		if (d<0) EM_AVL_SEARCH_GO_LEFT
344 		else if (d>0) EM_AVL_SEARCH_GO_RIGHT
345 	EM_AVL_SEARCH_END
346 	return element;
347 }
348 
349 
BeFirst()350 void emPanel::BeFirst()
351 {
352 	if (Prev) {
353 		Prev->Next=Next;
354 		if (Next) Next->Prev=Prev;
355 		else Parent->LastChild=Prev;
356 		Prev=NULL;
357 		Next=Parent->FirstChild;
358 		Next->Prev=this;
359 		Parent->FirstChild=this;
360 		Parent->AddPendingNotice(NF_CHILD_LIST_CHANGED);
361 		View.RestartInputRecursion=true;
362 		if (InViewedPath) {
363 			InvalidatePainting();
364 			View.SVPChoiceInvalid=true;
365 			View.CursorInvalid=true;
366 			View.UpdateEngine->WakeUp();
367 		}
368 	}
369 }
370 
371 
BeLast()372 void emPanel::BeLast()
373 {
374 	if (Next) {
375 		Next->Prev=Prev;
376 		if (Prev) Prev->Next=Next;
377 		else Parent->FirstChild=Next;
378 		Next=NULL;
379 		Prev=Parent->LastChild;
380 		Prev->Next=this;
381 		Parent->LastChild=this;
382 		Parent->AddPendingNotice(NF_CHILD_LIST_CHANGED);
383 		View.RestartInputRecursion=true;
384 		if (Parent->InViewedPath) {
385 			InvalidatePainting();
386 			View.SVPChoiceInvalid=true;
387 			View.CursorInvalid=true;
388 			View.UpdateEngine->WakeUp();
389 		}
390 	}
391 }
392 
393 
BePrevOf(emPanel * sister)394 void emPanel::BePrevOf(emPanel * sister)
395 {
396 	if (!sister) {
397 		BeLast();
398 		return;
399 	}
400 	if (sister==this || sister==Next || sister->Parent!=Parent) return;
401 	if (Prev) Prev->Next=Next;
402 	else Parent->FirstChild=Next;
403 	if (Next) Next->Prev=Prev;
404 	else Parent->LastChild=Prev;
405 	Next=sister;
406 	Prev=sister->Prev;
407 	sister->Prev=this;
408 	if (Prev) Prev->Next=this; else Parent->FirstChild=this;
409 	Parent->AddPendingNotice(NF_CHILD_LIST_CHANGED);
410 	View.RestartInputRecursion=true;
411 	if (Parent->InViewedPath) {
412 		InvalidatePainting();
413 		View.SVPChoiceInvalid=true;
414 		View.CursorInvalid=true;
415 		View.UpdateEngine->WakeUp();
416 	}
417 }
418 
419 
BeNextOf(emPanel * sister)420 void emPanel::BeNextOf(emPanel * sister)
421 {
422 	if (!sister) {
423 		BeFirst();
424 		return;
425 	}
426 	if (sister==this || sister==Prev || sister->Parent!=Parent) return;
427 	if (Next) Next->Prev=Prev;
428 	else Parent->LastChild=Prev;
429 	if (Prev) Prev->Next=Next;
430 	else Parent->FirstChild=Next;
431 	Prev=sister;
432 	Next=sister->Next;
433 	sister->Next=this;
434 	if (Next) Next->Prev=this; else Parent->LastChild=this;
435 	Parent->AddPendingNotice(NF_CHILD_LIST_CHANGED);
436 	View.RestartInputRecursion=true;
437 	if (Parent->InViewedPath) {
438 		InvalidatePainting();
439 		View.SVPChoiceInvalid=true;
440 		View.CursorInvalid=true;
441 		View.UpdateEngine->WakeUp();
442 	}
443 }
444 
445 
SortChildren(int (* compare)(emPanel * c1,emPanel * c2,void * context),void * context)446 void emPanel::SortChildren(
447 	int(*compare)(emPanel * c1, emPanel * c2, void * context),
448 	void * context
449 )
450 {
451 	if (
452 		emSortDoubleLinkedList(
453 			(void**)(void*)&FirstChild,
454 			(void**)(void*)&LastChild,
455 			offsetof(emPanel,Next),
456 			offsetof(emPanel,Prev),
457 			(int(*)(void*,void*,void*))compare,
458 			context
459 		)
460 	) {
461 		AddPendingNotice(NF_CHILD_LIST_CHANGED);
462 		View.RestartInputRecursion=true;
463 		if (InViewedPath) {
464 			InvalidatePainting();
465 			View.SVPChoiceInvalid=true;
466 			View.CursorInvalid=true;
467 			View.UpdateEngine->WakeUp();
468 		}
469 	}
470 }
471 
472 
DeleteAllChildren()473 void emPanel::DeleteAllChildren()
474 {
475 	while (LastChild) delete LastChild;
476 }
477 
478 
Layout(double layoutX,double layoutY,double layoutWidth,double layoutHeight,emColor canvasColor)479 void emPanel::Layout(
480 	double layoutX, double layoutY, double layoutWidth, double layoutHeight,
481 	emColor canvasColor
482 )
483 {
484 	emPanel * p;
485 	double x1,y1,x2,y2,rx,ry,ra;
486 	bool zoomedOut;
487 
488 	if (LayoutWidth<1E-100) LayoutWidth=1E-100;
489 	if (LayoutHeight<1E-100) LayoutHeight=1E-100;
490 
491 	if (!Parent) {
492 		layoutX=0.0;
493 		layoutY=0.0;
494 		if ((View.VFlags&emView::VF_ROOT_SAME_TALLNESS)!=0) {
495 			layoutHeight=View.GetHomeTallness();
496 		}
497 		else {
498 			layoutHeight/=layoutWidth;
499 		}
500 		layoutWidth=1.0;
501 	}
502 
503 	if (
504 		LayoutX==layoutX && LayoutY==layoutY &&
505 		LayoutWidth==layoutWidth && LayoutHeight==layoutHeight
506 	) {
507 		if (CanvasColor!=canvasColor) {
508 			CanvasColor=canvasColor;
509 			AddPendingNotice(NF_LAYOUT_CHANGED);
510 			InvalidatePainting();
511 		}
512 		return;
513 	}
514 
515 	AddPendingNotice(NF_LAYOUT_CHANGED);
516 	View.RestartInputRecursion=true;
517 	if (!Parent || Parent->InViewedPath) {
518 		InvalidatePainting();
519 		View.SVPChoiceInvalid=true;
520 		View.CursorInvalid=true;
521 		View.UpdateEngine->WakeUp();
522 	}
523 
524 	if (!Parent) {
525 		zoomedOut=View.IsZoomedOut();
526 		p=View.GetVisitedPanel(&rx,&ry,&ra);
527 		LayoutX=layoutX;
528 		LayoutY=layoutY;
529 		LayoutWidth=layoutWidth;
530 		LayoutHeight=layoutHeight;
531 		CanvasColor=canvasColor;
532 		if (!View.SettingGeometry) {
533 			if (zoomedOut) {
534 				View.RawZoomOut(true);
535 			}
536 			else if (p) {
537 				View.RawVisit(p,rx,ry,ra,true);
538 			}
539 		}
540 	}
541 	else if (
542 		InViewedPath &&
543 		(InActivePath || !Parent->Viewed) &&
544 		!View.SettingGeometry &&
545 		!View.IsZoomedOut()
546 	) {
547 		p=View.GetVisitedPanel(&rx,&ry,&ra);
548 		LayoutX=layoutX;
549 		LayoutY=layoutY;
550 		LayoutWidth=layoutWidth;
551 		LayoutHeight=layoutHeight;
552 		CanvasColor=canvasColor;
553 		View.RawVisit(p,rx,ry,ra,true);
554 	}
555 	else if (Parent->Viewed) {
556 		LayoutX=layoutX;
557 		LayoutY=layoutY;
558 		LayoutWidth=layoutWidth;
559 		LayoutHeight=layoutHeight;
560 		CanvasColor=canvasColor;
561 		x1=Parent->ViewedX+LayoutX*Parent->ViewedWidth;
562 		x2=LayoutWidth*Parent->ViewedWidth;
563 		y1=Parent->ViewedY+LayoutY*(Parent->ViewedWidth/View.CurrentPixelTallness);
564 		y2=LayoutHeight*(Parent->ViewedWidth/View.CurrentPixelTallness);
565 		ViewedX=x1;
566 		ViewedY=y1;
567 		ViewedWidth=x2;
568 		ViewedHeight=y2;
569 		x2+=x1;
570 		y2+=y1;
571 		if (x1<Parent->ClipX1) x1=Parent->ClipX1;
572 		if (x2>Parent->ClipX2) x2=Parent->ClipX2;
573 		if (y1<Parent->ClipY1) y1=Parent->ClipY1;
574 		if (y2>Parent->ClipY2) y2=Parent->ClipY2;
575 		ClipX1=x1;
576 		ClipX2=x2;
577 		ClipY1=y1;
578 		ClipY2=y2;
579 		if (x1<x2 && y1<y2) {
580 			InViewedPath=1;
581 			Viewed=1;
582 			AddPendingNotice(
583 				NF_VIEWING_CHANGED |
584 				NF_UPDATE_PRIORITY_CHANGED |
585 				NF_MEMORY_LIMIT_CHANGED
586 			);
587 			InvalidatePainting();
588 			UpdateChildrenViewing();
589 		}
590 		else if (InViewedPath) {
591 			InViewedPath=0;
592 			Viewed=0;
593 			AddPendingNotice(
594 				NF_VIEWING_CHANGED |
595 				NF_UPDATE_PRIORITY_CHANGED |
596 				NF_MEMORY_LIMIT_CHANGED
597 			);
598 			UpdateChildrenViewing();
599 		}
600 	}
601 	else {
602 		LayoutX=layoutX;
603 		LayoutY=layoutY;
604 		LayoutWidth=layoutWidth;
605 		LayoutHeight=layoutHeight;
606 		CanvasColor=canvasColor;
607 	}
608 }
609 
610 
GetSubstanceRect(double * pX,double * pY,double * pW,double * pH,double * pR) const611 void emPanel::GetSubstanceRect(
612 	double * pX, double * pY, double * pW, double * pH, double * pR
613 ) const
614 {
615 	*pX=0.0;
616 	*pY=0.0;
617 	*pW=1.0;
618 	*pH=GetHeight();
619 	*pR=0.0;
620 }
621 
622 
IsPointInSubstanceRect(double x,double y) const623 bool emPanel::IsPointInSubstanceRect(double x, double y) const
624 {
625 	double sx,sy,sw,sh,sr,dx,dy,sw2,sh2;
626 
627 	if (x<0.0 || x>=1.0 || y<0.0 || y>=GetHeight()) return false;
628 	GetSubstanceRect(&sx,&sy,&sw,&sh,&sr);
629 	sw2=sw*0.5;
630 	dx=fabs(x-sx-sw2);
631 	if (dx>sw2) return false;
632 	sh2=sh*0.5;
633 	dy=fabs(y-sy-sh2);
634 	if (dy>sh2) return false;
635 	if (sr>sw2) sr=sw2;
636 	if (sr>sh2) sr=sh2;
637 	dx-=sw2-sr;
638 	dy-=sh2-sr;
639 	return dx<0.0 || dy<0.0 || dx*dx+dy*dy <= sr*sr;
640 }
641 
642 
GetEssenceRect(double * pX,double * pY,double * pW,double * pH) const643 void emPanel::GetEssenceRect(
644 	double * pX, double * pY, double * pW, double * pH
645 ) const
646 {
647 	double r;
648 
649 	GetSubstanceRect(pX,pY,pW,pH,&r);
650 }
651 
652 
GetViewCondition(ViewConditionType vcType) const653 double emPanel::GetViewCondition(ViewConditionType vcType) const
654 {
655 	if (Viewed) {
656 		switch (vcType) {
657 			case VCT_AREA   : return ViewedWidth*ViewedHeight;
658 			case VCT_WIDTH  : return ViewedWidth;
659 			case VCT_HEIGHT : return ViewedHeight;
660 			case VCT_MIN_EXT: return emMin(ViewedWidth, ViewedHeight);
661 			case VCT_MAX_EXT: return emMax(ViewedWidth, ViewedHeight);
662 		};
663 	}
664 	else if (InViewedPath) {
665 		return 1E100;
666 	}
667 	return 0.0;
668 }
669 
670 
SetAutoExpansionThreshold(double thresholdValue,ViewConditionType vcType)671 void emPanel::SetAutoExpansionThreshold(
672 	double thresholdValue, ViewConditionType vcType
673 )
674 {
675 	if (
676 		AEThresholdValue!=thresholdValue ||
677 		((ViewConditionType)AEThresholdType)!=vcType
678 	) {
679 		AEThresholdValue=thresholdValue;
680 		AEThresholdType=vcType;
681 		AEDecisionInvalid=1;
682 		if (!NoticeNode.Next) View.AddToNoticeList(&NoticeNode);
683 	}
684 }
685 
686 
SetEnableSwitch(bool enableSwitch)687 void emPanel::SetEnableSwitch(bool enableSwitch)
688 {
689 	emPanel * p;
690 
691 	if (enableSwitch) {
692 		if (!EnableSwitch) {
693 			EnableSwitch=1;
694 			if (!Parent || Parent->Enabled) {
695 				p=this;
696 				for (;;) {
697 					if (p->EnableSwitch) {
698 						p->Enabled=1;
699 						p->AddPendingNotice(NF_ENABLE_CHANGED);
700 						if (p->FirstChild) {
701 							p=p->FirstChild;
702 							continue;
703 						}
704 					}
705 					while (p!=this && !p->Next) {
706 						p=p->Parent;
707 					}
708 					if (p==this) break;
709 					p=p->Next;
710 				}
711 			}
712 		}
713 	}
714 	else {
715 		if (EnableSwitch) {
716 			EnableSwitch=0;
717 			p=this;
718 			for (;;) {
719 				if (p->Enabled) {
720 					p->Enabled=0;
721 					p->AddPendingNotice(NF_ENABLE_CHANGED);
722 					if (p->FirstChild) {
723 						p=p->FirstChild;
724 						continue;
725 					}
726 				}
727 				while (p!=this && !p->Next) {
728 					p=p->Parent;
729 				}
730 				if (p==this) break;
731 				p=p->Next;
732 			}
733 		}
734 	}
735 }
736 
737 
SetFocusable(bool focusable)738 void emPanel::SetFocusable(bool focusable)
739 {
740 	if (((bool)Focusable)!=focusable) {
741 		if (!Parent && !focusable) {
742 			emDLog("emPanel::SetFocusable: a root panel cannot be set unfocusable");
743 			return;
744 		}
745 		Focusable=focusable;
746 		if (!Focusable && Active) {
747 			View.SetActivePanel(Parent,false);
748 		}
749 	}
750 }
751 
752 
GetFocusableParent() const753 emPanel * emPanel::GetFocusableParent() const
754 {
755 	const emPanel * p;
756 
757 	p=this;
758 	do {
759 		p=p->Parent;
760 	} while (p && !p->Focusable);
761 	return (emPanel*)p;
762 }
763 
764 
GetFocusableFirstChild() const765 emPanel * emPanel::GetFocusableFirstChild() const
766 {
767 	const emPanel * p;
768 
769 	p=FirstChild;
770 	if (!p) return NULL;
771 	for (;;) {
772 		if (p->Focusable) return (emPanel*)p;
773 		if (p->FirstChild) p=p->FirstChild;
774 		else {
775 			while (!p->Next) {
776 				p=p->Parent;
777 				if (p==this) return NULL;
778 			}
779 			p=p->Next;
780 		}
781 	}
782 }
783 
784 
GetFocusableLastChild() const785 emPanel * emPanel::GetFocusableLastChild() const
786 {
787 	const emPanel * p;
788 
789 	p=LastChild;
790 	if (!p) return NULL;
791 	for (;;) {
792 		if (p->Focusable) return (emPanel*)p;
793 		if (p->LastChild) p=p->LastChild;
794 		else {
795 			while (!p->Prev) {
796 				p=p->Parent;
797 				if (p==this) return NULL;
798 			}
799 			p=p->Prev;
800 		}
801 	}
802 }
803 
804 
GetFocusablePrev() const805 emPanel * emPanel::GetFocusablePrev() const
806 {
807 	const emPanel * p;
808 
809 	for (p=this;;) {
810 		while (!p->Prev) {
811 			p=p->Parent;
812 			if (!p || p->Focusable) return NULL;
813 		}
814 		p=p->Prev;
815 		for (;;) {
816 			if (p->Focusable) return (emPanel*)p;
817 			if (!p->LastChild) break;
818 			p=p->LastChild;
819 		}
820 	}
821 }
822 
823 
GetFocusableNext() const824 emPanel * emPanel::GetFocusableNext() const
825 {
826 	const emPanel * p;
827 
828 	for (p=this;;) {
829 		while (!p->Next) {
830 			p=p->Parent;
831 			if (!p || p->Focusable) return NULL;
832 		}
833 		p=p->Next;
834 		for (;;) {
835 			if (p->Focusable) return (emPanel*)p;
836 			if (!p->FirstChild) break;
837 			p=p->FirstChild;
838 		}
839 	}
840 }
841 
842 
Activate(bool adherent)843 void emPanel::Activate(bool adherent)
844 {
845 	View.SetActivePanel(this,adherent);
846 }
847 
848 
Focus(bool adherent)849 void emPanel::Focus(bool adherent)
850 {
851 	View.Focus();
852 	View.SetActivePanel(this,adherent);
853 }
854 
855 
GetUpdatePriority() const856 double emPanel::GetUpdatePriority() const
857 {
858 	double x1,y1,x2,y2,vx,vy,vw,vh,k,pri;
859 
860 	if (Viewed) {
861 		vx=View.GetCurrentX();
862 		vw=View.GetCurrentWidth();
863 		vy=View.GetCurrentY();
864 		vh=View.GetCurrentHeight();
865 		x1=(ClipX1-vx)/vw-0.5;
866 		x2=(ClipX2-vx)/vw-0.5;
867 		y1=(ClipY1-vy)/vh-0.5;
868 		y2=(ClipY2-vy)/vh-0.5;
869 		if (x1<x2 && y1<y2) {
870 			k=0.5;
871 			pri=
872 				(((x1*x1*x1-x2*x2*x2)+(x2-x1)*(k+0.25))/k) *
873 				(((y1*y1*y1-y2*y2*y2)+(y2-y1)*(k+0.25))/k)
874 			;
875 			// The above formula results in the range of 0.0 to 1.0.
876 			pri*=0.49;
877 			if (IsViewFocused()) pri+=0.5;
878 			return pri;
879 		}
880 		else {
881 			return 0.0;
882 		}
883 	}
884 	else if (InViewedPath) {
885 		if (IsViewFocused()) return 1.0;
886 		else return 0.5;
887 	}
888 	else {
889 		return 0.0;
890 	}
891 }
892 
893 
GetMemoryLimit() const894 emUInt64 emPanel::GetMemoryLimit() const
895 {
896 
897 #define MEMORY_LIMIT_VARIANT 3 //???
898 
899 #if MEMORY_LIMIT_VARIANT==1
900 
901 	// This is the stupid one. It produces too much flicker by
902 	// loading/unloading of panels.
903 
904 	double maxPerView;
905 
906 	if (!InViewedPath) return 0;
907 	maxPerView=View.CoreConfig->MaxMegabytesPerView*1000000.0;
908 	if (!Viewed || View.SeekPosPanel==this) return (emUInt64)maxPerView;
909 	return (emUInt64)(
910 		(ClipX2-ClipX1)*(ClipY2-ClipY1)*maxPerView/
911 		(View.GetCurrentWidth()*View.GetCurrentHeight())
912 	);
913 
914 #elif MEMORY_LIMIT_VARIANT==2
915 
916 	// This trivial one did it for years. It assumes that memory intensive
917 	// panels are making up no more than about 36% of the areas (like with
918 	// emDirEntryPanel), and that the view size is about 1280*1024 pixels.
919 
920 	double maxPerView,maxPerPanel,maxPerPixel,m;
921 
922 	if (!InViewedPath) return 0;
923 	maxPerView=View.CoreConfig->MaxMegabytesPerView*1000000.0;
924 	maxPerPanel=maxPerView/600.0*75.0;
925 	maxPerPixel=maxPerView/600000000.0*500.0;
926 	if (!Viewed || View.SeekPosPanel==this) return (emUInt64)maxPerPanel;
927 	m=ViewedWidth*ViewedHeight*maxPerPixel;
928 	if (m>maxPerPanel) m=maxPerPanel;
929 	return (emUInt64)m;
930 
931 #elif MEMORY_LIMIT_VARIANT==3
932 
933 	// This algorithm solves a good balance between reducing flicker and not
934 	// wasting too much memory for invisible things. But it assumes that
935 	// memory intensive panels are never overlapping each other. And it does
936 	// not take advantage of unused space between memory intensive panels.
937 	// And the limit is still for the sub-tree, not for the node itself.
938 
939 	double maxPerViewByUser,maxPerView,maxPerPanel;
940 	double viewExtension,viewExtensionValence;
941 	double vx,vy,vw,vh,evx1,evy1,evx2,evy2,ecx1,ecy1,ecx2,ecy2,fe,fn,f;
942 
943 	if (!InViewedPath) return 0;
944 	maxPerViewByUser=View.CoreConfig->MaxMegabytesPerView*1000000.0;
945 	maxPerView =maxPerViewByUser*2.0;
946 	maxPerPanel=maxPerViewByUser*0.33;
947 		// Explanation of the above two lines: When there are many small
948 		// panels, it is unlikely that every panel would be at the
949 		// limit, and the average memory usage is assumed to be small.
950 		// But with just one big panel, the limit can be easily reached.
951 		// On the other hand, it's the case of many panels for which a
952 		// user normally wants to configure a higher limit.
953 	if (!Viewed || View.SeekPosPanel==this) return (emUInt64)maxPerPanel;
954 	viewExtension=0.5;
955 	viewExtensionValence=0.5;
956 	vx=View.GetCurrentX();
957 	vy=View.GetCurrentY();
958 	vw=View.GetCurrentWidth();
959 	vh=View.GetCurrentHeight();
960 	evx1=vx-vw*(viewExtension*0.5);
961 	evy1=vy-vh*(viewExtension*0.5);
962 	evx2=evx1+vw*(1.0+viewExtension);
963 	evy2=evy1+vh*(1.0+viewExtension);
964 	ecx1=ViewedX;
965 	ecy1=ViewedY;
966 	ecx2=ecx1+ViewedWidth;
967 	ecy2=ecy1+ViewedHeight;
968 	if (ecx1<evx1) ecx1=evx1;
969 	if (ecy1<evy1) ecy1=evy1;
970 	if (ecx2>evx2) ecx2=evx2;
971 	if (ecy2>evy2) ecy2=evy2;
972 	fe=(ecx2-ecx1)*(ecy2-ecy1)/((evx2-evx1)*(evy2-evy1));
973 	fn=(ClipX2-ClipX1)*(ClipY2-ClipY1)/(vw*vh);
974 	f=fe*viewExtensionValence+fn*(1.0-viewExtensionValence);
975 	f*=maxPerView;
976 	if (f>maxPerPanel) f=maxPerPanel;
977 	if (f<0.0) f=0.0;
978 	return (emUInt64)f;
979 
980 #elif MEMORY_LIMIT_VARIANT==4
981 
982 	// This experimental algorithm distributes the memory limit of a parent
983 	// to its children depending on their visible areas and sizes. It solves
984 	// the problem of overlapped panels and it takes advantage of unused
985 	// space between panels. The result is quite nice, but if the value for
986 	// condensationLimit is not some less than the actual condensation of
987 	// memory intensive panels, we get the flicker problem again. Also,
988 	// before really using this algorithm, it should get a good optimization
989 	// by some caching or an update mechanism, because in its current form
990 	// it is very expensive (see the loop and the recursive call). And in
991 	// addition, the sending of NF_MEMORY_LIMIT_CHANGED would have to be
992 	// reviewed and adapted.
993 
994 	double maxPerView,viewExtension,viewExtensionValence,maxPerPanelFactor;
995 	double condensationLimit,ftotal,fself;
996 	double vx,vy,vw,vh,evx1,evy1,evx2,evy2,ecx1,ecy1,ecx2,ecy2,fe,fn,f;
997 	const emPanel * p;
998 
999 	if (!InViewedPath) return 0;
1000 	maxPerView=View.CoreConfig->MaxMegabytesPerView*1000000.0;
1001 	if (!Viewed || !Parent || View.SeekPosPanel==this) return (emUInt64)maxPerView;
1002 	maxPerPanelFactor=0.75;
1003 	viewExtension=0.5;
1004 	viewExtensionValence=0.5;
1005 	condensationLimit=2.0; // 2.0 is good for emDirEntryPanel
1006 	ftotal=0.0;
1007 	fself=0.0;
1008 	for (p=Parent->FirstChild; p; p=p->Next) {
1009 		if (!p->Viewed) continue;
1010 		vx=View.GetCurrentX();
1011 		vy=View.GetCurrentY();
1012 		vw=View.GetCurrentWidth();
1013 		vh=View.GetCurrentHeight();
1014 		evx1=vx-vw*(viewExtension*0.5);
1015 		evy1=vy-vh*(viewExtension*0.5);
1016 		evx2=evx1+vw*(1.0+viewExtension);
1017 		evy2=evy1+vh*(1.0+viewExtension);
1018 		ecx1=p->ViewedX;
1019 		ecy1=p->ViewedY;
1020 		ecx2=ecx1+p->ViewedWidth;
1021 		ecy2=ecy1+p->ViewedHeight;
1022 		if (ecx1<evx1) ecx1=evx1;
1023 		if (ecy1<evy1) ecy1=evy1;
1024 		if (ecx2>evx2) ecx2=evx2;
1025 		if (ecy2>evy2) ecy2=evy2;
1026 		fe=(ecx2-ecx1)*(ecy2-ecy1)/((evx2-evx1)*(evy2-evy1));
1027 		fn=(p->ClipX2-p->ClipX1)*(p->ClipY2-p->ClipY1)/(vw*vh);
1028 		f=fe*viewExtensionValence+fn*(1.0-viewExtensionValence);
1029 		if (f>maxPerPanelFactor) f=maxPerPanelFactor;
1030 		if (f<0.0) f=0.0;
1031 		ftotal+=f;
1032 		if (p==this) fself=f;
1033 	}
1034 	f=Parent->GetMemoryLimit()*fself/ftotal/maxPerView;
1035 	if (f>fself*condensationLimit) f=fself*condensationLimit;
1036 	if (f>maxPerPanelFactor) f=maxPerPanelFactor;
1037 	if (f<0.0) f=0.0;
1038 	return (emUInt64)(maxPerView*f);
1039 
1040 #endif
1041 }
1042 
1043 
GetTouchEventPriority(double touchX,double touchY) const1044 double emPanel::GetTouchEventPriority(double touchX, double touchY) const
1045 {
1046 	return Focusable ? 1.0 : 0.0;
1047 }
1048 
1049 
SetAutoplayHandling(AutoplayHandlingFlags flags)1050 void emPanel::SetAutoplayHandling(AutoplayHandlingFlags flags)
1051 {
1052 	AutoplayHandling=flags;
1053 }
1054 
1055 
IsContentReady(bool * pReadying) const1056 bool emPanel::IsContentReady(bool * pReadying) const
1057 {
1058 	if (pReadying) *pReadying=false;
1059 	return IsAutoExpanded();
1060 }
1061 
1062 
GetPlaybackState(bool * pPlaying,double * pPos) const1063 bool emPanel::GetPlaybackState(bool * pPlaying, double * pPos) const
1064 {
1065 	if (pPlaying) *pPlaying=false;
1066 	if (pPos) *pPos=0.0;
1067 	return false;
1068 }
1069 
1070 
SetPlaybackState(bool playing,double pos)1071 bool emPanel::SetPlaybackState(bool playing, double pos)
1072 {
1073 	return false;
1074 }
1075 
1076 
Cycle()1077 bool emPanel::Cycle()
1078 {
1079 	return false;
1080 }
1081 
1082 
Notice(NoticeFlags flags)1083 void emPanel::Notice(NoticeFlags flags)
1084 {
1085 }
1086 
1087 
Input(emInputEvent & event,const emInputState & state,double mx,double my)1088 void emPanel::Input(
1089 	emInputEvent & event, const emInputState & state, double mx, double my
1090 )
1091 {
1092 	if (Focusable && (event.IsMouseEvent() || event.IsTouchEvent())) {
1093 		Focus();
1094 		event.Eat();
1095 	}
1096 	else if (Active && event.IsKeyboardEvent()) {
1097 		switch (event.GetKey()) {
1098 		case EM_KEY_TAB:
1099 			if (state.IsNoMod()) {
1100 				View.VisitNext();
1101 				event.Eat();
1102 			}
1103 			else if (state.IsShiftMod()) {
1104 				View.VisitPrev();
1105 				event.Eat();
1106 			}
1107 			break;
1108 		case EM_KEY_CURSOR_LEFT:
1109 			if (state.IsNoMod()) {
1110 				View.VisitLeft();
1111 				event.Eat();
1112 			}
1113 			break;
1114 		case EM_KEY_CURSOR_RIGHT:
1115 			if (state.IsNoMod()) {
1116 				View.VisitRight();
1117 				event.Eat();
1118 			}
1119 			break;
1120 		case EM_KEY_CURSOR_UP:
1121 			if (state.IsNoMod()) {
1122 				View.VisitUp();
1123 				event.Eat();
1124 			}
1125 			break;
1126 		case EM_KEY_CURSOR_DOWN:
1127 			if (state.IsNoMod()) {
1128 				View.VisitDown();
1129 				event.Eat();
1130 			}
1131 			break;
1132 		case EM_KEY_PAGE_UP:
1133 			if (state.IsNoMod()) {
1134 				View.VisitIn();
1135 				event.Eat();
1136 			}
1137 			break;
1138 		case EM_KEY_PAGE_DOWN:
1139 			if (state.IsNoMod()) {
1140 				View.VisitOut();
1141 				event.Eat();
1142 			}
1143 			break;
1144 		case EM_KEY_HOME:
1145 			if (state.IsNoMod()) {
1146 				View.VisitFirst();
1147 				event.Eat();
1148 			}
1149 			else if (state.IsAltMod()) {
1150 				View.VisitFullsized(this,View.IsActivationAdherent());
1151 				event.Eat();
1152 			}
1153 			else if (state.IsShiftAltMod()) {
1154 				View.VisitFullsized(this,View.IsActivationAdherent(),true);
1155 				event.Eat();
1156 			}
1157 			break;
1158 		case EM_KEY_END:
1159 			if (state.IsNoMod()) {
1160 				View.VisitLast();
1161 				event.Eat();
1162 			}
1163 			break;
1164 		default:
1165 			break;
1166 		}
1167 	}
1168 }
1169 
1170 
GetCursor() const1171 emCursor emPanel::GetCursor() const
1172 {
1173 	if (Parent) return Parent->GetCursor();
1174 	else return emCursor::NORMAL;
1175 }
1176 
1177 
IsOpaque() const1178 bool emPanel::IsOpaque() const
1179 {
1180 	return false;
1181 }
1182 
1183 
Paint(const emPainter & painter,emColor canvasColor) const1184 void emPanel::Paint(const emPainter & painter, emColor canvasColor) const
1185 {
1186 }
1187 
1188 
AutoExpand()1189 void emPanel::AutoExpand()
1190 {
1191 }
1192 
1193 
AutoShrink()1194 void emPanel::AutoShrink()
1195 {
1196 	emPanel * p, * t;
1197 
1198 	for (p=LastChild; p;) {
1199 		t=p;
1200 		p=p->Prev;
1201 		if (t->CreatedByAE) delete t;
1202 	}
1203 }
1204 
1205 
LayoutChildren()1206 void emPanel::LayoutChildren()
1207 {
1208 }
1209 
1210 
CreateControlPanel(ParentArg parent,const emString & name)1211 emPanel * emPanel::CreateControlPanel(ParentArg parent, const emString & name)
1212 {
1213 	if (Parent) return Parent->CreateControlPanel(parent,name);
1214 	else return NULL;
1215 }
1216 
1217 
GetSoughtName() const1218 const char * emPanel::GetSoughtName() const
1219 {
1220 	if (View.SeekPosPanel==this) return View.SeekPosChildName;
1221 	else return NULL;
1222 }
1223 
1224 
IsHopeForSeeking() const1225 bool emPanel::IsHopeForSeeking() const
1226 {
1227 	return false;
1228 }
1229 
1230 
InvalidateTitle()1231 void emPanel::InvalidateTitle()
1232 {
1233 	if (InActivePath) {
1234 		View.TitleInvalid=true;
1235 		View.UpdateEngine->WakeUp();
1236 	}
1237 }
1238 
1239 
InvalidateCursor()1240 void emPanel::InvalidateCursor()
1241 {
1242 	if (InViewedPath) {
1243 		View.CursorInvalid=true;
1244 		View.UpdateEngine->WakeUp();
1245 	}
1246 }
1247 
1248 
InvalidatePainting()1249 void emPanel::InvalidatePainting()
1250 {
1251 	if (Viewed) {
1252 		if (!View.SVPChoiceByOpacityInvalid) {
1253 			View.SVPChoiceByOpacityInvalid=true;
1254 			View.UpdateEngine->WakeUp();
1255 		}
1256 		View.InvalidatePainting(ClipX1,ClipY1,ClipX2-ClipX1,ClipY2-ClipY1);
1257 	}
1258 }
1259 
1260 
InvalidatePainting(double x,double y,double w,double h)1261 void emPanel::InvalidatePainting(double x, double y, double w, double h)
1262 {
1263 	if (Viewed) {
1264 		if (!View.SVPChoiceByOpacityInvalid) {
1265 			View.SVPChoiceByOpacityInvalid=true;
1266 			View.UpdateEngine->WakeUp();
1267 		}
1268 		x=x*ViewedWidth+ViewedX;
1269 		w=w*ViewedWidth;
1270 		y=y*(ViewedWidth/View.CurrentPixelTallness)+ViewedY;
1271 		h=h*(ViewedWidth/View.CurrentPixelTallness);
1272 		if (x<ClipX1) { w+=x-ClipX1; x=ClipX1; }
1273 		if (y<ClipY1) { h+=y-ClipY1; y=ClipY1; }
1274 		if (w>ClipX2-x) w=ClipX2-x;
1275 		if (h>ClipY2-y) h=ClipY2-y;
1276 		View.InvalidatePainting(x,y,w,h);
1277 	}
1278 }
1279 
1280 
InvalidateAutoExpansion()1281 void emPanel::InvalidateAutoExpansion()
1282 {
1283 	if (!AEInvalid && AEExpanded) {
1284 		AEInvalid=1;
1285 		if (!NoticeNode.Next) View.AddToNoticeList(&NoticeNode);
1286 	}
1287 }
1288 
1289 
InvalidateControlPanel()1290 void emPanel::InvalidateControlPanel()
1291 {
1292 	if (InActivePath) Signal(View.ControlPanelSignal);
1293 }
1294 
1295 
GetTitle()1296 emString emPanel::GetTitle()
1297 {
1298 	return ((const emPanel*)this)->GetTitle();
1299 }
1300 
1301 
GetIconFileName()1302 emString emPanel::GetIconFileName()
1303 {
1304 	return ((const emPanel*)this)->GetIconFileName();
1305 }
1306 
1307 
GetSubstanceRect(double * pX,double * pY,double * pW,double * pH,double * pR)1308 void emPanel::GetSubstanceRect(
1309 	double * pX, double * pY, double * pW, double * pH, double * pR
1310 )
1311 {
1312 	return ((const emPanel*)this)->GetSubstanceRect(pX,pY,pW,pH,pR);
1313 }
1314 
1315 
GetEssenceRect(double * pX,double * pY,double * pW,double * pH)1316 void emPanel::GetEssenceRect(
1317 	double * pX, double * pY, double * pW, double * pH
1318 )
1319 {
1320 	return ((const emPanel*)this)->GetEssenceRect(pX,pY,pW,pH);
1321 }
1322 
1323 
GetTouchEventPriority(double touchX,double touchY)1324 double emPanel::GetTouchEventPriority(double touchX, double touchY)
1325 {
1326 	return ((const emPanel*)this)->GetTouchEventPriority(touchX,touchY);
1327 }
1328 
1329 
GetCursor()1330 emCursor emPanel::GetCursor()
1331 {
1332 	return ((const emPanel*)this)->GetCursor();
1333 }
1334 
1335 
IsOpaque()1336 bool emPanel::IsOpaque()
1337 {
1338 	return ((const emPanel*)this)->IsOpaque();
1339 }
1340 
1341 
Paint(const emPainter & painter,emColor canvasColor)1342 void emPanel::Paint(const emPainter & painter, emColor canvasColor)
1343 {
1344 	((const emPanel*)this)->Paint(painter,canvasColor);
1345 }
1346 
1347 
IsHopeForSeeking()1348 bool emPanel::IsHopeForSeeking()
1349 {
1350 	return ((const emPanel*)this)->IsHopeForSeeking();
1351 }
1352 
1353 
HandleNotice()1354 void emPanel::HandleNotice()
1355 {
1356 	NoticeFlags flags;
1357 
1358 	if (AEInvalid) {
1359 		AEInvalid=0;
1360 		if (AEExpanded) {
1361 			AEExpanded=0;
1362 			AEDecisionInvalid=1;
1363 			AutoShrink();
1364 		}
1365 	}
1366 
1367 	flags=PendingNoticeFlags;
1368 	if (flags) {
1369 		if (flags&(NF_SOUGHT_NAME_CHANGED|NF_VIEWING_CHANGED)) {
1370 			if (
1371 				View.SeekPosPanel==this ||
1372 				GetViewCondition((ViewConditionType)AEThresholdType)>=AEThresholdValue
1373 			) {
1374 				if (!AEExpanded) AEDecisionInvalid=1;
1375 			}
1376 			else {
1377 				if (AEExpanded) AEDecisionInvalid=1;
1378 			}
1379 		}
1380 		if (flags&(NF_LAYOUT_CHANGED|NF_CHILD_LIST_CHANGED)) {
1381 			if (FirstChild) ChildrenLayoutInvalid=1;
1382 		}
1383 		if (AEDecisionInvalid || ChildrenLayoutInvalid) {
1384 			if (!NoticeNode.Next) View.AddToNoticeList(&NoticeNode);
1385 		}
1386 		PendingNoticeFlags=0;
1387 		Notice(flags);
1388 		return; // Because Notice() is allowed to do a "delete this".
1389 	}
1390 
1391 	if (AEDecisionInvalid) {
1392 		AEDecisionInvalid=0;
1393 		if (
1394 			View.SeekPosPanel==this ||
1395 			GetViewCondition((ViewConditionType)AEThresholdType)>=AEThresholdValue
1396 		) {
1397 			if (!AEExpanded) {
1398 				AEExpanded=1;
1399 				AECalling=1;
1400 				AutoExpand();
1401 				AECalling=0;
1402 				if (PendingNoticeFlags) return;
1403 			}
1404 		}
1405 		else {
1406 			if (AEExpanded) {
1407 				AEExpanded=0;
1408 				AutoShrink();
1409 				if (PendingNoticeFlags) return;
1410 			}
1411 		}
1412 	}
1413 
1414 	if (ChildrenLayoutInvalid) {
1415 		if (FirstChild) LayoutChildren();
1416 		ChildrenLayoutInvalid=0;
1417 	}
1418 }
1419 
1420 
UpdateChildrenViewing()1421 void emPanel::UpdateChildrenViewing()
1422 {
1423 	emPanel * p;
1424 	double x1,y1,x2,y2;
1425 
1426 	if (!Viewed) {
1427 		if (InViewedPath) {
1428 			emFatalError("Illegal use of emPanel::UpdateChildrenViewing.");
1429 		}
1430 		for (p=FirstChild; p; p=p->Next) {
1431 			if (p->InViewedPath) {
1432 				p->Viewed=0;
1433 				p->InViewedPath=0;
1434 				p->AddPendingNotice(
1435 					NF_VIEWING_CHANGED |
1436 					NF_UPDATE_PRIORITY_CHANGED |
1437 					NF_MEMORY_LIMIT_CHANGED
1438 				);
1439 				if (p->FirstChild) p->UpdateChildrenViewing();
1440 			}
1441 		}
1442 	}
1443 	else {
1444 		for (p=FirstChild; p; p=p->Next) {
1445 			x1=ViewedX+p->LayoutX*ViewedWidth;
1446 			x2=p->LayoutWidth*ViewedWidth;
1447 			y1=ViewedY+p->LayoutY*(ViewedWidth/View.CurrentPixelTallness);
1448 			y2=p->LayoutHeight*(ViewedWidth/View.CurrentPixelTallness);
1449 			p->ViewedX=x1;
1450 			p->ViewedY=y1;
1451 			p->ViewedWidth=x2;
1452 			p->ViewedHeight=y2;
1453 			x2+=x1;
1454 			y2+=y1;
1455 			if (x1<ClipX1) x1=ClipX1;
1456 			if (x2>ClipX2) x2=ClipX2;
1457 			if (y1<ClipY1) y1=ClipY1;
1458 			if (y2>ClipY2) y2=ClipY2;
1459 			p->ClipX1=x1;
1460 			p->ClipX2=x2;
1461 			p->ClipY1=y1;
1462 			p->ClipY2=y2;
1463 			if (x1<x2 && y1<y2) {
1464 				p->InViewedPath=1;
1465 				p->Viewed=1;
1466 				p->AddPendingNotice(
1467 					NF_VIEWING_CHANGED |
1468 					NF_UPDATE_PRIORITY_CHANGED |
1469 					NF_MEMORY_LIMIT_CHANGED
1470 				);
1471 				if (p->FirstChild) p->UpdateChildrenViewing();
1472 			}
1473 			else if (p->InViewedPath) {
1474 				p->InViewedPath=0;
1475 				p->Viewed=0;
1476 				p->AddPendingNotice(
1477 					NF_VIEWING_CHANGED |
1478 					NF_UPDATE_PRIORITY_CHANGED |
1479 					NF_MEMORY_LIMIT_CHANGED
1480 				);
1481 				if (p->FirstChild) p->UpdateChildrenViewing();
1482 			}
1483 		}
1484 	}
1485 }
1486 
1487 
AvlInsertChild(emPanel * child)1488 void emPanel::AvlInsertChild(emPanel * child)
1489 {
1490 	EM_AVL_INSERT_VARS(emPanel)
1491 	int d;
1492 
1493 	EM_AVL_INSERT_BEGIN_SEARCH(emPanel,AvlNode,AvlTree)
1494 		d=strcmp(child->Name.Get(),element->Name.Get());
1495 		if (d<0) EM_AVL_INSERT_GO_LEFT
1496 		else if (d>0) EM_AVL_INSERT_GO_RIGHT
1497 		else {
1498 			emFatalError(
1499 				"emPanel: Panel name \"%s\" not unique within \"%s\".",
1500 				child->Name.Get(),
1501 				GetIdentity().Get()
1502 			);
1503 		}
1504 	EM_AVL_INSERT_END_SEARCH
1505 		element=child;
1506 	EM_AVL_INSERT_NOW(AvlNode)
1507 }
1508 
1509 
AvlRemoveChild(emPanel * child)1510 void emPanel::AvlRemoveChild(emPanel * child)
1511 {
1512 	EM_AVL_REMOVE_VARS(emPanel)
1513 	int d;
1514 
1515 	EM_AVL_REMOVE_BEGIN(emPanel,AvlNode,AvlTree)
1516 		d=strcmp(child->Name.Get(),element->Name.Get());
1517 		if (d<0) EM_AVL_REMOVE_GO_LEFT
1518 		else if (d>0) EM_AVL_REMOVE_GO_RIGHT
1519 		else EM_AVL_REMOVE_NOW
1520 	EM_AVL_REMOVE_END
1521 }
1522