bump product version to 7.6.3.2-android
[LibreOffice.git] / vcl / source / control / calendar.cxx
blob5f437ff0300e92d3680ac624c278c9473299d021
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <vcl/builder.hxx>
21 #include <vcl/svapp.hxx>
22 #include <vcl/help.hxx>
23 #include <vcl/kernarray.hxx>
24 #include <vcl/menu.hxx>
25 #include <vcl/settings.hxx>
26 #include <vcl/event.hxx>
27 #include <vcl/toolkit/calendar.hxx>
28 #include <vcl/commandevent.hxx>
29 #include <vcl/dockwin.hxx>
30 #include <unotools/calendarwrapper.hxx>
31 #include <unotools/localedatawrapper.hxx>
32 #include <com/sun/star/i18n/Weekdays.hpp>
33 #include <com/sun/star/i18n/CalendarDisplayIndex.hpp>
34 #include <com/sun/star/i18n/CalendarFieldIndex.hpp>
35 #include <sal/log.hxx>
37 #include <calendar.hxx>
38 #include <svdata.hxx>
39 #include <strings.hrc>
40 #include <memory>
42 #define DAY_OFFX 4
43 #define DAY_OFFY 2
44 #define MONTH_BORDERX 4
45 #define MONTH_OFFY 3
46 #define WEEKDAY_OFFY 3
47 #define TITLE_OFFY 3
48 #define TITLE_BORDERY 2
49 #define SPIN_OFFX 4
50 #define SPIN_OFFY TITLE_BORDERY
52 #define CALENDAR_HITTEST_DAY (sal_uInt16(0x0001))
53 #define CALENDAR_HITTEST_MONTHTITLE (sal_uInt16(0x0004))
54 #define CALENDAR_HITTEST_PREV (sal_uInt16(0x0008))
55 #define CALENDAR_HITTEST_NEXT (sal_uInt16(0x0010))
57 #define MENU_YEAR_COUNT 3
59 using namespace ::com::sun::star;
61 static void ImplCalendarSelectDate( IntDateSet* pTable, const Date& rDate, bool bSelect )
63 if ( bSelect )
64 pTable->insert( rDate.GetDate() );
65 else
66 pTable->erase( rDate.GetDate() );
71 void Calendar::ImplInit( WinBits nWinStyle )
73 mpSelectTable.reset(new IntDateSet);
74 mnDayCount = 0;
75 mnWinStyle = nWinStyle;
76 mnFirstYear = 0;
77 mnLastYear = 0;
78 mbCalc = true;
79 mbFormat = true;
80 mbDrag = false;
81 mbMenuDown = false;
82 mbSpinDown = false;
83 mbPrevIn = false;
84 mbNextIn = false;
86 OUString aGregorian( "gregorian");
87 maCalendarWrapper.loadCalendar( aGregorian,
88 Application::GetAppLocaleDataWrapper().getLanguageTag().getLocale());
89 if (maCalendarWrapper.getUniqueID() != aGregorian)
91 SAL_WARN( "vcl.control", "Calendar::ImplInit: No ``gregorian'' calendar available for locale ``"
92 << Application::GetAppLocaleDataWrapper().getLanguageTag().getBcp47()
93 << "'' and other calendars aren't supported. Using en-US fallback." );
95 /* If we ever wanted to support other calendars than Gregorian a lot of
96 * rewrite would be necessary to internally replace use of class Date
97 * with proper class CalendarWrapper methods, get rid of fixed 12
98 * months, fixed 7 days, ... */
99 maCalendarWrapper.loadCalendar( aGregorian, lang::Locale( "en", "US", ""));
102 SetFirstDate( maCurDate );
103 ImplCalendarSelectDate( mpSelectTable.get(), maCurDate, true );
105 // Sonstige Strings erzeugen
106 maDayText = VclResId(STR_SVT_CALENDAR_DAY);
107 maWeekText = VclResId(STR_SVT_CALENDAR_WEEK);
109 // Tagestexte anlegen
110 for (sal_Int32 i = 0; i < 31; ++i)
111 maDayTexts[i] = OUString::number(i+1);
113 ImplInitSettings();
116 void Calendar::ApplySettings(vcl::RenderContext& rRenderContext)
118 const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings();
119 maSelColor = rStyleSettings.GetHighlightTextColor();
120 SetPointFont(rRenderContext, rStyleSettings.GetToolFont());
121 rRenderContext.SetTextColor(rStyleSettings.GetFieldTextColor());
122 rRenderContext.SetBackground(Wallpaper(rStyleSettings.GetFieldColor()));
125 void Calendar::ImplInitSettings()
127 const StyleSettings& rStyleSettings = GetSettings().GetStyleSettings();
128 maSelColor = rStyleSettings.GetHighlightTextColor();
129 SetPointFont(*GetOutDev(), rStyleSettings.GetToolFont());
130 SetTextColor(rStyleSettings.GetFieldTextColor());
131 SetBackground(Wallpaper(rStyleSettings.GetFieldColor()));
134 Calendar::Calendar( vcl::Window* pParent, WinBits nWinStyle ) :
135 Control( pParent, nWinStyle & (WB_TABSTOP | WB_GROUP | WB_BORDER | WB_3DLOOK) ),
136 maCalendarWrapper( Application::GetAppLocaleDataWrapper().getComponentContext() ),
137 maOldFormatFirstDate( 0, 0, 1900 ),
138 maOldFormatLastDate( 0, 0, 1900 ),
139 maFirstDate( 0, 0, 1900 ),
140 maOldFirstDate( 0, 0, 1900 ),
141 maCurDate( Date::SYSTEM ),
142 maOldCurDate( 0, 0, 1900 )
144 ImplInit( nWinStyle );
147 Calendar::~Calendar()
149 disposeOnce();
152 void Calendar::dispose()
154 mpSelectTable.reset();
155 mpOldSelectTable.reset();
156 Control::dispose();
159 DayOfWeek Calendar::ImplGetWeekStart() const
161 // Map i18n::Weekdays to Date DayOfWeek
162 DayOfWeek eDay;
163 sal_Int16 nDay = maCalendarWrapper.getFirstDayOfWeek();
164 switch (nDay)
166 case i18n::Weekdays::SUNDAY :
167 eDay = SUNDAY;
168 break;
169 case i18n::Weekdays::MONDAY :
170 eDay = MONDAY;
171 break;
172 case i18n::Weekdays::TUESDAY :
173 eDay = TUESDAY;
174 break;
175 case i18n::Weekdays::WEDNESDAY :
176 eDay = WEDNESDAY;
177 break;
178 case i18n::Weekdays::THURSDAY :
179 eDay = THURSDAY;
180 break;
181 case i18n::Weekdays::FRIDAY :
182 eDay = FRIDAY;
183 break;
184 case i18n::Weekdays::SATURDAY :
185 eDay = SATURDAY;
186 break;
187 default:
188 SAL_WARN( "vcl.control", "Calendar::ImplGetWeekStart: broken i18n Gregorian calendar (getFirstDayOfWeek())");
189 eDay = SUNDAY;
191 return eDay;
194 void Calendar::ImplFormat()
196 if ( !mbFormat )
197 return;
199 if ( mbCalc )
201 Size aOutSize = GetOutputSizePixel();
203 if ( (aOutSize.Width() <= 1) || (aOutSize.Height() <= 1) )
204 return;
206 tools::Long n99TextWidth = GetTextWidth( "99" );
207 tools::Long nTextHeight = GetTextHeight();
209 // calculate width and x-position
210 mnDayWidth = n99TextWidth+DAY_OFFX;
211 mnMonthWidth = mnDayWidth*7;
212 mnMonthWidth += MONTH_BORDERX*2;
213 mnMonthPerLine = aOutSize.Width() / mnMonthWidth;
214 if ( !mnMonthPerLine )
215 mnMonthPerLine = 1;
216 tools::Long nOver = (aOutSize.Width()-(mnMonthPerLine*mnMonthWidth)) / mnMonthPerLine;
217 mnMonthWidth += nOver;
218 mnDaysOffX = MONTH_BORDERX;
219 mnDaysOffX += nOver/2;
221 // calculate height and y-position
222 mnDayHeight = nTextHeight + DAY_OFFY;
223 mnWeekDayOffY = nTextHeight + TITLE_OFFY + (TITLE_BORDERY*2);
224 mnDaysOffY = mnWeekDayOffY + nTextHeight + WEEKDAY_OFFY;
225 mnMonthHeight = (mnDayHeight*6) + mnDaysOffY;
226 mnMonthHeight += MONTH_OFFY;
227 mnLines = aOutSize.Height() / mnMonthHeight;
228 if ( !mnLines )
229 mnLines = 1;
230 mnMonthHeight += (aOutSize.Height()-(mnLines*mnMonthHeight)) / mnLines;
232 // calculate spinfields
233 tools::Long nSpinSize = nTextHeight+TITLE_BORDERY-SPIN_OFFY;
234 maPrevRect.SetLeft( SPIN_OFFX );
235 maPrevRect.SetTop( SPIN_OFFY );
236 maPrevRect.SetRight( maPrevRect.Left()+nSpinSize );
237 maPrevRect.SetBottom( maPrevRect.Top()+nSpinSize );
238 maNextRect.SetLeft( aOutSize.Width()-SPIN_OFFX-nSpinSize-1 );
239 maNextRect.SetTop( SPIN_OFFY );
240 maNextRect.SetRight( maNextRect.Left()+nSpinSize );
241 maNextRect.SetBottom( maNextRect.Top()+nSpinSize );
243 // Calculate DayOfWeekText (gets displayed in a narrow font)
244 maDayOfWeekText.clear();
245 tools::Long nStartOffX = 0;
246 sal_Int16 nDay = maCalendarWrapper.getFirstDayOfWeek();
247 for ( sal_Int16 nDayOfWeek = 0; nDayOfWeek < 7; nDayOfWeek++ )
249 // Use narrow name.
250 OUString aDayOfWeek( maCalendarWrapper.getDisplayName(
251 i18n::CalendarDisplayIndex::DAY, nDay, 2));
252 tools::Long nOffX = (mnDayWidth-GetTextWidth( aDayOfWeek ))/2;
253 if ( !nDayOfWeek )
254 nStartOffX = nOffX;
255 else
256 nOffX -= nStartOffX;
257 nOffX += nDayOfWeek * mnDayWidth;
258 mnDayOfWeekAry[nDayOfWeek] = nOffX;
259 maDayOfWeekText += aDayOfWeek;
260 nDay++;
261 nDay %= 7;
264 // header position for the last day of week
265 mnDayOfWeekAry[7] = mnMonthWidth;
267 mbCalc = false;
270 // calculate number of days
272 DayOfWeek eStartDay = ImplGetWeekStart();
274 sal_uInt16 nWeekDay;
275 Date aTempDate = GetFirstMonth();
276 maFirstDate = aTempDate;
277 nWeekDay = static_cast<sal_uInt16>(aTempDate.GetDayOfWeek());
278 nWeekDay = (nWeekDay+(7-static_cast<sal_uInt16>(eStartDay))) % 7;
279 maFirstDate.AddDays( -nWeekDay );
280 mnDayCount = nWeekDay;
281 sal_uInt16 nDaysInMonth;
282 sal_uInt16 nMonthCount = static_cast<sal_uInt16>(mnMonthPerLine*mnLines);
283 for ( sal_uInt16 i = 0; i < nMonthCount; i++ )
285 nDaysInMonth = aTempDate.GetDaysInMonth();
286 mnDayCount += nDaysInMonth;
287 aTempDate.AddDays( nDaysInMonth );
289 Date aTempDate2 = aTempDate;
290 --aTempDate2;
291 nDaysInMonth = aTempDate2.GetDaysInMonth();
292 aTempDate2.AddDays( -(nDaysInMonth-1) );
293 nWeekDay = static_cast<sal_uInt16>(aTempDate2.GetDayOfWeek());
294 nWeekDay = (nWeekDay+(7-static_cast<sal_uInt16>(eStartDay))) % 7;
295 mnDayCount += 42-nDaysInMonth-nWeekDay;
297 // determine colours
298 maOtherColor = COL_LIGHTGRAY;
299 if ( maOtherColor.IsRGBEqual( GetBackground().GetColor() ) )
300 maOtherColor = COL_GRAY;
302 Date aLastDate = GetLastDate();
303 if ( (maOldFormatLastDate != aLastDate) ||
304 (maOldFormatFirstDate != maFirstDate) )
306 maOldFormatFirstDate = maFirstDate;
307 maOldFormatLastDate = aLastDate;
310 // get DateInfo
311 sal_Int16 nNewFirstYear = maFirstDate.GetYear();
312 sal_Int16 nNewLastYear = GetLastDate().GetYear();
313 if ( mnFirstYear )
315 if ( nNewFirstYear < mnFirstYear )
317 mnFirstYear = nNewFirstYear;
319 if ( nNewLastYear > mnLastYear )
321 mnLastYear = nNewLastYear;
324 else
326 mnFirstYear = nNewFirstYear;
327 mnLastYear = nNewLastYear;
330 mbFormat = false;
333 sal_uInt16 Calendar::ImplDoHitTest( const Point& rPos, Date& rDate ) const
335 if ( mbFormat )
336 return 0;
338 if ( maPrevRect.Contains( rPos ) )
339 return CALENDAR_HITTEST_PREV;
340 else if ( maNextRect.Contains( rPos ) )
341 return CALENDAR_HITTEST_NEXT;
343 tools::Long nY;
344 tools::Long nOffX;
345 sal_Int32 nDay;
346 DayOfWeek eStartDay = ImplGetWeekStart();
348 rDate = GetFirstMonth();
349 nY = 0;
350 for ( tools::Long i = 0; i < mnLines; i++ )
352 if ( rPos.Y() < nY )
353 return 0;
355 tools::Long nX = 0;
356 tools::Long nYMonth = nY+mnMonthHeight;
357 for ( tools::Long j = 0; j < mnMonthPerLine; j++ )
359 if ( (rPos.X() < nX) && (rPos.Y() < nYMonth) )
360 return 0;
362 sal_uInt16 nDaysInMonth = rDate.GetDaysInMonth();
364 // matching month was found
365 if ( (rPos.X() > nX) && (rPos.Y() < nYMonth) &&
366 (rPos.X() < nX+mnMonthWidth) )
368 if ( rPos.Y() < (nY+(TITLE_BORDERY*2)+mnDayHeight))
369 return CALENDAR_HITTEST_MONTHTITLE;
370 else
372 tools::Long nDayX = nX+mnDaysOffX;
373 tools::Long nDayY = nY+mnDaysOffY;
374 if ( rPos.Y() < nDayY )
375 return 0;
376 sal_Int32 nDayIndex = static_cast<sal_Int32>(rDate.GetDayOfWeek());
377 nDayIndex = (nDayIndex+(7-static_cast<sal_Int32>(eStartDay))) % 7;
378 if ( (i == 0) && (j == 0) )
380 Date aTempDate = rDate;
381 aTempDate.AddDays( -nDayIndex );
382 for ( nDay = 0; nDay < nDayIndex; nDay++ )
384 nOffX = nDayX + (nDay*mnDayWidth);
385 if ( (rPos.Y() >= nDayY) && (rPos.Y() < nDayY+mnDayHeight) &&
386 (rPos.X() >= nOffX) && (rPos.X() < nOffX+mnDayWidth) )
388 rDate = aTempDate;
389 rDate.AddDays( nDay );
390 return CALENDAR_HITTEST_DAY;
394 for ( nDay = 1; nDay <= nDaysInMonth; nDay++ )
396 if ( rPos.Y() < nDayY )
398 rDate.AddDays( nDayIndex );
399 return 0;
401 nOffX = nDayX + (nDayIndex*mnDayWidth);
402 if ( (rPos.Y() >= nDayY) && (rPos.Y() < nDayY+mnDayHeight) &&
403 (rPos.X() >= nOffX) && (rPos.X() < nOffX+mnDayWidth) )
405 rDate.AddDays( nDay-1 );
406 return CALENDAR_HITTEST_DAY;
408 if ( nDayIndex == 6 )
410 nDayIndex = 0;
411 nDayY += mnDayHeight;
413 else
414 nDayIndex++;
416 if ( (i == mnLines-1) && (j == mnMonthPerLine-1) )
418 sal_uInt16 nWeekDay = static_cast<sal_uInt16>(rDate.GetDayOfWeek());
419 nWeekDay = (nWeekDay+(7-static_cast<sal_uInt16>(eStartDay))) % 7;
420 sal_Int32 nDayCount = 42-nDaysInMonth-nWeekDay;
421 Date aTempDate = rDate;
422 aTempDate.AddDays( nDaysInMonth );
423 for ( nDay = 1; nDay <= nDayCount; nDay++ )
425 if ( rPos.Y() < nDayY )
427 rDate.AddDays( nDayIndex );
428 return 0;
430 nOffX = nDayX + (nDayIndex*mnDayWidth);
431 if ( (rPos.Y() >= nDayY) && (rPos.Y() < nDayY+mnDayHeight) &&
432 (rPos.X() >= nOffX) && (rPos.X() < nOffX+mnDayWidth) )
434 rDate = aTempDate;
435 rDate.AddDays( nDay-1 );
436 return CALENDAR_HITTEST_DAY;
438 if ( nDayIndex == 6 )
440 nDayIndex = 0;
441 nDayY += mnDayHeight;
443 else
444 nDayIndex++;
450 rDate.AddDays( nDaysInMonth );
451 nX += mnMonthWidth;
454 nY += mnMonthHeight;
457 return 0;
460 namespace
463 void ImplDrawSpinArrow(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect, bool bPrev)
465 tools::Long i;
466 tools::Long n;
467 tools::Long nLines;
468 tools::Long nHeight = rRect.GetHeight();
469 tools::Long nWidth = rRect.GetWidth();
470 if (nWidth < nHeight)
471 n = nWidth;
472 else
473 n = nHeight;
474 if (!(n & 0x01))
475 n--;
476 nLines = n/2;
478 tools::Rectangle aRect(Point( rRect.Left() + (nWidth / 2) - (nLines / 2),
479 rRect.Top() + (nHeight / 2) ),
480 Size(1, 1));
481 if (!bPrev)
483 aRect.AdjustLeft(nLines );
484 aRect.AdjustRight(nLines );
487 rRenderContext.DrawRect(aRect);
488 for (i = 0; i < nLines; i++)
490 if (bPrev)
492 aRect.AdjustLeft( 1 );
493 aRect.AdjustRight( 1 );
495 else
497 aRect.AdjustLeft( -1 );
498 aRect.AdjustRight( -1 );
500 aRect.AdjustTop( -1 );
501 aRect.AdjustBottom( 1 );
502 rRenderContext.DrawRect(aRect);
506 } //end anonymous namespace
508 void Calendar::ImplDrawSpin(vcl::RenderContext& rRenderContext )
510 rRenderContext.SetLineColor();
511 rRenderContext.SetFillColor(rRenderContext.GetSettings().GetStyleSettings().GetButtonTextColor());
512 tools::Rectangle aOutRect = maPrevRect;
513 aOutRect.AdjustLeft(3 );
514 aOutRect.AdjustTop(3 );
515 aOutRect.AdjustRight( -3 );
516 aOutRect.AdjustBottom( -3 );
517 ImplDrawSpinArrow(rRenderContext, aOutRect, true);
518 aOutRect = maNextRect;
519 aOutRect.AdjustLeft(3 );
520 aOutRect.AdjustTop(3 );
521 aOutRect.AdjustRight( -3 );
522 aOutRect.AdjustBottom( -3 );
523 ImplDrawSpinArrow(rRenderContext, aOutRect, false);
526 void Calendar::ImplDrawDate(vcl::RenderContext& rRenderContext,
527 tools::Long nX, tools::Long nY,
528 sal_uInt16 nDay, sal_uInt16 nMonth, sal_Int16 nYear,
529 bool bOther, sal_Int32 nToday )
531 Color const * pTextColor = nullptr;
532 const OUString& rDay = maDayTexts[(nDay - 1) % std::size(maDayTexts)];
533 tools::Rectangle aDateRect(nX, nY, nX + mnDayWidth - 1, nY + mnDayHeight - 1);
535 bool bSel = false;
536 bool bFocus = false;
537 // actual day
538 if ((nDay == maCurDate.GetDay()) &&
539 (nMonth == maCurDate.GetMonth()) &&
540 (nYear == maCurDate.GetYear()))
542 bFocus = true;
544 if (mpSelectTable)
546 if (mpSelectTable->find(Date(nDay, nMonth, nYear).GetDate()) != mpSelectTable->end())
547 bSel = true;
550 // get textcolour
551 if (bSel)
552 pTextColor = &maSelColor;
553 else if (bOther)
554 pTextColor = &maOtherColor;
556 if (bFocus)
557 HideFocus();
559 // display background
560 const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings();
561 if (bSel)
563 rRenderContext.SetLineColor();
564 rRenderContext.SetFillColor(rStyleSettings.GetHighlightColor());
565 rRenderContext.DrawRect(aDateRect);
568 // display text
569 tools::Long nTextX = nX + (mnDayWidth - GetTextWidth(rDay)) - (DAY_OFFX / 2);
570 tools::Long nTextY = nY + (mnDayHeight - GetTextHeight()) / 2;
571 if (pTextColor)
573 Color aOldColor = rRenderContext.GetTextColor();
574 rRenderContext.SetTextColor(*pTextColor);
575 rRenderContext.DrawText(Point(nTextX, nTextY), rDay);
576 rRenderContext.SetTextColor(aOldColor);
578 else
579 rRenderContext.DrawText(Point(nTextX, nTextY), rDay);
581 // today
582 Date aTodayDate(maCurDate);
583 if (nToday)
584 aTodayDate.SetDate(nToday);
585 else
586 aTodayDate = Date(Date::SYSTEM);
587 if ((nDay == aTodayDate.GetDay()) &&
588 (nMonth == aTodayDate.GetMonth()) &&
589 (nYear == aTodayDate.GetYear()))
591 rRenderContext.SetLineColor(rStyleSettings.GetWindowTextColor());
592 rRenderContext.SetFillColor();
593 rRenderContext.DrawRect(aDateRect);
596 // if needed do FocusRect
597 if (bFocus && HasFocus())
598 ShowFocus(aDateRect);
601 void Calendar::ImplDraw(vcl::RenderContext& rRenderContext)
603 ImplFormat();
605 const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings();
606 Size aOutSize(GetOutputSizePixel());
607 tools::Long i;
608 tools::Long j;
609 tools::Long nY;
610 tools::Long nDeltaX;
611 tools::Long nDeltaY;
612 tools::Long nDayX;
613 tools::Long nDayY;
614 sal_Int32 nToday = Date(Date::SYSTEM).GetDate();
615 sal_uInt16 nDay;
616 sal_uInt16 nMonth;
617 sal_Int16 nYear;
618 Date aDate = GetFirstMonth();
619 DayOfWeek eStartDay = ImplGetWeekStart();
621 HideFocus();
623 nY = 0;
624 for (i = 0; i < mnLines; i++)
626 // display title bar
627 rRenderContext.SetLineColor();
628 rRenderContext.SetFillColor(rStyleSettings.GetFaceColor());
629 tools::Rectangle aTitleRect(0, nY, aOutSize.Width() - 1, nY + mnDayHeight - DAY_OFFY + TITLE_BORDERY * 2);
630 rRenderContext.DrawRect(aTitleRect);
631 Point aTopLeft1(aTitleRect.Left(), aTitleRect.Top());
632 Point aTopLeft2(aTitleRect.Left(), aTitleRect.Top() + 1);
633 Point aBottomRight1(aTitleRect.Right(), aTitleRect.Bottom());
634 Point aBottomRight2(aTitleRect.Right(), aTitleRect.Bottom() - 1);
635 rRenderContext.SetLineColor(rStyleSettings.GetDarkShadowColor());
636 rRenderContext.DrawLine(aTopLeft1, Point(aBottomRight1.X(), aTopLeft1.Y()));
637 rRenderContext.SetLineColor(rStyleSettings.GetLightColor() );
638 rRenderContext.DrawLine(aTopLeft2, Point(aBottomRight2.X(), aTopLeft2.Y()));
639 rRenderContext.DrawLine(aTopLeft2, Point(aTopLeft2.X(), aBottomRight2.Y()));
640 rRenderContext.SetLineColor(rStyleSettings.GetShadowColor() );
641 rRenderContext.DrawLine(Point(aTopLeft2.X(), aBottomRight2.Y()), aBottomRight2);
642 rRenderContext.DrawLine(Point(aBottomRight2.X(), aTopLeft2.Y()), aBottomRight2);
643 rRenderContext.SetLineColor(rStyleSettings.GetDarkShadowColor());
644 rRenderContext.DrawLine(Point(aTopLeft1.X(), aBottomRight1.Y()), aBottomRight1);
645 Point aSepPos1(0, aTitleRect.Top() + TITLE_BORDERY);
646 Point aSepPos2(0, aTitleRect.Bottom() - TITLE_BORDERY);
647 for (j = 0; j < mnMonthPerLine-1; j++)
649 aSepPos1.AdjustX(mnMonthWidth-1 );
650 aSepPos2.setX( aSepPos1.X() );
651 rRenderContext.SetLineColor(rStyleSettings.GetShadowColor());
652 rRenderContext.DrawLine(aSepPos1, aSepPos2);
653 aSepPos1.AdjustX( 1 );
654 aSepPos2.setX( aSepPos1.X() );
655 rRenderContext.SetLineColor(rStyleSettings.GetLightColor());
656 rRenderContext.DrawLine(aSepPos1, aSepPos2);
659 tools::Long nX = 0;
660 for (j = 0; j < mnMonthPerLine; j++)
662 nMonth = aDate.GetMonth();
663 nYear = aDate.GetYear();
665 // display month in title bar
666 nDeltaX = nX;
667 nDeltaY = nY + TITLE_BORDERY;
668 OUString aMonthText = maCalendarWrapper.getDisplayName(i18n::CalendarDisplayIndex::MONTH, nMonth - 1, 1)
669 + " "
670 + OUString::number(nYear);
671 tools::Long nMonthTextWidth = rRenderContext.GetTextWidth(aMonthText);
672 tools::Long nMonthOffX1 = 0;
673 tools::Long nMonthOffX2 = 0;
674 if (i == 0)
676 if (j == 0)
677 nMonthOffX1 = maPrevRect.Right() + 1;
678 if (j == mnMonthPerLine - 1)
679 nMonthOffX2 = aOutSize.Width() - maNextRect.Left() + 1;
681 tools::Long nMaxMonthWidth = mnMonthWidth - nMonthOffX1 - nMonthOffX2 - 4;
682 if (nMonthTextWidth > nMaxMonthWidth)
684 // Abbreviated month name.
685 aMonthText = maCalendarWrapper.getDisplayName(i18n::CalendarDisplayIndex::MONTH, nMonth - 1, 0)
686 + " "
687 + OUString::number(nYear);
688 nMonthTextWidth = rRenderContext.GetTextWidth(aMonthText);
690 tools::Long nTempOff = (mnMonthWidth - nMonthTextWidth + 1) / 2;
691 if (nTempOff < nMonthOffX1)
692 nDeltaX += nMonthOffX1 + 1;
693 else
695 if (nTempOff + nMonthTextWidth > mnMonthWidth - nMonthOffX2)
696 nDeltaX += mnMonthWidth - nMonthOffX2 - nMonthTextWidth;
697 else
698 nDeltaX += nTempOff;
700 rRenderContext.SetTextColor(rStyleSettings.GetButtonTextColor());
701 rRenderContext.DrawText(Point(nDeltaX, nDeltaY), aMonthText);
702 rRenderContext.SetTextColor(rStyleSettings.GetWindowTextColor());
704 // display week bar
705 nDayX = nX + mnDaysOffX;
706 nDayY = nY + mnWeekDayOffY;
707 nDeltaY = nDayY + mnDayHeight;
708 rRenderContext.SetLineColor(rStyleSettings.GetWindowTextColor());
709 Point aStartPos(nDayX, nDeltaY);
710 rRenderContext.DrawLine(aStartPos, Point(nDayX + (7 * mnDayWidth), nDeltaY));
711 KernArray aTmp;
712 for (int k=0; k<7; ++k)
713 aTmp.push_back(mnDayOfWeekAry[k+1]);
714 rRenderContext.DrawTextArray(Point(nDayX + mnDayOfWeekAry[0], nDayY), maDayOfWeekText, aTmp, {}, 0, aTmp.size());
716 // display days
717 sal_uInt16 nDaysInMonth = aDate.GetDaysInMonth();
718 nDayX = nX + mnDaysOffX;
719 nDayY = nY + mnDaysOffY;
720 sal_uInt16 nDayIndex = static_cast<sal_uInt16>(aDate.GetDayOfWeek());
721 nDayIndex = (nDayIndex + (7 - static_cast<sal_uInt16>(eStartDay))) % 7;
722 if (i == 0 && j == 0)
724 Date aTempDate = aDate;
725 aTempDate.AddDays( -nDayIndex );
726 for (nDay = 0; nDay < nDayIndex; ++nDay)
728 nDeltaX = nDayX + (nDay * mnDayWidth);
729 ImplDrawDate(rRenderContext, nDeltaX, nDayY, nDay + aTempDate.GetDay(),
730 aTempDate.GetMonth(), aTempDate.GetYear(),
731 true, nToday);
734 for (nDay = 1; nDay <= nDaysInMonth; nDay++)
736 nDeltaX = nDayX + (nDayIndex * mnDayWidth);
737 ImplDrawDate(rRenderContext, nDeltaX, nDayY, nDay, nMonth, nYear,
738 false, nToday);
739 if (nDayIndex == 6)
741 nDayIndex = 0;
742 nDayY += mnDayHeight;
744 else
745 nDayIndex++;
747 if ((i == mnLines - 1) && (j == mnMonthPerLine - 1))
749 sal_uInt16 nWeekDay = static_cast<sal_uInt16>(aDate.GetDayOfWeek());
750 nWeekDay = (nWeekDay + (7 - static_cast<sal_uInt16>(eStartDay))) % 7;
751 sal_uInt16 nDayCount = 42 - nDaysInMonth - nWeekDay;
752 Date aTempDate = aDate;
753 aTempDate.AddDays( nDaysInMonth );
754 for (nDay = 1; nDay <= nDayCount; ++nDay)
756 nDeltaX = nDayX + (nDayIndex * mnDayWidth);
757 ImplDrawDate(rRenderContext, nDeltaX, nDayY, nDay,
758 aTempDate.GetMonth(), aTempDate.GetYear(),
759 true, nToday);
760 if (nDayIndex == 6)
762 nDayIndex = 0;
763 nDayY += mnDayHeight;
765 else
766 nDayIndex++;
770 aDate.AddDays( nDaysInMonth );
771 nX += mnMonthWidth;
774 nY += mnMonthHeight;
777 // draw spin buttons
778 ImplDrawSpin(rRenderContext);
781 void Calendar::ImplUpdateDate( const Date& rDate )
783 if (IsReallyVisible() && IsUpdateMode())
785 tools::Rectangle aDateRect(GetDateRect(rDate));
786 if (!aDateRect.IsEmpty())
788 Invalidate(aDateRect);
793 void Calendar::ImplUpdateSelection( IntDateSet* pOld )
795 IntDateSet* pNew = mpSelectTable.get();
797 for (auto const& nKey : *pOld)
799 if ( pNew->find(nKey) == pNew->end() )
801 Date aTempDate(nKey);
802 ImplUpdateDate(aTempDate);
806 for (auto const& nKey : *pNew)
808 if ( pOld->find(nKey) == pOld->end() )
810 Date aTempDate(nKey);
811 ImplUpdateDate(aTempDate);
816 void Calendar::ImplMouseSelect( const Date& rDate, sal_uInt16 nHitTest )
818 IntDateSet aOldSel( *mpSelectTable );
819 Date aOldDate = maCurDate;
820 Date aTempDate = rDate;
822 if ( !(nHitTest & CALENDAR_HITTEST_DAY) )
823 --aTempDate;
825 if ( !(nHitTest & CALENDAR_HITTEST_DAY) )
826 aTempDate = maOldCurDate;
827 if ( aTempDate != maCurDate )
829 maCurDate = aTempDate;
830 ImplCalendarSelectDate( mpSelectTable.get(), aOldDate, false );
831 ImplCalendarSelectDate( mpSelectTable.get(), maCurDate, true );
834 bool bNewSel = aOldSel != *mpSelectTable;
835 if ( (maCurDate != aOldDate) || bNewSel )
837 HideFocus();
838 if ( bNewSel )
839 ImplUpdateSelection( &aOldSel );
840 if ( !bNewSel || aOldSel.find( aOldDate.GetDate() ) == aOldSel.end() )
841 ImplUpdateDate( aOldDate );
842 // assure focus rectangle is displayed again
843 if ( HasFocus() || !bNewSel
844 || mpSelectTable->find( maCurDate.GetDate() ) == mpSelectTable->end() )
845 ImplUpdateDate( maCurDate );
849 void Calendar::ImplUpdate( bool bCalcNew )
851 if (IsReallyVisible() && IsUpdateMode())
853 if (bCalcNew && !mbCalc)
855 Invalidate();
857 else if (!mbFormat && !mbCalc)
859 Invalidate();
863 if (bCalcNew)
864 mbCalc = true;
865 mbFormat = true;
868 void Calendar::ImplScrollCalendar( bool bPrev )
870 Date aNewFirstMonth = GetFirstMonth();
871 if ( bPrev )
873 --aNewFirstMonth;
874 aNewFirstMonth.AddDays( -(aNewFirstMonth.GetDaysInMonth()-1));
876 else
877 aNewFirstMonth.AddDays( aNewFirstMonth.GetDaysInMonth());
878 SetFirstDate( aNewFirstMonth );
881 void Calendar::ImplShowMenu( const Point& rPos, const Date& rDate )
883 EndSelection();
885 Date aOldFirstDate = GetFirstMonth();
886 ScopedVclPtrInstance<PopupMenu> aPopupMenu;
887 sal_uInt16 nMonthOff;
888 sal_uInt16 nCurItemId;
889 sal_uInt16 nYear = rDate.GetYear()-1;
890 sal_uInt16 i;
891 sal_uInt16 j;
892 sal_uInt16 nYearIdCount = 1000;
894 nMonthOff = (rDate.GetYear()-aOldFirstDate.GetYear())*12;
895 if ( aOldFirstDate.GetMonth() < rDate.GetMonth() )
896 nMonthOff += rDate.GetMonth()-aOldFirstDate.GetMonth();
897 else
898 nMonthOff -= aOldFirstDate.GetMonth()-rDate.GetMonth();
900 // construct menu (include years with different months)
901 for ( i = 0; i < MENU_YEAR_COUNT; i++ )
903 VclPtrInstance<PopupMenu> pYearPopupMenu;
904 for ( j = 1; j <= 12; j++ )
905 pYearPopupMenu->InsertItem( nYearIdCount+j,
906 maCalendarWrapper.getDisplayName(
907 i18n::CalendarDisplayIndex::MONTH, j-1, 1));
908 aPopupMenu->InsertItem( 10+i, OUString::number( nYear+i ) );
909 aPopupMenu->SetPopupMenu( 10+i, pYearPopupMenu );
910 nYearIdCount += 1000;
913 mbMenuDown = true;
914 nCurItemId = aPopupMenu->Execute( this, rPos );
915 mbMenuDown = false;
917 if ( !nCurItemId )
918 return;
920 sal_uInt16 nTempMonthOff = nMonthOff % 12;
921 sal_uInt16 nTempYearOff = nMonthOff / 12;
922 sal_uInt16 nNewMonth = nCurItemId % 1000;
923 sal_uInt16 nNewYear = nYear+((nCurItemId-1000)/1000);
924 if ( nTempMonthOff < nNewMonth )
925 nNewMonth = nNewMonth - nTempMonthOff;
926 else
928 nNewYear--;
929 nNewMonth = 12-(nTempMonthOff-nNewMonth);
931 nNewYear = nNewYear - nTempYearOff;
932 SetFirstDate( Date( 1, nNewMonth, nNewYear ) );
935 void Calendar::ImplTracking( const Point& rPos, bool bRepeat )
937 Date aTempDate = maCurDate;
938 sal_uInt16 nHitTest = ImplDoHitTest( rPos, aTempDate );
940 if ( mbSpinDown )
942 mbPrevIn = (nHitTest & CALENDAR_HITTEST_PREV) != 0;
943 mbNextIn = (nHitTest & CALENDAR_HITTEST_NEXT) != 0;
945 if ( bRepeat && (mbPrevIn || mbNextIn) )
947 ImplScrollCalendar( mbPrevIn );
950 else
951 ImplMouseSelect( aTempDate, nHitTest );
954 void Calendar::ImplEndTracking( bool bCancel )
956 bool bSelection = false;
957 bool bSpinDown = mbSpinDown;
959 mbDrag = false;
960 mbSpinDown = false;
961 mbPrevIn = false;
962 mbNextIn = false;
964 if ( bCancel )
966 if ( maOldFirstDate != maFirstDate )
967 SetFirstDate( maOldFirstDate );
969 if ( !bSpinDown )
971 IntDateSet aOldSel( *mpSelectTable );
972 Date aOldDate = maCurDate;
973 maCurDate = maOldCurDate;
974 *mpSelectTable = *mpOldSelectTable;
975 HideFocus();
976 ImplUpdateSelection( &aOldSel );
977 if ( aOldSel.find( aOldDate.GetDate() ) == aOldSel.end() )
978 ImplUpdateDate( aOldDate );
979 // assure focus rectangle is displayed again
980 if ( HasFocus() || mpSelectTable->find( maCurDate.GetDate() ) == mpSelectTable->end() )
981 ImplUpdateDate( maCurDate );
985 if ( bSpinDown )
986 return;
988 if ( !bCancel )
990 // determine if we should scroll the visible area
991 if ( !mpSelectTable->empty() )
993 Date aFirstSelDate( *mpSelectTable->begin() );
994 Date aLastSelDate( *mpSelectTable->rbegin() );
995 if ( aLastSelDate < GetFirstMonth() )
996 ImplScrollCalendar( true );
997 else if ( GetLastMonth() < aFirstSelDate )
998 ImplScrollCalendar( false );
1002 if ( !bCancel && ((maCurDate != maOldCurDate) || (*mpOldSelectTable != *mpSelectTable)) )
1003 Select();
1005 if ( !bSelection && (mnWinStyle & WB_TABSTOP) && !bCancel )
1006 GrabFocus();
1008 mpOldSelectTable.reset();
1011 void Calendar::MouseButtonDown( const MouseEvent& rMEvt )
1013 if ( rMEvt.IsLeft() && !mbMenuDown )
1015 Date aTempDate = maCurDate;
1016 sal_uInt16 nHitTest = ImplDoHitTest( rMEvt.GetPosPixel(), aTempDate );
1017 if ( nHitTest )
1019 if ( nHitTest & CALENDAR_HITTEST_MONTHTITLE )
1020 ImplShowMenu( rMEvt.GetPosPixel(), aTempDate );
1021 else
1023 maOldFirstDate = maFirstDate;
1025 mbPrevIn = (nHitTest & CALENDAR_HITTEST_PREV) != 0;
1026 mbNextIn = (nHitTest & CALENDAR_HITTEST_NEXT) != 0;
1027 if ( mbPrevIn || mbNextIn )
1029 mbSpinDown = true;
1030 ImplScrollCalendar( mbPrevIn );
1031 // it should really read BUTTONREPEAT, therefore do not
1032 // change it to SCROLLREPEAT, check with TH,
1033 // why it could be different (71775)
1034 StartTracking( StartTrackingFlags::ButtonRepeat );
1036 else
1038 if ( (rMEvt.GetClicks() != 2) || !(nHitTest & CALENDAR_HITTEST_DAY) )
1040 maOldCurDate = maCurDate;
1041 mpOldSelectTable.reset(new IntDateSet( *mpSelectTable ));
1043 mbDrag = true;
1044 StartTracking();
1046 ImplMouseSelect( aTempDate, nHitTest );
1048 if (rMEvt.GetClicks() == 2)
1049 maActivateHdl.Call(this);
1054 return;
1057 Control::MouseButtonDown( rMEvt );
1060 void Calendar::Tracking( const TrackingEvent& rTEvt )
1062 Point aMousePos = rTEvt.GetMouseEvent().GetPosPixel();
1064 if ( rTEvt.IsTrackingEnded() )
1065 ImplEndTracking( rTEvt.IsTrackingCanceled() );
1066 else
1067 ImplTracking( aMousePos, rTEvt.IsTrackingRepeat() );
1070 void Calendar::KeyInput( const KeyEvent& rKEvt )
1072 Date aNewDate = maCurDate;
1074 switch ( rKEvt.GetKeyCode().GetCode() )
1076 case KEY_HOME:
1077 aNewDate.SetDay( 1 );
1078 break;
1080 case KEY_END:
1081 aNewDate.SetDay( aNewDate.GetDaysInMonth() );
1082 break;
1084 case KEY_LEFT:
1085 --aNewDate;
1086 break;
1088 case KEY_RIGHT:
1089 ++aNewDate;
1090 break;
1092 case KEY_UP:
1093 aNewDate.AddDays( -7 );
1094 break;
1096 case KEY_DOWN:
1097 aNewDate.AddDays( 7 );
1098 break;
1100 case KEY_PAGEUP:
1102 Date aTempDate = aNewDate;
1103 aTempDate.AddDays( -(aNewDate.GetDay()+1) );
1104 aNewDate.AddDays( -aTempDate.GetDaysInMonth() );
1106 break;
1108 case KEY_PAGEDOWN:
1109 aNewDate.AddDays( aNewDate.GetDaysInMonth() );
1110 break;
1112 case KEY_RETURN:
1113 break;
1115 default:
1116 Control::KeyInput( rKEvt );
1117 break;
1120 if ( aNewDate != maCurDate )
1122 SetCurDate( aNewDate );
1123 Select();
1126 if (rKEvt.GetKeyCode().GetCode() == KEY_RETURN)
1128 if (maActivateHdl.IsSet())
1129 maActivateHdl.Call(this);
1130 else
1131 Control::KeyInput(rKEvt);
1135 void Calendar::Paint( vcl::RenderContext& rRenderContext, const tools::Rectangle& )
1137 ImplDraw(rRenderContext);
1140 void Calendar::GetFocus()
1142 ImplUpdateDate( maCurDate );
1143 Control::GetFocus();
1146 void Calendar::LoseFocus()
1148 HideFocus();
1149 Control::LoseFocus();
1152 void Calendar::Resize()
1154 ImplUpdate( true );
1155 Control::Resize();
1158 void Calendar::RequestHelp( const HelpEvent& rHEvt )
1160 if ( rHEvt.GetMode() & (HelpEventMode::QUICK | HelpEventMode::BALLOON) )
1162 Date aDate = maCurDate;
1163 if ( GetDate( ScreenToOutputPixel( rHEvt.GetMousePosPixel() ), aDate ) )
1165 tools::Rectangle aDateRect = GetDateRect( aDate );
1166 Point aPt = OutputToScreenPixel( aDateRect.TopLeft() );
1167 aDateRect.SetLeft( aPt.X() );
1168 aDateRect.SetTop( aPt.Y() );
1169 aPt = OutputToScreenPixel( aDateRect.BottomRight() );
1170 aDateRect.SetRight( aPt.X() );
1171 aDateRect.SetBottom( aPt.Y() );
1173 if ( rHEvt.GetMode() & HelpEventMode::QUICK )
1175 maCalendarWrapper.setGregorianDateTime( aDate);
1176 sal_uInt16 nWeek = static_cast<sal_uInt16>(maCalendarWrapper.getValue( i18n::CalendarFieldIndex::WEEK_OF_YEAR));
1177 sal_uInt16 nMonth = aDate.GetMonth();
1178 OUString aStr = maDayText
1179 + ": "
1180 + OUString::number(aDate.GetDayOfYear())
1181 + " / "
1182 + maWeekText
1183 + ": "
1184 + OUString::number(nWeek);
1185 // if year is not the same, add it
1186 if ( (nMonth == 12) && (nWeek == 1) )
1188 aStr += ", " + OUString::number(aDate.GetNextYear());
1190 else if ( (nMonth == 1) && (nWeek > 50) )
1192 aStr += ", " + OUString::number(aDate.GetYear()-1);
1194 Help::ShowQuickHelp( this, aDateRect, aStr );
1195 return;
1200 Control::RequestHelp( rHEvt );
1203 void Calendar::Command( const CommandEvent& rCEvt )
1205 if ( rCEvt.GetCommand() == CommandEventId::ContextMenu )
1207 if ( rCEvt.IsMouseEvent() )
1209 Date aTempDate = maCurDate;
1210 sal_uInt16 nHitTest = ImplDoHitTest( rCEvt.GetMousePosPixel(), aTempDate );
1211 if ( nHitTest & CALENDAR_HITTEST_MONTHTITLE )
1213 ImplShowMenu( rCEvt.GetMousePosPixel(), aTempDate );
1214 return;
1218 else if ( rCEvt.GetCommand() == CommandEventId::Wheel )
1220 const CommandWheelData* pData = rCEvt.GetWheelData();
1221 if ( pData->GetMode() == CommandWheelMode::SCROLL )
1223 tools::Long nNotchDelta = pData->GetNotchDelta();
1224 if ( nNotchDelta < 0 )
1226 while ( nNotchDelta < 0 )
1228 ImplScrollCalendar( true );
1229 nNotchDelta++;
1232 else
1234 while ( nNotchDelta > 0 )
1236 ImplScrollCalendar( false );
1237 nNotchDelta--;
1241 return;
1245 Control::Command( rCEvt );
1248 void Calendar::StateChanged( StateChangedType nType )
1250 Control::StateChanged( nType );
1252 if ( nType == StateChangedType::InitShow )
1253 ImplFormat();
1256 void Calendar::DataChanged( const DataChangedEvent& rDCEvt )
1258 Control::DataChanged( rDCEvt );
1260 if ( (rDCEvt.GetType() == DataChangedEventType::FONTS) ||
1261 (rDCEvt.GetType() == DataChangedEventType::FONTSUBSTITUTION) ||
1262 ((rDCEvt.GetType() == DataChangedEventType::SETTINGS) &&
1263 (rDCEvt.GetFlags() & AllSettingsFlags::STYLE)) )
1265 ImplInitSettings();
1266 Invalidate();
1270 void Calendar::Select()
1272 maSelectHdl.Call( this );
1275 Date Calendar::GetFirstSelectedDate() const
1277 if ( !mpSelectTable->empty() )
1278 return Date( *mpSelectTable->begin() );
1279 else
1281 Date aDate( 0, 0, 0 );
1282 return aDate;
1286 void Calendar::SetCurDate( const Date& rNewDate )
1288 if ( !rNewDate.IsValidAndGregorian() )
1289 return;
1291 if ( maCurDate == rNewDate )
1292 return;
1294 bool bUpdate = IsVisible() && IsUpdateMode();
1295 Date aOldDate = maCurDate;
1296 maCurDate = rNewDate;
1298 ImplCalendarSelectDate( mpSelectTable.get(), aOldDate, false );
1299 ImplCalendarSelectDate( mpSelectTable.get(), maCurDate, true );
1301 // shift actual date in the visible area
1302 if ( mbFormat || (maCurDate < GetFirstMonth()) )
1303 SetFirstDate( maCurDate );
1304 else if ( maCurDate > GetLastMonth() )
1306 Date aTempDate = GetLastMonth();
1307 tools::Long nDateOff = maCurDate-aTempDate;
1308 if ( nDateOff < 365 )
1310 Date aFirstDate = GetFirstMonth();
1311 aFirstDate.AddDays( aFirstDate.GetDaysInMonth() );
1312 ++aTempDate;
1313 while ( nDateOff > aTempDate.GetDaysInMonth() )
1315 aFirstDate.AddDays( aFirstDate.GetDaysInMonth() );
1316 sal_Int32 nDaysInMonth = aTempDate.GetDaysInMonth();
1317 aTempDate.AddDays( nDaysInMonth );
1318 nDateOff -= nDaysInMonth;
1320 SetFirstDate( aFirstDate );
1322 else
1323 SetFirstDate( maCurDate );
1325 else
1327 if ( bUpdate )
1329 HideFocus();
1330 ImplUpdateDate( aOldDate );
1331 ImplUpdateDate( maCurDate );
1336 void Calendar::SetFirstDate( const Date& rNewFirstDate )
1338 if ( maFirstDate != rNewFirstDate )
1340 maFirstDate = Date( 1, rNewFirstDate.GetMonth(), rNewFirstDate.GetYear() );
1341 ImplUpdate();
1345 Date Calendar::GetFirstMonth() const
1347 if ( maFirstDate.GetDay() > 1 )
1349 if ( maFirstDate.GetMonth() == 12 )
1350 return Date( 1, 1, maFirstDate.GetNextYear() );
1351 else
1352 return Date( 1, maFirstDate.GetMonth()+1, maFirstDate.GetYear() );
1354 else
1355 return maFirstDate;
1358 Date Calendar::GetLastMonth() const
1360 Date aDate = GetFirstMonth();
1361 sal_uInt16 nMonthCount = GetMonthCount();
1362 for ( sal_uInt16 i = 0; i < nMonthCount; i++ )
1363 aDate.AddDays( aDate.GetDaysInMonth() );
1364 --aDate;
1365 return aDate;
1368 sal_uInt16 Calendar::GetMonthCount() const
1370 if ( mbFormat )
1371 return 1;
1372 else
1373 return static_cast<sal_uInt16>(mnMonthPerLine*mnLines);
1376 bool Calendar::GetDate( const Point& rPos, Date& rDate ) const
1378 Date aDate = maCurDate;
1379 sal_uInt16 nHitTest = ImplDoHitTest( rPos, aDate );
1380 if ( nHitTest & CALENDAR_HITTEST_DAY )
1382 rDate = aDate;
1383 return true;
1385 else
1386 return false;
1389 tools::Rectangle Calendar::GetDateRect( const Date& rDate ) const
1391 tools::Rectangle aRect;
1393 if ( mbFormat || (rDate < maFirstDate) || (rDate > (maFirstDate+mnDayCount)) )
1394 return aRect;
1396 tools::Long nX;
1397 tools::Long nY;
1398 sal_Int32 nDaysOff;
1399 sal_uInt16 nDayIndex;
1400 Date aDate = GetFirstMonth();
1402 if ( rDate < aDate )
1404 aRect = GetDateRect( aDate );
1405 nDaysOff = aDate-rDate;
1406 nX = nDaysOff*mnDayWidth;
1407 aRect.AdjustLeft( -nX );
1408 aRect.AdjustRight( -nX );
1409 return aRect;
1411 else
1413 Date aLastDate = GetLastMonth();
1414 if ( rDate > aLastDate )
1416 sal_Int32 nWeekDay = static_cast<sal_Int32>(aLastDate.GetDayOfWeek());
1417 nWeekDay = (nWeekDay+(7-ImplGetWeekStart())) % 7;
1418 aLastDate.AddDays( -nWeekDay );
1419 aRect = GetDateRect( aLastDate );
1420 nDaysOff = rDate-aLastDate;
1421 nDayIndex = 0;
1422 for ( sal_Int32 i = 0; i <= nDaysOff; i++ )
1424 if ( aLastDate == rDate )
1426 aRect.AdjustLeft(nDayIndex*mnDayWidth );
1427 aRect.SetRight( aRect.Left()+mnDayWidth );
1428 return aRect;
1430 if ( nDayIndex == 6 )
1432 nDayIndex = 0;
1433 aRect.AdjustTop(mnDayHeight );
1434 aRect.AdjustBottom(mnDayHeight );
1436 else
1437 nDayIndex++;
1438 ++aLastDate;
1443 nY = 0;
1444 for ( tools::Long i = 0; i < mnLines; i++ )
1446 nX = 0;
1447 for ( tools::Long j = 0; j < mnMonthPerLine; j++ )
1449 sal_uInt16 nDaysInMonth = aDate.GetDaysInMonth();
1451 // month is called
1452 if ( (aDate.GetMonth() == rDate.GetMonth()) &&
1453 (aDate.GetYear() == rDate.GetYear()) )
1455 tools::Long nDayX = nX+mnDaysOffX;
1456 tools::Long nDayY = nY+mnDaysOffY;
1457 nDayIndex = static_cast<sal_uInt16>(aDate.GetDayOfWeek());
1458 nDayIndex = (nDayIndex+(7-static_cast<sal_uInt16>(ImplGetWeekStart()))) % 7;
1459 for ( sal_uInt16 nDay = 1; nDay <= nDaysInMonth; nDay++ )
1461 if ( nDay == rDate.GetDay() )
1463 aRect.SetLeft( nDayX + (nDayIndex*mnDayWidth) );
1464 aRect.SetTop( nDayY );
1465 aRect.SetRight( aRect.Left()+mnDayWidth );
1466 aRect.SetBottom( aRect.Top()+mnDayHeight );
1467 break;
1469 if ( nDayIndex == 6 )
1471 nDayIndex = 0;
1472 nDayY += mnDayHeight;
1474 else
1475 nDayIndex++;
1479 aDate.AddDays( nDaysInMonth );
1480 nX += mnMonthWidth;
1483 nY += mnMonthHeight;
1486 return aRect;
1489 void Calendar::EndSelection()
1491 if ( mbDrag || mbSpinDown )
1493 ReleaseMouse();
1495 mbDrag = false;
1496 mbSpinDown = false;
1497 mbPrevIn = false;
1498 mbNextIn = false;
1502 Size Calendar::CalcWindowSizePixel() const
1504 Size aSize;
1505 tools::Long n99TextWidth = GetTextWidth( "99" );
1506 tools::Long nTextHeight = GetTextHeight();
1508 aSize.AdjustWidth((n99TextWidth+DAY_OFFX)*7);
1509 aSize.AdjustWidth(MONTH_BORDERX*2 );
1511 aSize.setHeight( nTextHeight + TITLE_OFFY + (TITLE_BORDERY*2) );
1512 aSize.AdjustHeight(nTextHeight + WEEKDAY_OFFY );
1513 aSize.AdjustHeight((nTextHeight+DAY_OFFY)*6);
1514 aSize.AdjustHeight(MONTH_OFFY );
1516 return aSize;
1519 Size Calendar::GetOptimalSize() const
1521 return CalcWindowSizePixel();
1524 namespace
1526 class ImplCFieldFloat final
1528 private:
1529 std::unique_ptr<weld::Builder> mxBuilder;
1530 std::unique_ptr<weld::Container> mxContainer;
1531 std::unique_ptr<weld::Calendar> mxCalendar;
1532 std::unique_ptr<weld::Button> mxTodayBtn;
1533 std::unique_ptr<weld::Button> mxNoneBtn;
1535 public:
1536 ImplCFieldFloat(vcl::Window* pContainer)
1537 : mxBuilder(Application::CreateInterimBuilder(pContainer, "svt/ui/calendar.ui", false))
1538 , mxContainer(mxBuilder->weld_container("Calendar"))
1539 , mxCalendar(mxBuilder->weld_calendar("date"))
1540 , mxTodayBtn(mxBuilder->weld_button("today"))
1541 , mxNoneBtn(mxBuilder->weld_button("none"))
1545 weld::Calendar* GetCalendar() { return mxCalendar.get(); }
1546 weld::Button* EnableTodayBtn(bool bEnable);
1547 weld::Button* EnableNoneBtn(bool bEnable);
1549 void GrabFocus()
1551 mxCalendar->grab_focus();
1556 struct ImplCFieldFloatWin : public DropdownDockingWindow
1558 explicit ImplCFieldFloatWin(vcl::Window* pParent);
1559 virtual void dispose() override;
1560 virtual ~ImplCFieldFloatWin() override;
1561 virtual void GetFocus() override;
1563 std::unique_ptr<ImplCFieldFloat> mxWidget;
1566 ImplCFieldFloatWin::ImplCFieldFloatWin(vcl::Window* pParent)
1567 : DropdownDockingWindow(pParent)
1569 setDeferredProperties();
1570 mxWidget.reset(new ImplCFieldFloat(m_xBox.get()));
1573 ImplCFieldFloatWin::~ImplCFieldFloatWin()
1575 disposeOnce();
1578 void ImplCFieldFloatWin::dispose()
1580 mxWidget.reset();
1581 DropdownDockingWindow::dispose();
1584 void ImplCFieldFloatWin::GetFocus()
1586 DropdownDockingWindow::GetFocus();
1587 if (!mxWidget)
1588 return;
1589 mxWidget->GrabFocus();
1592 weld::Button* ImplCFieldFloat::EnableTodayBtn(bool bEnable)
1594 mxTodayBtn->set_visible(bEnable);
1595 return bEnable ? mxTodayBtn.get() : nullptr;
1598 weld::Button* ImplCFieldFloat::EnableNoneBtn(bool bEnable)
1600 mxNoneBtn->set_visible(bEnable);
1601 return bEnable ? mxNoneBtn.get() : nullptr;
1604 CalendarField::CalendarField(vcl::Window* pParent, WinBits nWinStyle)
1605 : DateField(pParent, nWinStyle)
1606 , mpFloatWin(nullptr)
1607 , mpTodayBtn(nullptr)
1608 , mpNoneBtn(nullptr)
1609 , mbToday(false)
1610 , mbNone(false)
1614 CalendarField::~CalendarField()
1616 disposeOnce();
1619 void CalendarField::dispose()
1621 mpTodayBtn = nullptr;
1622 mpNoneBtn = nullptr;
1623 mpFloatWin.disposeAndClear();
1624 DateField::dispose();
1627 IMPL_LINK(CalendarField, ImplSelectHdl, weld::Calendar&, rCalendar, void)
1629 Date aNewDate = rCalendar.get_date();
1631 vcl::Window::GetDockingManager()->EndPopupMode(mpFloatWin);
1632 mpFloatWin->EnableDocking(false);
1633 EndDropDown();
1634 GrabFocus();
1635 if ( IsEmptyDate() || ( aNewDate != GetDate() ) )
1637 SetDate( aNewDate );
1638 SetModifyFlag();
1639 Modify();
1643 IMPL_LINK(CalendarField, ImplClickHdl, weld::Button&, rBtn, void)
1645 vcl::Window::GetDockingManager()->EndPopupMode(mpFloatWin);
1646 mpFloatWin->EnableDocking(false);
1647 EndDropDown();
1648 GrabFocus();
1650 if (&rBtn == mpTodayBtn)
1652 Date aToday( Date::SYSTEM );
1653 if ( (aToday != GetDate()) || IsEmptyDate() )
1655 SetDate( aToday );
1656 SetModifyFlag();
1657 Modify();
1660 else if (&rBtn == mpNoneBtn)
1662 if ( !IsEmptyDate() )
1664 SetEmptyDate();
1665 SetModifyFlag();
1666 Modify();
1671 IMPL_LINK_NOARG(CalendarField, ImplPopupModeEndHdl, FloatingWindow*, void)
1673 EndDropDown();
1674 GrabFocus();
1677 bool CalendarField::ShowDropDown( bool bShow )
1679 if ( bShow )
1681 if ( !mpFloatWin )
1682 mpFloatWin = VclPtr<ImplCFieldFloatWin>::Create( this );
1684 Date aDate = GetDate();
1685 if ( IsEmptyDate() || !aDate.IsValidAndGregorian() )
1687 aDate = Date( Date::SYSTEM );
1689 weld::Calendar* pCalendar = mpFloatWin->mxWidget->GetCalendar();
1690 pCalendar->set_date( aDate );
1691 pCalendar->connect_activated(LINK(this, CalendarField, ImplSelectHdl));
1692 mpTodayBtn = mpFloatWin->mxWidget->EnableTodayBtn(mbToday);
1693 mpNoneBtn = mpFloatWin->mxWidget->EnableNoneBtn(mbNone);
1694 if (mpTodayBtn)
1695 mpTodayBtn->connect_clicked( LINK( this, CalendarField, ImplClickHdl ) );
1696 if (mpNoneBtn)
1697 mpNoneBtn->connect_clicked( LINK( this, CalendarField, ImplClickHdl ) );
1698 Point aPos(GetParent()->OutputToScreenPixel(GetPosPixel()));
1699 tools::Rectangle aRect(aPos, GetSizePixel());
1700 aRect.AdjustBottom( -1 );
1701 DockingManager* pDockingManager = vcl::Window::GetDockingManager();
1702 mpFloatWin->EnableDocking(true);
1703 pDockingManager->SetPopupModeEndHdl(mpFloatWin, LINK(this, CalendarField, ImplPopupModeEndHdl));
1704 pDockingManager->StartPopupMode(mpFloatWin, aRect, FloatWinPopupFlags::Down | FloatWinPopupFlags::GrabFocus);
1706 else
1708 vcl::Window::GetDockingManager()->EndPopupMode(mpFloatWin);
1709 mpFloatWin->EnableDocking(false);
1710 EndDropDown();
1712 return true;
1715 void CalendarField::StateChanged( StateChangedType nStateChange )
1717 DateField::StateChanged( nStateChange );
1719 if ( ( nStateChange == StateChangedType::Style ) && GetSubEdit() )
1721 WinBits nAllAlignmentBits = ( WB_LEFT | WB_CENTER | WB_RIGHT | WB_TOP | WB_VCENTER | WB_BOTTOM );
1722 WinBits nMyAlignment = GetStyle() & nAllAlignmentBits;
1723 GetSubEdit()->SetStyle( ( GetSubEdit()->GetStyle() & ~nAllAlignmentBits ) | nMyAlignment );
1727 // tdf#142783 consider the Edit and its DropDown as one compound control for the purpose of
1728 // notification of loss of focus from the control
1729 bool CalendarField::FocusWindowBelongsToControl(const vcl::Window* pFocusWin) const
1731 return DateField::FocusWindowBelongsToControl(pFocusWin) || (mpFloatWin && mpFloatWin->ImplIsWindowOrChild(pFocusWin));
1734 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */