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

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

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

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

#pragma once
#include <wx/grid.h>
#include <wx/generic/gridctrl.h>
#include <wx/datectrl.h>
#include <wx/dateevt.h>
#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();
/* ... */

Вот так в результате будет выглядеть наше приложение:

T-Rex

Share
Published by
T-Rex

Recent Posts

Разработка кроссплатформенных модульных приложений на C++ с библиотекой wxWidgets

Введение Уже долгое время не пишу статьи о разработке, хотя сам процесс написания мне очень…

11 years ago

wxWidgets App With Plugins (Windows/Linux/Mac) – Sample Source Code

I can see that there is still a lot of topics at wxWidgets forums related…

11 years ago

wxToolBox is Now Open-Source!

I've just published the source code of wxToolBox component and a couple of sample apps at…

11 years ago

Microsoft Kinect Helper Library and Sample for wxWidgets

Microsoft released their Kinect SDK several days ago. So, for those wxWidgets developers who are…

13 years ago

wxJSON 1.1.0 Released

JSON (JavaScript Object Notation) is a lightweight data-interchange format. It is easy for humans to…

15 years ago

wxRuby. Оно даже работает!

Вдохновленнный читаемой нынче книгой My Job Went to India: 52 Ways to Save Your Job…

15 years ago