Files
wxWidgets/src/propgrid/editors.cpp
Vadim Zeitlin bd30752410 Just deprecate but don't schedule for removal wxNORMAL and friends.
Deprecate the use of the old untyped constants but don't schedule them from
removal in a future wx version by removing "#if WXWIN_COMPATIBILITY_3_0" tests
around their definition and use. Provoking deprecation warnings in the code
using these constants is worth it as they are unclear and it's easy to make
mistakes when using them, but breaking this code compilation outright can't be
justified -- even in the future.

Also use more informational wxDEPRECATED_MSG() instead of a simple
wxDEPRECATED() as it might not be obvious at all how should the code be
updated exactly.

Finally, avoid the use of deprecated constants inside the library itself.

As a side effect, this closes #15814.

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@75565 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
2014-01-06 12:42:37 +00:00

2203 lines
61 KiB
C++

/////////////////////////////////////////////////////////////////////////////
// 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) )
{
wxPropertyGrid* pg = GetGrid();
#ifdef wxPG_TEXTCTRLXADJUST
textCtrlXAdjust = wxPG_TEXTCTRLXADJUST -
(wxPG_XBEFOREWIDGET+wxPG_CONTROL_MARGIN+1) - 1,
#endif
wxOwnerDrawnComboBox::PositionTextCtrl(
textCtrlXAdjust,
pg->GetSpacingY() + 2
);
}
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, 0 );
}
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);
// 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,wxPENSTYLE_SOLID);
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() == wxFONTWEIGHT_BOLD )
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() == wxFONTWEIGHT_BOLD )
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, 0);
}
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