1 //------------------------------------------------------------------------------
2 // emViewInputFilter.cpp
3 //
4 // Copyright (C) 2011-2012,2014-2016,2018-2020 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/emViewInputFilter.h>
22 #include <emCore/emInstallInfo.h>
23 #include <emCore/emScreen.h>
24 
25 
26 //==============================================================================
27 //============================= emViewInputFilter ==============================
28 //==============================================================================
29 
emViewInputFilter(emView & view,emViewInputFilter * next)30 emViewInputFilter::emViewInputFilter(emView & view, emViewInputFilter * next)
31 	: emEngine(view.GetScheduler()),
32 	View(view)
33 {
34 	Next=next;
35 	if (Next) {
36 		Prev=Next->Prev;
37 		Next->Prev=this;
38 	}
39 	else {
40 		Prev=View.LastVIF;
41 		View.LastVIF=this;
42 	}
43 	if (Prev) Prev->Next=this;
44 	else View.FirstVIF=this;
45 }
46 
47 
~emViewInputFilter()48 emViewInputFilter::~emViewInputFilter()
49 {
50 	if (Next) Next->Prev=Prev;
51 	else View.LastVIF=Prev;
52 	if (Prev) Prev->Next=Next;
53 	else View.FirstVIF=Next;
54 }
55 
56 
GetTouchEventPriority(double touchX,double touchY) const57 double emViewInputFilter::GetTouchEventPriority(double touchX, double touchY) const
58 {
59 	return GetForwardTouchEventPriority(touchX,touchY);
60 }
61 
62 
Input(emInputEvent & event,const emInputState & state)63 void emViewInputFilter::Input(emInputEvent & event, const emInputState & state)
64 {
65 	ForwardInput(event,state);
66 }
67 
68 
Cycle()69 bool emViewInputFilter::Cycle()
70 {
71 	return false;
72 }
73 
74 
75 //==============================================================================
76 //============================ emMouseZoomScrollVIF ============================
77 //==============================================================================
78 
emMouseZoomScrollVIF(emView & view,emViewInputFilter * next)79 emMouseZoomScrollVIF::emMouseZoomScrollVIF(emView & view, emViewInputFilter * next)
80 	: emViewInputFilter(view,next),
81 	MouseAnim(view),
82 	WheelAnim(view)
83 {
84 	CoreConfig=emCoreConfig::Acquire(view.GetRootContext());
85 	LastMouseX=0.0;
86 	LastMouseY=0.0;
87 	ZoomFixX=0.0;
88 	ZoomFixY=0.0;
89 	EmuMidButtonTime=0;
90 	EmuMidButtonRepeat=0;
91 	WheelZoomSpeed=0;
92 	WheelZoomTime=0;
93 	MagnetismAvoidance=false;
94 	MagAvMouseMoveX=0.0;
95 	MagAvMouseMoveY=0.0;
96 	MagAvTime=0;
97 }
98 
99 
~emMouseZoomScrollVIF()100 emMouseZoomScrollVIF::~emMouseZoomScrollVIF()
101 {
102 }
103 
104 
Input(emInputEvent & event,const emInputState & state)105 void emMouseZoomScrollVIF::Input(emInputEvent & event, const emInputState & state)
106 {
107 	double my,mx,dmx,dmy,f;
108 	emInputState rwstate;
109 	emPanel * p;
110 
111 	rwstate=state;
112 
113 	if (CoreConfig->EmulateMiddleButton) {
114 		EmulateMiddleButton(event,rwstate);
115 	}
116 
117 	if ((GetView().GetViewFlags()&emView::VF_NO_USER_NAVIGATION)!=0) {
118 		if (MouseAnim.IsActive()) MouseAnim.Deactivate();
119 		if (WheelAnim.IsActive()) WheelAnim.Deactivate();
120 		ForwardInput(event,rwstate);
121 		return;
122 	}
123 
124 	mx=rwstate.GetMouseX();
125 	my=rwstate.GetMouseY();
126 
127 	if (MouseAnim.IsActive() && MouseAnim.IsGripped()) {
128 		dmx=mx-LastMouseX;
129 		dmy=my-LastMouseY;
130 		UpdateMagnetismAvoidance(dmx,dmy);
131 		if (!rwstate.GetMiddleButton() || !GetView().IsFocused()) {
132 			MouseAnim.SetGripped(false);
133 			MouseAnim.SetDeactivateWhenIdle(true);
134 			if (!MagnetismAvoidance) {
135 				GetView().ActivateMagneticViewAnimator();
136 			}
137 		}
138 		else if (fabs(dmx)>0.1 || fabs(dmy)>0.1) {
139 			if (rwstate.GetCtrl()) {
140 				f=GetMouseZoomSpeed(rwstate.GetShift());
141 				MouseAnim.MoveGrip(2,-dmy*f);
142 				if (CoreConfig->StickMouseWhenNavigating) {
143 					MoveMousePointer(-dmx,-dmy);
144 					mx-=dmx;
145 					my-=dmy;
146 					rwstate.SetMouse(mx,my);
147 				}
148 				ZoomFixX=mx;
149 			}
150 			else {
151 				f=GetMouseScrollSpeed(rwstate.GetShift());
152 				MouseAnim.MoveGrip(0,dmx*f);
153 				MouseAnim.MoveGrip(1,dmy*f);
154 				if (
155 					CoreConfig->StickMouseWhenNavigating &&
156 					!CoreConfig->PanFunction
157 				) {
158 					MoveMousePointer(-dmx,-dmy);
159 					mx-=dmx;
160 					my-=dmy;
161 					rwstate.SetMouse(mx,my);
162 				}
163 				ZoomFixX=mx;
164 				ZoomFixY=my;
165 			}
166 			MouseAnim.SetZoomFixPoint(ZoomFixX,ZoomFixY);
167 			SetMouseAnimParams();
168 		}
169 	}
170 
171 	switch (event.GetKey()) {
172 	case EM_KEY_MIDDLE_BUTTON:
173 		if (!rwstate.GetAlt() && !rwstate.GetMeta()) {
174 			if (event.GetRepeat()) {
175 				p=GetView().GetFocusablePanelAt(mx,my,true);
176 				if (!p) p=GetView().GetRootPanel();
177 				if (p) GetView().VisitFullsized(p,true,((event.GetRepeat()&1)==0)!=rwstate.GetShift());
178 			}
179 			else {
180 				ZoomFixX=mx;
181 				ZoomFixY=my;
182 				InitMagnetismAvoidance();
183 				MouseAnim.Activate();
184 				SetMouseAnimParams();
185 				MouseAnim.SetZoomFixPoint(ZoomFixX,ZoomFixY);
186 				MouseAnim.SetDeactivateWhenIdle(false);
187 				MouseAnim.SetGripped(true);
188 			}
189 			event.Eat();
190 		}
191 		break;
192 	case EM_KEY_WHEEL_UP:
193 	case EM_KEY_WHEEL_DOWN:
194 		if (rwstate.IsNoMod() || rwstate.IsShiftMod()) {
195 			UpdateWheelZoomSpeed(
196 				event.GetKey()==EM_KEY_WHEEL_DOWN,
197 				rwstate.GetShift() || rwstate.Get(EM_KEY_MIDDLE_BUTTON)
198 			);
199 			ZoomFixX=mx;
200 			ZoomFixY=my;
201 			if (MouseAnim.IsActive() && MouseAnim.IsGripped()) {
202 				MouseAnim.MoveGrip(2,WheelZoomSpeed/GetView().GetZoomFactorLogarithmPerPixel());
203 				MouseAnim.SetZoomFixPoint(ZoomFixX,ZoomFixY);
204 				SetMouseAnimParams();
205 			}
206 			else {
207 				WheelAnim.Activate();
208 				SetWheelAnimParams();
209 				WheelAnim.SetDeactivateWhenIdle(false);
210 				WakeUp();
211 				WheelAnim.SetGripped(true);
212 				WheelAnim.SetZoomFixPoint(ZoomFixX,ZoomFixY);
213 				WheelAnim.MoveGrip(2,WheelZoomSpeed/GetView().GetZoomFactorLogarithmPerPixel());
214 			}
215 			event.Eat();
216 		}
217 		break;
218 	default:
219 		break;
220 	}
221 
222 	LastMouseX=mx;
223 	LastMouseY=my;
224 
225 	ForwardInput(event,rwstate);
226 }
227 
228 
Cycle()229 bool emMouseZoomScrollVIF::Cycle()
230 {
231 	bool busy;
232 
233 	busy=false;
234 
235 	if (WheelAnim.IsActive() && WheelAnim.IsGripped()) {
236 		if (
237 			WheelAnim.GetAbsVelocity()<10.0 &&
238 			WheelAnim.GetAbsSpringExtension()<0.5
239 		) {
240 			GetView().ActivateMagneticViewAnimator();
241 		}
242 		else {
243 			busy=true;
244 		}
245 	}
246 
247 	return busy;
248 }
249 
250 
EmulateMiddleButton(emInputEvent & event,emInputState & state)251 void emMouseZoomScrollVIF::EmulateMiddleButton(emInputEvent & event, emInputState & state)
252 {
253 	emUInt64 d;
254 
255 	// Remember that we have to make sure that the event is not emulated
256 	// multiple times by nested views. Therefore this condition:
257 	if (!state.Get(EM_KEY_MIDDLE_BUTTON)) {
258 		if (
259 			(event.GetKey()==EM_KEY_ALT || event.GetKey()==EM_KEY_ALT_GR) &&
260 			event.GetRepeat()==0
261 		) {
262 			state.Set(EM_KEY_MIDDLE_BUTTON,true);
263 			emInputState tmpState(state);
264 			tmpState.Set(EM_KEY_ALT,false);
265 			tmpState.Set(EM_KEY_ALT_GR,false);
266 			d=GetView().GetInputClockMS()-EmuMidButtonTime;
267 			if (d<330) EmuMidButtonRepeat++;
268 			else EmuMidButtonRepeat=0;
269 			EmuMidButtonTime+=d;
270 			emInputEvent tmpEvent;
271 			tmpEvent.Setup(EM_KEY_MIDDLE_BUTTON,emString(),EmuMidButtonRepeat,0);
272 			emMouseZoomScrollVIF::Input(tmpEvent,tmpState);
273 		}
274 		else if (state.Get(EM_KEY_ALT) || state.Get(EM_KEY_ALT_GR)) {
275 			state.Set(EM_KEY_MIDDLE_BUTTON,true);
276 		}
277 	}
278 }
279 
280 
MoveMousePointerBackIntoView(double * pmx,double * pmy)281 bool emMouseZoomScrollVIF::MoveMousePointerBackIntoView(double * pmx, double * pmy)
282 {
283 	//??? This is no longer called :-( (mouse wheel zoom-out on pop-up)
284 	double cx,cy,cw,ch,mx,my,safety,s;
285 	bool doMove;
286 
287 	safety=3.0;
288 	cx=GetView().GetCurrentX();
289 	cy=GetView().GetCurrentY();
290 	cw=GetView().GetCurrentWidth();
291 	ch=GetView().GetCurrentHeight();
292 	mx=*pmx;
293 	my=*pmy;
294 	s=safety;
295 	if (s>cw*0.5) s=cw*0.5;
296 	doMove=false;
297 	if (mx<cx+s) {
298 		mx=cx+s;
299 		doMove=true;
300 	}
301 	else if (mx>cx+cw-s) {
302 		mx=cx+cw-s;
303 		doMove=true;
304 	}
305 	s=safety;
306 	if (s>ch*0.5) s=ch*0.5;
307 	if (my<cy+s) {
308 		my=cy+s;
309 		doMove=true;
310 	}
311 	else if (my>cy+ch-s) {
312 		my=cy+ch-s;
313 		doMove=true;
314 	}
315 	if (doMove) {
316 		MoveMousePointer(mx-(*pmx),my-(*pmy));
317 		*pmx=mx;
318 		*pmy=my;
319 	}
320 	return doMove;
321 }
322 
323 
MoveMousePointer(double dx,double dy)324 void emMouseZoomScrollVIF::MoveMousePointer(double dx, double dy)
325 {
326 	emScreen * screen;
327 
328 	screen=GetView().GetScreen();
329 	if (!screen) {
330 		emFatalError(
331 			"emMouseZoomScrollVIF::MoveMousePointer: No screen interface found."
332 		);
333 	}
334 	screen->MoveMousePointer(dx,dy);
335 }
336 
337 
GetMouseZoomSpeed(bool fine) const338 double emMouseZoomScrollVIF::GetMouseZoomSpeed(bool fine) const
339 {
340 	double f;
341 
342 	f=CoreConfig->MouseZoomSpeed;
343 	if (fine) f*=0.1;
344 	return f*6.0;
345 }
346 
347 
GetMouseScrollSpeed(bool fine) const348 double emMouseZoomScrollVIF::GetMouseScrollSpeed(bool fine) const
349 {
350 	double f;
351 
352 	f=CoreConfig->MouseScrollSpeed;
353 	if (fine) f*=0.1;
354 	if (CoreConfig->PanFunction) f=-f; else f=6.0*f;
355 	return f;
356 }
357 
358 
UpdateWheelZoomSpeed(bool down,bool fine)359 void emMouseZoomScrollVIF::UpdateWheelZoomSpeed(bool down, bool fine)
360 {
361 	double dt,newSpeed,a,aMin,t1,t2,f1,f2;
362 	emUInt64 clk;
363 
364 	clk=GetView().GetInputClockMS();
365 	dt=(clk-WheelZoomTime)*0.001;
366 	WheelZoomTime=clk;
367 
368 	newSpeed=CoreConfig->MouseWheelZoomSpeed * log(sqrt(2.0));
369 	if (fine) newSpeed*=0.1;
370 	if (down) newSpeed=-newSpeed;
371 
372 	a = CoreConfig->MouseWheelZoomAcceleration;
373 	aMin = CoreConfig->MouseWheelZoomAcceleration.GetMinValue();
374 	if (a>aMin*1.0001) {
375 		t1 = 0.03;
376 		t2 = 0.35;
377 		f1 = pow(2.2,a);
378 		f2 = pow(0.4,a);
379 		if (newSpeed*WheelZoomSpeed<0.0) dt=t2;
380 		if (dt<t1) dt=t1;
381 		if (dt>t2) dt=t2;
382 		newSpeed *= exp(log(f1) + (log(f2)-log(f1))*(dt-t1)/(t2-t1));
383 	}
384 
385 	WheelZoomSpeed=newSpeed;
386 }
387 
388 
SetMouseAnimParams()389 void emMouseZoomScrollVIF::SetMouseAnimParams()
390 {
391 	double k,kMin,zflpp;
392 
393 	k=CoreConfig->KineticZoomingAndScrolling;
394 	kMin=CoreConfig->KineticZoomingAndScrolling.GetMinValue();
395 	if (k<kMin*1.0001) {
396 		k=0.001;
397 	}
398 	zflpp=GetView().GetZoomFactorLogarithmPerPixel();
399 	MouseAnim.SetSpringConstant(2500.0/(k*k));
400 	MouseAnim.SetFriction(2.0/zflpp/(k*k));
401 	MouseAnim.SetFrictionEnabled(true);
402 }
403 
404 
SetWheelAnimParams()405 void emMouseZoomScrollVIF::SetWheelAnimParams()
406 {
407 	double k,kMin,zflpp;
408 
409 	k=CoreConfig->KineticZoomingAndScrolling;
410 	kMin=CoreConfig->KineticZoomingAndScrolling.GetMinValue();
411 	if (k<kMin*1.0001) {
412 		k=0.001;
413 	}
414 	zflpp=GetView().GetZoomFactorLogarithmPerPixel();
415 	WheelAnim.SetSpringConstant(480.0/(k*k));
416 	WheelAnim.SetFriction(2.0/zflpp/(k*k));
417 	WheelAnim.SetFrictionEnabled(true);
418 }
419 
420 
InitMagnetismAvoidance()421 void emMouseZoomScrollVIF::InitMagnetismAvoidance()
422 {
423 	MagAvMouseMoveX=0.0;
424 	MagAvMouseMoveY=0.0;
425 	MagAvTime=GetView().GetInputClockMS();
426 	MagnetismAvoidance=false;
427 }
428 
429 
UpdateMagnetismAvoidance(double dmx,double dmy)430 void emMouseZoomScrollVIF::UpdateMagnetismAvoidance(double dmx, double dmy)
431 {
432 	static const double MOUSE_HOLD_MAX_MOVE = 2.0;
433 	static const emUInt64 MOUSE_HOLD_TIME = 750;
434 	emUInt64 clk;
435 	double r;
436 
437 	clk=GetView().GetInputClockMS();
438 	MagAvMouseMoveX += dmx;
439 	MagAvMouseMoveY += dmy;
440 	r=sqrt(MagAvMouseMoveX*MagAvMouseMoveX+MagAvMouseMoveY*MagAvMouseMoveY);
441 	if (r>MOUSE_HOLD_MAX_MOVE) {
442 		MagAvMouseMoveX=0.0;
443 		MagAvMouseMoveY=0.0;
444 		MagAvTime=clk;
445 	}
446 	MagnetismAvoidance = (clk-MagAvTime>=MOUSE_HOLD_TIME);
447 }
448 
449 
450 //==============================================================================
451 //========================== emKeyboardZoomScrollVIF ===========================
452 //==============================================================================
453 
emKeyboardZoomScrollVIF(emView & view,emViewInputFilter * next)454 emKeyboardZoomScrollVIF::emKeyboardZoomScrollVIF(emView & view, emViewInputFilter * next)
455 	: emViewInputFilter(view,next),
456 	Animator(view)
457 {
458 	CoreConfig=emCoreConfig::Acquire(view.GetRootContext());
459 	Active=false;
460 	NavByProgState=0;
461 }
462 
463 
~emKeyboardZoomScrollVIF()464 emKeyboardZoomScrollVIF::~emKeyboardZoomScrollVIF()
465 {
466 }
467 
468 
Input(emInputEvent & event,const emInputState & state)469 void emKeyboardZoomScrollVIF::Input(emInputEvent & event, const emInputState & state)
470 {
471 	double vs,vz,tx,ty,tz;
472 
473 	if ((GetView().GetViewFlags()&emView::VF_NO_USER_NAVIGATION)!=0) {
474 		if (Animator.IsActive()) Animator.Deactivate();
475 		Active=false;
476 		NavByProgState=0;
477 		ForwardInput(event,state);
478 		return;
479 	}
480 
481 	NavigateByProgram(event,state);
482 
483 	if (
484 		(
485 			state.IsAltMod() ||
486 			state.IsShiftAltMod()
487 		) && (
488 			event.GetKey()==EM_KEY_CURSOR_LEFT ||
489 			event.GetKey()==EM_KEY_CURSOR_RIGHT ||
490 			event.GetKey()==EM_KEY_CURSOR_UP ||
491 			event.GetKey()==EM_KEY_CURSOR_DOWN ||
492 			event.GetKey()==EM_KEY_PAGE_DOWN ||
493 			event.GetKey()==EM_KEY_PAGE_UP
494 		)
495 	) {
496 		Active=true;
497 		if (!Animator.IsActive()) {
498 			Animator.Activate();
499 			Animator.SetDeactivateWhenIdle(false);
500 		}
501 	}
502 
503 	if (!Animator.IsActive()) {
504 		Active=false;
505 	}
506 
507 	if (Active) {
508 		tx=0.0;
509 		ty=0.0;
510 		tz=0.0;
511 		if (GetView().IsFocused() && state.Get(EM_KEY_ALT)) {
512 			vs=GetScrollSpeed(state.Get(EM_KEY_SHIFT));
513 			vz=GetZoomSpeed(state.Get(EM_KEY_SHIFT));
514 			if (state.Get(EM_KEY_CURSOR_LEFT )) tx-=vs;
515 			if (state.Get(EM_KEY_CURSOR_RIGHT)) tx+=vs;
516 			if (state.Get(EM_KEY_CURSOR_UP   )) ty-=vs;
517 			if (state.Get(EM_KEY_CURSOR_DOWN )) ty+=vs;
518 			if (state.Get(EM_KEY_PAGE_DOWN   )) tz-=vz;
519 			if (state.Get(EM_KEY_PAGE_UP     )) tz+=vz;
520 		}
521 		SetAnimatorParameters();
522 		Animator.SetTargetVelocity(0,tx);
523 		Animator.SetTargetVelocity(1,ty);
524 		Animator.SetTargetVelocity(2,tz);
525 		if (Animator.GetAbsTargetVelocity()<0.01) {
526 			Animator.SetDeactivateWhenIdle(true);
527 			Active=false;
528 		}
529 	}
530 
531 	ForwardInput(event,state);
532 }
533 
534 
NavigateByProgram(emInputEvent & event,const emInputState & state)535 void emKeyboardZoomScrollVIF::NavigateByProgram(
536 	emInputEvent & event, const emInputState & state
537 )
538 {
539 	static const double scrollDelta=0.3;
540 	static const double zoomFac=1.0015;
541 	double cx,cy,cw,ch,cpt;
542 	int step;
543 
544 	// This implements a special key sequence for scrolling and zooming. The
545 	// sequence is meant to be generated by other programs. It is not useful
546 	// for control by human. The key sequence consists of three key
547 	// combinations:
548 	// 1.) Shift+Alt+End
549 	// 2.) Shift+Alt+A or Shift+Alt+B or Shift+Alt+C ... or Shift+Alt+Z
550 	//     This is the strength of the move (A = weakest, Z = strongest).
551 	// 3.) Shift+Alt+CursorUp|Down|Left|Right or Shift+Alt+PageUp|Down
552 	//     This is the direction of the operation (scrolling or zooming).
553 
554 	if (NavByProgState==0) {
555 		if (event.GetKey()==EM_KEY_END && state.IsShiftAltMod()) {
556 			NavByProgState=1;
557 			event.Eat();
558 		}
559 	}
560 	else if (NavByProgState==1) {
561 		if (event.GetKey()!=EM_KEY_NONE) {
562 			NavByProgState=0;
563 			if (state.IsShiftAltMod()) {
564 				step=((int)event.GetKey())-EM_KEY_A+1;
565 				if (step>=1 && step<=26) {
566 					NavByProgState=1+step;
567 					event.Eat();
568 				}
569 			}
570 		}
571 	}
572 	else if (NavByProgState>=2) {
573 		if (event.GetKey()!=EM_KEY_NONE) {
574 			step=NavByProgState-1;
575 			NavByProgState=0;
576 			if (state.IsShiftAltMod()) {
577 				cx=GetView().GetCurrentX();
578 				cy=GetView().GetCurrentY();
579 				cw=GetView().GetCurrentWidth();
580 				ch=GetView().GetCurrentHeight();
581 				cpt=GetView().GetCurrentPixelTallness();
582 				switch (event.GetKey()) {
583 				case EM_KEY_CURSOR_LEFT:
584 					GetView().Scroll(-scrollDelta*step,0.0);
585 					event.Eat();
586 					break;
587 				case EM_KEY_CURSOR_RIGHT:
588 					GetView().Scroll(scrollDelta*step,0.0);
589 					event.Eat();
590 					break;
591 				case EM_KEY_CURSOR_UP:
592 					GetView().Scroll(0.0,-scrollDelta*step/cpt);
593 					event.Eat();
594 					break;
595 				case EM_KEY_CURSOR_DOWN:
596 					GetView().Scroll(0.0,scrollDelta*step/cpt);
597 					event.Eat();
598 					break;
599 				case EM_KEY_PAGE_UP:
600 					GetView().Zoom(cx+cw*0.5,cy+ch*0.5,pow(zoomFac,step));
601 					event.Eat();
602 					break;
603 				case EM_KEY_PAGE_DOWN:
604 					GetView().Zoom(cx+cw*0.5,cy+ch*0.5,1.0/pow(zoomFac,step));
605 					event.Eat();
606 					break;
607 				default:
608 					break;
609 				}
610 			}
611 		}
612 	}
613 }
614 
615 
GetZoomSpeed(bool fine) const616 double emKeyboardZoomScrollVIF::GetZoomSpeed(bool fine) const
617 {
618 	double f;
619 
620 	f=CoreConfig->KeyboardZoomSpeed;
621 	if (fine) f*=0.1;
622 	f/=GetView().GetZoomFactorLogarithmPerPixel();
623 	f*=2.0;
624 	return f;
625 }
626 
627 
GetScrollSpeed(bool fine) const628 double emKeyboardZoomScrollVIF::GetScrollSpeed(bool fine) const
629 {
630 	double f;
631 
632 	f=CoreConfig->KeyboardScrollSpeed;
633 	if (fine) f*=0.1;
634 	f/=GetView().GetZoomFactorLogarithmPerPixel();
635 	f*=2.0;
636 	return f;
637 }
638 
639 
SetAnimatorParameters()640 void emKeyboardZoomScrollVIF::SetAnimatorParameters()
641 {
642 	static const double tFriction=1.6;
643 	static const double tAccel=0.6;
644 	static const double tReverse=0.2;
645 	double v,k,kMin;
646 
647 	v=(GetScrollSpeed()+GetZoomSpeed())*0.5;
648 	k=CoreConfig->KineticZoomingAndScrolling;
649 	kMin=CoreConfig->KineticZoomingAndScrolling.GetMinValue();
650 	if (k<kMin*1.0001) {
651 		k=0.001;
652 	}
653 	Animator.CenterZoomFixPoint();
654 	Animator.SetAcceleration(v/(k*tAccel));
655 	Animator.SetReverseAcceleration(v/(k*tReverse));
656 	Animator.SetFriction(v/(k*tFriction));
657 	Animator.SetFrictionEnabled(true);
658 }
659 
660 
661 //==============================================================================
662 //================================= emCheatVIF =================================
663 //==============================================================================
664 
emCheatVIF(emView & view,emViewInputFilter * next)665 emCheatVIF::emCheatVIF(emView & view, emViewInputFilter * next)
666 	: emViewInputFilter(view,next)
667 {
668 	CoreConfig=emCoreConfig::Acquire(view.GetRootContext());
669 	memset(CheatBuffer,0,sizeof(CheatBuffer));
670 }
671 
672 
~emCheatVIF()673 emCheatVIF::~emCheatVIF()
674 {
675 }
676 
677 
Input(emInputEvent & event,const emInputState & state)678 void emCheatVIF::Input(emInputEvent & event, const emInputState & state)
679 {
680 	const char * p, * func;
681 	emLibHandle lib;
682 	emString str;
683 	void * sym;
684 	size_t sz;
685 
686 	if ((GetView().GetViewFlags()&emView::VF_NO_USER_NAVIGATION)!=0) {
687 		ForwardInput(event,state);
688 		return;
689 	}
690 
691 	p=event.GetChars();
692 	if (!*p) goto L_DONE;
693 	sz=strlen(p);
694 	if (sz>sizeof(CheatBuffer)) sz=sizeof(CheatBuffer);
695 	memmove(CheatBuffer,CheatBuffer+sz,sizeof(CheatBuffer)-sz);
696 	memcpy(CheatBuffer+sizeof(CheatBuffer)-sz,p,sz);
697 	p=CheatBuffer+sizeof(CheatBuffer)-1;
698 	if (*p!='!') goto L_DONE;
699 	(*(char*)p)=0;
700 	do { p--; if (p<CheatBuffer || !*p) goto L_DONE; } while (*p!=':');
701 	func=p+1;
702 	p=getenv("EM_EASY_CHEATS");
703 	if (!p || strcasecmp(p,"enabled")!=0) {
704 		if (func-6<CheatBuffer) goto L_DONE;
705 		if (memcmp(func-6,"chEat",5)!=0) goto L_DONE;
706 	}
707 
708 	// Enable easy cheats for the whole process and even for child processes
709 	// (no need to type chEat): chEat:easy!
710 	if (strcmp(func,"easy")==0) {
711 		putenv((char*)"EM_EASY_CHEATS=enabled");
712 	}
713 
714 	// Stress test on/off: chEat:st!
715 	else if (strcmp(func,"st")==0) {
716 		GetView().SetViewFlags(GetView().GetViewFlags()^emView::VF_STRESS_TEST);
717 	}
718 
719 	// Popup-zoom on/off: chEat:pz!
720 	else if (strcmp(func,"pz")==0) {
721 		GetView().SetViewFlags(GetView().GetViewFlags()^emView::VF_POPUP_ZOOM);
722 	}
723 
724 	// Ego mode on/off: chEat:egomode!
725 	else if (strcmp(func,"egomode")==0) {
726 		GetView().SetViewFlags(GetView().GetViewFlags()^emView::VF_EGO_MODE);
727 	}
728 
729 	// StickMouseWhenNavigating on/off: chEat:smwn!
730 	else if (strcmp(func,"smwn")==0) {
731 		CoreConfig->StickMouseWhenNavigating.Invert();
732 		CoreConfig->Save();
733 	}
734 
735 	// EmulateMiddleButton on/off: chEat:emb!
736 	else if (strcmp(func,"emb")==0) {
737 		CoreConfig->EmulateMiddleButton.Invert();
738 		CoreConfig->Save();
739 	}
740 
741 	// PanFunction on/off: chEat:pan!
742 	else if (strcmp(func,"pan")==0) {
743 		CoreConfig->PanFunction.Invert();
744 		CoreConfig->Save();
745 	}
746 
747 	// Tree dump: chEat:td!
748 	else if (strcmp(func,"td")==0) {
749 		lib=NULL;
750 		try {
751 			lib=emTryOpenLib("emTreeDump",false);
752 			sym=emTryResolveSymbolFromLib(lib,"emTreeDumpFileFromRootContext");
753 			if (
754 				!((bool(*)(emRootContext*,const char *,emString*))sym)(
755 					&GetView().GetRootContext(),
756 					emGetInstallPath(EM_IDT_TMP,"emCore","debug.emTreeDump"),
757 					&str
758 				)
759 			) {
760 				throw emException("%s",str.Get());
761 			}
762 		}
763 		catch (const emException & exception) {
764 			emWarning("%s",exception.GetText().Get());
765 		}
766 		if (lib) emCloseLib(lib);
767 	}
768 
769 	// Debug log on/off: chEat:dlog!
770 	else if (strcmp(func,"dlog")==0) {
771 		emEnableDLog(!emIsDLogEnabled());
772 	}
773 
774 #if defined(_WIN32)
775 	// On Windows, simply press the Print key and find the screenshot in the
776 	// clipboard.
777 #else
778 	// Screenshot: chEat:ss!
779 	else if (strcmp(func,"ss")==0) {
780 		emString scPath;
781 		for (int scNum=0; ; scNum++) {
782 			scPath=emGetChildPath(
783 				emGetInstallPath(EM_IDT_TMP,"emCore"),
784 				emString::Format("emScreenshot%03d.xwd",scNum)
785 			);
786 			if (!emIsExistingPath(scPath)) break;
787 		}
788 		if (system(emString::Format("xwd -root > %s",scPath.Get()).Get())==-1) {
789 			emWarning("Could not run xwd: %s",emGetErrorText(errno).Get());
790 		}
791 		// Note: Sometimes xwdtopnm produces a black image (seen with
792 		// Netpbm 10.18.18). Better convert with gimp.
793 	}
794 #endif
795 
796 	// Crash by a segmentation fault: chEat:segfault!
797 	else if (strcmp(func,"segfault")==0) {
798 		*(volatile char*)NULL=0;
799 	}
800 
801 	// Crash by an arithmetic exception: chEat:divzero!
802 	else if (strcmp(func,"divzero")==0) {
803 		emSleepMS(255/func[strlen(func)]);
804 	}
805 
806 	// Call emFatalError: chEat:fatal!
807 	else if (strcmp(func,"fatal")==0) {
808 		emFatalError("You entered that cheat code!");
809 	}
810 
811 	// For application defined cheat codes.
812 	else GetView().DoCustomCheat(func);
813 
814 
815 L_DONE:
816 	ForwardInput(event,state);
817 }
818 
819 
820 //==============================================================================
821 //============================= emDefaultTouchVIF ==============================
822 //==============================================================================
823 
emDefaultTouchVIF(emView & view,emViewInputFilter * next)824 emDefaultTouchVIF::emDefaultTouchVIF(emView & view, emViewInputFilter * next)
825 	: emViewInputFilter(view,next)
826 {
827 	TouchCount=0;
828 	TouchesTime=GetView().GetInputClockMS();
829 	GestureState=0;
830 }
831 
832 
~emDefaultTouchVIF()833 emDefaultTouchVIF::~emDefaultTouchVIF()
834 {
835 }
836 
837 
GetTouchEventPriority(double touchX,double touchY) const838 double emDefaultTouchVIF::GetTouchEventPriority(double touchX, double touchY) const
839 {
840 	double pri;
841 
842 	if ((GetView().GetViewFlags()&emView::VF_NO_USER_NAVIGATION)!=0) {
843 		pri=2.0;
844 	}
845 	else {
846 		pri=3.0;
847 	}
848 	return emMax(pri,GetForwardTouchEventPriority(touchX,touchY));
849 }
850 
851 
Input(emInputEvent & event,const emInputState & state)852 void emDefaultTouchVIF::Input(emInputEvent & event, const emInputState & state)
853 {
854 	double pri,priForward;
855 	int i,j,oldState;
856 
857 	if (GestureState==0) {
858 		if (!event.IsTouchEvent() || state.GetTouchCount()<=0) {
859 			ForwardInput(event,state);
860 			return;
861 		}
862 		if ((GetView().GetViewFlags()&emView::VF_NO_USER_NAVIGATION)!=0) {
863 			pri=2.0;
864 		}
865 		else {
866 			pri=3.0;
867 		}
868 		priForward=GetForwardTouchEventPriority(state.GetTouchX(0),state.GetTouchY(0));
869 		if (priForward>pri) {
870 			ForwardInput(event,state);
871 			return;
872 		}
873 		TouchCount=0;
874 		TouchesTime=GetView().GetInputClockMS();
875 		WakeUp();
876 	}
877 
878 	//???:
879 	emDLog("emDefaultTouchVIF[%p]::Input:",(const void*)this);
880 	for (i=0; i<state.GetTouchCount(); i++) {
881 		emDLog(
882 			"  touch: id=%ld x=%g y=%g",
883 			(long)state.GetTouchId(i),
884 			state.GetTouchX(i),
885 			state.GetTouchY(i)
886 		);
887 	}
888 	//:???
889 
890 	if (event.IsTouchEvent()) event.Eat();
891 
892 	InputState=state;
893 
894 	NextTouches();
895 
896 	for (j=0; j<TouchCount; j++) Touches[j].Down=false;
897 	for (i=0; i<state.GetTouchCount(); i++) {
898 		for (j=0; j<TouchCount; j++) {
899 			if (Touches[j].Id==state.GetTouchId(i)) {
900 				Touches[j].Down=true;
901 				Touches[j].X=state.GetTouchX(i);
902 				Touches[j].Y=state.GetTouchY(i);
903 				break;
904 			}
905 		}
906 		if (j==TouchCount && j<MAX_TOUCH_COUNT) {
907 			Touches[j].Id=state.GetTouchId(i);
908 			Touches[j].MsTotal=0;
909 			Touches[j].MsSincePrev=0;
910 			Touches[j].Down=true;
911 			Touches[j].X=state.GetTouchX(i);
912 			Touches[j].Y=state.GetTouchY(i);
913 			Touches[j].PrevDown=false;
914 			Touches[j].PrevX=state.GetTouchX(i);
915 			Touches[j].PrevY=state.GetTouchY(i);
916 			Touches[j].DownX=state.GetTouchX(i);
917 			Touches[j].DownY=state.GetTouchY(i);
918 			TouchCount++;
919 		}
920 	}
921 
922 	for (;;) {
923 		oldState=GestureState;
924 		DoGesture();
925 		if (oldState==GestureState) break;
926 		NextTouches();
927 	}
928 
929 	ForwardInput(event,InputState);
930 }
931 
932 
Cycle()933 bool emDefaultTouchVIF::Cycle()
934 {
935 	int oldState;
936 
937 	do {
938 		oldState=GestureState;
939 		NextTouches();
940 		DoGesture();
941 	} while (oldState!=GestureState);
942 
943 	return GestureState!=0;
944 }
945 
946 
DoGesture()947 void emDefaultTouchVIF::DoGesture()
948 {
949 	enum {
950 		STATE_READY = 0,
951 		STATE_FIRST_DOWN,
952 		STATE_SCROLL,
953 		STATE_ZOOM_IN,
954 		STATE_ZOOM_OUT,
955 		STATE_FIRST_DOWN_UP,
956 		STATE_DOUBLE_DOWN,
957 		STATE_DOUBLE_DOWN_UP,
958 		STATE_TRIPLE_DOWN,
959 		STATE_TRIPLE_DOWN_UP,
960 		STATE_SECOND_DOWN,
961 		STATE_EMU_MOUSE_1,
962 		STATE_EMU_MOUSE_2,
963 		STATE_EMU_MOUSE_3,
964 		STATE_EMU_MOUSE_4,
965 		STATE_THIRD_DOWN,
966 		STATE_FOURTH_DOWN,
967 		STATE_FINISH
968 	};
969 	emPanel * p;
970 	double dx,dy;
971 
972 	switch (GestureState) {
973 	case STATE_READY:
974 		if (TouchCount>0) {
975 			GestureState=STATE_FIRST_DOWN;
976 		}
977 		break;
978 	case STATE_FIRST_DOWN:
979 		if (TouchCount>1) {
980 			GestureState=STATE_SECOND_DOWN;
981 			break;
982 		}
983 		if (!Touches[0].Down) {
984 			GestureState=STATE_FIRST_DOWN_UP;
985 			break;
986 		}
987 		if (GetTotalTouchMove(0)>20.0) {
988 			GetView().Scroll(-GetTotalTouchMoveX(0),-GetTotalTouchMoveY(0));
989 			GestureState=STATE_SCROLL;
990 			break;
991 		}
992 		if (Touches[0].MsTotal>250) {
993 			GestureState=STATE_ZOOM_IN;
994 			break;
995 		}
996 		break;
997 	case STATE_SCROLL:
998 		if (!Touches[0].Down) {
999 			GestureState=STATE_FINISH;
1000 			break;
1001 		}
1002 		GetView().Scroll(-GetTouchMoveX(0),-GetTouchMoveY(0));
1003 		break;
1004 	case STATE_ZOOM_IN:
1005 		if (!Touches[0].Down) {
1006 			GestureState=STATE_FINISH;
1007 			break;
1008 		}
1009 		GetView().Scroll(-GetTouchMoveX(0),-GetTouchMoveY(0));
1010 		GetView().Zoom(Touches[0].X,Touches[0].Y,exp(0.002*Touches[0].MsSincePrev));
1011 		break;
1012 	case STATE_ZOOM_OUT:
1013 		if (!Touches[0].Down) {
1014 			GestureState=STATE_FINISH;
1015 			break;
1016 		}
1017 		GetView().Scroll(-GetTouchMoveX(0),-GetTouchMoveY(0));
1018 		GetView().Zoom(Touches[0].X,Touches[0].Y,exp(-0.002*Touches[0].MsSincePrev));
1019 		break;
1020 	case STATE_FIRST_DOWN_UP:
1021 		if (TouchCount>1) {
1022 			RemoveTouch(0);
1023 			GestureState=STATE_DOUBLE_DOWN;
1024 			break;
1025 		}
1026 		if (Touches[0].MsTotal>250) {
1027 			GestureState=STATE_FINISH;
1028 			break;
1029 		}
1030 		break;
1031 	case STATE_DOUBLE_DOWN:
1032 		if (!Touches[0].Down) {
1033 			GestureState=STATE_DOUBLE_DOWN_UP;
1034 			break;
1035 		}
1036 		if (Touches[0].MsTotal>250) {
1037 			GestureState=STATE_ZOOM_OUT;
1038 			break;
1039 		}
1040 		break;
1041 	case STATE_DOUBLE_DOWN_UP:
1042 		if (TouchCount>1) {
1043 			RemoveTouch(0);
1044 			GestureState=STATE_TRIPLE_DOWN;
1045 			break;
1046 		}
1047 		if (Touches[0].MsTotal>250) {
1048 			p=GetView().GetFocusablePanelAt(Touches[0].X,Touches[0].Y,true);
1049 			if (!p) p=GetView().GetRootPanel();
1050 			if (p) GetView().VisitFullsized(p,true,false);
1051 			GestureState=STATE_FINISH;
1052 			break;
1053 		}
1054 		break;
1055 	case STATE_TRIPLE_DOWN:
1056 		if (!Touches[0].Down) {
1057 			GestureState=STATE_TRIPLE_DOWN_UP;
1058 			break;
1059 		}
1060 		if (Touches[0].MsTotal>250) {
1061 			GestureState=STATE_ZOOM_IN;
1062 			break;
1063 		}
1064 		break;
1065 	case STATE_TRIPLE_DOWN_UP:
1066 		if (TouchCount>1) {
1067 			RemoveTouch(0);
1068 			GestureState=STATE_DOUBLE_DOWN;
1069 			break;
1070 		}
1071 		if (Touches[0].MsTotal>250) {
1072 			p=GetView().GetFocusablePanelAt(Touches[0].X,Touches[0].Y,true);
1073 			if (!p) p=GetView().GetRootPanel();
1074 			if (p) GetView().VisitFullsized(p,true,true);
1075 			GestureState=STATE_FINISH;
1076 			break;
1077 		}
1078 		break;
1079 	case STATE_SECOND_DOWN:
1080 		if (TouchCount>2) {
1081 			GestureState=STATE_THIRD_DOWN;
1082 			break;
1083 		}
1084 		if (Touches[0].MsTotal>250 || !IsAnyTouchDown()) {
1085 			dx=Touches[1].X-Touches[0].X;
1086 			dy=Touches[1].Y-Touches[0].Y;
1087 			if (fabs(dx)>=fabs(dy)) {
1088 				if (dx>0) {
1089 					InputState.SetMouse(Touches[0].X,Touches[0].Y);
1090 					InputState.Set(EM_KEY_LEFT_BUTTON,true);
1091 					InputEvent.Setup(EM_KEY_LEFT_BUTTON,emString(),0,0);
1092 					ForwardInput(InputEvent,InputState);
1093 					GestureState=STATE_EMU_MOUSE_1;
1094 					break;
1095 				}
1096 				else {
1097 					InputState.SetMouse(Touches[0].X,Touches[0].Y);
1098 					InputState.Set(EM_KEY_RIGHT_BUTTON,true);
1099 					InputEvent.Setup(EM_KEY_RIGHT_BUTTON,emString(),0,0);
1100 					ForwardInput(InputEvent,InputState);
1101 					GestureState=STATE_EMU_MOUSE_2;
1102 					break;
1103 				}
1104 			}
1105 			else {
1106 				if (dy>0) {
1107 					InputState.SetMouse(Touches[0].X,Touches[0].Y);
1108 					InputState.Set(EM_KEY_SHIFT,true);
1109 					InputEvent.Setup(EM_KEY_SHIFT,emString(),0,0);
1110 					ForwardInput(InputEvent,InputState);
1111 					InputState.Set(EM_KEY_LEFT_BUTTON,true);
1112 					InputEvent.Setup(EM_KEY_LEFT_BUTTON,emString(),0,0);
1113 					ForwardInput(InputEvent,InputState);
1114 					GestureState=STATE_EMU_MOUSE_3;
1115 					break;
1116 				}
1117 				else {
1118 					InputState.SetMouse(Touches[0].X,Touches[0].Y);
1119 					InputState.Set(EM_KEY_CTRL,true);
1120 					InputEvent.Setup(EM_KEY_CTRL,emString(),0,0);
1121 					ForwardInput(InputEvent,InputState);
1122 					InputState.Set(EM_KEY_LEFT_BUTTON,true);
1123 					InputEvent.Setup(EM_KEY_LEFT_BUTTON,emString(),0,0);
1124 					ForwardInput(InputEvent,InputState);
1125 					GestureState=STATE_EMU_MOUSE_4;
1126 					break;
1127 				}
1128 			}
1129 		}
1130 		break;
1131 	case STATE_EMU_MOUSE_1:
1132 		InputState.SetMouse(Touches[0].X,Touches[0].Y);
1133 		if (!Touches[0].Down) {
1134 			InputState.Set(EM_KEY_LEFT_BUTTON,false);
1135 			InputEvent.Eat();
1136 			ForwardInput(InputEvent,InputState);
1137 			GestureState=STATE_FINISH;
1138 			break;
1139 		}
1140 		InputState.Set(EM_KEY_LEFT_BUTTON,true);
1141 		break;
1142 	case STATE_EMU_MOUSE_2:
1143 		InputState.SetMouse(Touches[0].X,Touches[0].Y);
1144 		if (!Touches[0].Down) {
1145 			InputState.Set(EM_KEY_RIGHT_BUTTON,false);
1146 			InputEvent.Eat();
1147 			ForwardInput(InputEvent,InputState);
1148 			GestureState=STATE_FINISH;
1149 			break;
1150 		}
1151 		InputState.Set(EM_KEY_RIGHT_BUTTON,true);
1152 		break;
1153 	case STATE_EMU_MOUSE_3:
1154 		InputState.SetMouse(Touches[0].X,Touches[0].Y);
1155 		if (!Touches[0].Down) {
1156 			InputState.Set(EM_KEY_SHIFT,false);
1157 			InputState.Set(EM_KEY_LEFT_BUTTON,false);
1158 			InputEvent.Eat();
1159 			ForwardInput(InputEvent,InputState);
1160 			GestureState=STATE_FINISH;
1161 			break;
1162 		}
1163 		InputState.Set(EM_KEY_SHIFT,true);
1164 		InputState.Set(EM_KEY_LEFT_BUTTON,true);
1165 		break;
1166 	case STATE_EMU_MOUSE_4:
1167 		InputState.SetMouse(Touches[0].X,Touches[0].Y);
1168 		if (!Touches[0].Down) {
1169 			InputState.Set(EM_KEY_CTRL,false);
1170 			InputState.Set(EM_KEY_LEFT_BUTTON,false);
1171 			InputEvent.Eat();
1172 			ForwardInput(InputEvent,InputState);
1173 			GestureState=STATE_FINISH;
1174 			break;
1175 		}
1176 		InputState.Set(EM_KEY_CTRL,true);
1177 		InputState.Set(EM_KEY_LEFT_BUTTON,true);
1178 		break;
1179 	case STATE_THIRD_DOWN:
1180 		if (TouchCount>3) {
1181 			GestureState=STATE_FOURTH_DOWN;
1182 			break;
1183 		}
1184 		if (!IsAnyTouchDown()) {
1185 			InputState.Set(EM_KEY_MENU,true);
1186 			InputEvent.Setup(EM_KEY_MENU,emString(),0,0);
1187 			ForwardInput(InputEvent,InputState);
1188 			InputState.Set(EM_KEY_MENU,false);
1189 			ForwardInput(InputEvent,InputState);
1190 			GestureState=STATE_FINISH;
1191 			break;
1192 		}
1193 		break;
1194 	case STATE_FOURTH_DOWN:
1195 		if (TouchCount>4) {
1196 			GestureState=STATE_FINISH;
1197 			break;
1198 		}
1199 		if (!IsAnyTouchDown()) {
1200 			GetView().ShowSoftKeyboard(!GetView().IsSoftKeyboardShown());
1201 			GestureState=STATE_FINISH;
1202 			break;
1203 		}
1204 		break;
1205 	case STATE_FINISH:
1206 		if (!IsAnyTouchDown()) {
1207 			ResetTouches();
1208 			GestureState=STATE_READY;
1209 		}
1210 		break;
1211 	}
1212 }
1213 
1214 
ResetTouches()1215 void emDefaultTouchVIF::ResetTouches()
1216 {
1217 	TouchCount=0;
1218 }
1219 
1220 
NextTouches()1221 void emDefaultTouchVIF::NextTouches()
1222 {
1223 	emUInt64 t;
1224 	int i,msSincePrev;
1225 
1226 	t=GetView().GetInputClockMS();
1227 	msSincePrev=(int)(t-TouchesTime);
1228 	TouchesTime=t;
1229 	for (i=TouchCount-1; i>=0; i--) {
1230 		Touches[i].MsTotal+=msSincePrev;
1231 		Touches[i].MsSincePrev=msSincePrev;
1232 		Touches[i].PrevDown=Touches[i].Down;
1233 		Touches[i].PrevX=Touches[i].X;
1234 		Touches[i].PrevY=Touches[i].Y;
1235 	}
1236 }
1237 
1238 
RemoveTouch(int index)1239 void emDefaultTouchVIF::RemoveTouch(int index)
1240 {
1241 	if (index>=0 && index<TouchCount) {
1242 		while (index<TouchCount-1) {
1243 			Touches[index]=Touches[index+1];
1244 			index++;
1245 		}
1246 		TouchCount--;
1247 	}
1248 }
1249 
1250 
IsAnyTouchDown() const1251 bool emDefaultTouchVIF::IsAnyTouchDown() const
1252 {
1253 	int i;
1254 
1255 	for (i=TouchCount-1; i>=0; i--) {
1256 		if (Touches[i].Down) return true;
1257 	}
1258 	return false;
1259 }
1260 
1261 
GetTouchMoveX(int index) const1262 double emDefaultTouchVIF::GetTouchMoveX(int index) const
1263 {
1264 	return Touches[index].X-Touches[index].PrevX;
1265 }
1266 
1267 
GetTouchMoveY(int index) const1268 double emDefaultTouchVIF::GetTouchMoveY(int index) const
1269 {
1270 	return Touches[index].Y-Touches[index].PrevY;
1271 }
1272 
1273 
GetTouchMove(int index) const1274 double emDefaultTouchVIF::GetTouchMove(int index) const
1275 {
1276 	double dx,dy;
1277 
1278 	dx=GetTouchMoveX(index);
1279 	dy=GetTouchMoveY(index);
1280 	return sqrt(dx*dx+dy*dy);
1281 }
1282 
1283 
GetTotalTouchMoveX(int index) const1284 double emDefaultTouchVIF::GetTotalTouchMoveX(int index) const
1285 {
1286 	return Touches[index].X-Touches[index].DownX;
1287 }
1288 
1289 
GetTotalTouchMoveY(int index) const1290 double emDefaultTouchVIF::GetTotalTouchMoveY(int index) const
1291 {
1292 	return Touches[index].Y-Touches[index].DownY;
1293 }
1294 
1295 
GetTotalTouchMove(int index) const1296 double emDefaultTouchVIF::GetTotalTouchMove(int index) const
1297 {
1298 	double dx,dy;
1299 
1300 	dx=GetTotalTouchMoveX(index);
1301 	dy=GetTotalTouchMoveY(index);
1302 	return sqrt(dx*dx+dy*dy);
1303 }
1304 
1305