calctrl.cpp

来自「A*算法 A*算法 A*算法 A*算法A*算法A*算法」· C++ 代码 · 共 1,791 行 · 第 1/4 页

CPP
1,791
字号

    bool sameMonth = m_date.GetMonth() == date.GetMonth(),
         sameYear = m_date.GetYear() == date.GetYear();

    if ( IsDateInRange(date) )
    {
        if ( sameMonth && sameYear )
        {
            // just change the day
            ChangeDay(date);
        }
        else
        {
            if ( AllowMonthChange() && (AllowYearChange() || sameYear) )
            {
                // change everything
                m_date = date;

                if ( !(GetWindowStyle() & wxCAL_SEQUENTIAL_MONTH_SELECTION) )
                {
                    // update the controls
                    m_comboMonth->SetSelection(m_date.GetMonth());

                    if ( AllowYearChange() )
                    {
                        if ( !m_userChangedYear )
                            m_spinYear->SetValue(m_date.Format(_T("%Y")));
                    }
                }

                // as the month changed, holidays did too
                SetHolidayAttrs();

                // update the calendar
                Refresh();
            }
            else
            {
                // forbidden
                retval = false;
            }
        }
    }

    m_userChangedYear = false;

    return retval;
}

void wxCalendarCtrl::ChangeDay(const wxDateTime& date)
{
    if ( m_date != date )
    {
        // we need to refresh the row containing the old date and the one
        // containing the new one
        wxDateTime dateOld = m_date;
        m_date = date;

        RefreshDate(dateOld);

        // if the date is in the same row, it was already drawn correctly
        if ( GetWeek(m_date) != GetWeek(dateOld) )
        {
            RefreshDate(m_date);
        }
    }
}

void wxCalendarCtrl::SetDateAndNotify(const wxDateTime& date)
{
    wxDateTime::Tm tm1 = m_date.GetTm(),
                   tm2 = date.GetTm();

    wxEventType type;
    if ( tm1.year != tm2.year )
        type = wxEVT_CALENDAR_YEAR_CHANGED;
    else if ( tm1.mon != tm2.mon )
        type = wxEVT_CALENDAR_MONTH_CHANGED;
    else if ( tm1.mday != tm2.mday )
        type = wxEVT_CALENDAR_DAY_CHANGED;
    else
        return;

    if ( SetDate(date) )
    {
        GenerateEvents(type, wxEVT_CALENDAR_SEL_CHANGED);
    }
}

// ----------------------------------------------------------------------------
// date range
// ----------------------------------------------------------------------------

bool wxCalendarCtrl::SetLowerDateLimit(const wxDateTime& date /* = wxDefaultDateTime */)
{
    bool retval = true;

    if ( !(date.IsValid()) || ( ( m_highdate.IsValid() ) ? ( date <= m_highdate ) : true ) )
    {
        m_lowdate = date;
    }
    else
    {
        retval = false;
    }

    return retval;
}

bool wxCalendarCtrl::SetUpperDateLimit(const wxDateTime& date /* = wxDefaultDateTime */)
{
    bool retval = true;

    if ( !(date.IsValid()) || ( ( m_lowdate.IsValid() ) ? ( date >= m_lowdate ) : true ) )
    {
        m_highdate = date;
    }
    else
    {
        retval = false;
    }

    return retval;
}

bool wxCalendarCtrl::SetDateRange(const wxDateTime& lowerdate /* = wxDefaultDateTime */, const wxDateTime& upperdate /* = wxDefaultDateTime */)
{
    bool retval = true;

    if (
        ( !( lowerdate.IsValid() ) || ( ( upperdate.IsValid() ) ? ( lowerdate <= upperdate ) : true ) ) &&
        ( !( upperdate.IsValid() ) || ( ( lowerdate.IsValid() ) ? ( upperdate >= lowerdate ) : true ) ) )
    {
        m_lowdate = lowerdate;
        m_highdate = upperdate;
    }
    else
    {
        retval = false;
    }

    return retval;
}

// ----------------------------------------------------------------------------
// date helpers
// ----------------------------------------------------------------------------

wxDateTime wxCalendarCtrl::GetStartDate() const
{
    wxDateTime::Tm tm = m_date.GetTm();

    wxDateTime date = wxDateTime(1, tm.mon, tm.year);

    // rewind back
    date.SetToPrevWeekDay(GetWindowStyle() & wxCAL_MONDAY_FIRST
                          ? wxDateTime::Mon : wxDateTime::Sun);

    if ( GetWindowStyle() & wxCAL_SHOW_SURROUNDING_WEEKS )
    {
        // We want to offset the calendar if we start on the first..
        if ( date.GetDay() == 1 )
        {
            date -= wxDateSpan::Week();
        }
    }

    return date;
}

bool wxCalendarCtrl::IsDateShown(const wxDateTime& date) const
{
    if ( !(GetWindowStyle() & wxCAL_SHOW_SURROUNDING_WEEKS) )
    {
        return date.GetMonth() == m_date.GetMonth();
    }
    else
    {
        return true;
    }
}

bool wxCalendarCtrl::IsDateInRange(const wxDateTime& date) const
{
    // Check if the given date is in the range specified
    return ( ( ( m_lowdate.IsValid() ) ? ( date >= m_lowdate ) : true )
        && ( ( m_highdate.IsValid() ) ? ( date <= m_highdate ) : true ) );
}

bool wxCalendarCtrl::ChangeYear(wxDateTime* target) const
{
    bool retval = false;

    if ( !(IsDateInRange(*target)) )
    {
        if ( target->GetYear() < m_date.GetYear() )
        {
            if ( target->GetYear() >= GetLowerDateLimit().GetYear() )
            {
                *target = GetLowerDateLimit();
                retval = true;
            }
            else
            {
                *target = m_date;
            }
        }
        else
        {
            if ( target->GetYear() <= GetUpperDateLimit().GetYear() )
            {
                *target = GetUpperDateLimit();
                retval = true;
            }
            else
            {
                *target = m_date;
            }
        }
    }
    else
    {
        retval = true;
    }

    return retval;
}

bool wxCalendarCtrl::ChangeMonth(wxDateTime* target) const
{
    bool retval = true;

    if ( !(IsDateInRange(*target)) )
    {
        retval = false;

        if ( target->GetMonth() < m_date.GetMonth() )
        {
            *target = GetLowerDateLimit();
        }
        else
        {
            *target = GetUpperDateLimit();
        }
    }

    return retval;
}

size_t wxCalendarCtrl::GetWeek(const wxDateTime& date) const
{
    size_t retval = date.GetWeekOfMonth(GetWindowStyle() & wxCAL_MONDAY_FIRST
                                   ? wxDateTime::Monday_First
                                   : wxDateTime::Sunday_First);

    if ( (GetWindowStyle() & wxCAL_SHOW_SURROUNDING_WEEKS) )
    {
        // we need to offset an extra week if we "start" on the 1st of the month
        wxDateTime::Tm tm = date.GetTm();

        wxDateTime datetest = wxDateTime(1, tm.mon, tm.year);

        // rewind back
        datetest.SetToPrevWeekDay(GetWindowStyle() & wxCAL_MONDAY_FIRST
                              ? wxDateTime::Mon : wxDateTime::Sun);

        if ( datetest.GetDay() == 1 )
        {
            retval += 1;
        }
    }

    return retval;
}

// ----------------------------------------------------------------------------
// size management
// ----------------------------------------------------------------------------

// this is a composite control and it must arrange its parts each time its
// size or position changes: the combobox and spinctrl are along the top of
// the available area and the calendar takes up therest of the space

// the static controls are supposed to be always smaller than combo/spin so we
// always use the latter for size calculations and position the static to take
// the same space

// the constants used for the layout
#define VERT_MARGIN     5           // distance between combo and calendar
#ifdef __WXMAC__
#define HORZ_MARGIN    5           //                            spin
#else
#define HORZ_MARGIN    15           //                            spin
#endif
wxSize wxCalendarCtrl::DoGetBestSize() const
{
    // calc the size of the calendar
    ((wxCalendarCtrl *)this)->RecalcGeometry(); // const_cast

    wxCoord width = 7*m_widthCol,
            height = 7*m_heightRow + m_rowOffset + VERT_MARGIN;

    if ( !HasFlag(wxCAL_SEQUENTIAL_MONTH_SELECTION) )
    {
        // the combobox doesn't report its height correctly (it returns the
        // height including the drop down list) so don't use it
        height += m_spinYear->GetBestSize().y;


        wxCoord w2 = m_comboMonth->GetBestSize().x + HORZ_MARGIN + GetCharWidth()*6;
        if (width < w2)
            width = w2;
    }

    if ( !HasFlag(wxBORDER_NONE) )
    {
        // the border would clip the last line otherwise
        height += 6;
        width += 4;
    }

    wxSize best(width, height);
    CacheBestSize(best);
    return best;
}

void wxCalendarCtrl::DoSetSize(int x, int y,
                               int width, int height,
                               int sizeFlags)
{
    wxControl::DoSetSize(x, y, width, height, sizeFlags);
}

void wxCalendarCtrl::DoMoveWindow(int x, int y, int width, int height)
{
    int yDiff;

    if ( !HasFlag(wxCAL_SEQUENTIAL_MONTH_SELECTION) )
    {
        wxSize sizeCombo = m_comboMonth->GetSize();
        wxSize sizeStatic = m_staticMonth->GetSize();
        wxSize sizeSpin = m_spinYear->GetSize();
        int dy = (sizeCombo.y - sizeStatic.y) / 2;
        m_comboMonth->Move(x, y);
        m_staticMonth->SetSize(x, y + dy, sizeCombo.x, sizeStatic.y);

        int xDiff = sizeCombo.x + HORZ_MARGIN;

        m_spinYear->SetSize(x + xDiff, y, width - xDiff, sizeCombo.y);
        m_staticYear->SetSize(x + xDiff, y + dy, width - xDiff, sizeStatic.y);

        yDiff = wxMax(sizeSpin.y, sizeCombo.y) + VERT_MARGIN;
    }
    else // no controls on the top
    {
        yDiff = 0;
    }

    wxControl::DoMoveWindow(x, y + yDiff, width, height - yDiff);
}

void wxCalendarCtrl::DoGetPosition(int *x, int *y) const
{
    wxControl::DoGetPosition(x, y);

    if ( !(GetWindowStyle() & wxCAL_SEQUENTIAL_MONTH_SELECTION) )
    {
        // our real top corner is not in this position
        if ( y )
        {
            *y -= GetMonthControl()->GetSize().y + VERT_MARGIN;
        }
    }
}

void wxCalendarCtrl::DoGetSize(int *width, int *height) const
{
    wxControl::DoGetSize(width, height);

    if ( !(GetWindowStyle() & wxCAL_SEQUENTIAL_MONTH_SELECTION) )
    {
        // our real height is bigger
        if ( height && GetMonthControl())
        {
            *height += GetMonthControl()->GetSize().y + VERT_MARGIN;
        }
    }
}

void wxCalendarCtrl::RecalcGeometry()
{
    wxClientDC dc(this);

    dc.SetFont(GetFont());

    // determine the column width (weekday names are not necessarily wider
    // than the numbers (in some languages), so let's not assume that they are)
    m_widthCol = 0;
    for ( int day = 10; day <= 31; day++)
    {
        wxCoord width;
        dc.GetTextExtent(wxString::Format(wxT("%d"), day), &width, &m_heightRow);
        if ( width > m_widthCol )
        {
            // 1.5 times the width gives nice margins even if the weekday
            // names are short
	     m_widthCol = width+width/2;
        }
    }
    wxDateTime::WeekDay wd;
    for ( wd = wxDateTime::Sun; wd < wxDateTime::Inv_WeekDay; wxNextWDay(wd) )
    {
        wxCoord width;
        dc.GetTextExtent(m_weekdays[wd], &width, &m_heightRow);
        if ( width > m_widthCol )
        {
            m_widthCol = width;
        }
    }

    // leave some margins
    m_widthCol += 2;
    m_heightRow += 2;

    m_rowOffset = (GetWindowStyle() & wxCAL_SEQUENTIAL_MONTH_SELECTION) ? m_heightRow : 0; // conditional in relation to style
}

// ----------------------------------------------------------------------------
// drawing
// ----------------------------------------------------------------------------

void wxCalendarCtrl::OnPaint(wxPaintEvent& WXUNUSED(event))
{
    wxPaintDC dc(this);

    dc.SetFont(GetFont());

    RecalcGeometry();

#if DEBUG_PAINT
    wxLogDebug("--- starting to paint, selection: %s, week %u\n",
           m_date.Format("%a %d-%m-%Y %H:%M:%S").c_str(),
           GetWeek(m_date));
#endif

    wxCoord y = 0;
    wxCoord x0 = wxMax( (GetSize().x - m_widthCol*7) /2 , 0 );

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?