1 //------------------------------------------------------------------------------
2 // emView.cpp
3 //
4 // Copyright (C) 2004-2011,2014,2016,2018 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/emPanel.h>
22 #include <emCore/emViewInputFilter.h>
23 
24 
25 //==============================================================================
26 //=================================== emView ===================================
27 //==============================================================================
28 
emView(emContext & parentContext,ViewFlags viewFlags)29 emView::emView(emContext & parentContext, ViewFlags viewFlags)
30 	: emContext(parentContext)
31 {
32 	emContext * c;
33 	emWindow * win;
34 
35 	CoreConfig=emCoreConfig::Acquire(GetRootContext());
36 	DummyViewPort=new emViewPort();
37 	DummyViewPort->CurrentView=this;
38 	DummyViewPort->HomeView=this;
39 	HomeViewPort=DummyViewPort;
40 	CurrentViewPort=DummyViewPort;
41 	Window=NULL;
42 	ScreenRef=NULL;
43 	PopupWindow=NULL;
44 	FirstVIF=NULL;
45 	LastVIF=NULL;
46 	ActiveAnimator=NULL;
47 	MagneticVA=NULL;
48 	VisitingVA=NULL;
49 	RootPanel=NULL;
50 	SupremeViewedPanel=NULL;
51 	MinSVP=NULL;
52 	MaxSVP=NULL;
53 	ActivePanel=NULL;
54 	HomeX=0.0;
55 	HomeY=0.0;
56 	HomeWidth=1.0;
57 	HomeHeight=1.0;
58 	HomePixelTallness=1.0;
59 	CurrentX=0.0;
60 	CurrentY=0.0;
61 	CurrentWidth=1.0;
62 	CurrentHeight=1.0;
63 	CurrentPixelTallness=1.0;
64 	LastMouseX=0.0;
65 	LastMouseY=0.0;
66 	Title="";
67 	Cursor=emCursor::NORMAL;
68 	BackgroundColor=0x808080FF;
69 	VFlags=0;
70 	Focused=false;
71 	ActivationAdherent=false;
72 	TitleInvalid=false;
73 	CursorInvalid=false;
74 	SVPChoiceInvalid=false;
75 	SVPChoiceByOpacityInvalid=false;
76 	GotPopupWindowCloseSignal=false;
77 	RestartInputRecursion=false;
78 	ZoomedOutBeforeSG=true;
79 	SettingGeometry=0;
80 	SVPUpdCount=0;
81 	SVPUpdSlice=0;
82 	NoticeList.Prev=&NoticeList;
83 	NoticeList.Next=&NoticeList;
84 	UpdateEngine=new UpdateEngineClass(*this);
85 	EOIEngine=NULL;
86 	SeekPosPanel=NULL;
87 	StressTest=NULL;
88 
89 	UpdateEngine->WakeUp();
90 
91 	SetViewFlags(viewFlags);
92 
93 	MagneticVA=new emMagneticViewAnimator(*this);
94 	VisitingVA=new emVisitingViewAnimator(*this);
95 
96 	new emDefaultTouchVIF(*this);
97 	new emCheatVIF(*this);
98 	new emKeyboardZoomScrollVIF(*this);
99 	new emMouseZoomScrollVIF(*this);
100 
101 	for (win=NULL, c=GetParentContext(); c!=NULL; c=c->GetParentContext()) {
102 		win=dynamic_cast<emWindow*>(c);
103 		if (win) break;
104 	}
105 	SetWindowAndScreen(win); // emWindow constructor also calls that...
106 }
107 
108 
~emView()109 emView::~emView()
110 {
111 	AbortActiveAnimator();
112 	CrossPtrList.BreakCrossPtrs();
113 	//??? Should we delete child views here like emWindow deletes its child
114 	//??? windows? If so, remember to adapt emSubViewPanel. (No panic,
115 	//??? child views are deleted at least by the context destructor)
116 	if (RootPanel) delete RootPanel;
117 	if (StressTest) delete StressTest;
118 	while (LastVIF) delete LastVIF;
119 	if (EOIEngine) delete EOIEngine;
120 	delete UpdateEngine;
121 	if (VisitingVA) { delete VisitingVA; VisitingVA=NULL; }
122 	if (MagneticVA) { delete MagneticVA; MagneticVA=NULL; }
123 	if (HomeViewPort!=DummyViewPort) {
124 		emFatalError("emView::~emView: View port must be destructed first.");
125 	}
126 	DummyViewPort->HomeView=NULL;
127 	DummyViewPort->CurrentView=NULL;
128 	delete DummyViewPort;
129 }
130 
131 
SetViewFlags(ViewFlags viewFlags)132 void emView::SetViewFlags(ViewFlags viewFlags)
133 {
134 	ViewFlags oldFlags;
135 
136 	if ((viewFlags&VF_NO_ZOOM)!=0) {
137 		viewFlags&=~(VF_POPUP_ZOOM|VF_EGO_MODE);
138 		viewFlags|=VF_NO_USER_NAVIGATION;
139 	}
140 	if (VFlags!=viewFlags) {
141 		oldFlags=VFlags;
142 		if (
143 			(viewFlags&VF_POPUP_ZOOM)!=0 &&
144 			(oldFlags&VF_POPUP_ZOOM)==0
145 		) {
146 			RawZoomOut();
147 		}
148 		VFlags=viewFlags;
149 		if (
150 			(viewFlags&VF_ROOT_SAME_TALLNESS)!=0 &&
151 			(oldFlags&VF_ROOT_SAME_TALLNESS)==0 &&
152 			RootPanel
153 		) {
154 			RootPanel->Layout(0,0,1,GetHomeTallness());
155 		}
156 		if (
157 			(viewFlags&VF_NO_ZOOM)!=0 &&
158 			(oldFlags&VF_NO_ZOOM)==0
159 		) {
160 			RawZoomOut();
161 		}
162 		if ((viewFlags&VF_EGO_MODE)!=(oldFlags&VF_EGO_MODE)) {
163 			CursorInvalid=true;
164 		}
165 		if ((viewFlags&VF_STRESS_TEST)!=0) {
166 			if (!StressTest) {
167 				StressTest=new StressTestClass(*this);
168 			}
169 		}
170 		else {
171 			if (StressTest) {
172 				delete StressTest;
173 				StressTest=NULL;
174 				InvalidatePainting();
175 			}
176 		}
177 		SVPChoiceInvalid=true;
178 		Signal(ViewFlagsSignal);
179 		UpdateEngine->WakeUp();
180 	}
181 }
182 
183 
SetBackgroundColor(emColor c)184 void emView::SetBackgroundColor(emColor c)
185 {
186 	if (BackgroundColor!=c) {
187 		BackgroundColor=c;
188 		InvalidatePainting();
189 	}
190 }
191 
192 
GetTitle() const193 emString emView::GetTitle() const
194 {
195 	return Title;
196 }
197 
198 
CreateControlPanel(emPanel & parent,const emString & name)199 emPanel * emView::CreateControlPanel(
200 	emPanel & parent, const emString & name
201 )
202 {
203 	if (!ActivePanel) return NULL;
204 	return ActivePanel->CreateControlPanel(parent,name);
205 }
206 
207 
Focus()208 void emView::Focus()
209 {
210 	if (!Focused) CurrentViewPort->RequestFocus();
211 }
212 
213 
GetScreen() const214 emScreen * emView::GetScreen() const
215 {
216 	return (emScreen*)ScreenRef.Get();
217 }
218 
219 
GetMaxPopupViewRect(double * pX,double * pY,double * pW,double * pH) const220 void emView::GetMaxPopupViewRect(
221 	double * pX, double * pY, double * pW, double * pH
222 ) const
223 {
224 	double x,y,w,h,mx,my,mw,mh,cx,cy;
225 	const emScreen * screen;
226 	bool found;
227 	int i,n;
228 
229 	x=CurrentX;
230 	y=CurrentY;
231 	w=CurrentWidth;
232 	h=CurrentHeight;
233 	screen=GetScreen();
234 	if (screen) {
235 		n=screen->GetMonitorCount();
236 		found=false;
237 		for (i=n-1; i>=0; i--) {
238 			screen->GetMonitorRect(i,&mx,&my,&mw,&mh);
239 			if (
240 				(!found && i==0) || (
241 					mx<HomeX+HomeWidth  && mx+mw>HomeX &&
242 					my<HomeY+HomeHeight && my+mh>HomeY
243 				)
244 			) {
245 				if (!found) {
246 					x=mx;
247 					y=my;
248 					w=mw;
249 					h=mh;
250 					found=true;
251 				}
252 				else {
253 					if (x>mx) { w+=x-mx; x=mx; }
254 					if (w<mx+mw-x) w=mx+mw-x;
255 					if (y>my) { h+=y-my; y=my; }
256 					if (h<my+mh-y) h=my+mh-y;
257 				}
258 			}
259 		}
260 		if (found) {
261 			// This is just for that the users sees more than
262 			// nothing even when the monitor rectangles are
263 			// completely wrong.
264 			cx=HomeX+HomeWidth*0.5;
265 			cy=HomeY+HomeHeight*0.5;
266 			if (x>cx) { w+=x-cx; x=cx; }
267 			if (w<cx-x) w=cx-x;
268 			if (y>cy) { h+=y-cy; y=cy; }
269 			if (h<cy-y) h=cy-y;
270 		}
271 	}
272 	if (pX) *pX=x;
273 	if (pY) *pY=y;
274 	if (pW) *pW=w;
275 	if (pH) *pH=h;
276 }
277 
278 
SetActivePanel(emPanel * panel,bool adherent)279 void emView::SetActivePanel(emPanel * panel, bool adherent)
280 {
281 	emPanel::NoticeFlags flags;
282 	emPanel * p;
283 
284 	if (!panel) return;
285 
286 	while (!panel->Focusable) panel=panel->Parent;
287 
288 	if (ActivePanel!=panel) {
289 		if (emIsDLogEnabled()) {
290 			emDLog("emView %p: Active=\"%s\"",(const void*)this,panel->GetIdentity().Get());
291 		}
292 		if (ActivePanel) InvalidateHighlight();
293 		flags=emPanel::NF_ACTIVE_CHANGED;
294 		if (Focused) flags|=emPanel::NF_FOCUS_CHANGED;
295 		if (ActivePanel) {
296 			p=ActivePanel;
297 			p->Active=0;
298 			do {
299 				p->InActivePath=0;
300 				p->AddPendingNotice(flags);
301 				p=p->Parent;
302 			} while (p);
303 		}
304 		p=panel;
305 		p->Active=1;
306 		do {
307 			p->InActivePath=1;
308 			p->AddPendingNotice(flags);
309 			p=p->Parent;
310 		} while (p);
311 		ActivePanel=panel;
312 		ActivationAdherent=adherent;
313 		InvalidateHighlight();
314 		TitleInvalid=true;
315 		UpdateEngine->WakeUp();
316 		Signal(ControlPanelSignal);
317 	}
318 	else if (ActivationAdherent!=adherent) {
319 		ActivationAdherent=adherent;
320 		InvalidateHighlight();
321 	}
322 }
323 
324 
SetActivePanelBestPossible()325 void emView::SetActivePanelBestPossible()
326 {
327 	emPanel * best, * p;
328 	double cx,cy,cw,ch,ex,ey,ew,eh,minW,minH,minA;
329 	bool adherent;
330 
331 	cx=CurrentX;
332 	cy=CurrentY;
333 	cw=CurrentWidth;
334 	ch=CurrentHeight;
335 	if (PopupWindow) {
336 		GetMaxPopupViewRect(&ex,&ey,&ew,&eh);
337 		if (ex<cx) { ew-=cx-ex; ex=cx; }
338 		if (ey<cy) { eh-=cy-ey; ey=cy; }
339 		if (ew>cx+cw-ex) { ew=cx+cw-ex; }
340 		if (eh>cy+ch-ey) { eh=cy+ch-ey; }
341 		if (ew>=10.0 && eh>=10.0) {
342 			cx=ex; cy=ey;
343 			cw=ew; ch=eh;
344 		}
345 	}
346 	minW=cw*0.99;
347 	minH=ch*0.99;
348 	minA=cw*ch*0.33;
349 	cx+=cw*0.5;
350 	cy+=ch*0.5;
351 	best=SupremeViewedPanel;
352 	if (!best) {
353 		return;
354 	}
355 	for (;;) {
356 		p=best->GetFocusableLastChild();
357 		if (!p) break;
358 		do {
359 			if (
360 				p->Viewed &&
361 				p->ClipX1<=cx && p->ClipX2>cx &&
362 				p->ClipY1<=cy && p->ClipY2>cy
363 			) break;
364 			p=p->GetFocusablePrev();
365 		} while(p);
366 		if (!p) break;
367 		if (
368 			p->ClipX2-p->ClipX1<minW &&
369 			p->ClipY2-p->ClipY1<minH &&
370 			(p->ClipX2-p->ClipX1)*(p->ClipY2-p->ClipY1)<minA
371 		) break;
372 		best=p;
373 	}
374 
375 	while (!best->Focusable) best=best->Parent;
376 
377 	adherent=false;
378 	if (
379 		ActivationAdherent &&
380 		ActivePanel &&
381 		ActivePanel->Viewed &&
382 		ActivePanel->ViewedWidth>=4 &&
383 		ActivePanel->ViewedHeight>=4 &&
384 		best->InActivePath
385 	) {
386 		best=ActivePanel;
387 		adherent=true;
388 	}
389 
390 	SetActivePanel(best,adherent);
391 }
392 
393 
GetPanelByIdentity(const char * identity) const394 emPanel * emView::GetPanelByIdentity(const char * identity) const
395 {
396 	emArray<emString> a;
397 	emPanel * p;
398 	int i;
399 
400 	p=RootPanel;
401 	if (p) {
402 		a=emPanel::DecodeIdentity(identity);
403 		if (a.GetCount() && a[0]==p->GetName()) {
404 			for (i=1; ; i++) {
405 				if (i>=a.GetCount()) return p;
406 				p=p->GetChild(a[i]);
407 				if (!p) break;
408 			}
409 		}
410 	}
411 	return NULL;
412 }
413 
414 
GetPanelAt(double x,double y) const415 emPanel * emView::GetPanelAt(double x, double y) const
416 {
417 	emPanel * p, * c;
418 
419 	p=SupremeViewedPanel;
420 	if (p && p->ClipX1<=x && p->ClipX2>x && p->ClipY1<=y && p->ClipY2>y) {
421 		c=p->GetLastChild();
422 		while (c) {
423 			if (c->Viewed && c->ClipX1<=x && c->ClipX2>x && c->ClipY1<=y &&
424 			    c->ClipY2>y) {
425 				p=c;
426 				c=p->GetLastChild();
427 			}
428 			else {
429 				c=c->GetPrev();
430 			}
431 		}
432 		return p;
433 	}
434 	else {
435 		return NULL;
436 	}
437 }
438 
439 
GetFocusablePanelAt(double x,double y,bool checkSubstance) const440 emPanel * emView::GetFocusablePanelAt(double x, double y, bool checkSubstance) const
441 {
442 	emPanel * p, * c;
443 
444 	p=SupremeViewedPanel;
445 	if (
446 		p && p->ClipX1<=x && p->ClipX2>x && p->ClipY1<=y && p->ClipY2>y && (
447 			!checkSubstance ||
448 			p->IsPointInSubstanceRect(p->ViewToPanelX(x),p->ViewToPanelY(y))
449 		)
450 	) {
451 		c=p->GetFocusableLastChild();
452 		while (c) {
453 			if (
454 				c->Viewed &&
455 				c->ClipX1<=x && c->ClipX2>x && c->ClipY1<=y && c->ClipY2>y && (
456 					!checkSubstance ||
457 					c->IsPointInSubstanceRect(c->ViewToPanelX(x),c->ViewToPanelY(y))
458 				)
459 			) {
460 				p=c;
461 				c=p->GetFocusableLastChild();
462 			}
463 			else {
464 				c=c->GetFocusablePrev();
465 			}
466 		}
467 		if (!p->IsFocusable()) p=p->GetFocusableParent();
468 		return p;
469 	}
470 	else {
471 		return NULL;
472 	}
473 }
474 
475 
GetVisitedPanel(double * pRelX,double * pRelY,double * pRelA) const476 emPanel * emView::GetVisitedPanel(
477 	double * pRelX, double * pRelY, double * pRelA
478 ) const
479 {
480 	emPanel * p;
481 
482 	p=ActivePanel;
483 	while (p && !p->InViewedPath) p=p->Parent;
484 	if (!p || !p->Viewed) p=SupremeViewedPanel;
485 
486 	if (p) {
487 		if (pRelX) *pRelX=(HomeX+HomeWidth*0.5-p->ViewedX)/p->ViewedWidth-0.5;
488 		if (pRelY) *pRelY=(HomeY+HomeHeight*0.5-p->ViewedY)/p->ViewedHeight-0.5;
489 		if (pRelA) *pRelA=(HomeWidth*HomeHeight)/(p->ViewedWidth*p->ViewedHeight);
490 	}
491 	else {
492 		if (pRelX) *pRelX=0.0;
493 		if (pRelY) *pRelY=0.0;
494 		if (pRelA) *pRelA=0.0;
495 	}
496 	return p;
497 }
498 
499 
Visit(emPanel * panel,double relX,double relY,double relA,bool adherent)500 void emView::Visit(
501 	emPanel * panel, double relX, double relY, double relA, bool adherent
502 )
503 {
504 	Visit(panel->GetIdentity(), relX, relY, relA, adherent, panel->GetTitle());
505 }
506 
507 
Visit(const char * identity,double relX,double relY,double relA,bool adherent,const char * subject)508 void emView::Visit(
509 	const char * identity, double relX, double relY, double relA,
510 	bool adherent, const char * subject
511 )
512 {
513 	VisitingVA->SetAnimParamsByCoreConfig(*CoreConfig);
514 	VisitingVA->SetGoal(identity, relX, relY, relA, adherent, subject);
515 	VisitingVA->Activate();
516 }
517 
518 
Visit(emPanel * panel,bool adherent)519 void emView::Visit(emPanel * panel, bool adherent)
520 {
521 	Visit(panel->GetIdentity(), adherent, panel->GetTitle());
522 }
523 
524 
Visit(const char * identity,bool adherent,const char * subject)525 void emView::Visit(const char * identity, bool adherent, const char * subject)
526 {
527 	VisitingVA->SetAnimParamsByCoreConfig(*CoreConfig);
528 	VisitingVA->SetGoal(identity, adherent, subject);
529 	VisitingVA->Activate();
530 }
531 
532 
VisitFullsized(emPanel * panel,bool adherent,bool utilizeView)533 void emView::VisitFullsized(emPanel * panel, bool adherent, bool utilizeView)
534 {
535 	VisitFullsized(panel->GetIdentity(), adherent, utilizeView, panel->GetTitle());
536 }
537 
538 
VisitFullsized(const char * identity,bool adherent,bool utilizeView,const char * subject)539 void emView::VisitFullsized(
540 	const char * identity, bool adherent, bool utilizeView,
541 	const char * subject
542 )
543 {
544 	VisitingVA->SetAnimParamsByCoreConfig(*CoreConfig);
545 	VisitingVA->SetGoalFullsized(identity, adherent, utilizeView, subject);
546 	VisitingVA->Activate();
547 }
548 
549 
RawVisit(emPanel * panel,double relX,double relY,double relA)550 void emView::RawVisit(emPanel * panel, double relX, double relY, double relA)
551 {
552 	RawVisit(panel,relX,relY,relA,false);
553 }
554 
555 
RawVisit(emPanel * panel)556 void emView::RawVisit(emPanel * panel)
557 {
558 	double relX,relY,relA;
559 
560 	if (!panel) return;
561 	CalcVisitCoords(panel,&relX,&relY,&relA);
562 	RawVisit(panel,relX,relY,relA);
563 }
564 
565 
RawVisitFullsized(emPanel * panel,bool utilizeView)566 void emView::RawVisitFullsized(emPanel * panel, bool utilizeView)
567 {
568 	RawVisit(panel,0.0,0.0,utilizeView?-1.0:0.0);
569 }
570 
571 
VisitNext()572 void emView::VisitNext()
573 {
574 	emPanel * p;
575 
576 	p=ActivePanel;
577 	if (p) {
578 		p=p->GetFocusableNext();
579 		if (!p) {
580 			p=ActivePanel->GetFocusableParent();
581 			if (!p) p=RootPanel;
582 			if (p!=ActivePanel) p=p->GetFocusableFirstChild();
583 		}
584 		Visit(p,true);
585 	}
586 }
587 
588 
VisitPrev()589 void emView::VisitPrev()
590 {
591 	emPanel * p;
592 
593 	p=ActivePanel;
594 	if (p) {
595 		p=p->GetFocusablePrev();
596 		if (!p) {
597 			p=ActivePanel->GetFocusableParent();
598 			if (!p) p=RootPanel;
599 			if (p!=ActivePanel) p=p->GetFocusableLastChild();
600 		}
601 		Visit(p,true);
602 	}
603 }
604 
605 
VisitFirst()606 void emView::VisitFirst()
607 {
608 	emPanel * p;
609 
610 	if (ActivePanel) {
611 		p=ActivePanel->GetFocusableParent();
612 		if (p) p=p->GetFocusableFirstChild();
613 		if (!p) p=ActivePanel;
614 		Visit(p,true);
615 	}
616 }
617 
618 
VisitLast()619 void emView::VisitLast()
620 {
621 	emPanel * p;
622 
623 	if (ActivePanel) {
624 		p=ActivePanel->GetFocusableParent();
625 		if (p) p=p->GetFocusableLastChild();
626 		if (!p) p=ActivePanel;
627 		Visit(p,true);
628 	}
629 }
630 
631 
VisitLeft()632 void emView::VisitLeft()
633 {
634 	VisitNeighbour(2);
635 }
636 
637 
VisitRight()638 void emView::VisitRight()
639 {
640 	VisitNeighbour(0);
641 }
642 
643 
VisitUp()644 void emView::VisitUp()
645 {
646 	VisitNeighbour(3);
647 }
648 
649 
VisitDown()650 void emView::VisitDown()
651 {
652 	VisitNeighbour(1);
653 }
654 
655 
VisitNeighbour(int direction)656 void emView::VisitNeighbour(int direction)
657 {
658 	emPanel * p, * n, * current, * parent, * best;
659 	double cx1,cy1,cx2,cy2,nx1,ny1,nx2,ny2,dx,dy,d,e,fx,fy,f,bestVal,val,defdx;
660 
661 	direction&=3;
662 	current=ActivePanel;
663 	if (!current) return;
664 	parent=current->GetFocusableParent();
665 	if (!parent) parent=RootPanel;
666 	if (parent!=current) {
667 		cx1=0; cy1=0; cx2=1.0; cy2=current->GetHeight();
668 		for (p=current; p!=parent; p=p->GetParent()) {
669 			f=p->GetLayoutWidth();
670 			fx=p->GetLayoutX();
671 			fy=p->GetLayoutY();
672 			cx1=cx1*f+fx;
673 			cy1=cy1*f+fy;
674 			cx2=cx2*f+fx;
675 			cy2=cy2*f+fy;
676 		}
677 		best=NULL;
678 		bestVal=0.0;
679 		defdx=-1.0;
680 		for (n=parent->GetFocusableFirstChild(); n; n=n->GetFocusableNext()) {
681 			if (n==current) { defdx=-defdx; continue; }
682 			nx1=0; ny1=0; nx2=1.0; ny2=n->GetHeight();
683 			for (p=n; p!=parent; p=p->GetParent()) {
684 				f=p->GetLayoutWidth();
685 				fx=p->GetLayoutX();
686 				fy=p->GetLayoutY();
687 				nx1=nx1*f+fx;
688 				ny1=ny1*f+fy;
689 				nx2=nx2*f+fx;
690 				ny2=ny2*f+fy;
691 			}
692 			dx=0.0;
693 			dy=0.0;
694 			fx=nx1-cx1;
695 			fy=ny1-cy1;
696 			f=sqrt(fx*fx+fy*fy);
697 			if (f>1E-30) { dx+=fx/f; dy+=fy/f; }
698 			fx=nx2-cx2;
699 			fy=ny1-cy1;
700 			f=sqrt(fx*fx+fy*fy);
701 			if (f>1E-30) { dx+=fx/f; dy+=fy/f; }
702 			fx=nx1-cx1;
703 			fy=ny2-cy2;
704 			f=sqrt(fx*fx+fy*fy);
705 			if (f>1E-30) { dx+=fx/f; dy+=fy/f; }
706 			fx=nx2-cx2;
707 			fy=ny2-cy2;
708 			f=sqrt(fx*fx+fy*fy);
709 			if (f>1E-30) { dx+=fx/f; dy+=fy/f; }
710 			f=sqrt(dx*dx+dy*dy);
711 			if (f>1E-30) { dx/=f; dy/=f; }
712 			else { dx=defdx; dy=0.0; }
713 			fx=(nx1+nx2-cx1-cx2)*0.5;
714 			fy=(ny1+ny2-cy1-cy2)*0.5;
715 			d=sqrt(fx*fx+fy*fy);
716 			if (nx2<cx1) fx=nx2-cx1;
717 			else if (nx1>cx2) fx=nx1-cx2;
718 			else fx=0.0;
719 			if (ny2<cy1) fy=ny2-cy1;
720 			else if (ny1>cy2) fy=ny1-cy2;
721 			else fy=0.0;
722 			e=sqrt(fx*fx+fy*fy);
723 			if ((direction&1)!=0) {
724 				f=dx;
725 				dx=dy;
726 				dy=-f;
727 			}
728 			if ((direction&2)!=0) {
729 				dx=-dx;
730 				dy=-dy;
731 			}
732 			if (dx<=1E-12) continue;
733 			val=(e*10+d)*(1+2*dy*dy);
734 			if (fabs(dy)>0.707) val*=1000*dy*dy*dy*dy;
735 			if (!best || val<bestVal) {
736 				best=n;
737 				bestVal=val;
738 			}
739 		}
740 		if (best) current=best;
741 	}
742 	Visit(current,true);
743 }
744 
745 
VisitIn()746 void emView::VisitIn()
747 {
748 	emPanel * p;
749 
750 	if (!ActivePanel) return;
751 	p=ActivePanel->GetFocusableFirstChild();
752 	if (p) Visit(p,true);
753 	else VisitFullsized(ActivePanel,true);
754 }
755 
756 
VisitOut()757 void emView::VisitOut()
758 {
759 	double relA,relA2;
760 	emPanel * p;
761 
762 	if (!ActivePanel) return;
763 	p=ActivePanel->GetFocusableParent();
764 	if (p) Visit(p,true);
765 	else if (RootPanel) {
766 		relA=HomeWidth*RootPanel->GetHeight()/HomePixelTallness/HomeHeight;
767 		relA2=HomeHeight/RootPanel->GetHeight()*HomePixelTallness/HomeWidth;
768 		if (relA<relA2) relA=relA2;
769 		Visit(RootPanel,0.0,0.0,relA,true);
770 	}
771 }
772 
773 
Scroll(double deltaX,double deltaY)774 void emView::Scroll(double deltaX, double deltaY)
775 {
776 	double rx,ry,ra;
777 	emPanel * p;
778 
779 	AbortActiveAnimator();
780 	if (deltaX!=0.0 || deltaY!=0.0) {
781 		p=GetVisitedPanel(&rx,&ry,&ra);
782 		if (p) {
783 			rx+=deltaX/p->ViewedWidth;
784 			ry+=deltaY/p->ViewedHeight;
785 			RawVisit(p,rx,ry,ra,true);
786 		}
787 	}
788 	SetActivePanelBestPossible();
789 }
790 
791 
Zoom(double fixX,double fixY,double factor)792 void emView::Zoom(double fixX, double fixY, double factor)
793 {
794 	double rx,ry,ra,reFac;
795 	emPanel * p;
796 
797 	AbortActiveAnimator();
798 	if (factor!=1.0 && factor>0.0) {
799 		p=GetVisitedPanel(&rx,&ry,&ra);
800 		if (p) {
801 			reFac=1.0/factor;
802 			rx+=(fixX-(HomeX+HomeWidth*0.5))*(1.0-reFac)/p->ViewedWidth;
803 			ry+=(fixY-(HomeY+HomeHeight*0.5))*(1.0-reFac)/p->ViewedHeight;
804 			ra*=reFac*reFac;
805 			RawVisit(p,rx,ry,ra,true);
806 		}
807 	}
808 	SetActivePanelBestPossible();
809 }
810 
811 
RawScrollAndZoom(double fixX,double fixY,double deltaX,double deltaY,double deltaZ,emPanel * panel,double * pDeltaXDone,double * pDeltaYDone,double * pDeltaZDone)812 void emView::RawScrollAndZoom(
813 	double fixX, double fixY,
814 	double deltaX, double deltaY, double deltaZ,
815 	emPanel * panel, double * pDeltaXDone,
816 	double * pDeltaYDone, double * pDeltaZDone
817 )
818 {
819 	double zflpp,hx,hy,hw,hh,hmx,hmy,pvx,pvy,pvw,pvh;
820 	double rx,ry,ra,rx2,ry2,ra2,reFac;
821 
822 	zflpp=GetZoomFactorLogarithmPerPixel();
823 
824 	hx=GetHomeX();
825 	hy=GetHomeY();
826 	hw=GetHomeWidth();
827 	hh=GetHomeHeight();
828 	hmx=hx+hw*0.5;
829 	hmy=hy+hh*0.5;
830 
831 	if (panel && panel->IsViewed()) {
832 		pvx=panel->GetViewedX();
833 		pvy=panel->GetViewedY();
834 		pvw=panel->GetViewedWidth();
835 		pvh=panel->GetViewedHeight();
836 		rx = (hmx-pvx) / pvw - 0.5;
837 		ry = (hmy-pvy) / pvh - 0.5;
838 		ra = (hw*hh) / (pvw*pvh);
839 	}
840 	else {
841 		panel = GetVisitedPanel(&rx,&ry,&ra);
842 		if (!panel) {
843 			if (pDeltaXDone) *pDeltaXDone=0.0;
844 			if (pDeltaYDone) *pDeltaYDone=0.0;
845 			if (pDeltaZDone) *pDeltaZDone=0.0;
846 			return;
847 		}
848 		pvw=panel->GetViewedWidth();
849 		pvh=panel->GetViewedHeight();
850 	}
851 
852 	reFac=exp(-deltaZ*zflpp);
853 	rx2 = rx + ((fixX-hmx)*(1.0-reFac) + deltaX)/pvw;
854 	ry2 = ry + ((fixY-hmy)*(1.0-reFac) + deltaY)/pvh;
855 	ra2 = ra * reFac*reFac;
856 
857 	RawVisit(panel,rx2,ry2,ra2);
858 
859 	if (panel->IsViewed()) {
860 		pvx=panel->GetViewedX();
861 		pvy=panel->GetViewedY();
862 		pvw=panel->GetViewedWidth();
863 		pvh=panel->GetViewedHeight();
864 
865 		rx2 = (hmx-pvx) / pvw - 0.5;
866 		ry2 = (hmy-pvy) / pvh - 0.5;
867 		ra2 = (hw*hh) / (pvw*pvh);
868 		reFac = sqrt(ra2/ra);
869 
870 		if (pDeltaXDone) {
871 			*pDeltaXDone = (rx2-rx)*pvw*reFac - (fixX-hmx)*(1.0-reFac);
872 		}
873 		if (pDeltaYDone) {
874 			*pDeltaYDone = (ry2-ry)*pvh*reFac - (fixY-hmy)*(1.0-reFac);
875 		}
876 		if (pDeltaZDone) {
877 			*pDeltaZDone = -log(reFac)/zflpp;
878 		}
879 	}
880 	else {
881 		if (pDeltaXDone) *pDeltaXDone = deltaX;
882 		if (pDeltaYDone) *pDeltaYDone = deltaY;
883 		if (pDeltaZDone) *pDeltaZDone = deltaZ;
884 	}
885 }
886 
887 
GetZoomFactorLogarithmPerPixel() const888 double emView::GetZoomFactorLogarithmPerPixel() const
889 {
890 	double w,h,r;
891 
892 	if ((GetViewFlags()&emView::VF_POPUP_ZOOM)!=0) {
893 		GetMaxPopupViewRect(NULL,NULL,&w,&h);
894 	}
895 	else {
896 		w=GetCurrentWidth();
897 		h=GetCurrentHeight();
898 	}
899 	r=(w+h)*0.25;
900 	if (r<1.0) r=1.0;
901 	return 1.33/r;
902 }
903 
904 
ZoomOut()905 void emView::ZoomOut()
906 {
907 	AbortActiveAnimator();
908 	RawZoomOut();
909 	SetActivePanelBestPossible();
910 }
911 
912 
RawZoomOut()913 void emView::RawZoomOut()
914 {
915 	RawZoomOut(false);
916 }
917 
918 
IsZoomedOut() const919 bool emView::IsZoomedOut() const
920 {
921 	double x,y,w,h;
922 	const emPanel * p;
923 
924 	if (SettingGeometry) return ZoomedOutBeforeSG;
925 	if (VFlags&VF_POPUP_ZOOM) return PopupWindow==NULL;
926 	p=SupremeViewedPanel;
927 	if (!p) return true;
928 	x=(HomeX-p->ViewedX)/p->ViewedWidth;
929 	y=(HomeY-p->ViewedY)*HomePixelTallness/p->ViewedWidth;
930 	w=HomeWidth/p->ViewedWidth;
931 	h=HomeHeight*HomePixelTallness/p->ViewedWidth;
932 	while (p->Parent) {
933 		x=p->LayoutX+x*p->LayoutWidth;
934 		y=p->LayoutY+y*p->LayoutWidth;
935 		w*=p->LayoutWidth;
936 		h*=p->LayoutWidth;
937 		p=p->Parent;
938 	}
939 	return
940 		x<=0.001 &&
941 		y<=0.001 &&
942 		x+w>=1.0-0.001 &&
943 		y+h>=p->GetHeight()-0.001
944 	;
945 }
946 
947 
SignalEOIDelayed()948 void emView::SignalEOIDelayed()
949 {
950 	if (!EOIEngine) EOIEngine=new EOIEngineClass(*this);
951 }
952 
953 
GetTouchEventPriority(double touchX,double touchY,bool afterVIFs) const954 double emView::GetTouchEventPriority(
955 	double touchX, double touchY, bool afterVIFs
956 ) const
957 {
958 	emPanel * p;
959 	double pri,t;
960 
961 	if (!afterVIFs && FirstVIF) {
962 		return FirstVIF->GetTouchEventPriority(touchX,touchY);
963 	}
964 	pri=-1E30;
965 	p=RootPanel;
966 	if (p) {
967 		for (;;) {
968 			if (
969 				p->InViewedPath && (
970 					!p->Viewed || (
971 						p->ClipX1<=touchX &&
972 						p->ClipY1<=touchY &&
973 						p->ClipX2>touchX &&
974 						p->ClipY2>touchY
975 					)
976 				)
977 			) {
978 				t=p->GetTouchEventPriority(touchX,touchY);
979 				if (pri<t) pri=t;
980 			}
981 			if (p->FirstChild) p=p->FirstChild;
982 			else if (p->Next) p=p->Next;
983 			else {
984 				do {
985 					p=p->Parent;
986 				} while (p && !p->Next);
987 				if (!p) break;
988 				p=p->Next;
989 			}
990 		}
991 	}
992 	return pri;
993 }
994 
995 
ActivateMagneticViewAnimator()996 void emView::ActivateMagneticViewAnimator()
997 {
998 	if (MagneticVA) MagneticVA->Activate();
999 }
1000 
1001 
AbortActiveAnimator()1002 void emView::AbortActiveAnimator()
1003 {
1004 	if (ActiveAnimator) ActiveAnimator->Deactivate();
1005 }
1006 
1007 
Input(emInputEvent & event,const emInputState & state)1008 void emView::Input(emInputEvent & event, const emInputState & state)
1009 {
1010 	emPanel * p;
1011 
1012 	if (ActiveAnimator) ActiveAnimator->Input(event,state);
1013 
1014 	if (
1015 		fabs(state.GetMouseX()-LastMouseX)>0.1 ||
1016 		fabs(state.GetMouseY()-LastMouseY)>0.1
1017 	) {
1018 		LastMouseX=state.GetMouseX();
1019 		LastMouseY=state.GetMouseY();
1020 		CursorInvalid=true;
1021 		UpdateEngine->WakeUp();
1022 	}
1023 
1024 	p=RootPanel;
1025 	if (p) {
1026 		for (;;) {
1027 			p->PendingInput=true;
1028 			if (p->FirstChild) p=p->FirstChild;
1029 			else if (p->Next) p=p->Next;
1030 			else {
1031 				do {
1032 					p=p->Parent;
1033 				} while (p && !p->Next);
1034 				if (!p) break;
1035 				p=p->Next;
1036 			}
1037 		}
1038 	}
1039 
1040 	do {
1041 		RestartInputRecursion=false;
1042 		RecurseInput(event,state);
1043 		if (RestartInputRecursion) {
1044 			emDLog("emView %p: Restarting input recursion.",(const void*)this);
1045 		}
1046 	} while (RestartInputRecursion);
1047 }
1048 
1049 
GetCursor() const1050 emCursor emView::GetCursor() const
1051 {
1052 	return Cursor;
1053 }
1054 
1055 
Paint(const emPainter & painter,emColor canvasColor) const1056 void emView::Paint(const emPainter & painter, emColor canvasColor) const
1057 {
1058 	emColor ncc;
1059 	emPainter pnt;
1060 	const emPanel * p;
1061 	double rx1,ry1,rx2,ry2,ox,oy,cx1,cy1,cx2,cy2;
1062 	bool wasNotInUserSpace;
1063 
1064 	if (painter.GetScaleX()!=1.0 || painter.GetScaleY()!=1.0) {
1065 		emFatalError("emView::Paint: Scaling not possible.");
1066 	}
1067 
1068 	wasNotInUserSpace=painter.EnterUserSpace();
1069 
1070 	if (!SupremeViewedPanel) {
1071 		painter.Clear(BackgroundColor,canvasColor);
1072 	}
1073 	else {
1074 		ox=painter.GetOriginX();
1075 		oy=painter.GetOriginY();
1076 		rx1=painter.GetClipX1()-ox;
1077 		ry1=painter.GetClipY1()-oy;
1078 		rx2=painter.GetClipX2()-ox;
1079 		ry2=painter.GetClipY2()-oy;
1080 		p=SupremeViewedPanel;
1081 		if (
1082 			!p->IsOpaque() ||
1083 			p->ViewedX                >rx1 ||
1084 			p->ViewedX+p->ViewedWidth <rx2 ||
1085 			p->ViewedY                >ry1 ||
1086 			p->ViewedY+p->ViewedHeight<ry2
1087 		) {
1088 			ncc=p->CanvasColor;
1089 			if (!ncc.IsOpaque()) ncc=BackgroundColor;
1090 			painter.Clear(ncc,canvasColor);
1091 			canvasColor=ncc;
1092 		}
1093 		cx1=p->ClipX1; if (cx1<rx1) cx1=rx1;
1094 		cx2=p->ClipX2; if (cx2>rx2) cx2=rx2;
1095 		cy1=p->ClipY1; if (cy1<ry1) cy1=ry1;
1096 		cy2=p->ClipY2; if (cy2>ry2) cy2=ry2;
1097 		if (cx1<cx2 && cy1<cy2) {
1098 			pnt=painter;
1099 			pnt.SetClipping(cx1+ox,cy1+oy,cx2+ox,cy2+oy);
1100 			pnt.SetTransformation(
1101 				p->ViewedX+ox,
1102 				p->ViewedY+oy,
1103 				p->ViewedWidth,
1104 				p->ViewedWidth/CurrentPixelTallness
1105 			);
1106 			p->Paint(pnt,canvasColor);
1107 			painter.LeaveUserSpace();
1108 			p=p->FirstChild;
1109 			if (p) {
1110 				for (;;) {
1111 					if (p->Viewed) {
1112 						cx1=p->ClipX1; if (cx1<rx1) cx1=rx1;
1113 						cx2=p->ClipX2; if (cx2>rx2) cx2=rx2;
1114 						if (cx1<cx2) {
1115 							cy1=p->ClipY1; if (cy1<ry1) cy1=ry1;
1116 							cy2=p->ClipY2; if (cy2>ry2) cy2=ry2;
1117 							if (cy1<cy2) {
1118 								pnt.SetClipping(cx1+ox,cy1+oy,cx2+ox,cy2+oy);
1119 								pnt.SetTransformation(
1120 									p->ViewedX+ox,
1121 									p->ViewedY+oy,
1122 									p->ViewedWidth,
1123 									p->ViewedWidth/CurrentPixelTallness
1124 								);
1125 								painter.EnterUserSpace();
1126 								p->Paint(pnt,p->CanvasColor);
1127 								painter.LeaveUserSpace();
1128 								if (p->FirstChild) {
1129 									p=p->FirstChild;
1130 									continue;
1131 								}
1132 							}
1133 						}
1134 					}
1135 					if (p->Next) p=p->Next;
1136 					else {
1137 						do {
1138 							p=p->Parent;
1139 						} while (p!=SupremeViewedPanel && !p->Next);
1140 						if (p==SupremeViewedPanel) break;
1141 						p=p->Next;
1142 					}
1143 				}
1144 			}
1145 			painter.EnterUserSpace();
1146 		}
1147 		PaintHighlight(painter);
1148 	}
1149 
1150 	if (ActiveAnimator) ActiveAnimator->Paint(painter);
1151 	if (StressTest) StressTest->PaintInfo(painter);
1152 
1153 	if (wasNotInUserSpace) painter.LeaveUserSpace();
1154 }
1155 
1156 
InvalidateTitle()1157 void emView::InvalidateTitle()
1158 {
1159 	Signal(TitleSignal);
1160 }
1161 
1162 
DoCustomCheat(const char * func)1163 void emView::DoCustomCheat(const char * func)
1164 {
1165 	emContext * c;
1166 	emView * v;
1167 
1168 	for (c=GetParentContext(); c; c=c->GetParentContext()) {
1169 		v=dynamic_cast<emView*>(c);
1170 		if (v) {
1171 			v->DoCustomCheat(func);
1172 			break;
1173 		}
1174 	}
1175 }
1176 
1177 
GetTitle()1178 emString emView::GetTitle()
1179 {
1180 	return ((const emView*)this)->GetTitle();
1181 }
1182 
1183 
GetTouchEventPriority(double touchX,double touchY,bool afterVIFs)1184 double emView::GetTouchEventPriority(
1185 	double touchX, double touchY, bool afterVIFs
1186 )
1187 {
1188 	return ((const emView*)this)->GetTouchEventPriority(touchX,touchY,afterVIFs);
1189 }
1190 
1191 
GetCursor()1192 emCursor emView::GetCursor()
1193 {
1194 	return ((const emView*)this)->GetCursor();
1195 }
1196 
1197 
Paint(const emPainter & painter,emColor canvasColor)1198 void emView::Paint(const emPainter & painter, emColor canvasColor)
1199 {
1200 	((const emView*)this)->Paint(painter,canvasColor);
1201 }
1202 
1203 
SetWindowAndScreen(emWindow * window)1204 void emView::SetWindowAndScreen(emWindow * window)
1205 {
1206 	// Called by the constructors of emView and emWindow.
1207 	Window=window;
1208 	if (window) ScreenRef=&window->GetScreen();
1209 	else ScreenRef=emScreen::LookupInherited(*this);
1210 }
1211 
1212 
SetFocused(bool focused)1213 void emView::SetFocused(bool focused)
1214 {
1215 	emPanel * p;
1216 	emPanel::NoticeFlags flags;
1217 
1218 	if (Focused==focused) return;
1219 	if (Focused) InvalidateHighlight();
1220 	Focused=focused;
1221 	if (Focused) InvalidateHighlight();
1222 	Signal(FocusSignal);
1223 	p=RootPanel;
1224 	if (p) {
1225 		for (;;) {
1226 			flags=
1227 				emPanel::NF_VIEW_FOCUS_CHANGED |
1228 				emPanel::NF_UPDATE_PRIORITY_CHANGED
1229 			;
1230 			if (p->InActivePath) flags|=emPanel::NF_FOCUS_CHANGED;
1231 			p->AddPendingNotice(flags);
1232 			if (p->FirstChild) p=p->FirstChild;
1233 			else if (p->Next) p=p->Next;
1234 			else {
1235 				do {
1236 					p=p->Parent;
1237 				} while (p && !p->Next);
1238 				if (!p) break;
1239 				p=p->Next;
1240 			}
1241 		}
1242 	}
1243 }
1244 
1245 
SetGeometry(double x,double y,double width,double height,double pixelTallness)1246 void emView::SetGeometry(
1247 	double x, double y, double width, double height, double pixelTallness
1248 )
1249 {
1250 	double rx,ry,ra;
1251 	emPanel * p;
1252 
1253 	if (width<0.0001) width=0.0001;
1254 	if (height<0.0001) height=0.0001;
1255 	if (pixelTallness<0.0001) pixelTallness=0.0001;
1256 	if (
1257 		CurrentX==x && CurrentY==y &&
1258 		CurrentWidth==width && CurrentHeight==height &&
1259 		CurrentPixelTallness==pixelTallness
1260 	) return;
1261 
1262 	ZoomedOutBeforeSG=IsZoomedOut();
1263 	SettingGeometry++;
1264 	p=GetVisitedPanel(&rx,&ry,&ra);
1265 	CurrentViewPort->HomeView->HomeX=x;
1266 	CurrentViewPort->HomeView->HomeY=y;
1267 	CurrentViewPort->HomeView->HomeWidth=width;
1268 	CurrentViewPort->HomeView->HomeHeight=height;
1269 	CurrentViewPort->HomeView->HomePixelTallness=pixelTallness;
1270 	CurrentX=x;
1271 	CurrentY=y;
1272 	CurrentWidth=width;
1273 	CurrentHeight=height;
1274 	CurrentPixelTallness=pixelTallness;
1275 	CurrentViewPort->HomeView->Signal(GeometrySignal);
1276 	Signal(GeometrySignal);
1277 	if ((VFlags&VF_ROOT_SAME_TALLNESS)!=0 && RootPanel) {
1278 		RootPanel->Layout(0,0,1,GetHomeTallness());
1279 	}
1280 	if (ZoomedOutBeforeSG) {
1281 		RawZoomOut(true);
1282 	}
1283 	else if (p) {
1284 		RawVisit(p,rx,ry,ra,true);
1285 	}
1286 	SettingGeometry--;
1287 }
1288 
1289 
AddToNoticeList(PanelRingNode * node)1290 void emView::AddToNoticeList(PanelRingNode * node)
1291 {
1292 	node->Next=&NoticeList;
1293 	node->Prev=NoticeList.Prev;
1294 	node->Prev->Next=node;
1295 	NoticeList.Prev=node;
1296 	UpdateEngine->WakeUp();
1297 }
1298 
1299 
Update()1300 void emView::Update()
1301 {
1302 	PanelRingNode * n;
1303 	emPanel * p;
1304 	emString tmp;
1305 	emCursor cur;
1306 
1307 	if (PopupWindow && IsSignaled(PopupWindow->GetCloseSignal())) {
1308 		GotPopupWindowCloseSignal=true;
1309 		ZoomOut();
1310 	}
1311 
1312 	for (;;) {
1313 		n=NoticeList.Next;
1314 		if (n!=&NoticeList) {
1315 			do {
1316 				NoticeList.Next=n->Next;
1317 				NoticeList.Next->Prev=&NoticeList;
1318 				n->Prev=NULL;
1319 				n->Next=NULL;
1320 				p=(emPanel*)(((char*)n)-offsetof(emPanel,NoticeNode));
1321 				p->HandleNotice();
1322 				n=NoticeList.Next;
1323 			} while (n!=&NoticeList);
1324 		}
1325 		else if (SVPChoiceByOpacityInvalid) {
1326 			SVPChoiceByOpacityInvalid=false;
1327 			if (!SVPChoiceInvalid && MinSVP!=MaxSVP) {
1328 				for (p=MinSVP; p!=MaxSVP; p=p->Parent) {
1329 					if (
1330 						p->CanvasColor.IsOpaque() ||
1331 						((const emPanel*)p)->IsOpaque()
1332 					) break;
1333 				}
1334 				if (SupremeViewedPanel!=p) {
1335 					emDLog("emView %p: SVP choice invalid by opacity.",(const void*)this);
1336 					SVPChoiceInvalid=true;
1337 				}
1338 			}
1339 		}
1340 		else if (SVPChoiceInvalid) {
1341 			SVPChoiceInvalid=false;
1342 			p=GetVisitedPanel();
1343 			if (p) {
1344 				RawVisitAbs(
1345 					p,
1346 					p->ViewedX,
1347 					p->ViewedY,
1348 					p->ViewedWidth,
1349 					false
1350 				);
1351 			}
1352 		}
1353 		else if (TitleInvalid) {
1354 			TitleInvalid=false;
1355 			if (ActivePanel) tmp=ActivePanel->GetTitle();
1356 			else tmp="";
1357 			if (Title!=tmp) {
1358 				Title=tmp;
1359 				InvalidateTitle();
1360 			}
1361 		}
1362 		else if (CursorInvalid) {
1363 			CursorInvalid=false;
1364 			p=GetPanelAt(LastMouseX,LastMouseY);
1365 			if (p) cur=p->GetCursor();
1366 			else cur=emCursor::NORMAL;
1367 			if ((VFlags&VF_EGO_MODE)!=0) {
1368 				if (cur==emCursor::NORMAL) cur=emCursor::CROSSHAIR;
1369 			}
1370 			if (Cursor!=cur) {
1371 				Cursor=cur;
1372 				InvalidateCursor();
1373 			}
1374 		}
1375 		else {
1376 			break;
1377 		}
1378 	}
1379 }
1380 
1381 
CalcVisitCoords(const emPanel * panel,double * pRelX,double * pRelY,double * pRelA) const1382 void emView::CalcVisitCoords(
1383 	const emPanel * panel, double * pRelX, double * pRelY, double * pRelA
1384 ) const
1385 {
1386 	static const double MIN_REL_DISTANCE=0.03;
1387 	static const double MIN_REL_CIRCUMFERENCE=0.05;
1388 	const emPanel * p, * cp;
1389 	double ph,dx,dy,sx,sy,sw,sh,minvw,maxvw,vx,vy,vw,vh;
1390 	double ctx,cty,ctw,cth,csx,csy,csw,csh;
1391 
1392 	ph=panel->GetHeight();
1393 
1394 	if ((VFlags&VF_POPUP_ZOOM)!=0) {
1395 		GetMaxPopupViewRect(&sx,&sy,&sw,&sh);
1396 	}
1397 	else {
1398 		sx=CurrentX;
1399 		sy=CurrentY;
1400 		sw=CurrentWidth;
1401 		sh=CurrentHeight;
1402 	}
1403 
1404 	dx=emMin(
1405 		CurrentWidth*MIN_REL_DISTANCE,
1406 		CurrentHeight*MIN_REL_DISTANCE*CurrentPixelTallness
1407 	);
1408 	dy=dx/CurrentPixelTallness;
1409 	sx+=dx;
1410 	sy+=dy;
1411 	sw-=2*dx;
1412 	sh-=2*dy;
1413 
1414 	maxvw=emMin(sw,sh/ph*CurrentPixelTallness);
1415 	minvw=emMin(
1416 		(CurrentWidth+CurrentHeight)*MIN_REL_CIRCUMFERENCE/
1417 			(1.0+ph/CurrentPixelTallness),
1418 		maxvw*0.999
1419 	);
1420 
1421 	if (
1422 		panel->Viewed &&
1423 		panel->ViewedWidth>=minvw &&
1424 		panel->ViewedWidth<=maxvw &&
1425 		panel->ViewedX>=sx &&
1426 		panel->ViewedX+panel->ViewedWidth<=sx+sw &&
1427 		panel->ViewedY>=sy &&
1428 		panel->ViewedY+panel->ViewedHeight<=sy+sh
1429 	) {
1430 		if (pRelX) *pRelX=(HomeX+HomeWidth*0.5-panel->ViewedX)/panel->ViewedWidth-0.5;
1431 		if (pRelY) *pRelY=(HomeY+HomeHeight*0.5-panel->ViewedY)/panel->ViewedHeight-0.5;
1432 		if (pRelA) *pRelA=(HomeWidth*HomeHeight)/(panel->ViewedWidth*panel->ViewedHeight);
1433 		return;
1434 	}
1435 
1436 	cp=panel;
1437 	ctx=0.0;
1438 	cty=0.0;
1439 	ctw=1.0;
1440 	cth=ph;
1441 	while (cp!=SupremeViewedPanel && (cp->Viewed || !cp->InViewedPath)) {
1442 		ctx=cp->LayoutX+ctx*cp->LayoutWidth;
1443 		cty=cp->LayoutY+cty*cp->LayoutWidth;
1444 		ctw*=cp->LayoutWidth;
1445 		cth*=cp->LayoutWidth;
1446 		cp=cp->Parent;
1447 	}
1448 
1449 	p=SupremeViewedPanel;
1450 	csx=(sx-p->ViewedX)/p->ViewedWidth;
1451 	csy=(sy-p->ViewedY)*CurrentPixelTallness/p->ViewedWidth;
1452 	csw=sw/p->ViewedWidth;
1453 	csh=sh*CurrentPixelTallness/p->ViewedWidth;
1454 	while (p!=cp) {
1455 		csx=p->LayoutX+csx*p->LayoutWidth;
1456 		csy=p->LayoutY+csy*p->LayoutWidth;
1457 		csw*=p->LayoutWidth;
1458 		csh*=p->LayoutWidth;
1459 		p=p->Parent;
1460 	}
1461 
1462 	if (ctw*sw>=maxvw*csw) vw=maxvw;
1463 	else if (ctw*sw<=minvw*csw) vw=minvw;
1464 	else vw=ctw/csw*sw;
1465 	vh=vw*ph/CurrentPixelTallness;
1466 
1467 	if (ctw>csw) {
1468 		vx=-(csx+csw*0.5-ctx)*vw;
1469 		if (vx<=(-sw*0.5)*ctw) vx=sx;
1470 		else if (vx>=(sw*0.5-vw)*ctw) vx=sx+sw-vw;
1471 		else vx=vx/ctw+sx+sw*0.5;
1472 	}
1473 	else {
1474 		vx=(ctx+ctw*0.5-csx)*sw;
1475 		if (vx<=vw*0.5*csw) vx=sx;
1476 		else if (vx>=(sw-vw*0.5)*csw) vx=sx+sw-vw;
1477 		else vx=vx/csw+sx-vw*0.5;
1478 	}
1479 
1480 	if (cth>csh) {
1481 		vy=-(csy+csh*0.5-cty)*vh;
1482 		if (vy<=(-sh*0.5)*cth) vy=sy;
1483 		else if (vy>=(sh*0.5-vh)*cth) vy=sy+sh-vh;
1484 		else vy=vy/cth+sy+sh*0.5;
1485 	}
1486 	else {
1487 		vy=(cty+cth*0.5-csy)*sh;
1488 		if (vy<=vh*0.5*csh) vy=sy;
1489 		else if (vy>=(sh-vh*0.5)*csh) vy=sy+sh-vh;
1490 		else vy=vy/csh+sy-vh*0.5;
1491 	}
1492 
1493 	if (pRelX) *pRelX=(HomeX+HomeWidth*0.5-vx)/vw-0.5;
1494 	if (pRelY) *pRelY=(HomeY+HomeHeight*0.5-vy)/vh-0.5;
1495 	if (pRelA) *pRelA=(HomeWidth*HomeHeight)/(vw*vh);
1496 }
1497 
1498 
CalcVisitFullsizedCoords(const emPanel * panel,double * pRelX,double * pRelY,double * pRelA,bool utilizeView) const1499 void emView::CalcVisitFullsizedCoords(
1500 	const emPanel * panel, double * pRelX, double * pRelY, double * pRelA,
1501 	bool utilizeView
1502 ) const
1503 {
1504 	double fx,fy,fw,fh,ph,vx,vy,vw,vh,ex,ey,ew,eh;
1505 
1506 	if ((VFlags&VF_POPUP_ZOOM)!=0) {
1507 		GetMaxPopupViewRect(&fx,&fy,&fw,&fh);
1508 	}
1509 	else {
1510 		fx=HomeX;
1511 		fy=HomeY;
1512 		fw=HomeWidth;
1513 		fh=HomeHeight;
1514 	}
1515 
1516 	panel->GetEssenceRect(&ex,&ey,&ew,&eh);
1517 	ph=panel->GetHeight();
1518 	if ((ew*fh*HomePixelTallness>=eh*fw) != utilizeView) {
1519 		vw=fw/ew;
1520 		vh=vw*ph/HomePixelTallness;
1521 	}
1522 	else {
1523 		vh=fh/eh*ph;
1524 		vw=vh/ph*HomePixelTallness;
1525 	}
1526 	vx=fx+fw*0.5-(ex+ew*0.5)*vw;
1527 	vy=fy+fh*0.5-(ey+eh*0.5)/ph*vh;
1528 
1529 	*pRelX=(HomeX+HomeWidth*0.5-vx)/vw-0.5;
1530 	*pRelY=(HomeY+HomeHeight*0.5-vy)/vh-0.5;
1531 	*pRelA=(HomeWidth*HomeHeight)/(vw*vh);
1532 }
1533 
1534 
RawVisit(emPanel * panel,double relX,double relY,double relA,bool forceViewingUpdate)1535 void emView::RawVisit(
1536 	emPanel * panel, double relX, double relY, double relA,
1537 	bool forceViewingUpdate
1538 )
1539 {
1540 	double vx,vy,vw,vh;
1541 
1542 	if (!panel) return;
1543 	if (relA<=0.0) CalcVisitFullsizedCoords(panel,&relX,&relY,&relA,relA<-0.9);
1544 	vw=sqrt(HomeWidth*HomeHeight*HomePixelTallness/(relA*panel->GetHeight()));
1545 	vh=vw*panel->GetHeight()/HomePixelTallness;
1546 	vx=HomeX+HomeWidth*0.5-(relX+0.5)*vw;
1547 	vy=HomeY+HomeHeight*0.5-(relY+0.5)*vh;
1548 	RawVisitAbs(panel,vx,vy,vw,forceViewingUpdate);
1549 }
1550 
1551 
RawVisitAbs(emPanel * panel,double vx,double vy,double vw,bool forceViewingUpdate)1552 void emView::RawVisitAbs(
1553 	emPanel * panel, double vx, double vy, double vw,
1554 	bool forceViewingUpdate
1555 )
1556 {
1557 	emPanel * vp, * p, * sp;
1558 	double w,h,vh,x1,y1,x2,y2,sx,sy,sw,sh;
1559 	bool wasFocused;
1560 
1561 	if (!panel) return;
1562 
1563 	SVPChoiceByOpacityInvalid=false;
1564 	SVPChoiceInvalid=false;
1565 
1566 	if (VFlags&VF_NO_ZOOM) {
1567 		vp=RootPanel;
1568 		h=vp->GetHeight();
1569 		if (CurrentHeight*CurrentPixelTallness>=CurrentWidth*h) {
1570 			vw=CurrentWidth;
1571 			vx=CurrentX;
1572 			vy=CurrentY+(CurrentHeight-vw*h/CurrentPixelTallness)*0.5;
1573 		}
1574 		else {
1575 			vw=CurrentHeight*CurrentPixelTallness/h;
1576 			vx=CurrentX+(CurrentWidth-vw)*0.5;
1577 			vy=CurrentY;
1578 		}
1579 	}
1580 	else {
1581 		vp=panel;
1582 	}
1583 
1584 	for (;;) {
1585 		p=vp->Parent;
1586 		if (!p) break;
1587 		w=vw/vp->LayoutWidth;
1588 		if (w>MaxSVPSize || w*p->GetHeight()>MaxSVPSize) break;
1589 		vx-=vp->LayoutX*w;
1590 		vy-=vp->LayoutY*w/CurrentPixelTallness;
1591 		vw=w;
1592 		vp=p;
1593 	}
1594 
1595 	vh=vp->GetHeight()*vw/HomePixelTallness;
1596 
1597 	if (vp==RootPanel) {
1598 		if (vw<HomeWidth && vh<HomeHeight) {
1599 			vx=(HomeX+HomeWidth*0.5-vx)/vw;
1600 			vy=(HomeY+HomeHeight*0.5-vy)/vh;
1601 			if (vh*HomeWidth<vw*HomeHeight) {
1602 				vw=HomeWidth;
1603 				vh=vw*vp->GetHeight()/HomePixelTallness;
1604 			}
1605 			else {
1606 				vh=HomeHeight;
1607 				vw=vh/vp->GetHeight()*HomePixelTallness;
1608 			}
1609 			vx=HomeX+HomeWidth*0.5-vx*vw;
1610 			vy=HomeY+HomeHeight*0.5-vy*vh;
1611 		}
1612 
1613 		if ((VFlags&VF_EGO_MODE)!=0) {
1614 			x1=x2=HomeX+HomeWidth*0.5;
1615 			y1=y2=HomeY+HomeHeight*0.5;
1616 		}
1617 		else {
1618 			if (vh*HomeWidth<vw*HomeHeight) {
1619 				x1=HomeX;
1620 				x2=HomeX+HomeWidth;
1621 				y1=HomeY+HomeHeight*0.5-HomeWidth*vp->GetHeight()/HomePixelTallness*0.5;
1622 				y2=HomeY+HomeHeight*0.5+HomeWidth*vp->GetHeight()/HomePixelTallness*0.5;
1623 			}
1624 			else {
1625 				x1=HomeX+HomeWidth*0.5-HomeHeight/vp->GetHeight()*HomePixelTallness*0.5;
1626 				x2=HomeX+HomeWidth*0.5+HomeHeight/vp->GetHeight()*HomePixelTallness*0.5;
1627 				y1=HomeY;
1628 				y2=HomeY+HomeHeight;
1629 			}
1630 		}
1631 		if (vx>x1) vx=x1;
1632 		if (vx<x2-vw) vx=x2-vw;
1633 		if (vy>y1) vy=y1;
1634 		if (vy<y2-vh) vy=y2-vh;
1635 	}
1636 
1637 	if ((VFlags&VF_POPUP_ZOOM)!=0) {
1638 		if (
1639 			vp!=RootPanel ||
1640 			vx<HomeX-0.1 || vx+vw>HomeX+HomeWidth+0.1 ||
1641 			vy<HomeY-0.1 || vy+vh>HomeY+HomeHeight+0.1
1642 		) {
1643 			if (!PopupWindow) {
1644 				wasFocused=Focused;
1645 				PopupWindow=new emWindow(
1646 					*this,
1647 					0,
1648 					emWindow::WF_POPUP,
1649 					"emViewPopup"
1650 				);
1651 				GotPopupWindowCloseSignal=false;
1652 				UpdateEngine->AddWakeUpSignal(PopupWindow->GetCloseSignal());
1653 				PopupWindow->SetBackgroundColor(GetBackgroundColor());
1654 				SwapViewPorts(true);
1655 				if (wasFocused && !Focused) CurrentViewPort->RequestFocus();
1656 			}
1657 			GetMaxPopupViewRect(&sx,&sy,&sw,&sh);
1658 			if (vp==RootPanel) {
1659 				x1=floor(vx);
1660 				y1=floor(vy);
1661 				x2=ceil(vx+vw);
1662 				y2=ceil(vy+vh);
1663 				if (x1<sx) x1=sx;
1664 				if (y1<sy) y1=sy;
1665 				if (x2>sx+sw) x2=sx+sw;
1666 				if (y2>sy+sh) y2=sy+sh;
1667 				if (x2<x1+1.0) x2=x1+1.0;
1668 				if (y2<y1+1.0) y2=y1+1.0;
1669 			}
1670 			else {
1671 				x1=sx;
1672 				y1=sy;
1673 				x2=sx+sw;
1674 				y2=sy+sh;
1675 			}
1676 			if (
1677 				fabs(x1-CurrentX)>0.01 || fabs(x2-CurrentX-CurrentWidth)>0.01 ||
1678 				fabs(y1-CurrentY)>0.01 || fabs(y2-CurrentY-CurrentHeight)>0.01
1679 			) {
1680 				SwapViewPorts(false);
1681 				PopupWindow->SetViewPosSize(x1,y1,x2-x1,y2-y1);
1682 				SwapViewPorts(false);
1683 				forceViewingUpdate=true;
1684 			}
1685 		}
1686 		else if (PopupWindow) {
1687 			wasFocused=Focused;
1688 			SwapViewPorts(true);
1689 			delete PopupWindow;
1690 			PopupWindow=NULL;
1691 			Signal(GeometrySignal);
1692 			forceViewingUpdate=true;
1693 			if (wasFocused && !Focused && !GotPopupWindowCloseSignal) {
1694 				CurrentViewPort->RequestFocus();
1695 			}
1696 		}
1697 	}
1698 
1699 	FindBestSVP(&vp,&vx,&vy,&vw);
1700 
1701 	p=vp;
1702 	w=vw;
1703 	for (;;) {
1704 		sp=p;
1705 		p=p->Parent;
1706 		if (!p) break;
1707 		w=w/sp->LayoutWidth;
1708 		if (w>MaxSVPSize || w*p->GetHeight()>MaxSVPSize) break;
1709 	}
1710 	MaxSVP=sp;
1711 
1712 	sp=vp;
1713 	sx=vx;
1714 	sy=vy;
1715 	sw=vw;
1716 	for (;;) {
1717 		p=sp->LastChild;
1718 		if (!p) break;
1719 		x1=(CurrentX+1E-4-sx)/sw;
1720 		x2=(CurrentX+CurrentWidth-1E-4-sx)/sw;
1721 		y1=(CurrentY+1E-4-sy)*(CurrentPixelTallness/sw);
1722 		y2=(CurrentY+CurrentHeight-1E-4-sy)*(CurrentPixelTallness/sw);
1723 		do {
1724 			if (
1725 				p->LayoutX<x2 && p->LayoutX+p->LayoutWidth>x1 &&
1726 				p->LayoutY<y2 && p->LayoutY+p->LayoutHeight>y1
1727 			) break;
1728 			p=p->Prev;
1729 		} while (p);
1730 		if (
1731 			!p || p->LayoutX>x1 || p->LayoutX+p->LayoutWidth<x2 ||
1732 			p->LayoutY>y1 || p->LayoutY+p->LayoutHeight<y2
1733 		) break;
1734 		sp=p;
1735 		sx+=p->LayoutX*sw;
1736 		sy+=p->LayoutY*sw/CurrentPixelTallness;
1737 		sw*=p->LayoutWidth;
1738 	}
1739 	MinSVP=sp;
1740 
1741 	if (
1742 		forceViewingUpdate ||
1743 		SupremeViewedPanel!=vp ||
1744 		fabs(vp->ViewedX-vx)>=0.001 ||
1745 		fabs(vp->ViewedY-vy)>=0.001 ||
1746 		fabs(vp->ViewedWidth-vw)>=0.001
1747 	) {
1748 		if (SVPUpdSlice!=GetScheduler().GetTimeSliceCounter()) {
1749 			SVPUpdSlice=GetScheduler().GetTimeSliceCounter();
1750 			SVPUpdCount=0;
1751 		}
1752 		SVPUpdCount++;
1753 		if (SVPUpdCount>1000) {
1754 			// Get out of a very unlikely situation where we have an
1755 			// end-less loop of choosing a different SVP and
1756 			// creating/destroying panels through the notice
1757 			// mechanism. SVP choice depends on floating-point
1758 			// calculations and that can result different in a
1759 			// repetition with the same input numbers...
1760 			if (SVPUpdCount%1000==1 || SVPUpdCount>10000) {
1761 				vx+=emGetDblRandom(-0.01,0.01);
1762 				vy+=emGetDblRandom(-0.01,0.01);
1763 				vw*=emGetDblRandom(0.9999999999,1.0000000001);
1764 			}
1765 		}
1766 		if (emIsDLogEnabled()) {
1767 			emDLog("emView %p: SVP=\"%s\"",(const void*)this,vp->GetIdentity().Get());
1768 		}
1769 		p=SupremeViewedPanel;
1770 		if (p) {
1771 			p->InViewedPath=0;
1772 			p->Viewed=0;
1773 			p->AddPendingNotice(
1774 				emPanel::NF_VIEWING_CHANGED |
1775 				emPanel::NF_UPDATE_PRIORITY_CHANGED |
1776 				emPanel::NF_MEMORY_LIMIT_CHANGED
1777 			);
1778 			p->UpdateChildrenViewing();
1779 			for (;;) {
1780 				p=p->Parent;
1781 				if (!p) break;
1782 				p->InViewedPath=0;
1783 				p->AddPendingNotice(
1784 					emPanel::NF_VIEWING_CHANGED |
1785 					emPanel::NF_UPDATE_PRIORITY_CHANGED |
1786 					emPanel::NF_MEMORY_LIMIT_CHANGED
1787 				);
1788 			}
1789 		}
1790 		SupremeViewedPanel=vp;
1791 		vp->InViewedPath=1;
1792 		vp->Viewed=1;
1793 		vp->ViewedX=vx;
1794 		vp->ViewedY=vy;
1795 		vp->ViewedWidth=vw;
1796 		vp->ViewedHeight=vw*vp->GetHeight()/CurrentPixelTallness;
1797 		vp->ClipX1=vp->ViewedX;
1798 		if (vp->ClipX1<CurrentX) vp->ClipX1=CurrentX;
1799 		vp->ClipY1=vp->ViewedY;
1800 		if (vp->ClipY1<CurrentY) vp->ClipY1=CurrentY;
1801 		vp->ClipX2=vp->ViewedX+vp->ViewedWidth;
1802 		if (vp->ClipX2>CurrentX+CurrentWidth) vp->ClipX2=CurrentX+CurrentWidth;
1803 		vp->ClipY2=vp->ViewedY+vp->ViewedHeight;
1804 		if (vp->ClipY2>CurrentY+CurrentHeight) vp->ClipY2=CurrentY+CurrentHeight;
1805 		vp->AddPendingNotice(
1806 			emPanel::NF_VIEWING_CHANGED |
1807 			emPanel::NF_UPDATE_PRIORITY_CHANGED |
1808 			emPanel::NF_MEMORY_LIMIT_CHANGED
1809 		);
1810 		vp->UpdateChildrenViewing();
1811 		for (p=vp->Parent; p; p=p->Parent) {
1812 			p->InViewedPath=1;
1813 			p->AddPendingNotice(
1814 				emPanel::NF_VIEWING_CHANGED |
1815 				emPanel::NF_UPDATE_PRIORITY_CHANGED |
1816 				emPanel::NF_MEMORY_LIMIT_CHANGED
1817 			);
1818 		}
1819 		RestartInputRecursion=true;
1820 		CursorInvalid=true;
1821 		UpdateEngine->WakeUp();
1822 		InvalidatePainting();
1823 	}
1824 }
1825 
1826 
RawZoomOut(bool forceViewingUpdate)1827 void emView::RawZoomOut(bool forceViewingUpdate)
1828 {
1829 	double relA,relA2;
1830 
1831 	if (RootPanel) {
1832 		relA=HomeWidth*RootPanel->GetHeight()/HomePixelTallness/HomeHeight;
1833 		relA2=HomeHeight/RootPanel->GetHeight()*HomePixelTallness/HomeWidth;
1834 		if (relA<relA2) relA=relA2;
1835 		RawVisit(RootPanel,0.0,0.0,relA,forceViewingUpdate);
1836 	}
1837 
1838 	if (IsPoppedUp()) {
1839 		emFatalError("emView::RawZoomOut: Inconsistent algorithms.");
1840 	}
1841 }
1842 
1843 
FindBestSVP(emPanel ** pPanel,double * pVx,double * pVy,double * pVw) const1844 void emView::FindBestSVP(
1845 	emPanel * * pPanel, double * pVx, double * pVy, double * pVw
1846 ) const
1847 {
1848 	emPanel * vp, * p, * op;
1849 	double vx,vy,vw,x,y,w,minS;
1850 	bool b;
1851 	int i;
1852 
1853 	vp=*pPanel;
1854 	vx=*pVx;
1855 	vy=*pVy;
1856 	vw=*pVw;
1857 	for (i=0; i<2; i++) {
1858 		minS = (i==0 ? MaxSVPSize : MaxSVPSearchSize);
1859 		op=vp;
1860 		for (;;) {
1861 			p=vp->Parent;
1862 			if (!p) break;
1863 			w=vw/vp->LayoutWidth;
1864 			if (w>minS || w*p->GetHeight()>minS) break;
1865 			vx-=vp->LayoutX*w;
1866 			vy-=vp->LayoutY*w/CurrentPixelTallness;
1867 			vw=w;
1868 			vp=p;
1869 		}
1870 		if (op==vp && i>0) break;
1871 		b=
1872 			vx<=CurrentX+1E-4 &&
1873 			vx+vw>=CurrentX+CurrentWidth-1E-4 &&
1874 			vy<=CurrentY+1E-4 &&
1875 			vy+vp->GetHeight()*vw/CurrentPixelTallness>=CurrentY+CurrentHeight-1E-4
1876 		;
1877 		p=vp; x=vx; y=vy; w=vw;
1878 		b=FindBestSVPInTree(&p,&x,&y,&w,b);
1879 		if (*pPanel!=p) {
1880 			*pPanel=p;
1881 			*pVx=x;
1882 			*pVy=y;
1883 			*pVw=w;
1884 		}
1885 		if (b) break;
1886 	}
1887 }
1888 
1889 
FindBestSVPInTree(emPanel ** pPanel,double * pVx,double * pVy,double * pVw,bool covering) const1890 bool emView::FindBestSVPInTree(
1891 	emPanel * * pPanel, double * pVx, double * pVy, double * pVw, bool covering
1892 ) const
1893 {
1894 	double f,vx,vy,vw,vwc,vs,vd,x1,y1,x2,y2,x,y,cx,cy,cw,cs,d;
1895 	emPanel * p, * cp;
1896 	bool cc,vc,tooLarge,overlapped;
1897 
1898 	p=*pPanel;
1899 	vx=*pVx;
1900 	vy=*pVy;
1901 	vw=*pVw;
1902 
1903 	vs=vw;
1904 	f=p->GetHeight();
1905 	if (f>1.0) vs*=f;
1906 
1907 	tooLarge=(vs>MaxSVPSize);
1908 
1909 	if (!covering && !tooLarge) return false;
1910 	vc=(
1911 		covering &&
1912 		(p->CanvasColor.IsOpaque() || ((const emPanel*)p)->IsOpaque())
1913 	);
1914 
1915 	p=p->LastChild;
1916 	if (!p) return vc;
1917 
1918 	x1=(CurrentX+1E-4-vx)/vw;
1919 	x2=(CurrentX+CurrentWidth-1E-4-vx)/vw;
1920 	vwc=vw/CurrentPixelTallness;
1921 	y1=(CurrentY+1E-4-vy)/vwc;
1922 	y2=(CurrentY+CurrentHeight-1E-4-vy)/vwc;
1923 	vd=1E+30;
1924 	overlapped=false;
1925 
1926 	do {
1927 		if (
1928 			p->LayoutX<x2 && p->LayoutX+p->LayoutWidth>x1 &&
1929 			p->LayoutY<y2 && p->LayoutY+p->LayoutHeight>y1
1930 		) {
1931 			cc=true;
1932 			if (
1933 				!covering ||
1934 				p->LayoutX>x1 || p->LayoutX+p->LayoutWidth<x2 ||
1935 				p->LayoutY>y1 || p->LayoutY+p->LayoutHeight<y2
1936 			) {
1937 				if (!tooLarge && vc) break;
1938 				cc=false;
1939 			}
1940 			cp=p;
1941 			cx=vx+p->LayoutX*vw;
1942 			cy=vy+p->LayoutY*vwc;
1943 			cw=p->LayoutWidth*vw;
1944 			cc=FindBestSVPInTree(&cp,&cx,&cy,&cw,cc);
1945 			if (!cc && !tooLarge && vc) break;
1946 			cs=cw;
1947 			f=cp->GetHeight();
1948 			if (f>1.0) cs*=f;
1949 			if (cc && cs<=MaxSVPSize) {
1950 				if (tooLarge || !overlapped) {
1951 					*pPanel=cp;
1952 					*pVx=cx;
1953 					*pVy=cy;
1954 					*pVw=cw;
1955 				}
1956 				return true;
1957 			}
1958 			overlapped=true;
1959 			if (tooLarge) {
1960 				x=(x2+x1)*0.5;
1961 				y=(y2+y1)*0.5;
1962 				if (x<p->LayoutX) x-=p->LayoutX;
1963 				else if (x>p->LayoutX+p->LayoutWidth) x-=p->LayoutX+p->LayoutWidth;
1964 				else x=0.0;
1965 				if (y<p->LayoutY) y-=p->LayoutY;
1966 				else if (y>p->LayoutY+p->LayoutHeight) y-=p->LayoutY+p->LayoutHeight;
1967 				else y=0.0;
1968 				d=x*x+y*y;
1969 				if (
1970 					(cs<=MaxSVPSize && d-0.1<=vd) ||
1971 					(vs>MaxSVPSize && cs<=vs)
1972 				) {
1973 					*pPanel=cp;
1974 					*pVx=cx;
1975 					*pVy=cy;
1976 					*pVw=cw;
1977 					vd=d;
1978 					vs=cs;
1979 					vc=cc;
1980 				}
1981 			}
1982 		}
1983 		p=p->Prev;
1984 	} while (p);
1985 
1986 	return vc;
1987 }
1988 
1989 
SwapViewPorts(bool swapFocus)1990 void emView::SwapViewPorts(bool swapFocus)
1991 {
1992 	emView * w;
1993 	emViewPort * vp;
1994 	bool fcs;
1995 
1996 	w=PopupWindow;
1997 	vp=w->CurrentViewPort;
1998 	w->CurrentViewPort=CurrentViewPort;
1999 	CurrentViewPort=vp;
2000 	CurrentViewPort->CurrentView=this;
2001 	w->CurrentViewPort->CurrentView=w;
2002 	CurrentX=CurrentViewPort->HomeView->HomeX;
2003 	CurrentY=CurrentViewPort->HomeView->HomeY;
2004 	CurrentWidth=CurrentViewPort->HomeView->HomeWidth;
2005 	CurrentHeight=CurrentViewPort->HomeView->HomeHeight;
2006 	CurrentPixelTallness=CurrentViewPort->HomeView->HomePixelTallness;
2007 	w->CurrentX=w->CurrentViewPort->HomeView->HomeX;
2008 	w->CurrentY=w->CurrentViewPort->HomeView->HomeY;
2009 	w->CurrentWidth=w->CurrentViewPort->HomeView->HomeWidth;
2010 	w->CurrentHeight=w->CurrentViewPort->HomeView->HomeHeight;
2011 	w->CurrentPixelTallness=w->CurrentViewPort->HomeView->HomePixelTallness;
2012 	if (swapFocus) {
2013 		fcs=Focused;
2014 		SetFocused(w->Focused);
2015 		w->SetFocused(fcs);
2016 	}
2017 }
2018 
2019 
RecurseInput(emInputEvent & event,const emInputState & state)2020 void emView::RecurseInput(emInputEvent & event, const emInputState & state)
2021 {
2022 	double mx,my,tx,ty;
2023 	emInputEvent * ebase, * e;
2024 	emPanel * panel, * child;
2025 
2026 	panel=SupremeViewedPanel;
2027 	if (!panel) return;
2028 
2029 	NoEvent.Eat();
2030 
2031 	ebase=&event;
2032 
2033 	mx=state.GetMouseX();
2034 	my=state.GetMouseY();
2035 	if (ebase->IsMouseEvent()) {
2036 		if (mx<panel->ClipX1 || mx>=panel->ClipX2 ||
2037 		    my<panel->ClipY1 || my>=panel->ClipY2) ebase=&NoEvent;
2038 	}
2039 	mx=(mx-panel->ViewedX)/panel->ViewedWidth;
2040 	my=(my-panel->ViewedY)/panel->ViewedWidth*CurrentPixelTallness;
2041 
2042 	if (state.GetTouchCount()>0) {
2043 		tx=state.GetTouchX(0);
2044 		ty=state.GetTouchY(0);
2045 	}
2046 	else {
2047 		tx=state.GetMouseX();
2048 		ty=state.GetMouseY();
2049 	}
2050 	if (ebase->IsTouchEvent()) {
2051 		if (tx<panel->ClipX1 || tx>=panel->ClipX2 ||
2052 		    ty<panel->ClipY1 || ty>=panel->ClipY2) ebase=&NoEvent;
2053 	}
2054 	tx=(tx-panel->ViewedX)/panel->ViewedWidth;
2055 	ty=(ty-panel->ViewedY)/panel->ViewedWidth*CurrentPixelTallness;
2056 
2057 	for (;;) {
2058 		if (panel->PendingInput) {
2059 			e=ebase;
2060 			if (!e->IsEmpty()) {
2061 				if (e->IsMouseEvent()) {
2062 					if (!panel->IsPointInSubstanceRect(mx,my)) {
2063 						e=&NoEvent;
2064 					}
2065 				}
2066 				else if (e->IsTouchEvent()) {
2067 					if (!panel->IsPointInSubstanceRect(tx,ty)) {
2068 						e=&NoEvent;
2069 					}
2070 				}
2071 				else if (e->IsKeyboardEvent()) {
2072 					if (!panel->InActivePath) {
2073 						e=&NoEvent;
2074 					}
2075 				}
2076 			}
2077 			for (child=panel->LastChild; child; child=child->Prev) {
2078 				RecurseInput(child,*e,state);
2079 				if (RestartInputRecursion) return;
2080 			}
2081 			panel->PendingInput=0;
2082 			panel->Input(*e,state,mx,my);
2083 			if (RestartInputRecursion) return;
2084 		}
2085 		if (!panel->Parent) break;
2086 		mx=mx*panel->LayoutWidth+panel->LayoutX;
2087 		my=my*panel->LayoutWidth+panel->LayoutY;
2088 		tx=tx*panel->LayoutWidth+panel->LayoutX;
2089 		ty=ty*panel->LayoutWidth+panel->LayoutY;
2090 		panel=panel->Parent;
2091 	}
2092 }
2093 
2094 
RecurseInput(emPanel * panel,emInputEvent & event,const emInputState & state)2095 void emView::RecurseInput(
2096 	emPanel * panel, emInputEvent & event, const emInputState & state
2097 )
2098 {
2099 	double mx,my,tx,ty;
2100 	emInputEvent * e;
2101 	emPanel * child;
2102 
2103 	if (!panel->PendingInput) return;
2104 
2105 	if (panel->Viewed) {
2106 		mx=(state.GetMouseX()-panel->ViewedX)/panel->ViewedWidth;
2107 		my=(state.GetMouseY()-panel->ViewedY)/panel->ViewedWidth*CurrentPixelTallness;
2108 		if (state.GetTouchCount()>0) {
2109 			tx=(state.GetTouchX(0)-panel->ViewedX)/panel->ViewedWidth;
2110 			ty=(state.GetTouchY(0)-panel->ViewedY)/panel->ViewedWidth*CurrentPixelTallness;
2111 		}
2112 		else {
2113 			tx=mx;
2114 			ty=my;
2115 		}
2116 	}
2117 	else {
2118 		mx=-1.0;
2119 		my=-1.0;
2120 		tx=-1.0;
2121 		ty=-1.0;
2122 	}
2123 
2124 	e=&event;
2125 	if (!e->IsEmpty()) {
2126 		if (e->IsMouseEvent()) {
2127 			if (!panel->IsPointInSubstanceRect(mx,my)) {
2128 				e=&NoEvent;
2129 			}
2130 		}
2131 		else if (e->IsTouchEvent()) {
2132 			if (!panel->IsPointInSubstanceRect(tx,ty)) {
2133 				e=&NoEvent;
2134 			}
2135 		}
2136 		else if (e->IsKeyboardEvent()) {
2137 			if (!panel->InActivePath) {
2138 				e=&NoEvent;
2139 			}
2140 		}
2141 	}
2142 
2143 	for (child=panel->LastChild; child; child=child->Prev) {
2144 		RecurseInput(child,*e,state);
2145 		if (RestartInputRecursion) return;
2146 	}
2147 
2148 	panel->PendingInput=0;
2149 	panel->Input(*e,state,mx,my);
2150 }
2151 
2152 
InvalidateHighlight()2153 void emView::InvalidateHighlight()
2154 {
2155 	if (
2156 		!ActivePanel || !ActivePanel->Viewed || (
2157 			(VFlags&VF_NO_ACTIVE_HIGHLIGHT)!=0 &&
2158 			((VFlags&VF_NO_FOCUS_HIGHLIGHT)!=0 || !Focused)
2159 		)
2160 	) return;
2161 	InvalidatePainting(); //??? too much
2162 }
2163 
2164 
PaintHighlight(const emPainter & painter) const2165 void emView::PaintHighlight(const emPainter & painter) const
2166 {
2167 	emColor shadowColor,arrowColor;
2168 	emPainter pnt;
2169 	double cx1,cy1,cx2,cy2,vx,vy,vw,vh,sx,sy,sw,sh,sr,x1,y1,x2,y2;
2170 	double goalX,goalY,lc,lh,lv,l,len,pc,pc2,pos,delta;
2171 	int i,j,n,m;
2172 
2173 	//??? These things could be configurable.
2174 	static const emColor highlightColor=emColor(255,255,255);
2175 	static const emColor adherentHighlightColor=emColor(255,255,187);
2176 	static const double arrowSize=11.0;
2177 	static const double arrowDistance=55.0;
2178 	static const double distanceFromPanel=2.0;
2179 
2180 	if (
2181 		!ActivePanel || !ActivePanel->Viewed || (
2182 			(VFlags&VF_NO_ACTIVE_HIGHLIGHT)!=0 &&
2183 			((VFlags&VF_NO_FOCUS_HIGHLIGHT)!=0 || !Focused)
2184 		)
2185 	) return;
2186 
2187 	pnt=painter;
2188 	pnt.SetScaling(1.0,1.0/CurrentPixelTallness);
2189 
2190 	vx=ActivePanel->GetViewedX();
2191 	vy=ActivePanel->GetViewedY()*CurrentPixelTallness;
2192 	vw=ActivePanel->GetViewedWidth();
2193 	vh=ActivePanel->GetViewedHeight()*CurrentPixelTallness;
2194 
2195 	((const emPanel*)ActivePanel)->GetSubstanceRect(&sx,&sy,&sw,&sh,&sr);
2196 	sx=vx+sx*vw;
2197 	sy=vy+sy*vw;
2198 	sw*=vw;
2199 	sh*=vw;
2200 	sr*=vw;
2201 	if (sw<0.0) sw=0.0; else if (sw>vw) sw=vw;
2202 	if (sh<0.0) sh=0.0; else if (sh>vh) sh=vh;
2203 	if (sx<vx) sx=vx; else if (sx>vx+vw-sw) sx=vx+vw-sw;
2204 	if (sy<vy) sy=vy; else if (sy>vy+vh-sh) sy=vy+vh-sh;
2205 	if (sr<0.0) sr=0.0;
2206 	if (sr>sw*0.5) sr=sw*0.5;
2207 	if (sr>sh*0.5) sr=sh*0.5;
2208 	sx-=distanceFromPanel;
2209 	sy-=distanceFromPanel;
2210 	sw+=2*distanceFromPanel;
2211 	sh+=2*distanceFromPanel;
2212 	sr+=distanceFromPanel;
2213 
2214 	cx1=pnt.GetUserClipX1()-arrowSize*2.0;
2215 	cy1=pnt.GetUserClipY1()-arrowSize*2.0;
2216 	cx2=pnt.GetUserClipX2()+arrowSize*2.0;
2217 	cy2=pnt.GetUserClipY2()+arrowSize*2.0;
2218 	if (sx>=cx2 || sx+sw<=cx1 || sy>=cy2 || sy+sh<=cy1) return;
2219 
2220 	pnt.LeaveUserSpace();
2221 
2222 	shadowColor=emColor(0,0,0,192);
2223 	if (ActivationAdherent) arrowColor=adherentHighlightColor;
2224 	else arrowColor=highlightColor;
2225 	if (!Focused || (VFlags&VF_NO_FOCUS_HIGHLIGHT)!=0) {
2226 		shadowColor.SetAlpha((emByte)(shadowColor.GetAlpha()/3));
2227 		arrowColor.SetAlpha((emByte)(arrowColor.GetAlpha()/3));
2228 	}
2229 
2230 	x1=sx+sr;
2231 	x2=sx+sw-sr;
2232 	y1=sy+sr;
2233 	y2=sy+sh-sr;
2234 
2235 	goalX=(x1+x2)*0.5;
2236 	goalY=(y1+y2)*0.5;
2237 
2238 	lh=x2-x1;
2239 	lv=y2-y1;
2240 	lc=0.5*M_PI*sr;
2241 
2242 	pc=lc*0.5;
2243 	if (lc>1E-10) {
2244 		pc2=(lv*0.5+lc+lh*0.5)*0.5-lv*0.5;
2245 		if (pc2<0.0) pc2=0.0;
2246 		if (pc2>lc) pc2=lc;
2247 		l=lc/(lc+emMin(lh,lv));
2248 		pc=pc*(1.0-l)+pc2*l;
2249 	}
2250 
2251 	for (i=0; i<4; i++) {
2252 		switch (i) {
2253 		case 0:
2254 			pos=pc;
2255 			len=(lc-pc)*2.0+lh;
2256 			break;
2257 		case 1:
2258 			pos=lc+lh+lc-pc;
2259 			len=pc*2.0+lv;
2260 			break;
2261 		case 2:
2262 			pos=lc+lh+lc+lv+pc;
2263 			len=(lc-pc)*2.0+lh;
2264 			break;
2265 		default:
2266 			pos=lc+lh+lc+lv+lc+lh+lc-pc;
2267 			len=pc*2.0+lv;
2268 			break;
2269 		}
2270 
2271 		n=(int)(emMin(len/arrowDistance,1E9)+0.5);
2272 		if (n<1) n=1;
2273 		for (m=1; m<n; m*=2);
2274 		n&=(m|(m>>1)|(m>>2));
2275 
2276 		delta=len/n;
2277 
2278 		for (j=0; n>0; j=(j+1)&7) {
2279 			if (!(j&1)) l=lc;
2280 			else if (j&2) l=lv;
2281 			else l=lh;
2282 			m=(int)floor(1.0+(l-pos)/delta);
2283 			if (m>0) {
2284 				if (m>n) m=n;
2285 				if (j&1) {
2286 					PaintHighlightArrowsOnLine(
2287 						pnt,
2288 						j==1 ? x2    : j==3 ? x1-sr : j==5 ? x1    : x2+sr,
2289 						j==1 ? y2+sr : j==3 ? y2    : j==5 ? y1-sr : y1,
2290 						j==1 ? -1.0 : j==5 ? 1.0 : 0.0,
2291 						j==3 ? -1.0 : j==7 ? 1.0 : 0.0,
2292 						pos,delta,m,goalX,goalY,
2293 						arrowSize,shadowColor,arrowColor
2294 					);
2295 				}
2296 				else {
2297 					PaintHighlightArrowsOnBow(
2298 						pnt,
2299 						j==2 || j==4 ? x1 : x2,
2300 						j>=4 ? y1 : y2,
2301 						sr,j/2,pos,delta,m,goalX,goalY,
2302 						arrowSize,shadowColor,arrowColor
2303 					);
2304 				}
2305 				pos+=delta*m;
2306 				n-=m;
2307 			}
2308 			pos-=l;
2309 		}
2310 	}
2311 
2312 	pnt.EnterUserSpace();
2313 }
2314 
2315 
PaintHighlightArrowsOnLine(const emPainter & painter,double x,double y,double dx,double dy,double pos,double delta,int count,double goalX,double goalY,double arrowSize,emColor shadowColor,emColor arrowColor) const2316 void emView::PaintHighlightArrowsOnLine(
2317 	const emPainter & painter, double x, double y,
2318 	double dx, double dy, double pos, double delta,
2319 	int count, double goalX, double goalY, double arrowSize,
2320 	emColor shadowColor, emColor arrowColor
2321 ) const
2322 {
2323 	double cx1,cy1,cx2,cy2,minPos,maxPos,t;
2324 
2325 	minPos=-1E100;
2326 	maxPos=1E100;
2327 
2328 	cx1=painter.GetUserClipX1()-arrowSize*2.0;
2329 	cx2=painter.GetUserClipX2()+arrowSize*2.0;
2330 	if (dx>1E-10) {
2331 		t=(cx1-x)/dx; if (minPos<t) minPos=t;
2332 		t=(cx2-x)/dx; if (maxPos>t) maxPos=t;
2333 	}
2334 	else if (dx<-1E-10) {
2335 		t=(cx2-x)/dx; if (minPos<t) minPos=t;
2336 		t=(cx1-x)/dx; if (maxPos>t) maxPos=t;
2337 	}
2338 	else if (x>=cx2 || x<=cx1) {
2339 		return;
2340 	}
2341 
2342 	cy1=painter.GetUserClipY1()-arrowSize*2.0;
2343 	cy2=painter.GetUserClipY2()+arrowSize*2.0;
2344 	if (dy>1E-10) {
2345 		t=(cy1-y)/dy; if (minPos<t) minPos=t;
2346 		t=(cy2-y)/dy; if (maxPos>t) maxPos=t;
2347 	}
2348 	else if (dy<-1E-10) {
2349 		t=(cy2-y)/dy; if (minPos<t) minPos=t;
2350 		t=(cy1-y)/dy; if (maxPos>t) maxPos=t;
2351 	}
2352 	else if (y>=cy2 || y<=cy1) {
2353 		return;
2354 	}
2355 
2356 	if (pos<minPos) {
2357 		t=ceil((minPos-pos)/delta);
2358 		if (t>=(double)count) return;
2359 		count-=(int)(t+0.5);
2360 		pos+=t*delta;
2361 	}
2362 
2363 	while (count>0 && pos<=maxPos) {
2364 		PaintHighlightArrow(
2365 			painter,x+dx*pos,y+dy*pos,
2366 			goalX,goalY,arrowSize,
2367 			shadowColor,arrowColor
2368 		);
2369 		pos+=delta;
2370 		count--;
2371 	}
2372 }
2373 
2374 
PaintHighlightArrowsOnBow(const emPainter & painter,double x,double y,double radius,int quadrant,double pos,double delta,int count,double goalX,double goalY,double arrowSize,emColor shadowColor,emColor arrowColor) const2375 void emView::PaintHighlightArrowsOnBow(
2376 	const emPainter & painter, double x, double y, double radius,
2377 	int quadrant, double pos, double delta, int count,
2378 	double goalX, double goalY, double arrowSize,
2379 	emColor shadowColor, emColor arrowColor
2380 ) const
2381 {
2382 	double cx1,cy1,cx2,cy2,minPos,maxPos,t,a;
2383 	int i;
2384 
2385 	minPos=-1E100;
2386 	maxPos=1E100;
2387 
2388 	cx1=painter.GetUserClipX1()-arrowSize*2.0;
2389 	cy1=painter.GetUserClipY1()-arrowSize*2.0;
2390 	cx2=painter.GetUserClipX2()+arrowSize*2.0;
2391 	cy2=painter.GetUserClipY2()+arrowSize*2.0;
2392 
2393 	cx1-=x;
2394 	cy1-=y;
2395 	cx2-=x;
2396 	cy2-=y;
2397 
2398 	quadrant&=3;
2399 	for (i=0; i<quadrant; i++) {
2400 		t=cx1;
2401 		cx1=cy1;
2402 		cy1=-cx2;
2403 		cx2=cy2;
2404 		cy2=-t;
2405 	}
2406 
2407 	if (cx1>=radius || cx2<=0.0) return;
2408 	if (cy1>=radius || cy2<=0.0) return;
2409 
2410 	if (cx1>0.0) {
2411 		t=acos(cx1/radius)*radius;
2412 		if (maxPos>t) maxPos=t;
2413 	}
2414 	if (cx2<radius) {
2415 		t=acos(cx2/radius)*radius;
2416 		if (minPos<t) minPos=t;
2417 	}
2418 	if (cy1>0.0) {
2419 		t=asin(cy1/radius)*radius;
2420 		if (minPos<t) minPos=t;
2421 	}
2422 	if (cy2<radius) {
2423 		t=asin(cy2/radius)*radius;
2424 		if (maxPos>t) maxPos=t;
2425 	}
2426 
2427 	if (pos<minPos) {
2428 		t=ceil((minPos-pos)/delta);
2429 		if (t>=(double)count) return;
2430 		count-=(int)(t+0.5);
2431 		pos+=t*delta;
2432 	}
2433 
2434 	while (count>0 && pos<=maxPos) {
2435 		a=quadrant*M_PI*0.5+pos/radius;
2436 		PaintHighlightArrow(
2437 			painter,
2438 			x+cos(a)*radius,
2439 			y+sin(a)*radius,
2440 			goalX,goalY,arrowSize,
2441 			shadowColor,arrowColor
2442 		);
2443 		pos+=delta;
2444 		count--;
2445 	}
2446 }
2447 
2448 
PaintHighlightArrow(const emPainter & painter,double x,double y,double goalX,double goalY,double arrowSize,emColor shadowColor,emColor arrowColor) const2449 void emView::PaintHighlightArrow(
2450 	const emPainter & painter, double x, double y,
2451 	double goalX, double goalY, double arrowSize,
2452 	emColor shadowColor, emColor arrowColor
2453 ) const
2454 {
2455 	double sxy[4*2],axy[4*2];
2456 	double dx,dy,d,aw,ah,ag,sd;
2457 
2458 	dx=x-goalX;
2459 	dy=y-goalY;
2460 	d=sqrt(dx*dx+dy*dy);
2461 	if (d<0.01) {
2462 		dx=0;
2463 		dy=1.0;
2464 	}
2465 	else {
2466 		dx/=d;
2467 		dy/=d;
2468 	}
2469 
2470 	ah=arrowSize;
2471 	ag=ah*0.8;
2472 	aw=ah*0.5;
2473 	sd=ah*0.2;
2474 
2475 	axy[0]=x;
2476 	axy[1]=y;
2477 	axy[2]=x+dx*ah-dy*aw*0.5;
2478 	axy[3]=y+dy*ah+dx*aw*0.5;
2479 	axy[4]=x+dx*ag;
2480 	axy[5]=y+(dy*ag);
2481 	axy[6]=x+dx*ah+dy*aw*0.5;
2482 	axy[7]=y+(dy*ah-dx*aw*0.5);
2483 
2484 	sxy[0]=axy[0];
2485 	sxy[1]=axy[1];
2486 	sxy[2]=axy[2]+sd;
2487 	sxy[3]=axy[3]+sd;
2488 	sxy[4]=axy[4]+sd*ag/ah;
2489 	sxy[5]=axy[5]+sd*ag/ah;
2490 	sxy[6]=axy[6]+sd;
2491 	sxy[7]=axy[7]+sd;
2492 
2493 	painter.PaintPolygon(sxy,4,shadowColor);
2494 	painter.PaintPolygon(axy,4,arrowColor);
2495 }
2496 
2497 
SetSeekPos(emPanel * panel,const char * childName)2498 void emView::SetSeekPos(emPanel * panel, const char * childName)
2499 {
2500 	if (!panel || !childName) childName="";
2501 	if (SeekPosPanel!=panel) {
2502 		if (SeekPosPanel) {
2503 			SeekPosPanel->AddPendingNotice(
2504 				emPanel::NF_SOUGHT_NAME_CHANGED|
2505 				emPanel::NF_MEMORY_LIMIT_CHANGED
2506 			);
2507 		}
2508 		SeekPosPanel=panel;
2509 		SeekPosChildName=childName;
2510 		if (SeekPosPanel) {
2511 			SeekPosPanel->AddPendingNotice(
2512 				emPanel::NF_SOUGHT_NAME_CHANGED|
2513 				emPanel::NF_MEMORY_LIMIT_CHANGED
2514 			);
2515 		}
2516 	}
2517 	else if (panel && SeekPosChildName!=childName) {
2518 		SeekPosChildName=childName;
2519 		SeekPosPanel->AddPendingNotice(emPanel::NF_SOUGHT_NAME_CHANGED);
2520 	}
2521 }
2522 
2523 
IsHopeForSeeking() const2524 bool emView::IsHopeForSeeking() const
2525 {
2526 	return SeekPosPanel && SeekPosPanel->IsHopeForSeeking();
2527 }
2528 
2529 
UpdateEngineClass(emView & view)2530 emView::UpdateEngineClass::UpdateEngineClass(emView & view)
2531 	: emEngine(view.GetScheduler()), View(view)
2532 {
2533 	SetEnginePriority(emEngine::HIGH_PRIORITY);
2534 }
2535 
2536 
Cycle()2537 bool emView::UpdateEngineClass::Cycle()
2538 {
2539 	View.Update();
2540 	return false;
2541 }
2542 
2543 
EOIEngineClass(emView & view)2544 emView::EOIEngineClass::EOIEngineClass(emView & view)
2545 	: emEngine(view.GetScheduler()), View(view)
2546 {
2547 	CountDown=5;
2548 	WakeUp();
2549 }
2550 
2551 
Cycle()2552 bool emView::EOIEngineClass::Cycle()
2553 {
2554 	CountDown--;
2555 	if (CountDown>0) return true;
2556 	Signal(View.EOISignal);
2557 	View.EOIEngine=NULL;
2558 	delete this;
2559 	return false;
2560 }
2561 
2562 
StressTestClass(emView & view)2563 emView::StressTestClass::StressTestClass(emView & view)
2564 	: emEngine(view.GetScheduler()), View(view)
2565 {
2566 	TCnt=128;
2567 	TPos=0;
2568 	TValid=0;
2569 	T=new emUInt64[TCnt];
2570 	FrameRate=0.0;
2571 	FRUpdate=0;
2572 	WakeUp();
2573 }
2574 
2575 
~StressTestClass()2576 emView::StressTestClass::~StressTestClass()
2577 {
2578 	delete [] T;
2579 }
2580 
2581 
PaintInfo(const emPainter & painter)2582 void emView::StressTestClass::PaintInfo(const emPainter & painter)
2583 {
2584 	char tmp[256];
2585 	double x,y,w,h,ch;
2586 
2587 	sprintf(tmp,"Stress Test\n%5.1f Hz",FrameRate);
2588 	x=View.CurrentX;
2589 	y=View.CurrentY;
2590 	ch=View.CurrentHeight/45;
2591 	if (ch<10.0) ch=10.0;
2592 	w=painter.GetTextSize(tmp,ch,true,0.0,&h);
2593 	painter.PaintRect(x,y,w,h,emColor(255,0,255,128));
2594 	painter.PaintTextBoxed(
2595 		x,y,w,h,
2596 		tmp,
2597 		ch,
2598 		emColor(255,255,0,192),
2599 		0,
2600 		EM_ALIGN_CENTER,
2601 		EM_ALIGN_CENTER
2602 	);
2603 }
2604 
2605 
Cycle()2606 bool emView::StressTestClass::Cycle()
2607 {
2608 	emUInt64 clk,dt;
2609 	int i;
2610 
2611 	clk=emGetClockMS();
2612 	TPos=(TPos+1)%TCnt;
2613 	T[TPos]=clk;
2614 	if (TValid<TCnt) TValid++;
2615 	if (clk-FRUpdate>100) {
2616 		FrameRate=0.0;
2617 		FRUpdate=clk;
2618 		for (i=1; i<TValid; i++) {
2619 			dt=clk-T[(TPos-i+TCnt)%TCnt];
2620 			if (dt>1000 && i>0) break;
2621 			FrameRate=(i)*1000.0/dt;
2622 		}
2623 	}
2624 
2625 	View.InvalidatePainting();
2626 
2627 	return true;
2628 }
2629 
2630 
2631 const double emView::MaxSVPSize=1E+12;
2632 const double emView::MaxSVPSearchSize=1E+14;
2633 
2634 
2635 //==============================================================================
2636 //================================= emViewPort =================================
2637 //==============================================================================
2638 
emViewPort(emView & homeView)2639 emViewPort::emViewPort(emView & homeView)
2640 {
2641 	HomeView=&homeView;
2642 	CurrentView=&homeView;
2643 	if (HomeView->DummyViewPort!=HomeView->HomeViewPort) {
2644 		emFatalError("emViewPort: The view has already a view port.");
2645 	}
2646 	HomeView->HomeViewPort=this;
2647 	HomeView->CurrentViewPort=this;
2648 }
2649 
2650 
~emViewPort()2651 emViewPort::~emViewPort()
2652 {
2653 	if (HomeView) {
2654 		if (HomeView->DummyViewPort==this) {
2655 			emFatalError(
2656 				"emViewPort::~emViewPort: Illegal destruction of dummy view port."
2657 			);
2658 		}
2659 		if (HomeView!=CurrentView) {
2660 			if (HomeView->PopupWindow) {
2661 				HomeView->RawZoomOut();
2662 			}
2663 			else {
2664 				emFatalError(
2665 					"emViewPort::~emViewPort: Illegal destruction of popup view port."
2666 				);
2667 			}
2668 		}
2669 		HomeView->HomeViewPort=HomeView->DummyViewPort;
2670 		HomeView->CurrentViewPort=HomeView->DummyViewPort;
2671 		HomeView=NULL;
2672 		CurrentView=NULL;
2673 	}
2674 }
2675 
2676 
RequestFocus()2677 void emViewPort::RequestFocus()
2678 {
2679 	SetViewFocused(true);
2680 }
2681 
2682 
IsSoftKeyboardShown() const2683 bool emViewPort::IsSoftKeyboardShown() const
2684 {
2685 	return false;
2686 }
2687 
2688 
ShowSoftKeyboard(bool show)2689 void emViewPort::ShowSoftKeyboard(bool show)
2690 {
2691 }
2692 
2693 
GetInputClockMS() const2694 emUInt64 emViewPort::GetInputClockMS() const
2695 {
2696 	return emGetClockMS();
2697 }
2698 
2699 
InputToView(emInputEvent & event,const emInputState & state)2700 void emViewPort::InputToView(
2701 	emInputEvent & event, const emInputState & state
2702 )
2703 {
2704 	if (!CurrentView->FirstVIF) CurrentView->Input(event,state);
2705 	else CurrentView->FirstVIF->Input(event,state);
2706 }
2707 
2708 
InvalidateCursor()2709 void emViewPort::InvalidateCursor()
2710 {
2711 }
2712 
2713 
InvalidatePainting(double x,double y,double w,double h)2714 void emViewPort::InvalidatePainting(double x, double y, double w, double h)
2715 {
2716 }
2717 
2718 
IsSoftKeyboardShown()2719 bool emViewPort::IsSoftKeyboardShown()
2720 {
2721 	return ((const emViewPort*)this)->IsSoftKeyboardShown();
2722 }
2723 
2724 
GetInputClockMS()2725 emUInt64 emViewPort::GetInputClockMS()
2726 {
2727 	return ((const emViewPort*)this)->GetInputClockMS();
2728 }
2729 
2730 
emViewPort()2731 emViewPort::emViewPort()
2732 {
2733 	HomeView=NULL;
2734 	CurrentView=NULL;
2735 }
2736