/////////////////////////////////////////////////////////////////////////////
// Name:        sampleprops.cpp
// Purpose:     wxPropertyGrid Sample Properties
// Author:      Jaakko Salli
// Modified by:
// Created:     Mar-05-2006
// RCS-ID:      $Id:
// Copyright:   (c) Jaakko Salli
// Licence:     wxWindows license
/////////////////////////////////////////////////////////////////////////////

// For compilers that support precompilation, includes "wx/wx.h".
#include "wx/wxprec.h"

#ifdef __BORLANDC__
    #pragma hdrstop
#endif

// for all others, include the necessary headers (this file is usually all you
// need because it includes almost all "standard" wxWidgets headers)
#ifndef WX_PRECOMP
    #include "wx/wx.h"
#endif

// -----------------------------------------------------------------------


#include <wx/propgrid/propgrid.h>
#include <wx/propgrid/propdev.h>
#include <wx/propgrid/advprops.h>

// You will *NOT* need to include this in your application.
#include <wx/propgrid/extras.h>

// This silly condition is required incase these extra properties
// were already part of the compiled library.
#if !defined(WX_PROPGRID_SAMPLEPROPS_H) || defined(__WXPYTHON__)

#ifndef WX_PROPGRID_SAMPLEPROPS_H
    #include "sampleprops.h"
#endif


// -----------------------------------------------------------------------
// wxFontDataProperty
// -----------------------------------------------------------------------

// Dummy comparison required by value type implementation.
bool operator == (const wxFontData&, const wxFontData&)
{
    return FALSE;
}

// Custom version of wxFontProperty that also holds colour in the value.
// Original version by Vladimir Vainer.

#include <wx/fontdlg.h>

WX_PG_IMPLEMENT_WXOBJECT_VARIANT_DATA(wxPGVariantDataFontData, wxFontData)

WX_PG_IMPLEMENT_PROPERTY_CLASS(wxFontDataProperty,wxFontProperty,
                               wxFontData,const wxFontData&,TextCtrlAndButton)

wxFontDataProperty::wxFontDataProperty( const wxString& label, const wxString& name,
   const wxFontData& value ) : wxFontProperty(label,name,value.GetInitialFont())
{
    // Set initial value - should be done in a simpler way like this
    // (instead of calling SetValue) in derived (wxObject) properties.
    m_value_wxFontData << value;

    wxFontData& fontData = wxFontDataFromVariant(m_value_wxFontData);

    // Fix value.
    fontData.SetChosenFont(value.GetInitialFont());
    if ( !fontData.GetColour().Ok() )
        fontData.SetColour(*wxBLACK);

    // Add extra children.
    AddPrivateChild( new wxColourProperty(_("Colour"), wxPG_LABEL,
                                          fontData.GetColour() ) );

}

wxFontDataProperty::~wxFontDataProperty () { }

void wxFontDataProperty::OnSetValue()
{
    if ( !(&wxFontDataFromVariant(m_value)) )
    {
        wxFont* pFont = &wxFontFromVariant(m_value);
        if ( pFont )
        {
            wxFontData fontData;
            fontData.SetChosenFont(*pFont);
            m_value = WXVARIANT(fontData);
        }
        else
        {
            wxFAIL_MSG(wxT("Value to wxFontDataProperty must be eithe wxFontData or wxFont"));
        }
    }

    // Set m_value to wxFont so that wxFontProperty methods will work
    // correctly.
    m_value_wxFontData = m_value;
    wxFontData& fontData = wxFontDataFromVariant(m_value_wxFontData);

    wxFont font = fontData.GetChosenFont();
    if ( !font.Ok() )
        font = wxFont(10,wxSWISS,wxNORMAL,wxNORMAL);
    m_value = WXVARIANT(font);
}

wxVariant wxFontDataProperty::DoGetValue() const
{
    return m_value_wxFontData;
}

// Must re-create font dialog displayer.
bool wxFontDataProperty::OnEvent( wxPropertyGrid* propgrid,
                                  wxWindow* WXUNUSED(primary), wxEvent& event )
{
    if ( propgrid->IsMainButtonEvent(event) )
    {
        // Update value from last minute changes
        PrepareValueForDialogEditing(propgrid);

        wxFontData& fontData = wxFontDataFromVariant(m_value_wxFontData);

        fontData.SetInitialFont(fontData.GetChosenFont());

        wxFontDialog dlg(propgrid, fontData);

        if ( dlg.ShowModal() == wxID_OK )
        {
            SetValueInEvent( wxFontDataToVariant(dlg.GetFontData()) );
            return true;
        }
    }
    return false;
}

void wxFontDataProperty::RefreshChildren()
{
    wxFontProperty::RefreshChildren();
    if ( GetCount() < 6 ) // Number is count of inherit prop's children + 1.
        return;
    wxFontData& fontData = wxFontDataFromVariant(m_value_wxFontData);
    wxVariant variant; variant << fontData.GetColour();
    Item(6)->SetValue( variant );
}

void wxFontDataProperty::ChildChanged( wxVariant& thisValue, int childIndex, wxVariant& childValue ) const
{
    wxFontData& fontData = wxFontDataFromVariant(thisValue);
    wxColour col;
    wxVariant variant;

    switch ( childIndex )
    {
        case 6:
            col << childValue;
            fontData.SetColour( col );
            break;
        default:
            // Transfer between subset to superset.
            variant = WXVARIANT(fontData.GetChosenFont());
            wxFontProperty::ChildChanged( variant, childIndex, childValue );
            fontData.SetChosenFont(wxFontFromVariant(variant));
    }
}

// -----------------------------------------------------------------------
// wxSizeProperty
// -----------------------------------------------------------------------

WX_PG_IMPLEMENT_PROPERTY_CLASS(wxSizeProperty,wxPGProperty,
                               wxSize,const wxSize&,TextCtrl)

wxSizeProperty::wxSizeProperty( const wxString& label, const wxString& name,
    const wxSize& value) : wxPGProperty(label,name)
{
    SetValueI(value);
    AddPrivateChild( new wxIntProperty(wxT("Width"),wxPG_LABEL,value.x) );
    AddPrivateChild( new wxIntProperty(wxT("Height"),wxPG_LABEL,value.y) );
}

wxSizeProperty::~wxSizeProperty() { }

void wxSizeProperty::RefreshChildren()
{
    if ( !GetCount() ) return;
    const wxSize& size = wxSizeFromVariant(m_value);
    Item(0)->SetValue( (long)size.x );
    Item(1)->SetValue( (long)size.y );
}

void wxSizeProperty::ChildChanged( wxVariant& thisValue, int childIndex, wxVariant& childValue ) const
{
    wxSize& size = wxSizeFromVariant(thisValue);
    int val = wxPGVariantToInt(childValue);
    switch ( childIndex )
    {
        case 0: size.x = val; break;
        case 1: size.y = val; break;
    }
}

// -----------------------------------------------------------------------
// wxPointProperty
// -----------------------------------------------------------------------

WX_PG_IMPLEMENT_PROPERTY_CLASS(wxPointProperty,wxPGProperty,
                               wxPoint,const wxPoint&,TextCtrl)

wxPointProperty::wxPointProperty( const wxString& label, const wxString& name,
    const wxPoint& value) : wxPGProperty(label,name)
{
    SetValueI(value);
    AddPrivateChild( new wxIntProperty(wxT("X"),wxPG_LABEL,value.x) );
    AddPrivateChild( new wxIntProperty(wxT("Y"),wxPG_LABEL,value.y) );
}

wxPointProperty::~wxPointProperty() { }

void wxPointProperty::RefreshChildren()
{
    if ( !GetCount() ) return;
    const wxPoint& point = wxPointFromVariant(m_value);
    Item(0)->SetValue( (long)point.x );
    Item(1)->SetValue( (long)point.y );
}

void wxPointProperty::ChildChanged( wxVariant& thisValue, int childIndex, wxVariant& childValue ) const
{
    wxPoint& point = wxPointFromVariant(thisValue);
    int val = wxPGVariantToInt(childValue);
    switch ( childIndex )
    {
        case 0: point.x = val; break;
        case 1: point.y = val; break;
    }
}


// -----------------------------------------------------------------------
// Dirs Property
// -----------------------------------------------------------------------

WX_PG_IMPLEMENT_ARRAYSTRING_PROPERTY_WITH_VALIDATOR(wxDirsProperty,wxT(','),wxT("Browse"))

#if wxUSE_VALIDATORS

wxValidator* wxDirsProperty::DoGetValidator() const
{
    return wxFileProperty::GetClassValidator();
}

#endif


bool wxDirsProperty::OnCustomStringEdit( wxWindow* parent, wxString& value )
{
    wxDirDialog dlg(parent,
                    _("Select a directory to be added to the list:"),
                    value,
                    0);

    if ( dlg.ShowModal() == wxID_OK )
    {
        value = dlg.GetPath();
        return TRUE;
    }
    return FALSE;
}

// -----------------------------------------------------------------------
// wxArrayDoubleEditorDialog
// -----------------------------------------------------------------------


//
// You can *almost* convert wxArrayDoubleEditorDialog to wxArrayXXXEditorDialog
// by replacing each ArrayDouble with ArrayXXX.
//

class wxArrayDoubleEditorDialog : public wxArrayEditorDialog
{
public:
    wxArrayDoubleEditorDialog();

    void Init();

    wxArrayDoubleEditorDialog(wxWindow *parent,
                              const wxString& message,
                              const wxString& caption,
                              wxArrayDouble& array,
                              long style = wxAEDIALOG_STYLE,
                              const wxPoint& pos = wxDefaultPosition,
                              const wxSize& sz = wxDefaultSize );

    bool Create(wxWindow *parent,
                const wxString& message,
                const wxString& caption,
                wxArrayDouble& array,
                long style = wxAEDIALOG_STYLE,
                const wxPoint& pos = wxDefaultPosition,
                const wxSize& sz = wxDefaultSize );

    const wxArrayDouble& GetArray() const { return m_array; }

    // Extra method for this type of array
    void SetPrecision ( int precision )
    {
        m_precision = precision;
        m_dtoaTemplate.Empty();
    }

protected:
    // Mandatory array of type
    wxArrayDouble   m_array;

    // Use this to avoid extra wxString creation+Printf
    // on double-to-wxString conversion.
    wxString        m_dtoaTemplate;

    int             m_precision;

    // Mandatory overridden methods
    virtual wxString ArrayGet( size_t index );
    virtual size_t ArrayGetCount();
    virtual bool ArrayInsert( const wxString& str, int index );
    virtual bool ArraySet( size_t index, const wxString& str );
    virtual void ArrayRemoveAt( int index );
    virtual void ArraySwap( size_t first, size_t second );

private:
    DECLARE_DYNAMIC_CLASS_NO_COPY(wxArrayDoubleEditorDialog)
};

IMPLEMENT_DYNAMIC_CLASS(wxArrayDoubleEditorDialog, wxArrayEditorDialog)

//
// Array dialog array access and manipulation
//

wxString wxArrayDoubleEditorDialog::ArrayGet( size_t index )
{
    wxString str;
    wxPropertyGrid::DoubleToString(str,m_array[index],m_precision,true,&m_dtoaTemplate);
    return str;
}

size_t wxArrayDoubleEditorDialog::ArrayGetCount()
{
    return m_array.GetCount();
}

bool wxArrayDoubleEditorDialog::ArrayInsert( const wxString& str, int index )
{
    double d;
    if ( !str.ToDouble(&d) )
        return FALSE;

    if (index<0)
        m_array.Add(d);
    else
        m_array.Insert(d,index);
    return TRUE;
}

bool wxArrayDoubleEditorDialog::ArraySet( size_t index, const wxString& str )
{
    double d;
    if ( !str.ToDouble(&d) )
        return FALSE;
    m_array[index] = d;
    return TRUE;
}

void wxArrayDoubleEditorDialog::ArrayRemoveAt( int index )
{
    m_array.RemoveAt(index);
}

void wxArrayDoubleEditorDialog::ArraySwap( size_t first, size_t second )
{
    double a = m_array[first];
    double b = m_array[second];
    m_array[first] = b;
    m_array[second] = a;
}

//
// Array dialog construction etc.
//

wxArrayDoubleEditorDialog::wxArrayDoubleEditorDialog()
    : wxArrayEditorDialog()
{
    Init();
}

void wxArrayDoubleEditorDialog::Init()
{
    wxArrayEditorDialog::Init();
    SetPrecision(-1);
}

wxArrayDoubleEditorDialog::wxArrayDoubleEditorDialog(wxWindow *parent,
                              const wxString& message,
                              const wxString& caption,
                              wxArrayDouble& array,
                              long style,
                              const wxPoint& pos,
                              const wxSize& sz )
                              : wxArrayEditorDialog()
{
    Init();
    Create(parent,message,caption,array,style,pos,sz);
}

bool wxArrayDoubleEditorDialog::Create(wxWindow *parent,
                const wxString& message,
                const wxString& caption,
                wxArrayDouble& array,
                long style,
                const wxPoint& pos,
                const wxSize& sz )
{

    m_array = array;

    return wxArrayEditorDialog::Create (parent,message,caption,style,pos,sz);
}

// -----------------------------------------------------------------------
// wxArrayDoubleProperty
// -----------------------------------------------------------------------

#include <math.h> // for fabs

// Comparison required by value type implementation.
bool operator == (const wxArrayDouble& a, const wxArrayDouble& b)
{
    if ( a.GetCount() != b.GetCount() )
        return FALSE;

    size_t i;

    for ( i=0; i<a.GetCount(); i++ )
    {
        // Can't do direct equality comparison with floating point numbers.
        if ( fabs(a[i] - b[i]) > 0.0000000001 )
        {
            //wxLogDebug(wxT("%f != %f"),a[i],b[i]);
            return FALSE;
        }
    }
    return TRUE;
}

WX_PG_IMPLEMENT_VARIANT_DATA(wxPGVariantDataArrayDouble, wxArrayDouble)

WX_PG_IMPLEMENT_PROPERTY_CLASS(wxArrayDoubleProperty,
                               wxPGProperty,
                               wxArrayDouble,
                               const wxArrayDouble&,
                               TextCtrlAndButton)


wxArrayDoubleProperty::wxArrayDoubleProperty (const wxString& label,
                                                        const wxString& name,
                                                        const wxArrayDouble& array )
    : wxPGProperty(label,name)
{
    m_precision = -1;

    //
    // Need to figure out delimiter needed for this locale
    // (ie. can't use comma when comma acts as decimal point in float).
    wxChar use_delimiter = wxT(',');

    if (wxString::Format(wxT("%.2f"),12.34).Find(use_delimiter) >= 0)
        use_delimiter = wxT(';');

    m_delimiter = use_delimiter;

    SetValue( wxArrayDoubleToVariant(array) );
}

wxArrayDoubleProperty::~wxArrayDoubleProperty () { }

void wxArrayDoubleProperty::OnSetValue()
{
    GenerateValueAsString( m_display, m_precision, true );
}

wxString wxArrayDoubleProperty::GetValueAsString( int arg_flags ) const
{
    if ( !(arg_flags & wxPG_FULL_VALUE ))
        return m_display;

    wxString s;
    GenerateValueAsString(s,-1,false);
    return s;
}

void wxArrayDoubleProperty::GenerateValueAsString( wxString& target, int prec, bool removeZeroes ) const
{
    wxString s;
    wxString template_str;
    wxChar between[3] = wxT(", ");
    size_t i;

    between[0] = m_delimiter;

    target.Empty();

    const wxArrayDouble& value = wxArrayDoubleFromVariant(m_value);

    for ( i=0; i<value.GetCount(); i++ )
    {

        wxPropertyGrid::DoubleToString(s,value[i],prec,removeZeroes,&template_str);

        target += s;

        if ( i<(value.GetCount()-1) )
            target += between;
    }
}

bool wxArrayDoubleProperty::OnEvent( wxPropertyGrid* propgrid,
                                     wxWindow* WXUNUSED(primary),
                                     wxEvent& event)
{
    if ( propgrid->IsMainButtonEvent(event) )
    {
        wxArrayDouble& value = wxArrayDoubleFromVariant(m_value);

        // Update the value in case of last minute changes
        PrepareValueForDialogEditing(propgrid);

        // Create editor dialog.
        wxArrayDoubleEditorDialog dlg;
        dlg.SetPrecision(m_precision);
        dlg.Create( propgrid, wxEmptyString, m_label, value );
        dlg.Move( propgrid->GetGoodEditorDialogPosition(this,dlg.GetSize()) );

        // Execute editor dialog
        int res = dlg.ShowModal();
        if ( res == wxID_OK && dlg.IsModified() )
        {
            SetValueInEvent( wxArrayDoubleToVariant(dlg.GetArray()) );
            return true;
        }
        return false;
    }
    return false;
}

bool wxArrayDoubleProperty::StringToValue( wxVariant& variant, const wxString& text, int ) const
{
    double tval;
    wxString tstr;
    // Add values to a temporary array so that in case
    // of error we can opt not to use them.
    wxArrayDouble new_array;

    bool ok = true;

    wxChar delimiter = m_delimiter;

    WX_PG_TOKENIZER1_BEGIN(text,delimiter)

        if ( token.length() )
        {

            // If token was invalid, exit the loop now
            if ( !token.ToDouble(&tval) )
            {
                tstr.Printf ( _("\"%s\" is not a floating-point number."), token.c_str() );
                ok = false;
                break;
            }
            // TODO: Put validator code here

            new_array.Add(tval);

        }

    WX_PG_TOKENIZER1_END()

    // When invalid token found, show error message and don't change anything
    if ( !ok )
    {
        //ShowError( tstr );
        return false;
    }

    if ( m_value.IsNull() || !(wxArrayDoubleFromVariant(m_value) == new_array) )
    {
        variant = wxArrayDoubleToVariant(new_array);
        return true;
    }

    return false;
}

bool wxArrayDoubleProperty::DoSetAttribute( const wxString& name, wxVariant& value )
{
    if ( name == wxPG_FLOAT_PRECISION )
    {
        m_precision = value.GetLong();
        GenerateValueAsString( m_display, m_precision, true );
        return true;
    }
    return false;
}


#endif // !defined(WX_PROPGRID_SAMPLEPROPS_H) || defined(__WXPYTHON__)
