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