예제 #1
0
        private void setContext(android.view.MotionEvent curr)
        {
            if (mCurrEvent != null)
            {
                mCurrEvent.recycle();
            }
            mCurrEvent   = android.view.MotionEvent.obtain(curr);
            mCurrLen     = -1;
            mPrevLen     = -1;
            mScaleFactor = -1;
            android.view.MotionEvent prev = mPrevEvent;
            int prevIndex0 = prev.findPointerIndex(mActiveId0);
            int prevIndex1 = prev.findPointerIndex(mActiveId1);
            int currIndex0 = curr.findPointerIndex(mActiveId0);
            int currIndex1 = curr.findPointerIndex(mActiveId1);

            if (prevIndex0 < 0 || prevIndex1 < 0 || currIndex0 < 0 || currIndex1 < 0)
            {
                mInvalidGesture = true;
                android.util.Log.e(TAG, "Invalid MotionEvent stream detected.", new System.Exception
                                       ());
                if (mGestureInProgress)
                {
                    mListener.onScaleEnd(this);
                }
                return;
            }
            float px0 = prev.getX(prevIndex0);
            float py0 = prev.getY(prevIndex0);
            float px1 = prev.getX(prevIndex1);
            float py1 = prev.getY(prevIndex1);
            float cx0 = curr.getX(currIndex0);
            float cy0 = curr.getY(currIndex0);
            float cx1 = curr.getX(currIndex1);
            float cy1 = curr.getY(currIndex1);
            float pvx = px1 - px0;
            float pvy = py1 - py0;
            float cvx = cx1 - cx0;
            float cvy = cy1 - cy0;

            mPrevFingerDiffX = pvx;
            mPrevFingerDiffY = pvy;
            mCurrFingerDiffX = cvx;
            mCurrFingerDiffY = cvy;
            mFocusX          = cx0 + cvx * 0.5f;
            mFocusY          = cy0 + cvy * 0.5f;
            mTimeDelta       = curr.getEventTime() - prev.getEventTime();
            mCurrPressure    = curr.getPressure(currIndex0) + curr.getPressure(currIndex1);
            mPrevPressure    = prev.getPressure(prevIndex0) + prev.getPressure(prevIndex1);
        }
예제 #2
0
 private void cancelSuperTouch(android.view.MotionEvent ev)
 {
     android.view.MotionEvent cancel = android.view.MotionEvent.obtain(ev);
     cancel.setAction(android.view.MotionEvent.ACTION_CANCEL);
     base.onTouchEvent(cancel);
     cancel.recycle();
 }
예제 #3
0
 private void cancelFling()
 {
     // Cancel the list fling
     android.view.MotionEvent cancelFling_1 = android.view.MotionEvent.obtain(0, 0, android.view.MotionEvent
                                                                              .ACTION_CANCEL, 0, 0, 0);
     mList.onTouchEvent(cancelFling_1);
     cancelFling_1.recycle();
 }
예제 #4
0
        /// <summary>
        /// Analyzes the given motion event and if applicable triggers the
        /// appropriate callbacks on the
        /// <see cref="OnGestureListener">OnGestureListener</see>
        /// supplied.
        /// </summary>
        /// <param name="ev">The current motion event.</param>
        /// <returns>
        /// true if the
        /// <see cref="OnGestureListener">OnGestureListener</see>
        /// consumed the event,
        /// else false.
        /// </returns>
        public virtual bool onTouchEvent(android.view.MotionEvent ev)
        {
            if (mInputEventConsistencyVerifier != null)
            {
                mInputEventConsistencyVerifier.onTouchEvent(ev, 0);
            }
            int   action = ev.getAction();
            float y      = ev.getY();
            float x      = ev.getX();

            if (mVelocityTracker == null)
            {
                mVelocityTracker = android.view.VelocityTracker.obtain();
            }
            mVelocityTracker.addMovement(ev);
            bool handled = false;

            switch (action & android.view.MotionEvent.ACTION_MASK)
            {
            case android.view.MotionEvent.ACTION_POINTER_DOWN:
            {
                if (mIgnoreMultitouch)
                {
                    // Multitouch event - abort.
                    cancel();
                }
                break;
            }

            case android.view.MotionEvent.ACTION_POINTER_UP:
            {
                // Ending a multitouch gesture and going back to 1 finger
                if (mIgnoreMultitouch && ev.getPointerCount() == 2)
                {
                    int index = (((action & android.view.MotionEvent.ACTION_POINTER_INDEX_MASK) >> android.view.MotionEvent
                                  .ACTION_POINTER_INDEX_SHIFT) == 0) ? 1 : 0;
                    mLastMotionX = ev.getX(index);
                    mLastMotionY = ev.getY(index);
                    mVelocityTracker.recycle();
                    mVelocityTracker = android.view.VelocityTracker.obtain();
                }
                break;
            }

            case android.view.MotionEvent.ACTION_DOWN:
            {
                if (mDoubleTapListener != null)
                {
                    bool hadTapMessage = mHandler.hasMessages(TAP);
                    if (hadTapMessage)
                    {
                        mHandler.removeMessages(TAP);
                    }
                    if ((mCurrentDownEvent != null) && (mPreviousUpEvent != null) && hadTapMessage &&
                        isConsideredDoubleTap(mCurrentDownEvent, mPreviousUpEvent, ev))
                    {
                        // This is a second tap
                        mIsDoubleTapping = true;
                        // Give a callback with the first tap of the double-tap
                        handled |= mDoubleTapListener.onDoubleTap(mCurrentDownEvent);
                        // Give a callback with down event of the double-tap
                        handled |= mDoubleTapListener.onDoubleTapEvent(ev);
                    }
                    else
                    {
                        // This is a first tap
                        mHandler.sendEmptyMessageDelayed(TAP, DOUBLE_TAP_TIMEOUT);
                    }
                }
                mLastMotionX = x;
                mLastMotionY = y;
                if (mCurrentDownEvent != null)
                {
                    mCurrentDownEvent.recycle();
                }
                mCurrentDownEvent        = android.view.MotionEvent.obtain(ev);
                mAlwaysInTapRegion       = true;
                mAlwaysInBiggerTapRegion = true;
                mStillDown   = true;
                mInLongPress = false;
                if (mIsLongpressEnabled)
                {
                    mHandler.removeMessages(LONG_PRESS);
                    mHandler.sendEmptyMessageAtTime(LONG_PRESS, mCurrentDownEvent.getDownTime() + TAP_TIMEOUT
                                                    + LONGPRESS_TIMEOUT);
                }
                mHandler.sendEmptyMessageAtTime(SHOW_PRESS, mCurrentDownEvent.getDownTime() + TAP_TIMEOUT
                                                );
                handled |= mListener.onDown(ev);
                break;
            }

            case android.view.MotionEvent.ACTION_MOVE:
            {
                if (mInLongPress || (mIgnoreMultitouch && ev.getPointerCount() > 1))
                {
                    break;
                }
                float scrollX = mLastMotionX - x;
                float scrollY = mLastMotionY - y;
                if (mIsDoubleTapping)
                {
                    // Give the move events of the double-tap
                    handled |= mDoubleTapListener.onDoubleTapEvent(ev);
                }
                else
                {
                    if (mAlwaysInTapRegion)
                    {
                        int deltaX   = (int)(x - mCurrentDownEvent.getX());
                        int deltaY   = (int)(y - mCurrentDownEvent.getY());
                        int distance = (deltaX * deltaX) + (deltaY * deltaY);
                        if (distance > mTouchSlopSquare)
                        {
                            handled            = mListener.onScroll(mCurrentDownEvent, ev, scrollX, scrollY);
                            mLastMotionX       = x;
                            mLastMotionY       = y;
                            mAlwaysInTapRegion = false;
                            mHandler.removeMessages(TAP);
                            mHandler.removeMessages(SHOW_PRESS);
                            mHandler.removeMessages(LONG_PRESS);
                        }
                        if (distance > mBiggerTouchSlopSquare)
                        {
                            mAlwaysInBiggerTapRegion = false;
                        }
                    }
                    else
                    {
                        if ((System.Math.Abs(scrollX) >= 1) || (System.Math.Abs(scrollY) >= 1))
                        {
                            handled      = mListener.onScroll(mCurrentDownEvent, ev, scrollX, scrollY);
                            mLastMotionX = x;
                            mLastMotionY = y;
                        }
                    }
                }
                break;
            }

            case android.view.MotionEvent.ACTION_UP:
            {
                mStillDown = false;
                android.view.MotionEvent currentUpEvent = android.view.MotionEvent.obtain(ev);
                if (mIsDoubleTapping)
                {
                    // Finally, give the up event of the double-tap
                    handled |= mDoubleTapListener.onDoubleTapEvent(ev);
                }
                else
                {
                    if (mInLongPress)
                    {
                        mHandler.removeMessages(TAP);
                        mInLongPress = false;
                    }
                    else
                    {
                        if (mAlwaysInTapRegion)
                        {
                            handled = mListener.onSingleTapUp(ev);
                        }
                        else
                        {
                            // A fling must travel the minimum tap distance
                            android.view.VelocityTracker velocityTracker = mVelocityTracker;
                            velocityTracker.computeCurrentVelocity(1000, mMaximumFlingVelocity);
                            float velocityY = velocityTracker.getYVelocity();
                            float velocityX = velocityTracker.getXVelocity();
                            if ((System.Math.Abs(velocityY) > mMinimumFlingVelocity) || (System.Math.Abs(velocityX
                                                                                                         ) > mMinimumFlingVelocity))
                            {
                                handled = mListener.onFling(mCurrentDownEvent, ev, velocityX, velocityY);
                            }
                        }
                    }
                }
                if (mPreviousUpEvent != null)
                {
                    mPreviousUpEvent.recycle();
                }
                // Hold the event we obtained above - listeners may have changed the original.
                mPreviousUpEvent = currentUpEvent;
                mVelocityTracker.recycle();
                mVelocityTracker = null;
                mIsDoubleTapping = false;
                mHandler.removeMessages(SHOW_PRESS);
                mHandler.removeMessages(LONG_PRESS);
                break;
            }

            case android.view.MotionEvent.ACTION_CANCEL:
            {
                cancel();
                break;
            }
            }
            if (!handled && mInputEventConsistencyVerifier != null)
            {
                mInputEventConsistencyVerifier.onUnhandledEvent(ev, 0);
            }
            return(handled);
        }
예제 #5
0
        public virtual bool onTouch(android.view.View v, android.view.MotionEvent @event)
        {
            int action = @event.getAction();

            if (@event.getPointerCount() > 1)
            {
                // ZoomButtonsController doesn't handle mutitouch. Give up control.
                return(false);
            }
            if (mReleaseTouchListenerOnUp)
            {
                // The controls were dismissed but we need to throw away all events until the up
                if (action == android.view.MotionEvent.ACTION_UP || action == android.view.MotionEvent
                    .ACTION_CANCEL)
                {
                    mOwnerView.setOnTouchListener(null);
                    setTouchTargetView(null);
                    mReleaseTouchListenerOnUp = false;
                }
                // Eat this event
                return(true);
            }
            dismissControlsDelayed(ZOOM_CONTROLS_TIMEOUT);
            android.view.View targetView = mTouchTargetView;
            switch (action)
            {
            case android.view.MotionEvent.ACTION_DOWN:
            {
                targetView = findViewForTouch((int)@event.getRawX(), (int)@event.getRawY());
                setTouchTargetView(targetView);
                break;
            }

            case android.view.MotionEvent.ACTION_UP:
            case android.view.MotionEvent.ACTION_CANCEL:
            {
                setTouchTargetView(null);
                break;
            }
            }
            if (targetView != null)
            {
                // The upperleft corner of the target view in raw coordinates
                int targetViewRawX = mContainerRawLocation[0] + mTouchTargetWindowLocation[0];
                int targetViewRawY = mContainerRawLocation[1] + mTouchTargetWindowLocation[1];
                android.view.MotionEvent containerEvent = android.view.MotionEvent.obtain(@event);
                // Convert the motion event into the target view's coordinates (from
                // owner view's coordinates)
                containerEvent.offsetLocation(mOwnerViewRawLocation[0] - targetViewRawX, mOwnerViewRawLocation
                                              [1] - targetViewRawY);
                // These are floats because we need to potentially offset away this exact amount
                float containerX = containerEvent.getX();
                float containerY = containerEvent.getY();
                if (containerX < 0 && containerX > -ZOOM_CONTROLS_TOUCH_PADDING)
                {
                    containerEvent.offsetLocation(-containerX, 0);
                }
                if (containerY < 0 && containerY > -ZOOM_CONTROLS_TOUCH_PADDING)
                {
                    containerEvent.offsetLocation(0, -containerY);
                }
                bool retValue = targetView.dispatchTouchEvent(containerEvent);
                containerEvent.recycle();
                return(retValue);
            }
            else
            {
                return(false);
            }
        }
예제 #6
0
        public virtual bool onTouchEvent(android.view.MotionEvent @event)
        {
            if (mInputEventConsistencyVerifier != null)
            {
                mInputEventConsistencyVerifier.onTouchEvent(@event, 0);
            }
            int action = @event.getActionMasked();

            if (action == android.view.MotionEvent.ACTION_DOWN)
            {
                reset();
            }
            // Start fresh
            bool handled = true;

            if (mInvalidGesture)
            {
                handled = false;
            }
            else
            {
                if (!mGestureInProgress)
                {
                    switch (action)
                    {
                    case android.view.MotionEvent.ACTION_DOWN:
                    {
                        mActiveId0         = @event.getPointerId(0);
                        mActive0MostRecent = true;
                        break;
                    }

                    case android.view.MotionEvent.ACTION_UP:
                    {
                        reset();
                        break;
                    }

                    case android.view.MotionEvent.ACTION_POINTER_DOWN:
                    {
                        // We have a new multi-finger gesture
                        // as orientation can change, query the metrics in touch down
                        android.util.DisplayMetrics metrics = mContext.getResources().getDisplayMetrics();
                        mRightSlopEdge  = metrics.widthPixels - mEdgeSlop;
                        mBottomSlopEdge = metrics.heightPixels - mEdgeSlop;
                        if (mPrevEvent != null)
                        {
                            mPrevEvent.recycle();
                        }
                        mPrevEvent = android.view.MotionEvent.obtain(@event);
                        mTimeDelta = 0;
                        int index1 = @event.getActionIndex();
                        int index0 = @event.findPointerIndex(mActiveId0);
                        mActiveId1 = @event.getPointerId(index1);
                        if (index0 < 0 || index0 == index1)
                        {
                            // Probably someone sending us a broken event stream.
                            index0     = findNewActiveIndex(@event, index0 == index1 ? -1 : mActiveId1, index0);
                            mActiveId0 = @event.getPointerId(index0);
                        }
                        mActive0MostRecent = false;
                        setContext(@event);
                        // Check if we have a sloppy gesture. If so, delay
                        // the beginning of the gesture until we're sure that's
                        // what the user wanted. Sloppy gestures can happen if the
                        // edge of the user's hand is touching the screen, for example.
                        float edgeSlop = mEdgeSlop;
                        float rightSlop = mRightSlopEdge;
                        float bottomSlop = mBottomSlopEdge;
                        float x0 = getRawX(@event, index0);
                        float y0 = getRawY(@event, index0);
                        float x1 = getRawX(@event, index1);
                        float y1 = getRawY(@event, index1);
                        bool  p0sloppy = x0 <edgeSlop || y0 <edgeSlop || x0> rightSlop || y0> bottomSlop;
                        bool  p1sloppy = x1 <edgeSlop || y1 <edgeSlop || x1> rightSlop || y1> bottomSlop;
                        if (p0sloppy && p1sloppy)
                        {
                            mFocusX        = -1;
                            mFocusY        = -1;
                            mSloppyGesture = true;
                        }
                        else
                        {
                            if (p0sloppy)
                            {
                                mFocusX        = @event.getX(index1);
                                mFocusY        = @event.getY(index1);
                                mSloppyGesture = true;
                            }
                            else
                            {
                                if (p1sloppy)
                                {
                                    mFocusX        = @event.getX(index0);
                                    mFocusY        = @event.getY(index0);
                                    mSloppyGesture = true;
                                }
                                else
                                {
                                    mSloppyGesture     = false;
                                    mGestureInProgress = mListener.onScaleBegin(this);
                                }
                            }
                        }
                        break;
                    }

                    case android.view.MotionEvent.ACTION_MOVE:
                    {
                        if (mSloppyGesture)
                        {
                            // Initiate sloppy gestures if we've moved outside of the slop area.
                            float edgeSlop = mEdgeSlop;
                            float rightSlop = mRightSlopEdge;
                            float bottomSlop = mBottomSlopEdge;
                            int   index0 = @event.findPointerIndex(mActiveId0);
                            int   index1 = @event.findPointerIndex(mActiveId1);
                            float x0 = getRawX(@event, index0);
                            float y0 = getRawY(@event, index0);
                            float x1 = getRawX(@event, index1);
                            float y1 = getRawY(@event, index1);
                            bool  p0sloppy = x0 <edgeSlop || y0 <edgeSlop || x0> rightSlop || y0> bottomSlop;
                            bool  p1sloppy = x1 <edgeSlop || y1 <edgeSlop || x1> rightSlop || y1> bottomSlop;
                            if (p0sloppy)
                            {
                                // Do we have a different pointer that isn't sloppy?
                                int index = findNewActiveIndex(@event, mActiveId1, index0);
                                if (index >= 0)
                                {
                                    index0     = index;
                                    mActiveId0 = @event.getPointerId(index);
                                    x0         = getRawX(@event, index);
                                    y0         = getRawY(@event, index);
                                    p0sloppy   = false;
                                }
                            }
                            if (p1sloppy)
                            {
                                // Do we have a different pointer that isn't sloppy?
                                int index = findNewActiveIndex(@event, mActiveId0, index1);
                                if (index >= 0)
                                {
                                    index1     = index;
                                    mActiveId1 = @event.getPointerId(index);
                                    x1         = getRawX(@event, index);
                                    y1         = getRawY(@event, index);
                                    p1sloppy   = false;
                                }
                            }
                            if (p0sloppy && p1sloppy)
                            {
                                mFocusX = -1;
                                mFocusY = -1;
                            }
                            else
                            {
                                if (p0sloppy)
                                {
                                    mFocusX = @event.getX(index1);
                                    mFocusY = @event.getY(index1);
                                }
                                else
                                {
                                    if (p1sloppy)
                                    {
                                        mFocusX = @event.getX(index0);
                                        mFocusY = @event.getY(index0);
                                    }
                                    else
                                    {
                                        mSloppyGesture     = false;
                                        mGestureInProgress = mListener.onScaleBegin(this);
                                    }
                                }
                            }
                        }
                        break;
                    }

                    case android.view.MotionEvent.ACTION_POINTER_UP:
                    {
                        if (mSloppyGesture)
                        {
                            int pointerCount = @event.getPointerCount();
                            int actionIndex  = @event.getActionIndex();
                            int actionId     = @event.getPointerId(actionIndex);
                            if (pointerCount > 2)
                            {
                                if (actionId == mActiveId0)
                                {
                                    int newIndex = findNewActiveIndex(@event, mActiveId1, actionIndex);
                                    if (newIndex >= 0)
                                    {
                                        mActiveId0 = @event.getPointerId(newIndex);
                                    }
                                }
                                else
                                {
                                    if (actionId == mActiveId1)
                                    {
                                        int newIndex = findNewActiveIndex(@event, mActiveId0, actionIndex);
                                        if (newIndex >= 0)
                                        {
                                            mActiveId1 = @event.getPointerId(newIndex);
                                        }
                                    }
                                }
                            }
                            else
                            {
                                // Set focus point to the remaining finger
                                int index = @event.findPointerIndex(actionId == mActiveId0 ? mActiveId1 : mActiveId0
                                                                    );
                                if (index < 0)
                                {
                                    mInvalidGesture = true;
                                    android.util.Log.e(TAG, "Invalid MotionEvent stream detected.", new System.Exception
                                                           ());
                                    if (mGestureInProgress)
                                    {
                                        mListener.onScaleEnd(this);
                                    }
                                    return(false);
                                }
                                mActiveId0         = @event.getPointerId(index);
                                mActive0MostRecent = true;
                                mActiveId1         = -1;
                                mFocusX            = @event.getX(index);
                                mFocusY            = @event.getY(index);
                            }
                        }
                        break;
                    }
                    }
                }
                else
                {
                    switch (action)
                    {
                    case android.view.MotionEvent.ACTION_POINTER_DOWN:
                    {
                        // Transform gesture in progress - attempt to handle it
                        // End the old gesture and begin a new one with the most recent two fingers.
                        mListener.onScaleEnd(this);
                        int oldActive0 = mActiveId0;
                        int oldActive1 = mActiveId1;
                        reset();
                        mPrevEvent         = android.view.MotionEvent.obtain(@event);
                        mActiveId0         = mActive0MostRecent ? oldActive0 : oldActive1;
                        mActiveId1         = @event.getPointerId(@event.getActionIndex());
                        mActive0MostRecent = false;
                        int index0 = @event.findPointerIndex(mActiveId0);
                        if (index0 < 0 || mActiveId0 == mActiveId1)
                        {
                            // Probably someone sending us a broken event stream.
                            android.util.Log.e(TAG, "Got " + android.view.MotionEvent.actionToString(action)
                                               + " with bad state while a gesture was in progress. " + "Did you forget to pass an event to "
                                               + "ScaleGestureDetector#onTouchEvent?");
                            index0 = findNewActiveIndex(@event, mActiveId0 == mActiveId1 ? -1 : mActiveId1, index0
                                                        );
                            mActiveId0 = @event.getPointerId(index0);
                        }
                        setContext(@event);
                        mGestureInProgress = mListener.onScaleBegin(this);
                        break;
                    }

                    case android.view.MotionEvent.ACTION_POINTER_UP:
                    {
                        int  pointerCount = @event.getPointerCount();
                        int  actionIndex  = @event.getActionIndex();
                        int  actionId     = @event.getPointerId(actionIndex);
                        bool gestureEnded = false;
                        if (pointerCount > 2)
                        {
                            if (actionId == mActiveId0)
                            {
                                int newIndex = findNewActiveIndex(@event, mActiveId1, actionIndex);
                                if (newIndex >= 0)
                                {
                                    mListener.onScaleEnd(this);
                                    mActiveId0         = @event.getPointerId(newIndex);
                                    mActive0MostRecent = true;
                                    mPrevEvent         = android.view.MotionEvent.obtain(@event);
                                    setContext(@event);
                                    mGestureInProgress = mListener.onScaleBegin(this);
                                }
                                else
                                {
                                    gestureEnded = true;
                                }
                            }
                            else
                            {
                                if (actionId == mActiveId1)
                                {
                                    int newIndex = findNewActiveIndex(@event, mActiveId0, actionIndex);
                                    if (newIndex >= 0)
                                    {
                                        mListener.onScaleEnd(this);
                                        mActiveId1         = @event.getPointerId(newIndex);
                                        mActive0MostRecent = false;
                                        mPrevEvent         = android.view.MotionEvent.obtain(@event);
                                        setContext(@event);
                                        mGestureInProgress = mListener.onScaleBegin(this);
                                    }
                                    else
                                    {
                                        gestureEnded = true;
                                    }
                                }
                            }
                            mPrevEvent.recycle();
                            mPrevEvent = android.view.MotionEvent.obtain(@event);
                            setContext(@event);
                        }
                        else
                        {
                            gestureEnded = true;
                        }
                        if (gestureEnded)
                        {
                            // Gesture ended
                            setContext(@event);
                            // Set focus point to the remaining finger
                            int activeId = actionId == mActiveId0 ? mActiveId1 : mActiveId0;
                            int index    = @event.findPointerIndex(activeId);
                            mFocusX = @event.getX(index);
                            mFocusY = @event.getY(index);
                            mListener.onScaleEnd(this);
                            reset();
                            mActiveId0         = activeId;
                            mActive0MostRecent = true;
                        }
                        break;
                    }

                    case android.view.MotionEvent.ACTION_CANCEL:
                    {
                        mListener.onScaleEnd(this);
                        reset();
                        break;
                    }

                    case android.view.MotionEvent.ACTION_UP:
                    {
                        reset();
                        break;
                    }

                    case android.view.MotionEvent.ACTION_MOVE:
                    {
                        setContext(@event);
                        // Only accept the event if our relative pressure is within
                        // a certain limit - this can help filter shaky data as a
                        // finger is lifted.
                        if (mCurrPressure / mPrevPressure > PRESSURE_THRESHOLD)
                        {
                            bool updatePrevious = mListener.onScale(this);
                            if (updatePrevious)
                            {
                                mPrevEvent.recycle();
                                mPrevEvent = android.view.MotionEvent.obtain(@event);
                            }
                        }
                        break;
                    }
                    }
                }
            }
            if (!handled && mInputEventConsistencyVerifier != null)
            {
                mInputEventConsistencyVerifier.onUnhandledEvent(@event, 0);
            }
            return(handled);
        }