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