| /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
| /* |
| * This file is part of the Collabora Office project. |
| * |
| * This Source Code Form is subject to the terms of the Mozilla Public |
| * License, v. 2.0. If a copy of the MPL was not distributed with this |
| * file, You can obtain one at http://mozilla.org/MPL/2.0/. |
| * |
| * This file incorporates work covered by the following license notice: |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed |
| * with this work for additional information regarding copyright |
| * ownership. The ASF licenses this file to you under the Apache |
| * License, Version 2.0 (the "License"); you may not use this file |
| * except in compliance with the License. You may obtain a copy of |
| * the License at http://www.apache.org/licenses/LICENSE-2.0 . |
| */ |
| |
| #include <vcl/help.hxx> |
| #include <vcl/menu.hxx> |
| #include <vcl/event.hxx> |
| #include <vcl/toolkit/calendar.hxx> |
| #include <vcl/commandevent.hxx> |
| #include <vcl/dockwin.hxx> |
| #include <unotools/localedatawrapper.hxx> |
| |
| #include <com/sun/star/i18n/Weekdays.hpp> |
| #include <com/sun/star/i18n/CalendarDisplayIndex.hpp> |
| #include <com/sun/star/i18n/CalendarFieldIndex.hpp> |
| |
| #include <calendar.hxx> |
| #include <svdata.hxx> |
| #include <strings.hrc> |
| #include <memory> |
| |
| constexpr tools::Long DAY_OFFX = 4; |
| constexpr tools::Long DAY_OFFY = 2; |
| constexpr tools::Long MONTH_BORDERX = 4; |
| constexpr tools::Long MONTH_OFFY = 3; |
| constexpr tools::Long WEEKDAY_OFFY = 3; |
| constexpr tools::Long TITLE_OFFY = 3; |
| constexpr tools::Long TITLE_BORDERY = 2; |
| constexpr tools::Long SPIN_OFFX = 4; |
| constexpr tools::Long SPIN_OFFY = TITLE_BORDERY; |
| |
| constexpr sal_uInt16 CALENDAR_HITTEST_DAY = 0x0001; |
| constexpr sal_uInt16 CALENDAR_HITTEST_MONTHTITLE = 0x0004; |
| constexpr sal_uInt16 CALENDAR_HITTEST_PREV = 0x0008; |
| constexpr sal_uInt16 CALENDAR_HITTEST_NEXT = 0x0010; |
| |
| constexpr sal_uInt16 MENU_YEAR_COUNT = 3; |
| |
| using namespace ::com::sun::star; |
| |
| static void ImplCalendarSelectDate(IntDateSet* pTable, const Date& rDate, bool bSelect) |
| { |
| if ( bSelect ) |
| pTable->insert( rDate.GetDate() ); |
| else |
| pTable->erase( rDate.GetDate() ); |
| } |
| |
| void Calendar::ImplInit( WinBits nWinStyle ) |
| { |
| mpSelectTable.reset(new IntDateSet); |
| mnDayCount = 0; |
| mnWinStyle = nWinStyle; |
| mnFirstYear = 0; |
| mnLastYear = 0; |
| mbCalc = true; |
| mbFormat = true; |
| mbDrag = false; |
| mbMenuDown = false; |
| mbSpinDown = false; |
| mbPrevIn = false; |
| mbNextIn = false; |
| |
| const OUString aGregorian(u"gregorian"_ustr); |
| maCalendarWrapper.loadCalendar( aGregorian, |
| Application::GetAppLocaleDataWrapper().getLanguageTag().getLocale()); |
| |
| if (maCalendarWrapper.getUniqueID() != aGregorian) |
| { |
| SAL_WARN( "vcl.control", "Calendar::ImplInit: No ``gregorian'' calendar available for locale ``" |
| << Application::GetAppLocaleDataWrapper().getLanguageTag().getBcp47() |
| << "'' and other calendars aren't supported. Using en-US fallback." ); |
| |
| /* If we ever wanted to support other calendars than Gregorian a lot of |
| * rewrite would be necessary to internally replace use of class Date |
| * with proper class CalendarWrapper methods, get rid of fixed 12 |
| * months, fixed 7 days, ... */ |
| maCalendarWrapper.loadCalendar( aGregorian, lang::Locale( u"en"_ustr, u"US"_ustr, u""_ustr)); |
| } |
| |
| SetFirstDate( maCurDate ); |
| ImplCalendarSelectDate( mpSelectTable.get(), maCurDate, true ); |
| |
| // generate other strings |
| maDayText = VclResId(STR_SVT_CALENDAR_DAY); |
| maWeekText = VclResId(STR_SVT_CALENDAR_WEEK); |
| |
| // create text for each day |
| for (sal_Int32 i = 0; i < 31; ++i) |
| { |
| maDayTexts[i] = OUString::number(i+1); |
| } |
| |
| ImplInitSettings(); |
| } |
| |
| void Calendar::ApplySettings(vcl::RenderContext& rRenderContext) |
| { |
| const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings(); |
| maSelColor = rStyleSettings.GetHighlightTextColor(); |
| SetPointFont(rRenderContext, rStyleSettings.GetToolFont()); |
| rRenderContext.SetTextColor(rStyleSettings.GetFieldTextColor()); |
| rRenderContext.SetBackground(Wallpaper(rStyleSettings.GetFieldColor())); |
| } |
| |
| void Calendar::ImplInitSettings() |
| { |
| const StyleSettings& rStyleSettings = GetSettings().GetStyleSettings(); |
| maSelColor = rStyleSettings.GetHighlightTextColor(); |
| SetPointFont(*GetOutDev(), rStyleSettings.GetToolFont()); |
| SetTextColor(rStyleSettings.GetFieldTextColor()); |
| SetBackground(Wallpaper(rStyleSettings.GetFieldColor())); |
| } |
| |
| Calendar::Calendar(vcl::Window* pParent, WinBits nWinStyle) |
| : Control(pParent, nWinStyle & (WB_TABSTOP | WB_GROUP | WB_BORDER | WB_3DLOOK)) |
| , maCalendarWrapper(Application::GetAppLocaleDataWrapper().getComponentContext()) |
| , maOldFormatFirstDate(0, 0, 1900) |
| , maOldFormatLastDate(0, 0, 1900) |
| , maFirstDate(0, 0, 1900) |
| , maOldFirstDate(0, 0, 1900) |
| , maCurDate(Date::SYSTEM) |
| , maOldCurDate(0, 0, 1900) |
| { |
| ImplInit( nWinStyle ); |
| } |
| |
| Calendar::~Calendar() |
| { |
| disposeOnce(); |
| } |
| |
| void Calendar::dispose() |
| { |
| mpSelectTable.reset(); |
| mpOldSelectTable.reset(); |
| Control::dispose(); |
| } |
| |
| DayOfWeek Calendar::ImplGetWeekStart() const |
| { |
| // Map i18n::Weekdays to Date DayOfWeek |
| DayOfWeek eDay; |
| const sal_Int16 nDay = maCalendarWrapper.getFirstDayOfWeek(); |
| |
| switch (nDay) |
| { |
| case i18n::Weekdays::SUNDAY : |
| eDay = SUNDAY; |
| break; |
| case i18n::Weekdays::MONDAY : |
| eDay = MONDAY; |
| break; |
| case i18n::Weekdays::TUESDAY : |
| eDay = TUESDAY; |
| break; |
| case i18n::Weekdays::WEDNESDAY : |
| eDay = WEDNESDAY; |
| break; |
| case i18n::Weekdays::THURSDAY : |
| eDay = THURSDAY; |
| break; |
| case i18n::Weekdays::FRIDAY : |
| eDay = FRIDAY; |
| break; |
| case i18n::Weekdays::SATURDAY : |
| eDay = SATURDAY; |
| break; |
| default: |
| SAL_WARN( "vcl.control", "Calendar::ImplGetWeekStart: broken i18n Gregorian calendar (getFirstDayOfWeek())"); |
| eDay = SUNDAY; |
| } |
| return eDay; |
| } |
| |
| void Calendar::ImplFormat() |
| { |
| if ( !mbFormat ) |
| return; |
| |
| if ( mbCalc ) |
| { |
| const Size aOutSize = GetOutputSizePixel(); |
| |
| if ( (aOutSize.Width() <= 1) || (aOutSize.Height() <= 1) ) |
| return; |
| |
| tools::Long n99TextWidth = GetTextWidth( u"99"_ustr ); |
| tools::Long nTextHeight = GetTextHeight(); |
| |
| // calculate width and x-position |
| mnDayWidth = n99TextWidth+DAY_OFFX; |
| mnMonthWidth = mnDayWidth*7; |
| mnMonthWidth += MONTH_BORDERX*2; |
| mnMonthPerLine = aOutSize.Width() / mnMonthWidth; |
| |
| if ( !mnMonthPerLine ) |
| mnMonthPerLine = 1; |
| |
| const tools::Long nOver = aOutSize.Width() - (mnMonthPerLine * mnMonthWidth) / mnMonthPerLine; |
| mnMonthWidth += nOver; |
| mnDaysOffX = MONTH_BORDERX; |
| mnDaysOffX += nOver/2; |
| |
| // calculate height and y-position |
| mnDayHeight = nTextHeight + DAY_OFFY; |
| mnWeekDayOffY = nTextHeight + TITLE_OFFY + (TITLE_BORDERY*2); |
| mnDaysOffY = mnWeekDayOffY + nTextHeight + WEEKDAY_OFFY; |
| mnMonthHeight = (mnDayHeight*6) + mnDaysOffY; |
| mnMonthHeight += MONTH_OFFY; |
| mnLines = aOutSize.Height() / mnMonthHeight; |
| |
| if ( !mnLines ) |
| mnLines = 1; |
| |
| mnMonthHeight += (aOutSize.Height()-(mnLines*mnMonthHeight)) / mnLines; |
| |
| // calculate spinfields |
| const tools::Long nSpinSize = nTextHeight + TITLE_BORDERY - SPIN_OFFY; |
| |
| maPrevRect.SetLeft( SPIN_OFFX ); |
| maPrevRect.SetTop( SPIN_OFFY ); |
| maPrevRect.SetRight( maPrevRect.Left()+nSpinSize ); |
| maPrevRect.SetBottom( maPrevRect.Top()+nSpinSize ); |
| maNextRect.SetLeft( aOutSize.Width()-SPIN_OFFX-nSpinSize-1 ); |
| maNextRect.SetTop( SPIN_OFFY ); |
| maNextRect.SetRight( maNextRect.Left()+nSpinSize ); |
| maNextRect.SetBottom( maNextRect.Top()+nSpinSize ); |
| |
| // Calculate DayOfWeekText (gets displayed in a narrow font) |
| maDayOfWeekText.clear(); |
| tools::Long nStartOffX = 0; |
| sal_Int16 nDay = maCalendarWrapper.getFirstDayOfWeek(); |
| |
| for ( sal_Int16 nDayOfWeek = 0; nDayOfWeek < 7; nDayOfWeek++ ) |
| { |
| // Use narrow name. |
| const OUString aDayOfWeek(maCalendarWrapper.getDisplayName( |
| i18n::CalendarDisplayIndex::DAY, nDay, 2)); |
| |
| tools::Long nOffX = (mnDayWidth-GetTextWidth( aDayOfWeek ))/2; |
| |
| if ( !nDayOfWeek ) |
| nStartOffX = nOffX; |
| else |
| nOffX -= nStartOffX; |
| |
| nOffX += nDayOfWeek * mnDayWidth; |
| mnDayOfWeekAry[nDayOfWeek] = nOffX; |
| maDayOfWeekText += aDayOfWeek; |
| nDay++; |
| nDay %= 7; |
| } |
| |
| // header position for the last day of week |
| mnDayOfWeekAry[7] = mnMonthWidth; |
| |
| mbCalc = false; |
| } |
| |
| // calculate number of days |
| |
| const DayOfWeek eStartDay = ImplGetWeekStart(); |
| |
| sal_uInt16 nWeekDay; |
| Date aTempDate = GetFirstMonth(); |
| maFirstDate = aTempDate; |
| nWeekDay = static_cast<sal_uInt16>(aTempDate.GetDayOfWeek()); |
| nWeekDay = (nWeekDay+(7-static_cast<sal_uInt16>(eStartDay))) % 7; |
| maFirstDate.AddDays( -nWeekDay ); |
| mnDayCount = nWeekDay; |
| sal_uInt16 nDaysInMonth; |
| sal_uInt16 nMonthCount = static_cast<sal_uInt16>(mnMonthPerLine*mnLines); |
| |
| for ( sal_uInt16 i = 0; i < nMonthCount; i++ ) |
| { |
| nDaysInMonth = aTempDate.GetDaysInMonth(); |
| mnDayCount += nDaysInMonth; |
| aTempDate.AddDays( nDaysInMonth ); |
| } |
| |
| Date aTempDate2 = aTempDate; |
| --aTempDate2; |
| nDaysInMonth = aTempDate2.GetDaysInMonth(); |
| aTempDate2.AddDays( -(nDaysInMonth-1) ); |
| nWeekDay = static_cast<sal_uInt16>(aTempDate2.GetDayOfWeek()); |
| nWeekDay = (nWeekDay+(7-static_cast<sal_uInt16>(eStartDay))) % 7; |
| mnDayCount += 42-nDaysInMonth-nWeekDay; |
| |
| // determine colours |
| maOtherColor = COL_LIGHTGRAY; |
| if ( maOtherColor.IsRGBEqual( GetBackground().GetColor() ) ) |
| maOtherColor = COL_GRAY; |
| |
| Date aLastDate = GetLastDate(); |
| if ( (maOldFormatLastDate != aLastDate) || |
| (maOldFormatFirstDate != maFirstDate) ) |
| { |
| maOldFormatFirstDate = maFirstDate; |
| maOldFormatLastDate = aLastDate; |
| } |
| |
| // get DateInfo |
| const sal_Int16 nNewFirstYear = maFirstDate.GetYear(); |
| const sal_Int16 nNewLastYear = GetLastDate().GetYear(); |
| |
| if ( mnFirstYear ) |
| { |
| if ( nNewFirstYear < mnFirstYear ) |
| mnFirstYear = nNewFirstYear; |
| |
| if ( nNewLastYear > mnLastYear ) |
| mnLastYear = nNewLastYear; |
| } |
| else |
| { |
| mnFirstYear = nNewFirstYear; |
| mnLastYear = nNewLastYear; |
| } |
| |
| mbFormat = false; |
| } |
| |
| sal_uInt16 Calendar::ImplDoHitTest(const Point& rPos, Date& rDate) const |
| { |
| if ( mbFormat ) |
| return 0; |
| |
| if ( maPrevRect.Contains( rPos ) ) |
| return CALENDAR_HITTEST_PREV; |
| else if ( maNextRect.Contains( rPos ) ) |
| return CALENDAR_HITTEST_NEXT; |
| |
| tools::Long nY; |
| tools::Long nOffX; |
| sal_Int32 nDay; |
| const DayOfWeek eStartDay = ImplGetWeekStart(); |
| |
| rDate = GetFirstMonth(); |
| nY = 0; |
| for ( tools::Long i = 0; i < mnLines; i++ ) |
| { |
| if ( rPos.Y() < nY ) |
| return 0; |
| |
| tools::Long nX = 0; |
| const tools::Long nYMonth = nY + mnMonthHeight; |
| |
| for ( tools::Long j = 0; j < mnMonthPerLine; j++ ) |
| { |
| if ( (rPos.X() < nX) && (rPos.Y() < nYMonth) ) |
| return 0; |
| |
| sal_uInt16 nDaysInMonth = rDate.GetDaysInMonth(); |
| |
| // matching month was found |
| if ( (rPos.X() > nX) && (rPos.Y() < nYMonth) && |
| (rPos.X() < nX+mnMonthWidth) ) |
| { |
| if ( rPos.Y() < (nY+(TITLE_BORDERY*2)+mnDayHeight)) |
| { |
| return CALENDAR_HITTEST_MONTHTITLE; |
| } |
| else |
| { |
| tools::Long nDayX = nX+mnDaysOffX; |
| tools::Long nDayY = nY+mnDaysOffY; |
| |
| if ( rPos.Y() < nDayY ) |
| return 0; |
| |
| sal_Int32 nDayIndex = static_cast<sal_Int32>(rDate.GetDayOfWeek()); |
| nDayIndex = (nDayIndex+(7-static_cast<sal_Int32>(eStartDay))) % 7; |
| |
| if ( (i == 0) && (j == 0) ) |
| { |
| Date aTempDate = rDate; |
| aTempDate.AddDays( -nDayIndex ); |
| |
| for ( nDay = 0; nDay < nDayIndex; nDay++ ) |
| { |
| nOffX = nDayX + (nDay*mnDayWidth); |
| if ( (rPos.Y() >= nDayY) && (rPos.Y() < nDayY+mnDayHeight) && |
| (rPos.X() >= nOffX) && (rPos.X() < nOffX+mnDayWidth) ) |
| { |
| rDate = aTempDate; |
| rDate.AddDays( nDay ); |
| return CALENDAR_HITTEST_DAY; |
| } |
| } |
| } |
| |
| for ( nDay = 1; nDay <= nDaysInMonth; nDay++ ) |
| { |
| if ( rPos.Y() < nDayY ) |
| { |
| rDate.AddDays( nDayIndex ); |
| return 0; |
| } |
| |
| nOffX = nDayX + (nDayIndex*mnDayWidth); |
| |
| if ( (rPos.Y() >= nDayY) && (rPos.Y() < nDayY+mnDayHeight) && |
| (rPos.X() >= nOffX) && (rPos.X() < nOffX+mnDayWidth) ) |
| { |
| rDate.AddDays( nDay-1 ); |
| return CALENDAR_HITTEST_DAY; |
| } |
| |
| if ( nDayIndex == 6 ) |
| { |
| nDayIndex = 0; |
| nDayY += mnDayHeight; |
| } |
| else |
| { |
| nDayIndex++; |
| } |
| } |
| |
| if ( (i == mnLines-1) && (j == mnMonthPerLine-1) ) |
| { |
| sal_uInt16 nWeekDay = static_cast<sal_uInt16>(rDate.GetDayOfWeek()); |
| nWeekDay = (nWeekDay + (7-static_cast<sal_uInt16>(eStartDay))) % 7; |
| const sal_Int32 nDayCount = 42 - nDaysInMonth - nWeekDay; |
| Date aTempDate = rDate; |
| aTempDate.AddDays( nDaysInMonth ); |
| for ( nDay = 1; nDay <= nDayCount; nDay++ ) |
| { |
| if ( rPos.Y() < nDayY ) |
| { |
| rDate.AddDays( nDayIndex ); |
| return 0; |
| } |
| |
| nOffX = nDayX + (nDayIndex*mnDayWidth); |
| |
| if ( (rPos.Y() >= nDayY) && (rPos.Y() < nDayY+mnDayHeight) && |
| (rPos.X() >= nOffX) && (rPos.X() < nOffX+mnDayWidth) ) |
| { |
| rDate = aTempDate; |
| rDate.AddDays( nDay-1 ); |
| return CALENDAR_HITTEST_DAY; |
| } |
| |
| if ( nDayIndex == 6 ) |
| { |
| nDayIndex = 0; |
| nDayY += mnDayHeight; |
| } |
| else |
| { |
| nDayIndex++; |
| } |
| } |
| } |
| } |
| } |
| |
| rDate.AddDays( nDaysInMonth ); |
| nX += mnMonthWidth; |
| } |
| |
| nY += mnMonthHeight; |
| } |
| |
| return 0; |
| } |
| |
| namespace |
| { |
| |
| void ImplDrawSpinArrow(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect, bool bPrev) |
| { |
| tools::Long i; |
| tools::Long n; |
| tools::Long nLines; |
| const tools::Long nHeight = rRect.GetHeight(); |
| const tools::Long nWidth = rRect.GetWidth(); |
| |
| if (nWidth < nHeight) |
| n = nWidth; |
| else |
| n = nHeight; |
| |
| if (!(n & 0x01)) |
| n--; |
| |
| nLines = n/2; |
| |
| tools::Rectangle aRect(Point( rRect.Left() + (nWidth / 2) - (nLines / 2), |
| rRect.Top() + (nHeight / 2) ), |
| Size(1, 1)); |
| if (!bPrev) |
| { |
| aRect.AdjustLeft(nLines ); |
| aRect.AdjustRight(nLines ); |
| } |
| |
| rRenderContext.DrawRect(aRect); |
| |
| for (i = 0; i < nLines; i++) |
| { |
| if (bPrev) |
| { |
| aRect.AdjustLeft( 1 ); |
| aRect.AdjustRight( 1 ); |
| } |
| else |
| { |
| aRect.AdjustLeft( -1 ); |
| aRect.AdjustRight( -1 ); |
| } |
| |
| aRect.AdjustTop( -1 ); |
| aRect.AdjustBottom( 1 ); |
| rRenderContext.DrawRect(aRect); |
| } |
| } |
| |
| } //end anonymous namespace |
| |
| void Calendar::ImplDrawSpin(vcl::RenderContext& rRenderContext ) |
| { |
| rRenderContext.SetLineColor(); |
| rRenderContext.SetFillColor(rRenderContext.GetSettings().GetStyleSettings().GetButtonTextColor()); |
| |
| tools::Rectangle aOutRect = maPrevRect; |
| aOutRect.AdjustLeft(3 ); |
| aOutRect.AdjustTop(3 ); |
| aOutRect.AdjustRight( -3 ); |
| aOutRect.AdjustBottom( -3 ); |
| |
| ImplDrawSpinArrow(rRenderContext, aOutRect, true); |
| |
| aOutRect = maNextRect; |
| aOutRect.AdjustLeft(3 ); |
| aOutRect.AdjustTop(3 ); |
| aOutRect.AdjustRight( -3 ); |
| aOutRect.AdjustBottom( -3 ); |
| |
| ImplDrawSpinArrow(rRenderContext, aOutRect, false); |
| } |
| |
| void Calendar::ImplDrawDate(vcl::RenderContext& rRenderContext, |
| tools::Long nX, tools::Long nY, |
| sal_uInt16 nDay, sal_uInt16 nMonth, sal_Int16 nYear, |
| bool bOther, sal_Int32 nToday ) |
| { |
| Color const* pTextColor = nullptr; |
| const OUString& rDay = maDayTexts[(nDay - 1) % std::size(maDayTexts)]; |
| tools::Rectangle aDateRect(nX, nY, nX + mnDayWidth - 1, nY + mnDayHeight - 1); |
| |
| bool bSel = false; |
| bool bFocus = false; |
| |
| // actual day |
| if ((nDay == maCurDate.GetDay()) && |
| (nMonth == maCurDate.GetMonth()) && |
| (nYear == maCurDate.GetYear())) |
| { |
| bFocus = true; |
| } |
| |
| if (mpSelectTable) |
| { |
| if (mpSelectTable->find(Date(nDay, nMonth, nYear).GetDate()) != mpSelectTable->end()) |
| bSel = true; |
| } |
| |
| // get textcolour |
| if (bSel) |
| pTextColor = &maSelColor; |
| else if (bOther) |
| pTextColor = &maOtherColor; |
| |
| if (bFocus) |
| HideFocus(); |
| |
| // display background |
| const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings(); |
| |
| if (bSel) |
| { |
| rRenderContext.SetLineColor(); |
| rRenderContext.SetFillColor(rStyleSettings.GetHighlightColor()); |
| rRenderContext.DrawRect(aDateRect); |
| } |
| |
| // display text |
| const tools::Long nTextX = nX + (mnDayWidth - GetTextWidth(rDay)) - (DAY_OFFX / 2); |
| const tools::Long nTextY = nY + (mnDayHeight - GetTextHeight()) / 2; |
| |
| if (pTextColor) |
| { |
| Color aOldColor = rRenderContext.GetTextColor(); |
| rRenderContext.SetTextColor(*pTextColor); |
| rRenderContext.DrawText(Point(nTextX, nTextY), rDay); |
| rRenderContext.SetTextColor(aOldColor); |
| } |
| else |
| { |
| rRenderContext.DrawText(Point(nTextX, nTextY), rDay); |
| } |
| |
| // today |
| Date aTodayDate(maCurDate); |
| |
| if (nToday) |
| aTodayDate.SetDate(nToday); |
| else |
| aTodayDate = Date(Date::SYSTEM); |
| |
| if ((nDay == aTodayDate.GetDay()) && |
| (nMonth == aTodayDate.GetMonth()) && |
| (nYear == aTodayDate.GetYear())) |
| { |
| rRenderContext.SetLineColor(rStyleSettings.GetWindowTextColor()); |
| rRenderContext.SetFillColor(); |
| rRenderContext.DrawRect(aDateRect); |
| } |
| |
| // if needed do FocusRect |
| if (bFocus && HasFocus()) |
| ShowFocus(aDateRect); |
| } |
| |
| void Calendar::ImplDraw(vcl::RenderContext& rRenderContext) |
| { |
| ImplFormat(); |
| |
| const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings(); |
| |
| Size aOutSize(GetOutputSizePixel()); |
| |
| tools::Long i; |
| tools::Long j; |
| tools::Long nY; |
| tools::Long nDeltaX; |
| tools::Long nDeltaY; |
| tools::Long nDayX; |
| tools::Long nDayY; |
| const sal_Int32 nToday = Date(Date::SYSTEM).GetDate(); |
| sal_uInt16 nDay; |
| sal_uInt16 nMonth; |
| sal_Int16 nYear; |
| Date aDate = GetFirstMonth(); |
| const DayOfWeek eStartDay = ImplGetWeekStart(); |
| |
| HideFocus(); |
| |
| nY = 0; |
| |
| for (i = 0; i < mnLines; i++) |
| { |
| // display title bar |
| rRenderContext.SetLineColor(); |
| rRenderContext.SetFillColor(rStyleSettings.GetFaceColor()); |
| |
| tools::Rectangle aTitleRect(0, nY, aOutSize.Width() - 1, nY + mnDayHeight - DAY_OFFY + TITLE_BORDERY * 2); |
| |
| rRenderContext.DrawRect(aTitleRect); |
| |
| Point aTopLeft1(aTitleRect.Left(), aTitleRect.Top()); |
| Point aTopLeft2(aTitleRect.Left(), aTitleRect.Top() + 1); |
| Point aBottomRight1(aTitleRect.Right(), aTitleRect.Bottom()); |
| Point aBottomRight2(aTitleRect.Right(), aTitleRect.Bottom() - 1); |
| |
| rRenderContext.SetLineColor(rStyleSettings.GetDarkShadowColor()); |
| rRenderContext.DrawLine(aTopLeft1, Point(aBottomRight1.X(), aTopLeft1.Y())); |
| rRenderContext.SetLineColor(rStyleSettings.GetLightColor() ); |
| rRenderContext.DrawLine(aTopLeft2, Point(aBottomRight2.X(), aTopLeft2.Y())); |
| rRenderContext.DrawLine(aTopLeft2, Point(aTopLeft2.X(), aBottomRight2.Y())); |
| rRenderContext.SetLineColor(rStyleSettings.GetShadowColor() ); |
| rRenderContext.DrawLine(Point(aTopLeft2.X(), aBottomRight2.Y()), aBottomRight2); |
| rRenderContext.DrawLine(Point(aBottomRight2.X(), aTopLeft2.Y()), aBottomRight2); |
| rRenderContext.SetLineColor(rStyleSettings.GetDarkShadowColor()); |
| rRenderContext.DrawLine(Point(aTopLeft1.X(), aBottomRight1.Y()), aBottomRight1); |
| |
| Point aSepPos1(0, aTitleRect.Top() + TITLE_BORDERY); |
| Point aSepPos2(0, aTitleRect.Bottom() - TITLE_BORDERY); |
| |
| for (j = 0; j < mnMonthPerLine-1; j++) |
| { |
| aSepPos1.AdjustX(mnMonthWidth-1 ); |
| aSepPos2.setX( aSepPos1.X() ); |
| |
| rRenderContext.SetLineColor(rStyleSettings.GetShadowColor()); |
| rRenderContext.DrawLine(aSepPos1, aSepPos2); |
| |
| aSepPos1.AdjustX( 1 ); |
| aSepPos2.setX( aSepPos1.X() ); |
| |
| rRenderContext.SetLineColor(rStyleSettings.GetLightColor()); |
| rRenderContext.DrawLine(aSepPos1, aSepPos2); |
| } |
| |
| tools::Long nX = 0; |
| |
| for (j = 0; j < mnMonthPerLine; j++) |
| { |
| nMonth = aDate.GetMonth(); |
| nYear = aDate.GetYear(); |
| |
| // display month in title bar |
| nDeltaX = nX; |
| nDeltaY = nY + TITLE_BORDERY; |
| OUString aMonthText = maCalendarWrapper.getDisplayName(i18n::CalendarDisplayIndex::MONTH, nMonth - 1, 1) |
| + " " |
| + OUString::number(nYear); |
| |
| tools::Long nMonthTextWidth = rRenderContext.GetTextWidth(aMonthText); |
| tools::Long nMonthOffX1 = 0; |
| tools::Long nMonthOffX2 = 0; |
| |
| if (i == 0) |
| { |
| if (j == 0) |
| nMonthOffX1 = maPrevRect.Right() + 1; |
| |
| if (j == mnMonthPerLine - 1) |
| nMonthOffX2 = aOutSize.Width() - maNextRect.Left() + 1; |
| } |
| |
| const tools::Long nMaxMonthWidth = mnMonthWidth - nMonthOffX1 - nMonthOffX2 - 4; |
| |
| if (nMonthTextWidth > nMaxMonthWidth) |
| { |
| // Abbreviated month name. |
| aMonthText = maCalendarWrapper.getDisplayName(i18n::CalendarDisplayIndex::MONTH, nMonth - 1, 0) |
| + " " |
| + OUString::number(nYear); |
| nMonthTextWidth = rRenderContext.GetTextWidth(aMonthText); |
| } |
| |
| tools::Long nTempOff = (mnMonthWidth - nMonthTextWidth + 1) / 2; |
| |
| if (nTempOff < nMonthOffX1) |
| { |
| nDeltaX += nMonthOffX1 + 1; |
| } |
| else |
| { |
| if (nTempOff + nMonthTextWidth > mnMonthWidth - nMonthOffX2) |
| nDeltaX += mnMonthWidth - nMonthOffX2 - nMonthTextWidth; |
| else |
| nDeltaX += nTempOff; |
| } |
| |
| rRenderContext.SetTextColor(rStyleSettings.GetButtonTextColor()); |
| rRenderContext.DrawText(Point(nDeltaX, nDeltaY), aMonthText); |
| rRenderContext.SetTextColor(rStyleSettings.GetWindowTextColor()); |
| |
| // display week bar |
| nDayX = nX + mnDaysOffX; |
| nDayY = nY + mnWeekDayOffY; |
| nDeltaY = nDayY + mnDayHeight; |
| |
| rRenderContext.SetLineColor(rStyleSettings.GetWindowTextColor()); |
| |
| const Point aStartPos(nDayX, nDeltaY); |
| |
| rRenderContext.DrawLine(aStartPos, Point(nDayX + (7 * mnDayWidth), nDeltaY)); |
| |
| KernArray aTmp; |
| |
| for (int k=0; k<7; ++k) |
| { |
| aTmp.push_back(mnDayOfWeekAry[k+1]); |
| } |
| |
| rRenderContext.DrawTextArray(Point(nDayX + mnDayOfWeekAry[0], nDayY), maDayOfWeekText, aTmp, {}, 0, aTmp.size()); |
| |
| // display days |
| const sal_uInt16 nDaysInMonth = aDate.GetDaysInMonth(); |
| nDayX = nX + mnDaysOffX; |
| nDayY = nY + mnDaysOffY; |
| sal_uInt16 nDayIndex = static_cast<sal_uInt16>(aDate.GetDayOfWeek()); |
| nDayIndex = (nDayIndex + (7 - static_cast<sal_uInt16>(eStartDay))) % 7; |
| |
| if (i == 0 && j == 0) |
| { |
| Date aTempDate = aDate; |
| aTempDate.AddDays( -nDayIndex ); |
| for (nDay = 0; nDay < nDayIndex; ++nDay) |
| { |
| nDeltaX = nDayX + (nDay * mnDayWidth); |
| ImplDrawDate(rRenderContext, nDeltaX, nDayY, nDay + aTempDate.GetDay(), |
| aTempDate.GetMonth(), aTempDate.GetYear(), |
| true, nToday); |
| } |
| } |
| |
| for (nDay = 1; nDay <= nDaysInMonth; nDay++) |
| { |
| nDeltaX = nDayX + (nDayIndex * mnDayWidth); |
| ImplDrawDate(rRenderContext, nDeltaX, nDayY, nDay, nMonth, nYear, |
| false, nToday); |
| if (nDayIndex == 6) |
| { |
| nDayIndex = 0; |
| nDayY += mnDayHeight; |
| } |
| else |
| { |
| nDayIndex++; |
| } |
| } |
| |
| if ((i == mnLines - 1) && (j == mnMonthPerLine - 1)) |
| { |
| sal_uInt16 nWeekDay = static_cast<sal_uInt16>(aDate.GetDayOfWeek()); |
| nWeekDay = (nWeekDay + (7 - static_cast<sal_uInt16>(eStartDay))) % 7; |
| const sal_uInt16 nDayCount = 42 - nDaysInMonth - nWeekDay; |
| Date aTempDate = aDate; |
| aTempDate.AddDays( nDaysInMonth ); |
| |
| for (nDay = 1; nDay <= nDayCount; ++nDay) |
| { |
| nDeltaX = nDayX + (nDayIndex * mnDayWidth); |
| ImplDrawDate(rRenderContext, nDeltaX, nDayY, nDay, |
| aTempDate.GetMonth(), aTempDate.GetYear(), |
| true, nToday); |
| if (nDayIndex == 6) |
| { |
| nDayIndex = 0; |
| nDayY += mnDayHeight; |
| } |
| else |
| { |
| nDayIndex++; |
| } |
| } |
| } |
| |
| aDate.AddDays( nDaysInMonth ); |
| nX += mnMonthWidth; |
| } |
| |
| nY += mnMonthHeight; |
| } |
| |
| // draw spin buttons |
| ImplDrawSpin(rRenderContext); |
| } |
| |
| void Calendar::ImplUpdateDate(const Date& rDate) |
| { |
| if (!IsReallyVisible() || !IsUpdateMode()) |
| return; |
| |
| tools::Rectangle aDateRect(GetDateRect(rDate)); |
| |
| if (!aDateRect.IsEmpty()) |
| Invalidate(aDateRect); |
| } |
| |
| void Calendar::ImplUpdateSelection( IntDateSet* pOld ) |
| { |
| IntDateSet* pNew = mpSelectTable.get(); |
| |
| for (auto const& nKey : *pOld) |
| { |
| if ( pNew->find(nKey) == pNew->end() ) |
| { |
| const Date aTempDate(nKey); |
| ImplUpdateDate(aTempDate); |
| } |
| } |
| |
| for (auto const& nKey : *pNew) |
| { |
| if ( pOld->find(nKey) == pOld->end() ) |
| { |
| const Date aTempDate(nKey); |
| ImplUpdateDate(aTempDate); |
| } |
| } |
| } |
| |
| void Calendar::ImplMouseSelect(const Date& rDate, sal_uInt16 nHitTest) |
| { |
| IntDateSet aOldSel( *mpSelectTable ); |
| const Date aOldDate = maCurDate; |
| Date aTempDate = rDate; |
| |
| if ( !(nHitTest & CALENDAR_HITTEST_DAY) ) |
| --aTempDate; |
| |
| if ( !(nHitTest & CALENDAR_HITTEST_DAY) ) |
| aTempDate = maOldCurDate; |
| |
| if ( aTempDate != maCurDate ) |
| { |
| maCurDate = aTempDate; |
| ImplCalendarSelectDate( mpSelectTable.get(), aOldDate, false ); |
| ImplCalendarSelectDate( mpSelectTable.get(), maCurDate, true ); |
| } |
| |
| const bool bNewSel = aOldSel != *mpSelectTable; |
| |
| if (maCurDate == aOldDate && !bNewSel) |
| return; |
| |
| HideFocus(); |
| |
| if ( bNewSel ) |
| ImplUpdateSelection( &aOldSel ); |
| |
| if ( !bNewSel || aOldSel.find( aOldDate.GetDate() ) == aOldSel.end() ) |
| ImplUpdateDate( aOldDate ); |
| |
| // assure focus rectangle is displayed again |
| if ( HasFocus() || !bNewSel |
| || mpSelectTable->find( maCurDate.GetDate() ) == mpSelectTable->end() ) |
| { |
| ImplUpdateDate( maCurDate ); |
| } |
| } |
| |
| void Calendar::ImplUpdate( bool bCalcNew ) |
| { |
| if (IsReallyVisible() && IsUpdateMode()) |
| { |
| if (bCalcNew && !mbCalc) |
| Invalidate(); |
| else if (!mbFormat && !mbCalc) |
| Invalidate(); |
| } |
| |
| if (bCalcNew) |
| mbCalc = true; |
| |
| mbFormat = true; |
| } |
| |
| void Calendar::ImplScrollCalendar( bool bPrev ) |
| { |
| Date aNewFirstMonth = GetFirstMonth(); |
| |
| if ( bPrev ) |
| { |
| --aNewFirstMonth; |
| aNewFirstMonth.AddDays( -(aNewFirstMonth.GetDaysInMonth()-1)); |
| } |
| else |
| { |
| aNewFirstMonth.AddDays( aNewFirstMonth.GetDaysInMonth()); |
| } |
| |
| SetFirstDate( aNewFirstMonth ); |
| } |
| |
| void Calendar::ImplShowMenu(const Point& rPos, const Date& rDate) |
| { |
| EndSelection(); |
| |
| Date aOldFirstDate = GetFirstMonth(); |
| ScopedVclPtrInstance<PopupMenu> aPopupMenu; |
| sal_uInt16 nMonthOff; |
| sal_uInt16 nCurItemId; |
| const sal_uInt16 nYear = rDate.GetYear() - 1; |
| sal_uInt16 i; |
| sal_uInt16 j; |
| sal_uInt16 nYearIdCount = 1000; |
| |
| nMonthOff = (rDate.GetYear()-aOldFirstDate.GetYear())*12; |
| |
| if ( aOldFirstDate.GetMonth() < rDate.GetMonth() ) |
| nMonthOff += rDate.GetMonth()-aOldFirstDate.GetMonth(); |
| else |
| nMonthOff -= aOldFirstDate.GetMonth()-rDate.GetMonth(); |
| |
| // construct menu (include years with different months) |
| for ( i = 0; i < MENU_YEAR_COUNT; i++ ) |
| { |
| VclPtrInstance<PopupMenu> pYearPopupMenu; |
| for ( j = 1; j <= 12; j++ ) |
| { |
| pYearPopupMenu->InsertItem( nYearIdCount+j, |
| maCalendarWrapper.getDisplayName( |
| i18n::CalendarDisplayIndex::MONTH, j-1, 1)); |
| } |
| |
| aPopupMenu->InsertItem( 10+i, OUString::number( nYear+i ) ); |
| aPopupMenu->SetPopupMenu( 10+i, pYearPopupMenu ); |
| nYearIdCount += 1000; |
| } |
| |
| mbMenuDown = true; |
| nCurItemId = aPopupMenu->Execute( this, rPos ); |
| mbMenuDown = false; |
| |
| if ( !nCurItemId ) |
| return; |
| |
| const sal_uInt16 nTempMonthOff = nMonthOff % 12; |
| const sal_uInt16 nTempYearOff = nMonthOff / 12; |
| sal_uInt16 nNewMonth = nCurItemId % 1000; |
| sal_uInt16 nNewYear = nYear+((nCurItemId-1000)/1000); |
| |
| if ( nTempMonthOff < nNewMonth ) |
| { |
| nNewMonth = nNewMonth - nTempMonthOff; |
| } |
| else |
| { |
| nNewYear--; |
| nNewMonth = 12-(nTempMonthOff-nNewMonth); |
| } |
| |
| nNewYear = nNewYear - nTempYearOff; |
| SetFirstDate( Date( 1, nNewMonth, nNewYear ) ); |
| } |
| |
| void Calendar::ImplTracking(const Point& rPos, bool bRepeat) |
| { |
| Date aTempDate = maCurDate; |
| const sal_uInt16 nHitTest = ImplDoHitTest(rPos, aTempDate); |
| |
| if (!mbSpinDown) |
| { |
| ImplMouseSelect( aTempDate, nHitTest ); |
| return; |
| } |
| |
| mbPrevIn = (nHitTest & CALENDAR_HITTEST_PREV) != 0; |
| mbNextIn = (nHitTest & CALENDAR_HITTEST_NEXT) != 0; |
| |
| if ( bRepeat && (mbPrevIn || mbNextIn) ) |
| ImplScrollCalendar( mbPrevIn ); |
| } |
| |
| void Calendar::ImplEndTracking( bool bCancel ) |
| { |
| const bool bSpinDown = mbSpinDown; |
| |
| mbDrag = false; |
| mbSpinDown = false; |
| mbPrevIn = false; |
| mbNextIn = false; |
| |
| if ( bCancel ) |
| { |
| if ( maOldFirstDate != maFirstDate ) |
| SetFirstDate( maOldFirstDate ); |
| |
| if ( !bSpinDown ) |
| { |
| IntDateSet aOldSel( *mpSelectTable ); |
| const Date aOldDate = maCurDate; |
| maCurDate = maOldCurDate; |
| *mpSelectTable = *mpOldSelectTable; |
| |
| HideFocus(); |
| ImplUpdateSelection( &aOldSel ); |
| |
| if ( aOldSel.find( aOldDate.GetDate() ) == aOldSel.end() ) |
| ImplUpdateDate( aOldDate ); |
| |
| // assure focus rectangle is displayed again |
| if ( HasFocus() || mpSelectTable->find( maCurDate.GetDate() ) == mpSelectTable->end() ) |
| ImplUpdateDate( maCurDate ); |
| } |
| } |
| |
| if ( bSpinDown ) |
| return; |
| |
| if ( !bCancel ) |
| { |
| // determine if we should scroll the visible area |
| if ( !mpSelectTable->empty() ) |
| { |
| const Date aFirstSelDate(*mpSelectTable->begin()); |
| const Date aLastSelDate(*mpSelectTable->rbegin()); |
| |
| if ( aLastSelDate < GetFirstMonth() ) |
| ImplScrollCalendar( true ); |
| else if ( GetLastMonth() < aFirstSelDate ) |
| ImplScrollCalendar( false ); |
| } |
| } |
| |
| if ( !bCancel && ((maCurDate != maOldCurDate) || (*mpOldSelectTable != *mpSelectTable)) ) |
| Select(); |
| |
| if ( (mnWinStyle & WB_TABSTOP) && !bCancel ) |
| GrabFocus(); |
| |
| mpOldSelectTable.reset(); |
| } |
| |
| void Calendar::MouseButtonDown(const MouseEvent& rMEvt) |
| { |
| if (!rMEvt.IsLeft() || mbMenuDown) |
| { |
| Control::MouseButtonDown( rMEvt ); |
| return; |
| } |
| |
| Date aTempDate = maCurDate; |
| const sal_uInt16 nHitTest = ImplDoHitTest(rMEvt.GetPosPixel(), aTempDate); |
| |
| if (!nHitTest) |
| return; |
| |
| if (nHitTest & CALENDAR_HITTEST_MONTHTITLE) |
| { |
| ImplShowMenu( rMEvt.GetPosPixel(), aTempDate ); |
| return; |
| } |
| |
| maOldFirstDate = maFirstDate; |
| |
| mbPrevIn = (nHitTest & CALENDAR_HITTEST_PREV) != 0; |
| mbNextIn = (nHitTest & CALENDAR_HITTEST_NEXT) != 0; |
| |
| if ( mbPrevIn || mbNextIn ) |
| { |
| mbSpinDown = true; |
| |
| ImplScrollCalendar( mbPrevIn ); |
| |
| // it should really read BUTTONREPEAT, therefore do not |
| // change it to SCROLLREPEAT, check with TH, |
| // why it could be different (71775) |
| StartTracking( StartTrackingFlags::ButtonRepeat ); |
| |
| return; |
| } |
| |
| if ( (rMEvt.GetClicks() != 2) || !(nHitTest & CALENDAR_HITTEST_DAY) ) |
| { |
| maOldCurDate = maCurDate; |
| mpOldSelectTable.reset(new IntDateSet( *mpSelectTable )); |
| |
| mbDrag = true; |
| StartTracking(); |
| |
| ImplMouseSelect( aTempDate, nHitTest ); |
| } |
| |
| if (rMEvt.GetClicks() == 2) |
| maActivateHdl.Call(this); |
| } |
| |
| void Calendar::Tracking(const TrackingEvent& rTEvt) |
| { |
| Point aMousePos = rTEvt.GetMouseEvent().GetPosPixel(); |
| |
| if ( rTEvt.IsTrackingEnded() ) |
| ImplEndTracking( rTEvt.IsTrackingCanceled() ); |
| else |
| ImplTracking( aMousePos, rTEvt.IsTrackingRepeat() ); |
| } |
| |
| void Calendar::KeyInput(const KeyEvent& rKEvt) |
| { |
| Date aNewDate = maCurDate; |
| |
| switch ( rKEvt.GetKeyCode().GetCode() ) |
| { |
| case KEY_HOME: |
| aNewDate.SetDay( 1 ); |
| break; |
| |
| case KEY_END: |
| aNewDate.SetDay( aNewDate.GetDaysInMonth() ); |
| break; |
| |
| case KEY_LEFT: |
| --aNewDate; |
| break; |
| |
| case KEY_RIGHT: |
| ++aNewDate; |
| break; |
| |
| case KEY_UP: |
| aNewDate.AddDays( -7 ); |
| break; |
| |
| case KEY_DOWN: |
| aNewDate.AddDays( 7 ); |
| break; |
| |
| case KEY_PAGEUP: |
| { |
| Date aTempDate = aNewDate; |
| aTempDate.AddDays( -(aNewDate.GetDay()+1) ); |
| aNewDate.AddDays( -aTempDate.GetDaysInMonth() ); |
| } |
| break; |
| |
| case KEY_PAGEDOWN: |
| aNewDate.AddDays( aNewDate.GetDaysInMonth() ); |
| break; |
| |
| case KEY_RETURN: |
| break; |
| |
| default: |
| Control::KeyInput( rKEvt ); |
| break; |
| } |
| |
| if ( aNewDate != maCurDate ) |
| { |
| SetCurDate( aNewDate ); |
| Select(); |
| } |
| |
| if (rKEvt.GetKeyCode().GetCode() == KEY_RETURN) |
| { |
| if (maActivateHdl.IsSet()) |
| maActivateHdl.Call(this); |
| else |
| Control::KeyInput(rKEvt); |
| } |
| } |
| |
| void Calendar::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle&) |
| { |
| ImplDraw(rRenderContext); |
| } |
| |
| void Calendar::GetFocus() |
| { |
| ImplUpdateDate( maCurDate ); |
| Control::GetFocus(); |
| } |
| |
| void Calendar::LoseFocus() |
| { |
| HideFocus(); |
| Control::LoseFocus(); |
| } |
| |
| void Calendar::Resize() |
| { |
| ImplUpdate( true ); |
| Control::Resize(); |
| } |
| |
| void Calendar::RequestHelp(const HelpEvent& rHEvt) |
| { |
| if (!(rHEvt.GetMode() & (HelpEventMode::QUICK | HelpEventMode::BALLOON))) |
| { |
| Control::RequestHelp(rHEvt); |
| return; |
| } |
| |
| Date aDate = maCurDate; |
| |
| if (!GetDate( ScreenToOutputPixel(rHEvt.GetMousePosPixel()), aDate )) |
| return; |
| |
| tools::Rectangle aDateRect = GetDateRect( aDate ); |
| Point aPt = OutputToScreenPixel( aDateRect.TopLeft() ); |
| aDateRect.SetLeft( aPt.X() ); |
| aDateRect.SetTop( aPt.Y() ); |
| aPt = OutputToScreenPixel( aDateRect.BottomRight() ); |
| aDateRect.SetRight( aPt.X() ); |
| aDateRect.SetBottom( aPt.Y() ); |
| |
| if (!(rHEvt.GetMode() & HelpEventMode::QUICK)) |
| return; |
| |
| maCalendarWrapper.setGregorianDateTime( DateTime(aDate) ); |
| const sal_uInt16 nWeek = static_cast<sal_uInt16>(maCalendarWrapper.getValue(i18n::CalendarFieldIndex::WEEK_OF_YEAR)); |
| const sal_uInt16 nMonth = aDate.GetMonth(); |
| OUString aStr = maDayText |
| + ": " |
| + OUString::number(aDate.GetDayOfYear()) |
| + " / " |
| + maWeekText |
| + ": " |
| + OUString::number(nWeek); |
| |
| // if year is not the same, add it |
| if ( (nMonth == 12) && (nWeek == 1) ) |
| aStr += ", " + OUString::number(aDate.GetNextYear()); |
| else if ( (nMonth == 1) && (nWeek > 50) ) |
| aStr += ", " + OUString::number(aDate.GetYear()-1); |
| |
| Help::ShowQuickHelp( this, aDateRect, aStr ); |
| } |
| |
| void Calendar::Command(const CommandEvent& rCEvt) |
| { |
| if ( rCEvt.GetCommand() == CommandEventId::ContextMenu ) |
| { |
| if ( rCEvt.IsMouseEvent() ) |
| { |
| Date aTempDate = maCurDate; |
| const sal_uInt16 nHitTest = ImplDoHitTest(rCEvt.GetMousePosPixel(), aTempDate); |
| |
| if ( nHitTest & CALENDAR_HITTEST_MONTHTITLE ) |
| { |
| ImplShowMenu( rCEvt.GetMousePosPixel(), aTempDate ); |
| return; |
| } |
| } |
| } |
| else if ( rCEvt.GetCommand() == CommandEventId::Wheel ) |
| { |
| CommandWheelData const* const pData = rCEvt.GetWheelData(); |
| |
| if ( pData->GetMode() == CommandWheelMode::SCROLL ) |
| { |
| tools::Long nNotchDelta = pData->GetNotchDelta(); |
| if ( nNotchDelta < 0 ) |
| { |
| while ( nNotchDelta < 0 ) |
| { |
| ImplScrollCalendar( true ); |
| nNotchDelta++; |
| } |
| } |
| else |
| { |
| while ( nNotchDelta > 0 ) |
| { |
| ImplScrollCalendar( false ); |
| nNotchDelta--; |
| } |
| } |
| |
| return; |
| } |
| } |
| |
| Control::Command( rCEvt ); |
| } |
| |
| void Calendar::StateChanged( StateChangedType nType ) |
| { |
| Control::StateChanged( nType ); |
| |
| if ( nType == StateChangedType::InitShow ) |
| ImplFormat(); |
| } |
| |
| void Calendar::DataChanged(const DataChangedEvent& rDCEvt) |
| { |
| Control::DataChanged( rDCEvt ); |
| |
| if ( (rDCEvt.GetType() == DataChangedEventType::FONTS) || |
| (rDCEvt.GetType() == DataChangedEventType::FONTSUBSTITUTION) || |
| ((rDCEvt.GetType() == DataChangedEventType::SETTINGS) && |
| (rDCEvt.GetFlags() & AllSettingsFlags::STYLE)) ) |
| { |
| ImplInitSettings(); |
| Invalidate(); |
| } |
| } |
| |
| void Calendar::Select() |
| { |
| maSelectHdl.Call( this ); |
| } |
| |
| Date Calendar::GetFirstSelectedDate() const |
| { |
| if ( !mpSelectTable->empty() ) |
| return Date( *mpSelectTable->begin() ); |
| |
| Date aDate( 0, 0, 0 ); |
| return aDate; |
| } |
| |
| void Calendar::SetCurDate(const Date& rNewDate) |
| { |
| if ( !rNewDate.IsValidAndGregorian() ) |
| return; |
| |
| if ( maCurDate == rNewDate ) |
| return; |
| |
| bool bUpdate = IsVisible() && IsUpdateMode(); |
| Date aOldDate = maCurDate; |
| maCurDate = rNewDate; |
| |
| ImplCalendarSelectDate( mpSelectTable.get(), aOldDate, false ); |
| ImplCalendarSelectDate( mpSelectTable.get(), maCurDate, true ); |
| |
| // shift actual date in the visible area |
| if ( mbFormat || (maCurDate < GetFirstMonth()) ) |
| { |
| SetFirstDate( maCurDate ); |
| } |
| else if ( maCurDate > GetLastMonth() ) |
| { |
| Date aTempDate = GetLastMonth(); |
| tools::Long nDateOff = maCurDate-aTempDate; |
| if ( nDateOff < 365 ) |
| { |
| Date aFirstDate = GetFirstMonth(); |
| aFirstDate.AddDays( aFirstDate.GetDaysInMonth() ); |
| ++aTempDate; |
| while ( nDateOff > aTempDate.GetDaysInMonth() ) |
| { |
| aFirstDate.AddDays( aFirstDate.GetDaysInMonth() ); |
| sal_Int32 nDaysInMonth = aTempDate.GetDaysInMonth(); |
| aTempDate.AddDays( nDaysInMonth ); |
| nDateOff -= nDaysInMonth; |
| } |
| SetFirstDate( aFirstDate ); |
| } |
| else |
| SetFirstDate( maCurDate ); |
| } |
| else |
| { |
| if ( bUpdate ) |
| { |
| HideFocus(); |
| ImplUpdateDate( aOldDate ); |
| ImplUpdateDate( maCurDate ); |
| } |
| } |
| } |
| |
| void Calendar::SetFirstDate(const Date& rNewFirstDate) |
| { |
| if ( maFirstDate != rNewFirstDate ) |
| { |
| maFirstDate = Date( 1, rNewFirstDate.GetMonth(), rNewFirstDate.GetYear() ); |
| ImplUpdate(); |
| } |
| } |
| |
| Date Calendar::GetFirstMonth() const |
| { |
| if (maFirstDate.GetDay() > 1) |
| { |
| if (maFirstDate.GetMonth() == 12) |
| return Date(1, 1, maFirstDate.GetNextYear()); |
| else |
| return Date(1, maFirstDate.GetMonth()+1, maFirstDate.GetYear()); |
| } |
| |
| return maFirstDate; |
| } |
| |
| Date Calendar::GetLastMonth() const |
| { |
| Date aDate = GetFirstMonth(); |
| const sal_uInt16 nMonthCount = GetMonthCount(); |
| |
| for ( sal_uInt16 i = 0; i < nMonthCount; i++ ) |
| { |
| aDate.AddDays( aDate.GetDaysInMonth() ); |
| } |
| |
| --aDate; |
| |
| return aDate; |
| } |
| |
| sal_uInt16 Calendar::GetMonthCount() const |
| { |
| if (mbFormat) |
| return 1; |
| |
| return static_cast<sal_uInt16>(mnMonthPerLine*mnLines); |
| } |
| |
| bool Calendar::GetDate(const Point& rPos, Date& rDate) const |
| { |
| Date aDate = maCurDate; |
| const sal_uInt16 nHitTest = ImplDoHitTest(rPos, aDate); |
| |
| if (nHitTest & CALENDAR_HITTEST_DAY) |
| { |
| rDate = aDate; |
| return true; |
| } |
| |
| return false; |
| } |
| |
| tools::Rectangle Calendar::GetDateRect(const Date& rDate) const |
| { |
| tools::Rectangle aRect; |
| |
| if ( mbFormat || (rDate < maFirstDate) || (rDate > (maFirstDate+mnDayCount)) ) |
| return aRect; |
| |
| tools::Long nX; |
| tools::Long nY; |
| sal_Int32 nDaysOff; |
| sal_uInt16 nDayIndex; |
| Date aDate = GetFirstMonth(); |
| |
| if ( rDate < aDate ) |
| { |
| aRect = GetDateRect( aDate ); |
| nDaysOff = aDate-rDate; |
| nX = nDaysOff*mnDayWidth; |
| aRect.AdjustLeft( -nX ); |
| aRect.AdjustRight( -nX ); |
| |
| return aRect; |
| } |
| else |
| { |
| Date aLastDate = GetLastMonth(); |
| |
| if ( rDate > aLastDate ) |
| { |
| sal_Int32 nWeekDay = static_cast<sal_Int32>(aLastDate.GetDayOfWeek()); |
| nWeekDay = (nWeekDay+(7-ImplGetWeekStart())) % 7; |
| aLastDate.AddDays( -nWeekDay ); |
| aRect = GetDateRect( aLastDate ); |
| nDaysOff = rDate-aLastDate; |
| nDayIndex = 0; |
| |
| for ( sal_Int32 i = 0; i <= nDaysOff; i++ ) |
| { |
| if ( aLastDate == rDate ) |
| { |
| aRect.AdjustLeft(nDayIndex*mnDayWidth ); |
| aRect.SetRight( aRect.Left()+mnDayWidth ); |
| return aRect; |
| } |
| |
| if ( nDayIndex == 6 ) |
| { |
| nDayIndex = 0; |
| aRect.AdjustTop(mnDayHeight ); |
| aRect.AdjustBottom(mnDayHeight ); |
| } |
| else |
| { |
| nDayIndex++; |
| } |
| |
| ++aLastDate; |
| } |
| } |
| } |
| |
| nY = 0; |
| |
| for ( tools::Long i = 0; i < mnLines; i++ ) |
| { |
| nX = 0; |
| |
| for ( tools::Long j = 0; j < mnMonthPerLine; j++ ) |
| { |
| sal_uInt16 nDaysInMonth = aDate.GetDaysInMonth(); |
| |
| // month is called |
| if ( (aDate.GetMonth() == rDate.GetMonth()) && |
| (aDate.GetYear() == rDate.GetYear()) ) |
| { |
| const tools::Long nDayX = nX + mnDaysOffX; |
| tools::Long nDayY = nY+mnDaysOffY; |
| nDayIndex = static_cast<sal_uInt16>(aDate.GetDayOfWeek()); |
| nDayIndex = (nDayIndex+(7-static_cast<sal_uInt16>(ImplGetWeekStart()))) % 7; |
| for ( sal_uInt16 nDay = 1; nDay <= nDaysInMonth; nDay++ ) |
| { |
| if ( nDay == rDate.GetDay() ) |
| { |
| aRect.SetLeft( nDayX + (nDayIndex*mnDayWidth) ); |
| aRect.SetTop( nDayY ); |
| aRect.SetRight( aRect.Left()+mnDayWidth ); |
| aRect.SetBottom( aRect.Top()+mnDayHeight ); |
| break; |
| } |
| |
| if ( nDayIndex == 6 ) |
| { |
| nDayIndex = 0; |
| nDayY += mnDayHeight; |
| } |
| else |
| { |
| nDayIndex++; |
| } |
| } |
| } |
| |
| aDate.AddDays( nDaysInMonth ); |
| nX += mnMonthWidth; |
| } |
| |
| nY += mnMonthHeight; |
| } |
| |
| return aRect; |
| } |
| |
| void Calendar::EndSelection() |
| { |
| if ( mbDrag || mbSpinDown ) |
| { |
| ReleaseMouse(); |
| |
| mbDrag = false; |
| mbSpinDown = false; |
| mbPrevIn = false; |
| mbNextIn = false; |
| } |
| } |
| |
| Size Calendar::CalcWindowSizePixel() const |
| { |
| Size aSize; |
| const tools::Long n99TextWidth = GetTextWidth(u"99"_ustr); |
| const tools::Long nTextHeight = GetTextHeight(); |
| |
| aSize.AdjustWidth((n99TextWidth+DAY_OFFX)*7); |
| aSize.AdjustWidth(MONTH_BORDERX*2 ); |
| |
| aSize.setHeight( nTextHeight + TITLE_OFFY + (TITLE_BORDERY*2) ); |
| aSize.AdjustHeight(nTextHeight + WEEKDAY_OFFY ); |
| aSize.AdjustHeight((nTextHeight+DAY_OFFY)*6); |
| aSize.AdjustHeight(MONTH_OFFY ); |
| |
| return aSize; |
| } |
| |
| Size Calendar::GetOptimalSize() const |
| { |
| return CalcWindowSizePixel(); |
| } |
| |
| void Calendar::DumpAsPropertyTree(tools::JsonWriter& rJsonWriter) |
| { |
| Control::DumpAsPropertyTree(rJsonWriter); |
| |
| const auto aDate = GetFirstSelectedDate(); |
| |
| rJsonWriter.put("type", "calendar"); |
| rJsonWriter.put("day", aDate.GetDay()); |
| rJsonWriter.put("month", aDate.GetMonth()); |
| rJsonWriter.put("year", aDate.GetYear()); |
| } |
| |
| namespace |
| { |
| class ImplCFieldFloat final |
| { |
| private: |
| std::unique_ptr<weld::Builder> mxBuilder; |
| std::unique_ptr<weld::Container> mxContainer; |
| std::unique_ptr<weld::Calendar> mxCalendar; |
| std::unique_ptr<weld::Button> mxTodayBtn; |
| std::unique_ptr<weld::Button> mxNoneBtn; |
| |
| public: |
| ImplCFieldFloat(vcl::Window* pContainer) |
| : mxBuilder(Application::CreateInterimBuilder(pContainer, u"svt/ui/calendar.ui"_ustr, false)) |
| , mxContainer(mxBuilder->weld_container(u"Calendar"_ustr)) |
| , mxCalendar(mxBuilder->weld_calendar(u"date"_ustr)) |
| , mxTodayBtn(mxBuilder->weld_button(u"today"_ustr)) |
| , mxNoneBtn(mxBuilder->weld_button(u"none"_ustr)) |
| { |
| } |
| |
| weld::Calendar* GetCalendar() { return mxCalendar.get(); } |
| weld::Button* EnableTodayBtn(bool bEnable); |
| weld::Button* EnableNoneBtn(bool bEnable); |
| |
| void GrabFocus() |
| { |
| mxCalendar->grab_focus(); |
| } |
| }; |
| } |
| |
| struct ImplCFieldFloatWin : public DropdownDockingWindow |
| { |
| explicit ImplCFieldFloatWin(vcl::Window* pParent); |
| virtual void dispose() override; |
| virtual ~ImplCFieldFloatWin() override; |
| virtual void GetFocus() override; |
| |
| std::unique_ptr<ImplCFieldFloat> mxWidget; |
| }; |
| |
| ImplCFieldFloatWin::ImplCFieldFloatWin(vcl::Window* pParent) |
| : DropdownDockingWindow(pParent) |
| { |
| setDeferredProperties(); |
| mxWidget.reset(new ImplCFieldFloat(m_xBox.get())); |
| } |
| |
| ImplCFieldFloatWin::~ImplCFieldFloatWin() |
| { |
| disposeOnce(); |
| } |
| |
| void ImplCFieldFloatWin::dispose() |
| { |
| mxWidget.reset(); |
| DropdownDockingWindow::dispose(); |
| } |
| |
| void ImplCFieldFloatWin::GetFocus() |
| { |
| DropdownDockingWindow::GetFocus(); |
| |
| if (!mxWidget) |
| return; |
| |
| mxWidget->GrabFocus(); |
| } |
| |
| weld::Button* ImplCFieldFloat::EnableTodayBtn(bool bEnable) |
| { |
| mxTodayBtn->set_visible(bEnable); |
| return bEnable ? mxTodayBtn.get() : nullptr; |
| } |
| |
| weld::Button* ImplCFieldFloat::EnableNoneBtn(bool bEnable) |
| { |
| mxNoneBtn->set_visible(bEnable); |
| return bEnable ? mxNoneBtn.get() : nullptr; |
| } |
| |
| CalendarField::CalendarField(vcl::Window* pParent, WinBits nWinStyle) |
| : DateField(pParent, nWinStyle) |
| , mpFloatWin(nullptr) |
| , mpTodayBtn(nullptr) |
| , mpNoneBtn(nullptr) |
| , mbToday(false) |
| , mbNone(false) |
| { |
| } |
| |
| CalendarField::~CalendarField() |
| { |
| disposeOnce(); |
| } |
| |
| void CalendarField::dispose() |
| { |
| mpTodayBtn = nullptr; |
| mpNoneBtn = nullptr; |
| mpFloatWin.disposeAndClear(); |
| DateField::dispose(); |
| } |
| |
| IMPL_LINK(CalendarField, ImplSelectHdl, weld::Calendar&, rCalendar, void) |
| { |
| Date aNewDate = rCalendar.get_date(); |
| |
| vcl::Window::GetDockingManager()->EndPopupMode(mpFloatWin); |
| mpFloatWin->EnableDocking(false); |
| |
| EndDropDown(); |
| GrabFocus(); |
| |
| if ( IsEmptyDate() || ( aNewDate != GetDate() ) ) |
| { |
| SetDate( aNewDate ); |
| SetModifyFlag(); |
| Modify(); |
| } |
| } |
| |
| IMPL_LINK(CalendarField, ImplClickHdl, weld::Button&, rBtn, void) |
| { |
| vcl::Window::GetDockingManager()->EndPopupMode(mpFloatWin); |
| mpFloatWin->EnableDocking(false); |
| EndDropDown(); |
| GrabFocus(); |
| |
| if (&rBtn == mpTodayBtn) |
| { |
| Date aToday( Date::SYSTEM ); |
| if ( (aToday != GetDate()) || IsEmptyDate() ) |
| { |
| SetDate( aToday ); |
| SetModifyFlag(); |
| Modify(); |
| } |
| } |
| else if (&rBtn == mpNoneBtn) |
| { |
| if ( !IsEmptyDate() ) |
| { |
| SetEmptyDate(); |
| SetModifyFlag(); |
| Modify(); |
| } |
| } |
| } |
| |
| IMPL_LINK_NOARG(CalendarField, ImplPopupModeEndHdl, FloatingWindow*, void) |
| { |
| EndDropDown(); |
| GrabFocus(); |
| } |
| |
| bool CalendarField::ShowDropDown( bool bShow ) |
| { |
| if (!bShow) |
| { |
| vcl::Window::GetDockingManager()->EndPopupMode(mpFloatWin); |
| |
| mpFloatWin->EnableDocking(false); |
| |
| EndDropDown(); |
| |
| return true; |
| } |
| |
| if ( !mpFloatWin ) |
| mpFloatWin = VclPtr<ImplCFieldFloatWin>::Create( this ); |
| |
| Date aDate = GetDate(); |
| |
| if ( IsEmptyDate() || !aDate.IsValidAndGregorian() ) |
| aDate = Date( Date::SYSTEM ); |
| |
| weld::Calendar* pCalendar = mpFloatWin->mxWidget->GetCalendar(); |
| pCalendar->set_date( aDate ); |
| pCalendar->connect_activated(LINK(this, CalendarField, ImplSelectHdl)); |
| |
| mpTodayBtn = mpFloatWin->mxWidget->EnableTodayBtn(mbToday); |
| mpNoneBtn = mpFloatWin->mxWidget->EnableNoneBtn(mbNone); |
| |
| if (mpTodayBtn) |
| mpTodayBtn->connect_clicked( LINK( this, CalendarField, ImplClickHdl ) ); |
| |
| if (mpNoneBtn) |
| mpNoneBtn->connect_clicked( LINK( this, CalendarField, ImplClickHdl ) ); |
| |
| const Point aPos(GetParent()->OutputToScreenPixel(GetPosPixel())); |
| |
| tools::Rectangle aRect(aPos, GetSizePixel()); |
| aRect.AdjustBottom( -1 ); |
| |
| DockingManager* pDockingManager = vcl::Window::GetDockingManager(); |
| mpFloatWin->EnableDocking(true); |
| pDockingManager->SetPopupModeEndHdl(mpFloatWin, LINK(this, CalendarField, ImplPopupModeEndHdl)); |
| pDockingManager->StartPopupMode(mpFloatWin, aRect, FloatWinPopupFlags::Down | FloatWinPopupFlags::GrabFocus); |
| |
| return true; |
| } |
| |
| void CalendarField::StateChanged( StateChangedType nStateChange ) |
| { |
| DateField::StateChanged( nStateChange ); |
| |
| if ( ( nStateChange == StateChangedType::Style ) && GetSubEdit() ) |
| { |
| const WinBits nAllAlignmentBits = (WB_LEFT | WB_CENTER | WB_RIGHT | WB_TOP | WB_VCENTER | WB_BOTTOM); |
| const WinBits nMyAlignment = GetStyle() & nAllAlignmentBits; |
| GetSubEdit()->SetStyle( ( GetSubEdit()->GetStyle() & ~nAllAlignmentBits ) | nMyAlignment ); |
| } |
| } |
| |
| // tdf#142783 consider the Edit and its DropDown as one compound control for the purpose of |
| // notification of loss of focus from the control |
| bool CalendarField::FocusWindowBelongsToControl(const vcl::Window* pFocusWin) const |
| { |
| return DateField::FocusWindowBelongsToControl(pFocusWin) || (mpFloatWin && mpFloatWin->ImplIsWindowOrChild(pFocusWin)); |
| } |
| |
| /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |