/////////////////////////////////////////////////////////////////////////////
// Name:        src/propgrid/editors.cpp
// Purpose:     wxPropertyGrid editors
// Author:      Jaakko Salli
// Modified by:
// Created:     2007-04-14
// Copyright:   (c) Jaakko Salli
// Licence:     wxWindows licence
/////////////////////////////////////////////////////////////////////////////

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

#ifdef __BORLANDC__
    #pragma hdrstop
#endif

#if wxUSE_PROPGRID

#ifndef WX_PRECOMP
    #include "wx/defs.h"
    #include "wx/object.h"
    #include "wx/hash.h"
    #include "wx/string.h"
    #include "wx/log.h"
    #include "wx/event.h"
    #include "wx/window.h"
    #include "wx/panel.h"
    #include "wx/dc.h"
    #include "wx/dcclient.h"
    #include "wx/dcmemory.h"
    #include "wx/button.h"
    #include "wx/pen.h"
    #include "wx/brush.h"
    #include "wx/cursor.h"
    #include "wx/dialog.h"
    #include "wx/settings.h"
    #include "wx/msgdlg.h"
    #include "wx/choice.h"
    #include "wx/stattext.h"
    #include "wx/scrolwin.h"
    #include "wx/dirdlg.h"
    #include "wx/sizer.h"
    #include "wx/textdlg.h"
    #include "wx/filedlg.h"
    #include "wx/statusbr.h"
    #include "wx/intl.h"
    #include "wx/frame.h"
#endif


#include "wx/timer.h"
#include "wx/dcbuffer.h"
#include "wx/bmpbuttn.h"


// This define is necessary to prevent macro clearing
#define __wxPG_SOURCE_FILE__

#include "wx/propgrid/propgrid.h"
#include "wx/propgrid/editors.h"
#include "wx/propgrid/props.h"

#if wxPG_USE_RENDERER_NATIVE
    #include "wx/renderer.h"
#endif

// How many pixels between textctrl and button
#ifdef __WXMAC__
    #define wxPG_TEXTCTRL_AND_BUTTON_SPACING        4
#else
    #define wxPG_TEXTCTRL_AND_BUTTON_SPACING        2
#endif

#define wxPG_BUTTON_SIZEDEC                         0

#include "wx/odcombo.h"

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

#if defined(__WXMSW__)
    // tested
    #define wxPG_NAT_BUTTON_BORDER_ANY          1
    #define wxPG_NAT_BUTTON_BORDER_X            1
    #define wxPG_NAT_BUTTON_BORDER_Y            1

    #define wxPG_CHECKMARK_XADJ                 1
    #define wxPG_CHECKMARK_YADJ                 (-1)
    #define wxPG_CHECKMARK_WADJ                 0
    #define wxPG_CHECKMARK_HADJ                 0
    #define wxPG_CHECKMARK_DEFLATE              0

    #define wxPG_TEXTCTRLYADJUST                (m_spacingy+0)

#elif defined(__WXGTK__)
    // tested
    #define wxPG_CHECKMARK_XADJ                 1
    #define wxPG_CHECKMARK_YADJ                 1
    #define wxPG_CHECKMARK_WADJ                 (-2)
    #define wxPG_CHECKMARK_HADJ                 (-2)
    #define wxPG_CHECKMARK_DEFLATE              3

    #define wxPG_NAT_BUTTON_BORDER_ANY      1
    #define wxPG_NAT_BUTTON_BORDER_X        1
    #define wxPG_NAT_BUTTON_BORDER_Y        1

    #define wxPG_TEXTCTRLYADJUST            0

#elif defined(__WXMAC__)
    // *not* tested
    #define wxPG_CHECKMARK_XADJ                 4
    #define wxPG_CHECKMARK_YADJ                 4
    #define wxPG_CHECKMARK_WADJ                 -6
    #define wxPG_CHECKMARK_HADJ                 -6
    #define wxPG_CHECKMARK_DEFLATE              0

    #define wxPG_NAT_BUTTON_BORDER_ANY      0
    #define wxPG_NAT_BUTTON_BORDER_X        0
    #define wxPG_NAT_BUTTON_BORDER_Y        0

    #define wxPG_TEXTCTRLYADJUST            0

#else
    // defaults
    #define wxPG_CHECKMARK_XADJ                 0
    #define wxPG_CHECKMARK_YADJ                 0
    #define wxPG_CHECKMARK_WADJ                 0
    #define wxPG_CHECKMARK_HADJ                 0
    #define wxPG_CHECKMARK_DEFLATE              0

    #define wxPG_NAT_BUTTON_BORDER_ANY      0
    #define wxPG_NAT_BUTTON_BORDER_X        0
    #define wxPG_NAT_BUTTON_BORDER_Y        0

    #define wxPG_TEXTCTRLYADJUST            0

#endif

// for odcombo
#ifdef __WXMAC__
#define wxPG_CHOICEXADJUST           -3 // required because wxComboCtrl reserves 3pixels for wxTextCtrl's focus ring
#define wxPG_CHOICEYADJUST           -3
#else
#define wxPG_CHOICEXADJUST           0
#define wxPG_CHOICEYADJUST           0
#endif

// Number added to image width for SetCustomPaintWidth
#define ODCB_CUST_PAINT_MARGIN               6

// Milliseconds to wait for two mouse-ups after focus in order
// to trigger a double-click.
#define DOUBLE_CLICK_CONVERSION_TRESHOLD        500

// -----------------------------------------------------------------------
// wxPGEditor
// -----------------------------------------------------------------------

IMPLEMENT_ABSTRACT_CLASS(wxPGEditor, wxObject)


wxPGEditor::~wxPGEditor()
{
}

wxString wxPGEditor::GetName() const
{
    return GetClassInfo()->GetClassName();
}

void wxPGEditor::DrawValue( wxDC& dc, const wxRect& rect,
                            wxPGProperty* WXUNUSED(property),
                            const wxString& text ) const
{
    dc.DrawText( text, rect.x+wxPG_XBEFORETEXT, rect.y );
}

bool wxPGEditor::GetValueFromControl( wxVariant&, wxPGProperty*, wxWindow* ) const
{
    return false;
}

void wxPGEditor::SetControlStringValue( wxPGProperty* WXUNUSED(property), wxWindow*, const wxString& ) const
{
}


void wxPGEditor::SetControlIntValue( wxPGProperty* WXUNUSED(property), wxWindow*, int ) const
{
}


int wxPGEditor::InsertItem( wxWindow*, const wxString&, int ) const
{
    return -1;
}


void wxPGEditor::DeleteItem( wxWindow*, int ) const
{
    return;
}


void wxPGEditor::OnFocus( wxPGProperty*, wxWindow* ) const
{
}

void wxPGEditor::SetControlAppearance( wxPropertyGrid* pg,
                                       wxPGProperty* property,
                                       wxWindow* ctrl,
                                       const wxPGCell& cell,
                                       const wxPGCell& oCell,
                                       bool unspecified ) const
{
    // Get old editor appearance
    wxTextCtrl* tc = NULL;
    wxComboCtrl* cb = NULL;
    if ( wxDynamicCast(ctrl, wxTextCtrl) )
    {
        tc = (wxTextCtrl*) ctrl;
    }
    else
    {
        if ( wxDynamicCast(ctrl, wxComboCtrl) )
        {
            cb = (wxComboCtrl*) ctrl;
            tc = cb->GetTextCtrl();
        }
    }

    if ( tc || cb )
    {
        wxString tcText;
        bool changeText = false;

        if ( cell.HasText() && !pg->IsEditorFocused() )
        {
            tcText = cell.GetText();
            changeText = true;
        }
        else if ( oCell.HasText() )
        {
            tcText = property->GetValueAsString(
                property->HasFlag(wxPG_PROP_READONLY)?0:wxPG_EDITABLE_VALUE);
            changeText = true;
        }

        if ( changeText )
        {
            // This prevents value from being modified
            if ( tc )
            {
                pg->SetupTextCtrlValue(tcText);
                tc->SetValue(tcText);
            }
            else
            {
                cb->SetText(tcText);
            }
        }
    }

    // Do not make the mistake of calling GetClassDefaultAttributes()
    // here. It is static, while GetDefaultAttributes() is virtual
    // and the correct one to use.
    wxVisualAttributes vattrs = ctrl->GetDefaultAttributes();

    // Foreground colour
    const wxColour& fgCol = cell.GetFgCol();
    if ( fgCol.IsOk() )
    {
        ctrl->SetForegroundColour(fgCol);
    }
    else if ( oCell.GetFgCol().IsOk() )
    {
        ctrl->SetForegroundColour(vattrs.colFg);
    }

    // Background colour
    const wxColour& bgCol = cell.GetBgCol();
    if ( bgCol.IsOk() )
    {
        ctrl->SetBackgroundColour(bgCol);
    }
    else if ( oCell.GetBgCol().IsOk() )
    {
        ctrl->SetBackgroundColour(vattrs.colBg);
    }

    // Font
    const wxFont& font = cell.GetFont();
    if ( font.IsOk() )
    {
        ctrl->SetFont(font);
    }
    else if ( oCell.GetFont().IsOk() )
    {
        ctrl->SetFont(vattrs.font);
    }

    // Also call the old SetValueToUnspecified()
    if ( unspecified )
        SetValueToUnspecified(property, ctrl);
}

void wxPGEditor::SetValueToUnspecified( wxPGProperty* WXUNUSED(property),
                                        wxWindow* WXUNUSED(ctrl) ) const
{
}

bool wxPGEditor::CanContainCustomImage() const
{
    return false;
}

// -----------------------------------------------------------------------
// wxPGTextCtrlEditor
// -----------------------------------------------------------------------

WX_PG_IMPLEMENT_INTERNAL_EDITOR_CLASS(TextCtrl,wxPGTextCtrlEditor,wxPGEditor)


wxPGWindowList wxPGTextCtrlEditor::CreateControls( wxPropertyGrid* propGrid,
                                                   wxPGProperty* property,
                                                   const wxPoint& pos,
                                                   const wxSize& sz ) const
{
    wxString text;

    //
    // If has children, and limited editing is specified, then don't create.
    if ( (property->GetFlags() & wxPG_PROP_NOEDITOR) &&
         property->GetChildCount() )
        return NULL;

    int argFlags = 0;
    if ( !property->HasFlag(wxPG_PROP_READONLY) &&
         !property->IsValueUnspecified() )
        argFlags |= wxPG_EDITABLE_VALUE;
    text = property->GetValueAsString(argFlags);

    int flags = 0;
    if ( (property->GetFlags() & wxPG_PROP_PASSWORD) &&
         wxDynamicCast(property, wxStringProperty) )
        flags |= wxTE_PASSWORD;

    wxWindow* wnd = propGrid->GenerateEditorTextCtrl(pos,sz,text,NULL,flags,
                                                     property->GetMaxLength());

    return wnd;
}

#if 0
void wxPGTextCtrlEditor::DrawValue( wxDC& dc, wxPGProperty* property, const wxRect& rect ) const
{
    if ( !property->IsValueUnspecified() )
    {
        wxString drawStr = property->GetDisplayedString();

        // Code below should no longer be needed, as the obfuscation
        // is now done in GetValueAsString.
        /*if ( (property->GetFlags() & wxPG_PROP_PASSWORD) &&
             property->IsKindOf(WX_PG_CLASSINFO(wxStringProperty)) )
        {
            size_t a = drawStr.length();
            drawStr.Empty();
            drawStr.Append(wxS('*'),a);
        }*/
        dc.DrawText( drawStr, rect.x+wxPG_XBEFORETEXT, rect.y );
    }
}
#endif

void wxPGTextCtrlEditor::UpdateControl( wxPGProperty* property, wxWindow* ctrl ) const
{
    wxTextCtrl* tc = wxDynamicCast(ctrl, wxTextCtrl);
    if (!tc) return;

    wxString s;

    if ( tc->HasFlag(wxTE_PASSWORD) )
        s = property->GetValueAsString(wxPG_FULL_VALUE);
    else
        s = property->GetDisplayedString();

    wxPropertyGrid* pg = property->GetGrid();

    pg->SetupTextCtrlValue(s);
    tc->SetValue(s);

    //
    // Fix indentation, just in case (change in font boldness is one good
    // reason).
    tc->SetMargins(0);
}

// Provided so that, for example, ComboBox editor can use the same code
// (multiple inheritance would get way too messy).
bool wxPGTextCtrlEditor::OnTextCtrlEvent( wxPropertyGrid* propGrid,
                                          wxPGProperty* WXUNUSED(property),
                                          wxWindow* ctrl,
                                          wxEvent& event )
{
    if ( !ctrl )
        return false;

    if ( event.GetEventType() == wxEVT_TEXT_ENTER )
    {
        if ( propGrid->IsEditorsValueModified() )
        {
            return true;
        }
    }
    else if ( event.GetEventType() == wxEVT_TEXT )
    {
        //
        // Pass this event outside wxPropertyGrid so that,
        // if necessary, program can tell when user is editing
        // a textctrl.
        // FIXME: Is it safe to change event id in the middle of event
        //        processing (seems to work, but...)?
        event.Skip();
        event.SetId(propGrid->GetId());

        propGrid->EditorsValueWasModified();
    }
    return false;
}


bool wxPGTextCtrlEditor::OnEvent( wxPropertyGrid* propGrid,
                                  wxPGProperty* property,
                                  wxWindow* ctrl,
                                  wxEvent& event ) const
{
    return wxPGTextCtrlEditor::OnTextCtrlEvent(propGrid,property,ctrl,event);
}


bool wxPGTextCtrlEditor::GetTextCtrlValueFromControl( wxVariant& variant, wxPGProperty* property, wxWindow* ctrl )
{
    wxTextCtrl* tc = wxStaticCast(ctrl, wxTextCtrl);
    wxString textVal = tc->GetValue();

    if ( property->UsesAutoUnspecified() && textVal.empty() )
    {
        variant.MakeNull();
        return true;
    }

    bool res = property->StringToValue(variant, textVal, wxPG_EDITABLE_VALUE);

    // Changing unspecified always causes event (returning
    // true here should be enough to trigger it).
    // TODO: Move to propgrid.cpp
    if ( !res && variant.IsNull() )
        res = true;

    return res;
}


bool wxPGTextCtrlEditor::GetValueFromControl( wxVariant& variant, wxPGProperty* property, wxWindow* ctrl ) const
{
    return wxPGTextCtrlEditor::GetTextCtrlValueFromControl(variant, property, ctrl);
}


void wxPGTextCtrlEditor::SetControlStringValue( wxPGProperty* property, wxWindow* ctrl, const wxString& txt ) const
{
    wxTextCtrl* tc = wxStaticCast(ctrl, wxTextCtrl);

    wxPropertyGrid* pg = property->GetGrid();
    wxASSERT(pg);  // Really, property grid should exist if editor does
    if ( pg )
    {
        pg->SetupTextCtrlValue(txt);
        tc->SetValue(txt);
    }
}


void wxPGTextCtrlEditor_OnFocus( wxPGProperty* property,
                                 wxTextCtrl* tc )
{
    // Make sure there is correct text (instead of unspecified value
    // indicator or hint text)
    int flags = property->HasFlag(wxPG_PROP_READONLY) ?
        0 : wxPG_EDITABLE_VALUE;
    wxString correctText = property->GetValueAsString(flags);

    if ( tc->GetValue() != correctText )
    {
        property->GetGrid()->SetupTextCtrlValue(correctText);
        tc->SetValue(correctText);
    }

    tc->SelectAll();
}

void wxPGTextCtrlEditor::OnFocus( wxPGProperty* property,
                                  wxWindow* wnd ) const
{
    wxTextCtrl* tc = wxStaticCast(wnd, wxTextCtrl);
    wxPGTextCtrlEditor_OnFocus(property, tc);
}

wxPGTextCtrlEditor::~wxPGTextCtrlEditor()
{
    // Reset the global pointer. Useful when wxPropertyGrid is accessed
    // from an external main loop.
    wxPG_EDITOR(TextCtrl) = NULL;
}


// -----------------------------------------------------------------------
// wxPGChoiceEditor
// -----------------------------------------------------------------------


WX_PG_IMPLEMENT_INTERNAL_EDITOR_CLASS(Choice,wxPGChoiceEditor,wxPGEditor)


// This is a special enhanced double-click processor class.
// In essence, it allows for double-clicks for which the
// first click "created" the control.
class wxPGDoubleClickProcessor : public wxEvtHandler
{
public:

    wxPGDoubleClickProcessor( wxOwnerDrawnComboBox* combo, wxPGProperty* property )
        : wxEvtHandler()
    {
        m_timeLastMouseUp = 0;
        m_combo = combo;
        m_property = property;
        m_downReceived = false;
    }

protected:

    void OnMouseEvent( wxMouseEvent& event )
    {
        wxLongLong t = ::wxGetLocalTimeMillis();
        int evtType = event.GetEventType();

        if ( m_property->HasFlag(wxPG_PROP_USE_DCC) &&
             wxDynamicCast(m_property, wxBoolProperty) &&
             !m_combo->IsPopupShown() )
        {
            // Just check that it is in the text area
            wxPoint pt = event.GetPosition();
            if ( m_combo->GetTextRect().Contains(pt) )
            {
                if ( evtType == wxEVT_LEFT_DOWN )
                {
                    // Set value to avoid up-events without corresponding downs
                    m_downReceived = true;
                }
                else if ( evtType == wxEVT_LEFT_DCLICK )
                {
                    // We'll make our own double-clicks
                    event.SetEventType(0);
                    return;
                }
                else if ( evtType == wxEVT_LEFT_UP )
                {
                    if ( m_downReceived || m_timeLastMouseUp == 1 )
                    {
                        wxLongLong timeFromLastUp = (t-m_timeLastMouseUp);

                        if ( timeFromLastUp < DOUBLE_CLICK_CONVERSION_TRESHOLD )
                        {
                            event.SetEventType(wxEVT_LEFT_DCLICK);
                            m_timeLastMouseUp = 1;
                        }
                        else
                        {
                            m_timeLastMouseUp = t;
                        }
                    }
                }
            }
        }

        event.Skip();
    }

    void OnSetFocus( wxFocusEvent& event )
    {
        m_timeLastMouseUp = ::wxGetLocalTimeMillis();
        event.Skip();
    }

private:
    wxLongLong                  m_timeLastMouseUp;
    wxOwnerDrawnComboBox*       m_combo;
    wxPGProperty*               m_property;  // Selected property
    bool                        m_downReceived;

    DECLARE_EVENT_TABLE()
};

BEGIN_EVENT_TABLE(wxPGDoubleClickProcessor, wxEvtHandler)
    EVT_MOUSE_EVENTS(wxPGDoubleClickProcessor::OnMouseEvent)
    EVT_SET_FOCUS(wxPGDoubleClickProcessor::OnSetFocus)
END_EVENT_TABLE()



class wxPGComboBox : public wxOwnerDrawnComboBox
{
public:

    wxPGComboBox()
        : wxOwnerDrawnComboBox()
    {
        m_dclickProcessor = NULL;
        m_sizeEventCalled = false;
    }

    ~wxPGComboBox()
    {
        if ( m_dclickProcessor )
        {
            RemoveEventHandler(m_dclickProcessor);
            delete m_dclickProcessor;
        }
    }

    bool Create(wxWindow *parent,
                wxWindowID id,
                const wxString& value,
                const wxPoint& pos,
                const wxSize& size,
                const wxArrayString& choices,
                long style = 0,
                const wxValidator& validator = wxDefaultValidator,
                const wxString& name = wxS("wxOwnerDrawnComboBox"))
    {
        if ( !wxOwnerDrawnComboBox::Create( parent,
                                            id,
                                            value,
                                            pos,
                                            size,
                                            choices,
                                            style,
                                            validator,
                                            name ) )
            return false;

        m_dclickProcessor = new
            wxPGDoubleClickProcessor( this, GetGrid()->GetSelection() );

        PushEventHandler(m_dclickProcessor);

        return true;
    }

    virtual void OnDrawItem( wxDC& dc,
                             const wxRect& rect,
                             int item,
                             int flags ) const
    {
        wxPropertyGrid* pg = GetGrid();

        // Handle hint text via super class
        if ( (flags & wxODCB_PAINTING_CONTROL) &&
             ShouldUseHintText(flags) )
        {
            wxOwnerDrawnComboBox::OnDrawItem(dc, rect, item, flags);
        }
        else
        {
            pg->OnComboItemPaint( this, item, &dc, (wxRect&)rect, flags );
        }
    }

    virtual wxCoord OnMeasureItem( size_t item ) const
    {
        wxPropertyGrid* pg = GetGrid();
        wxRect rect;
        rect.x = -1;
        rect.width = 0;
        pg->OnComboItemPaint( this, item, NULL, rect, 0 );
        return rect.height;
    }

    wxPropertyGrid* GetGrid() const
    {
        wxPropertyGrid* pg = wxDynamicCast(GetParent(),
                                           wxPropertyGrid);
        wxASSERT(pg);
        return pg;
    }

    virtual wxCoord OnMeasureItemWidth( size_t item ) const
    {
        wxPropertyGrid* pg = GetGrid();
        wxRect rect;
        rect.x = -1;
        rect.width = -1;
        pg->OnComboItemPaint( this, item, NULL, rect, 0 );
        return rect.width;
    }

    virtual void PositionTextCtrl( int textCtrlXAdjust,
                                   int WXUNUSED(textCtrlYAdjust) )
    {
    #ifdef wxPG_TEXTCTRLXADJUST
        textCtrlXAdjust = wxPG_TEXTCTRLXADJUST -
                          (wxPG_XBEFOREWIDGET+wxPG_CONTROL_MARGIN+1) - 1,
    #endif
        wxOwnerDrawnComboBox::PositionTextCtrl(
            textCtrlXAdjust,
            0 // Under MSW vertical position is already properly adjusted.
              // (This parameter is not used by other ports.)
        );
    }

private:
    wxPGDoubleClickProcessor*   m_dclickProcessor;
    bool                        m_sizeEventCalled;
};


void wxPropertyGrid::OnComboItemPaint( const wxPGComboBox* pCb,
                                       int item,
                                       wxDC* pDc,
                                       wxRect& rect,
                                       int flags )
{
    wxPGProperty* p = GetSelection();
    wxString text;

    const wxPGChoices& choices = p->GetChoices();
    const wxPGCommonValue* comVal = NULL;
    int comVals = p->GetDisplayedCommonValueCount();
    int comValIndex = -1;

    int choiceCount = 0;
    if ( choices.IsOk() )
        choiceCount = choices.GetCount();

    if ( item >= choiceCount && comVals > 0 )
    {
        comValIndex = item - choiceCount;
        comVal = GetCommonValue(comValIndex);
        if ( !p->IsValueUnspecified() )
            text = comVal->GetLabel();
    }
    else
    {
        if ( !(flags & wxODCB_PAINTING_CONTROL) )
        {
            text = pCb->GetString(item);
        }
        else
        {
            if ( !p->IsValueUnspecified() )
                text = p->GetValueAsString(0);
        }
    }

    if ( item < 0 )
        return;

    wxSize cis;

    const wxBitmap* itemBitmap = NULL;

    if ( item >= 0 && choices.IsOk() && choices.Item(item).GetBitmap().IsOk() && comValIndex == -1 )
        itemBitmap = &choices.Item(item).GetBitmap();

    //
    // Decide what custom image size to use
    if ( itemBitmap )
    {
        cis.x = itemBitmap->GetWidth();
        cis.y = itemBitmap->GetHeight();
    }
    else
    {
        cis = GetImageSize(p, item);
    }

    if ( rect.x < 0 )
    {
        // Default measure behaviour (no flexible, custom paint image only)
        if ( rect.width < 0 )
        {
            wxCoord x, y;
            pCb->GetTextExtent(text, &x, &y, 0, 0);
            rect.width = cis.x + wxCC_CUSTOM_IMAGE_MARGIN1 + wxCC_CUSTOM_IMAGE_MARGIN2 + 9 + x;
        }

        rect.height = cis.y + 2;
        return;
    }

    wxPGPaintData paintdata;
    paintdata.m_parent = NULL;
    paintdata.m_choiceItem = item;

    // This is by the current (1.0.0b) spec - if painting control, item is -1
    if ( (flags & wxODCB_PAINTING_CONTROL) )
        paintdata.m_choiceItem = -1;

    if ( pDc )
        pDc->SetBrush(*wxWHITE_BRUSH);

    wxPGCellRenderer* renderer = NULL;
    const wxPGChoiceEntry* cell = NULL;

    if ( rect.x >= 0 )
    {
        //
        // DrawItem call
        wxDC& dc = *pDc;

        wxPoint pt(rect.x + wxPG_CONTROL_MARGIN - wxPG_CHOICEXADJUST - 1,
                   rect.y + 1);

        int renderFlags = wxPGCellRenderer::DontUseCellColours;
        bool useCustomPaintProcedure;

        // If custom image had some size, we will start from the assumption
        // that custom paint procedure is required
        if ( cis.x > 0 )
            useCustomPaintProcedure = true;
        else
            useCustomPaintProcedure = false;

        if ( flags & wxODCB_PAINTING_SELECTED )
            renderFlags |= wxPGCellRenderer::Selected;

        if ( flags & wxODCB_PAINTING_CONTROL )
        {
            renderFlags |= wxPGCellRenderer::Control;

            // If wxPG_PROP_CUSTOMIMAGE was set, then that means any custom
            // image will not appear on the control row (it may be too
            // large to fit, for instance). Also do not draw custom image
            // if no choice was selected.
            if ( !p->HasFlag(wxPG_PROP_CUSTOMIMAGE) || item < 0 )
                useCustomPaintProcedure = false;
        }
        else
        {
            renderFlags |= wxPGCellRenderer::ChoicePopup;

            // For consistency, always use normal font when drawing drop down
            // items
            dc.SetFont(GetFont());
        }

        // If not drawing a selected popup item, then give property's
        // m_valueBitmap a chance.
        if ( p->m_valueBitmap && item != pCb->GetSelection() )
            useCustomPaintProcedure = false;
        // If current choice had a bitmap set by the application, then
        // use it instead of any custom paint procedure.
        else if ( itemBitmap )
            useCustomPaintProcedure = false;

        if ( useCustomPaintProcedure )
        {
            pt.x += wxCC_CUSTOM_IMAGE_MARGIN1;
            wxRect r(pt.x,pt.y,cis.x,cis.y);

            if ( flags & wxODCB_PAINTING_CONTROL )
            {
                //r.width = cis.x;
                r.height = wxPG_STD_CUST_IMAGE_HEIGHT(m_lineHeight);
            }

            paintdata.m_drawnWidth = r.width;

            dc.SetPen(m_colPropFore);
            if ( comValIndex >= 0 )
            {
                const wxPGCommonValue* cv = GetCommonValue(comValIndex);
                renderer = cv->GetRenderer();
                r.width = rect.width;
                renderer->Render( dc, r, this, p, m_selColumn, comValIndex, renderFlags );
                return;
            }
            else if ( item >= 0 )
            {
                p->OnCustomPaint( dc, r, paintdata );
            }
            else
            {
                dc.DrawRectangle( r );
            }

            pt.x += paintdata.m_drawnWidth + wxCC_CUSTOM_IMAGE_MARGIN2 - 1;
        }
        else
        {
            // TODO: This aligns text so that it seems to be horizontally
            //       on the same line as property values. Not really
            //       sure if its needed, but seems to not cause any harm.
            pt.x -= 1;

            if ( item < 0 && (flags & wxODCB_PAINTING_CONTROL) )
                item = pCb->GetSelection();

            if ( choices.IsOk() && item >= 0 && comValIndex < 0 )
            {
                cell = &choices.Item(item);
                renderer = wxPGGlobalVars->m_defaultRenderer;
                int imageOffset = renderer->PreDrawCell(dc, rect, *cell,
                                                        renderFlags );
                if ( imageOffset )
                    imageOffset += wxCC_CUSTOM_IMAGE_MARGIN1 +
                                   wxCC_CUSTOM_IMAGE_MARGIN2;
                pt.x += imageOffset;
            }
        }

        //
        // Draw text
        //

        pt.y += (rect.height-m_fontHeight)/2 - 1;

        pt.x += 1;

        dc.DrawText( text, pt.x + wxPG_XBEFORETEXT, pt.y );

        if ( renderer )
            renderer->PostDrawCell(dc, this, *cell, renderFlags);
    }
    else
    {
        //
        // MeasureItem call
        wxDC& dc = *pDc;

        p->OnCustomPaint( dc, rect, paintdata );
        rect.height = paintdata.m_drawnHeight + 2;
        rect.width = cis.x + wxCC_CUSTOM_IMAGE_MARGIN1 + wxCC_CUSTOM_IMAGE_MARGIN2 + 9;
    }
}

bool wxPGChoiceEditor_SetCustomPaintWidth( wxPropertyGrid* propGrid, wxPGComboBox* cb, int cmnVal )
{
    wxPGProperty* property = propGrid->GetSelectedProperty();
    wxASSERT( property );

    wxSize imageSize;
    bool res;

    // TODO: Do this always when cell has custom text.
    if ( property->IsValueUnspecified() )
    {
        cb->SetCustomPaintWidth( 0 );
        return true;
    }

    if ( cmnVal >= 0 )
    {
        // Yes, a common value is being selected
        property->SetCommonValue( cmnVal );
        imageSize = propGrid->GetCommonValue(cmnVal)->
                            GetRenderer()->GetImageSize(property, 1, cmnVal);
        res = false;
    }
    else
    {
        imageSize = propGrid->GetImageSize(property, -1);
        res = true;
    }

    if ( imageSize.x )
        imageSize.x += ODCB_CUST_PAINT_MARGIN;
    cb->SetCustomPaintWidth( imageSize.x );

    return res;
}

// CreateControls calls this with CB_READONLY in extraStyle
wxWindow* wxPGChoiceEditor::CreateControlsBase( wxPropertyGrid* propGrid,
                                                wxPGProperty* property,
                                                const wxPoint& pos,
                                                const wxSize& sz,
                                                long extraStyle ) const
{
    // Since it is not possible (yet) to create a read-only combo box in
    // the same sense that wxTextCtrl is read-only, simply do not create
    // the control in this case.
    if ( property->HasFlag(wxPG_PROP_READONLY) )
        return NULL;

    const wxPGChoices& choices = property->GetChoices();
    wxString defString;
    int index = property->GetChoiceSelection();

    int argFlags = 0;
    if ( !property->HasFlag(wxPG_PROP_READONLY) &&
         !property->IsValueUnspecified() )
        argFlags |= wxPG_EDITABLE_VALUE;
    defString = property->GetValueAsString(argFlags);

    wxArrayString labels = choices.GetLabels();

    wxPGComboBox* cb;

    wxPoint po(pos);
    wxSize si(sz);
    po.y += wxPG_CHOICEYADJUST;
    si.y -= (wxPG_CHOICEYADJUST*2);

    po.x += wxPG_CHOICEXADJUST;
    si.x -= wxPG_CHOICEXADJUST;
    wxWindow* ctrlParent = propGrid->GetPanel();

    int odcbFlags = extraStyle | wxBORDER_NONE | wxTE_PROCESS_ENTER;

    if ( (property->GetFlags() & wxPG_PROP_USE_DCC) &&
         wxDynamicCast(property, wxBoolProperty) )
        odcbFlags |= wxODCB_DCLICK_CYCLES;

    //
    // If common value specified, use appropriate index
    unsigned int cmnVals = property->GetDisplayedCommonValueCount();
    if ( cmnVals )
    {
        if ( !property->IsValueUnspecified() )
        {
            int cmnVal = property->GetCommonValue();
            if ( cmnVal >= 0 )
            {
                index = labels.size() + cmnVal;
            }
        }

        unsigned int i;
        for ( i=0; i<cmnVals; i++ )
            labels.Add(propGrid->GetCommonValueLabel(i));
    }

    cb = new wxPGComboBox();
#ifdef __WXMSW__
    cb->Hide();
#endif
    cb->Create(ctrlParent,
               wxPG_SUBID1,
               wxString(),
               po,
               si,
               labels,
               odcbFlags);

    cb->SetButtonPosition(si.y,0,wxRIGHT);
    cb->SetMargins(wxPG_XBEFORETEXT-1);

    // Set hint text
    cb->SetHint(property->GetHintText());

    wxPGChoiceEditor_SetCustomPaintWidth( propGrid, cb,
                                          property->GetCommonValue() );

    if ( index >= 0 && index < (int)cb->GetCount() )
    {
        cb->SetSelection( index );
        if ( !defString.empty() )
            cb->SetText( defString );
    }
    else if ( !(extraStyle & wxCB_READONLY) && !defString.empty() )
    {
        propGrid->SetupTextCtrlValue(defString);
        cb->SetValue( defString );
    }
    else
    {
        cb->SetSelection( -1 );
    }

#ifdef __WXMSW__
    cb->Show();
#endif

    return (wxWindow*) cb;
}


void wxPGChoiceEditor::UpdateControl( wxPGProperty* property, wxWindow* ctrl ) const
{
    wxASSERT( ctrl );
    wxOwnerDrawnComboBox* cb = (wxOwnerDrawnComboBox*)ctrl;
    wxASSERT( wxDynamicCast(cb, wxOwnerDrawnComboBox));
    int ind = property->GetChoiceSelection();
    cb->SetSelection(ind);
}

wxPGWindowList wxPGChoiceEditor::CreateControls( wxPropertyGrid* propGrid, wxPGProperty* property,
        const wxPoint& pos, const wxSize& sz ) const
{
    return CreateControlsBase(propGrid,property,pos,sz,wxCB_READONLY);
}


int wxPGChoiceEditor::InsertItem( wxWindow* ctrl, const wxString& label, int index ) const
{
    wxASSERT( ctrl );
    wxOwnerDrawnComboBox* cb = (wxOwnerDrawnComboBox*)ctrl;
    wxASSERT( wxDynamicCast(cb, wxOwnerDrawnComboBox));

    if (index < 0)
        index = cb->GetCount();

    return cb->Insert(label,index);
}


void wxPGChoiceEditor::DeleteItem( wxWindow* ctrl, int index ) const
{
    wxASSERT( ctrl );
    wxOwnerDrawnComboBox* cb = (wxOwnerDrawnComboBox*)ctrl;
    wxASSERT( wxDynamicCast(cb, wxOwnerDrawnComboBox));

    cb->Delete(index);
}

bool wxPGChoiceEditor::OnEvent( wxPropertyGrid* propGrid, wxPGProperty* property,
    wxWindow* ctrl, wxEvent& event ) const
{
    if ( event.GetEventType() == wxEVT_COMBOBOX )
    {
        wxPGComboBox* cb = (wxPGComboBox*)ctrl;
        int index = cb->GetSelection();
        int cmnValIndex = -1;
        int cmnVals = property->GetDisplayedCommonValueCount();
        int items = cb->GetCount();

        if ( index >= (items-cmnVals) )
        {
            // Yes, a common value is being selected
            cmnValIndex = index - (items-cmnVals);
            property->SetCommonValue( cmnValIndex );

            // Truly set value to unspecified?
            if ( propGrid->GetUnspecifiedCommonValue() == cmnValIndex )
            {
                if ( !property->IsValueUnspecified() )
                    propGrid->SetInternalFlag(wxPG_FL_VALUE_CHANGE_IN_EVENT);
                property->SetValueToUnspecified();
                if ( !cb->HasFlag(wxCB_READONLY) )
                {
                    wxString unspecValueText;
                    unspecValueText = propGrid->GetUnspecifiedValueText();
                    propGrid->SetupTextCtrlValue(unspecValueText);
                    cb->GetTextCtrl()->SetValue(unspecValueText);
                }
                return false;
            }
        }
        return wxPGChoiceEditor_SetCustomPaintWidth( propGrid, cb, cmnValIndex );
    }
    return false;
}


bool wxPGChoiceEditor::GetValueFromControl( wxVariant& variant, wxPGProperty* property, wxWindow* ctrl ) const
{
    wxOwnerDrawnComboBox* cb = (wxOwnerDrawnComboBox*)ctrl;

    int index = cb->GetSelection();

    if ( index != property->GetChoiceSelection() ||
        // Changing unspecified always causes event (returning
        // true here should be enough to trigger it).
         property->IsValueUnspecified()
       )
    {
        return property->IntToValue(variant, index, wxPG_PROPERTY_SPECIFIC);
    }
    return false;
}


void wxPGChoiceEditor::SetControlStringValue( wxPGProperty* property,
                                              wxWindow* ctrl,
                                              const wxString& txt ) const
{
    wxOwnerDrawnComboBox* cb = (wxOwnerDrawnComboBox*)ctrl;
    wxASSERT( cb );
    property->GetGrid()->SetupTextCtrlValue(txt);
    cb->SetValue(txt);
}


void wxPGChoiceEditor::SetControlIntValue( wxPGProperty* WXUNUSED(property), wxWindow* ctrl, int value ) const
{
    wxOwnerDrawnComboBox* cb = (wxOwnerDrawnComboBox*)ctrl;
    wxASSERT( cb );
    cb->SetSelection(value);
}


void wxPGChoiceEditor::SetValueToUnspecified( wxPGProperty* WXUNUSED(property),
                                              wxWindow* ctrl ) const
{
    wxOwnerDrawnComboBox* cb = (wxOwnerDrawnComboBox*)ctrl;

    if ( cb->HasFlag(wxCB_READONLY) )
        cb->SetSelection(-1);
}


bool wxPGChoiceEditor::CanContainCustomImage() const
{
    return true;
}


wxPGChoiceEditor::~wxPGChoiceEditor()
{
    wxPG_EDITOR(Choice) = NULL;
}


// -----------------------------------------------------------------------
// wxPGComboBoxEditor
// -----------------------------------------------------------------------


WX_PG_IMPLEMENT_INTERNAL_EDITOR_CLASS(ComboBox,
                                      wxPGComboBoxEditor,
                                      wxPGChoiceEditor)


void wxPGComboBoxEditor::UpdateControl( wxPGProperty* property, wxWindow* ctrl ) const
{
    wxOwnerDrawnComboBox* cb = (wxOwnerDrawnComboBox*)ctrl;
    wxString s = property->GetValueAsString(wxPG_EDITABLE_VALUE);
    property->GetGrid()->SetupTextCtrlValue(s);
    cb->SetValue(s);

    // TODO: If string matches any selection, then select that.
}


wxPGWindowList wxPGComboBoxEditor::CreateControls( wxPropertyGrid* propGrid,
                                                   wxPGProperty* property,
                                                   const wxPoint& pos,
                                                   const wxSize& sz ) const
{
    return CreateControlsBase(propGrid,property,pos,sz,0);
}


bool wxPGComboBoxEditor::OnEvent( wxPropertyGrid* propGrid,
                                  wxPGProperty* property,
                                  wxWindow* ctrl,
                                  wxEvent& event ) const
{
    wxOwnerDrawnComboBox* cb = NULL;
    wxWindow* textCtrl = NULL;

    if ( ctrl )
    {
        cb = (wxOwnerDrawnComboBox*)ctrl;
        textCtrl = cb->GetTextCtrl();
    }

    if ( wxPGTextCtrlEditor::OnTextCtrlEvent(propGrid,property,textCtrl,event) )
        return true;

    return wxPGChoiceEditor::OnEvent(propGrid,property,ctrl,event);
}


bool wxPGComboBoxEditor::GetValueFromControl( wxVariant& variant, wxPGProperty* property, wxWindow* ctrl ) const
{
    wxOwnerDrawnComboBox* cb = (wxOwnerDrawnComboBox*)ctrl;
    wxString textVal = cb->GetValue();

    if ( property->UsesAutoUnspecified() && textVal.empty() )
    {
        variant.MakeNull();
        return true;
    }

    bool res = property->StringToValue(variant, textVal, wxPG_EDITABLE_VALUE|wxPG_PROPERTY_SPECIFIC);

    // Changing unspecified always causes event (returning
    // true here should be enough to trigger it).
    if ( !res && variant.IsNull() )
        res = true;

    return res;
}


void wxPGComboBoxEditor::OnFocus( wxPGProperty* property,
                                  wxWindow* ctrl ) const
{
    wxOwnerDrawnComboBox* cb = (wxOwnerDrawnComboBox*)ctrl;
    wxPGTextCtrlEditor_OnFocus(property, cb->GetTextCtrl());
}


wxPGComboBoxEditor::~wxPGComboBoxEditor()
{
    wxPG_EDITOR(ComboBox) = NULL;
}



// -----------------------------------------------------------------------
// wxPGChoiceAndButtonEditor
// -----------------------------------------------------------------------


WX_PG_IMPLEMENT_INTERNAL_EDITOR_CLASS(ChoiceAndButton,
                                      wxPGChoiceAndButtonEditor,
                                      wxPGChoiceEditor)


wxPGWindowList wxPGChoiceAndButtonEditor::CreateControls( wxPropertyGrid* propGrid,
                                                          wxPGProperty* property,
                                                          const wxPoint& pos,
                                                          const wxSize& sz ) const
{
    // Use one two units smaller to match size of the combo's dropbutton.
    // (normally a bigger button is used because it looks better)
    int bt_wid = sz.y;
    bt_wid -= 2;
    wxSize bt_sz(bt_wid,bt_wid);

    // Position of button.
    wxPoint bt_pos(pos.x+sz.x-bt_sz.x,pos.y);
#ifdef __WXMAC__
    bt_pos.y -= 1;
#else
    bt_pos.y += 1;
#endif

    wxWindow* bt = propGrid->GenerateEditorButton( bt_pos, bt_sz );

    // Size of choice.
    wxSize ch_sz(sz.x-bt->GetSize().x,sz.y);

#ifdef __WXMAC__
    ch_sz.x -= wxPG_TEXTCTRL_AND_BUTTON_SPACING;
#endif

    wxWindow* ch = wxPGEditor_Choice->CreateControls(propGrid,property,
        pos,ch_sz).m_primary;

#ifdef __WXMSW__
    bt->Show();
#endif

    return wxPGWindowList(ch, bt);
}


wxPGChoiceAndButtonEditor::~wxPGChoiceAndButtonEditor()
{
    wxPG_EDITOR(ChoiceAndButton) = NULL;
}

// -----------------------------------------------------------------------
// wxPGTextCtrlAndButtonEditor
// -----------------------------------------------------------------------

WX_PG_IMPLEMENT_INTERNAL_EDITOR_CLASS(TextCtrlAndButton,
                                      wxPGTextCtrlAndButtonEditor,
                                      wxPGTextCtrlEditor)


wxPGWindowList wxPGTextCtrlAndButtonEditor::CreateControls( wxPropertyGrid* propGrid,
                                                            wxPGProperty* property,
                                                            const wxPoint& pos,
                                                            const wxSize& sz ) const
{
    wxWindow* wnd2;
    wxWindow* wnd = propGrid->GenerateEditorTextCtrlAndButton( pos, sz, &wnd2,
        property->GetFlags() & wxPG_PROP_NOEDITOR, property);

    return wxPGWindowList(wnd, wnd2);
}


wxPGTextCtrlAndButtonEditor::~wxPGTextCtrlAndButtonEditor()
{
    wxPG_EDITOR(TextCtrlAndButton) = NULL;
}

// -----------------------------------------------------------------------
// wxPGCheckBoxEditor
// -----------------------------------------------------------------------

#if wxPG_INCLUDE_CHECKBOX

WX_PG_IMPLEMENT_INTERNAL_EDITOR_CLASS(CheckBox,
                                      wxPGCheckBoxEditor,
                                      wxPGEditor)


// Check box state flags
enum
{
    wxSCB_STATE_UNCHECKED   = 0,
    wxSCB_STATE_CHECKED     = 1,
    wxSCB_STATE_BOLD        = 2,
    wxSCB_STATE_UNSPECIFIED = 4
};

const int wxSCB_SETVALUE_CYCLE = 2;


static void DrawSimpleCheckBox( wxDC& dc, const wxRect& rect, int box_hei,
                                int state )
{
    // Box rectangle.
    wxRect r(rect.x+wxPG_XBEFORETEXT,rect.y+((rect.height-box_hei)/2),
             box_hei,box_hei);
    wxColour useCol = dc.GetTextForeground();

    if ( state & wxSCB_STATE_UNSPECIFIED )
    {
        useCol = wxColour(220, 220, 220);
    }

    // Draw check mark first because it is likely to overdraw the
    // surrounding rectangle.
    if ( state & wxSCB_STATE_CHECKED )
    {
        wxRect r2(r.x+wxPG_CHECKMARK_XADJ,
                  r.y+wxPG_CHECKMARK_YADJ,
                  r.width+wxPG_CHECKMARK_WADJ,
                  r.height+wxPG_CHECKMARK_HADJ);
    #if wxPG_CHECKMARK_DEFLATE
        r2.Deflate(wxPG_CHECKMARK_DEFLATE);
    #endif
        dc.DrawCheckMark(r2);

        // This would draw a simple cross check mark.
        // dc.DrawLine(r.x,r.y,r.x+r.width-1,r.y+r.height-1);
        // dc.DrawLine(r.x,r.y+r.height-1,r.x+r.width-1,r.y);
    }

    if ( !(state & wxSCB_STATE_BOLD) )
    {
        // Pen for thin rectangle.
        dc.SetPen(useCol);
    }
    else
    {
        // Pen for bold rectangle.
        wxPen linepen(useCol,2,wxSOLID);
        linepen.SetJoin(wxJOIN_MITER); // This prevents round edges.
        dc.SetPen(linepen);
        r.x++;
        r.y++;
        r.width--;
        r.height--;
    }

    dc.SetBrush(*wxTRANSPARENT_BRUSH);

    dc.DrawRectangle(r);
    dc.SetPen(*wxTRANSPARENT_PEN);
}

//
// Real simple custom-drawn checkbox-without-label class.
//
class wxSimpleCheckBox : public wxControl
{
public:

    void SetValue( int value );

    wxSimpleCheckBox( wxWindow* parent,
                      wxWindowID id,
                      const wxPoint& pos = wxDefaultPosition,
                      const wxSize& size = wxDefaultSize )
        : wxControl(parent,id,pos,size,wxBORDER_NONE|wxWANTS_CHARS)
    {
        // Due to SetOwnFont stuff necessary for GTK+ 1.2, we need to have this
        SetFont( parent->GetFont() );

        m_state = 0;
        m_boxHeight = 12;

        SetBackgroundStyle( wxBG_STYLE_CUSTOM );
    }

    virtual ~wxSimpleCheckBox();

    int m_state;
    int m_boxHeight;

private:
    void OnPaint( wxPaintEvent& event );
    void OnLeftClick( wxMouseEvent& event );
    void OnKeyDown( wxKeyEvent& event );

    void OnResize( wxSizeEvent& event )
    {
        Refresh();
        event.Skip();
    }

    static wxBitmap* ms_doubleBuffer;

    DECLARE_EVENT_TABLE()
};

BEGIN_EVENT_TABLE(wxSimpleCheckBox, wxControl)
    EVT_PAINT(wxSimpleCheckBox::OnPaint)
    EVT_LEFT_DOWN(wxSimpleCheckBox::OnLeftClick)
    EVT_LEFT_DCLICK(wxSimpleCheckBox::OnLeftClick)
    EVT_KEY_DOWN(wxSimpleCheckBox::OnKeyDown)
    EVT_SIZE(wxSimpleCheckBox::OnResize)
END_EVENT_TABLE()

wxSimpleCheckBox::~wxSimpleCheckBox()
{
    wxDELETE(ms_doubleBuffer);
}

wxBitmap* wxSimpleCheckBox::ms_doubleBuffer = NULL;

void wxSimpleCheckBox::OnPaint( wxPaintEvent& WXUNUSED(event) )
{
    wxSize clientSize = GetClientSize();
    wxAutoBufferedPaintDC dc(this);

    dc.Clear();
    wxRect rect(0,0,clientSize.x,clientSize.y);
    rect.y += 1;
    rect.width += 1;

    wxColour bgcol = GetBackgroundColour();
    dc.SetBrush( bgcol );
    dc.SetPen( bgcol );
    dc.DrawRectangle( rect );

    dc.SetTextForeground(GetForegroundColour());

    int state = m_state;
    if ( !(state & wxSCB_STATE_UNSPECIFIED) &&
         GetFont().GetWeight() == wxBOLD )
        state |= wxSCB_STATE_BOLD;

    DrawSimpleCheckBox(dc, rect, m_boxHeight, state);
}

void wxSimpleCheckBox::OnLeftClick( wxMouseEvent& event )
{
    if ( (event.m_x > (wxPG_XBEFORETEXT-2)) &&
         (event.m_x <= (wxPG_XBEFORETEXT-2+m_boxHeight)) )
    {
        SetValue(wxSCB_SETVALUE_CYCLE);
    }
}

void wxSimpleCheckBox::OnKeyDown( wxKeyEvent& event )
{
    if ( event.GetKeyCode() == WXK_SPACE )
    {
        SetValue(wxSCB_SETVALUE_CYCLE);
    }
}

void wxSimpleCheckBox::SetValue( int value )
{
    if ( value == wxSCB_SETVALUE_CYCLE )
    {
        if ( m_state & wxSCB_STATE_CHECKED )
            m_state &= ~wxSCB_STATE_CHECKED;
        else
            m_state |= wxSCB_STATE_CHECKED;
    }
    else
    {
        m_state = value;
    }
    Refresh();

    wxCommandEvent evt(wxEVT_CHECKBOX,GetParent()->GetId());

    wxPropertyGrid* propGrid = (wxPropertyGrid*) GetParent();
    wxASSERT( wxDynamicCast(propGrid, wxPropertyGrid) );
    propGrid->HandleCustomEditorEvent(evt);
}

wxPGWindowList wxPGCheckBoxEditor::CreateControls( wxPropertyGrid* propGrid,
                                                   wxPGProperty* property,
                                                   const wxPoint& pos,
                                                   const wxSize& size ) const
{
    if ( property->HasFlag(wxPG_PROP_READONLY) )
        return NULL;

    wxPoint pt = pos;
    pt.x -= wxPG_XBEFOREWIDGET;
    wxSize sz = size;
    sz.x = propGrid->GetFontHeight() + (wxPG_XBEFOREWIDGET*2) + 4;

    wxSimpleCheckBox* cb = new wxSimpleCheckBox(propGrid->GetPanel(),
                                                wxPG_SUBID1, pt, sz);

    cb->SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));

    UpdateControl(property, cb);

    if ( !property->IsValueUnspecified() )
    {
        // If mouse cursor was on the item, toggle the value now.
        if ( propGrid->GetInternalFlags() & wxPG_FL_ACTIVATION_BY_CLICK )
        {
            wxPoint point = cb->ScreenToClient(::wxGetMousePosition());
            if ( point.x <= (wxPG_XBEFORETEXT-2+cb->m_boxHeight) )
            {
                if ( cb->m_state & wxSCB_STATE_CHECKED )
                    cb->m_state &= ~wxSCB_STATE_CHECKED;
                else
                    cb->m_state |= wxSCB_STATE_CHECKED;

                // Makes sure wxPG_EVT_CHANGING etc. is sent for this initial
                // click
                propGrid->ChangePropertyValue(property,
                                              wxPGVariant_Bool(cb->m_state));
            }
        }
    }

    propGrid->SetInternalFlag( wxPG_FL_FIXED_WIDTH_EDITOR );

    return cb;
}

void wxPGCheckBoxEditor::DrawValue( wxDC& dc, const wxRect& rect,
                                    wxPGProperty* property,
                                    const wxString& WXUNUSED(text) ) const
{
    int state = wxSCB_STATE_UNCHECKED;

    if ( !property->IsValueUnspecified() )
    {
        state = property->GetChoiceSelection();
        if ( dc.GetFont().GetWeight() == wxBOLD )
            state |= wxSCB_STATE_BOLD;
    }
    else
    {
        state |= wxSCB_STATE_UNSPECIFIED;
    }

    DrawSimpleCheckBox(dc, rect, dc.GetCharHeight(), state);
}

void wxPGCheckBoxEditor::UpdateControl( wxPGProperty* property,
                                        wxWindow* ctrl ) const
{
    wxSimpleCheckBox* cb = (wxSimpleCheckBox*) ctrl;
    wxASSERT( cb );

    if ( !property->IsValueUnspecified() )
        cb->m_state = property->GetChoiceSelection();
    else
        cb->m_state = wxSCB_STATE_UNSPECIFIED;

    wxPropertyGrid* propGrid = property->GetGrid();
    cb->m_boxHeight = propGrid->GetFontHeight();

    cb->Refresh();
}

bool wxPGCheckBoxEditor::OnEvent( wxPropertyGrid* WXUNUSED(propGrid), wxPGProperty* WXUNUSED(property),
    wxWindow* WXUNUSED(ctrl), wxEvent& event ) const
{
    if ( event.GetEventType() == wxEVT_CHECKBOX )
    {
        return true;
    }
    return false;
}


bool wxPGCheckBoxEditor::GetValueFromControl( wxVariant& variant, wxPGProperty* property, wxWindow* ctrl ) const
{
    wxSimpleCheckBox* cb = (wxSimpleCheckBox*)ctrl;

    int index = cb->m_state;

    if ( index != property->GetChoiceSelection() ||
         // Changing unspecified always causes event (returning
         // true here should be enough to trigger it).
         property->IsValueUnspecified()
       )
    {
        return property->IntToValue(variant, index, wxPG_PROPERTY_SPECIFIC);
    }
    return false;
}


void wxPGCheckBoxEditor::SetControlIntValue( wxPGProperty* WXUNUSED(property), wxWindow* ctrl, int value ) const
{
    if ( value != 0 ) value = 1;
    ((wxSimpleCheckBox*)ctrl)->m_state = value;
    ctrl->Refresh();
}


void wxPGCheckBoxEditor::SetValueToUnspecified( wxPGProperty* WXUNUSED(property), wxWindow* ctrl ) const
{
    ((wxSimpleCheckBox*)ctrl)->m_state = wxSCB_STATE_UNSPECIFIED;
    ctrl->Refresh();
}


wxPGCheckBoxEditor::~wxPGCheckBoxEditor()
{
    wxPG_EDITOR(CheckBox) = NULL;
}

#endif // wxPG_INCLUDE_CHECKBOX

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

wxWindow* wxPropertyGrid::GetEditorControl() const
{
    wxWindow* ctrl = m_wndEditor;

    if ( !ctrl )
        return ctrl;

    return ctrl;
}

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

void wxPropertyGrid::CorrectEditorWidgetSizeX()
{
    int secWid = 0;

    // Use fixed selColumn 1 for main editor widgets
    int newSplitterx = m_pState->DoGetSplitterPosition(0);
    int newWidth = newSplitterx + m_pState->m_colWidths[1];

    if ( m_wndEditor2 )
    {
        // if width change occurred, move secondary wnd by that amount
        wxRect r = m_wndEditor2->GetRect();
        secWid = r.width;
        r.x = newWidth - secWid;

        m_wndEditor2->SetSize( r );

        // if primary is textctrl, then we have to add some extra space
#ifdef __WXMAC__
        if ( m_wndEditor )
#else
        if ( wxDynamicCast(m_wndEditor, wxTextCtrl) )
#endif
            secWid += wxPG_TEXTCTRL_AND_BUTTON_SPACING;
    }

    if ( m_wndEditor )
    {
        wxRect r = m_wndEditor->GetRect();

        r.x = newSplitterx+m_ctrlXAdjust;

        if ( !(m_iFlags & wxPG_FL_FIXED_WIDTH_EDITOR) )
            r.width = newWidth - r.x - secWid;

        m_wndEditor->SetSize(r);
    }

    if ( m_wndEditor2 )
        m_wndEditor2->Refresh();
}

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

void wxPropertyGrid::CorrectEditorWidgetPosY()
{
    wxPGProperty* selected = GetSelection();

    if ( selected )
    {
        if ( m_labelEditor )
        {
            wxRect r = GetEditorWidgetRect(selected, m_selColumn);
            wxPoint pos = m_labelEditor->GetPosition();

            // Calculate y offset
            int offset = pos.y % m_lineHeight;

            m_labelEditor->Move(pos.x, r.y + offset);
        }

        if ( m_wndEditor || m_wndEditor2 )
        {
            wxRect r = GetEditorWidgetRect(selected, 1);

            if ( m_wndEditor )
            {
                wxPoint pos = m_wndEditor->GetPosition();

                // Calculate y offset
                int offset = pos.y % m_lineHeight;

                m_wndEditor->Move(pos.x, r.y + offset);
            }

            if ( m_wndEditor2 )
            {
                wxPoint pos = m_wndEditor2->GetPosition();

                m_wndEditor2->Move(pos.x, r.y);
            }
        }
    }
}

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

// Fixes position of wxTextCtrl-like control (wxSpinCtrl usually
// fits into that category as well).
void wxPropertyGrid::FixPosForTextCtrl( wxWindow* ctrl,
                                        unsigned int WXUNUSED(forColumn),
                                        const wxPoint& offset )
{
    // Center the control vertically
    wxRect finalPos = ctrl->GetRect();
    int y_adj = (m_lineHeight - finalPos.height)/2 + wxPG_TEXTCTRLYADJUST;

    // Prevent over-sized control
    int sz_dec = (y_adj + finalPos.height) - m_lineHeight;
    if ( sz_dec < 0 ) sz_dec = 0;

    finalPos.y += y_adj;
    finalPos.height -= (y_adj+sz_dec);

#ifndef wxPG_TEXTCTRLXADJUST
    int textCtrlXAdjust = wxPG_XBEFORETEXT - 1;

    wxTextCtrl* tc = static_cast<wxTextCtrl*>(ctrl);
    tc->SetMargins(0);
#else
    int textCtrlXAdjust = wxPG_TEXTCTRLXADJUST;
#endif

    finalPos.x += textCtrlXAdjust;
    finalPos.width -= textCtrlXAdjust;

    finalPos.x += offset.x;
    finalPos.y += offset.y;

    ctrl->SetSize(finalPos);
}

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

wxWindow* wxPropertyGrid::GenerateEditorTextCtrl( const wxPoint& pos,
                                                  const wxSize& sz,
                                                  const wxString& value,
                                                  wxWindow* secondary,
                                                  int extraStyle,
                                                  int maxLen,
                                                  unsigned int forColumn )
{
    wxWindowID id = wxPG_SUBID1;
    wxPGProperty* prop = GetSelection();
    wxASSERT(prop);

    int tcFlags = wxTE_PROCESS_ENTER | extraStyle;

    if ( prop->HasFlag(wxPG_PROP_READONLY) && forColumn == 1 )
        tcFlags |= wxTE_READONLY;

    wxPoint p(pos.x,pos.y);
    wxSize s(sz.x,sz.y);

   // Need to reduce width of text control on Mac
#if defined(__WXMAC__)
    s.x -= 8;
#endif

    // For label editors, trim the size to allow better splitter grabbing
    if ( forColumn != 1 )
        s.x -= 2;

    // Take button into acccount
    if ( secondary )
    {
        s.x -= (secondary->GetSize().x + wxPG_TEXTCTRL_AND_BUTTON_SPACING);
        m_iFlags &= ~(wxPG_FL_PRIMARY_FILLS_ENTIRE);
    }

    // If the height is significantly higher, then use border, and fill the rect exactly.
    bool hasSpecialSize = false;

    if ( (sz.y - m_lineHeight) > 5 )
        hasSpecialSize = true;

    wxWindow* ctrlParent = GetPanel();

    if ( !hasSpecialSize )
        tcFlags |= wxBORDER_NONE;

    wxTextCtrl* tc = new wxTextCtrl();

#if defined(__WXMSW__)
    tc->Hide();
#endif
    SetupTextCtrlValue(value);
    tc->Create(ctrlParent,id,value, p, s,tcFlags);

#if defined(__WXMSW__)
    // On Windows, we need to override read-only text ctrl's background
    // colour to white. One problem with native 'grey' background is that
    // tc->GetBackgroundColour() doesn't seem to return correct value
    // for it.
    if ( tcFlags & wxTE_READONLY )
    {
        wxVisualAttributes vattrs = tc->GetDefaultAttributes();
        tc->SetBackgroundColour(vattrs.colBg);
    }
#endif

    // This code is repeated from DoSelectProperty(). However, font boldness
    // must be set before margin is set up below in FixPosForTextCtrl().
    if ( forColumn == 1 &&
         prop->HasFlag(wxPG_PROP_MODIFIED) &&
         HasFlag(wxPG_BOLD_MODIFIED) )
         tc->SetFont( m_captionFont );

    // Center the control vertically
    if ( !hasSpecialSize )
        FixPosForTextCtrl(tc, forColumn);

    if ( forColumn != 1 )
    {
        tc->SetBackgroundColour(m_colSelBack);
        tc->SetForegroundColour(m_colSelFore);
    }

#ifdef __WXMSW__
    tc->Show();
    if ( secondary )
        secondary->Show();
#endif

    // Set maximum length
    if ( maxLen > 0 )
        tc->SetMaxLength( maxLen );

    wxVariant attrVal = prop->GetAttribute(wxPG_ATTR_AUTOCOMPLETE);
    if ( !attrVal.IsNull() )
    {
        wxASSERT(attrVal.GetType() == wxS("arrstring"));
        tc->AutoComplete(attrVal.GetArrayString());
    }

    // Set hint text
    tc->SetHint(prop->GetHintText());

    return tc;
}

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

wxWindow* wxPropertyGrid::GenerateEditorButton( const wxPoint& pos, const wxSize& sz )
{
    wxWindowID id = wxPG_SUBID2;
    wxPGProperty* selected = GetSelection();
    wxASSERT(selected);

#ifdef __WXMAC__
   // Decorations are chunky on Mac, and we can't make the button square, so
   // do things a bit differently on this platform.

   wxPoint p(pos.x+sz.x,
             pos.y+wxPG_BUTTON_SIZEDEC-wxPG_NAT_BUTTON_BORDER_Y);
   wxSize s(25, -1);

   wxButton* but = new wxButton();
   but->Create(GetPanel(),id,wxS("..."),p,s,wxWANTS_CHARS);

   // Now that we know the size, move to the correct position
   p.x = pos.x + sz.x - but->GetSize().x - 2;
   but->Move(p);

#else
    wxSize s(sz.y-(wxPG_BUTTON_SIZEDEC*2)+(wxPG_NAT_BUTTON_BORDER_Y*2),
        sz.y-(wxPG_BUTTON_SIZEDEC*2)+(wxPG_NAT_BUTTON_BORDER_Y*2));

    // Reduce button width to lineheight
    if ( s.x > m_lineHeight )
        s.x = m_lineHeight;

#ifdef __WXGTK__
    // On wxGTK, take fixed button margins into account
    if ( s.x < 25 )
        s.x = 25;
#endif

    wxPoint p(pos.x+sz.x-s.x,
        pos.y+wxPG_BUTTON_SIZEDEC-wxPG_NAT_BUTTON_BORDER_Y);

    wxButton* but = new wxButton();
  #ifdef __WXMSW__
    but->Hide();
  #endif
    but->Create(GetPanel(),id,wxS("..."),p,s,wxWANTS_CHARS);

  #ifdef __WXGTK__
    wxFont font = GetFont();
    font.SetPointSize(font.GetPointSize()-2);
    but->SetFont(font);
  #else
    but->SetFont(GetFont());
  #endif
#endif

    if ( selected->HasFlag(wxPG_PROP_READONLY) )
        but->Disable();

    return but;
}

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

wxWindow* wxPropertyGrid::GenerateEditorTextCtrlAndButton( const wxPoint& pos,
                                                           const wxSize& sz,
                                                           wxWindow** psecondary,
                                                           int limitedEditing,
                                                           wxPGProperty* property )
{
    wxButton* but = (wxButton*)GenerateEditorButton(pos,sz);
    *psecondary = (wxWindow*)but;

    if ( limitedEditing )
    {
    #ifdef __WXMSW__
        // There is button Show in GenerateEditorTextCtrl as well
        but->Show();
    #endif
        return NULL;
    }

    wxString text;

    if ( !property->IsValueUnspecified() )
        text = property->GetValueAsString(property->HasFlag(wxPG_PROP_READONLY)?0:wxPG_EDITABLE_VALUE);

    return GenerateEditorTextCtrl(pos,sz,text,but,property->m_maxLen);
}

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

void wxPropertyGrid::SetEditorAppearance( const wxPGCell& cell,
                                          bool unspecified )
{
    wxPGProperty* property = GetSelection();
    if ( !property )
        return;
    wxWindow* ctrl = GetEditorControl();
    if ( !ctrl )
        return;

    property->GetEditorClass()->SetControlAppearance( this,
                                                      property,
                                                      ctrl,
                                                      cell,
                                                      m_editorAppearance,
                                                      unspecified );

    m_editorAppearance = cell;
}

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

wxTextCtrl* wxPropertyGrid::GetEditorTextCtrl() const
{
    wxWindow* wnd = GetEditorControl();

    if ( !wnd )
        return NULL;

    if ( wxDynamicCast(wnd, wxTextCtrl) )
        return wxStaticCast(wnd, wxTextCtrl);

    if ( wxDynamicCast(wnd, wxOwnerDrawnComboBox) )
    {
        wxOwnerDrawnComboBox* cb = wxStaticCast(wnd, wxOwnerDrawnComboBox);
        return cb->GetTextCtrl();
    }

    return NULL;
}

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

wxPGEditor* wxPropertyGridInterface::GetEditorByName( const wxString& editorName )
{
    wxPGHashMapS2P::const_iterator it;

    it = wxPGGlobalVars->m_mapEditorClasses.find(editorName);
    if ( it == wxPGGlobalVars->m_mapEditorClasses.end() )
        return NULL;
    return (wxPGEditor*) it->second;
}

// -----------------------------------------------------------------------
// wxPGEditorDialogAdapter
// -----------------------------------------------------------------------

IMPLEMENT_ABSTRACT_CLASS(wxPGEditorDialogAdapter, wxObject)

bool wxPGEditorDialogAdapter::ShowDialog( wxPropertyGrid* propGrid, wxPGProperty* property )
{
    if ( !propGrid->EditorValidate() )
        return false;

    bool res = DoShowDialog( propGrid, property );

    if ( res )
    {
        propGrid->ValueChangeInEvent( m_value );
        return true;
    }

    return false;
}

// -----------------------------------------------------------------------
// wxPGMultiButton
// -----------------------------------------------------------------------

wxPGMultiButton::wxPGMultiButton( wxPropertyGrid* pg, const wxSize& sz )
    : wxWindow( pg->GetPanel(), wxPG_SUBID2, wxPoint(-100,-100), wxSize(0, sz.y) ),
      m_fullEditorSize(sz), m_buttonsWidth(0)
{
    SetBackgroundColour(pg->GetCellBackgroundColour());
}

void wxPGMultiButton::Finalize( wxPropertyGrid* WXUNUSED(propGrid),
                                const wxPoint& pos )
{
    Move( pos.x + m_fullEditorSize.x - m_buttonsWidth, pos.y );
}

int wxPGMultiButton::GenId( int itemid ) const
{
    if ( itemid < -1 )
    {
        if ( m_buttons.size() )
            itemid = GetButton(m_buttons.size()-1)->GetId() + 1;
        else
            itemid = wxPG_SUBID2;
    }
    return itemid;
}

#if wxUSE_BMPBUTTON
void wxPGMultiButton::Add( const wxBitmap& bitmap, int itemid )
{
    itemid = GenId(itemid);
    wxSize sz = GetSize();
    wxButton* button = new wxBitmapButton( this, itemid, bitmap,
                                           wxPoint(sz.x, 0),
                                           wxSize(sz.y, sz.y) );
    DoAddButton( button, sz );
}
#endif

void wxPGMultiButton::Add( const wxString& label, int itemid )
{
    itemid = GenId(itemid);
    wxSize sz = GetSize();
    wxButton* button = new wxButton( this, itemid, label, wxPoint(sz.x, 0),
                                     wxSize(sz.y, sz.y) );
    DoAddButton( button, sz );
}

void wxPGMultiButton::DoAddButton( wxWindow* button,
                                   const wxSize& sz )
{
    m_buttons.push_back(button);
    int bw = button->GetSize().x;
    SetSize(wxSize(sz.x+bw,sz.y));
    m_buttonsWidth += bw;
}

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

#endif  // wxUSE_PROPGRID
