Adding label editing to native OS X listctrl.
git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@43053 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
@@ -16,6 +16,8 @@
|
|||||||
|
|
||||||
class wxMacDataBrowserListCtrlControl;
|
class wxMacDataBrowserListCtrlControl;
|
||||||
class wxMacListControl;
|
class wxMacListControl;
|
||||||
|
class wxListCtrlTextCtrlWrapper;
|
||||||
|
class wxListCtrlRenameTimer;
|
||||||
|
|
||||||
WX_DECLARE_EXPORTED_LIST(wxListItem, wxColumnList);
|
WX_DECLARE_EXPORTED_LIST(wxListItem, wxColumnList);
|
||||||
|
|
||||||
@@ -318,6 +320,25 @@ class WXDLLEXPORT wxListCtrl: public wxControl
|
|||||||
virtual bool SetBackgroundColour(const wxColour& colour);
|
virtual bool SetBackgroundColour(const wxColour& colour);
|
||||||
virtual wxColour GetBackgroundColour();
|
virtual wxColour GetBackgroundColour();
|
||||||
|
|
||||||
|
// functions for editing/timer
|
||||||
|
void OnRenameTimer();
|
||||||
|
bool OnRenameAccept(long itemEdit, const wxString& value);
|
||||||
|
void OnRenameCancelled(long itemEdit);
|
||||||
|
|
||||||
|
void ChangeCurrent(long current);
|
||||||
|
void ResetCurrent() { ChangeCurrent((long)-1); }
|
||||||
|
bool HasCurrent() const { return m_current != (long)-1; }
|
||||||
|
|
||||||
|
void OnLeftDown(wxMouseEvent& event);
|
||||||
|
void OnDblClick(wxMouseEvent& event);
|
||||||
|
|
||||||
|
void FinishEditing(wxTextCtrl *text)
|
||||||
|
{
|
||||||
|
delete text;
|
||||||
|
m_textctrlWrapper = NULL;
|
||||||
|
SetFocus();
|
||||||
|
}
|
||||||
|
|
||||||
// with CG, we need to get the context from an kEventControlDraw event
|
// with CG, we need to get the context from an kEventControlDraw event
|
||||||
// unfortunately, the DataBrowser callbacks don't provide the context
|
// unfortunately, the DataBrowser callbacks don't provide the context
|
||||||
// and we need it, so we need to set/remove it before and after draw
|
// and we need it, so we need to set/remove it before and after draw
|
||||||
@@ -333,6 +354,9 @@ protected:
|
|||||||
|
|
||||||
virtual wxSize DoGetBestSize() const;
|
virtual wxSize DoGetBestSize() const;
|
||||||
|
|
||||||
|
long m_current;
|
||||||
|
wxListCtrlTextCtrlWrapper *m_textctrlWrapper;
|
||||||
|
wxListCtrlRenameTimer *m_renameTimer;
|
||||||
// common part of all ctors
|
// common part of all ctors
|
||||||
void Init();
|
void Init();
|
||||||
|
|
||||||
@@ -364,6 +388,9 @@ protected:
|
|||||||
// keep track of inserted/deleted columns
|
// keep track of inserted/deleted columns
|
||||||
|
|
||||||
int m_count; // for virtual lists, store item count
|
int m_count; // for virtual lists, store item count
|
||||||
|
|
||||||
|
private:
|
||||||
|
DECLARE_EVENT_TABLE()
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@@ -36,6 +36,7 @@
|
|||||||
|
|
||||||
#include "wx/imaglist.h"
|
#include "wx/imaglist.h"
|
||||||
#include "wx/sysopt.h"
|
#include "wx/sysopt.h"
|
||||||
|
#include "wx/timer.h"
|
||||||
|
|
||||||
#define wxMAC_ALWAYS_USE_GENERIC_LISTCTRL wxT("mac.listctrl.always_use_generic")
|
#define wxMAC_ALWAYS_USE_GENERIC_LISTCTRL wxT("mac.listctrl.always_use_generic")
|
||||||
|
|
||||||
@@ -304,6 +305,203 @@ bool wxMacListCtrlEventDelegate::ProcessEvent( wxEvent& event )
|
|||||||
return wxEvtHandler::ProcessEvent(event);
|
return wxEvtHandler::ProcessEvent(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// wxListCtrlRenameTimer (internal)
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
class wxListCtrlRenameTimer: public wxTimer
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
wxListCtrl *m_owner;
|
||||||
|
|
||||||
|
public:
|
||||||
|
wxListCtrlRenameTimer( wxListCtrl *owner );
|
||||||
|
void Notify();
|
||||||
|
};
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// wxListCtrlTextCtrlWrapper: wraps a wxTextCtrl to make it work for inline editing
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
class wxListCtrlTextCtrlWrapper : public wxEvtHandler
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
// NB: text must be a valid object but not Create()d yet
|
||||||
|
wxListCtrlTextCtrlWrapper(wxListCtrl *owner,
|
||||||
|
wxTextCtrl *text,
|
||||||
|
long itemEdit);
|
||||||
|
|
||||||
|
wxTextCtrl *GetText() const { return m_text; }
|
||||||
|
|
||||||
|
void AcceptChangesAndFinish();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void OnChar( wxKeyEvent &event );
|
||||||
|
void OnKeyUp( wxKeyEvent &event );
|
||||||
|
void OnKillFocus( wxFocusEvent &event );
|
||||||
|
|
||||||
|
bool AcceptChanges();
|
||||||
|
void Finish();
|
||||||
|
|
||||||
|
private:
|
||||||
|
wxListCtrl *m_owner;
|
||||||
|
wxTextCtrl *m_text;
|
||||||
|
wxString m_startValue;
|
||||||
|
long m_itemEdited;
|
||||||
|
bool m_finished;
|
||||||
|
bool m_aboutToFinish;
|
||||||
|
|
||||||
|
DECLARE_EVENT_TABLE()
|
||||||
|
};
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// wxListCtrlRenameTimer (internal)
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
wxListCtrlRenameTimer::wxListCtrlRenameTimer( wxListCtrl *owner )
|
||||||
|
{
|
||||||
|
m_owner = owner;
|
||||||
|
}
|
||||||
|
|
||||||
|
void wxListCtrlRenameTimer::Notify()
|
||||||
|
{
|
||||||
|
m_owner->OnRenameTimer();
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// wxListCtrlTextCtrlWrapper (internal)
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
BEGIN_EVENT_TABLE(wxListCtrlTextCtrlWrapper, wxEvtHandler)
|
||||||
|
EVT_CHAR (wxListCtrlTextCtrlWrapper::OnChar)
|
||||||
|
EVT_KEY_UP (wxListCtrlTextCtrlWrapper::OnKeyUp)
|
||||||
|
EVT_KILL_FOCUS (wxListCtrlTextCtrlWrapper::OnKillFocus)
|
||||||
|
END_EVENT_TABLE()
|
||||||
|
|
||||||
|
wxListCtrlTextCtrlWrapper::wxListCtrlTextCtrlWrapper(wxListCtrl *owner,
|
||||||
|
wxTextCtrl *text,
|
||||||
|
long itemEdit)
|
||||||
|
: m_startValue(owner->GetItemText(itemEdit)),
|
||||||
|
m_itemEdited(itemEdit)
|
||||||
|
{
|
||||||
|
m_owner = owner;
|
||||||
|
m_text = text;
|
||||||
|
m_finished = false;
|
||||||
|
m_aboutToFinish = false;
|
||||||
|
|
||||||
|
wxRect rectLabel;
|
||||||
|
owner->GetItemRect(itemEdit, rectLabel);
|
||||||
|
|
||||||
|
m_text->Create(owner, wxID_ANY, m_startValue,
|
||||||
|
wxPoint(rectLabel.x+8,rectLabel.y),
|
||||||
|
wxSize(rectLabel.width,rectLabel.height));
|
||||||
|
m_text->SetFocus();
|
||||||
|
|
||||||
|
m_text->PushEventHandler(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void wxListCtrlTextCtrlWrapper::Finish()
|
||||||
|
{
|
||||||
|
if ( !m_finished )
|
||||||
|
{
|
||||||
|
m_finished = true;
|
||||||
|
|
||||||
|
m_text->RemoveEventHandler(this);
|
||||||
|
m_owner->FinishEditing(m_text);
|
||||||
|
|
||||||
|
wxPendingDelete.Append( this );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool wxListCtrlTextCtrlWrapper::AcceptChanges()
|
||||||
|
{
|
||||||
|
const wxString value = m_text->GetValue();
|
||||||
|
|
||||||
|
if ( value == m_startValue )
|
||||||
|
// nothing changed, always accept
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if ( !m_owner->OnRenameAccept(m_itemEdited, value) )
|
||||||
|
// vetoed by the user
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// accepted, do rename the item
|
||||||
|
m_owner->SetItemText(m_itemEdited, value);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void wxListCtrlTextCtrlWrapper::AcceptChangesAndFinish()
|
||||||
|
{
|
||||||
|
m_aboutToFinish = true;
|
||||||
|
|
||||||
|
// Notify the owner about the changes
|
||||||
|
AcceptChanges();
|
||||||
|
|
||||||
|
// Even if vetoed, close the control (consistent with MSW)
|
||||||
|
Finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
void wxListCtrlTextCtrlWrapper::OnChar( wxKeyEvent &event )
|
||||||
|
{
|
||||||
|
switch ( event.m_keyCode )
|
||||||
|
{
|
||||||
|
case WXK_RETURN:
|
||||||
|
AcceptChangesAndFinish();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case WXK_ESCAPE:
|
||||||
|
m_owner->OnRenameCancelled( m_itemEdited );
|
||||||
|
Finish();
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
event.Skip();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void wxListCtrlTextCtrlWrapper::OnKeyUp( wxKeyEvent &event )
|
||||||
|
{
|
||||||
|
if (m_finished)
|
||||||
|
{
|
||||||
|
event.Skip();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// auto-grow the textctrl:
|
||||||
|
wxSize parentSize = m_owner->GetSize();
|
||||||
|
wxPoint myPos = m_text->GetPosition();
|
||||||
|
wxSize mySize = m_text->GetSize();
|
||||||
|
int sx, sy;
|
||||||
|
m_text->GetTextExtent(m_text->GetValue() + _T("MM"), &sx, &sy);
|
||||||
|
if (myPos.x + sx > parentSize.x)
|
||||||
|
sx = parentSize.x - myPos.x;
|
||||||
|
if (mySize.x > sx)
|
||||||
|
sx = mySize.x;
|
||||||
|
m_text->SetSize(sx, wxDefaultCoord);
|
||||||
|
|
||||||
|
event.Skip();
|
||||||
|
}
|
||||||
|
|
||||||
|
void wxListCtrlTextCtrlWrapper::OnKillFocus( wxFocusEvent &event )
|
||||||
|
{
|
||||||
|
if ( !m_finished && !m_aboutToFinish )
|
||||||
|
{
|
||||||
|
if ( !AcceptChanges() )
|
||||||
|
m_owner->OnRenameCancelled( m_itemEdited );
|
||||||
|
|
||||||
|
Finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
// We must let the native text control handle focus
|
||||||
|
event.Skip();
|
||||||
|
}
|
||||||
|
|
||||||
|
BEGIN_EVENT_TABLE(wxListCtrl, wxControl)
|
||||||
|
EVT_LEFT_DOWN(wxListCtrl::OnLeftDown)
|
||||||
|
EVT_LEFT_DCLICK(wxListCtrl::OnDblClick)
|
||||||
|
END_EVENT_TABLE()
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// implementation
|
// implementation
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
@@ -336,6 +534,9 @@ void wxListCtrl::Init()
|
|||||||
m_colsInfo = wxColumnList();
|
m_colsInfo = wxColumnList();
|
||||||
m_textColor = wxNullColour;
|
m_textColor = wxNullColour;
|
||||||
m_bgColor = wxNullColour;
|
m_bgColor = wxNullColour;
|
||||||
|
m_textctrlWrapper = NULL;
|
||||||
|
m_current = -1;
|
||||||
|
m_renameTimer = new wxListCtrlRenameTimer( this );
|
||||||
}
|
}
|
||||||
|
|
||||||
class wxGenericListCtrlHook : public wxGenericListCtrl
|
class wxGenericListCtrlHook : public wxGenericListCtrl
|
||||||
@@ -378,6 +579,35 @@ protected:
|
|||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
void wxListCtrl::OnLeftDown(wxMouseEvent& event)
|
||||||
|
{
|
||||||
|
if ( m_textctrlWrapper )
|
||||||
|
{
|
||||||
|
m_current = -1;
|
||||||
|
m_textctrlWrapper->AcceptChangesAndFinish();
|
||||||
|
}
|
||||||
|
|
||||||
|
int hitResult;
|
||||||
|
long current = HitTest(event.GetPosition(), hitResult);
|
||||||
|
if ((current == m_current) &&
|
||||||
|
(hitResult == wxLIST_HITTEST_ONITEM) &&
|
||||||
|
HasFlag(wxLC_EDIT_LABELS) )
|
||||||
|
{
|
||||||
|
m_renameTimer->Start( 100, true );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_current = current;
|
||||||
|
}
|
||||||
|
event.Skip();
|
||||||
|
}
|
||||||
|
|
||||||
|
void wxListCtrl::OnDblClick(wxMouseEvent& event)
|
||||||
|
{
|
||||||
|
m_current = -1;
|
||||||
|
event.Skip();
|
||||||
|
}
|
||||||
|
|
||||||
bool wxListCtrl::Create(wxWindow *parent,
|
bool wxListCtrl::Create(wxWindow *parent,
|
||||||
wxWindowID id,
|
wxWindowID id,
|
||||||
const wxPoint& pos,
|
const wxPoint& pos,
|
||||||
@@ -393,7 +623,7 @@ bool wxListCtrl::Create(wxWindow *parent,
|
|||||||
// Also, use generic list control in VIRTUAL mode.
|
// Also, use generic list control in VIRTUAL mode.
|
||||||
if ( (wxSystemOptions::HasOption( wxMAC_ALWAYS_USE_GENERIC_LISTCTRL )
|
if ( (wxSystemOptions::HasOption( wxMAC_ALWAYS_USE_GENERIC_LISTCTRL )
|
||||||
&& (wxSystemOptions::GetOptionInt( wxMAC_ALWAYS_USE_GENERIC_LISTCTRL ) == 1)) ||
|
&& (wxSystemOptions::GetOptionInt( wxMAC_ALWAYS_USE_GENERIC_LISTCTRL ) == 1)) ||
|
||||||
(style & wxLC_ICON) || (style & wxLC_SMALL_ICON) || (style & wxLC_LIST) || (style & wxLC_EDIT_LABELS) )
|
(style & wxLC_ICON) || (style & wxLC_SMALL_ICON) || (style & wxLC_LIST) )
|
||||||
{
|
{
|
||||||
m_macIsUserPane = true;
|
m_macIsUserPane = true;
|
||||||
|
|
||||||
@@ -436,6 +666,8 @@ wxListCtrl::~wxListCtrl()
|
|||||||
delete m_imageListSmall;
|
delete m_imageListSmall;
|
||||||
if (m_ownsImageListState)
|
if (m_ownsImageListState)
|
||||||
delete m_imageListState;
|
delete m_imageListState;
|
||||||
|
|
||||||
|
delete m_renameTimer;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
@@ -943,8 +1175,9 @@ bool wxListCtrl::GetItemRect(long item, wxRect& rect, int code) const
|
|||||||
|
|
||||||
rect.x = bounds.left;
|
rect.x = bounds.left;
|
||||||
rect.y = bounds.top;
|
rect.y = bounds.top;
|
||||||
rect.width = GetClientSize().x; // we need the width of the whole row, not just the item.
|
rect.width = bounds.right - bounds.left; //GetClientSize().x; // we need the width of the whole row, not just the item.
|
||||||
rect.height = bounds.bottom - bounds.top;
|
rect.height = bounds.bottom - bounds.top;
|
||||||
|
//fprintf("id = %d, bounds = %d, %d, %d, %d\n", id, rect.x, rect.y, rect.width, rect.height);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -1333,8 +1566,29 @@ wxTextCtrl* wxListCtrl::EditLabel(long item, wxClassInfo* textControlClass)
|
|||||||
|
|
||||||
if (m_dbImpl)
|
if (m_dbImpl)
|
||||||
{
|
{
|
||||||
wxMacDataItem* id = m_dbImpl->GetItemFromLine(item);
|
wxCHECK_MSG( (item >= 0) && ((long)item < GetItemCount()), NULL,
|
||||||
verify_noerr( SetDataBrowserEditItem(m_dbImpl->GetControlRef(), (DataBrowserItemID)id, kMinColumnId) );
|
wxT("wrong index in wxListCtrl::EditLabel()") );
|
||||||
|
|
||||||
|
wxASSERT_MSG( textControlClass->IsKindOf(CLASSINFO(wxTextCtrl)),
|
||||||
|
wxT("EditLabel() needs a text control") );
|
||||||
|
|
||||||
|
long itemEdit = (long)item;
|
||||||
|
|
||||||
|
wxListEvent le( wxEVT_COMMAND_LIST_BEGIN_LABEL_EDIT, GetParent()->GetId() );
|
||||||
|
le.SetEventObject( this );
|
||||||
|
le.m_itemIndex = item;
|
||||||
|
le.m_col = 0;
|
||||||
|
GetItem( le.m_item );
|
||||||
|
|
||||||
|
if ( GetParent()->GetEventHandler()->ProcessEvent( le ) && !le.IsAllowed() )
|
||||||
|
{
|
||||||
|
// vetoed by user code
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
wxTextCtrl * const text = (wxTextCtrl *)textControlClass->CreateObject();
|
||||||
|
m_textctrlWrapper = new wxListCtrlTextCtrlWrapper(this, text, item);
|
||||||
|
return m_textctrlWrapper->GetText();
|
||||||
}
|
}
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
@@ -1421,6 +1675,11 @@ wxListCtrl::HitTest(const wxPoint& point, int& flags, long *ptrSubItem) const
|
|||||||
m_dbImpl->GetDefaultRowHeight(&rowHeight);
|
m_dbImpl->GetDefaultRowHeight(&rowHeight);
|
||||||
|
|
||||||
int y = point.y;
|
int y = point.y;
|
||||||
|
// get the actual row by taking scroll position into account
|
||||||
|
UInt32 offsetX, offsetY;
|
||||||
|
m_dbImpl->GetScrollPosition( &offsetY, &offsetX );
|
||||||
|
y += offsetY;
|
||||||
|
|
||||||
if ( !(GetWindowStyleFlag() & wxLC_NO_HEADER) )
|
if ( !(GetWindowStyleFlag() & wxLC_NO_HEADER) )
|
||||||
y -= colHeaderHeight;
|
y -= colHeaderHeight;
|
||||||
|
|
||||||
@@ -1619,6 +1878,39 @@ bool wxListCtrl::SortItems(wxListCtrlCompare fn, long data)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void wxListCtrl::OnRenameTimer()
|
||||||
|
{
|
||||||
|
wxCHECK_RET( HasCurrent(), wxT("unexpected rename timer") );
|
||||||
|
|
||||||
|
EditLabel( m_current );
|
||||||
|
}
|
||||||
|
|
||||||
|
bool wxListCtrl::OnRenameAccept(long itemEdit, const wxString& value)
|
||||||
|
{
|
||||||
|
wxListEvent le( wxEVT_COMMAND_LIST_END_LABEL_EDIT, GetId() );
|
||||||
|
le.SetEventObject( this );
|
||||||
|
le.m_itemIndex = itemEdit;
|
||||||
|
|
||||||
|
GetItem( le.m_item );
|
||||||
|
le.m_item.m_text = value;
|
||||||
|
return !GetEventHandler()->ProcessEvent( le ) ||
|
||||||
|
le.IsAllowed();
|
||||||
|
}
|
||||||
|
|
||||||
|
void wxListCtrl::OnRenameCancelled(long itemEdit)
|
||||||
|
{
|
||||||
|
// let owner know that the edit was cancelled
|
||||||
|
wxListEvent le( wxEVT_COMMAND_LIST_END_LABEL_EDIT, GetParent()->GetId() );
|
||||||
|
|
||||||
|
le.SetEditCanceled(true);
|
||||||
|
|
||||||
|
le.SetEventObject( this );
|
||||||
|
le.m_itemIndex = itemEdit;
|
||||||
|
|
||||||
|
GetItem( le.m_item );
|
||||||
|
GetEventHandler()->ProcessEvent( le );
|
||||||
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
// virtual list controls
|
// virtual list controls
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
Reference in New Issue
Block a user