of wxDataViewCtrl. Added in-place editing of strings (no user vetoing and such). The code uses the corrected version of the respective code from wxListCtrl. git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@38452 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
1188 lines
31 KiB
C++
1188 lines
31 KiB
C++
/////////////////////////////////////////////////////////////////////////////
|
|
// Name: src/generic/datavgen.cpp
|
|
// Purpose: wxDataViewCtrl generic implementation
|
|
// Author: Robert Roebling
|
|
// Id: $Id$
|
|
// Copyright: (c) 1998 Robert Roebling
|
|
// Licence: wxWindows licence
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
// For compilers that support precompilation, includes "wx.h".
|
|
#include "wx/wxprec.h"
|
|
|
|
#if wxUSE_DATAVIEWCTRL
|
|
|
|
#include "wx/dataview.h"
|
|
|
|
#ifdef wxUSE_GENERICDATAVIEWCTRL
|
|
|
|
#ifndef WX_PRECOMP
|
|
#include "wx/sizer.h"
|
|
#include "wx/log.h"
|
|
#endif
|
|
|
|
#include "wx/stockitem.h"
|
|
#include "wx/dcclient.h"
|
|
#include "wx/calctrl.h"
|
|
#include "wx/popupwin.h"
|
|
#include "wx/renderer.h"
|
|
#include "wx/timer.h"
|
|
|
|
#ifdef __WXMSW__
|
|
#include "wx/msw/wrapwin.h"
|
|
#endif
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// classes
|
|
//-----------------------------------------------------------------------------
|
|
|
|
class wxDataViewCtrl;
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// wxDataViewHeaderWindow
|
|
//-----------------------------------------------------------------------------
|
|
|
|
class wxDataViewHeaderWindow: public wxWindow
|
|
{
|
|
public:
|
|
wxDataViewHeaderWindow( wxDataViewCtrl *parent,
|
|
wxWindowID id,
|
|
const wxPoint &pos = wxDefaultPosition,
|
|
const wxSize &size = wxDefaultSize,
|
|
const wxString &name = wxT("wxdataviewctrlheaderwindow") );
|
|
~wxDataViewHeaderWindow();
|
|
|
|
void SetOwner( wxDataViewCtrl* owner ) { m_owner = owner; }
|
|
wxDataViewCtrl *GetOwner() { return m_owner; }
|
|
|
|
void OnPaint( wxPaintEvent &event );
|
|
void OnMouse( wxMouseEvent &event );
|
|
void OnSetFocus( wxFocusEvent &event );
|
|
|
|
private:
|
|
wxDataViewCtrl *m_owner;
|
|
wxCursor *m_resizeCursor;
|
|
|
|
private:
|
|
DECLARE_DYNAMIC_CLASS(wxDataViewHeaderWindow)
|
|
DECLARE_EVENT_TABLE()
|
|
};
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// wxDataViewRenameTimer
|
|
//-----------------------------------------------------------------------------
|
|
|
|
class wxDataViewRenameTimer: public wxTimer
|
|
{
|
|
private:
|
|
wxDataViewMainWindow *m_owner;
|
|
|
|
public:
|
|
wxDataViewRenameTimer( wxDataViewMainWindow *owner );
|
|
void Notify();
|
|
};
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// wxDataViewTextCtrlWrapper: wraps a wxTextCtrl for inline editing
|
|
//-----------------------------------------------------------------------------
|
|
|
|
class wxDataViewTextCtrlWrapper : public wxEvtHandler
|
|
{
|
|
public:
|
|
// NB: text must be a valid object but not Create()d yet
|
|
wxDataViewTextCtrlWrapper( wxDataViewMainWindow *owner,
|
|
wxTextCtrl *text,
|
|
wxDataViewListModel *model,
|
|
size_t col, size_t row,
|
|
wxRect cellLabel );
|
|
|
|
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:
|
|
wxDataViewMainWindow *m_owner;
|
|
wxTextCtrl *m_text;
|
|
wxString m_startValue;
|
|
wxDataViewListModel *m_model;
|
|
size_t m_col;
|
|
size_t m_row;
|
|
bool m_finished;
|
|
bool m_aboutToFinish;
|
|
|
|
DECLARE_EVENT_TABLE()
|
|
};
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// wxDataViewMainWindow
|
|
//-----------------------------------------------------------------------------
|
|
|
|
class wxDataViewMainWindow: public wxWindow
|
|
{
|
|
public:
|
|
wxDataViewMainWindow( wxDataViewCtrl *parent,
|
|
wxWindowID id,
|
|
const wxPoint &pos = wxDefaultPosition,
|
|
const wxSize &size = wxDefaultSize,
|
|
const wxString &name = wxT("wxdataviewctrlmainwindow") );
|
|
~wxDataViewMainWindow();
|
|
|
|
// notifications from wxDataViewListModel
|
|
bool RowAppended();
|
|
bool RowPrepended();
|
|
bool RowInserted( size_t before );
|
|
bool RowDeleted( size_t row );
|
|
bool RowChanged( size_t row );
|
|
bool ValueChanged( size_t col, size_t row );
|
|
bool RowsReordered( size_t *new_order );
|
|
bool Cleared();
|
|
|
|
void SetOwner( wxDataViewCtrl* owner ) { m_owner = owner; }
|
|
wxDataViewCtrl *GetOwner() { return m_owner; }
|
|
|
|
void OnPaint( wxPaintEvent &event );
|
|
void OnMouse( wxMouseEvent &event );
|
|
void OnSetFocus( wxFocusEvent &event );
|
|
|
|
void UpdateDisplay();
|
|
void RecalculateDisplay();
|
|
void OnInternalIdle();
|
|
|
|
void OnRenameTimer();
|
|
void FinishEditing( wxTextCtrl *text );
|
|
|
|
void ScrollWindow( int dx, int dy, const wxRect *rect );
|
|
private:
|
|
wxDataViewCtrl *m_owner;
|
|
int m_lineHeight;
|
|
bool m_dirty;
|
|
|
|
wxDataViewColumn *m_currentCol;
|
|
size_t m_currentRow;
|
|
|
|
wxDataViewRenameTimer *m_renameTimer;
|
|
wxDataViewTextCtrlWrapper *m_textctrlWrapper;
|
|
bool m_lastOnSame;
|
|
|
|
private:
|
|
DECLARE_DYNAMIC_CLASS(wxDataViewMainWindow)
|
|
DECLARE_EVENT_TABLE()
|
|
};
|
|
|
|
// ---------------------------------------------------------
|
|
// wxGenericDataViewListModelNotifier
|
|
// ---------------------------------------------------------
|
|
|
|
class wxGenericDataViewListModelNotifier: public wxDataViewListModelNotifier
|
|
{
|
|
public:
|
|
wxGenericDataViewListModelNotifier( wxDataViewMainWindow *mainWindow )
|
|
{ m_mainWindow = mainWindow; }
|
|
|
|
virtual bool RowAppended()
|
|
{ return m_mainWindow->RowAppended(); }
|
|
virtual bool RowPrepended()
|
|
{ return m_mainWindow->RowPrepended(); }
|
|
virtual bool RowInserted( size_t before )
|
|
{ return m_mainWindow->RowInserted( before ); }
|
|
virtual bool RowDeleted( size_t row )
|
|
{ return m_mainWindow->RowDeleted( row ); }
|
|
virtual bool RowChanged( size_t row )
|
|
{ return m_mainWindow->RowChanged( row ); }
|
|
virtual bool ValueChanged( size_t col, size_t row )
|
|
{ return m_mainWindow->ValueChanged( col, row ); }
|
|
virtual bool RowsReordered( size_t *new_order )
|
|
{ return m_mainWindow->RowsReordered( new_order ); }
|
|
virtual bool Cleared()
|
|
{ return m_mainWindow->Cleared(); }
|
|
|
|
wxDataViewMainWindow *m_mainWindow;
|
|
};
|
|
|
|
// ---------------------------------------------------------
|
|
// wxDataViewCell
|
|
// ---------------------------------------------------------
|
|
|
|
IMPLEMENT_ABSTRACT_CLASS(wxDataViewCell, wxDataViewCellBase)
|
|
|
|
wxDataViewCell::wxDataViewCell( const wxString &varianttype, wxDataViewCellMode mode ) :
|
|
wxDataViewCellBase( varianttype, mode )
|
|
{
|
|
m_dc = NULL;
|
|
}
|
|
|
|
wxDataViewCell::~wxDataViewCell()
|
|
{
|
|
if (m_dc)
|
|
delete m_dc;
|
|
}
|
|
|
|
wxDC *wxDataViewCell::GetDC()
|
|
{
|
|
if (m_dc == NULL)
|
|
{
|
|
if (GetOwner() == NULL)
|
|
return NULL;
|
|
if (GetOwner()->GetOwner() == NULL)
|
|
return NULL;
|
|
m_dc = new wxClientDC( GetOwner()->GetOwner() );
|
|
}
|
|
|
|
return m_dc;
|
|
}
|
|
|
|
// ---------------------------------------------------------
|
|
// wxDataViewCustomCell
|
|
// ---------------------------------------------------------
|
|
|
|
IMPLEMENT_ABSTRACT_CLASS(wxDataViewCustomCell, wxDataViewCell)
|
|
|
|
wxDataViewCustomCell::wxDataViewCustomCell( const wxString &varianttype,
|
|
wxDataViewCellMode mode ) :
|
|
wxDataViewCell( varianttype, mode )
|
|
{
|
|
}
|
|
|
|
// ---------------------------------------------------------
|
|
// wxDataViewTextCell
|
|
// ---------------------------------------------------------
|
|
|
|
IMPLEMENT_ABSTRACT_CLASS(wxDataViewTextCell, wxDataViewCustomCell)
|
|
|
|
wxDataViewTextCell::wxDataViewTextCell( const wxString &varianttype, wxDataViewCellMode mode ) :
|
|
wxDataViewCustomCell( varianttype, mode )
|
|
{
|
|
}
|
|
|
|
bool wxDataViewTextCell::SetValue( const wxVariant &value )
|
|
{
|
|
m_text = value.GetString();
|
|
|
|
return true;
|
|
}
|
|
|
|
bool wxDataViewTextCell::GetValue( wxVariant& WXUNUSED(value) )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bool wxDataViewTextCell::Render( wxRect cell, wxDC *dc, int WXUNUSED(state) )
|
|
{
|
|
dc->DrawText( m_text, cell.x, cell.y );
|
|
|
|
return true;
|
|
}
|
|
|
|
wxSize wxDataViewTextCell::GetSize()
|
|
{
|
|
return wxSize(80,20);
|
|
}
|
|
|
|
// ---------------------------------------------------------
|
|
// wxDataViewToggleCell
|
|
// ---------------------------------------------------------
|
|
|
|
IMPLEMENT_ABSTRACT_CLASS(wxDataViewToggleCell, wxDataViewCustomCell)
|
|
|
|
wxDataViewToggleCell::wxDataViewToggleCell( const wxString &varianttype,
|
|
wxDataViewCellMode mode ) :
|
|
wxDataViewCustomCell( varianttype, mode )
|
|
{
|
|
m_toggle = false;
|
|
}
|
|
|
|
bool wxDataViewToggleCell::SetValue( const wxVariant &value )
|
|
{
|
|
m_toggle = value.GetBool();
|
|
|
|
return true;;
|
|
}
|
|
|
|
bool wxDataViewToggleCell::GetValue( wxVariant &WXUNUSED(value) )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bool wxDataViewToggleCell::Render( wxRect cell, wxDC *dc, int WXUNUSED(state) )
|
|
{
|
|
// User wxRenderer here
|
|
|
|
if (GetMode() == wxDATAVIEW_CELL_ACTIVATABLE)
|
|
dc->SetPen( *wxBLACK_PEN );
|
|
else
|
|
dc->SetPen( *wxGREY_PEN );
|
|
dc->SetBrush( *wxTRANSPARENT_BRUSH );
|
|
wxRect rect;
|
|
rect.x = cell.x + cell.width/2 - 10;
|
|
rect.width = 20;
|
|
rect.y = cell.y + cell.height/2 - 10;
|
|
rect.height = 20;
|
|
dc->DrawRectangle( rect );
|
|
if (m_toggle)
|
|
{
|
|
rect.x += 2;
|
|
rect.y += 2;
|
|
rect.width -= 4;
|
|
rect.height -= 4;
|
|
dc->DrawLine( rect.x, rect.y, rect.x+rect.width, rect.y+rect.height );
|
|
dc->DrawLine( rect.x+rect.width, rect.y, rect.x, rect.y+rect.height );
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool wxDataViewToggleCell::Activate( wxRect WXUNUSED(cell), wxDataViewListModel *model, size_t col, size_t row )
|
|
{
|
|
bool value = !m_toggle;
|
|
wxVariant variant = value;
|
|
model->SetValue( variant, col, row );
|
|
model->ValueChanged( col, row );
|
|
return true;
|
|
}
|
|
|
|
wxSize wxDataViewToggleCell::GetSize()
|
|
{
|
|
return wxSize(20,20);
|
|
}
|
|
|
|
// ---------------------------------------------------------
|
|
// wxDataViewProgressCell
|
|
// ---------------------------------------------------------
|
|
|
|
IMPLEMENT_ABSTRACT_CLASS(wxDataViewProgressCell, wxDataViewCustomCell)
|
|
|
|
wxDataViewProgressCell::wxDataViewProgressCell( const wxString &label,
|
|
const wxString &varianttype, wxDataViewCellMode mode ) :
|
|
wxDataViewCustomCell( varianttype, mode )
|
|
{
|
|
m_label = label;
|
|
m_value = 0;
|
|
}
|
|
|
|
wxDataViewProgressCell::~wxDataViewProgressCell()
|
|
{
|
|
}
|
|
|
|
bool wxDataViewProgressCell::SetValue( const wxVariant &value )
|
|
{
|
|
m_value = (long) value;
|
|
|
|
if (m_value < 0) m_value = 0;
|
|
if (m_value > 100) m_value = 100;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool wxDataViewProgressCell::Render( wxRect cell, wxDC *dc, int WXUNUSED(state) )
|
|
{
|
|
double pct = (double)m_value / 100.0;
|
|
wxRect bar = cell;
|
|
bar.width = (int)(cell.width * pct);
|
|
dc->SetPen( *wxTRANSPARENT_PEN );
|
|
dc->SetBrush( *wxBLUE_BRUSH );
|
|
dc->DrawRectangle( bar );
|
|
|
|
dc->SetBrush( *wxTRANSPARENT_BRUSH );
|
|
dc->SetPen( *wxBLACK_PEN );
|
|
dc->DrawRectangle( cell );
|
|
|
|
return true;
|
|
}
|
|
|
|
wxSize wxDataViewProgressCell::GetSize()
|
|
{
|
|
return wxSize(40,12);
|
|
}
|
|
|
|
// ---------------------------------------------------------
|
|
// wxDataViewDateCell
|
|
// ---------------------------------------------------------
|
|
|
|
class wxDataViewDateCellPopupTransient: public wxPopupTransientWindow
|
|
{
|
|
public:
|
|
wxDataViewDateCellPopupTransient( wxWindow* parent, wxDateTime *value,
|
|
wxDataViewListModel *model, size_t col, size_t row ) :
|
|
wxPopupTransientWindow( parent, wxBORDER_SIMPLE )
|
|
{
|
|
m_model = model;
|
|
m_col = col;
|
|
m_row = row;
|
|
m_cal = new wxCalendarCtrl( this, wxID_ANY, *value );
|
|
wxBoxSizer *sizer = new wxBoxSizer( wxHORIZONTAL );
|
|
sizer->Add( m_cal, 1, wxGROW );
|
|
SetSizer( sizer );
|
|
sizer->Fit( this );
|
|
}
|
|
|
|
virtual void OnDismiss()
|
|
{
|
|
}
|
|
|
|
void OnCalendar( wxCalendarEvent &event );
|
|
|
|
wxCalendarCtrl *m_cal;
|
|
wxDataViewListModel *m_model;
|
|
size_t m_col;
|
|
size_t m_row;
|
|
|
|
private:
|
|
DECLARE_EVENT_TABLE()
|
|
};
|
|
|
|
BEGIN_EVENT_TABLE(wxDataViewDateCellPopupTransient,wxPopupTransientWindow)
|
|
EVT_CALENDAR( wxID_ANY, wxDataViewDateCellPopupTransient::OnCalendar )
|
|
END_EVENT_TABLE()
|
|
|
|
void wxDataViewDateCellPopupTransient::OnCalendar( wxCalendarEvent &event )
|
|
{
|
|
wxDateTime date = event.GetDate();
|
|
wxVariant value = date;
|
|
m_model->SetValue( value, m_col, m_row );
|
|
m_model->ValueChanged( m_col, m_row );
|
|
DismissAndNotify();
|
|
}
|
|
|
|
IMPLEMENT_ABSTRACT_CLASS(wxDataViewDateCell, wxDataViewCustomCell)
|
|
|
|
wxDataViewDateCell::wxDataViewDateCell( const wxString &varianttype,
|
|
wxDataViewCellMode mode ) :
|
|
wxDataViewCustomCell( varianttype, mode )
|
|
{
|
|
}
|
|
|
|
bool wxDataViewDateCell::SetValue( const wxVariant &value )
|
|
{
|
|
m_date = value.GetDateTime();
|
|
|
|
return true;
|
|
}
|
|
|
|
bool wxDataViewDateCell::Render( wxRect cell, wxDC *dc, int WXUNUSED(state) )
|
|
{
|
|
dc->SetFont( GetOwner()->GetOwner()->GetFont() );
|
|
wxString tmp = m_date.FormatDate();
|
|
dc->DrawText( tmp, cell.x, cell.y );
|
|
|
|
return true;
|
|
}
|
|
|
|
wxSize wxDataViewDateCell::GetSize()
|
|
{
|
|
wxDataViewCtrl* view = GetOwner()->GetOwner();
|
|
wxString tmp = m_date.FormatDate();
|
|
wxCoord x,y,d;
|
|
view->GetTextExtent( tmp, &x, &y, &d );
|
|
return wxSize(x,y+d);
|
|
}
|
|
|
|
bool wxDataViewDateCell::Activate( wxRect WXUNUSED(cell), wxDataViewListModel *model, size_t col, size_t row )
|
|
{
|
|
wxVariant variant;
|
|
model->GetValue( variant, col, row );
|
|
wxDateTime value = variant.GetDateTime();
|
|
|
|
wxDataViewDateCellPopupTransient *popup = new wxDataViewDateCellPopupTransient(
|
|
GetOwner()->GetOwner()->GetParent(), &value, model, col, row );
|
|
wxPoint pos = wxGetMousePosition();
|
|
popup->Move( pos );
|
|
popup->Layout();
|
|
popup->Popup( popup->m_cal );
|
|
|
|
return true;
|
|
}
|
|
|
|
// ---------------------------------------------------------
|
|
// wxDataViewColumn
|
|
// ---------------------------------------------------------
|
|
|
|
IMPLEMENT_ABSTRACT_CLASS(wxDataViewColumn, wxDataViewColumnBase)
|
|
|
|
wxDataViewColumn::wxDataViewColumn( const wxString &title, wxDataViewCell *cell,
|
|
size_t model_column, int flags ) :
|
|
wxDataViewColumnBase( title, cell, model_column, flags )
|
|
{
|
|
m_width = 80;
|
|
}
|
|
|
|
wxDataViewColumn::~wxDataViewColumn()
|
|
{
|
|
}
|
|
|
|
void wxDataViewColumn::SetTitle( const wxString &title )
|
|
{
|
|
wxDataViewColumnBase::SetTitle( title );
|
|
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// wxDataViewHeaderWindow
|
|
//-----------------------------------------------------------------------------
|
|
|
|
IMPLEMENT_ABSTRACT_CLASS(wxDataViewHeaderWindow, wxWindow)
|
|
|
|
BEGIN_EVENT_TABLE(wxDataViewHeaderWindow,wxWindow)
|
|
EVT_PAINT (wxDataViewHeaderWindow::OnPaint)
|
|
EVT_MOUSE_EVENTS (wxDataViewHeaderWindow::OnMouse)
|
|
EVT_SET_FOCUS (wxDataViewHeaderWindow::OnSetFocus)
|
|
END_EVENT_TABLE()
|
|
|
|
wxDataViewHeaderWindow::wxDataViewHeaderWindow( wxDataViewCtrl *parent, wxWindowID id,
|
|
const wxPoint &pos, const wxSize &size, const wxString &name ) :
|
|
wxWindow( parent, id, pos, size, 0, name )
|
|
{
|
|
SetOwner( parent );
|
|
|
|
m_resizeCursor = new wxCursor( wxCURSOR_SIZEWE );
|
|
|
|
wxVisualAttributes attr = wxPanel::GetClassDefaultAttributes();
|
|
SetOwnForegroundColour( attr.colFg );
|
|
SetOwnBackgroundColour( attr.colBg );
|
|
if (!m_hasFont)
|
|
SetOwnFont( attr.font );
|
|
}
|
|
|
|
wxDataViewHeaderWindow::~wxDataViewHeaderWindow()
|
|
{
|
|
delete m_resizeCursor;
|
|
}
|
|
|
|
void wxDataViewHeaderWindow::OnPaint( wxPaintEvent &WXUNUSED(event) )
|
|
{
|
|
int w, h;
|
|
GetClientSize( &w, &h );
|
|
|
|
wxPaintDC dc( this );
|
|
|
|
int xpix;
|
|
m_owner->GetScrollPixelsPerUnit( &xpix, NULL );
|
|
|
|
int x;
|
|
m_owner->GetViewStart( &x, NULL );
|
|
|
|
// account for the horz scrollbar offset
|
|
dc.SetDeviceOrigin( -x * xpix, 0 );
|
|
|
|
dc.SetFont( GetFont() );
|
|
|
|
size_t cols = GetOwner()->GetNumberOfColumns();
|
|
size_t i;
|
|
int xpos = 0;
|
|
for (i = 0; i < cols; i++)
|
|
{
|
|
wxDataViewColumn *col = GetOwner()->GetColumn( i );
|
|
int width = col->GetWidth();
|
|
|
|
// the width of the rect to draw: make it smaller to fit entirely
|
|
// inside the column rect
|
|
#ifdef __WXMAC__
|
|
int cw = width;
|
|
int ch = h;
|
|
#else
|
|
int cw = width - 2;
|
|
int ch = h - 2;
|
|
#endif
|
|
|
|
wxRendererNative::Get().DrawHeaderButton
|
|
(
|
|
this,
|
|
dc,
|
|
wxRect(xpos, 0, cw, ch),
|
|
m_parent->IsEnabled() ? 0
|
|
: (int)wxCONTROL_DISABLED
|
|
);
|
|
|
|
dc.DrawText( col->GetTitle(), xpos+3, 3 );
|
|
|
|
xpos += width;
|
|
}
|
|
}
|
|
|
|
void wxDataViewHeaderWindow::OnMouse( wxMouseEvent &WXUNUSED(event) )
|
|
{
|
|
}
|
|
|
|
void wxDataViewHeaderWindow::OnSetFocus( wxFocusEvent &event )
|
|
{
|
|
event.Skip();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// wxDataViewRenameTimer
|
|
//-----------------------------------------------------------------------------
|
|
|
|
wxDataViewRenameTimer::wxDataViewRenameTimer( wxDataViewMainWindow *owner )
|
|
{
|
|
m_owner = owner;
|
|
}
|
|
|
|
void wxDataViewRenameTimer::Notify()
|
|
{
|
|
m_owner->OnRenameTimer();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// wxDataViewTextCtrlWrapper: wraps a wxTextCtrl for inline editing
|
|
//-----------------------------------------------------------------------------
|
|
|
|
BEGIN_EVENT_TABLE(wxDataViewTextCtrlWrapper, wxEvtHandler)
|
|
EVT_CHAR (wxDataViewTextCtrlWrapper::OnChar)
|
|
EVT_KEY_UP (wxDataViewTextCtrlWrapper::OnKeyUp)
|
|
EVT_KILL_FOCUS (wxDataViewTextCtrlWrapper::OnKillFocus)
|
|
END_EVENT_TABLE()
|
|
|
|
wxDataViewTextCtrlWrapper::wxDataViewTextCtrlWrapper(
|
|
wxDataViewMainWindow *owner,
|
|
wxTextCtrl *text,
|
|
wxDataViewListModel *model,
|
|
size_t col, size_t row,
|
|
wxRect rectLabel )
|
|
{
|
|
m_owner = owner;
|
|
m_model = model;
|
|
m_row = row;
|
|
m_col = col;
|
|
m_text = text;
|
|
|
|
m_finished = false;
|
|
m_aboutToFinish = false;
|
|
|
|
wxVariant value;
|
|
model->GetValue( value, col, row );
|
|
m_startValue = value.GetString();
|
|
|
|
m_owner->GetOwner()->CalcScrolledPosition(
|
|
rectLabel.x, rectLabel.y, &rectLabel.x, &rectLabel.y );
|
|
|
|
m_text->Create( owner, wxID_ANY, m_startValue,
|
|
wxPoint(rectLabel.x-2,rectLabel.y-2),
|
|
wxSize(rectLabel.width+7,rectLabel.height+4) );
|
|
m_text->SetFocus();
|
|
|
|
m_text->PushEventHandler(this);
|
|
}
|
|
|
|
void wxDataViewTextCtrlWrapper::AcceptChangesAndFinish()
|
|
{
|
|
m_aboutToFinish = true;
|
|
|
|
// Notify the owner about the changes
|
|
AcceptChanges();
|
|
|
|
// Even if vetoed, close the control (consistent with MSW)
|
|
Finish();
|
|
}
|
|
|
|
void wxDataViewTextCtrlWrapper::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 wxDataViewTextCtrlWrapper::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 wxDataViewTextCtrlWrapper::OnKillFocus( wxFocusEvent &event )
|
|
{
|
|
if ( !m_finished && !m_aboutToFinish )
|
|
{
|
|
AcceptChanges();
|
|
//if ( !AcceptChanges() )
|
|
// m_owner->OnRenameCancelled( m_itemEdited );
|
|
|
|
Finish();
|
|
}
|
|
|
|
// We must let the native text control handle focus
|
|
event.Skip();
|
|
}
|
|
|
|
bool wxDataViewTextCtrlWrapper::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
|
|
wxVariant variant;
|
|
variant = value;
|
|
m_model->SetValue( variant, m_col, m_row );
|
|
m_model->ValueChanged( m_col, m_row );
|
|
|
|
return true;
|
|
}
|
|
|
|
void wxDataViewTextCtrlWrapper::Finish()
|
|
{
|
|
if ( !m_finished )
|
|
{
|
|
m_finished = true;
|
|
|
|
m_text->RemoveEventHandler(this);
|
|
m_owner->FinishEditing(m_text);
|
|
|
|
// delete later
|
|
wxPendingDelete.Append( this );
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// wxDataViewMainWindow
|
|
//-----------------------------------------------------------------------------
|
|
|
|
IMPLEMENT_ABSTRACT_CLASS(wxDataViewMainWindow, wxWindow)
|
|
|
|
BEGIN_EVENT_TABLE(wxDataViewMainWindow,wxWindow)
|
|
EVT_PAINT (wxDataViewMainWindow::OnPaint)
|
|
EVT_MOUSE_EVENTS (wxDataViewMainWindow::OnMouse)
|
|
EVT_SET_FOCUS (wxDataViewMainWindow::OnSetFocus)
|
|
END_EVENT_TABLE()
|
|
|
|
wxDataViewMainWindow::wxDataViewMainWindow( wxDataViewCtrl *parent, wxWindowID id,
|
|
const wxPoint &pos, const wxSize &size, const wxString &name ) :
|
|
wxWindow( parent, id, pos, size, 0, name )
|
|
{
|
|
SetOwner( parent );
|
|
|
|
m_lastOnSame = false;
|
|
m_renameTimer = new wxDataViewRenameTimer( this );
|
|
m_textctrlWrapper = NULL;
|
|
|
|
// TODO: user better initial values/nothing selected
|
|
m_currentCol = NULL;
|
|
m_currentRow = 0;
|
|
|
|
// TODO: we need to calculate this smartly
|
|
m_lineHeight = 20;
|
|
|
|
UpdateDisplay();
|
|
}
|
|
|
|
wxDataViewMainWindow::~wxDataViewMainWindow()
|
|
{
|
|
delete m_renameTimer;
|
|
}
|
|
|
|
void wxDataViewMainWindow::OnRenameTimer()
|
|
{
|
|
// We have to call this here because changes may just have
|
|
// been made and no screen update taken place.
|
|
if ( m_dirty )
|
|
wxSafeYield();
|
|
|
|
|
|
int xpos = 0;
|
|
size_t cols = GetOwner()->GetNumberOfColumns();
|
|
size_t i;
|
|
for (i = 0; i < cols; i++)
|
|
{
|
|
wxDataViewColumn *c = GetOwner()->GetColumn( i );
|
|
if (c == m_currentCol)
|
|
break;
|
|
xpos += c->GetWidth();
|
|
}
|
|
wxRect labelRect( xpos, m_currentRow * m_lineHeight, m_currentCol->GetWidth(), m_lineHeight );
|
|
|
|
wxClassInfo *textControlClass = CLASSINFO(wxTextCtrl);
|
|
|
|
wxTextCtrl * const text = (wxTextCtrl *)textControlClass->CreateObject();
|
|
m_textctrlWrapper = new wxDataViewTextCtrlWrapper(this, text, GetOwner()->GetModel(),
|
|
m_currentCol->GetModelColumn(), m_currentRow, labelRect );
|
|
}
|
|
|
|
void wxDataViewMainWindow::FinishEditing( wxTextCtrl *text )
|
|
{
|
|
delete text;
|
|
m_textctrlWrapper = NULL;
|
|
SetFocus();
|
|
// SetFocusIgnoringChildren();
|
|
}
|
|
|
|
bool wxDataViewMainWindow::RowAppended()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bool wxDataViewMainWindow::RowPrepended()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bool wxDataViewMainWindow::RowInserted( size_t WXUNUSED(before) )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bool wxDataViewMainWindow::RowDeleted( size_t WXUNUSED(row) )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bool wxDataViewMainWindow::RowChanged( size_t WXUNUSED(row) )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bool wxDataViewMainWindow::ValueChanged( size_t WXUNUSED(col), size_t row )
|
|
{
|
|
wxRect rect( 0, row*m_lineHeight, 10000, m_lineHeight );
|
|
m_owner->CalcScrolledPosition( rect.x, rect.y, &rect.x, &rect.y );
|
|
Refresh( true, &rect );
|
|
|
|
return true;
|
|
}
|
|
|
|
bool wxDataViewMainWindow::RowsReordered( size_t *WXUNUSED(new_order) )
|
|
{
|
|
Refresh();
|
|
|
|
return true;
|
|
}
|
|
|
|
bool wxDataViewMainWindow::Cleared()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
void wxDataViewMainWindow::UpdateDisplay()
|
|
{
|
|
m_dirty = true;
|
|
}
|
|
|
|
void wxDataViewMainWindow::OnInternalIdle()
|
|
{
|
|
wxWindow::OnInternalIdle();
|
|
|
|
if (m_dirty)
|
|
{
|
|
RecalculateDisplay();
|
|
m_dirty = false;
|
|
}
|
|
}
|
|
|
|
void wxDataViewMainWindow::RecalculateDisplay()
|
|
{
|
|
wxDataViewListModel *model = GetOwner()->GetModel();
|
|
if (!model)
|
|
{
|
|
Refresh();
|
|
return;
|
|
}
|
|
|
|
int width = 0;
|
|
size_t cols = GetOwner()->GetNumberOfColumns();
|
|
size_t i;
|
|
for (i = 0; i < cols; i++)
|
|
{
|
|
wxDataViewColumn *col = GetOwner()->GetColumn( i );
|
|
width += col->GetWidth();
|
|
}
|
|
|
|
int height = model->GetNumberOfRows() * m_lineHeight;
|
|
|
|
SetVirtualSize( width, height );
|
|
GetOwner()->SetScrollRate( 10, m_lineHeight );
|
|
|
|
Refresh();
|
|
}
|
|
|
|
void wxDataViewMainWindow::ScrollWindow( int dx, int dy, const wxRect *rect )
|
|
{
|
|
wxWindow::ScrollWindow( dx, dy, rect );
|
|
GetOwner()->m_headerArea->ScrollWindow( dx, 0 );
|
|
}
|
|
|
|
void wxDataViewMainWindow::OnPaint( wxPaintEvent &WXUNUSED(event) )
|
|
{
|
|
wxPaintDC dc( this );
|
|
|
|
GetOwner()->PrepareDC( dc );
|
|
|
|
dc.SetFont( GetFont() );
|
|
|
|
wxRect update = GetUpdateRegion().GetBox();
|
|
m_owner->CalcUnscrolledPosition( update.x, update.y, &update.x, &update.y );
|
|
|
|
wxDataViewListModel *model = GetOwner()->GetModel();
|
|
|
|
size_t item_start = wxMax( 0, update.y / m_lineHeight );
|
|
size_t item_count = wxMin( (update.height / m_lineHeight) + 1,
|
|
(int)(model->GetNumberOfRows()-item_start) );
|
|
|
|
wxRect cell_rect;
|
|
cell_rect.x = 0;
|
|
cell_rect.height = m_lineHeight;
|
|
size_t cols = GetOwner()->GetNumberOfColumns();
|
|
size_t i;
|
|
for (i = 0; i < cols; i++)
|
|
{
|
|
wxDataViewColumn *col = GetOwner()->GetColumn( i );
|
|
wxDataViewCell *cell = col->GetCell();
|
|
cell_rect.width = col->GetWidth();
|
|
|
|
size_t item;
|
|
for (item = item_start; item < item_start+item_count; item++)
|
|
{
|
|
cell_rect.y = item*m_lineHeight;
|
|
wxVariant value;
|
|
model->GetValue( value, col->GetModelColumn(), item );
|
|
cell->SetValue( value );
|
|
wxSize size = cell->GetSize();
|
|
// cannot be bigger than allocated space
|
|
size.x = wxMin( size.x, cell_rect.width );
|
|
size.y = wxMin( size.y, cell_rect.height );
|
|
// TODO: check for left/right/centre alignment here
|
|
wxRect item_rect;
|
|
// for now: centre
|
|
item_rect.x = cell_rect.x + (cell_rect.width / 2) - (size.x / 2);
|
|
item_rect.y = cell_rect.y + (cell_rect.height / 2) - (size.y / 2);
|
|
|
|
item_rect.width = size.x;
|
|
item_rect.height= size.y;
|
|
cell->Render( item_rect, &dc, 0 );
|
|
}
|
|
|
|
cell_rect.x += cell_rect.width;
|
|
}
|
|
}
|
|
|
|
void wxDataViewMainWindow::OnMouse( wxMouseEvent &event )
|
|
{
|
|
int x = event.GetX();
|
|
int y = event.GetY();
|
|
m_owner->CalcUnscrolledPosition( x, y, &x, &y );
|
|
|
|
wxDataViewColumn *col = NULL;
|
|
|
|
int xpos = 0;
|
|
size_t cols = GetOwner()->GetNumberOfColumns();
|
|
size_t i;
|
|
for (i = 0; i < cols; i++)
|
|
{
|
|
wxDataViewColumn *c = GetOwner()->GetColumn( i );
|
|
if (x < xpos + c->GetWidth())
|
|
{
|
|
col = c;
|
|
break;
|
|
}
|
|
xpos += c->GetWidth();
|
|
}
|
|
if (!col)
|
|
return;
|
|
wxDataViewCell *cell = col->GetCell();
|
|
|
|
size_t row = y / m_lineHeight;
|
|
|
|
wxDataViewListModel *model = GetOwner()->GetModel();
|
|
|
|
if (event.ButtonDClick())
|
|
{
|
|
m_renameTimer->Stop();
|
|
m_lastOnSame = false;
|
|
}
|
|
|
|
if (event.LeftDClick())
|
|
{
|
|
if (cell->GetMode() == wxDATAVIEW_CELL_ACTIVATABLE)
|
|
{
|
|
wxVariant value;
|
|
model->GetValue( value, col->GetModelColumn(), row );
|
|
cell->SetValue( value );
|
|
wxRect cell_rect( xpos, row * m_lineHeight, col->GetWidth(), m_lineHeight );
|
|
cell->Activate( cell_rect, model, col->GetModelColumn(), row );
|
|
}
|
|
|
|
return;
|
|
} else
|
|
if (event.LeftUp())
|
|
{
|
|
if (m_lastOnSame)
|
|
{
|
|
if ((col == m_currentCol) & (row == m_currentRow) &&
|
|
(cell->GetMode() == wxDATAVIEW_CELL_EDITABLE) )
|
|
{
|
|
m_renameTimer->Start( 100, true );
|
|
}
|
|
}
|
|
|
|
m_lastOnSame = false;
|
|
} else
|
|
if (event.LeftDown())
|
|
{
|
|
wxDataViewColumn *oldCurrentCol = m_currentCol;
|
|
size_t oldCurrentRow = m_currentRow;
|
|
|
|
// Update selection here...
|
|
m_currentCol = col;
|
|
m_currentRow = row;
|
|
|
|
m_lastOnSame = (col == oldCurrentCol) && (row == oldCurrentRow);
|
|
|
|
return;
|
|
}
|
|
|
|
event.Skip();
|
|
}
|
|
|
|
void wxDataViewMainWindow::OnSetFocus( wxFocusEvent &event )
|
|
{
|
|
event.Skip();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// wxDataViewCtrl
|
|
//-----------------------------------------------------------------------------
|
|
|
|
IMPLEMENT_DYNAMIC_CLASS(wxDataViewCtrl, wxDataViewCtrlBase)
|
|
|
|
BEGIN_EVENT_TABLE(wxDataViewCtrl, wxDataViewCtrlBase)
|
|
EVT_SIZE(wxDataViewCtrl::OnSize)
|
|
END_EVENT_TABLE()
|
|
|
|
wxDataViewCtrl::~wxDataViewCtrl()
|
|
{
|
|
if (m_notifier)
|
|
GetModel()->RemoveNotifier( m_notifier );
|
|
}
|
|
|
|
void wxDataViewCtrl::Init()
|
|
{
|
|
m_notifier = NULL;
|
|
}
|
|
|
|
bool wxDataViewCtrl::Create(wxWindow *parent, wxWindowID id,
|
|
const wxPoint& pos, const wxSize& size,
|
|
long style, const wxValidator& validator )
|
|
{
|
|
if (!wxControl::Create( parent, id, pos, size, style | wxScrolledWindowStyle|wxSUNKEN_BORDER, validator))
|
|
return false;
|
|
|
|
Init();
|
|
|
|
#ifdef __WXMAC__
|
|
MacSetClipChildren( true ) ;
|
|
#endif
|
|
|
|
m_clientArea = new wxDataViewMainWindow( this, wxID_ANY );
|
|
m_headerArea = new wxDataViewHeaderWindow( this, wxID_ANY, wxDefaultPosition, wxSize(wxDefaultCoord,25) );
|
|
|
|
SetTargetWindow( m_clientArea );
|
|
|
|
wxBoxSizer *sizer = new wxBoxSizer( wxVERTICAL );
|
|
sizer->Add( m_headerArea, 0, wxGROW );
|
|
sizer->Add( m_clientArea, 1, wxGROW );
|
|
SetSizer( sizer );
|
|
|
|
return true;
|
|
}
|
|
|
|
#ifdef __WXMSW__
|
|
WXLRESULT wxDataViewCtrl::MSWWindowProc(WXUINT nMsg,
|
|
WXWPARAM wParam,
|
|
WXLPARAM lParam)
|
|
{
|
|
WXLRESULT rc = wxDataViewCtrlBase::MSWWindowProc(nMsg, wParam, lParam);
|
|
|
|
#ifndef __WXWINCE__
|
|
// we need to process arrows ourselves for scrolling
|
|
if ( nMsg == WM_GETDLGCODE )
|
|
{
|
|
rc |= DLGC_WANTARROWS;
|
|
}
|
|
#endif
|
|
|
|
return rc;
|
|
}
|
|
#endif
|
|
|
|
void wxDataViewCtrl::OnSize( wxSizeEvent &WXUNUSED(event) )
|
|
{
|
|
// We need to override OnSize so that our scrolled
|
|
// window a) does call Layout() to use sizers for
|
|
// positioning the controls but b) does not query
|
|
// the sizer for their size and use that for setting
|
|
// the scrollable area as set that ourselves by
|
|
// calling SetScrollbar() further down.
|
|
|
|
Layout();
|
|
|
|
AdjustScrollbars();
|
|
}
|
|
|
|
bool wxDataViewCtrl::AssociateModel( wxDataViewListModel *model )
|
|
{
|
|
if (!wxDataViewCtrlBase::AssociateModel( model ))
|
|
return false;
|
|
|
|
m_notifier = new wxGenericDataViewListModelNotifier( m_clientArea );
|
|
|
|
model->AddNotifier( m_notifier );
|
|
|
|
m_clientArea->UpdateDisplay();
|
|
|
|
return true;
|
|
}
|
|
|
|
bool wxDataViewCtrl::AppendColumn( wxDataViewColumn *col )
|
|
{
|
|
if (!wxDataViewCtrlBase::AppendColumn(col))
|
|
return false;
|
|
|
|
m_clientArea->UpdateDisplay();
|
|
|
|
return true;
|
|
}
|
|
|
|
#endif
|
|
// !wxUSE_GENERICDATAVIEWCTRL
|
|
|
|
#endif
|
|
// wxUSE_DATAVIEWCTRL
|
|
|