Классы редактирования даты и времени в ячейках wxGrid

Александр (sandy) Илюшенко любезно предоставил статью о создании редактора ячеек wxGrid:

Данная статья посвящена внедрению в грид ячеек для редактирования дат и времени. Сам котрол для дат существует – wxDatePickerCtrl. Остается вопрос, как прикрутить его к гриду.

Я позаимствовал готовый контрол, за что спасибо автору, и переделал его под свои нужды, а именно, мне нужно было время в 24-часовом формате. Реализацию самого эдитора времени, который я назвал Бcode>wxTimeCtrl я приводить в статье не буду, т.к. много текста. Но обращу Ваше внимание на то, что макрос GRID_TIME_EDITOR, объявленный в wxTextCtrl.h, нужен исключительно для использования контрола в гриде, потому, если нужно будет использовать его в ином качестве, закоментируйте строку #define GRID_TIME_EDITOR.
wxGridCell.h

#pragma once
#include 
#include 
#include 
#include 
#include "wxTimeCtrl.h"

/*** Renderers ***/
/*** Date Renderer ***/
class wxGridCellDateRenderer: public wxGridCellDateTimeRenderer {
public:
    wxGridCellDateRenderer(wxString sOutFormat = wxT("%d.%m.%Y"),
                           wxString sInputFormat = wxT("%Y%m%d"));
};
/*** Time Renderer ***/
class wxGridCellTimeRenderer: public wxGridCellDateTimeRenderer {
public:
    wxGridCellTimeRenderer();
};

/*** Editors ***/
/*** Date Editor ***/
class wxGridCellDateEditor: public wxGridCellEditor {
    const wxString m_Format;
    wxString m_startValue;

    void SetDateValue();
    wxDatePickerCtrl* DateCtrl() const;
protected:
//    void OnDateChanged(wxDateEvent& event);
    DECLARE_NO_COPY_CLASS(wxGridCellDateEditor)
public:
    wxGridCellDateEditor(const wxString& format=wxT("%d.%m.%Y"));
    ~wxGridCellDateEditor();

    void Create(wxWindow* parent, wxWindowID id, wxEvtHandler* evtHandler);
    void PaintBackground(const wxRect& rectCell, wxGridCellAttr* attr);
    void SetSize(const wxRect& rectOrig);
    void BeginEdit(int row, int col, wxGrid* grid);
    bool EndEdit(int row, int col, wxGrid* grid);
    bool IsAcceptedKey(wxKeyEvent& event);
    void Reset();
    wxString GetValue() const;
    wxGridCellEditor* Clone() const;
};

/*** Time Editor ***/
class wxGridCellTimeEditor: public wxGridCellEditor {
    wxString m_startValue;

    void SetTimeValue();
    wxTimeCtrl* TimeCtrl() const;
protected:
    DECLARE_NO_COPY_CLASS(wxGridCellTimeEditor)
public:
    wxGridCellTimeEditor();
    ~wxGridCellTimeEditor();

    void Create(wxWindow* parent, wxWindowID id, wxEvtHandler* evtHandler);
    void PaintBackground(const wxRect& rectCell, wxGridCellAttr* attr);
    void SetSize(const wxRect& rectOrig);
    void BeginEdit(int row, int col, wxGrid* grid);
    bool EndEdit(int row, int col, wxGrid* grid);
    bool IsAcceptedKey(wxKeyEvent& event);
    void Reset();
    wxString GetValue() const;
    wxGridCellEditor* Clone() const;
};

Теперь несколько слов о предоставленных классах:

  • wxGridCellDateRenderer – нужен для отображения дат, в том виде, как это нужно программисту, что определяется параметрами wxString sOutFormat – это формат собственно отображения даты, например, wxT("%d.%m.%Y"), т.е. ДД.ММ.ГГГГ, и wxString sInputFormat – это формат, в котором помещается в грид строка, содержащая дату
  • wxGridCellTimeRenderer – нужен для отображения времени. Пераметров он не имеет, принимает и отображает время в формате ЧЧ:ММ:СС
  • wxGridCellDateEditor – это редактор дат, который параметром принимает формат даты
  • wxGridCellTimeEditor – это редактор времени

wxGridCell.cpp

#include "wxGridCell.h"

/*** Renderers ***/
/*** Date Renderer ***/
wxGridCellDateRenderer::wxGridCellDateRenderer(wxString sOutFormat,
                                               wxString sInputFormat)
    : wxGridCellDateTimeRenderer(sOutFormat,sInputFormat)
{
}
/*** Time Renderer ***/
wxGridCellTimeRenderer::wxGridCellTimeRenderer()
    : wxGridCellDateTimeRenderer(wxT("%H:%M:%S"),wxT("%H:%M:%S"))
{
}


/*** Editors ***/
/*** Date Editor ***/
wxGridCellDateEditor::wxGridCellDateEditor(const wxString& format)
    : wxGridCellEditor(),
    m_Format(format),
    m_startValue(wxT(""))
{
}

wxGridCellDateEditor::~wxGridCellDateEditor()
{
    if (m_control) {
        m_control->Destroy();
        m_control = NULL;
    }
}

void wxGridCellDateEditor::Create(wxWindow* parent,
                                  wxWindowID id,
                                  wxEvtHandler* evtHandler)
{
    m_control = new wxDatePickerCtrl(parent, id,
        wxDefaultDateTime, wxDefaultPosition, wxDefaultSize,
        wxDP_SPIN | wxDP_SHOWCENTURY);
    wxGridCellEditor::Create(parent, id, evtHandler);
}

wxDatePickerCtrl* wxGridCellDateEditor::DateCtrl() const
{
    return (wxDatePickerCtrl*)m_control;
}

void wxGridCellDateEditor::PaintBackground(const wxRect& rectCell,
                                           wxGridCellAttr* attr)
{
    wxGridCellEditor::PaintBackground(rectCell, attr);
}

void wxGridCellDateEditor::SetSize(const wxRect& rectOrig)
{
    wxRect rect(rectOrig);
#if defined(__WXGTK__)
    if (rect.x != 0) {
        rect.x+=1;
        rect.y+=1;
        rect.width-=1;
        rect.height-=1;
    }
#elif defined(__WXMSW__)
//    rect.x-=2;
//    rect.y-=2;
//    rect.width+=2;
    rect.height+=2;
#else
    int extra_x=(rect.x>2)?2:1;
    int extra_y=(rect.y>2)?2:1;
    #if defined(__WXMOTIF__)
        extra_x*=2;
        extra_y*=2;
    #endif
    rect.SetLeft(wxMax(0, rect.x-extra_x));
    rect.SetTop(wxMax(0, rect.y-extra_y));
    rect.SetRight(rect.GetRight()+2*extra_x);
    rect.SetBottom(rect.GetBottom()+2*extra_y);
#endif
    wxGridCellEditor::SetSize(rect);
}

void wxGridCellDateEditor::SetDateValue()
{
    wxDateTime tmp;
    if (!tmp.ParseFormat(m_startValue.c_str(),m_Format.c_str())) {
        tmp.SetToCurrent();
    }
    DateCtrl()->SetValue(tmp);
}

void wxGridCellDateEditor::BeginEdit(int row, int col, wxGrid* grid)
{
    wxASSERT_MSG(m_control, wxT("The wxGridCellEditor must be created first!"));

    m_startValue = grid->GetTable()->GetValue(row, col);
    SetDateValue();
    DateCtrl()->SetFocus();
}

bool wxGridCellDateEditor::EndEdit(int row, int col, wxGrid* grid)
{
    wxASSERT_MSG(m_control, wxT("The wxGridCellEditor must be created first!"));
    bool changed = false;
    wxDateTime tmp = DateCtrl()->GetValue();
    wxString s=tmp.Format(m_Format.c_str());

    if (s != m_startValue)
        changed = true;
    if (changed)
        grid->GetTable()->SetValue(row, col, s);

    m_startValue = wxEmptyString;
    tmp.SetToCurrent();
    DateCtrl()->SetValue(tmp);

    return changed;
}

bool wxGridCellDateEditor::IsAcceptedKey(wxKeyEvent& event)
{
    if (wxGridCellEditor::IsAcceptedKey(event)) {
        int keycode=event.GetKeyCode();
        if (keycode<128 && (wxIsdigit(keycode) || keycode=='.')) {
            return true;
        }
    }
    return false;
}

void wxGridCellDateEditor::Reset()
{
    wxASSERT_MSG(m_control, wxT("The wxGridCellEditor must be created first!"));
    SetDateValue();
}

wxString wxGridCellDateEditor::GetValue() const
{
    wxDateTime tmp = DateCtrl()->GetValue();
    wxString s=tmp.Format(m_Format.c_str());
    return s;
}

wxGridCellEditor* wxGridCellDateEditor::Clone() const
{
    return new wxGridCellDateEditor(m_Format);
}

/*** Time Editor ***/
wxGridCellTimeEditor::wxGridCellTimeEditor()
    : wxGridCellEditor(),
    m_startValue(wxT(""))
{
}

wxGridCellTimeEditor::~wxGridCellTimeEditor()
{
    if (m_control) {
        m_control->Destroy();
        m_control = NULL;
    }
}

void wxGridCellTimeEditor::Create(wxWindow* parent,
                                  wxWindowID id,
                                  wxEvtHandler* evtHandler)
{
    m_control = new wxTimeCtrl(parent, id);
    wxGridCellEditor::Create(parent, id, evtHandler);
}

void wxGridCellTimeEditor::PaintBackground(const wxRect& rectCell,
                                           wxGridCellAttr* attr)
{
    wxGridCellEditor::PaintBackground(rectCell, attr);
}

wxTimeCtrl* wxGridCellTimeEditor::TimeCtrl() const
{
    return (wxTimeCtrl*)m_control;
}

void wxGridCellTimeEditor::SetSize(const wxRect& rectOrig)
{
    wxRect rect(rectOrig);
#if defined(__WXGTK__)
    if (rect.x != 0) {
        rect.x+=1;
        rect.y+=1;
        rect.width-=1;
        rect.height-=1;
    }
#elif defined(__WXMSW__)
//    rect.x-=2;
//    rect.y-=2;
//    rect.width+=2;
    rect.height+=2;
#else
    int extra_x=(rect.x>2)?2:1;
    int extra_y=(rect.y>2)?2:1;
    #if defined(__WXMOTIF__)
        extra_x*=2;
        extra_y*=2;
    #endif
    rect.SetLeft(wxMax(0, rect.x-extra_x));
    rect.SetTop(wxMax(0, rect.y-extra_y));
    rect.SetRight(rect.GetRight()+2*extra_x);
    rect.SetBottom(rect.GetBottom()+2*extra_y);
#endif
    wxGridCellEditor::SetSize(rect);
}

void wxGridCellTimeEditor::SetTimeValue()
{
    wxString s=m_startValue;
    if (s.IsEmpty()) {
        s=wxTimeCtrl::GetCurrentTime();
    }
    TimeCtrl()->SetValue(s);
}

void wxGridCellTimeEditor::BeginEdit(int row, int col, wxGrid* grid)
{
    wxASSERT_MSG(m_control, wxT("The wxGridCellEditor must be created first!"));

    m_startValue = grid->GetTable()->GetValue(row, col);
    SetTimeValue();
    TimeCtrl()->SetFocus();
}

bool wxGridCellTimeEditor::EndEdit(int row, int col, wxGrid* grid)
{
    wxASSERT_MSG(m_control, wxT("The wxGridCellEditor must be created first!"));

    bool changed = false;
    wxString s=TimeCtrl()->GetValue();

    if (s != m_startValue)
        changed = true;
    if (changed)
        grid->GetTable()->SetValue(row, col, s);

    m_startValue = wxEmptyString;
    TimeCtrl()->SetValue(m_startValue);

    return changed;
}

bool wxGridCellTimeEditor::IsAcceptedKey(wxKeyEvent& event)
{
    if (wxGridCellEditor::IsAcceptedKey(event)) {
        int keycode=event.GetKeyCode();
        if (keycode<128 && (wxIsdigit(keycode) || keycode=='.')) {
            return true;
        }
    }
    return false;
}

void wxGridCellTimeEditor::Reset()
{
    wxASSERT_MSG(m_control, wxT("The wxGridCellEditor must be created first!"));
    SetTimeValue();
}

wxString wxGridCellTimeEditor::GetValue() const
{
    return TimeCtrl()->GetValue();
}

wxGridCellEditor* wxGridCellTimeEditor::Clone() const
{
    return new wxGridCellTimeEditor();
}

Как это все работает? Вот пример, в котором я использую wxGridCtrl из предыдущей статьи.
Example.cpp

#include "wxGridCtrl.h"
#include "wxGridCell.h"
/* ... */
    m_pGrid = new wxGridCtrl(this, IDC_CHILD_GRID);
    m_pGrid->CreateGrid(0,0);
    m_pGrid->SetRowLabelSize(20);
    m_pGrid->SetColLabelSize(20);
    m_pGrid->DisableDragRowSize();
    m_pGrid->SetDefaultCellBackgroundColour(wxColour(128,128,128));
				
    m_pGrid->AppendCols(3);

    wxGridCellAttr* pDateAttr = new wxGridCellAttr;
    pDateAttr->SetBackgroundColour(wxColour(255,255,255));
    pDateAttr->SetAlignment(wxALIGN_RIGHT,wxALIGN_CENTRE);
    pDateAttr->SetRenderer(new wxGridCellDateRenderer());
    pDateAttr->SetEditor(new wxGridCellDateEditor(wxT("%Y%m%d")));

    wxGridCellAttr* pTimeAttr = new wxGridCellAttr;
    pTimeAttr->SetBackgroundColour(wxColour(255,255,255));
    pTimeAttr->SetAlignment(wxALIGN_RIGHT,wxALIGN_CENTRE);
    pTimeAttr->SetRenderer(new wxGridCellTimeRenderer());
    pTimeAttr->SetEditor(new wxGridCellTimeEditor());

    m_pGrid->SetColLabelValue(0,wxT("DateCells"));
    m_pGrid->SetColLabelValue(1,wxT("TimeCells"));
    m_pGrid->SetColAttr(0,pDateAttr);
    m_pGrid->SetColAttr(1,pTimeAttr);
    m_pGrid->Refresh();
/* ... */    

Вот так в результате будет выглядеть наше приложение:
Классы редактирования даты и времени в ячейках wxGrid

Leave a Reply

Your email address will not be published. Required fields are marked *

Please leave these two fields as-is: