/////////////////////////////////////////////////////////////////////////////
// Name:        wx/osx/core/private.h
// Purpose:     Private declarations: as this header is only included by
//              wxWidgets itself, it may contain identifiers which don't start
//              with "wx".
// Author:      Stefan Csomor
// Modified by:
// Created:     1998-01-01
// Copyright:   (c) Stefan Csomor
// Licence:     wxWindows licence
/////////////////////////////////////////////////////////////////////////////

#ifndef _WX_PRIVATE_CORE_H_
#define _WX_PRIVATE_CORE_H_

#include "wx/defs.h"

#include <CoreFoundation/CoreFoundation.h>

#include "wx/osx/core/cfstring.h"
#include "wx/osx/core/cfdataref.h"

// platform specific Clang analyzer support
#ifndef NS_RETURNS_RETAINED
#   if WX_HAS_CLANG_FEATURE(attribute_ns_returns_retained)
#       define NS_RETURNS_RETAINED __attribute__((ns_returns_retained))
#   else
#       define NS_RETURNS_RETAINED
#   endif
#endif

#ifndef CF_RETURNS_RETAINED
#   if WX_HAS_CLANG_FEATURE(attribute_cf_returns_retained)
#       define CF_RETURNS_RETAINED __attribute__((cf_returns_retained))
#   else
#       define CF_RETURNS_RETAINED
#   endif
#endif

#if ( !wxUSE_GUI && !wxOSX_USE_IPHONE ) || wxOSX_USE_COCOA_OR_CARBON

// Carbon functions are currently still used in wxOSX/Cocoa too (including
// wxBase part of it).
#include <Carbon/Carbon.h>

void WXDLLIMPEXP_CORE wxMacStringToPascal( const wxString&from , unsigned char * to );
wxString WXDLLIMPEXP_CORE wxMacMakeStringFromPascal( const unsigned char * from );

WXDLLIMPEXP_BASE wxString wxMacFSRefToPath( const FSRef *fsRef , CFStringRef additionalPathComponent = NULL );
WXDLLIMPEXP_BASE OSStatus wxMacPathToFSRef( const wxString&path , FSRef *fsRef );
WXDLLIMPEXP_BASE wxString wxMacHFSUniStrToString( ConstHFSUniStr255Param uniname );

// keycode utils from app.cpp

WXDLLIMPEXP_BASE CGKeyCode wxCharCodeWXToOSX(wxKeyCode code);
WXDLLIMPEXP_BASE long wxMacTranslateKey(unsigned char key, unsigned char code);

#endif

#if wxUSE_GUI

#if wxOSX_USE_IPHONE
#include <CoreGraphics/CoreGraphics.h>
#else
#include <ApplicationServices/ApplicationServices.h>
#endif

#include "wx/bitmap.h"
#include "wx/window.h"

class WXDLLIMPEXP_CORE wxMacCGContextStateSaver
{
    wxDECLARE_NO_COPY_CLASS(wxMacCGContextStateSaver);

public:
    wxMacCGContextStateSaver( CGContextRef cg )
    {
        m_cg = cg;
        CGContextSaveGState( cg );
    }
    ~wxMacCGContextStateSaver()
    {
        CGContextRestoreGState( m_cg );
    }
private:
    CGContextRef m_cg;
};

class WXDLLIMPEXP_CORE wxDeferredObjectDeleter : public wxObject
{
public :
    wxDeferredObjectDeleter( wxObject* obj ) : m_obj(obj)
    {
    }
    virtual ~wxDeferredObjectDeleter()
    {
        delete m_obj;
    }
protected :
    wxObject* m_obj ;
} ;

// Quartz

WXDLLIMPEXP_CORE CGImageRef wxMacCreateCGImageFromBitmap( const wxBitmap& bitmap );

WXDLLIMPEXP_CORE CGDataProviderRef wxMacCGDataProviderCreateWithCFData( CFDataRef data );
WXDLLIMPEXP_CORE CGDataConsumerRef wxMacCGDataConsumerCreateWithCFData( CFMutableDataRef data );
WXDLLIMPEXP_CORE CGDataProviderRef wxMacCGDataProviderCreateWithMemoryBuffer( const wxMemoryBuffer& buf );

WXDLLIMPEXP_CORE CGColorSpaceRef wxMacGetGenericRGBColorSpace(void);

WXDLLIMPEXP_CORE double wxOSXGetMainScreenContentScaleFactor();

class wxWindowMac;
// to
extern wxWindow* g_MacLastWindow;
class wxNonOwnedWindow;

// temporary typedef so that no additional casts are necessary within carbon code at the moment

class wxMacControl;
class wxWidgetImpl;
class wxComboBox;
class wxNotebook;
class wxTextCtrl;
class wxSearchCtrl;

WXDLLIMPEXP_CORE wxWindowMac * wxFindWindowFromWXWidget(WXWidget inControl );

typedef wxWidgetImpl wxWidgetImplType;

#if wxUSE_MENUS
class wxMenuItemImpl : public wxObject
{
public :
    wxMenuItemImpl( wxMenuItem* peer ) : m_peer(peer)
    {
    }

    virtual ~wxMenuItemImpl() ;
    virtual void SetBitmap( const wxBitmap& bitmap ) = 0;
    virtual void Enable( bool enable ) = 0;
    virtual void Check( bool check ) = 0;
    virtual void SetLabel( const wxString& text, wxAcceleratorEntry *entry ) = 0;
    virtual void Hide( bool hide = true ) = 0;

    virtual void * GetHMenuItem() = 0;

    wxMenuItem* GetWXPeer() { return m_peer ; }

    static wxMenuItemImpl* Create( wxMenuItem* peer, wxMenu *pParentMenu,
                       int id,
                       const wxString& text,
                       wxAcceleratorEntry *entry,
                       const wxString& strHelp,
                       wxItemKind kind,
                       wxMenu *pSubMenu );

    // handle OS specific menu items if they weren't handled during normal processing
    virtual bool DoDefault() { return false; }
protected :
    wxMenuItem* m_peer;

    wxDECLARE_ABSTRACT_CLASS(wxMenuItemImpl);
} ;

class wxMenuImpl : public wxObject
{
public :
    wxMenuImpl( wxMenu* peer ) : m_peer(peer)
    {
    }

    virtual ~wxMenuImpl() ;
    virtual void InsertOrAppend(wxMenuItem *pItem, size_t pos) = 0;
    virtual void Remove( wxMenuItem *pItem ) = 0;

    virtual void MakeRoot() = 0;

    virtual void SetTitle( const wxString& text ) = 0;

    virtual WXHMENU GetHMenu() = 0;

    wxMenu* GetWXPeer() { return m_peer ; }

    virtual void PopUp( wxWindow *win, int x, int y ) = 0;

    virtual void GetMenuBarDimensions(int &x, int &y, int &width, int &height) const
    {
        x = y = width = height = -1;
    }

    static wxMenuImpl* Create( wxMenu* peer, const wxString& title );
    static wxMenuImpl* CreateRootMenu( wxMenu* peer );
protected :
    wxMenu* m_peer;

    wxDECLARE_ABSTRACT_CLASS(wxMenuItemImpl);
} ;
#endif

class WXDLLIMPEXP_CORE wxWidgetImpl : public wxObject
{
public :
    wxWidgetImpl( wxWindowMac* peer , bool isRootControl = false, bool isUserPane = false );
    wxWidgetImpl();
    virtual ~wxWidgetImpl();

    void Init();

    bool                IsRootControl() const { return m_isRootControl; }

    bool                IsUserPane() const { return m_isUserPane; }

    wxWindowMac*        GetWXPeer() const { return m_wxPeer; }

    bool IsOk() const { return GetWXWidget() != NULL; }

    // not only the control itself, but also all its parents must be visible
    // in order for this function to return true
    virtual bool        IsVisible() const = 0;
    // set the visibility of this widget (maybe latent)
    virtual void        SetVisibility( bool visible ) = 0;

    virtual bool ShowWithEffect(bool WXUNUSED(show),
                                wxShowEffect WXUNUSED(effect),
                                unsigned WXUNUSED(timeout))
    {
        return false;
    }

    virtual void        Raise() = 0;

    virtual void        Lower() = 0;

    virtual void        ScrollRect( const wxRect *rect, int dx, int dy ) = 0;

    virtual WXWidget    GetWXWidget() const = 0;

    virtual void        SetBackgroundColour( const wxColour& col ) = 0;
    virtual bool        SetBackgroundStyle(wxBackgroundStyle style) = 0;

    // all coordinates in native parent widget relative coordinates
    virtual void        GetContentArea( int &left , int &top , int &width , int &height ) const = 0;
    virtual void        Move(int x, int y, int width, int height) = 0;
    virtual void        GetPosition( int &x, int &y ) const = 0;
    virtual void        GetSize( int &width, int &height ) const = 0;
    virtual void        SetControlSize( wxWindowVariant variant ) = 0;
    virtual double      GetContentScaleFactor() const
    {
        return 1.0;
    }

    // the native coordinates may have an 'aura' for shadows etc, if this is the case the layout
    // inset indicates on which insets the real control is drawn
    virtual void        GetLayoutInset(int &left , int &top , int &right, int &bottom) const
    {
        left = top = right = bottom = 0;
    }

    // native view coordinates are topleft to bottom right (flipped regarding CoreGraphics origin)
    virtual bool        IsFlipped() const { return true; }

    virtual void        SetNeedsDisplay( const wxRect* where = NULL ) = 0;
    virtual bool        GetNeedsDisplay() const = 0;

    virtual bool        NeedsFocusRect() const;
    virtual void        SetNeedsFocusRect( bool needs );

    virtual bool        NeedsFrame() const;
    virtual void        SetNeedsFrame( bool needs );

    virtual void        SetDrawingEnabled(bool enabled);

    virtual bool        CanFocus() const = 0;
    // return true if successful
    virtual bool        SetFocus() = 0;
    virtual bool        HasFocus() const = 0;

    virtual void        RemoveFromParent() = 0;
    virtual void        Embed( wxWidgetImpl *parent ) = 0;

    virtual void        SetDefaultButton( bool isDefault ) = 0;
    virtual void        PerformClick() = 0;
    virtual void        SetLabel( const wxString& title, wxFontEncoding encoding ) = 0;
#if wxUSE_MARKUP && wxOSX_USE_COCOA
    virtual void        SetLabelMarkup( const wxString& WXUNUSED(markup) ) { }
#endif

    virtual void        SetCursor( const wxCursor & cursor ) = 0;
    virtual void        CaptureMouse() = 0;
    virtual void        ReleaseMouse() = 0;

    virtual void        SetDropTarget( wxDropTarget * WXUNUSED(dropTarget) ) {}

    virtual wxInt32     GetValue() const = 0;
    virtual void        SetValue( wxInt32 v ) = 0;
    virtual wxBitmap    GetBitmap() const = 0;
    virtual void        SetBitmap( const wxBitmap& bitmap ) = 0;
    virtual void        SetBitmapPosition( wxDirection dir ) = 0;
    virtual void        SetupTabs( const wxNotebook& WXUNUSED(notebook) ) {}
    virtual int         TabHitTest( const wxPoint & WXUNUSED(pt), long *flags ) {*flags=1; return -1;}
    virtual void        GetBestRect( wxRect *r ) const = 0;
    virtual bool        IsEnabled() const = 0;
    virtual void        Enable( bool enable ) = 0;
    virtual void        SetMinimum( wxInt32 v ) = 0;
    virtual void        SetMaximum( wxInt32 v ) = 0;
    virtual wxInt32     GetMinimum() const = 0;
    virtual wxInt32     GetMaximum() const = 0;
    virtual void        PulseGauge() = 0;
    virtual void        SetScrollThumb( wxInt32 value, wxInt32 thumbSize ) = 0;

    virtual void        SetFont( const wxFont & font , const wxColour& foreground , long windowStyle, bool ignoreBlack = true ) = 0;

    virtual void        SetToolTip(wxToolTip* WXUNUSED(tooltip)) { }

    // is the clicked event sent AFTER the state already changed, so no additional
    // state changing logic is required from the outside
    virtual bool        ButtonClickDidStateChange() = 0;

    virtual void        InstallEventHandler( WXWidget control = NULL ) = 0;

    // Mechanism used to keep track of whether a change should send an event
    // Do SendEvents(false) when starting actions that would trigger programmatic events
    // and SendEvents(true) at the end of the block.
    virtual void        SendEvents(bool shouldSendEvents) { m_shouldSendEvents = shouldSendEvents; }
    virtual bool        ShouldSendEvents() { return m_shouldSendEvents; }

    // static methods for associating native controls and their implementations

    // finds the impl associated with this native control
    static wxWidgetImpl*
                        FindFromWXWidget(WXWidget control);

    // finds the impl associated with this native control, if the native control itself is not known
    // also checks whether its parent is eg a registered scrollview, ie whether the control is a native subpart
    // of a known control
    static wxWidgetImpl*
                        FindBestFromWXWidget(WXWidget control);

    static void         RemoveAssociations( wxWidgetImpl* impl);

    static void         Associate( WXWidget control, wxWidgetImpl *impl );

    static WXWidget     FindFocus();

    // static creation methods, must be implemented by all toolkits

    static wxWidgetImplType*    CreateUserPane( wxWindowMac* wxpeer,
                                    wxWindowMac* parent,
                                    wxWindowID id,
                                    const wxPoint& pos,
                                    const wxSize& size,
                                    long style,
                                    long extraStyle) ;
    static wxWidgetImplType*    CreateContentView( wxNonOwnedWindow* now ) ;

    static wxWidgetImplType*    CreateButton( wxWindowMac* wxpeer,
                                    wxWindowMac* parent,
                                    wxWindowID id,
                                    const wxString& label,
                                    const wxPoint& pos,
                                    const wxSize& size,
                                    long style,
                                    long extraStyle) ;

    static wxWidgetImplType*    CreateDisclosureTriangle( wxWindowMac* wxpeer,
                                    wxWindowMac* parent,
                                    wxWindowID id,
                                    const wxString& label,
                                    const wxPoint& pos,
                                    const wxSize& size,
                                    long style,
                                    long extraStyle) ;

    static wxWidgetImplType*    CreateStaticLine( wxWindowMac* wxpeer,
                                    wxWindowMac* parent,
                                    wxWindowID id,
                                    const wxPoint& pos,
                                    const wxSize& size,
                                    long style,
                                    long extraStyle) ;

    static wxWidgetImplType*    CreateGroupBox( wxWindowMac* wxpeer,
                                    wxWindowMac* parent,
                                    wxWindowID id,
                                    const wxString& label,
                                    const wxPoint& pos,
                                    const wxSize& size,
                                    long style,
                                    long extraStyle) ;

    static wxWidgetImplType*    CreateStaticText( wxWindowMac* wxpeer,
                                    wxWindowMac* parent,
                                    wxWindowID id,
                                    const wxString& label,
                                    const wxPoint& pos,
                                    const wxSize& size,
                                    long style,
                                    long extraStyle) ;

    static wxWidgetImplType*    CreateTextControl( wxTextCtrl* wxpeer,
                                    wxWindowMac* parent,
                                    wxWindowID id,
                                    const wxString& content,
                                    const wxPoint& pos,
                                    const wxSize& size,
                                    long style,
                                    long extraStyle) ;

    static wxWidgetImplType*    CreateSearchControl( wxSearchCtrl* wxpeer,
                                    wxWindowMac* parent,
                                    wxWindowID id,
                                    const wxString& content,
                                    const wxPoint& pos,
                                    const wxSize& size,
                                    long style,
                                    long extraStyle) ;

    static wxWidgetImplType*    CreateCheckBox( wxWindowMac* wxpeer,
                                    wxWindowMac* parent,
                                    wxWindowID id,
                                    const wxString& label,
                                    const wxPoint& pos,
                                    const wxSize& size,
                                    long style,
                                    long extraStyle);

    static wxWidgetImplType*    CreateRadioButton( wxWindowMac* wxpeer,
                                    wxWindowMac* parent,
                                    wxWindowID id,
                                    const wxString& label,
                                    const wxPoint& pos,
                                    const wxSize& size,
                                    long style,
                                    long extraStyle);

    static wxWidgetImplType*    CreateToggleButton( wxWindowMac* wxpeer,
                                    wxWindowMac* parent,
                                    wxWindowID id,
                                    const wxString& label,
                                    const wxPoint& pos,
                                    const wxSize& size,
                                    long style,
                                    long extraStyle);

    static wxWidgetImplType*    CreateBitmapToggleButton( wxWindowMac* wxpeer,
                                    wxWindowMac* parent,
                                    wxWindowID id,
                                    const wxBitmap& bitmap,
                                    const wxPoint& pos,
                                    const wxSize& size,
                                    long style,
                                    long extraStyle);

    static wxWidgetImplType*    CreateBitmapButton( wxWindowMac* wxpeer,
                                    wxWindowMac* parent,
                                    wxWindowID id,
                                    const wxBitmap& bitmap,
                                    const wxPoint& pos,
                                    const wxSize& size,
                                    long style,
                                    long extraStyle);

    static wxWidgetImplType*    CreateTabView( wxWindowMac* wxpeer,
                                    wxWindowMac* parent,
                                    wxWindowID id,
                                    const wxPoint& pos,
                                    const wxSize& size,
                                    long style,
                                    long extraStyle);

    static wxWidgetImplType*    CreateGauge( wxWindowMac* wxpeer,
                                    wxWindowMac* parent,
                                    wxWindowID id,
                                    wxInt32 value,
                                    wxInt32 minimum,
                                    wxInt32 maximum,
                                    const wxPoint& pos,
                                    const wxSize& size,
                                    long style,
                                    long extraStyle);

    static wxWidgetImplType*    CreateSlider( wxWindowMac* wxpeer,
                                    wxWindowMac* parent,
                                    wxWindowID id,
                                    wxInt32 value,
                                    wxInt32 minimum,
                                    wxInt32 maximum,
                                    const wxPoint& pos,
                                    const wxSize& size,
                                    long style,
                                    long extraStyle);

    static wxWidgetImplType*    CreateSpinButton( wxWindowMac* wxpeer,
                                    wxWindowMac* parent,
                                    wxWindowID id,
                                    wxInt32 value,
                                    wxInt32 minimum,
                                    wxInt32 maximum,
                                    const wxPoint& pos,
                                    const wxSize& size,
                                    long style,
                                    long extraStyle);

    static wxWidgetImplType*    CreateScrollBar( wxWindowMac* wxpeer,
                                    wxWindowMac* parent,
                                    wxWindowID id,
                                    const wxPoint& pos,
                                    const wxSize& size,
                                    long style,
                                    long extraStyle);

    static wxWidgetImplType*    CreateChoice( wxWindowMac* wxpeer,
                                    wxWindowMac* parent,
                                    wxWindowID id,
                                    wxMenu* menu,
                                    const wxPoint& pos,
                                    const wxSize& size,
                                    long style,
                                    long extraStyle);

    static wxWidgetImplType*    CreateListBox( wxWindowMac* wxpeer,
                                    wxWindowMac* parent,
                                    wxWindowID id,
                                    const wxPoint& pos,
                                    const wxSize& size,
                                    long style,
                                    long extraStyle);

#if wxOSX_USE_COCOA
    static wxWidgetImplType*    CreateComboBox( wxComboBox* wxpeer,
                                    wxWindowMac* parent,
                                    wxWindowID id,
                                    wxMenu* menu,
                                    const wxPoint& pos,
                                    const wxSize& size,
                                    long style,
                                    long extraStyle);
#endif

    // converts from Toplevel-Content relative to local
    static void Convert( wxPoint *pt , wxWidgetImpl *from , wxWidgetImpl *to );
protected :
    bool                m_isRootControl;
    bool                m_isUserPane;
    wxWindowMac*        m_wxPeer;
    bool                m_needsFocusRect;
    bool                m_needsFrame;
    bool                m_shouldSendEvents;

    wxDECLARE_ABSTRACT_CLASS(wxWidgetImpl);
};

//
// the interface to be implemented eg by a listbox
//

class WXDLLIMPEXP_CORE wxListWidgetColumn
{
public :
    virtual ~wxListWidgetColumn() {}
} ;

class WXDLLIMPEXP_CORE wxListWidgetCellValue
{
public :
    wxListWidgetCellValue() {}
    virtual ~wxListWidgetCellValue() {}

   virtual void Set( CFStringRef value ) = 0;
    virtual void Set( const wxString& value ) = 0;
    virtual void Set( int value ) = 0;
    virtual void Check( bool check );

    virtual bool IsChecked() const;
    virtual int GetIntValue() const = 0;
    virtual wxString GetStringValue() const = 0;
} ;

class WXDLLIMPEXP_CORE wxListWidgetImpl
{
public:
    wxListWidgetImpl() {}
    virtual ~wxListWidgetImpl() { }

    virtual wxListWidgetColumn*     InsertTextColumn( unsigned pos, const wxString& title, bool editable = false,
                                wxAlignment just = wxALIGN_LEFT , int defaultWidth = -1) = 0 ;
    virtual wxListWidgetColumn*     InsertCheckColumn( unsigned pos , const wxString& title, bool editable = false,
                                wxAlignment just = wxALIGN_LEFT , int defaultWidth =  -1) = 0 ;

    // add and remove

    // TODO will be replaced
    virtual void            ListDelete( unsigned int n ) = 0;
    virtual void            ListInsert( unsigned int n ) = 0;
    virtual void            ListClear() = 0;

    // selecting

    virtual void            ListDeselectAll() = 0;
    virtual void            ListSetSelection( unsigned int n, bool select, bool multi ) = 0;
    virtual int             ListGetSelection() const = 0;
    virtual int             ListGetSelections( wxArrayInt& aSelections ) const = 0;
    virtual bool            ListIsSelected( unsigned int n ) const = 0;

    // display

    virtual void            ListScrollTo( unsigned int n ) = 0;
    virtual int             ListGetTopItem() const = 0;
    virtual void            UpdateLine( unsigned int n, wxListWidgetColumn* col = NULL ) = 0;
    virtual void            UpdateLineToEnd( unsigned int n) = 0;

    // accessing content

    virtual unsigned int    ListGetCount() const = 0;

    virtual int             DoListHitTest( const wxPoint& inpoint ) const = 0;
};

//
// interface to be implemented by a textcontrol
//

class WXDLLIMPEXP_FWD_CORE wxTextAttr;
class WXDLLIMPEXP_FWD_CORE wxTextEntry;

// common interface for all implementations
class WXDLLIMPEXP_CORE wxTextWidgetImpl

{
public :
    // Any widgets implementing this interface must be associated with a
    // wxTextEntry so instead of requiring the derived classes to implement
    // another (pure) virtual function, just take the pointer to this entry in
    // our ctor and implement GetTextEntry() ourselves.
    wxTextWidgetImpl(wxTextEntry *entry) : m_entry(entry) {}

    virtual ~wxTextWidgetImpl() {}

    wxTextEntry *GetTextEntry() const { return m_entry; }

    virtual bool CanFocus() const { return true; }

    virtual wxString GetStringValue() const = 0 ;
    virtual void SetStringValue( const wxString &val ) = 0 ;
    virtual void SetSelection( long from, long to ) = 0 ;
    virtual void GetSelection( long* from, long* to ) const = 0 ;
    virtual void WriteText( const wxString& str ) = 0 ;

    virtual bool CanClipMaxLength() const { return false; }
    virtual void SetMaxLength(unsigned long WXUNUSED(len)) {}

    virtual bool CanForceUpper() { return false; }
    virtual void ForceUpper() {}

    virtual bool GetStyle( long position, wxTextAttr& style);
    virtual void SetStyle( long start, long end, const wxTextAttr& style ) ;
    virtual void Copy() ;
    virtual void Cut() ;
    virtual void Paste() ;
    virtual bool CanPaste() const ;
    virtual void SetEditable( bool editable ) ;
    virtual long GetLastPosition() const ;
    virtual void Replace( long from, long to, const wxString &str ) ;
    virtual void Remove( long from, long to ) ;

    virtual bool HasOwnContextMenu() const
        { return false ; }

    virtual bool SetupCursor( const wxPoint& WXUNUSED(pt) )
        { return false ; }

    virtual void Clear() ;
    virtual bool CanUndo() const;
    virtual void Undo() ;
    virtual bool CanRedo() const;
    virtual void Redo() ;
    virtual int GetNumberOfLines() const ;
    virtual long XYToPosition(long x, long y) const;
    virtual bool PositionToXY(long pos, long *x, long *y) const ;
    virtual void ShowPosition(long WXUNUSED(pos)) ;
    virtual int GetLineLength(long lineNo) const ;
    virtual wxString GetLineText(long lineNo) const ;
    virtual void CheckSpelling(bool WXUNUSED(check)) { }

    virtual wxSize GetBestSize() const { return wxDefaultSize; }

    virtual bool SetHint(const wxString& WXUNUSED(hint)) { return false; }
private:
    wxTextEntry * const m_entry;

    wxDECLARE_NO_COPY_CLASS(wxTextWidgetImpl);
};

// common interface for all implementations
class WXDLLIMPEXP_CORE wxComboWidgetImpl

{
public :
    wxComboWidgetImpl() {}

    virtual ~wxComboWidgetImpl() {}

    virtual int GetSelectedItem() const { return -1; }
    virtual void SetSelectedItem(int WXUNUSED(item)) {}

    virtual int GetNumberOfItems() const { return -1; }

    virtual void InsertItem(int WXUNUSED(pos), const wxString& WXUNUSED(item)) {}

    virtual void RemoveItem(int WXUNUSED(pos)) {}

    virtual void Clear() {}
    virtual void Popup() {}
    virtual void Dismiss() {}

    virtual wxString GetStringAtIndex(int WXUNUSED(pos)) const { return wxEmptyString; }

    virtual int FindString(const wxString& WXUNUSED(text)) const { return -1; }
};

//
// common interface for buttons
//

class wxButtonImpl
{
    public :
    wxButtonImpl(){}
    virtual ~wxButtonImpl(){}

    virtual void SetPressedBitmap( const wxBitmap& bitmap ) = 0;
} ;

//
// common interface for search controls
//

class wxSearchWidgetImpl
{
public :
    wxSearchWidgetImpl(){}
    virtual ~wxSearchWidgetImpl(){}

    // search field options
    virtual void ShowSearchButton( bool show ) = 0;
    virtual bool IsSearchButtonVisible() const = 0;

    virtual void ShowCancelButton( bool show ) = 0;
    virtual bool IsCancelButtonVisible() const = 0;

    virtual void SetSearchMenu( wxMenu* menu ) = 0;

    virtual void SetDescriptiveText(const wxString& text) = 0;
} ;

//
// toplevel window implementation class
//

class wxNonOwnedWindowImpl : public wxObject
{
public :
    wxNonOwnedWindowImpl( wxNonOwnedWindow* nonownedwnd) : m_wxPeer(nonownedwnd)
    {
    }
    wxNonOwnedWindowImpl()
    {
    }
    virtual ~wxNonOwnedWindowImpl()
    {
    }

    virtual void WillBeDestroyed()
    {
    }

    virtual void Create( wxWindow* parent, const wxPoint& pos, const wxSize& size,
    long style, long extraStyle, const wxString& name ) = 0;

    virtual WXWindow GetWXWindow() const = 0;

    virtual void Raise()
    {
    }

    virtual void Lower()
    {
    }

    virtual bool Show(bool WXUNUSED(show))
    {
        return false;
    }

    virtual bool ShowWithEffect(bool show, wxShowEffect WXUNUSED(effect), unsigned WXUNUSED(timeout))
    {
        return Show(show);
    }

    virtual void Update()
    {
    }

    virtual bool SetTransparent(wxByte WXUNUSED(alpha))
    {
        return false;
    }

    virtual bool SetBackgroundColour(const wxColour& WXUNUSED(col) )
    {
        return false;
    }

    virtual void SetExtraStyle( long WXUNUSED(exStyle) )
    {
    }

    virtual void SetWindowStyleFlag( long WXUNUSED(style) )
    {
    }

    virtual bool SetBackgroundStyle(wxBackgroundStyle WXUNUSED(style))
    {
        return false ;
    }

    virtual bool CanSetTransparent()
    {
        return false;
    }

    virtual void GetContentArea( int &left , int &top , int &width , int &height ) const = 0;
    virtual void MoveWindow(int x, int y, int width, int height) = 0;
    virtual void GetPosition( int &x, int &y ) const = 0;
    virtual void GetSize( int &width, int &height ) const = 0;

    virtual bool SetShape(const wxRegion& WXUNUSED(region))
    {
        return false;
    }

    virtual void SetTitle( const wxString& title, wxFontEncoding encoding ) = 0;

    virtual bool EnableCloseButton(bool enable) = 0;
    virtual bool EnableMaximizeButton(bool enable) = 0;
    virtual bool EnableMinimizeButton(bool enable) = 0;

    virtual bool IsMaximized() const = 0;

    virtual bool IsIconized() const= 0;

    virtual void Iconize( bool iconize )= 0;

    virtual void Maximize(bool maximize) = 0;

    virtual bool IsFullScreen() const= 0;

    virtual void ShowWithoutActivating() { Show(true); }

    virtual bool EnableFullScreenView(bool enable) = 0;

    virtual bool ShowFullScreen(bool show, long style)= 0;

    virtual void RequestUserAttention(int flags) = 0;

    virtual void ScreenToWindow( int *x, int *y ) = 0;

    virtual void WindowToScreen( int *x, int *y ) = 0;

    virtual bool IsActive() = 0;

    wxNonOwnedWindow*   GetWXPeer() { return m_wxPeer; }

    static wxNonOwnedWindowImpl*
                FindFromWXWindow(WXWindow window);

    static void  RemoveAssociations( wxNonOwnedWindowImpl* impl);

    static void  Associate( WXWindow window, wxNonOwnedWindowImpl *impl );

    // static creation methods, must be implemented by all toolkits

    static wxNonOwnedWindowImpl* CreateNonOwnedWindow( wxNonOwnedWindow* wxpeer, wxWindow* parent, WXWindow native) ;

    static wxNonOwnedWindowImpl* CreateNonOwnedWindow( wxNonOwnedWindow* wxpeer, wxWindow* parent, const wxPoint& pos, const wxSize& size,
    long style, long extraStyle, const wxString& name  ) ;

    virtual void SetModified(bool WXUNUSED(modified)) { }
    virtual bool IsModified() const { return false; }

    virtual void SetRepresentedFilename(const wxString& WXUNUSED(filename)) { }

#if wxOSX_USE_IPHONE
    virtual CGFloat GetWindowLevel() const { return 0.0; }
#else
    virtual CGWindowLevel GetWindowLevel() const { return kCGNormalWindowLevel; }
#endif
    virtual void RestoreWindowLevel() {}
protected :
    wxNonOwnedWindow*   m_wxPeer;
    wxDECLARE_ABSTRACT_CLASS(wxNonOwnedWindowImpl);
};

#endif // wxUSE_GUI

//---------------------------------------------------------------------------
// cocoa bridging utilities
//---------------------------------------------------------------------------

bool wxMacInitCocoa();

class WXDLLIMPEXP_CORE wxMacAutoreleasePool
{
public :
    wxMacAutoreleasePool();
    ~wxMacAutoreleasePool();
private :
    void* m_pool;
};

// NSObject

void wxMacCocoaRelease( void* obj );
void wxMacCocoaAutorelease( void* obj );
void* wxMacCocoaRetain( void* obj );

#endif
    // _WX_PRIVATE_CORE_H_


/*
 *  MaplyTexture_private.h
 *  WhirlyGlobe-MaplyComponent
 *
 *  Created by Steve Gifford on 10/25/13.
 *  Copyright 2011-2017 mousebird consulting
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 */

#import <Foundation/Foundation.h>
#import "MaplyTexture.h"
#import "MaplyRenderController.h"
#import "WhirlyGlobe.h"

@class MaplyBaseInteractionLayer;

@interface MaplyTexture()

// The view controller the texture is nominally associated with
@property (nonatomic,weak) MaplyBaseInteractionLayer *interactLayer;

// If this came from a UIImage, the UIImage it came from
@property (nonatomic,weak) UIImage *image;

// Set if this is a sub texture reference
@property (nonatomic) bool isSubTex;

// Set if we created the texture
@property (nonatomic,assign) int width,height;

// If set, the texture ID associated with this texture
@property (nonatomic) WhirlyKit::SimpleIdentity texID;

// Set while we're waiting for the texture to go away
@property (nonatomic) bool isBeingRemoved;

// Clear out the texture we're holding
- (void)clear;

@end


package com.naman14.timber.slidinguppanel;

import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.os.Parcel;
import android.os.Parcelable;
import android.support.v4.content.ContextCompat;
import android.support.v4.view.MotionEventCompat;
import android.support.v4.view.ViewCompat;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityEvent;

import com.naman14.timber.R;

public class SlidingUpPanelLayout extends ViewGroup {

    private static final String TAG = SlidingUpPanelLayout.class.getSimpleName();

    /**
     * Default peeking out panel height
     */
    private static final int DEFAULT_PANEL_HEIGHT = 68; // dp;

    /**
     * Default anchor point height
     */
    private static final float DEFAULT_ANCHOR_POINT = 1.0f; // In relative %
    /**
     * Default height of the shadow above the peeking out panel
     */
    private static final int DEFAULT_SHADOW_HEIGHT = 4; // dp;
    /**
     * If no fade color is given by default it will fade to 80% gray.
     */
    private static final int DEFAULT_FADE_COLOR = 0x99000000;
    /**
     * Whether we should hook up the drag view clickable state
     */
    private static final boolean DEFAULT_DRAG_VIEW_CLICKABLE = true;
    /**
     * Default Minimum velocity that will be detected as a fling
     */
    private static final int DEFAULT_MIN_FLING_VELOCITY = 400; // dips per second
    /**
     * Default is set to false because that is how it was written
     */
    private static final boolean DEFAULT_OVERLAY_FLAG = false;
    /**
     * Default attributes for layout
     */
    private static final int[] DEFAULT_ATTRS = new int[]{
            android.R.attr.gravity
    };
    /**
     * Default paralax length of the main view
     */
    private static final int DEFAULT_PARALAX_OFFSET = 0;
    /**
     * Default slide panel offset when collapsed
     */
    private static final int DEFAULT_SLIDE_PANEL_OFFSET = 0;
    /**
     * Default direct offset flag
     */
    private static final boolean DEFAULT_DIRECT_OFFSET_FLAG = false;
    /**
     * Default initial state for the component
     */
    private static SlideState DEFAULT_SLIDE_STATE = SlideState.COLLAPSED;
    /**
     * The paint used to dim the main layout when sliding
     */
    private final Paint mCoveredFadePaint = new Paint();
    /**
     * Drawable used to draw the shadow between panes.
     */
    private final Drawable mShadowDrawable;
    private final ViewDragHelper mDragHelper;
    private final Rect mTmpRect = new Rect();
    /**
     * Minimum velocity that will be detected as a fling
     */
    private int mMinFlingVelocity = DEFAULT_MIN_FLING_VELOCITY;
    /**
     * The fade color used for the panel covered by the slider. 0 = no fading.
     */
    private int mCoveredFadeColor = DEFAULT_FADE_COLOR;
    /**
     * The size of the overhang in pixels.
     */
    private int mPanelHeight = -1;
    /**
     * Determines how much to slide the panel off when expanded
     */
    private int mSlidePanelOffset = 0;
    /**
     * The size of the shadow in pixels.
     */
    private int mShadowHeight = -1;
    /**
     * Paralax offset
     */
    private int mParallaxOffset = -1;
    /**
     * Clamps the Main view to the slideable view
     */
    private boolean mDirectOffset = false;
    /**
     * True if the collapsed panel should be dragged up.
     */
    private boolean mIsSlidingUp;
    /**
     * Panel overlays the windows instead of putting it underneath it.
     */
    private boolean mOverlayContent = DEFAULT_OVERLAY_FLAG;
    /**
     * If provided, the panel can be dragged by only this view. Otherwise, the entire panel can be
     * used for dragging.
     */
    private View mDragView;
    /**
     * If provided, the panel can be dragged by only this view. Otherwise, the entire panel can be
     * used for dragging.
     */
    private int mDragViewResId = -1;
    /**
     * Whether clicking on the drag view will expand/collapse
     */
    private boolean mDragViewClickable = DEFAULT_DRAG_VIEW_CLICKABLE;
    /**
     * The child view that can slide, if any.
     */
    private View mSlideableView;
    /**
     * The main view
     */
    private View mMainView;
    /**
     * The background view
     */
    private View mBackgroundView;
    private SlideState mSlideState = SlideState.COLLAPSED;
    /**
     * How far the panel is offset from its expanded position.
     * range [0, 1] where 0 = collapsed, 1 = expanded.
     */
    private float mSlideOffset;
    /**
     * How far in pixels the slideable panel may move.
     */
    private int mSlideRange;
    /**
     * A panel view is locked into internal scrolling or another condition that
     * is preventing a drag.
     */
    private boolean mIsUnableToDrag;
    /**
     * Flag indicating that sliding feature is enabled\disabled
     */
    private boolean mIsSlidingEnabled;
    /**
     * Flag indicating if a drag view can have its own touch events.  If set
     * to true, a drag view can scroll horizontally and have its own click listener.
     * <p/>
     * Default is set to false.
     */
    private boolean mIsUsingDragViewTouchEvents;
    private float mInitialMotionX;
    private float mInitialMotionY;
    private float mAnchorPoint = 1.f;
    private PanelSlideListener mPanelSlideListener;
    /**
     * Stores whether or not the pane was expanded the last time it was slideable.
     * If expand/collapse operations are invoked this state is modified. Used by
     * instance state save/restore.
     */
    private boolean mFirstLayout = true;

    public SlidingUpPanelLayout(Context context) {
        this(context, null);
    }

    public SlidingUpPanelLayout(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public SlidingUpPanelLayout(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);

        if (isInEditMode()) {
            mShadowDrawable = null;
            mDragHelper = null;
            return;
        }

        if (attrs != null) {
            TypedArray defAttrs = context.obtainStyledAttributes(attrs, DEFAULT_ATTRS);

            if (defAttrs != null) {
                int gravity = defAttrs.getInt(0, Gravity.NO_GRAVITY);
                if (gravity != Gravity.TOP && gravity != Gravity.BOTTOM) {
                    throw new IllegalArgumentException("gravity must be set to either top or bottom");
                }
                mIsSlidingUp = gravity == Gravity.BOTTOM;
            }

            defAttrs.recycle();

            TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.SlidingUpPanelLayout);

            if (ta != null) {
                mPanelHeight = ta.getDimensionPixelSize(R.styleable.SlidingUpPanelLayout_panelHeight, -1);
                mSlidePanelOffset = ta.getDimensionPixelSize(R.styleable.SlidingUpPanelLayout_slidePanelOffset, DEFAULT_SLIDE_PANEL_OFFSET);
                mShadowHeight = ta.getDimensionPixelSize(R.styleable.SlidingUpPanelLayout_shadowHeight, -1);
                mParallaxOffset = ta.getDimensionPixelSize(R.styleable.SlidingUpPanelLayout_paralaxOffset, -1);
                mDirectOffset = ta.getBoolean(R.styleable.SlidingUpPanelLayout_directOffset, DEFAULT_DIRECT_OFFSET_FLAG);

                mMinFlingVelocity = ta.getInt(R.styleable.SlidingUpPanelLayout_flingVelocity, DEFAULT_MIN_FLING_VELOCITY);
                mCoveredFadeColor = ta.getColor(R.styleable.SlidingUpPanelLayout_fadeColor, DEFAULT_FADE_COLOR);

                mDragViewResId = ta.getResourceId(R.styleable.SlidingUpPanelLayout_dragView, -1);
                mDragViewClickable = ta.getBoolean(R.styleable.SlidingUpPanelLayout_dragViewClickable, DEFAULT_DRAG_VIEW_CLICKABLE);

                mOverlayContent = ta.getBoolean(R.styleable.SlidingUpPanelLayout_overlay, DEFAULT_OVERLAY_FLAG);

                mAnchorPoint = ta.getFloat(R.styleable.SlidingUpPanelLayout_anchorPoint, DEFAULT_ANCHOR_POINT);

                mSlideState = SlideState.values()[ta.getInt(R.styleable.SlidingUpPanelLayout_initialState, DEFAULT_SLIDE_STATE.ordinal())];
            }

            ta.recycle();
        }

        final float density = context.getResources().getDisplayMetrics().density;
        if (mPanelHeight == -1) {
            mPanelHeight = (int) (DEFAULT_PANEL_HEIGHT * density + 0.5f);
        }
        if (mShadowHeight == -1) {
            mShadowHeight = (int) (DEFAULT_SHADOW_HEIGHT * density + 0.5f);
        }
        if (mParallaxOffset == -1) {
            mParallaxOffset = (int) (DEFAULT_PARALAX_OFFSET * density);
        }
        // If the shadow height is zero, don't show the shadow
        if (mShadowHeight > 0) {
            if (mIsSlidingUp) {
                mShadowDrawable = ContextCompat.getDrawable(context, R.drawable.above_shadow);
            } else {
                mShadowDrawable = ContextCompat.getDrawable(context, R.drawable.below_shadow);
            }

        } else {
            mShadowDrawable = null;
        }

        setWillNotDraw(false);

        mDragHelper = ViewDragHelper.create(this, 0.5f, new DragHelperCallback());
        mDragHelper.setMinVelocity(mMinFlingVelocity * density);

        mIsSlidingEnabled = true;
    }

    private static boolean hasOpaqueBackground(View v) {
        final Drawable bg = v.getBackground();
        return bg != null && bg.getOpacity() == PixelFormat.OPAQUE;
    }

    /**
     * Set the Drag View after the view is inflated
     */
    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        if (mDragViewResId != -1) {
            setDragView(findViewById(mDragViewResId));
        }
    }

    /**
     * @return The ARGB-packed color value used to fade the fixed pane
     */
    public int getCoveredFadeColor() {
        return mCoveredFadeColor;
    }

    /**
     * Set the color used to fade the pane covered by the sliding pane out when the pane
     * will become fully covered in the expanded state.
     *
     * @param color An ARGB-packed color value
     */
    public void setCoveredFadeColor(int color) {
        mCoveredFadeColor = color;
        invalidate();
    }

    public boolean isSlidingEnabled() {
        return mIsSlidingEnabled && mSlideableView != null;
    }

    /**
     * Set sliding enabled flag
     *
     * @param enabled flag value
     */
    public void setSlidingEnabled(boolean enabled) {
        mIsSlidingEnabled = enabled;
    }

    /**
     * @return The current collapsed panel height
     */
    public int getPanelHeight() {
        return mPanelHeight;
    }

    /**
     * Set the collapsed panel height in pixels
     *
     * @param val A height in pixels
     */
    public void setPanelHeight(int val) {
        mPanelHeight = val;
        requestLayout();
    }

    /**
     * Sets the panel offset when collapsed so you can exit
     * the boundaries of the top of the screen
     *
     * @param val Offset in pixels
     */
    public void setSlidePanelOffset(int val) {
        mSlidePanelOffset = val;
        requestLayout();
    }

    /**
     * @return The current paralax offset
     */
    public int getCurrentParalaxOffset() {
        if (mParallaxOffset < 0) {
            return 0;
        }

        return (int) (mParallaxOffset * getDirectionalSlideOffset());
    }

    /**
     * @return The directional slide offset
     */
    protected float getDirectionalSlideOffset() {
        return mIsSlidingUp ? -mSlideOffset : mSlideOffset;
    }

    /**
     * Sets the panel slide listener
     *
     * @param listener
     */
    public void setPanelSlideListener(PanelSlideListener listener) {
        mPanelSlideListener = listener;
    }

    /**
     * Set the draggable view portion. Use to null, to allow the whole panel to be draggable
     *
     * @param dragView A view that will be used to drag the panel.
     */
    public void setDragView(View dragView) {
        if (mDragView != null && mDragViewClickable) {
            mDragView.setOnClickListener(null);
        }
        mDragView = dragView;
        if (mDragView != null) {
            mDragView.setClickable(true);
            mDragView.setFocusable(false);
            mDragView.setFocusableInTouchMode(false);
            if (mDragViewClickable) {
                mDragView.setOnClickListener(new OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        if (!isEnabled()) return;
                        if (!isPanelExpanded() && !isPanelAnchored()) {
                            expandPanel(mAnchorPoint);
                        } else {
                            collapsePanel();
                        }
                    }
                });
            }
        }
    }

    /**
     * Gets the currently set anchor point
     *
     * @return the currently set anchor point
     */
    public float getAnchorPoint() {
        return mAnchorPoint;
    }

    /**
     * Set an anchor point where the panel can stop during sliding
     *
     * @param anchorPoint A value between 0 and 1, determining the position of the anchor point
     *                    starting from the top of the layout.
     */
    public void setAnchorPoint(float anchorPoint) {
        if (anchorPoint > 0 && anchorPoint <= 1) {
            mAnchorPoint = anchorPoint;
        }
    }

    /**
     * Check if the panel is set as an overlay.
     */
    public boolean isOverlayed() {
        return mOverlayContent;
    }

    /**
     * Sets whether or not the panel overlays the content
     *
     * @param overlayed
     */
    public void setOverlayed(boolean overlayed) {
        mOverlayContent = overlayed;
    }

    void dispatchOnPanelSlide(View panel) {
        if (mPanelSlideListener != null) {
            mPanelSlideListener.onPanelSlide(panel, mSlideOffset);
        }
    }

    void dispatchOnPanelExpanded(View panel) {
        if (mPanelSlideListener != null) {
            mPanelSlideListener.onPanelExpanded(panel);
        }
        sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
    }

    void dispatchOnPanelCollapsed(View panel) {
        if (mPanelSlideListener != null) {
            mPanelSlideListener.onPanelCollapsed(panel);
        }
        sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
    }

    void dispatchOnPanelAnchored(View panel) {
        if (mPanelSlideListener != null) {
            mPanelSlideListener.onPanelAnchored(panel);
        }
        sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
    }

    void dispatchOnPanelHidden(View panel) {
        if (mPanelSlideListener != null) {
            mPanelSlideListener.onPanelHidden(panel);
        }
        sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
    }

    void updateObscuredViewVisibility() {
        if (getChildCount() == 0) {
            return;
        }
        final int leftBound = getPaddingLeft();
        final int rightBound = getWidth() - getPaddingRight();
        final int topBound = getPaddingTop();
        final int bottomBound = getHeight() - getPaddingBottom();
        final int left;
        final int right;
        final int top;
        final int bottom;
        if (mSlideableView != null && hasOpaqueBackground(mSlideableView)) {
            left = mSlideableView.getLeft();
            right = mSlideableView.getRight();
            top = mSlideableView.getTop();
            bottom = mSlideableView.getBottom();
        } else {
            left = right = top = bottom = 0;
        }
        View child = mMainView;
        final int clampedChildLeft = Math.max(leftBound, child.getLeft());
        final int clampedChildTop = Math.max(topBound, child.getTop());
        final int clampedChildRight = Math.min(rightBound, child.getRight());
        final int clampedChildBottom = Math.min(bottomBound, child.getBottom());
        final int vis;
        if (clampedChildLeft >= left && clampedChildTop >= top &&
                clampedChildRight <= right && clampedChildBottom <= bottom) {
            vis = INVISIBLE;
        } else {
            vis = VISIBLE;
        }
        child.setVisibility(vis);
    }

    void setAllChildrenVisible() {
        for (int i = 0, childCount = getChildCount(); i < childCount; i++) {
            final View child = getChildAt(i);
            if (child.getVisibility() == INVISIBLE) {
                child.setVisibility(VISIBLE);
            }
        }
    }

    @Override
    protected void onAttachedToWindow() {
        super.onAttachedToWindow();
        mFirstLayout = true;
    }

    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        mFirstLayout = true;
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        final int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        final int heightSize = MeasureSpec.getSize(heightMeasureSpec);

        if (widthMode != MeasureSpec.EXACTLY) {
            throw new IllegalStateException("Width must have an exact value or MATCH_PARENT");
        } else if (heightMode != MeasureSpec.EXACTLY) {
            throw new IllegalStateException("Height must have an exact value or MATCH_PARENT");
        }

        final int childCount = getChildCount();

        if (childCount != 2 && childCount != 3) {
            throw new IllegalStateException("Sliding up panel layout must have exactly 2 or 3 children!");
        }

        if (childCount == 2) {
            mMainView = getChildAt(0);
            mSlideableView = getChildAt(1);
        } else {
            mBackgroundView = getChildAt(0);
            mMainView = getChildAt(1);
            mSlideableView = getChildAt(2);
        }

        if (mDragView == null) {
            setDragView(mSlideableView);
        }

        // If the sliding panel is not visible, then put the whole view in the hidden state
        if (mSlideableView.getVisibility() == GONE) {
            mSlideState = SlideState.HIDDEN;
        }

        int layoutHeight = heightSize - getPaddingTop() - getPaddingBottom();

        // First pass. Measure based on child LayoutParams width/height.
        for (int i = 0; i < childCount; i++) {
            final View child = getChildAt(i);
            final LayoutParams lp = (LayoutParams) child.getLayoutParams();

            // We always measure the sliding panel in order to know it's height (needed for show panel)
            if (child.getVisibility() == GONE && child == mMainView) {
                continue;
            }

            int height = layoutHeight;
            if (child == mMainView && !mOverlayContent && mSlideState != SlideState.HIDDEN) {
                height -= mPanelHeight;
            }

            int childWidthSpec;
            if (lp.width == LayoutParams.WRAP_CONTENT) {
                childWidthSpec = MeasureSpec.makeMeasureSpec(widthSize, MeasureSpec.AT_MOST);
            } else if (lp.width == LayoutParams.MATCH_PARENT) {
                childWidthSpec = MeasureSpec.makeMeasureSpec(widthSize, MeasureSpec.EXACTLY);
            } else {
                childWidthSpec = MeasureSpec.makeMeasureSpec(lp.width, MeasureSpec.EXACTLY);
            }

            int childHeightSpec;
            if (lp.height == LayoutParams.WRAP_CONTENT) {
                childHeightSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST);
            } else if (lp.height == LayoutParams.MATCH_PARENT) {
                childHeightSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);
            } else {
                childHeightSpec = MeasureSpec.makeMeasureSpec(lp.height, MeasureSpec.EXACTLY);
            }

            if (child == mSlideableView) {
                mSlideRange = MeasureSpec.getSize(childHeightSpec) - mPanelHeight + mSlidePanelOffset;
                childHeightSpec += mSlidePanelOffset;
            }

            child.measure(childWidthSpec, childHeightSpec);
        }

        setMeasuredDimension(widthSize, heightSize);
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        final int paddingLeft = getPaddingLeft();
        final int paddingTop = getPaddingTop();

        final int childCount = getChildCount();

        if (mFirstLayout) {
            switch (mSlideState) {
                case EXPANDED:
                    mSlideOffset = 1.0f;
                    break;
                case ANCHORED:
                    mSlideOffset = mAnchorPoint;
                    break;
                case HIDDEN:
                    int newTop = computePanelTopPosition(0.0f) + (mIsSlidingUp ? +mPanelHeight : -mPanelHeight);
                    mSlideOffset = computeSlideOffset(newTop);
                    break;
                default:
                    mSlideOffset = 0.f;
                    break;
            }
        }

        for (int i = 0; i < childCount; i++) {
            final View child = getChildAt(i);

            // Always layout the sliding view on the first layout
            if (child.getVisibility() == GONE && (child == mMainView || mFirstLayout)) {
                continue;
            }

            final int childHeight = child.getMeasuredHeight();
            int childTop = paddingTop;

            if (child == mSlideableView) {
                childTop = computePanelTopPosition(mSlideOffset);
            }

            if (!mIsSlidingUp) {
                if (child == mMainView && !mOverlayContent) {
                    childTop = computePanelTopPosition(mSlideOffset) + mSlideableView.getMeasuredHeight();
                }
            }
            final int childBottom = childTop + childHeight;
            final int childLeft = paddingLeft;
            final int childRight = childLeft + child.getMeasuredWidth();

            child.layout(childLeft, childTop, childRight, childBottom);
        }

        if (mFirstLayout) {
            updateObscuredViewVisibility();
        }

        mFirstLayout = false;
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        // Recalculate sliding panes and their details
        if (h != oldh) {
            mFirstLayout = true;
        }
    }

    /**
     * Set if the drag view can have its own touch events.  If set
     * to true, a drag view can scroll horizontally and have its own click listener.
     * <p/>
     * Default is set to false.
     */
    public void setEnableDragViewTouchEvents(boolean enabled) {
        mIsUsingDragViewTouchEvents = enabled;
    }

    @Override
    public void setEnabled(boolean enabled) {
        if (!enabled) {
            collapsePanel();
        }
        super.setEnabled(enabled);
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        final int action = MotionEventCompat.getActionMasked(ev);

        if (!isEnabled() || !mIsSlidingEnabled || (mIsUnableToDrag && action != MotionEvent.ACTION_DOWN)) {
            mDragHelper.cancel();
            return super.onInterceptTouchEvent(ev);
        }

        if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {
            mDragHelper.cancel();
            return false;
        }

        final float x = ev.getX();
        final float y = ev.getY();

        switch (action) {
            case MotionEvent.ACTION_DOWN: {
                mIsUnableToDrag = false;
                mInitialMotionX = x;
                mInitialMotionY = y;
                break;
            }

            case MotionEvent.ACTION_MOVE: {
                final float adx = Math.abs(x - mInitialMotionX);
                final float ady = Math.abs(y - mInitialMotionY);
                final int dragSlop = mDragHelper.getTouchSlop();

                // Handle any horizontal scrolling on the drag view.
                if (mIsUsingDragViewTouchEvents && adx > dragSlop && ady < dragSlop) {
                    return super.onInterceptTouchEvent(ev);
                }

                if ((ady > dragSlop && adx > ady) || !isDragViewUnder((int) mInitialMotionX, (int) mInitialMotionY)) {
                    mDragHelper.cancel();
                    mIsUnableToDrag = true;
                    return false;
                }
                break;
            }
        }

        return mDragHelper.shouldInterceptTouchEvent(ev);
    }

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        if (!isSlidingEnabled()) {
            return super.onTouchEvent(ev);
        }
        mDragHelper.processTouchEvent(ev);
        return true;
    }

    private boolean isDragViewUnder(int x, int y) {
        if (mDragView == null) return false;
        int[] viewLocation = new int[2];
        mDragView.getLocationOnScreen(viewLocation);
        int[] parentLocation = new int[2];
        this.getLocationOnScreen(parentLocation);
        int screenX = parentLocation[0] + x;
        int screenY = parentLocation[1] + y;
        return screenX >= viewLocation[0] && screenX < viewLocation[0] + mDragView.getWidth() &&
                screenY >= viewLocation[1] && screenY < viewLocation[1] + mDragView.getHeight();
    }

    private boolean expandPanel(View pane, int initialVelocity, float mSlideOffset) {
        return mFirstLayout || smoothSlideTo(mSlideOffset, initialVelocity);
    }

    private boolean collapsePanel(View pane, int initialVelocity) {
        return mFirstLayout || smoothSlideTo(0.0f, initialVelocity);
    }

    /*
     * Computes the top position of the panel based on the slide offset.
     */
    private int computePanelTopPosition(float slideOffset) {
        int slidingViewHeight = mSlideableView != null ? mSlideableView.getMeasuredHeight() : 0;
        int slidePixelOffset = (int) (slideOffset * mSlideRange);
        // Compute the top of the panel if its collapsed
        return mIsSlidingUp
                ? getMeasuredHeight() - getPaddingBottom() - mPanelHeight - slidePixelOffset
                : getPaddingTop() - slidingViewHeight + mPanelHeight + slidePixelOffset;
    }

    /*
     * Computes the slide offset based on the top position of the panel
     */
    private float computeSlideOffset(int topPosition) {
        // Compute the panel top position if the panel is collapsed (offset 0)
        final int topBoundCollapsed = computePanelTopPosition(0);

        // Determine the new slide offset based on the collapsed top position and the new required
        // top position
        return (mIsSlidingUp
                ? (float) (topBoundCollapsed - topPosition) / mSlideRange
                : (float) (topPosition - topBoundCollapsed) / mSlideRange);
    }

    /**
     * Collapse the sliding pane if it is currently slideable. If first layout
     * has already completed this will animate.
     *
     * @return true if the pane was slideable and is now collapsed/in the process of collapsing
     */
    public boolean collapsePanel() {
        if (mFirstLayout) {
            mSlideState = SlideState.COLLAPSED;
            return true;
        } else {
            if (mSlideState == SlideState.HIDDEN || mSlideState == SlideState.COLLAPSED)
                return false;
            return collapsePanel(mSlideableView, 0);
        }
    }

    /**
     * Expand the sliding pane if it is currently slideable.
     *
     * @return true if the pane was slideable and is now expanded/in the process of expading
     */
    public boolean expandPanel() {
        if (mFirstLayout) {
            mSlideState = SlideState.EXPANDED;
            return true;
        } else {
            return expandPanel(1.0f);
        }
    }

    /**
     * Expand the sliding pane to the anchor point if it is currently slideable.
     *
     * @return true if the pane was slideable and is now expanded/in the process of expading
     */
    public boolean anchorPanel() {
        if (mFirstLayout) {
            mSlideState = SlideState.ANCHORED;
            return true;
        } else {
            return expandPanel(mAnchorPoint);
        }
    }

    /**
     * Partially expand the sliding panel up to a specific offset
     *
     * @param mSlideOffset Value between 0 and 1, where 0 is completely expanded.
     * @return true if the pane was slideable and is now expanded/in the process of expanding
     */
    public boolean expandPanel(float mSlideOffset) {
        if (mSlideableView == null || mSlideState == SlideState.EXPANDED) return false;
        mSlideableView.setVisibility(View.VISIBLE);
        return expandPanel(mSlideableView, 0, mSlideOffset);
    }

    /**
     * Check if the sliding panel in this layout is fully expanded.
     *
     * @return true if sliding panel is completely expanded
     */
    public boolean isPanelExpanded() {
        return mSlideState == SlideState.EXPANDED;
    }

    /**
     * Check if the sliding panel in this layout is anchored.
     *
     * @return true if sliding panel is anchored
     */
    public boolean isPanelAnchored() {
        return mSlideState == SlideState.ANCHORED;
    }

    /**
     * Check if the sliding panel in this layout is currently visible.
     *
     * @return true if the sliding panel is visible.
     */
    public boolean isPanelHidden() {
        return mSlideState == SlideState.HIDDEN;
    }

    /**
     * Shows the panel from the hidden state
     */
    public void showPanel() {
        if (mFirstLayout) {
            mSlideState = SlideState.COLLAPSED;
        } else {
            if (mSlideableView == null || mSlideState != SlideState.HIDDEN) return;
            mSlideableView.setVisibility(View.VISIBLE);
            requestLayout();
            smoothSlideTo(0, 0);
        }
    }

    /**
     * Hides the sliding panel entirely.
     */
    public void hidePanel() {
        if (mFirstLayout) {
            mSlideState = SlideState.HIDDEN;
        } else {
            if (mSlideState == SlideState.DRAGGING || mSlideState == SlideState.HIDDEN) return;
            int newTop = computePanelTopPosition(0.0f) + (mIsSlidingUp ? +mPanelHeight : -mPanelHeight);
            smoothSlideTo(computeSlideOffset(newTop), 0);
        }
    }

    @SuppressLint("NewApi")
    private void onPanelDragged(int newTop) {
        mSlideState = SlideState.DRAGGING;
        // Recompute the slide offset based on the new top position
        mSlideOffset = computeSlideOffset(newTop);
        // Update the parallax based on the new slide offset
        if ((mParallaxOffset > 0 || mDirectOffset) && mSlideOffset >= 0) {
            int mainViewOffset = 0;
            if (mParallaxOffset > 0) {
                mainViewOffset = getCurrentParalaxOffset();
            } else {
                mainViewOffset = (int) (getDirectionalSlideOffset() * mSlideRange);
            }

            mMainView.setTranslationY(mainViewOffset);
        }

        // Dispatch the slide event
        dispatchOnPanelSlide(mSlideableView);
        // If the slide offset is negative, and overlay is not on, we need to increase the
        // height of the main content
        if (mSlideOffset <= 0 && !mOverlayContent) {
            // expand the main view
            LayoutParams lp = (LayoutParams) mMainView.getLayoutParams();
            lp.height = mIsSlidingUp ? (newTop - getPaddingBottom()) : (getHeight() - getPaddingBottom() - mSlideableView.getMeasuredHeight() - newTop);
            mMainView.requestLayout();
        }
    }

    @Override
    protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
        boolean result;
        final int save = canvas.save(Canvas.CLIP_SAVE_FLAG);

        if (isSlidingEnabled() && mMainView == child) {
            // Clip against the slider; no sense drawing what will immediately be covered,
            // Unless the panel is set to overlay content
            if (!mOverlayContent) {
                canvas.getClipBounds(mTmpRect);
                if (mIsSlidingUp) {
                    mTmpRect.bottom = Math.min(mTmpRect.bottom, mSlideableView.getTop());
                } else {
                    mTmpRect.top = Math.max(mTmpRect.top, mSlideableView.getBottom());
                }
                canvas.clipRect(mTmpRect);
            }
        }

        result = super.drawChild(canvas, child, drawingTime);
        canvas.restoreToCount(save);

        if (mCoveredFadeColor != 0 && mSlideOffset > 0) {
            final int baseAlpha = (mCoveredFadeColor & 0xff000000) >>> 24;
            final int imag = (int) (baseAlpha * mSlideOffset);
            final int color = imag << 24 | (mCoveredFadeColor & 0xffffff);
            mCoveredFadePaint.setColor(color);
            canvas.drawRect(mTmpRect, mCoveredFadePaint);
        }

        return result;
    }

    /**
     * Smoothly animate mDraggingPane to the target X position within its range.
     *
     * @param slideOffset position to animate to
     * @param velocity    initial velocity in case of fling, or 0.
     */
    boolean smoothSlideTo(float slideOffset, int velocity) {
        if (!isSlidingEnabled()) {
            // Nothing to do.
            return false;
        }

        int panelTop = computePanelTopPosition(slideOffset);
        if (mDragHelper.smoothSlideViewTo(mSlideableView, mSlideableView.getLeft(), panelTop)) {
            setAllChildrenVisible();
            ViewCompat.postInvalidateOnAnimation(this);
            return true;
        }
        return false;
    }

    @Override
    public void computeScroll() {
        if (mDragHelper != null && mDragHelper.continueSettling(true)) {
            if (!isSlidingEnabled()) {
                mDragHelper.abort();
                return;
            }

            ViewCompat.postInvalidateOnAnimation(this);
        }
    }

    @Override
    public void draw(Canvas c) {
        super.draw(c);

        if (!isSlidingEnabled()) {
            // No need to draw a shadow if we don't have one.
            return;
        }

        final int right = mSlideableView.getRight();
        final int top;
        final int bottom;
        if (mIsSlidingUp) {
            top = mSlideableView.getTop() - mShadowHeight;
            bottom = mSlideableView.getTop();
        } else {
            top = mSlideableView.getBottom();
            bottom = mSlideableView.getBottom() + mShadowHeight;
        }
        final int left = mSlideableView.getLeft();

        if (mShadowDrawable != null) {
            mShadowDrawable.setBounds(left, top, right, bottom);
            mShadowDrawable.draw(c);
        }
    }

    /**
     * Tests scrollability within child views of v given a delta of dx.
     *
     * @param v      View to test for horizontal scrollability
     * @param checkV Whether the view v passed should itself be checked for scrollability (true),
     *               or just its children (false).
     * @param dx     Delta scrolled in pixels
     * @param x      X coordinate of the active touch point
     * @param y      Y coordinate of the active touch point
     * @return true if child views of v can be scrolled by delta of dx.
     */
    protected boolean canScroll(View v, boolean checkV, int dx, int x, int y) {
        if (v instanceof ViewGroup) {
            final ViewGroup group = (ViewGroup) v;
            final int scrollX = v.getScrollX();
            final int scrollY = v.getScrollY();
            final int count = group.getChildCount();
            // Count backwards - let topmost views consume scroll distance first.
            for (int i = count - 1; i >= 0; i--) {
                final View child = group.getChildAt(i);
                if (x + scrollX >= child.getLeft() && x + scrollX < child.getRight() &&
                        y + scrollY >= child.getTop() && y + scrollY < child.getBottom() &&
                        canScroll(child, true, dx, x + scrollX - child.getLeft(),
                                y + scrollY - child.getTop())) {
                    return true;
                }
            }
        }
        return checkV && ViewCompat.canScrollHorizontally(v, -dx);
    }

    @Override
    protected ViewGroup.LayoutParams generateDefaultLayoutParams() {
        return new LayoutParams();
    }

    @Override
    protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
        return p instanceof MarginLayoutParams
                ? new LayoutParams((MarginLayoutParams) p)
                : new LayoutParams(p);
    }

    @Override
    protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
        return p instanceof LayoutParams && super.checkLayoutParams(p);
    }

    @Override
    public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) {
        return new LayoutParams(getContext(), attrs);
    }

    @Override
    public Parcelable onSaveInstanceState() {
        Parcelable superState = super.onSaveInstanceState();

        SavedState ss = new SavedState(superState);
        ss.mSlideState = mSlideState;

        return ss;
    }

    @Override
    public void onRestoreInstanceState(Parcelable state) {
        SavedState ss = (SavedState) state;
        super.onRestoreInstanceState(ss.getSuperState());
        mSlideState = ss.mSlideState;
    }

    /**
     * Current state of the slideable view.
     */
    private enum SlideState {
        EXPANDED,
        COLLAPSED,
        ANCHORED,
        HIDDEN,
        DRAGGING
    }

    /**
     * Listener for monitoring events about sliding panes.
     */
    public interface PanelSlideListener {
        /**
         * Called when a sliding pane's position changes.
         *
         * @param panel       The child view that was moved
         * @param slideOffset The new offset of this sliding pane within its range, from 0-1
         */
        void onPanelSlide(View panel, float slideOffset);

        /**
         * Called when a sliding panel becomes slid completely collapsed.
         *
         * @param panel The child view that was slid to an collapsed position
         */
        void onPanelCollapsed(View panel);

        /**
         * Called when a sliding panel becomes slid completely expanded.
         *
         * @param panel The child view that was slid to a expanded position
         */
        void onPanelExpanded(View panel);

        /**
         * Called when a sliding panel becomes anchored.
         *
         * @param panel The child view that was slid to a anchored position
         */
        void onPanelAnchored(View panel);

        /**
         * Called when a sliding panel becomes completely hidden.
         *
         * @param panel The child view that was slid to a hidden position
         */
        void onPanelHidden(View panel);
    }

    /**
     * No-op stubs for {@link PanelSlideListener}. If you only want to implement a subset
     * of the listener methods you can extend this instead of implement the full interface.
     */
    public static class SimplePanelSlideListener implements PanelSlideListener {
        @Override
        public void onPanelSlide(View panel, float slideOffset) {
        }

        @Override
        public void onPanelCollapsed(View panel) {
        }

        @Override
        public void onPanelExpanded(View panel) {
        }

        @Override
        public void onPanelAnchored(View panel) {
        }

        @Override
        public void onPanelHidden(View panel) {
        }
    }

    public static class LayoutParams extends MarginLayoutParams {
        private static final int[] ATTRS = new int[]{
                android.R.attr.layout_weight
        };

        public LayoutParams() {
            super(MATCH_PARENT, MATCH_PARENT);
        }

        public LayoutParams(int width, int height) {
            super(width, height);
        }

        public LayoutParams(ViewGroup.LayoutParams source) {
            super(source);
        }

        public LayoutParams(MarginLayoutParams source) {
            super(source);
        }

        public LayoutParams(LayoutParams source) {
            super(source);
        }

        public LayoutParams(Context c, AttributeSet attrs) {
            super(c, attrs);

            final TypedArray a = c.obtainStyledAttributes(attrs, ATTRS);
            a.recycle();
        }

    }

    static class SavedState extends BaseSavedState {
        public static final Creator<SavedState> CREATOR =
                new Creator<SavedState>() {
                    @Override
                    public SavedState createFromParcel(Parcel in) {
                        return new SavedState(in);
                    }

                    @Override
                    public SavedState[] newArray(int size) {
                        return new SavedState[size];
                    }
                };
        SlideState mSlideState;

        SavedState(Parcelable superState) {
            super(superState);
        }

        private SavedState(Parcel in) {
            super(in);
            try {
                mSlideState = Enum.valueOf(SlideState.class, in.readString());
            } catch (IllegalArgumentException e) {
                mSlideState = SlideState.COLLAPSED;
            }
        }

        @Override
        public void writeToParcel(Parcel out, int flags) {
            super.writeToParcel(out, flags);
            out.writeString(mSlideState.toString());
        }
    }

    private class DragHelperCallback extends ViewDragHelper.Callback {

        @Override
        public boolean tryCaptureView(View child, int pointerId) {
            if (mIsUnableToDrag) {
                return false;
            }

            return child == mSlideableView;
        }

        @Override
        public void onViewDragStateChanged(int state) {
            if (mDragHelper.getViewDragState() == ViewDragHelper.STATE_IDLE) {
                mSlideOffset = computeSlideOffset(mSlideableView.getTop());

                if (mSlideOffset == 1) {
                    if (mSlideState != SlideState.EXPANDED) {
                        updateObscuredViewVisibility();
                        mSlideState = SlideState.EXPANDED;
                        dispatchOnPanelExpanded(mSlideableView);
                    }
                } else if (mSlideOffset == 0) {
                    if (mSlideState != SlideState.COLLAPSED) {
                        mSlideState = SlideState.COLLAPSED;
                        dispatchOnPanelCollapsed(mSlideableView);
                    }
                } else if (mSlideOffset < 0) {
                    mSlideState = SlideState.HIDDEN;
                    mSlideableView.setVisibility(View.GONE);
                    dispatchOnPanelHidden(mSlideableView);
                } else if (mSlideState != SlideState.ANCHORED) {
                    updateObscuredViewVisibility();
                    mSlideState = SlideState.ANCHORED;
                    dispatchOnPanelAnchored(mSlideableView);
                }
            }
        }

        @Override
        public void onViewCaptured(View capturedChild, int activePointerId) {
            setAllChildrenVisible();
        }

        @Override
        public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {
            onPanelDragged(top);
            invalidate();
        }

        @Override
        public void onViewReleased(View releasedChild, float xvel, float yvel) {
            int target = 0;

            // direction is always positive if we are sliding in the expanded direction
            float direction = mIsSlidingUp ? -yvel : yvel;

            if (direction > 0) {
                // swipe up -> expand
                target = computePanelTopPosition(1.0f);
            } else if (direction < 0) {
                // swipe down -> collapse
                target = computePanelTopPosition(0.0f);
            } else if (mAnchorPoint != 1 && mSlideOffset >= (1.f + mAnchorPoint) / 2) {
                // zero velocity, and far enough from anchor point => expand to the top
                target = computePanelTopPosition(1.0f);
            } else if (mAnchorPoint == 1 && mSlideOffset >= 0.5f) {
                // zero velocity, and far enough from anchor point => expand to the top
                target = computePanelTopPosition(1.0f);
            } else if (mAnchorPoint != 1 && mSlideOffset >= mAnchorPoint) {
                target = computePanelTopPosition(mAnchorPoint);
            } else if (mAnchorPoint != 1 && mSlideOffset >= mAnchorPoint / 2) {
                target = computePanelTopPosition(mAnchorPoint);
            } else {
                // settle at the bottom
                target = computePanelTopPosition(0.0f);
            }

            mDragHelper.settleCapturedViewAt(releasedChild.getLeft(), target);
            invalidate();
        }

        @Override
        public int getViewVerticalDragRange(View child) {
            return mSlideRange;
        }

        @Override
        public int clampViewPositionVertical(View child, int top, int dy) {
            final int collapsedTop = computePanelTopPosition(0.f);
            final int expandedTop = computePanelTopPosition(1.0f);
            if (mIsSlidingUp) {
                return Math.min(Math.max(top, expandedTop), collapsedTop);
            } else {
                return Math.min(Math.max(top, collapsedTop), expandedTop);
            }
        }
    }
}


//
//  YPCycleBanner.h
//  Wuxianda
//
//  Created by MichaelPPP on 16/7/11.
//  Copyright © 2016年 michaelhuyp. All rights reserved.
//  无线轮播图控件封装

#import <UIKit/UIKit.h>

/** 轮播图将要开始拖动发出的通知 */
UIKIT_EXTERN NSString* const kCycleBannerWillBeginDraggingNotification;
/** 轮播图结束滑动发出的通知 */
UIKIT_EXTERN NSString* const kCycleBannerDidEndDeceleratingNotification;

@class YPCycleBanner;
typedef void(^YPCycleBannerBlock)(NSUInteger didselectIndex);

@interface YPCycleBanner : UIView

/** 图片url数组 */
@property (nonatomic, copy) NSArray *models;

/** 自动滚动间隔时间,默认5s */
@property (nonatomic, assign) NSTimeInterval autoScrollTimeInterval;

/**
 *  初始化方法
 *
 *  @param frame            轮播图的frame
 *  @param placeholderImage 占位图片
 *  @param block            block
 *
 *  @return 轮播图实例
 */
+ (instancetype)bannerViewWithFrame:(CGRect)frame placeholderImage:(UIImage *)placeholderImage block:(YPCycleBannerBlock)block;

@end


Welcome to the new official home of the FirePHP documentation.

NOTE: Some external links to FirePHP information are still included as not everything has been consolidated here yet.

Status
======

FirePHP 1.0 is currently in **public BETA** and will go STABLE very soon. Your [feedback](OpenSource#support) is important to get us there.

FirePHP 1.0
===========

Welcome to FirePHP 1.0, the result of 3+ years of learning and experimenting on how to build a PHP development and debugging solution that
can save you some real time and frustration.

**FirePHP is an advanced logging system that can display PHP variables in the browser as an application is navigated.** All communication
is out of band to the application meaning that the logging data will not interfere with the normal functioning of the application.

Traditionally, if you are not using an interactive debugger, you would typically debug your application with:

    CODE: {"lang":"php"}

    var_dump($variable);
    // or
    print_r($variable);

The problem with this approach is that:

  * The debug output is intermingled with your response data leading to broken page display and ajax responses
  * It is difficult to distinguish between many printed variables
  * It is impossible to determine where a specific debug message was triggered
  * The debug statements must be removed again when done
  * Larger object graphs (especially cyclical ones) cannot be printed or are too large too navigate intelligently

Previous attempts to solve these shortcomings have resulted in:

  * Interactive debuggers
  * PHP libraries that make printed variables more easy to navigate on the page
  * PHP libraries that log variables to files for later inspection

Each of these solutions have their place but overall the shortcomings make for a less than ideal day-to-day development
and debugging experience for the average developer in many/most cases.

The original goal of FirePHP (back in 2007) was to provide another approach by sending debug data in the response headers
(no interference with response data) and display this nicely in the [Firebug](http://getfirebug.com/) [Console](http://getfirebug.com/commandline).
The approach seems to solve some real problems and is getting some good attention as FirePHP has become and continues to become more popular.

The FirePHP 1.0 system is the next incarnation of this approach and built from the groud up based on the
[latest research and technology](http://christophdorn.com/Research/) to provide the foundation for a new kind of
development and debugging approach that combines the best of:

  * **Error Reporting** - PHP native and used to detect syntax errors and API usage violations.
  * **Print Statements** - Deliberate *var_dump()* or *print()* statements by developer used to track execution flow and variables.
  * **Logging** - Deliberate logging to a file or other facility used to track events, execution flow and variables.
  * **Interactive Debugging** - PHP extension used by developer to directly interact with a running script to track live variable state.

PLANNED: FirePHP integration with [Xdebug](http://xdebug.org/) is planned.

With FirePHP 1.0, variables are logged using the [Insight API](API/Insight):

    CODE: {"lang":"php", "run": "http://reference.developercompanion.com/Tools/FirePHPCompanion/Run/Examples/TestRunner/index.php?x-insight=activate&action=run&snippet=insight-devcomp/snippets/HelloWorld"}

    $console = FirePHP::to('page')->console();
    $console->label('The Label')->log('Hello World');
    $console->label('A Variable')->log($_GET);

NOTE: The [traditional FirePHP API](API/FirePHP) is still supported.

And displayed in the [Firebug](http://getfirebug.com/) [Console](http://getfirebug.com/commandline) or the [DeveloperCompanion](Clients#devcomp) Request Inspector respectively:

![Firebug Console Image](resources/images/screenshots/HelloWorldFirebugConsole.png) ![DeveloperCompanion Request Inspector Image](resources/images/screenshots/HelloWorldDeveloperCompanion.png)

FirePHP 1.0 is all about getting all types of meta and debug data from the server/application to the client in an intelligent way.
Now that variable logging is pretty much complete further development will be adding support for displaying server log files, PHP configuration information,
application source files and much more in the client.

We welcome your interest in FirePHP and hope that it will save you time and frustration.

Next Step: [Install](Install)


var baseFor = require('./_baseFor'),
    castFunction = require('./_castFunction'),
    keysIn = require('./keysIn');

/**
 * Iterates over own and inherited enumerable string keyed properties of an
 * object and invokes `iteratee` for each property. The iteratee is invoked
 * with three arguments: (value, key, object). Iteratee functions may exit
 * iteration early by explicitly returning `false`.
 *
 * @static
 * @memberOf _
 * @since 0.3.0
 * @category Object
 * @param {Object} object The object to iterate over.
 * @param {Function} [iteratee=_.identity] The function invoked per iteration.
 * @returns {Object} Returns `object`.
 * @see _.forInRight
 * @example
 *
 * function Foo() {
 *   this.a = 1;
 *   this.b = 2;
 * }
 *
 * Foo.prototype.c = 3;
 *
 * _.forIn(new Foo, function(value, key) {
 *   console.log(key);
 * });
 * // => Logs 'a', 'b', then 'c' (iteration order is not guaranteed).
 */
function forIn(object, iteratee) {
  return object == null
    ? object
    : baseFor(object, castFunction(iteratee), keysIn);
}

module.exports = forIn;


import AbstractStore from './AbstractStore';
import preferences from '../../preferences';

export default class MSTChangesStore extends AbstractStore {
  mstLogEnabled = false;

  itemsDataByRootId = {};
  rootNamesById = {};

  constructor(bridge) {
    super();
    this.bridge = bridge;

    this.addDisposer(
      bridge.sub('frontend:append-mst-log-items', (newLogItem) => {
        const rootId = newLogItem.rootId;
        if (!this.itemsDataByRootId[rootId]) {
          if (!this.activeRootId) {
            this.activeRootId = rootId;
            this.emit('activeRootId');
          }
          this.itemsDataByRootId[rootId] = Object.create(null);
          this.itemsDataByRootId[rootId].logItemsIds = [];
          this.itemsDataByRootId[rootId].logItemsById = {};
        }
        const itemData = this.itemsDataByRootId[rootId];
        if (newLogItem.length > 500) {
          this.spliceLogItems(rootId, 0, itemData.logItemsIds.length - 480);
        }
        itemData.activeLogItemId = newLogItem.id;
        itemData.activeLogItemIndex = itemData.logItemsIds.length;
        itemData.logItemsIds.push(newLogItem.id);
        itemData.logItemsById[newLogItem.id] = newLogItem;

        this.emit('activeLogItemId');
        this.emit('mstLogItems');
        this.selectLogItemId(newLogItem.id);
      }),
      bridge.sub('mst-log-item-details', (logItem) => {
        const itemData = this.itemsDataByRootId[logItem.rootId];
        if (!itemData) return;
        itemData.logItemsById[logItem.id] = logItem;
        this.emit(logItem.id);
      }),
      bridge.sub('frontend:mst-roots', (roots) => {
        roots.forEach(({ id, name }) => {
          this.rootNamesById[id] = name;
        });
        this.emit('mstRootsUpdated');
      }),
      bridge.sub('frontend:remove-mst-root', (rootId) => {
        delete this.rootNamesById[rootId];
        delete this.itemsDataByRootId[rootId];
        this.emit('mstRootsUpdated');
      })
    );

    preferences.get('mstLogEnabled').then(({ mstLogEnabled = true }) => {
      if (mstLogEnabled) this.toggleMstLogging(true);
    });
  }

  toggleMstLogging(mstLogEnabled = !this.mstLogEnabled) {
    if (mstLogEnabled !== this.mstLogEnabled) {
      this.mstLogEnabled = mstLogEnabled;
      preferences.set({ mstLogEnabled });
      this.emit('mstLogEnabled');
      this.bridge.send('backend-mst:set-tracking-enabled', mstLogEnabled);
    }
  }

  commitAll() {
    Object.keys(this.itemsDataByRootId).forEach((rootId) => {
      this.spliceLogItems(rootId, 0, this.itemsDataByRootId[rootId].logItemsIds.length - 1);
    });
    this.emit('mstLogItems');
  }

  spliceLogItems(rootId, startIndex = 0, endIndex = Infinity) {
    const itemData = this.itemsDataByRootId[rootId];
    if (!itemData) return;
    const logItemsIds = itemData.logItemsIds;
    const removedItemsIds = logItemsIds.splice(startIndex, endIndex);
    removedItemsIds.forEach((id) => {
      delete itemData.logItemsById[id];
    });
    if (itemData.selectLogItemId && removedItemsIds.indexOf(itemData.selectedLogItemId) !== -1) {
      this.selectLogItemId(undefined);
    }
    this.bridge.send('backend-mst:forget-mst-items', { rootId, itemsIds: removedItemsIds });
  }

  activateRootId(rootId) {
    this.activeRootId = rootId;
    this.emit('activeRootId');
  }

  activateLogItemId(logItemId) {
    const rootId = this.activeRootId;
    const itemData = this.itemsDataByRootId[rootId];
    if (!itemData) return;
    this.bridge.send('backend-mst:activate-log-item-id', { rootId, logItemId });
    itemData.activeLogItemId = logItemId;
    itemData.activeLogItemIndex = itemData.logItemsIds.indexOf(logItemId);
    this.emit('activeLogItemId');
  }

  commitLogItemId(logItemId) {
    this.activateLogItemId(logItemId);
    const rootId = this.activeRootId;
    const itemData = this.itemsDataByRootId[rootId];
    if (!itemData) return;
    const idx = itemData.logItemsIds.indexOf(logItemId);
    if (idx !== -1) {
      this.spliceLogItems(rootId, 0, idx);
    }
    this.emit('mstLogItems');
  }

  cancelLogItemId(logItemId) {
    this.activateLogItemId(logItemId);
    const rootId = this.activeRootId;
    const itemData = this.itemsDataByRootId[rootId];
    if (!itemData) return;
    const idx = itemData.logItemsIds.indexOf(logItemId);
    if (idx !== -1 && idx !== 0) {
      this.activateLogItemId(itemData.logItemsIds[idx - 1]);
      this.spliceLogItems(rootId, idx);
    }
    this.emit('mstLogItems');
  }

  selectLogItemId(logItemId) {
    const rootId = this.activeRootId;
    const itemData = this.itemsDataByRootId[rootId];
    if (!itemData) return;
    itemData.selectedLogItemId = logItemId;
    this.emit('selectedLogItemId');
    this.getDetails(logItemId);
  }

  getDetails(logItemId) {
    this.bridge.send('get-mst-log-item-details', {
      rootId: this.activeRootId,
      logItemId,
    });
  }
}


/*
Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
*/
CKEDITOR.plugins.setLang( 'table', 'fi', {
	border: 'Rajan paksuus',
	caption: 'Otsikko',
	cell: {
		menu: 'Solu',
		insertBefore: 'Lisää solu eteen',
		insertAfter: 'Lisää solu perään',
		deleteCell: 'Poista solut',
		merge: 'Yhdistä solut',
		mergeRight: 'Yhdistä oikealla olevan kanssa',
		mergeDown: 'Yhdistä alla olevan kanssa',
		splitHorizontal: 'Jaa solu vaakasuunnassa',
		splitVertical: 'Jaa solu pystysuunnassa',
		title: 'Solun ominaisuudet',
		cellType: 'Solun tyyppi',
		rowSpan: 'Rivin jatkuvuus',
		colSpan: 'Solun jatkuvuus',
		wordWrap: 'Rivitys',
		hAlign: 'Horisontaali kohdistus',
		vAlign: 'Vertikaali kohdistus',
		alignBaseline: 'Alas (teksti)',
		bgColor: 'Taustan väri',
		borderColor: 'Reunan väri',
		data: 'Data',
		header: 'Ylätunniste',
		yes: 'Kyllä',
		no: 'Ei',
		invalidWidth: 'Solun leveyden täytyy olla numero.',
		invalidHeight: 'Solun korkeuden täytyy olla numero.',
		invalidRowSpan: 'Rivin jatkuvuuden täytyy olla kokonaisluku.',
		invalidColSpan: 'Solun jatkuvuuden täytyy olla kokonaisluku.',
		chooseColor: 'Valitse'
	},
	cellPad: 'Solujen sisennys',
	cellSpace: 'Solujen väli',
	column: {
		menu: 'Sarake',
		insertBefore: 'Lisää sarake vasemmalle',
		insertAfter: 'Lisää sarake oikealle',
		deleteColumn: 'Poista sarakkeet'
	},
	columns: 'Sarakkeet',
	deleteTable: 'Poista taulu',
	headers: 'Ylätunnisteet',
	headersBoth: 'Molemmat',
	headersColumn: 'Ensimmäinen sarake',
	headersNone: 'Ei',
	headersRow: 'Ensimmäinen rivi',
	invalidBorder: 'Reunan koon täytyy olla numero.',
	invalidCellPadding: 'Solujen sisennyksen täytyy olla numero.',
	invalidCellSpacing: 'Solujen välin täytyy olla numero.',
	invalidCols: 'Sarakkeiden määrän täytyy olla suurempi kuin 0.',
	invalidHeight: 'Taulun korkeuden täytyy olla numero.',
	invalidRows: 'Rivien määrän täytyy olla suurempi kuin 0.',
	invalidWidth: 'Taulun leveyden täytyy olla numero.',
	menu: 'Taulun ominaisuudet',
	row: {
		menu: 'Rivi',
		insertBefore: 'Lisää rivi yläpuolelle',
		insertAfter: 'Lisää rivi alapuolelle',
		deleteRow: 'Poista rivit'
	},
	rows: 'Rivit',
	summary: 'Yhteenveto',
	title: 'Taulun ominaisuudet',
	toolbar: 'Taulu',
	widthPc: 'prosenttia',
	widthPx: 'pikseliä',
	widthUnit: 'leveysyksikkö'
} );


package com.matisse.ucrop.immersion;

import android.os.Build;
import android.text.TextUtils;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.regex.Pattern;

/**
 * @author：luck
 * @data：2018/3/28 下午1:02
 * @描述: Rom版本管理
 */

public class CropRomUtils {
    public class AvailableRomType {
        public static final int MIUI = 1;
        public static final int FLYME = 2;
        public static final int ANDROID_NATIVE = 3;
        public static final int NA = 4;
    }

    private static Integer romType;

    public static int getLightStatausBarAvailableRomType() {
        if (romType != null) {
            return romType;
        }

        if (isMIUIV6OrAbove()) {
            romType = AvailableRomType.MIUI;
            return romType;
        }

        if (isFlymeV4OrAbove()) {
            romType = AvailableRomType.FLYME;
            return romType;
        }

        if (isAndroid5OrAbove()) {
            romType = AvailableRomType.ANDROID_NATIVE;
            return romType;
        }

        romType = AvailableRomType.NA;
        return romType;
    }

    //Flyme V4的displayId格式为 [Flyme OS 4.x.x.xA]
    //Flyme V5的displayId格式为 [Flyme 5.x.x.x beta]
    private static boolean isFlymeV4OrAbove() {
        return (getFlymeVersion() >= 4);
    }

    //Flyme V4的displayId格式为 [Flyme OS 4.x.x.xA]
    //Flyme V5的displayId格式为 [Flyme 5.x.x.x beta]
    public static int getFlymeVersion() {
        String displayId = Build.DISPLAY;
        if (!TextUtils.isEmpty(displayId) && displayId.contains("Flyme")) {
            displayId = displayId.replaceAll("Flyme", "");
            displayId = displayId.replaceAll("OS", "");
            displayId = displayId.replaceAll(" ", "");

            String version = displayId.substring(0, 1);

            if (version != null) {
                return stringToInt(version);
            }
        }
        return 0;
    }

    //MIUI V6对应的versionCode是4
    //MIUI V7对应的versionCode是5
    private static boolean isMIUIV6OrAbove() {
        String miuiVersionCodeStr = getSystemProperty("ro.miui.ui.version.code");
        if (!TextUtils.isEmpty(miuiVersionCodeStr)) {
            try {
                int miuiVersionCode = Integer.parseInt(miuiVersionCodeStr);
                if (miuiVersionCode >= 4) {
                    return true;
                }
            } catch (Exception e) {
            }
        }
        return false;
    }

    public static int getMIUIVersionCode() {
        String miuiVersionCodeStr = getSystemProperty("ro.miui.ui.version.code");
        int miuiVersionCode = 0;
        if (!TextUtils.isEmpty(miuiVersionCodeStr)) {
            try {
                miuiVersionCode = Integer.parseInt(miuiVersionCodeStr);
                return miuiVersionCode;
            } catch (Exception e) {
            }
        }
        return miuiVersionCode;
    }

    //Android Api 23以上
    private static boolean isAndroid5OrAbove() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            return true;
        }
        return false;
    }

    public static String getSystemProperty(String propName) {
        String line;
        BufferedReader input = null;
        try {
            Process p = Runtime.getRuntime().exec("getprop " + propName);
            input = new BufferedReader(new InputStreamReader(p.getInputStream()), 1024);
            line = input.readLine();
            input.close();
        } catch (IOException ex) {
            return null;
        } finally {
            if (input != null) {
                try {
                    input.close();
                } catch (IOException e) {
                }
            }
        }
        return line;
    }

    public static int stringToInt(String str) {
        Pattern pattern = Pattern.compile("^[-\\+]?[\\d]+$");
        if (pattern.matcher(str).matches()) {
            return Integer.valueOf(str);
        }
        return 0;
    }
}