diff options
Diffstat (limited to 'vcl/source/control/calendar.cxx')
-rw-r--r-- | vcl/source/control/calendar.cxx | 1747 |
1 files changed, 1747 insertions, 0 deletions
diff --git a/vcl/source/control/calendar.cxx b/vcl/source/control/calendar.cxx new file mode 100644 index 0000000000..cce0bce63a --- /dev/null +++ b/vcl/source/control/calendar.cxx @@ -0,0 +1,1747 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice 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/builder.hxx> +#include <vcl/svapp.hxx> +#include <vcl/help.hxx> +#include <vcl/kernarray.hxx> +#include <vcl/menu.hxx> +#include <vcl/settings.hxx> +#include <vcl/event.hxx> +#include <vcl/toolkit/calendar.hxx> +#include <vcl/commandevent.hxx> +#include <vcl/dockwin.hxx> +#include <unotools/calendarwrapper.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 <sal/log.hxx> +#include <tools/json_writer.hxx> + +#include <calendar.hxx> +#include <svdata.hxx> +#include <strings.hrc> +#include <memory> + +#define DAY_OFFX 4 +#define DAY_OFFY 2 +#define MONTH_BORDERX 4 +#define MONTH_OFFY 3 +#define WEEKDAY_OFFY 3 +#define TITLE_OFFY 3 +#define TITLE_BORDERY 2 +#define SPIN_OFFX 4 +#define SPIN_OFFY TITLE_BORDERY + +#define CALENDAR_HITTEST_DAY (sal_uInt16(0x0001)) +#define CALENDAR_HITTEST_MONTHTITLE (sal_uInt16(0x0004)) +#define CALENDAR_HITTEST_PREV (sal_uInt16(0x0008)) +#define CALENDAR_HITTEST_NEXT (sal_uInt16(0x0010)) + +#define 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; + + OUString aGregorian( "gregorian"); + 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( "en", "US", "")); + } + + SetFirstDate( maCurDate ); + ImplCalendarSelectDate( mpSelectTable.get(), maCurDate, true ); + + // Sonstige Strings erzeugen + maDayText = VclResId(STR_SVT_CALENDAR_DAY); + maWeekText = VclResId(STR_SVT_CALENDAR_WEEK); + + // Tagestexte anlegen + 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; + 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 ) + { + Size aOutSize = GetOutputSizePixel(); + + if ( (aOutSize.Width() <= 1) || (aOutSize.Height() <= 1) ) + return; + + tools::Long n99TextWidth = GetTextWidth( "99" ); + 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; + 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 + 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. + 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 + + 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 + sal_Int16 nNewFirstYear = maFirstDate.GetYear(); + 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; + 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; + 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; + 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; + tools::Long nHeight = rRect.GetHeight(); + 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 + tools::Long nTextX = nX + (mnDayWidth - GetTextWidth(rDay)) - (DAY_OFFX / 2); + 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; + sal_Int32 nToday = Date(Date::SYSTEM).GetDate(); + sal_uInt16 nDay; + sal_uInt16 nMonth; + sal_Int16 nYear; + Date aDate = GetFirstMonth(); + 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; + } + 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()); + 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 + 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; + 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()) + { + 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() ) + { + Date aTempDate(nKey); + ImplUpdateDate(aTempDate); + } + } + + for (auto const& nKey : *pNew) + { + if ( pOld->find(nKey) == pOld->end() ) + { + Date aTempDate(nKey); + ImplUpdateDate(aTempDate); + } + } +} + +void Calendar::ImplMouseSelect( const Date& rDate, sal_uInt16 nHitTest ) +{ + IntDateSet aOldSel( *mpSelectTable ); + 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 ); + } + + bool bNewSel = aOldSel != *mpSelectTable; + if ( (maCurDate != aOldDate) || bNewSel ) + { + 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; + 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; + + sal_uInt16 nTempMonthOff = nMonthOff % 12; + 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; + sal_uInt16 nHitTest = ImplDoHitTest( rPos, aTempDate ); + + if ( mbSpinDown ) + { + mbPrevIn = (nHitTest & CALENDAR_HITTEST_PREV) != 0; + mbNextIn = (nHitTest & CALENDAR_HITTEST_NEXT) != 0; + + if ( bRepeat && (mbPrevIn || mbNextIn) ) + { + ImplScrollCalendar( mbPrevIn ); + } + } + else + ImplMouseSelect( aTempDate, nHitTest ); +} + +void Calendar::ImplEndTracking( bool bCancel ) +{ + bool bSelection = false; + bool bSpinDown = mbSpinDown; + + mbDrag = false; + mbSpinDown = false; + mbPrevIn = false; + mbNextIn = false; + + if ( bCancel ) + { + if ( maOldFirstDate != maFirstDate ) + SetFirstDate( maOldFirstDate ); + + if ( !bSpinDown ) + { + IntDateSet aOldSel( *mpSelectTable ); + 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() ) + { + Date aFirstSelDate( *mpSelectTable->begin() ); + Date aLastSelDate( *mpSelectTable->rbegin() ); + if ( aLastSelDate < GetFirstMonth() ) + ImplScrollCalendar( true ); + else if ( GetLastMonth() < aFirstSelDate ) + ImplScrollCalendar( false ); + } + } + + if ( !bCancel && ((maCurDate != maOldCurDate) || (*mpOldSelectTable != *mpSelectTable)) ) + Select(); + + if ( !bSelection && (mnWinStyle & WB_TABSTOP) && !bCancel ) + GrabFocus(); + + mpOldSelectTable.reset(); +} + +void Calendar::MouseButtonDown( const MouseEvent& rMEvt ) +{ + if ( rMEvt.IsLeft() && !mbMenuDown ) + { + Date aTempDate = maCurDate; + sal_uInt16 nHitTest = ImplDoHitTest( rMEvt.GetPosPixel(), aTempDate ); + if ( nHitTest ) + { + if ( nHitTest & CALENDAR_HITTEST_MONTHTITLE ) + ImplShowMenu( rMEvt.GetPosPixel(), aTempDate ); + else + { + 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 ); + } + else + { + 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); + } + } + } + + return; + } + + Control::MouseButtonDown( rMEvt ); +} + +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) ) + { + Date aDate = maCurDate; + if ( GetDate( ScreenToOutputPixel( rHEvt.GetMousePosPixel() ), aDate ) ) + { + 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 ) + { + maCalendarWrapper.setGregorianDateTime( aDate); + sal_uInt16 nWeek = static_cast<sal_uInt16>(maCalendarWrapper.getValue( i18n::CalendarFieldIndex::WEEK_OF_YEAR)); + 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 ); + return; + } + } + } + + Control::RequestHelp( rHEvt ); +} + +void Calendar::Command( const CommandEvent& rCEvt ) +{ + if ( rCEvt.GetCommand() == CommandEventId::ContextMenu ) + { + if ( rCEvt.IsMouseEvent() ) + { + Date aTempDate = maCurDate; + sal_uInt16 nHitTest = ImplDoHitTest( rCEvt.GetMousePosPixel(), aTempDate ); + if ( nHitTest & CALENDAR_HITTEST_MONTHTITLE ) + { + ImplShowMenu( rCEvt.GetMousePosPixel(), aTempDate ); + return; + } + } + } + else if ( rCEvt.GetCommand() == CommandEventId::Wheel ) + { + const CommandWheelData* 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() ); + else + { + 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() ); + } + else + return maFirstDate; +} + +Date Calendar::GetLastMonth() const +{ + Date aDate = GetFirstMonth(); + 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; + else + return static_cast<sal_uInt16>(mnMonthPerLine*mnLines); +} + +bool Calendar::GetDate( const Point& rPos, Date& rDate ) const +{ + Date aDate = maCurDate; + sal_uInt16 nHitTest = ImplDoHitTest( rPos, aDate ); + if ( nHitTest & CALENDAR_HITTEST_DAY ) + { + rDate = aDate; + return true; + } + else + 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()) ) + { + 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; + tools::Long n99TextWidth = GetTextWidth( "99" ); + 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); + + 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, "svt/ui/calendar.ui", false)) + , mxContainer(mxBuilder->weld_container("Calendar")) + , mxCalendar(mxBuilder->weld_calendar("date")) + , mxTodayBtn(mxBuilder->weld_button("today")) + , mxNoneBtn(mxBuilder->weld_button("none")) + { + } + + 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 ) + { + 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 ) ); + 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); + } + else + { + vcl::Window::GetDockingManager()->EndPopupMode(mpFloatWin); + mpFloatWin->EnableDocking(false); + EndDropDown(); + } + return true; +} + +void CalendarField::StateChanged( StateChangedType nStateChange ) +{ + DateField::StateChanged( nStateChange ); + + if ( ( nStateChange == StateChangedType::Style ) && GetSubEdit() ) + { + WinBits nAllAlignmentBits = ( WB_LEFT | WB_CENTER | WB_RIGHT | WB_TOP | WB_VCENTER | WB_BOTTOM ); + 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: */ |