diff options
author | Miklos Vajna <vmiklos@collabora.com> | 2022-05-25 09:58:28 +0200 |
---|---|---|
committer | Miklos Vajna <vmiklos@collabora.com> | 2022-05-25 11:52:02 +0200 |
commit | ce5d609da9d20b3c91f6f8eb4ee88451cbd55a9d (patch) | |
tree | 2aba1fb0c225d7b89d67e244e56d68220b041135 | |
parent | d77db907465547f8704fd32b2c90165b54873dba (diff) |
sw content controls, date: show a date picker on click
- add a new SwContentControl::GetDateString() that knows how to produce
a formatted date, taking the language and the date format into account
- add a new SwDateContentControlButton that knows how to open popup a
calendar on click and that puts the selected date into
SwContentControl::m_oSelectedDate
- extend SwWrtShell::GotoContentControl() to consume that selected date
& update the document text accordingly
- in case SwSelPaintRects::HighlightContentControl() notices a date
content control, create an instance of this newly introduced
SwDateContentControlButton
Change-Id: Ia2cb0fa3aefbf543b8dc2e96bcebb41408eb12c3
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/134926
Reviewed-by: Miklos Vajna <vmiklos@collabora.com>
Tested-by: Jenkins
-rw-r--r-- | sw/Library_sw.mk | 1 | ||||
-rw-r--r-- | sw/UIConfig_swriter.mk | 1 | ||||
-rw-r--r-- | sw/inc/formatcontentcontrol.hxx | 10 | ||||
-rw-r--r-- | sw/qa/core/unocore/unocore.cxx | 7 | ||||
-rw-r--r-- | sw/qa/uibase/wrtsh/wrtsh.cxx | 37 | ||||
-rw-r--r-- | sw/source/core/crsr/crstrvl.cxx | 2 | ||||
-rw-r--r-- | sw/source/core/crsr/datecontentcontrolbutton.cxx | 59 | ||||
-rw-r--r-- | sw/source/core/crsr/viscrs.cxx | 21 | ||||
-rw-r--r-- | sw/source/core/inc/datecontentcontrolbutton.hxx | 41 | ||||
-rw-r--r-- | sw/source/core/txtnode/attrcontentcontrol.cxx | 35 | ||||
-rw-r--r-- | sw/source/uibase/wrtsh/wrtsh3.cxx | 21 | ||||
-rw-r--r-- | sw/uiconfig/swriter/ui/contentcontrolcalendar.ui | 28 |
12 files changed, 256 insertions, 7 deletions
diff --git a/sw/Library_sw.mk b/sw/Library_sw.mk index eec16fd5612c..22694869fb54 100644 --- a/sw/Library_sw.mk +++ b/sw/Library_sw.mk @@ -164,6 +164,7 @@ $(eval $(call gb_Library_add_exception_objects,sw,\ sw/source/core/crsr/crstrvl1 \ sw/source/core/crsr/DateFormFieldButton \ sw/source/core/crsr/DropDownFormFieldButton \ + sw/source/core/crsr/datecontentcontrolbutton \ sw/source/core/crsr/dropdowncontentcontrolbutton \ sw/source/core/crsr/findattr \ sw/source/core/crsr/findcoll \ diff --git a/sw/UIConfig_swriter.mk b/sw/UIConfig_swriter.mk index 012d86008b39..5dafec57ee88 100644 --- a/sw/UIConfig_swriter.mk +++ b/sw/UIConfig_swriter.mk @@ -121,6 +121,7 @@ $(eval $(call gb_UIConfig_add_uifiles,modules/swriter,\ sw/uiconfig/swriter/ui/combobox \ sw/uiconfig/swriter/ui/comboboxfragment \ sw/uiconfig/swriter/ui/conditionpage \ + sw/uiconfig/swriter/ui/contentcontrolcalendar \ sw/uiconfig/swriter/ui/contentcontroldlg \ sw/uiconfig/swriter/ui/contentcontroldropdown \ sw/uiconfig/swriter/ui/contentcontrollistitemdlg \ diff --git a/sw/inc/formatcontentcontrol.hxx b/sw/inc/formatcontentcontrol.hxx index 6e3da73412ba..5137037df62a 100644 --- a/sw/inc/formatcontentcontrol.hxx +++ b/sw/inc/formatcontentcontrol.hxx @@ -137,6 +137,9 @@ class SW_DLLPUBLIC SwContentControl : public sw::BroadcastingModify /// Stores a list item index, in case the doc model is not yet updated. std::optional<size_t> m_oSelectedListItem; + /// Stores a date timestamp, in case the doc model is not yet updated. + std::optional<double> m_oSelectedDate; + public: SwTextContentControl* GetTextAttr() const; @@ -212,6 +215,9 @@ public: OUString GetDateLanguage() const { return m_aDateLanguage; } + /// Formats m_oSelectedDate, taking m_aDateFormat and m_aDateLanguage into account. + OUString GetDateString() const; + void SetSelectedListItem(std::optional<size_t> oSelectedListItem) { m_oSelectedListItem = oSelectedListItem; @@ -219,6 +225,10 @@ public: std::optional<size_t> GetSelectedListItem() const { return m_oSelectedListItem; } + void SetSelectedDate(std::optional<double> oSelectedDate) { m_oSelectedDate = oSelectedDate; } + + std::optional<double> GetSelectedDate() const { return m_oSelectedDate; } + virtual void dumpAsXml(xmlTextWriterPtr pWriter) const; }; diff --git a/sw/qa/core/unocore/unocore.cxx b/sw/qa/core/unocore/unocore.cxx index 16959a06f377..a998810e6029 100644 --- a/sw/qa/core/unocore/unocore.cxx +++ b/sw/qa/core/unocore/unocore.cxx @@ -590,12 +590,7 @@ CPPUNIT_TEST_FIXTURE(SwCoreUnocoreTest, testContentControlDate) uno::Reference<text::XTextDocument> xTextDocument(mxComponent, uno::UNO_QUERY); uno::Reference<text::XText> xText = xTextDocument->getText(); uno::Reference<text::XTextCursor> xCursor = xText->createTextCursor(); - uno::Reference<beans::XPropertySet> xTextGraphic( - xMSF->createInstance("com.sun.star.text.TextGraphicObject"), uno::UNO_QUERY); - xTextGraphic->setPropertyValue("AnchorType", - uno::Any(text::TextContentAnchorType_AS_CHARACTER)); - uno::Reference<text::XTextContent> xTextContent(xTextGraphic, uno::UNO_QUERY); - xText->insertTextContent(xCursor, xTextContent, false); + xText->insertString(xCursor, "test", /*bAbsorb=*/false); xCursor->gotoStart(/*bExpand=*/false); xCursor->gotoEnd(/*bExpand=*/true); uno::Reference<text::XTextContent> xContentControl( diff --git a/sw/qa/uibase/wrtsh/wrtsh.cxx b/sw/qa/uibase/wrtsh/wrtsh.cxx index 65a41086dce4..06bb8a45edc1 100644 --- a/sw/qa/uibase/wrtsh/wrtsh.cxx +++ b/sw/qa/uibase/wrtsh/wrtsh.cxx @@ -307,6 +307,43 @@ CPPUNIT_TEST_FIXTURE(Test, testInsertPictureContentControl) CPPUNIT_ASSERT(pContentControl->GetPicture()); CPPUNIT_ASSERT(pTextNode->GetTextAttrForCharAt(1, RES_TXTATR_FLYCNT)); } + +CPPUNIT_TEST_FIXTURE(Test, testSelectDateContentControl) +{ + // Given a document with a date content control: + SwDoc* pDoc = createSwDoc(); + uno::Reference<lang::XMultiServiceFactory> xMSF(mxComponent, uno::UNO_QUERY); + uno::Reference<text::XTextDocument> xTextDocument(mxComponent, uno::UNO_QUERY); + uno::Reference<text::XText> xText = xTextDocument->getText(); + uno::Reference<text::XTextCursor> xCursor = xText->createTextCursor(); + xText->insertString(xCursor, "test", /*bAbsorb=*/false); + xCursor->gotoStart(/*bExpand=*/false); + xCursor->gotoEnd(/*bExpand=*/true); + uno::Reference<text::XTextContent> xContentControl( + xMSF->createInstance("com.sun.star.text.ContentControl"), uno::UNO_QUERY); + uno::Reference<beans::XPropertySet> xContentControlProps(xContentControl, uno::UNO_QUERY); + xContentControlProps->setPropertyValue("Date", uno::Any(true)); + xContentControlProps->setPropertyValue("DateFormat", uno::Any(OUString("YYYY-MM-DD"))); + xContentControlProps->setPropertyValue("DateLanguage", uno::Any(OUString("en-US"))); + xText->insertTextContent(xCursor, xContentControl, /*bAbsorb=*/true); + + // When clicking on that content control: + SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell(); + SwTextNode* pTextNode = pWrtShell->GetCursor()->GetNode().GetTextNode(); + SwTextAttr* pAttr = pTextNode->GetTextAttrForCharAt(0, RES_TXTATR_CONTENTCONTROL); + auto pTextContentControl = static_txtattr_cast<SwTextContentControl*>(pAttr); + auto& rFormatContentControl + = static_cast<SwFormatContentControl&>(pTextContentControl->GetAttr()); + rFormatContentControl.GetContentControl()->SetSelectedDate(44705); + pWrtShell->GotoContentControl(rFormatContentControl); + + // Then make sure that the document text is updated: + // Without the accompanying fix in place, this test would have failed with: + // - Expected: 2022-05-24 + // - Actual : test + // i.e. the content control was not updated. + CPPUNIT_ASSERT_EQUAL(OUString("2022-05-24"), pTextNode->GetExpandText(pWrtShell->GetLayout())); +} } CPPUNIT_PLUGIN_IMPLEMENT(); diff --git a/sw/source/core/crsr/crstrvl.cxx b/sw/source/core/crsr/crstrvl.cxx index e36cf1671007..ee12399323ac 100644 --- a/sw/source/core/crsr/crstrvl.cxx +++ b/sw/source/core/crsr/crstrvl.cxx @@ -858,7 +858,7 @@ bool SwCursorShell::GotoFormatContentControl(const SwFormatContentControl& rCont bool bRet = false; std::shared_ptr<SwContentControl> pContentControl = rContentControl.GetContentControl(); if (!pContentControl->GetShowingPlaceHolder() && !pContentControl->GetCheckbox() - && !pContentControl->GetSelectedListItem()) + && !pContentControl->GetSelectedListItem() && !pContentControl->GetSelectedDate()) { return bRet; } diff --git a/sw/source/core/crsr/datecontentcontrolbutton.cxx b/sw/source/core/crsr/datecontentcontrolbutton.cxx new file mode 100644 index 000000000000..c52f546e9c6f --- /dev/null +++ b/sw/source/core/crsr/datecontentcontrolbutton.cxx @@ -0,0 +1,59 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */ +/* + * 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/. + */ + +#include <datecontentcontrolbutton.hxx> + +#include <svl/numformat.hxx> +#include <tools/date.hxx> +#include <vcl/svapp.hxx> + +#include <edtwin.hxx> +#include <formatcontentcontrol.hxx> +#include <view.hxx> +#include <wrtsh.hxx> + +IMPL_LINK(SwDateContentControlButton, SelectHandler, weld::Calendar&, rCalendar, void) +{ + const Date& rNullDate = m_pNumberFormatter->GetNullDate(); + double fDate = rCalendar.get_date() - rNullDate; + m_xPopup->popdown(); + m_pContentControl->SetSelectedDate(fDate); + SwView& rView = static_cast<SwEditWin*>(GetParent())->GetView(); + SwWrtShell& rWrtShell = rView.GetWrtShell(); + rWrtShell.GotoContentControl(*m_pContentControl->GetFormatContentControl()); +} + +SwDateContentControlButton::SwDateContentControlButton( + SwEditWin* pEditWin, const std::shared_ptr<SwContentControl>& pContentControl, + SvNumberFormatter* pNumberFormatter) + : SwContentControlButton(pEditWin, pContentControl) + , m_pNumberFormatter(pNumberFormatter) +{ +} + +SwDateContentControlButton::~SwDateContentControlButton() { disposeOnce(); } + +void SwDateContentControlButton::LaunchPopup() +{ + m_xPopupBuilder = Application::CreateBuilder(GetFrameWeld(), + "modules/swriter/ui/contentcontrolcalendar.ui"); + m_xPopup = m_xPopupBuilder->weld_popover("Calendar"); + m_xCalendar = m_xPopupBuilder->weld_calendar("date"); + m_xCalendar->connect_activated(LINK(this, SwDateContentControlButton, SelectHandler)); + SwContentControlButton::LaunchPopup(); + m_xCalendar->grab_focus(); +} + +void SwDateContentControlButton::DestroyPopup() +{ + m_xCalendar.reset(); + SwContentControlButton::DestroyPopup(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/core/crsr/viscrs.cxx b/sw/source/core/crsr/viscrs.cxx index 6991ee8a7a95..9863e1a75324 100644 --- a/sw/source/core/crsr/viscrs.cxx +++ b/sw/source/core/crsr/viscrs.cxx @@ -63,6 +63,7 @@ #include <wrtsh.hxx> #include <textcontentcontrol.hxx> #include <dropdowncontentcontrolbutton.hxx> +#include <datecontentcontrolbutton.hxx> // Here static members are defined. They will get changed on alteration of the // MapMode. This is done so that on ShowCursor the same size does not have to be @@ -753,6 +754,26 @@ void SwSelPaintRects::HighlightContentControl() m_pContentControlButton->Show(); } } + if (pContentControl && pContentControl->GetDate()) + { + auto pWrtShell = dynamic_cast<const SwWrtShell*>(GetShell()); + if (pWrtShell) + { + auto& rEditWin = const_cast<SwEditWin&>(pWrtShell->GetView().GetEditWin()); + if (m_pContentControlButton + && m_pContentControlButton->GetContentControl() != pContentControl) + { + m_pContentControlButton.disposeAndClear(); + } + if (!m_pContentControlButton) + { + m_pContentControlButton = VclPtr<SwDateContentControlButton>::Create( + &rEditWin, pContentControl, pWrtShell->GetDoc()->GetNumberFormatter()); + } + m_pContentControlButton->CalcPosAndSize(aLastPortionPaintArea); + m_pContentControlButton->Show(); + } + } } else { diff --git a/sw/source/core/inc/datecontentcontrolbutton.hxx b/sw/source/core/inc/datecontentcontrolbutton.hxx new file mode 100644 index 000000000000..48b08f2db981 --- /dev/null +++ b/sw/source/core/inc/datecontentcontrolbutton.hxx @@ -0,0 +1,41 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */ +/* + * 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/. + */ + +#pragma once + +#include "contentcontrolbutton.hxx" + +class SwEditWin; +class SvNumberFormatter; +class SwContentControl; + +/** + * This button is shown when the cursor is on a date content control. The user can select a date + * from a date picker. + */ +class SwDateContentControlButton final : public SwContentControlButton +{ +private: + SvNumberFormatter* m_pNumberFormatter; + + std::unique_ptr<weld::Calendar> m_xCalendar; + + DECL_LINK(SelectHandler, weld::Calendar&, void); + +public: + SwDateContentControlButton(SwEditWin* pEditWin, + const std::shared_ptr<SwContentControl>& pContentControl, + SvNumberFormatter* pNumberFormatter); + ~SwDateContentControlButton() override; + + void LaunchPopup() override; + void DestroyPopup() override; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/core/txtnode/attrcontentcontrol.cxx b/sw/source/core/txtnode/attrcontentcontrol.cxx index 7eb39907ce0c..06dc388ee1ef 100644 --- a/sw/source/core/txtnode/attrcontentcontrol.cxx +++ b/sw/source/core/txtnode/attrcontentcontrol.cxx @@ -24,9 +24,11 @@ #include <sal/log.hxx> #include <comphelper/propertyvalue.hxx> #include <comphelper/sequenceashashmap.hxx> +#include <svl/numformat.hxx> #include <ndtxt.hxx> #include <textcontentcontrol.hxx> +#include <doc.hxx> using namespace com::sun::star; @@ -207,6 +209,39 @@ void SwContentControl::SwClientNotify(const SwModify&, const SfxHint& rHint) } } +OUString SwContentControl::GetDateString() const +{ + SwDoc& rDoc = m_pTextNode->GetDoc(); + SvNumberFormatter* pNumberFormatter = rDoc.GetNumberFormatter(); + sal_uInt32 nFormat = pNumberFormatter->GetEntryKey( + m_aDateFormat, LanguageTag(m_aDateLanguage).getLanguageType()); + + if (nFormat == NUMBERFORMAT_ENTRY_NOT_FOUND) + { + // Try to find a format based on just the language. + sal_Int32 nCheckPos = 0; + SvNumFormatType nType; + OUString aFormat = m_aDateFormat; + pNumberFormatter->PutEntry(aFormat, nCheckPos, nType, nFormat, + LanguageTag(m_aDateLanguage).getLanguageType()); + } + + const Color* pColor = nullptr; + OUString aFormatted; + if (!m_oSelectedDate) + { + return OUString(); + } + + if (nFormat == NUMBERFORMAT_ENTRY_NOT_FOUND) + { + return OUString(); + } + + pNumberFormatter->GetOutputString(*m_oSelectedDate, nFormat, aFormatted, &pColor, false); + return aFormatted; +} + void SwContentControl::dumpAsXml(xmlTextWriterPtr pWriter) const { (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwContentControl")); diff --git a/sw/source/uibase/wrtsh/wrtsh3.cxx b/sw/source/uibase/wrtsh/wrtsh3.cxx index fcb123662bcf..f01946e45d17 100644 --- a/sw/source/uibase/wrtsh/wrtsh3.cxx +++ b/sw/source/uibase/wrtsh/wrtsh3.cxx @@ -178,6 +178,27 @@ bool SwWrtShell::GotoContentControl(const SwFormatContentControl& rContentContro LockView(/*bViewLocked=*/false); ShowCursor(); } + else if (bRet && pContentControl && pContentControl->GetSelectedDate()) + { + // Date: GotoFormatContentControl() selected the old content. + LockView(/*bViewLocked=*/true); + OUString aOldState = GetCursorDescr(); + OUString aNewState = pContentControl->GetDateString(); + SwRewriter aRewriter; + aRewriter.AddRule(UndoArg1, aOldState); + aRewriter.AddRule(UndoArg2, SwResId(STR_YIELDS)); + aRewriter.AddRule(UndoArg3, SwResId(STR_START_QUOTE) + aNewState + SwResId(STR_END_QUOTE)); + GetIDocumentUndoRedo().StartUndo(SwUndoId::REPLACE, &aRewriter); + + // Update the content. + DelLeft(); + pContentControl->SetSelectedDate(std::nullopt); + Insert(aNewState); + + GetIDocumentUndoRedo().EndUndo(SwUndoId::REPLACE, &aRewriter); + LockView(/*bViewLocked=*/false); + ShowCursor(); + } if (bRet && IsSelFrameMode()) { diff --git a/sw/uiconfig/swriter/ui/contentcontrolcalendar.ui b/sw/uiconfig/swriter/ui/contentcontrolcalendar.ui new file mode 100644 index 000000000000..e5355f723621 --- /dev/null +++ b/sw/uiconfig/swriter/ui/contentcontrolcalendar.ui @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Generated with glade 3.38.2 --> +<interface domain="sw"> + <requires lib="gtk+" version="3.20"/> + <object class="GtkPopover" id="Calendar"> + <property name="can-focus">False</property> + <property name="position">bottom</property> + <child> + <object class="GtkBox"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="orientation">vertical</property> + <property name="spacing">6</property> + <child> + <object class="GtkCalendar" id="date"> + <property name="visible">True</property> + <property name="can-focus">True</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + </object> + </child> + </object> +</interface> |