Updated Indicator to use SetTexture
[platform/core/uifw/dali-adaptor.git] / adaptors / ecore / common / ecore-indicator-impl.cpp
1 /*
2  * Copyright (c) 2014 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */
17
18 // CLASS HEADER
19 #include "ecore-indicator-impl.h"
20
21 // EXTERNAL INCLUDES
22 #include <Ecore.h>
23 #include <Evas.h>
24
25 #include <sys/types.h>
26 #include <sys/stat.h>
27 #include <unistd.h>
28 #include <fcntl.h>
29 #include <errno.h>
30
31 #include <dali/public-api/images/native-image.h>
32 #include <dali/public-api/events/touch-event.h>
33 #include <dali/public-api/events/touch-point.h>
34 #include <dali/public-api/common/stage.h>
35 #include <dali/public-api/actors/blending.h>
36 #include <dali/public-api/shader-effects/shader-effect.h>
37 #include <dali/public-api/images/buffer-image.h>
38
39 #include <dali/integration-api/debug.h>
40
41 // INTERNAL INCLUDES
42 #include <adaptor-impl.h>
43 #include <accessibility-adaptor-impl.h>
44 #include <native-image-source.h>
45
46 using Dali::Vector4;
47
48 #if defined(DEBUG_ENABLED)
49 #define STATE_DEBUG_STRING(state) (state==DISCONNECTED?"DISCONNECTED":state==CONNECTED?"CONNECTED":"UNKNOWN")
50 #endif
51
52 namespace
53 {
54
55 const float SLIDING_ANIMATION_DURATION( 0.2f ); // 200 milli seconds
56 const float AUTO_INDICATOR_STAY_DURATION(3.0f); // 3 seconds
57 const float SHOWING_DISTANCE_HEIGHT_RATE(0.34f); // 20 pixels
58
59 enum
60 {
61   KEEP_SHOWING = -1,
62   HIDE_NOW = 0
63 };
64
65 const int NUM_GRADIENT_INTERVALS(5); // Number of gradient intervals
66 const float GRADIENT_ALPHA[NUM_GRADIENT_INTERVALS+1] = { 0.6f, 0.38f, 0.20f, 0.08f, 0.0f, 0.0f };
67
68 #define MAKE_SHADER(A)#A
69
70 const char* BACKGROUND_VERTEX_SHADER = MAKE_SHADER(
71   attribute mediump vec2 aPosition;
72   attribute mediump float aAlpha;
73   varying mediump float vAlpha;
74   uniform mediump mat4 uMvpMatrix;
75   uniform mediump vec3 uSize;
76
77   void main()
78   {
79     mediump vec4 vertexPosition = vec4( aPosition * uSize.xy, 0.0, 1.0 );
80     vertexPosition = uMvpMatrix * vertexPosition;
81
82     vAlpha = aAlpha;
83     gl_Position = vertexPosition;
84   }
85 );
86
87 const char* BACKGROUND_FRAGMENT_SHADER = MAKE_SHADER(
88   uniform lowp vec4 uColor;
89   varying mediump float vAlpha;
90
91   void main()
92   {
93     gl_FragColor = uColor;
94     gl_FragColor.a *= vAlpha;
95   }
96 );
97
98 const char* FOREGROUND_VERTEX_SHADER = DALI_COMPOSE_SHADER(
99   attribute mediump vec2 aPosition;\n
100   varying mediump vec2 vTexCoord;\n
101   uniform mediump mat4 uMvpMatrix;\n
102   uniform mediump vec3 uSize;\n
103   uniform mediump vec4 sTextureRect;\n
104   \n
105   void main()\n
106   {\n
107     gl_Position = uMvpMatrix * vec4(aPosition * uSize.xy, 0.0, 1.0);\n
108     vTexCoord = aPosition + vec2(0.5);\n
109   }\n
110 );
111
112 const char* FOREGROUND_FRAGMENT_SHADER = DALI_COMPOSE_SHADER(
113   varying mediump vec2 vTexCoord;\n
114   uniform sampler2D sTexture;\n
115   \n
116   void main()\n
117   {\n
118     gl_FragColor = texture2D( sTexture, vTexCoord );\n // the foreground does not apply actor color
119   }\n
120 );
121
122 const float OPAQUE_THRESHOLD(0.99f);
123 const float TRANSPARENT_THRESHOLD(0.05f);
124
125 // indicator service name
126 const char* INDICATOR_SERVICE_NAME("elm_indicator");
127
128 // Copied from ecore_evas_extn_engine.h
129
130 enum // opcodes
131 {
132    OP_RESIZE,
133    OP_SHOW,
134    OP_HIDE,
135    OP_FOCUS,
136    OP_UNFOCUS,
137    OP_UPDATE,
138    OP_UPDATE_DONE,
139    OP_SHM_REF0,
140    OP_SHM_REF1,
141    OP_SHM_REF2,
142    OP_PROFILE_CHANGE_REQUEST,
143    OP_PROFILE_CHANGE_DONE,
144    OP_EV_MOUSE_IN,
145    OP_EV_MOUSE_OUT,
146    OP_EV_MOUSE_UP,
147    OP_EV_MOUSE_DOWN,
148    OP_EV_MOUSE_MOVE,
149    OP_EV_MOUSE_WHEEL,
150    OP_EV_MULTI_UP,
151    OP_EV_MULTI_DOWN,
152    OP_EV_MULTI_MOVE,
153    OP_EV_KEY_UP,
154    OP_EV_KEY_DOWN,
155    OP_EV_HOLD,
156    OP_MSG_PARENT,
157    OP_MSG
158 };
159
160 // Copied from elm_conform.c
161
162 const int MSG_DOMAIN_CONTROL_INDICATOR( 0x10001 );
163 const int MSG_ID_INDICATOR_REPEAT_EVENT( 0x10002 );
164 const int MSG_ID_INDICATOR_ROTATION( 0x10003 );
165 const int MSG_ID_INDICATOR_OPACITY( 0X1004 );
166 const int MSG_ID_INDICATOR_TYPE( 0X1005 );
167 const int MSG_ID_INDICATOR_START_ANIMATION( 0X10006 );
168
169 struct IpcDataUpdate
170 {
171    int x, w, y, h;
172 };
173
174 struct IpcDataResize
175 {
176   int w, h;
177 };
178
179 struct IpcIndicatorDataAnimation
180 {
181   unsigned int xwin;
182   double       duration;
183 };
184
185 struct IpcDataEvMouseUp
186 {
187   int               b;
188   Evas_Button_Flags flags;
189   int               mask;
190   unsigned int      timestamp;
191   Evas_Event_Flags  event_flags;
192
193   IpcDataEvMouseUp(unsigned long timestamp)
194   : b(1),
195     flags(EVAS_BUTTON_NONE),
196     mask(0),
197     timestamp(static_cast<unsigned int>(timestamp)),
198     event_flags(EVAS_EVENT_FLAG_NONE)
199   {
200   }
201 };
202
203 struct IpcDataEvMouseDown
204 {
205   int                b;
206   Evas_Button_Flags  flags;
207   int                mask;
208   unsigned int       timestamp;
209   Evas_Event_Flags   event_flags;
210
211   IpcDataEvMouseDown(unsigned long timestamp)
212   : b(1),
213     flags(EVAS_BUTTON_NONE),
214     mask(0),
215     timestamp(static_cast<unsigned int>(timestamp)),
216     event_flags(EVAS_EVENT_FLAG_NONE)
217   {
218   }
219 };
220
221 struct IpcDataEvMouseMove
222 {
223   int                x, y;
224   Evas_Button_Flags  flags;
225   int                mask;
226   unsigned int       timestamp;
227   Evas_Event_Flags   event_flags;
228
229   IpcDataEvMouseMove(const Dali::TouchPoint& touchPoint, unsigned long timestamp)
230   : x(static_cast<Evas_Coord>(touchPoint.local.x)),
231     y(static_cast<Evas_Coord>(touchPoint.local.y)),
232     flags(EVAS_BUTTON_NONE),
233     mask(0),
234     timestamp(static_cast<unsigned int>(timestamp)),
235     event_flags(EVAS_EVENT_FLAG_NONE)
236   {
237   }
238 };
239
240 struct IpcDataEvMouseOut
241 {
242   unsigned int     timestamp;
243   int              mask;
244   Evas_Event_Flags event_flags;
245
246   IpcDataEvMouseOut(unsigned long timestamp)
247   : timestamp(static_cast<unsigned int>(timestamp)),
248     mask(0),
249     event_flags(EVAS_EVENT_FLAG_NONE)
250   {
251   }
252 };
253
254 } // anonymous namespace
255
256
257 namespace Dali
258 {
259 namespace Internal
260 {
261 namespace Adaptor
262 {
263 #if defined(DEBUG_ENABLED)
264 Debug::Filter* gIndicatorLogFilter = Debug::Filter::New(Debug::Concise, false, "LOG_INDICATOR");
265 #endif
266
267
268 Indicator::LockFile::LockFile(const std::string filename)
269 : mFilename(filename),
270   mErrorThrown(false)
271 {
272   mFileDescriptor = open(filename.c_str(), O_RDWR);
273   if( mFileDescriptor == -1 )
274   {
275     mFileDescriptor = 0;
276     mErrorThrown = true;
277     DALI_LOG_ERROR( "### Cannot open %s for indicator lock ###\n", mFilename.c_str() );
278   }
279 }
280
281 Indicator::LockFile::~LockFile()
282 {
283   // Closing file descriptor also unlocks file.
284   close( mFileDescriptor );
285 }
286
287 bool Indicator::LockFile::Lock()
288 {
289   DALI_LOG_TRACE_METHOD( gIndicatorLogFilter );
290
291   bool locked = false;
292   if( mFileDescriptor > 0 )
293   {
294     if( lockf( mFileDescriptor, F_LOCK, 0 ) == 0 ) // Note, operation may block.
295     {
296       locked = true;
297     }
298     else
299     {
300       if( errno == EBADF )
301       {
302         // file descriptor is no longer valid or not writable
303         mFileDescriptor = 0;
304         mErrorThrown = true;
305         DALI_LOG_ERROR( "### Cannot lock indicator: bad file descriptor for %s ###\n", mFilename.c_str() );
306       }
307     }
308   }
309
310   return locked;
311 }
312
313 void Indicator::LockFile::Unlock()
314 {
315   DALI_LOG_TRACE_METHOD( gIndicatorLogFilter );
316   if( lockf( mFileDescriptor, F_ULOCK, 0 ) != 0 )
317   {
318     if( errno == EBADF )
319     {
320       // file descriptor is no longer valid or not writable
321       mFileDescriptor = 0;
322       mErrorThrown = true;
323       DALI_LOG_ERROR( "### Cannot unlock indicator: bad file descriptor for %s\n", mFilename.c_str() );
324     }
325   }
326 }
327
328 bool Indicator::LockFile::RetrieveAndClearErrorStatus()
329 {
330   bool error = mErrorThrown;
331   mErrorThrown = false;
332   return error;
333 }
334
335 Indicator::ScopedLock::ScopedLock(LockFile* lockFile)
336 : mLockFile(lockFile),
337   mLocked(false)
338 {
339   if(mLockFile)
340   {
341     mLocked = mLockFile->Lock();
342   }
343 }
344
345 Indicator::ScopedLock::~ScopedLock()
346 {
347   if( mLockFile )
348   {
349     mLockFile->Unlock();
350   }
351 }
352
353 bool Indicator::ScopedLock::IsLocked()
354 {
355   return mLocked;
356 }
357
358 Indicator::Indicator( Adaptor* adaptor, Dali::Window::WindowOrientation orientation, IndicatorInterface::Observer* observer )
359 : mPixmap( 0 ),
360   mGestureDetected( false ),
361   mConnection( this ),
362   mOpacityMode( Dali::Window::OPAQUE ),
363   mState( DISCONNECTED ),
364   mAdaptor(adaptor),
365   mServerConnection( NULL ),
366   mObserver( observer ),
367   mOrientation( orientation ),
368   mImageWidth( 0 ),
369   mImageHeight( 0 ),
370   mVisible( Dali::Window::INVISIBLE ),
371   mIsShowing( true ),
372   mIsAnimationPlaying( false ),
373   mCurrentSharedFile( 0 ),
374   mBackgroundVisible( false )
375 {
376   mIndicatorContentActor = Dali::Actor::New();
377   mIndicatorContentActor.SetParentOrigin( ParentOrigin::TOP_CENTER );
378   mIndicatorContentActor.SetAnchorPoint( AnchorPoint::TOP_CENTER );
379
380   // Indicator image handles the touch event including "leave"
381   mIndicatorContentActor.SetLeaveRequired( true );
382   mIndicatorContentActor.TouchedSignal().Connect( this, &Indicator::OnTouched );
383   mIndicatorContentActor.SetColor( Color::BLACK );
384
385   mIndicatorActor = Dali::Actor::New();
386   mIndicatorActor.Add( mIndicatorContentActor );
387
388   // Event handler to find out flick down gesture
389   mEventActor = Dali::Actor::New();
390   mEventActor.SetParentOrigin( ParentOrigin::TOP_CENTER );
391   mEventActor.SetAnchorPoint( AnchorPoint::TOP_CENTER );
392   mIndicatorActor.Add( mEventActor );
393
394   // Attach pan gesture to find flick down during hiding.
395   // It can prevent the problem that scrollview gets pan gesture even indicator area is touched,
396   // since it consumes the pan gesture in advance.
397   mPanDetector = Dali::PanGestureDetector::New();
398   mPanDetector.DetectedSignal().Connect( this, &Indicator::OnPan );
399   mPanDetector.Attach( mEventActor );
400
401   Open( orientation );
402
403   // register indicator to accessibility adaptor
404   Dali::AccessibilityAdaptor accessibilityAdaptor = AccessibilityAdaptor::Get();
405   if(accessibilityAdaptor)
406   {
407     AccessibilityAdaptor::GetImplementation( accessibilityAdaptor ).SetIndicator( this );
408   }
409   // hide the indicator by default
410   mIndicatorActor.SetVisible( false );
411 }
412
413 Indicator::~Indicator()
414 {
415   if(mEventActor)
416   {
417     mEventActor.TouchedSignal().Disconnect( this, &Indicator::OnTouched );
418   }
419   Disconnect();
420 }
421
422 void Indicator::SetAdaptor(Adaptor* adaptor)
423 {
424   mAdaptor = adaptor;
425   mIndicatorBuffer->SetAdaptor( adaptor );
426 }
427
428 Dali::Actor Indicator::GetActor()
429 {
430   return mIndicatorActor;
431 }
432
433 void Indicator::Open( Dali::Window::WindowOrientation orientation )
434 {
435   DALI_LOG_TRACE_METHOD( gIndicatorLogFilter );
436
437   // Calls from Window should be set up to ensure we are in a
438   // disconnected state before opening a second time.
439   DALI_ASSERT_DEBUG( mState == DISCONNECTED );
440
441   mOrientation = orientation;
442
443   Connect();
444
445   // Change background visibility depending on orientation
446   if( mOrientation == Dali::Window::LANDSCAPE || mOrientation == Dali::Window::LANDSCAPE_INVERSE  )
447   {
448     if( mBackgroundRenderer )
449     {
450       mIndicatorContentActor.RemoveRenderer( mBackgroundRenderer );
451       mBackgroundVisible = false;
452     }
453   }
454   else
455   {
456     SetOpacityMode( mOpacityMode );
457   }
458 }
459
460 void Indicator::Close()
461 {
462   DALI_LOG_TRACE_METHOD_FMT( gIndicatorLogFilter, "State: %s", STATE_DEBUG_STRING(mState) );
463
464   if( mState == CONNECTED )
465   {
466     Disconnect();
467     if( mObserver != NULL )
468     {
469       mObserver->IndicatorClosed( this );
470     }
471   }
472
473   Dali::Texture emptyTexture;
474   SetForegroundImage( emptyTexture );
475 }
476
477 void Indicator::SetOpacityMode( Dali::Window::IndicatorBgOpacity mode )
478 {
479   mOpacityMode = mode;
480
481   Dali::Geometry geometry = CreateBackgroundGeometry();
482   if( geometry )
483   {
484     if( mBackgroundRenderer )
485     {
486       if( mBackgroundRenderer.GetGeometry() != geometry )
487       {
488         mBackgroundRenderer.SetGeometry( geometry );
489       }
490     }
491     else
492     {
493       if( !mBackgroundShader )
494       {
495         mBackgroundShader = Dali::Shader::New( BACKGROUND_VERTEX_SHADER, BACKGROUND_FRAGMENT_SHADER, Dali::Shader::HINT_OUTPUT_IS_TRANSPARENT );
496       }
497
498       mBackgroundRenderer = Dali::Renderer::New( geometry, mBackgroundShader );
499     }
500
501     if( !mBackgroundVisible )
502     {
503       mIndicatorContentActor.AddRenderer( mBackgroundRenderer );
504       mBackgroundVisible = true;
505     }
506   }
507   else if( mBackgroundRenderer )
508   {
509     mIndicatorContentActor.RemoveRenderer( mBackgroundRenderer );
510     mBackgroundVisible = false;
511   }
512 }
513
514 void Indicator::SetVisible( Dali::Window::IndicatorVisibleMode visibleMode, bool forceUpdate )
515 {
516   if ( visibleMode != mVisible || forceUpdate )
517   {
518     // If we were previously hidden, then we should update the image data before we display the indicator
519     if ( mVisible == Dali::Window::INVISIBLE )
520     {
521       UpdateImageData( mCurrentSharedFile );
522     }
523     if ( visibleMode != Dali::Window::INVISIBLE )
524     {
525       mIndicatorActor.SetVisible( true );
526     }
527
528     mVisible = visibleMode;
529
530     if( mForegroundRenderer && mForegroundRenderer.GetTextures().GetImage( 0u ) )
531     {
532       if( CheckVisibleState() && mVisible == Dali::Window::AUTO )
533       {
534         // hide indicator
535         ShowIndicator( AUTO_INDICATOR_STAY_DURATION /* stay n sec */ );
536       }
537       else if( CheckVisibleState() && mVisible == Dali::Window::VISIBLE )
538       {
539         // show indicator
540         ShowIndicator( KEEP_SHOWING );
541       }
542       else
543       {
544         // hide indicator
545         ShowIndicator( HIDE_NOW );
546       }
547     }
548   }
549 }
550
551 bool Indicator::IsConnected()
552 {
553   return ( mState == CONNECTED );
554 }
555
556 bool Indicator::SendMessage( int messageDomain, int messageId, const void *data, int size )
557 {
558  if(IsConnected())
559  {
560    return mServerConnection->SendEvent( OP_MSG, messageDomain, messageId, data, size );
561  }
562  else
563  {
564    return false;
565  }
566 }
567
568 bool Indicator::OnTouched(Dali::Actor indicator, const Dali::TouchEvent& touchEvent)
569 {
570   if( mServerConnection )
571   {
572     const TouchPoint& touchPoint = touchEvent.GetPoint( 0 );
573
574     // Send touch event to indicator server when indicator is showing
575     if( CheckVisibleState() || mIsShowing )
576     {
577       switch( touchPoint.state )
578       {
579         case Dali::PointState::DOWN:
580         {
581           IpcDataEvMouseMove ipcMove( touchPoint, touchEvent.time );
582           IpcDataEvMouseDown ipcDown( touchEvent.time );
583           mServerConnection->SendEvent( OP_EV_MOUSE_MOVE, &ipcMove, sizeof(ipcMove) );
584           mServerConnection->SendEvent( OP_EV_MOUSE_DOWN, &ipcDown, sizeof(ipcDown) );
585
586           if( mVisible == Dali::Window::AUTO )
587           {
588             // Stop hiding indicator
589             ShowIndicator( KEEP_SHOWING );
590           }
591         }
592         break;
593
594         case Dali::PointState::MOTION:
595         {
596           IpcDataEvMouseMove ipcMove( touchPoint, touchEvent.time );
597           mServerConnection->SendEvent( OP_EV_MOUSE_MOVE, &ipcMove, sizeof(ipcMove) );
598         }
599         break;
600
601         case Dali::PointState::UP:
602         {
603           IpcDataEvMouseUp ipcUp( touchEvent.time );
604           mServerConnection->SendEvent( OP_EV_MOUSE_UP, &ipcUp, sizeof(ipcUp) );
605
606           if( mVisible == Dali::Window::AUTO )
607           {
608             // Hide indicator
609             ShowIndicator( 0.5f /* hide after 0.5 sec */ );
610           }
611         }
612         break;
613
614         case Dali::TouchPoint::Leave:
615         {
616           IpcDataEvMouseMove ipcMove( touchPoint, touchEvent.time );
617           mServerConnection->SendEvent( OP_EV_MOUSE_MOVE, &ipcMove, sizeof(ipcMove) );
618           IpcDataEvMouseUp ipcOut( touchEvent.time );
619           mServerConnection->SendEvent( OP_EV_MOUSE_OUT, &ipcOut, sizeof(ipcOut) );
620         }
621         break;
622
623         default:
624           break;
625       }
626
627       return true;
628     }
629   }
630
631   return false;
632 }
633
634 bool Indicator::Connect()
635 {
636   DALI_LOG_TRACE_METHOD( gIndicatorLogFilter );
637
638   DALI_ASSERT_DEBUG( mState == DISCONNECTED );
639
640   bool connected = false;
641
642   mServerConnection = new ServerConnection( INDICATOR_SERVICE_NAME, 0, false, this );
643   if( mServerConnection )
644   {
645     connected = mServerConnection->IsConnected();
646     if( ! connected )
647     {
648       delete mServerConnection;
649       mServerConnection = NULL;
650     }
651   }
652
653   if( !connected )
654   {
655     StartReconnectionTimer();
656   }
657   else
658   {
659     mState = CONNECTED;
660   }
661
662   return connected;
663 }
664
665 void Indicator::StartReconnectionTimer()
666 {
667   if( ! mReconnectTimer )
668   {
669     mReconnectTimer = Dali::Timer::New(1000);
670     mConnection.DisconnectAll();
671     mReconnectTimer.TickSignal().Connect( mConnection, &Indicator::OnReconnectTimer );
672   }
673   mReconnectTimer.Start();
674 }
675
676 bool Indicator::OnReconnectTimer()
677 {
678   bool retry = false;
679
680   if( mState == DISCONNECTED )
681   {
682     if( !Connect() )
683     {
684       retry = true;
685     }
686   }
687
688   return retry;
689 }
690
691 void Indicator::Disconnect()
692 {
693   DALI_LOG_TRACE_METHOD( gIndicatorLogFilter );
694
695   mState = DISCONNECTED;
696
697   delete mServerConnection;
698   mServerConnection = NULL;
699
700   ClearSharedFileInfo();
701 }
702
703 void Indicator::Resize( int width, int height )
704 {
705   if( width < 1 )
706   {
707     width = 1;
708   }
709   if( height < 1 )
710   {
711     height = 1;
712   }
713
714   if( mImageWidth != width || mImageHeight != height )
715   {
716     mImageWidth = width;
717     mImageHeight = height;
718
719     mIndicatorContentActor.SetSize( mImageWidth, mImageHeight );
720     mIndicatorActor.SetSize( mImageWidth, mImageHeight );
721     mEventActor.SetSize(mImageWidth, mImageHeight);
722   }
723 }
724
725 void Indicator::SetLockFileInfo( Ecore_Ipc_Event_Server_Data *epcEvent )
726 {
727   DALI_LOG_TRACE_METHOD( gIndicatorLogFilter );
728
729   // epcEvent->ref == w
730   // epcEvent->ref_to == h
731   // epcEvent->response == buffer num
732   // epcEvent->data = lockfile + nul byte
733
734   if( (epcEvent->ref > 0) && (epcEvent->ref_to > 0) && (epcEvent->data) &&
735       (((unsigned char *)epcEvent->data)[epcEvent->size - 1] == 0) )
736   {
737     int n = epcEvent->response;
738
739     if( n >= 0 && n < SHARED_FILE_NUMBER )
740     {
741       mCurrentSharedFile = n;
742
743       mSharedFileInfo[n].mImageWidth  = epcEvent->ref;
744       mSharedFileInfo[n].mImageHeight = epcEvent->ref_to;
745
746       mSharedFileInfo[n].mLockFileName.clear();
747
748       mSharedFileInfo[n].mLockFileName = static_cast< char* >( epcEvent->data );
749
750       DALI_LOG_INFO( gIndicatorLogFilter, Debug::General, "SetLockFileInfo: buffer num = %d, w = %d, h = %d, lock = %s\n",
751                      n, mSharedFileInfo[n].mImageWidth, mSharedFileInfo[n].mImageHeight, mSharedFileInfo[n].mLockFileName.c_str() );
752     }
753   }
754 }
755
756 void Indicator::SetSharedImageInfo( Ecore_Ipc_Event_Server_Data *epcEvent )
757 {
758   DALI_LOG_TRACE_METHOD( gIndicatorLogFilter );
759
760   // epcEvent->ref == shm id
761   // epcEvent->ref_to == shm num
762   // epcEvent->response == buffer num
763   // epcEvent->data = shm ref string + nul byte
764
765   if ( (epcEvent->data) &&
766        (epcEvent->size > 0) &&
767        (((unsigned char *)epcEvent->data)[epcEvent->size - 1] == 0) )
768   {
769     int n = epcEvent->response;
770
771     if( n >= 0 && n < SHARED_FILE_NUMBER )
772     {
773       mCurrentSharedFile = n;
774
775       mSharedFileInfo[n].mSharedFileName.clear();
776
777       mSharedFileInfo[n].mSharedFileName = static_cast< char* >( epcEvent->data );
778
779       mSharedFileInfo[n].mSharedFileID = epcEvent->ref;
780       mSharedFileInfo[n].mSharedFileNumber = epcEvent->ref_to;
781
782       DALI_LOG_INFO( gIndicatorLogFilter, Debug::General, "SetSharedImageInfo: buffer num %d, shared file = %s, id = %d, num = %d\n",
783                      n, mSharedFileInfo[n].mSharedFileName.c_str(), mSharedFileInfo[n].mSharedFileID, mSharedFileInfo[n].mSharedFileNumber );
784     }
785   }
786 }
787
788 void Indicator::LoadSharedImage( Ecore_Ipc_Event_Server_Data *epcEvent )
789 {
790   DALI_LOG_TRACE_METHOD( gIndicatorLogFilter );
791
792   // epcEvent->ref == alpha
793   // epcEvent->ref_to == sys
794   // epcEvent->response == buffer num
795
796   int n = epcEvent->response;
797
798   if( n >= 0 && n < SHARED_FILE_NUMBER )
799   {
800     mCurrentSharedFile = n;
801
802     delete mSharedFileInfo[n].mSharedFile;
803     mSharedFileInfo[n].mSharedFile = NULL;
804
805     delete mSharedFileInfo[n].mLock;
806     mSharedFileInfo[n].mLock = NULL;
807
808     std::stringstream sharedFileID;
809     std::stringstream sharedFileNumber;
810
811     sharedFileID << mSharedFileInfo[n].mSharedFileID;
812     sharedFileNumber << mSharedFileInfo[n].mSharedFileNumber;
813
814     std::string sharedFilename = "/" + mSharedFileInfo[n].mSharedFileName + "-" + sharedFileID.str() + "." + sharedFileNumber.str();
815
816     DALI_LOG_INFO( gIndicatorLogFilter, Debug::General, "LoadSharedImage: file name = %s\n", sharedFilename.c_str() );
817
818     mSharedFileInfo[n].mSharedFile = SharedFile::New( sharedFilename.c_str(), mSharedFileInfo[n].mImageWidth * mSharedFileInfo[n].mImageWidth * 4, true );
819     if( mSharedFileInfo[n].mSharedFile != NULL )
820     {
821       mSharedFileInfo[n].mLock = new Indicator::LockFile( mSharedFileInfo[n].mLockFileName );
822       if( mSharedFileInfo[n].mLock->RetrieveAndClearErrorStatus() )
823       {
824         DALI_LOG_ERROR( "### Indicator error: Cannot open lock file %s ###\n", mSharedFileInfo[n].mLockFileName.c_str() );
825       }
826
827       CreateNewImage( n );
828
829       if( CheckVisibleState() )
830       {
831         // set default indicator type (enable the quick panel)
832         OnIndicatorTypeChanged( INDICATOR_TYPE_1 );
833       }
834       else
835       {
836         // set default indicator type (disable the quick panel)
837         OnIndicatorTypeChanged( INDICATOR_TYPE_2 );
838       }
839
840       SetVisible(mVisible, true);
841     }
842   }
843 }
844
845 void Indicator::LoadPixmapImage( Ecore_Ipc_Event_Server_Data *epcEvent )
846 {
847   DALI_LOG_TRACE_METHOD( gIndicatorLogFilter );
848
849   // epcEvent->ref == w
850   // epcEvent->ref_to == h
851   // epcEvent->response == alpha
852   // epcEvent->data = pixmap id
853
854   if( ( epcEvent->data ) &&
855       (epcEvent->size >= (int)sizeof(PixmapId)) )
856   {
857     ClearSharedFileInfo();
858
859     if( (epcEvent->ref > 0) && (epcEvent->ref_to > 0) )
860     {
861       mImageWidth  = epcEvent->ref;
862       mImageHeight = epcEvent->ref_to;
863
864       mPixmap = *(static_cast<PixmapId*>(epcEvent->data));
865       CreateNewPixmapImage();
866
867       if( CheckVisibleState() )
868       {
869         // set default indicator type (enable the quick panel)
870         OnIndicatorTypeChanged( INDICATOR_TYPE_1 );
871       }
872       else
873       {
874         // set default indicator type (disable the quick panel)
875         OnIndicatorTypeChanged( INDICATOR_TYPE_2 );
876       }
877
878       SetVisible(mVisible, true);
879     }
880   }
881 }
882
883 void Indicator::UpdateImageData( int bufferNumber )
884 {
885   DALI_LOG_TRACE_METHOD_FMT( gIndicatorLogFilter, "State: %s  mVisible: %s", STATE_DEBUG_STRING(mState), mVisible?"T":"F" );
886
887   if( mState == CONNECTED && mVisible )
888   {
889     if(mPixmap == 0)
890     {
891       // in case of shm indicator (not pixmap), not sure we can skip it when mIsShowing is false
892       CopyToBuffer( bufferNumber );
893     }
894     else
895     {
896       if(mIsShowing)
897       {
898         mAdaptor->RequestUpdateOnce();
899       }
900     }
901   }
902 }
903
904 bool Indicator::CopyToBuffer( int bufferNumber )
905 {
906   bool success = false;
907
908   if( mSharedFileInfo[bufferNumber].mLock )
909   {
910     Indicator::ScopedLock scopedLock(mSharedFileInfo[bufferNumber].mLock);
911     if( mSharedFileInfo[bufferNumber].mLock->RetrieveAndClearErrorStatus() )
912     {
913       // Do nothing here.
914     }
915     else if( scopedLock.IsLocked() )
916     {
917       unsigned char *src = mSharedFileInfo[bufferNumber].mSharedFile->GetAddress();
918       size_t size = mSharedFileInfo[bufferNumber].mImageWidth * mSharedFileInfo[bufferNumber].mImageHeight * 4;
919
920       if( mIndicatorBuffer->UpdatePixels( src, size ) )
921       {
922         mAdaptor->RequestUpdateOnce();
923         success = true;
924       }
925     }
926   }
927
928   return success;
929 }
930
931 void Indicator::CreateNewPixmapImage()
932 {
933   DALI_LOG_TRACE_METHOD_FMT( gIndicatorLogFilter, "W:%d H:%d", mImageWidth, mImageHeight );
934   Dali::NativeImageSourcePtr nativeImageSource = Dali::NativeImageSource::New( mPixmap );
935
936   if( nativeImageSource )
937   {
938     Dali::Texture texture = Dali::Texture::New( *nativeImageSource );
939     SetForegroundImage( texture );
940     mIndicatorContentActor.SetSize( mImageWidth, mImageHeight );
941     mIndicatorActor.SetSize( mImageWidth, mImageHeight );
942     mEventActor.SetSize( mImageWidth, mImageHeight );
943   }
944   else
945   {
946     DALI_LOG_WARNING("### Cannot create indicator image - disconnecting ###\n");
947     Disconnect();
948     if( mObserver != NULL )
949     {
950       mObserver->IndicatorClosed( this );
951     }
952     // Don't do connection in this callback - strange things happen!
953     StartReconnectionTimer();
954   }
955 }
956
957 void Indicator::CreateNewImage( int bufferNumber )
958 {
959   DALI_LOG_TRACE_METHOD_FMT( gIndicatorLogFilter, "W:%d H:%d", mSharedFileInfo[bufferNumber].mImageWidth, mSharedFileInfo[bufferNumber].mImageHeight );
960   mIndicatorBuffer = new IndicatorBuffer( mAdaptor, mSharedFileInfo[bufferNumber].mImageWidth, mSharedFileInfo[bufferNumber].mImageHeight, Pixel::BGRA8888 );
961   bool success = false;
962
963   if( CopyToBuffer( bufferNumber ) ) // Only create images if we have valid image buffer
964   {
965     Dali::Texture texture = Dali::Texture::New( mIndicatorBuffer->GetNativeImage() );
966     if( texture )
967     {
968       SetForegroundImage( texture );
969       success = true;
970     }
971   }
972
973   if( !success )
974   {
975     DALI_LOG_WARNING("### Cannot create indicator image - disconnecting ###\n");
976     Disconnect();
977     if( mObserver != NULL )
978     {
979       mObserver->IndicatorClosed( this );
980     }
981     // Don't do connection in this callback - strange things happen!
982     StartReconnectionTimer();
983   }
984 }
985
986 Dali::Geometry Indicator::CreateBackgroundGeometry()
987 {
988   switch( mOpacityMode )
989   {
990     case Dali::Window::TRANSLUCENT:
991       if( !mTranslucentGeometry )
992       {
993         // Construct 5 interval mesh
994         // 0  +---+  1
995         //    | \ |
996         // 2  +---+  3
997         //    | \ |
998         // 4  +---+  5
999         //    | \ |
1000         // 6  +---+  7
1001         //    | \ |
1002         // 8  +---+  9
1003         //    | \ |
1004         // 10 +---+  11
1005
1006         // Create vertices
1007         struct BackgroundVertex
1008         {
1009           Vector2 mPosition;
1010           float   mAlpha;
1011         };
1012
1013         unsigned int numVertices = 2 * ( NUM_GRADIENT_INTERVALS + 1 );
1014         BackgroundVertex vertices[ numVertices ];
1015
1016         float d = -0.5f;
1017         float delta = 1.0f / NUM_GRADIENT_INTERVALS;
1018         BackgroundVertex* currentVertex = vertices;
1019         for( int y = 0; y < NUM_GRADIENT_INTERVALS + 1; ++y, d += delta )
1020         {
1021           currentVertex->mPosition = Vector2( -0.5f, d );
1022           currentVertex->mAlpha = GRADIENT_ALPHA[ y ];
1023           currentVertex++;
1024
1025           currentVertex->mPosition = Vector2( 0.5f, d );
1026           currentVertex->mAlpha = GRADIENT_ALPHA[ y ];
1027           currentVertex++;
1028         }
1029
1030         // Create indices
1031         unsigned int numIndices = 2 * 3 * NUM_GRADIENT_INTERVALS;
1032         unsigned short indices[ numIndices ];
1033
1034         unsigned short* currentIndex = indices;
1035         for( int y = 0; y < NUM_GRADIENT_INTERVALS; ++y )
1036         {
1037           *currentIndex++ = (2 * y);
1038           *currentIndex++ = (2 * y) + 3;
1039           *currentIndex++ = (2 * y) + 1;
1040
1041           *currentIndex++ = (2 * y);
1042           *currentIndex++ = (2 * y) + 2;
1043           *currentIndex++ = (2 * y) + 3;
1044         }
1045
1046         Dali::Property::Map vertexFormat;
1047         vertexFormat[ "aPosition" ] = Dali::Property::VECTOR2;
1048         vertexFormat[ "aAlpha" ] = Dali::Property::FLOAT;
1049         Dali::PropertyBuffer vertexPropertyBuffer = Dali::PropertyBuffer::New( vertexFormat );
1050         vertexPropertyBuffer.SetData( vertices, numVertices );
1051
1052         // Create the geometry object
1053         mTranslucentGeometry = Dali::Geometry::New();
1054         mTranslucentGeometry.AddVertexBuffer( vertexPropertyBuffer );
1055         mTranslucentGeometry.SetIndexBuffer( &indices[0], numIndices );
1056       }
1057
1058       return mTranslucentGeometry;
1059     case Dali::Window::OPAQUE:
1060
1061       if( !mSolidGeometry )
1062       {
1063         // Create vertices
1064         struct BackgroundVertex
1065         {
1066           Vector2 mPosition;
1067           float   mAlpha;
1068         };
1069
1070         BackgroundVertex vertices[ 4 ] = { { Vector2( -0.5f, -0.5f ), 1.0f }, { Vector2( 0.5f, -0.5f ), 1.0f },
1071                                            { Vector2( -0.5f,  0.5f ), 1.0f }, { Vector2( 0.5f,  0.5f ), 1.0f } };
1072
1073         // Create indices
1074         unsigned short indices[ 6 ] = { 0, 3, 1, 0, 2, 3 };
1075
1076         Dali::Property::Map vertexFormat;
1077         vertexFormat[ "aPosition" ] = Dali::Property::VECTOR2;
1078         vertexFormat[ "aAlpha" ] = Dali::Property::FLOAT;
1079         Dali::PropertyBuffer vertexPropertyBuffer = Dali::PropertyBuffer::New( vertexFormat );
1080         vertexPropertyBuffer.SetData( vertices, 4 );
1081
1082
1083         // Create the geometry object
1084         mSolidGeometry = Dali::Geometry::New();
1085         mSolidGeometry.AddVertexBuffer( vertexPropertyBuffer );
1086         mSolidGeometry.SetIndexBuffer( &indices[0], 6 );
1087       }
1088
1089       return mSolidGeometry;
1090     case Dali::Window::TRANSPARENT:
1091       break;
1092   }
1093
1094   return Dali::Geometry();
1095 }
1096
1097 void Indicator::SetForegroundImage( Dali::Texture texture )
1098 {
1099   if( !mForegroundRenderer && texture )
1100   {
1101     // Create Shader
1102     Dali::Shader shader = Dali::Shader::New( FOREGROUND_VERTEX_SHADER, FOREGROUND_FRAGMENT_SHADER );
1103
1104     // Create renderer from geometry and material
1105     Dali::Geometry quad = Dali::Geometry::QUAD();
1106     mForegroundRenderer = Dali::Renderer::New( quad, shader );
1107     // Make sure the foreground stays in front of the background
1108     mForegroundRenderer.SetProperty( Dali::Renderer::Property::DEPTH_INDEX, 1.f );
1109
1110     // Set blend function
1111     mForegroundRenderer.SetProperty( Dali::Renderer::Property::BLEND_FACTOR_SRC_RGB,    Dali::BlendFactor::ONE );
1112     mForegroundRenderer.SetProperty( Dali::Renderer::Property::BLEND_FACTOR_DEST_RGB,   Dali::BlendFactor::ONE_MINUS_SRC_ALPHA );
1113     mForegroundRenderer.SetProperty( Dali::Renderer::Property::BLEND_FACTOR_SRC_ALPHA,  Dali::BlendFactor::ONE );
1114     mForegroundRenderer.SetProperty( Dali::Renderer::Property::BLEND_FACTOR_DEST_ALPHA, Dali::BlendFactor::ONE );
1115
1116     // Create a texture-set and add to renderer
1117
1118     Dali::TextureSet textureSet = Dali::TextureSet::New();
1119     textureSet.SetTexture( 0u, texture );
1120     mForegroundRenderer.SetTextures( textureSet );
1121
1122     mIndicatorContentActor.AddRenderer( mForegroundRenderer );
1123   }
1124   else if( mForegroundRenderer )
1125   {
1126     Dali::TextureSet textureSet = mForegroundRenderer.GetTextures();
1127     textureSet.SetTexture( 0u, texture );
1128   }
1129
1130   if( mImageWidth == 0 && mImageHeight == 0  && texture)
1131   {
1132     Resize( texture.GetWidth(), texture.GetHeight() );
1133   }
1134 }
1135
1136 void Indicator::OnIndicatorTypeChanged( Type indicatorType )
1137 {
1138   if( mObserver != NULL )
1139   {
1140     mObserver->IndicatorTypeChanged( indicatorType );
1141   }
1142 }
1143
1144 void Indicator::DataReceived( void* event )
1145 {
1146   DALI_LOG_TRACE_METHOD( gIndicatorLogFilter );
1147   Ecore_Ipc_Event_Server_Data *epcEvent = static_cast<Ecore_Ipc_Event_Server_Data *>( event );
1148
1149   switch( epcEvent->minor )
1150   {
1151     case OP_UPDATE:
1152     {
1153       DALI_LOG_INFO( gIndicatorLogFilter, Debug::General, "Indicator client received: OP_UPDATE\n" );
1154       if( mIsShowing )
1155       {
1156         mAdaptor->RequestUpdateOnce();
1157       }
1158       break;
1159     }
1160     case OP_UPDATE_DONE:
1161     {
1162       DALI_LOG_INFO( gIndicatorLogFilter, Debug::General, "Indicator client received: OP_UPDATE_DONE [%d]\n", epcEvent->response );
1163       // epcEvent->response == display buffer #
1164       UpdateImageData( epcEvent->response );
1165       break;
1166     }
1167     case OP_SHM_REF0:
1168     {
1169       DALI_LOG_INFO( gIndicatorLogFilter, Debug::General, "Indicator client received: OP_SHM_REF0\n" );
1170       SetSharedImageInfo( epcEvent );
1171       break;
1172     }
1173     case OP_SHM_REF1:
1174     {
1175       DALI_LOG_INFO( gIndicatorLogFilter, Debug::General, "Indicator client received: OP_SHM_REF1\n" );
1176       SetLockFileInfo( epcEvent );
1177       break;
1178     }
1179     case OP_SHM_REF2:
1180     {
1181       DALI_LOG_INFO( gIndicatorLogFilter, Debug::General, "Indicator client received: OP_SHM_REF2\n" );
1182       LoadSharedImage( epcEvent );
1183       break;
1184     }
1185     case OP_RESIZE:
1186     {
1187       DALI_LOG_INFO( gIndicatorLogFilter, Debug::General, "Indicator client received: OP_RESIZE\n" );
1188
1189       if( (epcEvent->data) && (epcEvent->size >= (int)sizeof(IpcDataResize)) )
1190       {
1191         IpcDataResize *newSize = static_cast<IpcDataResize*>( epcEvent->data );
1192         Resize( newSize->w, newSize->h );
1193       }
1194       break;
1195     }
1196     case OP_MSG_PARENT:
1197     {
1198       int msgDomain = epcEvent->ref;
1199       int msgId = epcEvent->ref_to;
1200
1201       void *msgData = NULL;
1202       int msgDataSize = 0;
1203       msgData = epcEvent->data;
1204       msgDataSize = epcEvent->size;
1205
1206       DALI_LOG_INFO( gIndicatorLogFilter, Debug::General, "Indicator client received: OP_MSG_PARENT. msgDomain = %d\n", msgDomain );
1207
1208       if( msgDomain == MSG_DOMAIN_CONTROL_INDICATOR )
1209       {
1210         switch( msgId )
1211         {
1212           case MSG_ID_INDICATOR_TYPE:
1213           {
1214             DALI_LOG_INFO( gIndicatorLogFilter, Debug::General, "Indicator client received: OP_MSG_PARENT, INDICATOR_TYPE\n" );
1215             Type* indicatorType = static_cast<Type*>( epcEvent->data );
1216             OnIndicatorTypeChanged( *indicatorType );
1217             break;
1218           }
1219
1220           case MSG_ID_INDICATOR_START_ANIMATION:
1221           {
1222             DALI_LOG_INFO( gIndicatorLogFilter, Debug::General, "Indicator client received: MSG_ID_INDICATOR_START_ANIMATION\n" );
1223
1224             if (msgDataSize != (int)sizeof(IpcIndicatorDataAnimation))
1225             {
1226               DALI_LOG_ERROR("Message data is incorrect");
1227               break;
1228             }
1229
1230             IpcIndicatorDataAnimation *animData = static_cast<IpcIndicatorDataAnimation*>(msgData);
1231
1232             if(!CheckVisibleState())
1233             {
1234               ShowIndicator( animData->duration /* n sec */ );
1235             }
1236             break;
1237           }
1238
1239         }
1240       }
1241       break;
1242     }
1243   }
1244 }
1245
1246 void Indicator::ConnectionClosed()
1247 {
1248   DALI_LOG_TRACE_METHOD( gIndicatorLogFilter );
1249
1250   // Will get this callback if the server connection failed to start up.
1251   delete mServerConnection;
1252   mServerConnection = NULL;
1253   mState = DISCONNECTED;
1254
1255   // Attempt to re-connect
1256   Connect();
1257 }
1258
1259 bool Indicator::CheckVisibleState()
1260 {
1261   if( mOrientation == Dali::Window::LANDSCAPE
1262     || mOrientation == Dali::Window::LANDSCAPE_INVERSE
1263     || (mVisible != Dali::Window::VISIBLE) )
1264   {
1265     return false;
1266   }
1267
1268   return true;
1269 }
1270
1271 void Indicator::ClearSharedFileInfo()
1272 {
1273   for( int i = 0; i < SHARED_FILE_NUMBER; i++ )
1274   {
1275     delete mSharedFileInfo[i].mLock;
1276     mSharedFileInfo[i].mLock = NULL;
1277
1278     delete mSharedFileInfo[i].mSharedFile;
1279     mSharedFileInfo[i].mSharedFile = NULL;
1280
1281     mSharedFileInfo[i].mLockFileName.clear();
1282     mSharedFileInfo[i].mSharedFileName.clear();
1283   }
1284 }
1285
1286 /**
1287  * duration can be this
1288  *
1289  * enum
1290  * {
1291  *  KEEP_SHOWING = -1,
1292  *  HIDE_NOW = 0
1293  * };
1294  */
1295 void Indicator::ShowIndicator(float duration)
1296 {
1297   if( !mIndicatorAnimation )
1298   {
1299     mIndicatorAnimation = Dali::Animation::New(SLIDING_ANIMATION_DURATION);
1300     mIndicatorAnimation.FinishedSignal().Connect(this, &Indicator::OnAnimationFinished);
1301   }
1302
1303   if(mIsShowing && !EqualsZero(duration))
1304   {
1305     // If need to show during showing, do nothing.
1306     // In 2nd phase (below) will update timer
1307   }
1308   else if(!mIsShowing && mIsAnimationPlaying && EqualsZero(duration))
1309   {
1310     // If need to hide during hiding or hidden already, do nothing
1311   }
1312   else
1313   {
1314     if( EqualsZero(duration) )
1315     {
1316       mIndicatorAnimation.AnimateTo( Property( mIndicatorContentActor, Dali::Actor::Property::POSITION ), Vector3(0, -mImageHeight, 0), Dali::AlphaFunction::EASE_OUT );
1317
1318       mIsShowing = false;
1319
1320       OnIndicatorTypeChanged( INDICATOR_TYPE_2 ); // un-toucable
1321     }
1322     else
1323     {
1324       mIndicatorAnimation.AnimateTo( Property( mIndicatorContentActor, Dali::Actor::Property::POSITION ), Vector3(0, 0, 0), Dali::AlphaFunction::EASE_OUT );
1325
1326       mIsShowing = true;
1327
1328       OnIndicatorTypeChanged( INDICATOR_TYPE_1 ); // touchable
1329     }
1330
1331     mIndicatorAnimation.Play();
1332     mIsAnimationPlaying = true;
1333   }
1334
1335   if(duration > 0)
1336   {
1337     if(!mShowTimer)
1338     {
1339       mShowTimer = Dali::Timer::New(1000 * duration);
1340       mShowTimer.TickSignal().Connect(this, &Indicator::OnShowTimer);
1341     }
1342     mShowTimer.SetInterval(1000* duration);
1343     mShowTimer.Start();
1344
1345     if( mVisible == Dali::Window::AUTO )
1346     {
1347       // check the stage touch
1348       Dali::Stage::GetCurrent().TouchedSignal().Connect( this, &Indicator::OnStageTouched );
1349     }
1350   }
1351   else
1352   {
1353     if(mShowTimer && mShowTimer.IsRunning())
1354     {
1355       mShowTimer.Stop();
1356     }
1357
1358     if( mVisible == Dali::Window::AUTO )
1359     {
1360       // check the stage touch
1361       Dali::Stage::GetCurrent().TouchedSignal().Disconnect( this, &Indicator::OnStageTouched );
1362     }
1363   }
1364 }
1365
1366 bool Indicator::OnShowTimer()
1367 {
1368   // after time up, hide indicator
1369   ShowIndicator( HIDE_NOW );
1370
1371   return false;
1372 }
1373
1374 void Indicator::OnAnimationFinished(Dali::Animation& animation)
1375 {
1376   mIsAnimationPlaying = false;
1377   // once animation is finished and indicator is hidden, take it off stage
1378   if( !mIsShowing )
1379   {
1380     if( mObserver != NULL )
1381     {
1382       mObserver->IndicatorVisibilityChanged( mIsShowing ); // is showing?
1383     }
1384   }
1385 }
1386
1387 void Indicator::OnPan( Dali::Actor actor, const Dali::PanGesture& gesture )
1388 {
1389   if( mServerConnection )
1390   {
1391     switch( gesture.state )
1392     {
1393       case Gesture::Started:
1394       {
1395         mGestureDetected = false;
1396
1397         // The gesture position is the current position after it has moved by the displacement.
1398         // We want to reference the original position.
1399         mGestureDeltaY = gesture.position.y - gesture.displacement.y;
1400       }
1401
1402       // No break, Fall through
1403       case Gesture::Continuing:
1404       {
1405         if( mVisible == Dali::Window::AUTO && !mIsShowing )
1406         {
1407           // Only take one touch point
1408           if( gesture.numberOfTouches == 1 && mGestureDetected == false )
1409           {
1410             mGestureDeltaY += gesture.displacement.y;
1411
1412             if( mGestureDeltaY >= mImageHeight * SHOWING_DISTANCE_HEIGHT_RATE )
1413             {
1414               ShowIndicator( AUTO_INDICATOR_STAY_DURATION );
1415               mGestureDetected = true;
1416             }
1417           }
1418         }
1419
1420         break;
1421       }
1422
1423       case Gesture::Finished:
1424       case Gesture::Cancelled:
1425       {
1426         // if indicator is showing, hide again when touching is finished (Since touch leave is activated, checking it in gesture::finish instead of touch::up)
1427         if( mVisible == Dali::Window::AUTO && mIsShowing )
1428         {
1429           ShowIndicator( AUTO_INDICATOR_STAY_DURATION );
1430         }
1431         break;
1432       }
1433
1434
1435       default:
1436         break;
1437     }
1438   }
1439 }
1440
1441 void Indicator::OnStageTouched(const Dali::TouchEvent& touchEvent)
1442 {
1443   const TouchPoint& touchPoint = touchEvent.GetPoint( 0 );
1444
1445   // when stage is touched while indicator is showing temporary, hide it
1446   if( mIsShowing && ( CheckVisibleState() == false || mVisible == Dali::Window::AUTO ) )
1447   {
1448     switch( touchPoint.state )
1449     {
1450       case Dali::PointState::DOWN:
1451       {
1452         ShowIndicator( HIDE_NOW );
1453         break;
1454       }
1455
1456       default:
1457       break;
1458     }
1459   }
1460 }
1461
1462 } // Adaptor
1463 } // Internal
1464 } // Dali