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:
Kevin Ollivier 2006-11-04 19:54:08 +00:00
parent a4e15a8d05
commit 7ac21a6761
2 changed files with 323 additions and 4 deletions

View File

@ -16,6 +16,8 @@
class wxMacDataBrowserListCtrlControl;
class wxMacListControl;
class wxListCtrlTextCtrlWrapper;
class wxListCtrlRenameTimer;
WX_DECLARE_EXPORTED_LIST(wxListItem, wxColumnList);
@ -318,6 +320,25 @@ class WXDLLEXPORT wxListCtrl: public wxControl
virtual bool SetBackgroundColour(const wxColour& colour);
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
// unfortunately, the DataBrowser callbacks don't provide the context
// and we need it, so we need to set/remove it before and after draw
@ -333,6 +354,9 @@ protected:
virtual wxSize DoGetBestSize() const;
long m_current;
wxListCtrlTextCtrlWrapper *m_textctrlWrapper;
wxListCtrlRenameTimer *m_renameTimer;
// common part of all ctors
void Init();
@ -364,6 +388,9 @@ protected:
// keep track of inserted/deleted columns
int m_count; // for virtual lists, store item count
private:
DECLARE_EVENT_TABLE()
};
#endif

View File

@ -36,6 +36,7 @@
#include "wx/imaglist.h"
#include "wx/sysopt.h"
#include "wx/timer.h"
#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);
}
//-----------------------------------------------------------------------------
// 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
// ============================================================================
@ -336,6 +534,9 @@ void wxListCtrl::Init()
m_colsInfo = wxColumnList();
m_textColor = wxNullColour;
m_bgColor = wxNullColour;
m_textctrlWrapper = NULL;
m_current = -1;
m_renameTimer = new wxListCtrlRenameTimer( this );
}
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,
wxWindowID id,
const wxPoint& pos,
@ -393,7 +623,7 @@ bool wxListCtrl::Create(wxWindow *parent,
// Also, use generic list control in VIRTUAL mode.
if ( (wxSystemOptions::HasOption( wxMAC_ALWAYS_USE_GENERIC_LISTCTRL )
&& (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;
@ -436,6 +666,8 @@ wxListCtrl::~wxListCtrl()
delete m_imageListSmall;
if (m_ownsImageListState)
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.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;
//fprintf("id = %d, bounds = %d, %d, %d, %d\n", id, rect.x, rect.y, rect.width, rect.height);
}
return true;
}
@ -1333,8 +1566,29 @@ wxTextCtrl* wxListCtrl::EditLabel(long item, wxClassInfo* textControlClass)
if (m_dbImpl)
{
wxMacDataItem* id = m_dbImpl->GetItemFromLine(item);
verify_noerr( SetDataBrowserEditItem(m_dbImpl->GetControlRef(), (DataBrowserItemID)id, kMinColumnId) );
wxCHECK_MSG( (item >= 0) && ((long)item < GetItemCount()), NULL,
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;
}
@ -1421,6 +1675,11 @@ wxListCtrl::HitTest(const wxPoint& point, int& flags, long *ptrSubItem) const
m_dbImpl->GetDefaultRowHeight(&rowHeight);
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) )
y -= colHeaderHeight;
@ -1619,6 +1878,39 @@ bool wxListCtrl::SortItems(wxListCtrlCompare fn, long data)
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
// ----------------------------------------------------------------------------