sdext: adapt xpdfwrapper to poppler 24.12
[LibreOffice.git] / vcl / source / control / calendar.cxx
blob901a53000e0f73caa9f6856d3971b37accf211be
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>
36 #include <tools/json_writer.hxx>
38 #include <calendar.hxx>
39 #include <svdata.hxx>
40 #include <strings.hrc>
41 #include <memory>
43 #define DAY_OFFX 4
44 #define DAY_OFFY 2
45 #define MONTH_BORDERX 4
46 #define MONTH_OFFY 3
47 #define WEEKDAY_OFFY 3
48 #define TITLE_OFFY 3
49 #define TITLE_BORDERY 2
50 #define SPIN_OFFX 4
51 #define SPIN_OFFY TITLE_BORDERY
53 #define CALENDAR_HITTEST_DAY (sal_uInt16(0x0001))
54 #define CALENDAR_HITTEST_MONTHTITLE (sal_uInt16(0x0004))
55 #define CALENDAR_HITTEST_PREV (sal_uInt16(0x0008))
56 #define CALENDAR_HITTEST_NEXT (sal_uInt16(0x0010))
58 #define MENU_YEAR_COUNT 3
60 using namespace ::com::sun::star;
62 static void ImplCalendarSelectDate( IntDateSet* pTable, const Date& rDate, bool bSelect )
64 if ( bSelect )
65 pTable->insert( rDate.GetDate() );
66 else
67 pTable->erase( rDate.GetDate() );
72 void Calendar::ImplInit( WinBits nWinStyle )
74 mpSelectTable.reset(new IntDateSet);
75 mnDayCount = 0;
76 mnWinStyle = nWinStyle;
77 mnFirstYear = 0;
78 mnLastYear = 0;
79 mbCalc = true;
80 mbFormat = true;
81 mbDrag = false;
82 mbMenuDown = false;
83 mbSpinDown = false;
84 mbPrevIn = false;
85 mbNextIn = false;
87 OUString aGregorian( u"gregorian"_ustr);
88 maCalendarWrapper.loadCalendar( aGregorian,
89 Application::GetAppLocaleDataWrapper().getLanguageTag().getLocale());
90 if (maCalendarWrapper.getUniqueID() != aGregorian)
92 SAL_WARN( "vcl.control", "Calendar::ImplInit: No ``gregorian'' calendar available for locale ``"
93 << Application::GetAppLocaleDataWrapper().getLanguageTag().getBcp47()
94 << "'' and other calendars aren't supported. Using en-US fallback." );
96 /* If we ever wanted to support other calendars than Gregorian a lot of
97 * rewrite would be necessary to internally replace use of class Date
98 * with proper class CalendarWrapper methods, get rid of fixed 12
99 * months, fixed 7 days, ... */
100 maCalendarWrapper.loadCalendar( aGregorian, lang::Locale( u"en"_ustr, u"US"_ustr, u""_ustr));
103 SetFirstDate( maCurDate );
104 ImplCalendarSelectDate( mpSelectTable.get(), maCurDate, true );
106 // generate other strings
107 maDayText = VclResId(STR_SVT_CALENDAR_DAY);
108 maWeekText = VclResId(STR_SVT_CALENDAR_WEEK);
110 // create text for each day
111 for (sal_Int32 i = 0; i < 31; ++i)
112 maDayTexts[i] = OUString::number(i+1);
114 ImplInitSettings();
117 void Calendar::ApplySettings(vcl::RenderContext& rRenderContext)
119 const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings();
120 maSelColor = rStyleSettings.GetHighlightTextColor();
121 SetPointFont(rRenderContext, rStyleSettings.GetToolFont());
122 rRenderContext.SetTextColor(rStyleSettings.GetFieldTextColor());
123 rRenderContext.SetBackground(Wallpaper(rStyleSettings.GetFieldColor()));
126 void Calendar::ImplInitSettings()
128 const StyleSettings& rStyleSettings = GetSettings().GetStyleSettings();
129 maSelColor = rStyleSettings.GetHighlightTextColor();
130 SetPointFont(*GetOutDev(), rStyleSettings.GetToolFont());
131 SetTextColor(rStyleSettings.GetFieldTextColor());
132 SetBackground(Wallpaper(rStyleSettings.GetFieldColor()));
135 Calendar::Calendar(vcl::Window* pParent, WinBits nWinStyle)
136 : Control(pParent, nWinStyle & (WB_TABSTOP | WB_GROUP | WB_BORDER | WB_3DLOOK))
137 , maCalendarWrapper(Application::GetAppLocaleDataWrapper().getComponentContext())
138 , maOldFormatFirstDate(0, 0, 1900)
139 , maOldFormatLastDate(0, 0, 1900)
140 , maFirstDate(0, 0, 1900)
141 , maOldFirstDate(0, 0, 1900)
142 , maCurDate(Date::SYSTEM)
143 , maOldCurDate(0, 0, 1900)
145 ImplInit( nWinStyle );
148 Calendar::~Calendar()
150 disposeOnce();
153 void Calendar::dispose()
155 mpSelectTable.reset();
156 mpOldSelectTable.reset();
157 Control::dispose();
160 DayOfWeek Calendar::ImplGetWeekStart() const
162 // Map i18n::Weekdays to Date DayOfWeek
163 DayOfWeek eDay;
164 sal_Int16 nDay = maCalendarWrapper.getFirstDayOfWeek();
165 switch (nDay)
167 case i18n::Weekdays::SUNDAY :
168 eDay = SUNDAY;
169 break;
170 case i18n::Weekdays::MONDAY :
171 eDay = MONDAY;
172 break;
173 case i18n::Weekdays::TUESDAY :
174 eDay = TUESDAY;
175 break;
176 case i18n::Weekdays::WEDNESDAY :
177 eDay = WEDNESDAY;
178 break;
179 case i18n::Weekdays::THURSDAY :
180 eDay = THURSDAY;
181 break;
182 case i18n::Weekdays::FRIDAY :
183 eDay = FRIDAY;
184 break;
185 case i18n::Weekdays::SATURDAY :
186 eDay = SATURDAY;
187 break;
188 default:
189 SAL_WARN( "vcl.control", "Calendar::ImplGetWeekStart: broken i18n Gregorian calendar (getFirstDayOfWeek())");
190 eDay = SUNDAY;
192 return eDay;
195 void Calendar::ImplFormat()
197 if ( !mbFormat )
198 return;
200 if ( mbCalc )
202 Size aOutSize = GetOutputSizePixel();
204 if ( (aOutSize.Width() <= 1) || (aOutSize.Height() <= 1) )
205 return;
207 tools::Long n99TextWidth = GetTextWidth( u"99"_ustr );
208 tools::Long nTextHeight = GetTextHeight();
210 // calculate width and x-position
211 mnDayWidth = n99TextWidth+DAY_OFFX;
212 mnMonthWidth = mnDayWidth*7;
213 mnMonthWidth += MONTH_BORDERX*2;
214 mnMonthPerLine = aOutSize.Width() / mnMonthWidth;
215 if ( !mnMonthPerLine )
216 mnMonthPerLine = 1;
217 tools::Long nOver = (aOutSize.Width()-(mnMonthPerLine*mnMonthWidth)) / mnMonthPerLine;
218 mnMonthWidth += nOver;
219 mnDaysOffX = MONTH_BORDERX;
220 mnDaysOffX += nOver/2;
222 // calculate height and y-position
223 mnDayHeight = nTextHeight + DAY_OFFY;
224 mnWeekDayOffY = nTextHeight + TITLE_OFFY + (TITLE_BORDERY*2);
225 mnDaysOffY = mnWeekDayOffY + nTextHeight + WEEKDAY_OFFY;
226 mnMonthHeight = (mnDayHeight*6) + mnDaysOffY;
227 mnMonthHeight += MONTH_OFFY;
228 mnLines = aOutSize.Height() / mnMonthHeight;
229 if ( !mnLines )
230 mnLines = 1;
231 mnMonthHeight += (aOutSize.Height()-(mnLines*mnMonthHeight)) / mnLines;
233 // calculate spinfields
234 tools::Long nSpinSize = nTextHeight+TITLE_BORDERY-SPIN_OFFY;
235 maPrevRect.SetLeft( SPIN_OFFX );
236 maPrevRect.SetTop( SPIN_OFFY );
237 maPrevRect.SetRight( maPrevRect.Left()+nSpinSize );
238 maPrevRect.SetBottom( maPrevRect.Top()+nSpinSize );
239 maNextRect.SetLeft( aOutSize.Width()-SPIN_OFFX-nSpinSize-1 );
240 maNextRect.SetTop( SPIN_OFFY );
241 maNextRect.SetRight( maNextRect.Left()+nSpinSize );
242 maNextRect.SetBottom( maNextRect.Top()+nSpinSize );
244 // Calculate DayOfWeekText (gets displayed in a narrow font)
245 maDayOfWeekText.clear();
246 tools::Long nStartOffX = 0;
247 sal_Int16 nDay = maCalendarWrapper.getFirstDayOfWeek();
248 for ( sal_Int16 nDayOfWeek = 0; nDayOfWeek < 7; nDayOfWeek++ )
250 // Use narrow name.
251 OUString aDayOfWeek( maCalendarWrapper.getDisplayName(
252 i18n::CalendarDisplayIndex::DAY, nDay, 2));
253 tools::Long nOffX = (mnDayWidth-GetTextWidth( aDayOfWeek ))/2;
254 if ( !nDayOfWeek )
255 nStartOffX = nOffX;
256 else
257 nOffX -= nStartOffX;
258 nOffX += nDayOfWeek * mnDayWidth;
259 mnDayOfWeekAry[nDayOfWeek] = nOffX;
260 maDayOfWeekText += aDayOfWeek;
261 nDay++;
262 nDay %= 7;
265 // header position for the last day of week
266 mnDayOfWeekAry[7] = mnMonthWidth;
268 mbCalc = false;
271 // calculate number of days
273 DayOfWeek eStartDay = ImplGetWeekStart();
275 sal_uInt16 nWeekDay;
276 Date aTempDate = GetFirstMonth();
277 maFirstDate = aTempDate;
278 nWeekDay = static_cast<sal_uInt16>(aTempDate.GetDayOfWeek());
279 nWeekDay = (nWeekDay+(7-static_cast<sal_uInt16>(eStartDay))) % 7;
280 maFirstDate.AddDays( -nWeekDay );
281 mnDayCount = nWeekDay;
282 sal_uInt16 nDaysInMonth;
283 sal_uInt16 nMonthCount = static_cast<sal_uInt16>(mnMonthPerLine*mnLines);
284 for ( sal_uInt16 i = 0; i < nMonthCount; i++ )
286 nDaysInMonth = aTempDate.GetDaysInMonth();
287 mnDayCount += nDaysInMonth;
288 aTempDate.AddDays( nDaysInMonth );
290 Date aTempDate2 = aTempDate;
291 --aTempDate2;
292 nDaysInMonth = aTempDate2.GetDaysInMonth();
293 aTempDate2.AddDays( -(nDaysInMonth-1) );
294 nWeekDay = static_cast<sal_uInt16>(aTempDate2.GetDayOfWeek());
295 nWeekDay = (nWeekDay+(7-static_cast<sal_uInt16>(eStartDay))) % 7;
296 mnDayCount += 42-nDaysInMonth-nWeekDay;
298 // determine colours
299 maOtherColor = COL_LIGHTGRAY;
300 if ( maOtherColor.IsRGBEqual( GetBackground().GetColor() ) )
301 maOtherColor = COL_GRAY;
303 Date aLastDate = GetLastDate();
304 if ( (maOldFormatLastDate != aLastDate) ||
305 (maOldFormatFirstDate != maFirstDate) )
307 maOldFormatFirstDate = maFirstDate;
308 maOldFormatLastDate = aLastDate;
311 // get DateInfo
312 sal_Int16 nNewFirstYear = maFirstDate.GetYear();
313 sal_Int16 nNewLastYear = GetLastDate().GetYear();
314 if ( mnFirstYear )
316 if ( nNewFirstYear < mnFirstYear )
318 mnFirstYear = nNewFirstYear;
320 if ( nNewLastYear > mnLastYear )
322 mnLastYear = nNewLastYear;
325 else
327 mnFirstYear = nNewFirstYear;
328 mnLastYear = nNewLastYear;
331 mbFormat = false;
334 sal_uInt16 Calendar::ImplDoHitTest( const Point& rPos, Date& rDate ) const
336 if ( mbFormat )
337 return 0;
339 if ( maPrevRect.Contains( rPos ) )
340 return CALENDAR_HITTEST_PREV;
341 else if ( maNextRect.Contains( rPos ) )
342 return CALENDAR_HITTEST_NEXT;
344 tools::Long nY;
345 tools::Long nOffX;
346 sal_Int32 nDay;
347 DayOfWeek eStartDay = ImplGetWeekStart();
349 rDate = GetFirstMonth();
350 nY = 0;
351 for ( tools::Long i = 0; i < mnLines; i++ )
353 if ( rPos.Y() < nY )
354 return 0;
356 tools::Long nX = 0;
357 tools::Long nYMonth = nY+mnMonthHeight;
358 for ( tools::Long j = 0; j < mnMonthPerLine; j++ )
360 if ( (rPos.X() < nX) && (rPos.Y() < nYMonth) )
361 return 0;
363 sal_uInt16 nDaysInMonth = rDate.GetDaysInMonth();
365 // matching month was found
366 if ( (rPos.X() > nX) && (rPos.Y() < nYMonth) &&
367 (rPos.X() < nX+mnMonthWidth) )
369 if ( rPos.Y() < (nY+(TITLE_BORDERY*2)+mnDayHeight))
370 return CALENDAR_HITTEST_MONTHTITLE;
371 else
373 tools::Long nDayX = nX+mnDaysOffX;
374 tools::Long nDayY = nY+mnDaysOffY;
375 if ( rPos.Y() < nDayY )
376 return 0;
377 sal_Int32 nDayIndex = static_cast<sal_Int32>(rDate.GetDayOfWeek());
378 nDayIndex = (nDayIndex+(7-static_cast<sal_Int32>(eStartDay))) % 7;
379 if ( (i == 0) && (j == 0) )
381 Date aTempDate = rDate;
382 aTempDate.AddDays( -nDayIndex );
383 for ( nDay = 0; nDay < nDayIndex; nDay++ )
385 nOffX = nDayX + (nDay*mnDayWidth);
386 if ( (rPos.Y() >= nDayY) && (rPos.Y() < nDayY+mnDayHeight) &&
387 (rPos.X() >= nOffX) && (rPos.X() < nOffX+mnDayWidth) )
389 rDate = aTempDate;
390 rDate.AddDays( nDay );
391 return CALENDAR_HITTEST_DAY;
395 for ( nDay = 1; nDay <= nDaysInMonth; nDay++ )
397 if ( rPos.Y() < nDayY )
399 rDate.AddDays( nDayIndex );
400 return 0;
402 nOffX = nDayX + (nDayIndex*mnDayWidth);
403 if ( (rPos.Y() >= nDayY) && (rPos.Y() < nDayY+mnDayHeight) &&
404 (rPos.X() >= nOffX) && (rPos.X() < nOffX+mnDayWidth) )
406 rDate.AddDays( nDay-1 );
407 return CALENDAR_HITTEST_DAY;
409 if ( nDayIndex == 6 )
411 nDayIndex = 0;
412 nDayY += mnDayHeight;
414 else
415 nDayIndex++;
417 if ( (i == mnLines-1) && (j == mnMonthPerLine-1) )
419 sal_uInt16 nWeekDay = static_cast<sal_uInt16>(rDate.GetDayOfWeek());
420 nWeekDay = (nWeekDay+(7-static_cast<sal_uInt16>(eStartDay))) % 7;
421 sal_Int32 nDayCount = 42-nDaysInMonth-nWeekDay;
422 Date aTempDate = rDate;
423 aTempDate.AddDays( nDaysInMonth );
424 for ( nDay = 1; nDay <= nDayCount; nDay++ )
426 if ( rPos.Y() < nDayY )
428 rDate.AddDays( nDayIndex );
429 return 0;
431 nOffX = nDayX + (nDayIndex*mnDayWidth);
432 if ( (rPos.Y() >= nDayY) && (rPos.Y() < nDayY+mnDayHeight) &&
433 (rPos.X() >= nOffX) && (rPos.X() < nOffX+mnDayWidth) )
435 rDate = aTempDate;
436 rDate.AddDays( nDay-1 );
437 return CALENDAR_HITTEST_DAY;
439 if ( nDayIndex == 6 )
441 nDayIndex = 0;
442 nDayY += mnDayHeight;
444 else
445 nDayIndex++;
451 rDate.AddDays( nDaysInMonth );
452 nX += mnMonthWidth;
455 nY += mnMonthHeight;
458 return 0;
461 namespace
464 void ImplDrawSpinArrow(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect, bool bPrev)
466 tools::Long i;
467 tools::Long n;
468 tools::Long nLines;
469 tools::Long nHeight = rRect.GetHeight();
470 tools::Long nWidth = rRect.GetWidth();
471 if (nWidth < nHeight)
472 n = nWidth;
473 else
474 n = nHeight;
475 if (!(n & 0x01))
476 n--;
477 nLines = n/2;
479 tools::Rectangle aRect(Point( rRect.Left() + (nWidth / 2) - (nLines / 2),
480 rRect.Top() + (nHeight / 2) ),
481 Size(1, 1));
482 if (!bPrev)
484 aRect.AdjustLeft(nLines );
485 aRect.AdjustRight(nLines );
488 rRenderContext.DrawRect(aRect);
489 for (i = 0; i < nLines; i++)
491 if (bPrev)
493 aRect.AdjustLeft( 1 );
494 aRect.AdjustRight( 1 );
496 else
498 aRect.AdjustLeft( -1 );
499 aRect.AdjustRight( -1 );
501 aRect.AdjustTop( -1 );
502 aRect.AdjustBottom( 1 );
503 rRenderContext.DrawRect(aRect);
507 } //end anonymous namespace
509 void Calendar::ImplDrawSpin(vcl::RenderContext& rRenderContext )
511 rRenderContext.SetLineColor();
512 rRenderContext.SetFillColor(rRenderContext.GetSettings().GetStyleSettings().GetButtonTextColor());
513 tools::Rectangle aOutRect = maPrevRect;
514 aOutRect.AdjustLeft(3 );
515 aOutRect.AdjustTop(3 );
516 aOutRect.AdjustRight( -3 );
517 aOutRect.AdjustBottom( -3 );
518 ImplDrawSpinArrow(rRenderContext, aOutRect, true);
519 aOutRect = maNextRect;
520 aOutRect.AdjustLeft(3 );
521 aOutRect.AdjustTop(3 );
522 aOutRect.AdjustRight( -3 );
523 aOutRect.AdjustBottom( -3 );
524 ImplDrawSpinArrow(rRenderContext, aOutRect, false);
527 void Calendar::ImplDrawDate(vcl::RenderContext& rRenderContext,
528 tools::Long nX, tools::Long nY,
529 sal_uInt16 nDay, sal_uInt16 nMonth, sal_Int16 nYear,
530 bool bOther, sal_Int32 nToday )
532 Color const * pTextColor = nullptr;
533 const OUString& rDay = maDayTexts[(nDay - 1) % std::size(maDayTexts)];
534 tools::Rectangle aDateRect(nX, nY, nX + mnDayWidth - 1, nY + mnDayHeight - 1);
536 bool bSel = false;
537 bool bFocus = false;
538 // actual day
539 if ((nDay == maCurDate.GetDay()) &&
540 (nMonth == maCurDate.GetMonth()) &&
541 (nYear == maCurDate.GetYear()))
543 bFocus = true;
545 if (mpSelectTable)
547 if (mpSelectTable->find(Date(nDay, nMonth, nYear).GetDate()) != mpSelectTable->end())
548 bSel = true;
551 // get textcolour
552 if (bSel)
553 pTextColor = &maSelColor;
554 else if (bOther)
555 pTextColor = &maOtherColor;
557 if (bFocus)
558 HideFocus();
560 // display background
561 const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings();
562 if (bSel)
564 rRenderContext.SetLineColor();
565 rRenderContext.SetFillColor(rStyleSettings.GetHighlightColor());
566 rRenderContext.DrawRect(aDateRect);
569 // display text
570 tools::Long nTextX = nX + (mnDayWidth - GetTextWidth(rDay)) - (DAY_OFFX / 2);
571 tools::Long nTextY = nY + (mnDayHeight - GetTextHeight()) / 2;
572 if (pTextColor)
574 Color aOldColor = rRenderContext.GetTextColor();
575 rRenderContext.SetTextColor(*pTextColor);
576 rRenderContext.DrawText(Point(nTextX, nTextY), rDay);
577 rRenderContext.SetTextColor(aOldColor);
579 else
580 rRenderContext.DrawText(Point(nTextX, nTextY), rDay);
582 // today
583 Date aTodayDate(maCurDate);
584 if (nToday)
585 aTodayDate.SetDate(nToday);
586 else
587 aTodayDate = Date(Date::SYSTEM);
588 if ((nDay == aTodayDate.GetDay()) &&
589 (nMonth == aTodayDate.GetMonth()) &&
590 (nYear == aTodayDate.GetYear()))
592 rRenderContext.SetLineColor(rStyleSettings.GetWindowTextColor());
593 rRenderContext.SetFillColor();
594 rRenderContext.DrawRect(aDateRect);
597 // if needed do FocusRect
598 if (bFocus && HasFocus())
599 ShowFocus(aDateRect);
602 void Calendar::ImplDraw(vcl::RenderContext& rRenderContext)
604 ImplFormat();
606 const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings();
607 Size aOutSize(GetOutputSizePixel());
608 tools::Long i;
609 tools::Long j;
610 tools::Long nY;
611 tools::Long nDeltaX;
612 tools::Long nDeltaY;
613 tools::Long nDayX;
614 tools::Long nDayY;
615 sal_Int32 nToday = Date(Date::SYSTEM).GetDate();
616 sal_uInt16 nDay;
617 sal_uInt16 nMonth;
618 sal_Int16 nYear;
619 Date aDate = GetFirstMonth();
620 DayOfWeek eStartDay = ImplGetWeekStart();
622 HideFocus();
624 nY = 0;
625 for (i = 0; i < mnLines; i++)
627 // display title bar
628 rRenderContext.SetLineColor();
629 rRenderContext.SetFillColor(rStyleSettings.GetFaceColor());
630 tools::Rectangle aTitleRect(0, nY, aOutSize.Width() - 1, nY + mnDayHeight - DAY_OFFY + TITLE_BORDERY * 2);
631 rRenderContext.DrawRect(aTitleRect);
632 Point aTopLeft1(aTitleRect.Left(), aTitleRect.Top());
633 Point aTopLeft2(aTitleRect.Left(), aTitleRect.Top() + 1);
634 Point aBottomRight1(aTitleRect.Right(), aTitleRect.Bottom());
635 Point aBottomRight2(aTitleRect.Right(), aTitleRect.Bottom() - 1);
636 rRenderContext.SetLineColor(rStyleSettings.GetDarkShadowColor());
637 rRenderContext.DrawLine(aTopLeft1, Point(aBottomRight1.X(), aTopLeft1.Y()));
638 rRenderContext.SetLineColor(rStyleSettings.GetLightColor() );
639 rRenderContext.DrawLine(aTopLeft2, Point(aBottomRight2.X(), aTopLeft2.Y()));
640 rRenderContext.DrawLine(aTopLeft2, Point(aTopLeft2.X(), aBottomRight2.Y()));
641 rRenderContext.SetLineColor(rStyleSettings.GetShadowColor() );
642 rRenderContext.DrawLine(Point(aTopLeft2.X(), aBottomRight2.Y()), aBottomRight2);
643 rRenderContext.DrawLine(Point(aBottomRight2.X(), aTopLeft2.Y()), aBottomRight2);
644 rRenderContext.SetLineColor(rStyleSettings.GetDarkShadowColor());
645 rRenderContext.DrawLine(Point(aTopLeft1.X(), aBottomRight1.Y()), aBottomRight1);
646 Point aSepPos1(0, aTitleRect.Top() + TITLE_BORDERY);
647 Point aSepPos2(0, aTitleRect.Bottom() - TITLE_BORDERY);
648 for (j = 0; j < mnMonthPerLine-1; j++)
650 aSepPos1.AdjustX(mnMonthWidth-1 );
651 aSepPos2.setX( aSepPos1.X() );
652 rRenderContext.SetLineColor(rStyleSettings.GetShadowColor());
653 rRenderContext.DrawLine(aSepPos1, aSepPos2);
654 aSepPos1.AdjustX( 1 );
655 aSepPos2.setX( aSepPos1.X() );
656 rRenderContext.SetLineColor(rStyleSettings.GetLightColor());
657 rRenderContext.DrawLine(aSepPos1, aSepPos2);
660 tools::Long nX = 0;
661 for (j = 0; j < mnMonthPerLine; j++)
663 nMonth = aDate.GetMonth();
664 nYear = aDate.GetYear();
666 // display month in title bar
667 nDeltaX = nX;
668 nDeltaY = nY + TITLE_BORDERY;
669 OUString aMonthText = maCalendarWrapper.getDisplayName(i18n::CalendarDisplayIndex::MONTH, nMonth - 1, 1)
670 + " "
671 + OUString::number(nYear);
672 tools::Long nMonthTextWidth = rRenderContext.GetTextWidth(aMonthText);
673 tools::Long nMonthOffX1 = 0;
674 tools::Long nMonthOffX2 = 0;
675 if (i == 0)
677 if (j == 0)
678 nMonthOffX1 = maPrevRect.Right() + 1;
679 if (j == mnMonthPerLine - 1)
680 nMonthOffX2 = aOutSize.Width() - maNextRect.Left() + 1;
682 tools::Long nMaxMonthWidth = mnMonthWidth - nMonthOffX1 - nMonthOffX2 - 4;
683 if (nMonthTextWidth > nMaxMonthWidth)
685 // Abbreviated month name.
686 aMonthText = maCalendarWrapper.getDisplayName(i18n::CalendarDisplayIndex::MONTH, nMonth - 1, 0)
687 + " "
688 + OUString::number(nYear);
689 nMonthTextWidth = rRenderContext.GetTextWidth(aMonthText);
691 tools::Long nTempOff = (mnMonthWidth - nMonthTextWidth + 1) / 2;
692 if (nTempOff < nMonthOffX1)
693 nDeltaX += nMonthOffX1 + 1;
694 else
696 if (nTempOff + nMonthTextWidth > mnMonthWidth - nMonthOffX2)
697 nDeltaX += mnMonthWidth - nMonthOffX2 - nMonthTextWidth;
698 else
699 nDeltaX += nTempOff;
701 rRenderContext.SetTextColor(rStyleSettings.GetButtonTextColor());
702 rRenderContext.DrawText(Point(nDeltaX, nDeltaY), aMonthText);
703 rRenderContext.SetTextColor(rStyleSettings.GetWindowTextColor());
705 // display week bar
706 nDayX = nX + mnDaysOffX;
707 nDayY = nY + mnWeekDayOffY;
708 nDeltaY = nDayY + mnDayHeight;
709 rRenderContext.SetLineColor(rStyleSettings.GetWindowTextColor());
710 Point aStartPos(nDayX, nDeltaY);
711 rRenderContext.DrawLine(aStartPos, Point(nDayX + (7 * mnDayWidth), nDeltaY));
712 KernArray aTmp;
713 for (int k=0; k<7; ++k)
714 aTmp.push_back(mnDayOfWeekAry[k+1]);
715 rRenderContext.DrawTextArray(Point(nDayX + mnDayOfWeekAry[0], nDayY), maDayOfWeekText, aTmp, {}, 0, aTmp.size());
717 // display days
718 sal_uInt16 nDaysInMonth = aDate.GetDaysInMonth();
719 nDayX = nX + mnDaysOffX;
720 nDayY = nY + mnDaysOffY;
721 sal_uInt16 nDayIndex = static_cast<sal_uInt16>(aDate.GetDayOfWeek());
722 nDayIndex = (nDayIndex + (7 - static_cast<sal_uInt16>(eStartDay))) % 7;
723 if (i == 0 && j == 0)
725 Date aTempDate = aDate;
726 aTempDate.AddDays( -nDayIndex );
727 for (nDay = 0; nDay < nDayIndex; ++nDay)
729 nDeltaX = nDayX + (nDay * mnDayWidth);
730 ImplDrawDate(rRenderContext, nDeltaX, nDayY, nDay + aTempDate.GetDay(),
731 aTempDate.GetMonth(), aTempDate.GetYear(),
732 true, nToday);
735 for (nDay = 1; nDay <= nDaysInMonth; nDay++)
737 nDeltaX = nDayX + (nDayIndex * mnDayWidth);
738 ImplDrawDate(rRenderContext, nDeltaX, nDayY, nDay, nMonth, nYear,
739 false, nToday);
740 if (nDayIndex == 6)
742 nDayIndex = 0;
743 nDayY += mnDayHeight;
745 else
746 nDayIndex++;
748 if ((i == mnLines - 1) && (j == mnMonthPerLine - 1))
750 sal_uInt16 nWeekDay = static_cast<sal_uInt16>(aDate.GetDayOfWeek());
751 nWeekDay = (nWeekDay + (7 - static_cast<sal_uInt16>(eStartDay))) % 7;
752 sal_uInt16 nDayCount = 42 - nDaysInMonth - nWeekDay;
753 Date aTempDate = aDate;
754 aTempDate.AddDays( nDaysInMonth );
755 for (nDay = 1; nDay <= nDayCount; ++nDay)
757 nDeltaX = nDayX + (nDayIndex * mnDayWidth);
758 ImplDrawDate(rRenderContext, nDeltaX, nDayY, nDay,
759 aTempDate.GetMonth(), aTempDate.GetYear(),
760 true, nToday);
761 if (nDayIndex == 6)
763 nDayIndex = 0;
764 nDayY += mnDayHeight;
766 else
767 nDayIndex++;
771 aDate.AddDays( nDaysInMonth );
772 nX += mnMonthWidth;
775 nY += mnMonthHeight;
778 // draw spin buttons
779 ImplDrawSpin(rRenderContext);
782 void Calendar::ImplUpdateDate( const Date& rDate )
784 if (IsReallyVisible() && IsUpdateMode())
786 tools::Rectangle aDateRect(GetDateRect(rDate));
787 if (!aDateRect.IsEmpty())
789 Invalidate(aDateRect);
794 void Calendar::ImplUpdateSelection( IntDateSet* pOld )
796 IntDateSet* pNew = mpSelectTable.get();
798 for (auto const& nKey : *pOld)
800 if ( pNew->find(nKey) == pNew->end() )
802 Date aTempDate(nKey);
803 ImplUpdateDate(aTempDate);
807 for (auto const& nKey : *pNew)
809 if ( pOld->find(nKey) == pOld->end() )
811 Date aTempDate(nKey);
812 ImplUpdateDate(aTempDate);
817 void Calendar::ImplMouseSelect( const Date& rDate, sal_uInt16 nHitTest )
819 IntDateSet aOldSel( *mpSelectTable );
820 Date aOldDate = maCurDate;
821 Date aTempDate = rDate;
823 if ( !(nHitTest & CALENDAR_HITTEST_DAY) )
824 --aTempDate;
826 if ( !(nHitTest & CALENDAR_HITTEST_DAY) )
827 aTempDate = maOldCurDate;
828 if ( aTempDate != maCurDate )
830 maCurDate = aTempDate;
831 ImplCalendarSelectDate( mpSelectTable.get(), aOldDate, false );
832 ImplCalendarSelectDate( mpSelectTable.get(), maCurDate, true );
835 bool bNewSel = aOldSel != *mpSelectTable;
836 if ( (maCurDate != aOldDate) || bNewSel )
838 HideFocus();
839 if ( bNewSel )
840 ImplUpdateSelection( &aOldSel );
841 if ( !bNewSel || aOldSel.find( aOldDate.GetDate() ) == aOldSel.end() )
842 ImplUpdateDate( aOldDate );
843 // assure focus rectangle is displayed again
844 if ( HasFocus() || !bNewSel
845 || mpSelectTable->find( maCurDate.GetDate() ) == mpSelectTable->end() )
846 ImplUpdateDate( maCurDate );
850 void Calendar::ImplUpdate( bool bCalcNew )
852 if (IsReallyVisible() && IsUpdateMode())
854 if (bCalcNew && !mbCalc)
856 Invalidate();
858 else if (!mbFormat && !mbCalc)
860 Invalidate();
864 if (bCalcNew)
865 mbCalc = true;
866 mbFormat = true;
869 void Calendar::ImplScrollCalendar( bool bPrev )
871 Date aNewFirstMonth = GetFirstMonth();
872 if ( bPrev )
874 --aNewFirstMonth;
875 aNewFirstMonth.AddDays( -(aNewFirstMonth.GetDaysInMonth()-1));
877 else
878 aNewFirstMonth.AddDays( aNewFirstMonth.GetDaysInMonth());
879 SetFirstDate( aNewFirstMonth );
882 void Calendar::ImplShowMenu( const Point& rPos, const Date& rDate )
884 EndSelection();
886 Date aOldFirstDate = GetFirstMonth();
887 ScopedVclPtrInstance<PopupMenu> aPopupMenu;
888 sal_uInt16 nMonthOff;
889 sal_uInt16 nCurItemId;
890 sal_uInt16 nYear = rDate.GetYear()-1;
891 sal_uInt16 i;
892 sal_uInt16 j;
893 sal_uInt16 nYearIdCount = 1000;
895 nMonthOff = (rDate.GetYear()-aOldFirstDate.GetYear())*12;
896 if ( aOldFirstDate.GetMonth() < rDate.GetMonth() )
897 nMonthOff += rDate.GetMonth()-aOldFirstDate.GetMonth();
898 else
899 nMonthOff -= aOldFirstDate.GetMonth()-rDate.GetMonth();
901 // construct menu (include years with different months)
902 for ( i = 0; i < MENU_YEAR_COUNT; i++ )
904 VclPtrInstance<PopupMenu> pYearPopupMenu;
905 for ( j = 1; j <= 12; j++ )
906 pYearPopupMenu->InsertItem( nYearIdCount+j,
907 maCalendarWrapper.getDisplayName(
908 i18n::CalendarDisplayIndex::MONTH, j-1, 1));
909 aPopupMenu->InsertItem( 10+i, OUString::number( nYear+i ) );
910 aPopupMenu->SetPopupMenu( 10+i, pYearPopupMenu );
911 nYearIdCount += 1000;
914 mbMenuDown = true;
915 nCurItemId = aPopupMenu->Execute( this, rPos );
916 mbMenuDown = false;
918 if ( !nCurItemId )
919 return;
921 sal_uInt16 nTempMonthOff = nMonthOff % 12;
922 sal_uInt16 nTempYearOff = nMonthOff / 12;
923 sal_uInt16 nNewMonth = nCurItemId % 1000;
924 sal_uInt16 nNewYear = nYear+((nCurItemId-1000)/1000);
925 if ( nTempMonthOff < nNewMonth )
926 nNewMonth = nNewMonth - nTempMonthOff;
927 else
929 nNewYear--;
930 nNewMonth = 12-(nTempMonthOff-nNewMonth);
932 nNewYear = nNewYear - nTempYearOff;
933 SetFirstDate( Date( 1, nNewMonth, nNewYear ) );
936 void Calendar::ImplTracking( const Point& rPos, bool bRepeat )
938 Date aTempDate = maCurDate;
939 sal_uInt16 nHitTest = ImplDoHitTest( rPos, aTempDate );
941 if ( mbSpinDown )
943 mbPrevIn = (nHitTest & CALENDAR_HITTEST_PREV) != 0;
944 mbNextIn = (nHitTest & CALENDAR_HITTEST_NEXT) != 0;
946 if ( bRepeat && (mbPrevIn || mbNextIn) )
948 ImplScrollCalendar( mbPrevIn );
951 else
952 ImplMouseSelect( aTempDate, nHitTest );
955 void Calendar::ImplEndTracking( bool bCancel )
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 ( (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( DateTime(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( u"99"_ustr );
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 void Calendar::DumpAsPropertyTree(tools::JsonWriter& rJsonWriter)
1526 Control::DumpAsPropertyTree(rJsonWriter);
1528 auto aDate = GetFirstSelectedDate();
1530 rJsonWriter.put("type", "calendar");
1531 rJsonWriter.put("day", aDate.GetDay());
1532 rJsonWriter.put("month", aDate.GetMonth());
1533 rJsonWriter.put("year", aDate.GetYear());
1536 namespace
1538 class ImplCFieldFloat final
1540 private:
1541 std::unique_ptr<weld::Builder> mxBuilder;
1542 std::unique_ptr<weld::Container> mxContainer;
1543 std::unique_ptr<weld::Calendar> mxCalendar;
1544 std::unique_ptr<weld::Button> mxTodayBtn;
1545 std::unique_ptr<weld::Button> mxNoneBtn;
1547 public:
1548 ImplCFieldFloat(vcl::Window* pContainer)
1549 : mxBuilder(Application::CreateInterimBuilder(pContainer, u"svt/ui/calendar.ui"_ustr, false))
1550 , mxContainer(mxBuilder->weld_container(u"Calendar"_ustr))
1551 , mxCalendar(mxBuilder->weld_calendar(u"date"_ustr))
1552 , mxTodayBtn(mxBuilder->weld_button(u"today"_ustr))
1553 , mxNoneBtn(mxBuilder->weld_button(u"none"_ustr))
1557 weld::Calendar* GetCalendar() { return mxCalendar.get(); }
1558 weld::Button* EnableTodayBtn(bool bEnable);
1559 weld::Button* EnableNoneBtn(bool bEnable);
1561 void GrabFocus()
1563 mxCalendar->grab_focus();
1568 struct ImplCFieldFloatWin : public DropdownDockingWindow
1570 explicit ImplCFieldFloatWin(vcl::Window* pParent);
1571 virtual void dispose() override;
1572 virtual ~ImplCFieldFloatWin() override;
1573 virtual void GetFocus() override;
1575 std::unique_ptr<ImplCFieldFloat> mxWidget;
1578 ImplCFieldFloatWin::ImplCFieldFloatWin(vcl::Window* pParent)
1579 : DropdownDockingWindow(pParent)
1581 setDeferredProperties();
1582 mxWidget.reset(new ImplCFieldFloat(m_xBox.get()));
1585 ImplCFieldFloatWin::~ImplCFieldFloatWin()
1587 disposeOnce();
1590 void ImplCFieldFloatWin::dispose()
1592 mxWidget.reset();
1593 DropdownDockingWindow::dispose();
1596 void ImplCFieldFloatWin::GetFocus()
1598 DropdownDockingWindow::GetFocus();
1599 if (!mxWidget)
1600 return;
1601 mxWidget->GrabFocus();
1604 weld::Button* ImplCFieldFloat::EnableTodayBtn(bool bEnable)
1606 mxTodayBtn->set_visible(bEnable);
1607 return bEnable ? mxTodayBtn.get() : nullptr;
1610 weld::Button* ImplCFieldFloat::EnableNoneBtn(bool bEnable)
1612 mxNoneBtn->set_visible(bEnable);
1613 return bEnable ? mxNoneBtn.get() : nullptr;
1616 CalendarField::CalendarField(vcl::Window* pParent, WinBits nWinStyle)
1617 : DateField(pParent, nWinStyle)
1618 , mpFloatWin(nullptr)
1619 , mpTodayBtn(nullptr)
1620 , mpNoneBtn(nullptr)
1621 , mbToday(false)
1622 , mbNone(false)
1626 CalendarField::~CalendarField()
1628 disposeOnce();
1631 void CalendarField::dispose()
1633 mpTodayBtn = nullptr;
1634 mpNoneBtn = nullptr;
1635 mpFloatWin.disposeAndClear();
1636 DateField::dispose();
1639 IMPL_LINK(CalendarField, ImplSelectHdl, weld::Calendar&, rCalendar, void)
1641 Date aNewDate = rCalendar.get_date();
1643 vcl::Window::GetDockingManager()->EndPopupMode(mpFloatWin);
1644 mpFloatWin->EnableDocking(false);
1645 EndDropDown();
1646 GrabFocus();
1647 if ( IsEmptyDate() || ( aNewDate != GetDate() ) )
1649 SetDate( aNewDate );
1650 SetModifyFlag();
1651 Modify();
1655 IMPL_LINK(CalendarField, ImplClickHdl, weld::Button&, rBtn, void)
1657 vcl::Window::GetDockingManager()->EndPopupMode(mpFloatWin);
1658 mpFloatWin->EnableDocking(false);
1659 EndDropDown();
1660 GrabFocus();
1662 if (&rBtn == mpTodayBtn)
1664 Date aToday( Date::SYSTEM );
1665 if ( (aToday != GetDate()) || IsEmptyDate() )
1667 SetDate( aToday );
1668 SetModifyFlag();
1669 Modify();
1672 else if (&rBtn == mpNoneBtn)
1674 if ( !IsEmptyDate() )
1676 SetEmptyDate();
1677 SetModifyFlag();
1678 Modify();
1683 IMPL_LINK_NOARG(CalendarField, ImplPopupModeEndHdl, FloatingWindow*, void)
1685 EndDropDown();
1686 GrabFocus();
1689 bool CalendarField::ShowDropDown( bool bShow )
1691 if ( bShow )
1693 if ( !mpFloatWin )
1694 mpFloatWin = VclPtr<ImplCFieldFloatWin>::Create( this );
1696 Date aDate = GetDate();
1697 if ( IsEmptyDate() || !aDate.IsValidAndGregorian() )
1699 aDate = Date( Date::SYSTEM );
1701 weld::Calendar* pCalendar = mpFloatWin->mxWidget->GetCalendar();
1702 pCalendar->set_date( aDate );
1703 pCalendar->connect_activated(LINK(this, CalendarField, ImplSelectHdl));
1704 mpTodayBtn = mpFloatWin->mxWidget->EnableTodayBtn(mbToday);
1705 mpNoneBtn = mpFloatWin->mxWidget->EnableNoneBtn(mbNone);
1706 if (mpTodayBtn)
1707 mpTodayBtn->connect_clicked( LINK( this, CalendarField, ImplClickHdl ) );
1708 if (mpNoneBtn)
1709 mpNoneBtn->connect_clicked( LINK( this, CalendarField, ImplClickHdl ) );
1710 Point aPos(GetParent()->OutputToScreenPixel(GetPosPixel()));
1711 tools::Rectangle aRect(aPos, GetSizePixel());
1712 aRect.AdjustBottom( -1 );
1713 DockingManager* pDockingManager = vcl::Window::GetDockingManager();
1714 mpFloatWin->EnableDocking(true);
1715 pDockingManager->SetPopupModeEndHdl(mpFloatWin, LINK(this, CalendarField, ImplPopupModeEndHdl));
1716 pDockingManager->StartPopupMode(mpFloatWin, aRect, FloatWinPopupFlags::Down | FloatWinPopupFlags::GrabFocus);
1718 else
1720 vcl::Window::GetDockingManager()->EndPopupMode(mpFloatWin);
1721 mpFloatWin->EnableDocking(false);
1722 EndDropDown();
1724 return true;
1727 void CalendarField::StateChanged( StateChangedType nStateChange )
1729 DateField::StateChanged( nStateChange );
1731 if ( ( nStateChange == StateChangedType::Style ) && GetSubEdit() )
1733 WinBits nAllAlignmentBits = ( WB_LEFT | WB_CENTER | WB_RIGHT | WB_TOP | WB_VCENTER | WB_BOTTOM );
1734 WinBits nMyAlignment = GetStyle() & nAllAlignmentBits;
1735 GetSubEdit()->SetStyle( ( GetSubEdit()->GetStyle() & ~nAllAlignmentBits ) | nMyAlignment );
1739 // tdf#142783 consider the Edit and its DropDown as one compound control for the purpose of
1740 // notification of loss of focus from the control
1741 bool CalendarField::FocusWindowBelongsToControl(const vcl::Window* pFocusWin) const
1743 return DateField::FocusWindowBelongsToControl(pFocusWin) || (mpFloatWin && mpFloatWin->ImplIsWindowOrChild(pFocusWin));
1746 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */