xref: /core/sfx2/source/dialog/dinfdlg.cxx (revision 0572ef35)
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
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/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
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 .
18  */
19 
20 #include <svl/eitem.hxx>
21 #include <tools/datetime.hxx>
22 #include <tools/duration.hxx>
23 #include <tools/debug.hxx>
24 #include <tools/urlobj.hxx>
25 #include <utility>
26 #include <vcl/svapp.hxx>
27 #include <vcl/weld.hxx>
28 #include <vcl/weldutils.hxx>
29 #include <unotools/datetime.hxx>
30 #include <unotools/localedatawrapper.hxx>
31 #include <unotools/cmdoptions.hxx>
32 #include <comphelper/processfactory.hxx>
33 #include <comphelper/propertyvalue.hxx>
34 #include <comphelper/stl_types.hxx>
35 #include <comphelper/xmlsechelper.hxx>
36 #include <unotools/useroptions.hxx>
37 #include <svtools/ctrlbox.hxx>
38 #include <svtools/imagemgr.hxx>
39 #include <sal/log.hxx>
40 #include <osl/diagnose.h>
41 #include <osl/file.hxx>
42 #include <comphelper/lok.hxx>
43 #include <LibreOfficeKit/LibreOfficeKitEnums.h>
44 #include <tools/json_writer.hxx>
45 
46 #include <memory>
47 
48 #include <comphelper/sequence.hxx>
49 #include <comphelper/string.hxx>
50 #include <com/sun/star/security/DocumentSignatureInformation.hpp>
51 #include <com/sun/star/security/DocumentDigitalSignatures.hpp>
52 #include <unotools/syslocale.hxx>
53 #include <rtl/math.hxx>
54 #include <com/sun/star/beans/PropertyAttribute.hpp>
55 #include <com/sun/star/beans/XPropertyContainer.hpp>
56 #include <com/sun/star/beans/XPropertySet.hpp>
57 #include <com/sun/star/util/DateTime.hpp>
58 #include <com/sun/star/util/Date.hpp>
59 #include <com/sun/star/util/DateTimeWithTimezone.hpp>
60 #include <com/sun/star/util/DateWithTimezone.hpp>
61 #include <com/sun/star/util/Duration.hpp>
62 #include <com/sun/star/document/XDocumentProperties2.hpp>
63 #include <com/sun/star/document/CmisProperty.hpp>
64 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
65 
66 #include <vcl/timer.hxx>
67 #include <vcl/settings.hxx>
68 #include <sfx2/sfxresid.hxx>
69 #include <sfx2/frame.hxx>
70 #include <sfx2/filedlghelper.hxx>
71 #include <sfx2/dinfdlg.hxx>
72 #include <sfx2/sfxsids.hrc>
73 #include <helper.hxx>
74 #include <sfx2/objsh.hxx>
75 #include <sfx2/docfile.hxx>
76 #include <vcl/abstdlg.hxx>
77 #include <sfx2/viewsh.hxx>
78 
79 #include <documentfontsdialog.hxx>
80 #include <dinfdlg.hrc>
81 #include <sfx2/strings.hrc>
82 #include <strings.hxx>
83 #include <comphelper/diagnose_ex.hxx>
84 #include "securitypage.hxx"
85 
86 #include <algorithm>
87 
88 using namespace ::com::sun::star;
89 using namespace ::com::sun::star::uno;
90 
91 struct CustomProperty
92 {
93     OUString         m_sName;
94     css::uno::Any    m_aValue;
95 
CustomPropertyCustomProperty96     CustomProperty( OUString sName, css::uno::Any aValue ) :
97         m_sName(std::move( sName )), m_aValue(std::move( aValue )) {}
98 
operator ==CustomProperty99     bool operator==(const CustomProperty& rProp) const
100     {
101         return m_sName == rProp.m_sName && m_aValue == rProp.m_aValue;
102     }
103 };
104 
CreateDefault()105 SfxPoolItem* SfxDocumentInfoItem::CreateDefault() { return new SfxDocumentInfoItem; }
106 
107 namespace {
108 
CreateSizeText(sal_Int64 nSize)109 OUString CreateSizeText( sal_Int64 nSize )
110 {
111     OUString aUnitStr = " " + SfxResId(STR_BYTES);
112     sal_Int64 nSize1 = nSize;
113     sal_Int64 nSize2 = nSize1;
114     sal_Int64 nMega = 1024 * 1024;
115     sal_Int64 nGiga = nMega * 1024;
116     double fSize = nSize;
117     int nDec = 0;
118 
119     if ( nSize1 >= 10000 && nSize1 < nMega )
120     {
121         nSize1 /= 1024;
122         aUnitStr = " " + SfxResId(STR_KB);
123         fSize /= 1024;
124         nDec = 0;
125     }
126     else if ( nSize1 >= nMega && nSize1 < nGiga )
127     {
128         nSize1 /= nMega;
129         aUnitStr = " " + SfxResId(STR_MB);
130         fSize /= nMega;
131         nDec = 2;
132     }
133     else if ( nSize1 >= nGiga )
134     {
135         nSize1 /= nGiga;
136         aUnitStr = " " + SfxResId(STR_GB);
137         fSize /= nGiga;
138         nDec = 3;
139     }
140     const SvtSysLocale aSysLocale;
141     const LocaleDataWrapper& rLocaleWrapper = aSysLocale.GetLocaleData();
142     OUString aSizeStr = rLocaleWrapper.getNum( nSize1, 0 ) + aUnitStr;
143     if ( nSize1 < nSize2 )
144     {
145         aSizeStr = ::rtl::math::doubleToUString( fSize,
146                 rtl_math_StringFormat_F, nDec,
147                 rLocaleWrapper.getNumDecimalSep()[0] )
148                  + aUnitStr
149                  + " ("
150                  + rLocaleWrapper.getNum( nSize2, 0 )
151                  + " "
152                  + SfxResId(STR_BYTES)
153                  + ")";
154     }
155     return aSizeStr;
156 }
157 
ConvertDateTime_Impl(std::u16string_view rName,const util::DateTime & uDT,const LocaleDataWrapper & rWrapper)158 OUString ConvertDateTime_Impl( std::u16string_view rName,
159     const util::DateTime& uDT, const LocaleDataWrapper& rWrapper )
160 {
161      Date aD(uDT);
162      tools::Time aT(uDT);
163      static constexpr OUString aDelim( u", "_ustr );
164      OUString aStr = rWrapper.getDate( aD )
165                    + aDelim
166                    + rWrapper.getTime( aT );
167      std::u16string_view aAuthor = comphelper::string::stripStart(rName, ' ');
168      if (!aAuthor.empty())
169      {
170         aStr += aDelim + aAuthor;
171      }
172      return aStr;
173 }
174 
175 }
176 
177 
SfxDocumentInfoItem()178 SfxDocumentInfoItem::SfxDocumentInfoItem()
179     : m_AutoloadDelay(0)
180     , m_isAutoloadEnabled(false)
181     , m_EditingCycles(0)
182     , m_EditingDuration(0)
183     , m_nFileSize(-1)
184     , m_bHasTemplate( true )
185     , m_bDeleteUserData( false )
186     , m_bUseUserData( true )
187     , m_bUseThumbnailSave( true )
188 {
189 }
190 
SfxDocumentInfoItem(const OUString & rFile,const uno::Reference<document::XDocumentProperties> & i_xDocProps,const uno::Sequence<document::CmisProperty> & i_cmisProps,bool bIs,bool _bIs,sal_Int64 _nFileSize)191 SfxDocumentInfoItem::SfxDocumentInfoItem( const OUString& rFile,
192         const uno::Reference<document::XDocumentProperties>& i_xDocProps,
193         const uno::Sequence<document::CmisProperty>& i_cmisProps,
194         bool bIs, bool _bIs, sal_Int64 _nFileSize )
195     : SfxStringItem( SID_DOCINFO, rFile, SfxItemType::SfxDocumentInfoItemType )
196     , m_AutoloadDelay( i_xDocProps->getAutoloadSecs() )
197     , m_AutoloadURL( i_xDocProps->getAutoloadURL() )
198     , m_isAutoloadEnabled( (m_AutoloadDelay > 0) || !m_AutoloadURL.isEmpty() )
199     , m_DefaultTarget( i_xDocProps->getDefaultTarget() )
200     , m_TemplateName( i_xDocProps->getTemplateName() )
201     , m_Author( i_xDocProps->getAuthor() )
202     , m_CreationDate( i_xDocProps->getCreationDate() )
203     , m_ModifiedBy( i_xDocProps->getModifiedBy() )
204     , m_ModificationDate( i_xDocProps->getModificationDate() )
205     , m_PrintedBy( i_xDocProps->getPrintedBy() )
206     , m_PrintDate( i_xDocProps->getPrintDate() )
207     , m_EditingCycles( i_xDocProps->getEditingCycles() )
208     , m_EditingDuration( i_xDocProps->getEditingDuration() )
209     , m_Description( i_xDocProps->getDescription() )
210     , m_Keywords( ::comphelper::string::convertCommaSeparated(
211                     i_xDocProps->getKeywords()) )
212     , m_Subject( i_xDocProps->getSubject() )
213     , m_Title( i_xDocProps->getTitle() )
214     , m_nFileSize( _nFileSize )
215     , m_bHasTemplate( true )
216     , m_bDeleteUserData( false )
217     , m_bUseUserData( bIs )
218     , m_bUseThumbnailSave( _bIs )
219 {
220     Reference< document::XDocumentProperties2 > xDocProps2(i_xDocProps, UNO_QUERY);
221     m_Contributor = ::comphelper::string::convertCommaSeparated(xDocProps2->getContributor());
222     m_Coverage = xDocProps2->getCoverage();
223     m_Identifier = xDocProps2->getIdentifier();
224     m_Publisher = ::comphelper::string::convertCommaSeparated(xDocProps2->getPublisher());
225     m_Relation = ::comphelper::string::convertCommaSeparated(xDocProps2->getRelation());
226     m_Rights = xDocProps2->getRights();
227     m_Source = xDocProps2->getSource();
228     m_Type = xDocProps2->getType();
229 
230     try
231     {
232         Reference< beans::XPropertyContainer > xContainer = i_xDocProps->getUserDefinedProperties();
233         if ( xContainer.is() )
234         {
235             Reference < beans::XPropertySet > xSet( xContainer, UNO_QUERY );
236             const Sequence< beans::Property > lProps = xSet->getPropertySetInfo()->getProperties();
237             for ( const beans::Property& rProp : lProps )
238             {
239                 // "fix" property? => not a custom property => ignore it!
240                 if (!(rProp.Attributes & css::beans::PropertyAttribute::REMOVABLE))
241                 {
242                     SAL_WARN( "sfx.dialog", "non-removable user-defined property?");
243                     continue;
244                 }
245 
246                 uno::Any aValue = xSet->getPropertyValue(rProp.Name);
247                 AddCustomProperty( rProp.Name, aValue );
248             }
249         }
250 
251         // get CMIS properties
252         m_aCmisProperties = i_cmisProps;
253     }
254     catch ( Exception& ) {}
255 }
256 
257 
SfxDocumentInfoItem(const SfxDocumentInfoItem & rItem)258 SfxDocumentInfoItem::SfxDocumentInfoItem( const SfxDocumentInfoItem& rItem )
259     : SfxStringItem( rItem )
260     , m_AutoloadDelay( rItem.getAutoloadDelay() )
261     , m_AutoloadURL( rItem.getAutoloadURL() )
262     , m_isAutoloadEnabled( rItem.isAutoloadEnabled() )
263     , m_DefaultTarget( rItem.getDefaultTarget() )
264     , m_TemplateName( rItem.getTemplateName() )
265     , m_Author( rItem.getAuthor() )
266     , m_CreationDate( rItem.getCreationDate() )
267     , m_ModifiedBy( rItem.getModifiedBy() )
268     , m_ModificationDate( rItem.getModificationDate() )
269     , m_PrintedBy( rItem.getPrintedBy() )
270     , m_PrintDate( rItem.getPrintDate() )
271     , m_EditingCycles( rItem.getEditingCycles() )
272     , m_EditingDuration( rItem.getEditingDuration() )
273     , m_Description( rItem.getDescription() )
274     , m_Keywords( rItem.getKeywords() )
275     , m_Contributor(rItem.getContributor())
276     , m_Coverage(rItem.getCoverage())
277     , m_Identifier(rItem.getIdentifier())
278     , m_Publisher(rItem.getPublisher())
279     , m_Relation(rItem.getRelation())
280     , m_Rights(rItem.getRights())
281     , m_Source(rItem.getSource())
282     , m_Type(rItem.getType())
283     , m_Subject( rItem.getSubject() )
284     , m_Title( rItem.getTitle() )
285     , m_nFileSize ( rItem.m_nFileSize )
286     , m_bHasTemplate( rItem.m_bHasTemplate )
287     , m_bDeleteUserData( rItem.m_bDeleteUserData )
288     , m_bUseUserData( rItem.m_bUseUserData )
289     , m_bUseThumbnailSave( rItem.m_bUseThumbnailSave )
290 {
291     for (auto const & pOtherProp : rItem.m_aCustomProperties)
292     {
293         AddCustomProperty( pOtherProp->m_sName, pOtherProp->m_aValue );
294     }
295 
296     m_aCmisProperties = rItem.m_aCmisProperties;
297 }
298 
~SfxDocumentInfoItem()299 SfxDocumentInfoItem::~SfxDocumentInfoItem()
300 {
301     ClearCustomProperties();
302 }
303 
Clone(SfxItemPool *) const304 SfxDocumentInfoItem* SfxDocumentInfoItem::Clone( SfxItemPool * ) const
305 {
306     return new SfxDocumentInfoItem( *this );
307 }
308 
operator ==(const SfxPoolItem & rItem) const309 bool SfxDocumentInfoItem::operator==( const SfxPoolItem& rItem) const
310 {
311     if (!SfxStringItem::operator==(rItem))
312         return false;
313     const SfxDocumentInfoItem& rInfoItem(static_cast<const SfxDocumentInfoItem&>(rItem));
314 
315     return
316          m_AutoloadDelay        == rInfoItem.m_AutoloadDelay     &&
317          m_AutoloadURL          == rInfoItem.m_AutoloadURL       &&
318          m_isAutoloadEnabled    == rInfoItem.m_isAutoloadEnabled &&
319          m_DefaultTarget        == rInfoItem.m_DefaultTarget     &&
320          m_Author               == rInfoItem.m_Author            &&
321          m_CreationDate         == rInfoItem.m_CreationDate      &&
322          m_ModifiedBy           == rInfoItem.m_ModifiedBy        &&
323          m_ModificationDate     == rInfoItem.m_ModificationDate  &&
324          m_PrintedBy            == rInfoItem.m_PrintedBy         &&
325          m_PrintDate            == rInfoItem.m_PrintDate         &&
326          m_EditingCycles        == rInfoItem.m_EditingCycles     &&
327          m_EditingDuration      == rInfoItem.m_EditingDuration   &&
328          m_Description          == rInfoItem.m_Description       &&
329          m_Keywords             == rInfoItem.m_Keywords          &&
330          m_Contributor          == rInfoItem.m_Contributor       &&
331          m_Coverage             == rInfoItem.m_Coverage          &&
332          m_Identifier           == rInfoItem.m_Identifier        &&
333          m_Publisher            == rInfoItem.m_Publisher         &&
334          m_Relation             == rInfoItem.m_Relation          &&
335          m_Rights               == rInfoItem.m_Rights            &&
336          m_Source               == rInfoItem.m_Source            &&
337          m_Type                 == rInfoItem.m_Type              &&
338          m_Subject              == rInfoItem.m_Subject           &&
339          m_Title                == rInfoItem.m_Title             &&
340          comphelper::ContainerUniquePtrEquals(m_aCustomProperties, rInfoItem.m_aCustomProperties) &&
341          m_aCmisProperties.getLength() == rInfoItem.m_aCmisProperties.getLength();
342 }
343 
344 
resetUserData(const OUString & i_rAuthor)345 void SfxDocumentInfoItem::resetUserData(const OUString & i_rAuthor)
346 {
347     m_Author = i_rAuthor;
348     DateTime now( DateTime::SYSTEM );
349     m_CreationDate = now.GetUNODateTime();
350     m_ModifiedBy = OUString();
351     m_PrintedBy = OUString();
352     m_ModificationDate = util::DateTime();
353     m_PrintDate = util::DateTime();
354     m_EditingDuration = 0;
355     m_EditingCycles = 1;
356 }
357 
358 
UpdateDocumentInfo(const uno::Reference<document::XDocumentProperties> & i_xDocProps,bool i_bDoNotUpdateUserDefined) const359 void SfxDocumentInfoItem::UpdateDocumentInfo(
360         const uno::Reference<document::XDocumentProperties>& i_xDocProps,
361         bool i_bDoNotUpdateUserDefined) const
362 {
363     if (isAutoloadEnabled()) {
364         i_xDocProps->setAutoloadSecs(getAutoloadDelay());
365         i_xDocProps->setAutoloadURL(getAutoloadURL());
366     } else {
367         i_xDocProps->setAutoloadSecs(0);
368         i_xDocProps->setAutoloadURL(OUString());
369     }
370     i_xDocProps->setDefaultTarget(getDefaultTarget());
371     i_xDocProps->setAuthor(getAuthor());
372     i_xDocProps->setCreationDate(getCreationDate());
373     i_xDocProps->setModifiedBy(getModifiedBy());
374     i_xDocProps->setModificationDate(getModificationDate());
375     i_xDocProps->setPrintedBy(getPrintedBy());
376     i_xDocProps->setPrintDate(getPrintDate());
377     i_xDocProps->setEditingCycles(getEditingCycles());
378     i_xDocProps->setEditingDuration(getEditingDuration());
379     i_xDocProps->setDescription(getDescription());
380     i_xDocProps->setKeywords(
381         ::comphelper::string::convertCommaSeparated(getKeywords()));
382 
383     Reference<document::XDocumentProperties2> xDocProps2(i_xDocProps, UNO_QUERY);
384     xDocProps2->setContributor(::comphelper::string::convertCommaSeparated(getContributor()));
385     xDocProps2->setCoverage(getCoverage());
386     xDocProps2->setIdentifier(getIdentifier());
387     xDocProps2->setPublisher(::comphelper::string::convertCommaSeparated(getPublisher()));
388     xDocProps2->setRelation(::comphelper::string::convertCommaSeparated(getRelation()));
389     xDocProps2->setRights(getRights());
390     xDocProps2->setSource(getSource());
391     xDocProps2->setType(getType());
392     xDocProps2->setSubject(getSubject());
393     xDocProps2->setTitle(getTitle());
394 
395     // this is necessary in case of replaying a recorded macro:
396     // in this case, the macro may contain the 4 old user-defined DocumentInfo
397     // fields, but not any of the DocumentInfo properties;
398     // as a consequence, most of the UserDefined properties of the
399     // DocumentProperties would be summarily deleted here, which does not
400     // seem like a good idea.
401     if (i_bDoNotUpdateUserDefined)
402         return;
403 
404     try
405     {
406         Reference< beans::XPropertyContainer > xContainer = i_xDocProps->getUserDefinedProperties();
407         Reference < beans::XPropertySet > xSet( xContainer, UNO_QUERY );
408         Reference< beans::XPropertySetInfo > xSetInfo = xSet->getPropertySetInfo();
409         const Sequence< beans::Property > lProps = xSetInfo->getProperties();
410         for ( const beans::Property& rProp : lProps )
411         {
412             if (rProp.Attributes & css::beans::PropertyAttribute::REMOVABLE)
413             {
414                 xContainer->removeProperty( rProp.Name );
415             }
416         }
417 
418         for (auto const & pProp : m_aCustomProperties)
419         {
420             try
421             {
422                 xContainer->addProperty( pProp->m_sName,
423                     beans::PropertyAttribute::REMOVABLE, pProp->m_aValue );
424             }
425             catch ( Exception const & )
426             {
427                 TOOLS_WARN_EXCEPTION( "sfx.dialog", "SfxDocumentInfoItem::updateDocumentInfo(): exception while adding custom properties" );
428             }
429         }
430     }
431     catch ( Exception const & )
432     {
433         TOOLS_WARN_EXCEPTION( "sfx.dialog", "SfxDocumentInfoItem::updateDocumentInfo(): exception while removing custom properties" );
434     }
435 }
436 
437 
SetDeleteUserData(bool bSet)438 void SfxDocumentInfoItem::SetDeleteUserData( bool bSet )
439 {
440     m_bDeleteUserData = bSet;
441 }
442 
443 
SetUseUserData(bool bSet)444 void SfxDocumentInfoItem::SetUseUserData( bool bSet )
445 {
446     m_bUseUserData = bSet;
447 }
448 
SetUseThumbnailSave(bool bSet)449 void SfxDocumentInfoItem::SetUseThumbnailSave( bool bSet )
450 {
451     m_bUseThumbnailSave = bSet;
452 }
453 
GetCustomProperties() const454 std::vector< std::unique_ptr<CustomProperty> > SfxDocumentInfoItem::GetCustomProperties() const
455 {
456     std::vector< std::unique_ptr<CustomProperty> > aRet;
457     for (auto const & pOtherProp : m_aCustomProperties)
458     {
459         std::unique_ptr<CustomProperty> pProp(new CustomProperty( pOtherProp->m_sName,
460                                                     pOtherProp->m_aValue ));
461         aRet.push_back( std::move(pProp) );
462     }
463 
464     return aRet;
465 }
466 
ClearCustomProperties()467 void SfxDocumentInfoItem::ClearCustomProperties()
468 {
469     m_aCustomProperties.clear();
470 }
471 
AddCustomProperty(const OUString & sName,const Any & rValue)472 void SfxDocumentInfoItem::AddCustomProperty( const OUString& sName, const Any& rValue )
473 {
474     std::unique_ptr<CustomProperty> pProp(new CustomProperty( sName, rValue ));
475     m_aCustomProperties.push_back( std::move(pProp) );
476 }
477 
478 
SetCmisProperties(const Sequence<document::CmisProperty> & cmisProps)479 void SfxDocumentInfoItem::SetCmisProperties( const Sequence< document::CmisProperty >& cmisProps)
480 {
481     m_aCmisProperties = cmisProps;
482 }
483 
QueryValue(Any & rVal,sal_uInt8 nMemberId) const484 bool SfxDocumentInfoItem::QueryValue( Any& rVal, sal_uInt8 nMemberId ) const
485 {
486     OUString aValue;
487     sal_Int32 nValue = 0;
488     bool bValue = false;
489     bool bIsInt = false;
490     bool bIsString = false;
491     nMemberId &= ~CONVERT_TWIPS;
492     switch ( nMemberId )
493     {
494         case MID_DOCINFO_USEUSERDATA:
495             bValue = IsUseUserData();
496             break;
497         case MID_DOCINFO_USETHUMBNAILSAVE:
498             bValue = IsUseThumbnailSave();
499             break;
500         case MID_DOCINFO_DELETEUSERDATA:
501             bValue = m_bDeleteUserData;
502             break;
503         case MID_DOCINFO_AUTOLOADENABLED:
504             bValue = isAutoloadEnabled();
505             break;
506         case MID_DOCINFO_AUTOLOADSECS:
507             bIsInt = true;
508             nValue = getAutoloadDelay();
509             break;
510         case MID_DOCINFO_AUTOLOADURL:
511             bIsString = true;
512             aValue = getAutoloadURL();
513             break;
514         case MID_DOCINFO_DEFAULTTARGET:
515             bIsString = true;
516             aValue = getDefaultTarget();
517             break;
518         case MID_DOCINFO_DESCRIPTION:
519             bIsString = true;
520             aValue = getDescription();
521             break;
522         case MID_DOCINFO_KEYWORDS:
523             bIsString = true;
524             aValue = getKeywords();
525             break;
526         case MID_DOCINFO_SUBJECT:
527             bIsString = true;
528             aValue = getSubject();
529             break;
530         case MID_DOCINFO_TITLE:
531             bIsString = true;
532             aValue = getTitle();
533             break;
534         default:
535             OSL_FAIL("Wrong MemberId!");
536             return false;
537      }
538 
539     if ( bIsString )
540         rVal <<= aValue;
541     else if ( bIsInt )
542         rVal <<= nValue;
543     else
544         rVal <<= bValue;
545     return true;
546 }
547 
PutValue(const Any & rVal,sal_uInt8 nMemberId)548 bool SfxDocumentInfoItem::PutValue( const Any& rVal, sal_uInt8 nMemberId )
549 {
550     OUString aValue;
551     sal_Int32 nValue=0;
552     bool bValue = false;
553     bool bRet = false;
554     nMemberId &= ~CONVERT_TWIPS;
555     switch ( nMemberId )
556     {
557         case MID_DOCINFO_USEUSERDATA:
558             bRet = (rVal >>= bValue);
559             if ( bRet )
560                 SetUseUserData( bValue );
561             break;
562         case MID_DOCINFO_USETHUMBNAILSAVE:
563             bRet = (rVal >>=bValue);
564             if ( bRet )
565                 SetUseThumbnailSave( bValue );
566             break;
567         case MID_DOCINFO_DELETEUSERDATA:
568             // QUESTION: deleting user data was done here; seems to be superfluous!
569             bRet = (rVal >>= bValue);
570             if ( bRet )
571                 SetDeleteUserData( bValue );
572             break;
573         case MID_DOCINFO_AUTOLOADENABLED:
574             bRet = (rVal >>= bValue);
575             if ( bRet )
576                 m_isAutoloadEnabled = bValue;
577             break;
578         case MID_DOCINFO_AUTOLOADSECS:
579             bRet = (rVal >>= nValue);
580             if ( bRet )
581                 m_AutoloadDelay = nValue;
582             break;
583         case MID_DOCINFO_AUTOLOADURL:
584             bRet = (rVal >>= aValue);
585             if ( bRet )
586                 m_AutoloadURL = aValue;
587             break;
588         case MID_DOCINFO_DEFAULTTARGET:
589             bRet = (rVal >>= aValue);
590             if ( bRet )
591                 m_DefaultTarget = aValue;
592             break;
593         case MID_DOCINFO_DESCRIPTION:
594             bRet = (rVal >>= aValue);
595             if ( bRet )
596                 setDescription(aValue);
597             break;
598         case MID_DOCINFO_KEYWORDS:
599             bRet = (rVal >>= aValue);
600             if ( bRet )
601                 setKeywords(aValue);
602             break;
603         case MID_DOCINFO_SUBJECT:
604             bRet = (rVal >>= aValue);
605             if ( bRet )
606                 setSubject(aValue);
607             break;
608         case MID_DOCINFO_TITLE:
609             bRet = (rVal >>= aValue);
610             if ( bRet )
611                 setTitle(aValue);
612             break;
613         default:
614             OSL_FAIL("Wrong MemberId!");
615             return false;
616     }
617 
618     return bRet;
619 }
620 
SfxDocumentDescPage(weld::Container * pPage,weld::DialogController * pController,const SfxItemSet & rItemSet)621 SfxDocumentDescPage::SfxDocumentDescPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rItemSet)
622     : SfxTabPage(pPage, pController, u"sfx/ui/descriptioninfopage.ui"_ustr, u"DescriptionInfoPage"_ustr, &rItemSet)
623     , m_pInfoItem(nullptr)
624     , m_xTitleEd(m_xBuilder->weld_entry(u"title"_ustr))
625     , m_xThemaEd(m_xBuilder->weld_entry(u"subject"_ustr))
626     , m_xKeywordsEd(m_xBuilder->weld_entry(u"keywords"_ustr))
627     , m_xMoreTypes(m_xBuilder->weld_combo_box(u"cbMoreTypes"_ustr))
628     , m_xMoreValue(m_xBuilder->weld_entry(u"edMoreValue"_ustr))
629     , m_xCommentEd(m_xBuilder->weld_text_view(u"comments"_ustr))
630 {
631     m_xCommentEd->set_size_request(-1, //m_xKeywordsEd->get_preferred_size().Width(),
632                                    m_xCommentEd->get_height_rows(8));
633     m_xMoreTypes->connect_changed(LINK(this, SfxDocumentDescPage, MoreTypesHdl));
634     m_xMoreValue->connect_changed(LINK(this, SfxDocumentDescPage, MoreValueHdl));
635 }
636 
~SfxDocumentDescPage()637 SfxDocumentDescPage::~SfxDocumentDescPage()
638 {
639 }
640 
Create(weld::Container * pPage,weld::DialogController * pController,const SfxItemSet * rItemSet)641 std::unique_ptr<SfxTabPage> SfxDocumentDescPage::Create(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet *rItemSet)
642 {
643      return std::make_unique<SfxDocumentDescPage>(pPage, pController, *rItemSet);
644 }
645 
FillItemSet(SfxItemSet * rSet)646 bool SfxDocumentDescPage::FillItemSet(SfxItemSet *rSet)
647 {
648     // Test whether a change is present
649     const bool bTitleMod = m_xTitleEd->get_value_changed_from_saved();
650     const bool bThemeMod = m_xThemaEd->get_value_changed_from_saved();
651     const bool bKeywordsMod = m_xKeywordsEd->get_value_changed_from_saved();
652 
653     const bool bContributorMod = m_sMoreValue_Contributor != m_sMoreValue_Contributor_Stored;
654     const bool bCoverageMod = m_sMoreValue_Coverage != m_sMoreValue_Coverage_Stored;
655     const bool bIdentifierMod = m_sMoreValue_Identifier != m_sMoreValue_Identifier_Stored;
656     const bool bPublisherMod = m_sMoreValue_Publisher != m_sMoreValue_Publisher_Stored;
657     const bool bRelationMod = m_sMoreValue_Relation != m_sMoreValue_Relation_Stored;
658     const bool bRightsMod = m_sMoreValue_Rights != m_sMoreValue_Rights_Stored;
659     const bool bSourceMod = m_sMoreValue_Source != m_sMoreValue_Source_Stored;
660     const bool bTypeMod = m_sMoreValue_Type != m_sMoreValue_Type_Stored;
661 
662     const bool bCommentMod = m_xCommentEd->get_value_changed_from_saved();
663     if (!(bTitleMod || bThemeMod || bKeywordsMod || bTitleMod || bThemeMod || bKeywordsMod
664           || bContributorMod || bCoverageMod || bIdentifierMod || bPublisherMod || bRelationMod
665           || bRightsMod || bSourceMod || bTypeMod || bCommentMod))
666     {
667         return false;
668     }
669 
670     // Generating the output data
671     const SfxDocumentInfoItem* pItem = nullptr;
672     SfxDocumentInfoItem* pInfo = nullptr;
673     const SfxItemSet* pExSet = GetDialogExampleSet();
674 
675     if ( pExSet && !(pItem = pExSet->GetItemIfSet( SID_DOCINFO )) )
676         pInfo = m_pInfoItem;
677     else if ( pItem )
678         pInfo = new SfxDocumentInfoItem( *pItem );
679 
680     if ( !pInfo )
681     {
682         SAL_WARN( "sfx.dialog", "SfxDocumentDescPage::FillItemSet(): no item found" );
683         return false;
684     }
685 
686     if ( bTitleMod )
687     {
688         pInfo->setTitle( m_xTitleEd->get_text() );
689     }
690     if ( bThemeMod )
691     {
692         pInfo->setSubject( m_xThemaEd->get_text() );
693     }
694     if ( bKeywordsMod )
695     {
696         pInfo->setKeywords( m_xKeywordsEd->get_text() );
697     }
698     if (bContributorMod)
699     {
700         pInfo->setContributor(m_sMoreValue_Contributor);
701     }
702     if (bCoverageMod)
703     {
704         pInfo->setCoverage(m_sMoreValue_Coverage);
705     }
706     if (bIdentifierMod)
707     {
708         pInfo->setIdentifier(m_sMoreValue_Identifier);
709     }
710     if (bPublisherMod)
711     {
712         pInfo->setPublisher(m_sMoreValue_Publisher);
713     }
714     if (bRelationMod)
715     {
716         pInfo->setRelation(m_sMoreValue_Relation);
717     }
718     if (bRightsMod)
719     {
720         pInfo->setRights(m_sMoreValue_Rights);
721     }
722     if (bSourceMod)
723     {
724         pInfo->setSource(m_sMoreValue_Source);
725     }
726     if (bTypeMod)
727     {
728         pInfo->setType(m_sMoreValue_Type);
729     }
730     if ( bCommentMod )
731     {
732         pInfo->setDescription( m_xCommentEd->get_text() );
733     }
734     rSet->Put( *pInfo );
735     // ptr compare OK, pInfo was created above as temporary data holder
736     if ( !areSfxPoolItemPtrsEqual(pInfo, m_pInfoItem) )
737     {
738         delete pInfo;
739     }
740 
741     return true;
742 }
743 
Reset(const SfxItemSet * rSet)744 void SfxDocumentDescPage::Reset(const SfxItemSet *rSet)
745 {
746     m_pInfoItem = const_cast<SfxDocumentInfoItem*>(&rSet->Get(SID_DOCINFO));
747 
748     m_xTitleEd->set_text(m_pInfoItem->getTitle());
749     m_xThemaEd->set_text(m_pInfoItem->getSubject());
750     m_xKeywordsEd->set_text(m_pInfoItem->getKeywords());
751 
752     m_sMoreValue_Contributor = m_pInfoItem->getContributor();
753     m_sMoreValue_Coverage = m_pInfoItem->getCoverage();
754     m_sMoreValue_Identifier = m_pInfoItem->getIdentifier();
755     m_sMoreValue_Publisher = m_pInfoItem->getPublisher();
756     m_sMoreValue_Relation = m_pInfoItem->getRelation();
757     m_sMoreValue_Rights = m_pInfoItem->getRights();
758     m_sMoreValue_Source = m_pInfoItem->getSource();
759     m_sMoreValue_Type = m_pInfoItem->getType();
760     m_xCommentEd->set_text(m_pInfoItem->getDescription());
761 
762     MoreTypesHdl(*m_xMoreTypes); //triggers m_xMoreValue update
763 
764     m_xTitleEd->save_value();
765     m_xThemaEd->save_value();
766     m_xKeywordsEd->save_value();
767 
768     m_sMoreValue_Contributor_Stored = m_sMoreValue_Contributor;
769     m_sMoreValue_Coverage_Stored = m_sMoreValue_Coverage;
770     m_sMoreValue_Identifier_Stored = m_sMoreValue_Identifier;
771     m_sMoreValue_Publisher_Stored = m_sMoreValue_Publisher;
772     m_sMoreValue_Relation_Stored = m_sMoreValue_Relation;
773     m_sMoreValue_Rights_Stored = m_sMoreValue_Rights;
774     m_sMoreValue_Source_Stored = m_sMoreValue_Source;
775     m_sMoreValue_Type_Stored = m_sMoreValue_Type;
776 
777     m_xCommentEd->save_value();
778 
779     const SfxBoolItem* pROItem = SfxItemSet::GetItem<SfxBoolItem>(rSet, SID_DOC_READONLY, false);
780     if (pROItem && pROItem->GetValue())
781     {
782         m_xTitleEd->set_editable(false);
783         m_xThemaEd->set_editable(false);
784         m_xKeywordsEd->set_editable(false);
785         m_xMoreValue->set_editable(false);
786         m_xCommentEd->set_editable(false);
787     }
788 }
789 
IMPL_LINK_NOARG(SfxDocumentDescPage,MoreValueHdl,weld::Entry &,void)790 IMPL_LINK_NOARG(SfxDocumentDescPage, MoreValueHdl, weld::Entry&, void)
791 {
792     switch (m_xMoreTypes->get_active())
793     {
794         case 0: m_sMoreValue_Contributor = m_xMoreValue->get_text(); break;
795         case 1: m_sMoreValue_Coverage = m_xMoreValue->get_text(); break;
796         case 2: m_sMoreValue_Identifier = m_xMoreValue->get_text(); break;
797         case 3: m_sMoreValue_Publisher = m_xMoreValue->get_text(); break;
798         case 4: m_sMoreValue_Relation = m_xMoreValue->get_text(); break;
799         case 5: m_sMoreValue_Rights = m_xMoreValue->get_text(); break;
800         case 6: m_sMoreValue_Source = m_xMoreValue->get_text(); break;
801         case 7: m_sMoreValue_Type = m_xMoreValue->get_text(); break;
802         default:
803             DBG_ASSERT(true, "MoreValueHdl out of range");
804     }
805 }
806 
IMPL_LINK_NOARG(SfxDocumentDescPage,MoreTypesHdl,weld::ComboBox &,void)807 IMPL_LINK_NOARG(SfxDocumentDescPage, MoreTypesHdl, weld::ComboBox&, void)
808 {
809     switch (m_xMoreTypes->get_active())
810     {
811         case 0: m_xMoreValue->set_text(m_sMoreValue_Contributor); break;
812         case 1: m_xMoreValue->set_text(m_sMoreValue_Coverage); break;
813         case 2: m_xMoreValue->set_text(m_sMoreValue_Identifier); break;
814         case 3: m_xMoreValue->set_text(m_sMoreValue_Publisher); break;
815         case 4: m_xMoreValue->set_text(m_sMoreValue_Relation); break;
816         case 5: m_xMoreValue->set_text(m_sMoreValue_Rights); break;
817         case 6: m_xMoreValue->set_text(m_sMoreValue_Source); break;
818         case 7: m_xMoreValue->set_text(m_sMoreValue_Type); break;
819         default:
820             DBG_ASSERT(true, "MoreTypesHdl out of range");
821     }
822 }
823 
SfxDocumentPage(weld::Container * pPage,weld::DialogController * pController,const SfxItemSet & rItemSet)824 SfxDocumentPage::SfxDocumentPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rItemSet)
825     : SfxTabPage(pPage, pController, u"sfx/ui/documentinfopage.ui"_ustr, u"DocumentInfoPage"_ustr, &rItemSet)
826     , bEnableUseUserData( false )
827     , bHandleDelete( false )
828     , m_xBmp(m_xBuilder->weld_image(u"icon"_ustr))
829     , m_xNameED(m_xBuilder->weld_label(u"nameed"_ustr))
830     , m_xChangePassBtn(m_xBuilder->weld_button(u"changepass"_ustr))
831     , m_xShowTypeFT(m_xBuilder->weld_label(u"showtype"_ustr))
832     , m_xFileValEd(m_xBuilder->weld_link_button(u"showlocation"_ustr))
833     , m_xShowSizeFT(m_xBuilder->weld_label(u"showsize"_ustr))
834     , m_xCreateValFt(m_xBuilder->weld_label(u"showcreate"_ustr))
835     , m_xChangeValFt(m_xBuilder->weld_label(u"showmodify"_ustr))
836     , m_xSignedValFt(m_xBuilder->weld_label(u"showsigned"_ustr))
837     , m_xSignatureBtn(m_xBuilder->weld_button(u"signature"_ustr))
838     , m_xPrintValFt(m_xBuilder->weld_label(u"showprint"_ustr))
839     , m_xTimeLogValFt(m_xBuilder->weld_label(u"showedittime"_ustr))
840     , m_xDocNoValFt(m_xBuilder->weld_label(u"showrevision"_ustr))
841     , m_xUseUserDataCB(m_xBuilder->weld_check_button(u"userdatacb"_ustr))
842     , m_xDeleteBtn(m_xBuilder->weld_button(u"reset"_ustr))
843     , m_xUseThumbnailSaveCB(m_xBuilder->weld_check_button(u"thumbnailsavecb"_ustr))
844     , m_xTemplFt(m_xBuilder->weld_label(u"templateft"_ustr))
845     , m_xTemplValFt(m_xBuilder->weld_label(u"showtemplate"_ustr))
846     , m_xImagePreferredDpiCheckButton(m_xBuilder->weld_check_button(u"image-preferred-dpi-checkbutton"_ustr))
847     , m_xImagePreferredDpiComboBox(m_xBuilder->weld_combo_box(u"image-preferred-dpi-combobox"_ustr))
848 {
849     m_xUseUserDataCB->set_accessible_description(SfxResId(STR_A11Y_DESC_USERDATA));
850 
851     m_aUnknownSize = m_xShowSizeFT->get_label();
852     m_xShowSizeFT->set_label(OUString());
853 
854     m_aMultiSignedStr = m_xSignedValFt->get_label();
855     m_xSignedValFt->set_label(OUString());
856 
857     ImplUpdateSignatures();
858     ImplCheckPasswordState();
859     m_xChangePassBtn->connect_clicked( LINK( this, SfxDocumentPage, ChangePassHdl ) );
860     m_xSignatureBtn->connect_clicked( LINK( this, SfxDocumentPage, SignatureHdl ) );
861     if (comphelper::LibreOfficeKit::isActive())
862         m_xSignatureBtn->hide();
863     m_xDeleteBtn->connect_clicked( LINK( this, SfxDocumentPage, DeleteHdl ) );
864     m_xImagePreferredDpiCheckButton->connect_toggled(LINK(this, SfxDocumentPage, ImagePreferredDPICheckBoxClicked));
865 
866     // [i96288] Check if the document signature command is enabled
867     // on the main list enable/disable the pushbutton accordingly
868     SvtCommandOptions aCmdOptions;
869     if ( aCmdOptions.LookupDisabled( u"Signature"_ustr ) )
870         m_xSignatureBtn->set_sensitive(false);
871 }
872 
~SfxDocumentPage()873 SfxDocumentPage::~SfxDocumentPage()
874 {
875     if (m_xPasswordDialog)
876     {
877         m_xPasswordDialog->Response(RET_CANCEL);
878         m_xPasswordDialog.clear();
879     }
880 }
881 
IMPL_LINK_NOARG(SfxDocumentPage,DeleteHdl,weld::Button &,void)882 IMPL_LINK_NOARG(SfxDocumentPage, DeleteHdl, weld::Button&, void)
883 {
884     OUString aName;
885     if (bEnableUseUserData && m_xUseUserDataCB->get_active())
886         aName = SvtUserOptions().GetFullName();
887     const LocaleDataWrapper& rLocaleWrapper( Application::GetSettings().GetLocaleDataWrapper() );
888     DateTime now( DateTime::SYSTEM );
889     util::DateTime uDT( now.GetUNODateTime() );
890     m_xCreateValFt->set_label( ConvertDateTime_Impl( aName, uDT, rLocaleWrapper ) );
891     m_xChangeValFt->set_label( u""_ustr );
892     m_xPrintValFt->set_label( u""_ustr );
893     m_xTimeLogValFt->set_label( rLocaleWrapper.getDuration( tools::Duration() ) );
894     m_xDocNoValFt->set_label(OUString('1'));
895     bHandleDelete = true;
896 }
897 
IMPL_LINK_NOARG(SfxDocumentPage,SignatureHdl,weld::Button &,void)898 IMPL_LINK_NOARG(SfxDocumentPage, SignatureHdl, weld::Button&, void)
899 {
900     SfxObjectShell* pDoc = SfxObjectShell::Current();
901     if( pDoc )
902     {
903         pDoc->SignDocumentContent(GetFrameWeld());
904 
905         ImplUpdateSignatures();
906     }
907 }
908 
IMPL_LINK_NOARG(SfxDocumentPage,ImagePreferredDPICheckBoxClicked,weld::Toggleable &,void)909 IMPL_LINK_NOARG(SfxDocumentPage, ImagePreferredDPICheckBoxClicked, weld::Toggleable&, void)
910 {
911     bool bEnabled = m_xImagePreferredDpiCheckButton->get_state() == TRISTATE_TRUE;
912     m_xImagePreferredDpiComboBox->set_sensitive(bEnabled);
913 }
914 
IMPL_LINK_NOARG(SfxDocumentPage,ChangePassHdl,weld::Button &,void)915 IMPL_LINK_NOARG(SfxDocumentPage, ChangePassHdl, weld::Button&, void)
916 {
917     SfxObjectShell* pShell = SfxObjectShell::Current();
918     do
919     {
920         if (!pShell)
921             break;
922         SfxItemSet& rMedSet = pShell->GetMedium()->GetItemSet();
923         std::shared_ptr<const SfxFilter> pFilter = pShell->GetMedium()->GetFilter();
924         if (!pFilter)
925             break;
926         if (comphelper::LibreOfficeKit::isActive())
927         {
928             // MS Types support max len of 15 characters while OOXML is "unlimited"
929             const sal_uInt16 maxPwdLen = sfx2::IsMSType(pFilter) && !sfx2::IsOOXML(pFilter) ? 15 : 0;
930             // handle the pwd dialog asynchronously
931             VclAbstractDialogFactory * pFact = VclAbstractDialogFactory::Create();
932             m_xPasswordDialog = pFact->CreatePasswordToOpenModifyDialog(GetFrameWeld(), maxPwdLen, false);
933             m_xPasswordDialog->AllowEmpty(); // needed to remove password
934             m_xPasswordDialog->StartExecuteAsync([this, pFilter, &rMedSet, pShell](sal_Int32 nResult)
935             {
936                 if (nResult == RET_OK)
937                 {
938                     sfx2::SetPassword(pFilter, &rMedSet, m_xPasswordDialog->GetPasswordToOpen(),
939                                       m_xPasswordDialog->GetPasswordToOpen(), true);
940                     tools::JsonWriter payloadJson;
941                     payloadJson.put("password", m_xPasswordDialog->GetPasswordToOpen());
942                     payloadJson.put("isToModify", false);
943 
944                     SfxViewShell *pViewShell = SfxViewShell::Current();
945                     if (pViewShell)
946                         pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_DOCUMENT_PASSWORD_RESET, payloadJson.finishAndGetAsOString());
947 
948                     pShell->SetModified();
949                 }
950                 m_xPasswordDialog->disposeOnce();
951             });
952         } else {
953             sfx2::RequestPassword(pFilter, OUString(), &rMedSet, GetFrameWeld()->GetXWindow());
954             pShell->SetModified();
955         }
956     }
957     while (false);
958 }
959 
ImplUpdateSignatures()960 void SfxDocumentPage::ImplUpdateSignatures()
961 {
962     SfxObjectShell* pDoc = SfxObjectShell::Current();
963     if ( !pDoc )
964         return;
965 
966     SfxMedium* pMedium = pDoc->GetMedium();
967     if ( !pMedium || pMedium->GetName().isEmpty() || !pMedium->GetStorage().is() )
968         return;
969 
970     Reference< security::XDocumentDigitalSignatures > xD;
971     try
972     {
973         xD = security::DocumentDigitalSignatures::createDefault(comphelper::getProcessComponentContext());
974         xD->setParentWindow(GetDialogController()->getDialog()->GetXWindow());
975     }
976     catch ( const css::uno::DeploymentException& )
977     {
978     }
979     OUString s;
980     Sequence< security::DocumentSignatureInformation > aInfos;
981 
982     if ( xD.is() )
983         aInfos = xD->verifyDocumentContentSignatures( pMedium->GetZipStorageToSign_Impl(),
984                                                       uno::Reference< io::XInputStream >() );
985     if ( aInfos.getLength() > 1 )
986         s = m_aMultiSignedStr;
987     else if ( aInfos.getLength() == 1 )
988     {
989         const security::DocumentSignatureInformation& rInfo = aInfos[ 0 ];
990         s = utl::GetDateTimeString( rInfo.SignatureDate, rInfo.SignatureTime ) + ", " +
991             comphelper::xmlsec::GetContentPart(rInfo.Signer->getSubjectName(), rInfo.Signer->getCertificateKind());
992     }
993     m_xSignedValFt->set_label(s);
994 }
995 
ImplCheckPasswordState()996 void SfxDocumentPage::ImplCheckPasswordState()
997 {
998     SfxObjectShell* pShell = SfxObjectShell::Current();
999     do
1000     {
1001         if (!pShell)
1002             break;
1003         const SfxUnoAnyItem* pEncryptionDataItem = pShell->GetMedium()->GetItemSet().GetItem(SID_ENCRYPTIONDATA, false);
1004         uno::Sequence< beans::NamedValue > aEncryptionData;
1005         if (pEncryptionDataItem)
1006             pEncryptionDataItem->GetValue() >>= aEncryptionData;
1007         else
1008              break;
1009 
1010         if (!aEncryptionData.hasElements())
1011              break;
1012         m_xChangePassBtn->set_sensitive(true);
1013         return;
1014     }
1015     while (false);
1016     m_xChangePassBtn->set_sensitive(comphelper::LibreOfficeKit::isActive());
1017 }
1018 
Create(weld::Container * pPage,weld::DialogController * pController,const SfxItemSet * rItemSet)1019 std::unique_ptr<SfxTabPage> SfxDocumentPage::Create(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet* rItemSet)
1020 {
1021      return std::make_unique<SfxDocumentPage>(pPage, pController, *rItemSet);
1022 }
1023 
EnableUseUserData()1024 void SfxDocumentPage::EnableUseUserData()
1025 {
1026     bEnableUseUserData = true;
1027     m_xUseUserDataCB->show();
1028     m_xDeleteBtn->show();
1029 }
1030 
FillItemSet(SfxItemSet * rSet)1031 bool SfxDocumentPage::FillItemSet( SfxItemSet* rSet )
1032 {
1033     bool bRet = false;
1034 
1035     if ( !bHandleDelete && bEnableUseUserData &&
1036          m_xUseUserDataCB->get_state_changed_from_saved() )
1037     {
1038         const SfxItemSet* pExpSet = GetDialogExampleSet();
1039         const SfxDocumentInfoItem* pInfoItem;
1040 
1041         if ( pExpSet && (pInfoItem = pExpSet->GetItemIfSet( SID_DOCINFO ) ) )
1042         {
1043             bool bUseData = ( TRISTATE_TRUE == m_xUseUserDataCB->get_state() );
1044             const_cast<SfxDocumentInfoItem*>(pInfoItem)->SetUseUserData( bUseData );
1045             rSet->Put( *pInfoItem );
1046             bRet = true;
1047         }
1048     }
1049 
1050     if ( bHandleDelete )
1051     {
1052         const SfxItemSet* pExpSet = GetDialogExampleSet();
1053         const SfxDocumentInfoItem* pInfoItem;
1054         if ( pExpSet && (pInfoItem = pExpSet->GetItemIfSet( SID_DOCINFO )) )
1055         {
1056             bool bUseAuthor = bEnableUseUserData && m_xUseUserDataCB->get_active();
1057             SfxDocumentInfoItem newItem( *pInfoItem );
1058             newItem.resetUserData( bUseAuthor
1059                 ? SvtUserOptions().GetFullName()
1060                 : OUString() );
1061             const_cast<SfxDocumentInfoItem*>(pInfoItem)->SetUseUserData( TRISTATE_TRUE == m_xUseUserDataCB->get_state() );
1062             newItem.SetUseUserData( TRISTATE_TRUE == m_xUseUserDataCB->get_state() );
1063 
1064             newItem.SetDeleteUserData( true );
1065             rSet->Put( newItem );
1066             bRet = true;
1067         }
1068     }
1069 
1070     if ( m_xUseThumbnailSaveCB->get_state_changed_from_saved() )
1071     {
1072         const SfxItemSet* pExpSet = GetDialogExampleSet();
1073         const SfxDocumentInfoItem* pInfoItem;
1074 
1075         if ( pExpSet && (pInfoItem = pExpSet->GetItemIfSet( SID_DOCINFO )) )
1076         {
1077             bool bUseThumbnail = ( TRISTATE_TRUE == m_xUseThumbnailSaveCB->get_state() );
1078             const_cast<SfxDocumentInfoItem*>(pInfoItem)->SetUseThumbnailSave( bUseThumbnail );
1079             rSet->Put( *pInfoItem );
1080             bRet = true;
1081         }
1082     }
1083 
1084     SfxObjectShell* pDocSh = SfxObjectShell::Current();
1085     if (pDocSh)
1086     {
1087         uno::Reference<lang::XMultiServiceFactory> xFac(pDocSh->GetModel(), uno::UNO_QUERY);
1088         if (xFac.is())
1089         {
1090             uno::Reference<beans::XPropertySet> xProps(xFac->createInstance(u"com.sun.star.document.Settings"_ustr), uno::UNO_QUERY);
1091             if (xProps.is())
1092             {
1093                 sal_Int32 nImagePreferredDPI = 0;
1094                 if (m_xImagePreferredDpiCheckButton->get_state() == TRISTATE_TRUE)
1095                 {
1096                     OUString aImagePreferredDPIString = m_xImagePreferredDpiComboBox->get_active_text();
1097                     nImagePreferredDPI = aImagePreferredDPIString.toInt32();
1098                 }
1099                 xProps->setPropertyValue(u"ImagePreferredDPI"_ustr, uno::Any(nImagePreferredDPI));
1100             }
1101         }
1102     }
1103 
1104     return bRet;
1105 }
1106 
Reset(const SfxItemSet * rSet)1107 void SfxDocumentPage::Reset( const SfxItemSet* rSet )
1108 {
1109     // Determine the document information
1110     const SfxDocumentInfoItem& rInfoItem = rSet->Get(SID_DOCINFO);
1111 
1112     // template data
1113     if (rInfoItem.HasTemplate())
1114     {
1115         const OUString& rName = rInfoItem.getTemplateName();
1116         if (rName.getLength() > SAL_MAX_INT16) // tdf#122780 pick some ~arbitrary max size
1117             m_xTemplValFt->set_label(rName.copy(0, SAL_MAX_INT16));
1118         else
1119             m_xTemplValFt->set_label(rName);
1120     }
1121     else
1122     {
1123         m_xTemplFt->hide();
1124         m_xTemplValFt->hide();
1125     }
1126 
1127     // determine file name
1128     OUString aFile( rInfoItem.GetValue() );
1129     OUString aFactory( aFile );
1130     if ( aFile.getLength() > 2 && aFile[0] == '[' )
1131     {
1132         sal_Int32 nPos = aFile.indexOf( ']' );
1133         aFactory = aFile.copy( 1, nPos-1  );
1134         aFile = aFile.copy( nPos+1 );
1135     }
1136 
1137     // determine name
1138     INetURLObject aURL(aFile);
1139     OUString aName = aURL.GetLastName(INetURLObject::DecodeMechanism::WithCharset);
1140     if ( aName.isEmpty() || aURL.GetProtocol() == INetProtocol::PrivSoffice )
1141         aName = SfxResId( STR_NONAME );
1142     m_xNameED->set_label( aName );
1143 
1144     // determine context symbol
1145     aURL.SetSmartProtocol( INetProtocol::File );
1146     aURL.SetSmartURL( aFactory);
1147     const OUString& rMainURL = aURL.GetMainURL( INetURLObject::DecodeMechanism::NONE );
1148     OUString aImage = SvFileInformationManager::GetImageId( aURL, true );
1149     m_xBmp->set_from_icon_name(aImage);
1150 
1151     // determine size and type
1152     OUString aSizeText( m_aUnknownSize );
1153     // we might already know the size as an optional argument passed to .uno:SetDocumentProperties
1154     sal_Int64 nSize = rInfoItem.getFileSize();
1155     // otherwise, for some protocols we can reliably query for it
1156     if (nSize == -1 && (aURL.GetProtocol() == INetProtocol::File || aURL.isAnyKnownWebDAVScheme()))
1157         nSize = SfxContentHelper::GetSize( aURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ) );
1158     if (nSize != -1)
1159         aSizeText = CreateSizeText( nSize );
1160     m_xShowSizeFT->set_label( aSizeText );
1161 
1162     OUString aDescription = SvFileInformationManager::GetDescription( INetURLObject(rMainURL) );
1163     if ( aDescription.isEmpty() )
1164         aDescription = SfxResId( STR_SFX_NEWOFFICEDOC );
1165     m_xShowTypeFT->set_label( aDescription );
1166 
1167     // determine location
1168     // online we don't know file location so we just set it as the name
1169     if (comphelper::LibreOfficeKit::isActive())
1170     {
1171         m_xFileValEd->set_label(aName);
1172         m_xFileValEd->set_uri(aName);
1173 
1174         // Disable setting/changing password on text files.
1175         // Perhaps this needs to be done for both Online and Desktop.
1176         OUString sExtension(INetURLObject(rMainURL).getExtension());
1177         if (!sExtension.isEmpty())
1178         {
1179             sExtension = sExtension.toAsciiLowerCase();
1180             if (sExtension == "txt" || sExtension == "csv")
1181                 m_xChangePassBtn->set_sensitive(false);
1182         }
1183     }
1184     else
1185     {
1186         aURL.SetSmartURL( aFile);
1187         if ( aURL.GetProtocol() == INetProtocol::File )
1188         {
1189             INetURLObject aPath( aURL );
1190             aPath.setFinalSlash();
1191             aPath.removeSegment();
1192             // we know it's a folder -> don't need the final slash, but it's better for WB_PATHELLIPSIS
1193             aPath.removeFinalSlash();
1194             OUString aText( aPath.PathToFileName() ); //! (pb) MaxLen?
1195             m_xFileValEd->set_label(aText);
1196             OUString aURLStr;
1197             osl::FileBase::getFileURLFromSystemPath(aText, aURLStr);
1198             m_xFileValEd->set_uri(aURLStr);
1199         }
1200         else if (aURL.GetProtocol() != INetProtocol::PrivSoffice)
1201         {
1202             m_xFileValEd->set_label(aURL.GetPartBeforeLastName());
1203             m_xFileValEd->set_uri(m_xFileValEd->get_label());
1204         }
1205     }
1206 
1207 
1208     // handle access data
1209     bool bUseUserData = rInfoItem.IsUseUserData();
1210     const LocaleDataWrapper& rLocaleWrapper( Application::GetSettings().GetLocaleDataWrapper() );
1211     m_xCreateValFt->set_label( ConvertDateTime_Impl( rInfoItem.getAuthor(),
1212         rInfoItem.getCreationDate(), rLocaleWrapper ) );
1213     util::DateTime aTime( rInfoItem.getModificationDate() );
1214     if ( aTime.Month > 0 )
1215         m_xChangeValFt->set_label( ConvertDateTime_Impl(
1216             rInfoItem.getModifiedBy(), aTime, rLocaleWrapper ) );
1217     aTime = rInfoItem.getPrintDate();
1218     if ( aTime.Month > 0 )
1219         m_xPrintValFt->set_label( ConvertDateTime_Impl( rInfoItem.getPrintedBy(),
1220             aTime, rLocaleWrapper ) );
1221     const tools::Long nTime = rInfoItem.getEditingDuration();
1222     if ( bUseUserData )
1223     {
1224         assert(SAL_MIN_INT32 <= nTime/86400 && nTime/86400 <= SAL_MAX_INT32);
1225         const tools::Duration aD( static_cast<sal_Int32>(nTime)/86400,
1226                 (nTime%86400)/3600, (nTime%3600)/60, nTime%60, 0);
1227         m_xTimeLogValFt->set_label( rLocaleWrapper.getDuration( aD ) );
1228         m_xDocNoValFt->set_label( OUString::number(
1229             rInfoItem.getEditingCycles() ) );
1230     }
1231 
1232     bool bUseThumbnailSave = rInfoItem.IsUseThumbnailSave();
1233 
1234     // Check for cmis properties where otherwise unavailable
1235     if ( rInfoItem.isCmisDocument( ) )
1236     {
1237         const uno::Sequence< document::CmisProperty > aCmisProps = rInfoItem.GetCmisProperties();
1238         for ( const auto& rCmisProp : aCmisProps )
1239         {
1240             if ( rCmisProp.Id == "cmis:contentStreamLength" &&
1241                  aSizeText == m_aUnknownSize )
1242             {
1243                 Sequence< sal_Int64 > seqValue;
1244                 rCmisProp.Value >>= seqValue;
1245                 SvNumberFormatter aNumberFormatter( ::comphelper::getProcessComponentContext(),
1246                         Application::GetSettings().GetLanguageTag().getLanguageType() );
1247                 sal_uInt32 nIndex = aNumberFormatter.GetFormatIndex( NF_NUMBER_SYSTEM );
1248                 if ( seqValue.hasElements() )
1249                 {
1250                     OUString sValue;
1251                     aNumberFormatter.GetInputLineString( seqValue[0], nIndex, sValue );
1252                     m_xShowSizeFT->set_label( CreateSizeText( sValue.toInt64( ) ) );
1253                 }
1254             }
1255 
1256             util::DateTime uDT;
1257             OUString emptyDate = ConvertDateTime_Impl( u"", uDT, rLocaleWrapper );
1258             if ( rCmisProp.Id == "cmis:creationDate" &&
1259                  (m_xCreateValFt->get_label() == emptyDate ||
1260                   m_xCreateValFt->get_label().isEmpty()))
1261             {
1262                 Sequence< util::DateTime > seqValue;
1263                 rCmisProp.Value >>= seqValue;
1264                 if ( seqValue.hasElements() )
1265                 {
1266                     m_xCreateValFt->set_label( ConvertDateTime_Impl( u"", seqValue[0], rLocaleWrapper ) );
1267                 }
1268             }
1269             if ( rCmisProp.Id == "cmis:lastModificationDate" &&
1270                  (m_xChangeValFt->get_label() == emptyDate ||
1271                   m_xChangeValFt->get_label().isEmpty()))
1272             {
1273                 Sequence< util::DateTime > seqValue;
1274                 rCmisProp.Value >>= seqValue;
1275                 if ( seqValue.hasElements() )
1276                 {
1277                     m_xChangeValFt->set_label( ConvertDateTime_Impl( u"", seqValue[0], rLocaleWrapper ) );
1278                 }
1279             }
1280         }
1281     }
1282 
1283     m_xUseUserDataCB->set_active(bUseUserData);
1284     m_xUseUserDataCB->save_state();
1285     m_xUseUserDataCB->set_sensitive( bEnableUseUserData );
1286     bHandleDelete = false;
1287     m_xDeleteBtn->set_sensitive( bEnableUseUserData );
1288     m_xUseThumbnailSaveCB->set_active(bUseThumbnailSave);
1289     m_xUseThumbnailSaveCB->save_state();
1290 
1291     SfxObjectShell* pDocSh = SfxObjectShell::Current();
1292     sal_Int32 nImagePreferredDPI = 0;
1293     if (pDocSh)
1294     {
1295         try
1296         {
1297             uno::Reference< lang::XMultiServiceFactory > xFac( pDocSh->GetModel(), uno::UNO_QUERY_THROW );
1298             uno::Reference< beans::XPropertySet > xProps( xFac->createInstance(u"com.sun.star.document.Settings"_ustr), uno::UNO_QUERY_THROW );
1299 
1300             xProps->getPropertyValue(u"ImagePreferredDPI"_ustr) >>= nImagePreferredDPI;
1301         }
1302         catch( uno::Exception& )
1303         {
1304         }
1305     }
1306     if (nImagePreferredDPI > 0)
1307     {
1308         m_xImagePreferredDpiCheckButton->set_state(TRISTATE_TRUE);
1309         m_xImagePreferredDpiComboBox->set_sensitive(true);
1310         m_xImagePreferredDpiComboBox->set_entry_text(OUString::number(nImagePreferredDPI));
1311     }
1312     else
1313     {
1314         m_xImagePreferredDpiCheckButton->set_state(TRISTATE_FALSE);
1315         m_xImagePreferredDpiComboBox->set_sensitive(false);
1316         m_xImagePreferredDpiComboBox->set_entry_text(u""_ustr);
1317     }
1318 
1319 }
1320 
SfxDocumentInfoDialog(weld::Window * pParent,const SfxItemSet & rItemSet)1321 SfxDocumentInfoDialog::SfxDocumentInfoDialog(weld::Window* pParent, const SfxItemSet& rItemSet)
1322     : SfxTabDialogController(pParent, u"sfx/ui/documentpropertiesdialog.ui"_ustr,
1323                              u"DocumentPropertiesDialog"_ustr, &rItemSet)
1324 {
1325     const SfxDocumentInfoItem& rInfoItem = rItemSet.Get( SID_DOCINFO );
1326 
1327 #ifdef DBG_UTIL
1328     const SfxStringItem* pURLItem = rItemSet.GetItem<SfxStringItem>(SID_BASEURL, false);
1329     DBG_ASSERT( pURLItem, "No BaseURL provided for InternetTabPage!" );
1330 #endif
1331 
1332      // Determine the Titles
1333     OUString aTitle(m_xDialog->get_title());
1334     const SfxStringItem* pItem = rItemSet.GetItemIfSet( SID_EXPLORER_PROPS_START, false );
1335     if ( !pItem )
1336     {
1337         // File name
1338         const OUString& aFile( rInfoItem.GetValue() );
1339 
1340         INetURLObject aURL;
1341         aURL.SetSmartProtocol( INetProtocol::File );
1342         aURL.SetSmartURL( aFile);
1343         if ( INetProtocol::PrivSoffice != aURL.GetProtocol() )
1344         {
1345             OUString aLastName( aURL.GetLastName() );
1346             if ( !aLastName.isEmpty() )
1347                 aTitle = aTitle.replaceFirst("%1", aLastName);
1348             else
1349                 aTitle = aTitle.replaceFirst("%1", aFile);
1350         }
1351         else
1352             aTitle = aTitle.replaceFirst("%1", SfxResId( STR_NONAME ));
1353     }
1354     else
1355     {
1356         aTitle = aTitle.replaceFirst("%1", pItem->GetValue());
1357     }
1358     m_xDialog->set_title(aTitle);
1359 
1360     // Property Pages
1361     AddTabPage(u"general"_ustr, SfxDocumentPage::Create, nullptr);
1362     AddTabPage(u"description"_ustr, SfxDocumentDescPage::Create, nullptr);
1363 
1364     AddTabPage(u"customprops"_ustr, SfxCustomPropertiesPage::Create, nullptr);
1365 
1366     if (rInfoItem.isCmisDocument())
1367         AddTabPage(u"cmisprops"_ustr, SfxCmisPropertiesPage::Create, nullptr);
1368     else
1369         RemoveTabPage(u"cmisprops"_ustr);
1370     // Disable security page for online as not fully asynced yet
1371     if (!comphelper::LibreOfficeKit::isActive())
1372         AddTabPage(u"security"_ustr, SfxSecurityPage::Create, nullptr);
1373     else
1374         RemoveTabPage(u"security"_ustr);
1375 }
1376 
PageCreated(const OUString & rId,SfxTabPage & rPage)1377 void SfxDocumentInfoDialog::PageCreated(const OUString& rId, SfxTabPage &rPage)
1378 {
1379     if (rId == "general")
1380         static_cast<SfxDocumentPage&>(rPage).EnableUseUserData();
1381 }
1382 
AddFontTabPage()1383 void SfxDocumentInfoDialog::AddFontTabPage()
1384 {
1385     AddTabPage(u"font"_ustr, SfxResId(STR_FONT_TABPAGE), SfxDocumentFontsPage::Create);
1386 }
1387 
1388 // class CustomPropertiesYesNoButton -------------------------------------
1389 
CustomPropertiesYesNoButton(std::unique_ptr<weld::Widget> xTopLevel,std::unique_ptr<weld::RadioButton> xYesButton,std::unique_ptr<weld::RadioButton> xNoButton)1390 CustomPropertiesYesNoButton::CustomPropertiesYesNoButton(std::unique_ptr<weld::Widget> xTopLevel,
1391                                                          std::unique_ptr<weld::RadioButton> xYesButton,
1392                                                          std::unique_ptr<weld::RadioButton> xNoButton)
1393     : m_xTopLevel(std::move(xTopLevel))
1394     , m_xYesButton(std::move(xYesButton))
1395     , m_xNoButton(std::move(xNoButton))
1396 {
1397     CheckNo();
1398 }
1399 
~CustomPropertiesYesNoButton()1400 CustomPropertiesYesNoButton::~CustomPropertiesYesNoButton()
1401 {
1402 }
1403 
1404 
DurationDialog_Impl(weld::Widget * pParent,const util::Duration & rDuration)1405 DurationDialog_Impl::DurationDialog_Impl(weld::Widget* pParent, const util::Duration& rDuration)
1406     : GenericDialogController(pParent, u"sfx/ui/editdurationdialog.ui"_ustr, u"EditDurationDialog"_ustr)
1407     , m_xNegativeCB(m_xBuilder->weld_check_button(u"negative"_ustr))
1408     , m_xYearNF(m_xBuilder->weld_spin_button(u"years"_ustr))
1409     , m_xMonthNF(m_xBuilder->weld_spin_button(u"months"_ustr))
1410     , m_xDayNF(m_xBuilder->weld_spin_button(u"days"_ustr))
1411     , m_xHourNF(m_xBuilder->weld_spin_button(u"hours"_ustr))
1412     , m_xMinuteNF(m_xBuilder->weld_spin_button(u"minutes"_ustr))
1413     , m_xSecondNF(m_xBuilder->weld_spin_button(u"seconds"_ustr))
1414     , m_xMSecondNF(m_xBuilder->weld_spin_button(u"milliseconds"_ustr))
1415 {
1416     m_xNegativeCB->set_active(rDuration.Negative);
1417     m_xYearNF->set_value(rDuration.Years);
1418     m_xMonthNF->set_value(rDuration.Months);
1419     m_xDayNF->set_value(rDuration.Days);
1420     m_xHourNF->set_value(rDuration.Hours);
1421     m_xMinuteNF->set_value(rDuration.Minutes);
1422     m_xSecondNF->set_value(rDuration.Seconds);
1423     m_xMSecondNF->set_value(rDuration.NanoSeconds);
1424 }
1425 
GetDuration() const1426 util::Duration  DurationDialog_Impl::GetDuration() const
1427 {
1428     util::Duration  aRet;
1429     aRet.Negative = m_xNegativeCB->get_active();
1430     aRet.Years = m_xYearNF->get_value();
1431     aRet.Months = m_xMonthNF->get_value();
1432     aRet.Days = m_xDayNF->get_value();
1433     aRet.Hours  = m_xHourNF->get_value();
1434     aRet.Minutes = m_xMinuteNF->get_value();
1435     aRet.Seconds = m_xSecondNF->get_value();
1436     aRet.NanoSeconds = m_xMSecondNF->get_value();
1437     return aRet;
1438 }
1439 
CustomPropertiesDurationField(std::unique_ptr<weld::Entry> xEntry,std::unique_ptr<weld::Button> xEditButton)1440 CustomPropertiesDurationField::CustomPropertiesDurationField(std::unique_ptr<weld::Entry> xEntry,
1441                                                              std::unique_ptr<weld::Button> xEditButton)
1442     : m_xEntry(std::move(xEntry))
1443     , m_xEditButton(std::move(xEditButton))
1444 {
1445     m_xEditButton->connect_clicked(LINK(this, CustomPropertiesDurationField, ClickHdl));
1446     SetDuration( util::Duration(false, 0, 0, 0, 0, 0, 0, 0) );
1447 }
1448 
set_visible(bool bVisible)1449 void CustomPropertiesDurationField::set_visible(bool bVisible)
1450 {
1451     m_xEntry->set_visible(bVisible);
1452     m_xEditButton->set_visible(bVisible);
1453 }
1454 
SetDuration(const util::Duration & rDuration)1455 void CustomPropertiesDurationField::SetDuration( const util::Duration& rDuration )
1456 {
1457     m_aDuration = rDuration;
1458     OUString sText = (rDuration.Negative ? OUString('-') : OUString('+')) +
1459         SfxResId(SFX_ST_DURATION_FORMAT);
1460     sText = sText.replaceFirst( "%1", OUString::number( rDuration.Years ) );
1461     sText = sText.replaceFirst( "%2", OUString::number( rDuration.Months ) );
1462     sText = sText.replaceFirst( "%3", OUString::number( rDuration.Days   ) );
1463     sText = sText.replaceFirst( "%4", OUString::number( rDuration.Hours  ) );
1464     sText = sText.replaceFirst( "%5", OUString::number( rDuration.Minutes) );
1465     sText = sText.replaceFirst( "%6", OUString::number( rDuration.Seconds) );
1466     m_xEntry->set_text(sText);
1467 }
1468 
IMPL_LINK(CustomPropertiesDurationField,ClickHdl,weld::Button &,rButton,void)1469 IMPL_LINK(CustomPropertiesDurationField, ClickHdl, weld::Button&, rButton, void)
1470 {
1471     m_xDurationDialog = std::make_shared<DurationDialog_Impl>(&rButton, GetDuration());
1472     weld::DialogController::runAsync(m_xDurationDialog, [&](sal_Int32 response)
1473     {
1474         if (response == RET_OK)
1475         {
1476             SetDuration(m_xDurationDialog->GetDuration());
1477         }
1478     });
1479 }
1480 
~CustomPropertiesDurationField()1481 CustomPropertiesDurationField::~CustomPropertiesDurationField()
1482 {
1483     if (m_xDurationDialog)
1484         m_xDurationDialog->response(RET_CANCEL);
1485 }
1486 
1487 namespace
1488 {
fillNameBox(weld::ComboBox & rNameBox)1489     void fillNameBox(weld::ComboBox& rNameBox)
1490     {
1491         for (size_t i = 0; i < SAL_N_ELEMENTS(SFX_CB_PROPERTY_STRINGARRAY); ++i)
1492             rNameBox.append_text(SfxResId(SFX_CB_PROPERTY_STRINGARRAY[i]));
1493         Size aSize(rNameBox.get_preferred_size());
1494         rNameBox.set_size_request(aSize.Width(), aSize.Height());
1495     }
1496 
fillTypeBox(weld::ComboBox & rTypeBox)1497     void fillTypeBox(weld::ComboBox& rTypeBox)
1498     {
1499         for (size_t i = 0; i < SAL_N_ELEMENTS(SFX_LB_PROPERTY_STRINGARRAY); ++i)
1500         {
1501             OUString sId(OUString::number(SFX_LB_PROPERTY_STRINGARRAY[i].second));
1502             rTypeBox.append(sId, SfxResId(SFX_LB_PROPERTY_STRINGARRAY[i].first));
1503         }
1504         rTypeBox.set_active(0);
1505         Size aSize(rTypeBox.get_preferred_size());
1506         rTypeBox.set_size_request(aSize.Width(), aSize.Height());
1507     }
1508 }
1509 
1510 // struct CustomPropertyLine ---------------------------------------------
CustomPropertyLine(CustomPropertiesWindow * pParent,weld::Widget * pContainer)1511 CustomPropertyLine::CustomPropertyLine(CustomPropertiesWindow* pParent, weld::Widget* pContainer)
1512     : m_pParent(pParent)
1513     , m_xBuilder(Application::CreateBuilder(pContainer, u"sfx/ui/linefragment.ui"_ustr))
1514     , m_xLine(m_xBuilder->weld_container(u"lineentry"_ustr))
1515     , m_xNameBox(m_xBuilder->weld_combo_box(u"namebox"_ustr))
1516     , m_xTypeBox(m_xBuilder->weld_combo_box(u"typebox"_ustr))
1517     , m_xValueEdit(m_xBuilder->weld_entry(u"valueedit"_ustr))
1518     , m_xDateTimeBox(m_xBuilder->weld_widget(u"datetimebox"_ustr))
1519     , m_xDateField(new CustomPropertiesDateField(new SvtCalendarBox(m_xBuilder->weld_menu_button(u"date"_ustr))))
1520     , m_xTimeField(new CustomPropertiesTimeField(m_xBuilder->weld_formatted_spin_button(u"time"_ustr)))
1521     , m_xDurationBox(m_xBuilder->weld_widget(u"durationbox"_ustr))
1522     , m_xDurationField(new CustomPropertiesDurationField(m_xBuilder->weld_entry(u"duration"_ustr),
1523                                                          m_xBuilder->weld_button(u"durationbutton"_ustr)))
1524     , m_xYesNoButton(new CustomPropertiesYesNoButton(m_xBuilder->weld_widget(u"yesno"_ustr),
1525                                                      m_xBuilder->weld_radio_button(u"yes"_ustr),
1526                                                      m_xBuilder->weld_radio_button(u"no"_ustr)))
1527     , m_xRemoveButton(m_xBuilder->weld_button(u"remove"_ustr))
1528     , m_bTypeLostFocus( false )
1529 {
1530     fillNameBox(*m_xNameBox);
1531     fillTypeBox(*m_xTypeBox);
1532 
1533     m_xTypeBox->connect_changed(LINK(this, CustomPropertyLine, TypeHdl));
1534     m_xRemoveButton->connect_clicked(LINK(this, CustomPropertyLine, RemoveHdl));
1535     m_xValueEdit->connect_focus_out(LINK(this, CustomPropertyLine, EditLoseFocusHdl));
1536     //add lose focus handlers of date/time fields
1537     m_xTypeBox->connect_focus_out(LINK(this, CustomPropertyLine, BoxLoseFocusHdl));
1538 }
1539 
Clear()1540 void CustomPropertyLine::Clear()
1541 {
1542     m_xNameBox->set_active(-1);
1543     m_xValueEdit->set_text(OUString());
1544 
1545 }
1546 
Hide()1547 void CustomPropertyLine::Hide()
1548 {
1549     m_xLine->hide();
1550 }
1551 
CustomPropertiesWindow(weld::Container & rParent,weld::Label & rHeaderAccName,weld::Label & rHeaderAccType,weld::Label & rHeaderAccValue)1552 CustomPropertiesWindow::CustomPropertiesWindow(weld::Container& rParent, weld::Label& rHeaderAccName,
1553                                                weld::Label& rHeaderAccType, weld::Label& rHeaderAccValue)
1554     : m_nHeight(0)
1555     , m_nLineHeight(0)
1556     , m_nScrollPos(0)
1557     , m_pCurrentLine(nullptr)
1558     , m_aNumberFormatter(::comphelper::getProcessComponentContext(),
1559                          Application::GetSettings().GetLanguageTag().getLanguageType())
1560     , m_aEditLoseFocusIdle("sfx2 CustomPropertiesWindow loseFocusIdle")
1561     , m_aBoxLoseFocusIdle("sfx2 CustomPropertiesWindow m_aBoxLoseFocusIdle")
1562     , m_rBody(rParent)
1563     , m_rHeaderAccName(rHeaderAccName)
1564     , m_rHeaderAccType(rHeaderAccType)
1565     , m_rHeaderAccValue(rHeaderAccValue)
1566 {
1567     m_aEditLoseFocusIdle.SetPriority( TaskPriority::LOWEST );
1568     m_aEditLoseFocusIdle.SetInvokeHandler( LINK( this, CustomPropertiesWindow, EditTimeoutHdl ) );
1569     m_aBoxLoseFocusIdle.SetPriority( TaskPriority::LOWEST );
1570     m_aBoxLoseFocusIdle.SetInvokeHandler( LINK( this, CustomPropertiesWindow, BoxTimeoutHdl ) );
1571 }
1572 
~CustomPropertiesWindow()1573 CustomPropertiesWindow::~CustomPropertiesWindow()
1574 {
1575     m_aEditLoseFocusIdle.Stop();
1576     m_aBoxLoseFocusIdle.Stop();
1577 
1578     m_pCurrentLine = nullptr;
1579 }
1580 
DoTypeHdl(const weld::ComboBox & rBox)1581 void CustomPropertyLine::DoTypeHdl(const weld::ComboBox& rBox)
1582 {
1583     auto nType = rBox.get_active_id().toInt32();
1584     m_xValueEdit->set_visible( (Custom_Type_Text == nType) || (Custom_Type_Number == nType) );
1585     m_xDateTimeBox->set_visible( (Custom_Type_Date == nType) || (Custom_Type_Datetime == nType) );
1586     m_xDateField->set_visible( (Custom_Type_Date == nType) || (Custom_Type_Datetime == nType) );
1587     m_xTimeField->set_visible( Custom_Type_Datetime == nType );
1588     m_xDurationBox->set_visible( Custom_Type_Duration == nType );
1589     m_xDurationField->set_visible( Custom_Type_Duration == nType );
1590     m_xYesNoButton->set_visible( Custom_Type_Boolean == nType );
1591 }
1592 
IMPL_LINK(CustomPropertyLine,TypeHdl,weld::ComboBox &,rBox,void)1593 IMPL_LINK(CustomPropertyLine, TypeHdl, weld::ComboBox&, rBox, void)
1594 {
1595     DoTypeHdl(rBox);
1596 }
1597 
Remove(const CustomPropertyLine * pLine)1598 void CustomPropertiesWindow::Remove(const CustomPropertyLine* pLine)
1599 {
1600     StoreCustomProperties();
1601 
1602     auto pFound = std::find_if( m_aCustomPropertiesLines.begin(), m_aCustomPropertiesLines.end(),
1603                     [&] (const std::unique_ptr<CustomPropertyLine>& p) { return p.get() == pLine; });
1604     if ( pFound != m_aCustomPropertiesLines.end() )
1605     {
1606         sal_uInt32 nLineNumber = pFound - m_aCustomPropertiesLines.begin();
1607         sal_uInt32 nDataModelIndex = GetCurrentDataModelPosition() + nLineNumber;
1608         m_aCustomProperties.erase(m_aCustomProperties.begin() + nDataModelIndex);
1609 
1610         ReloadLinesContent();
1611     }
1612 
1613     m_aRemovedHdl.Call(nullptr);
1614 }
1615 
IMPL_LINK_NOARG(CustomPropertyLine,RemoveHdl,weld::Button &,void)1616 IMPL_LINK_NOARG(CustomPropertyLine, RemoveHdl, weld::Button&, void)
1617 {
1618     m_pParent->Remove(this);
1619 }
1620 
EditLoseFocus(CustomPropertyLine * pLine)1621 void CustomPropertiesWindow::EditLoseFocus(CustomPropertyLine* pLine)
1622 {
1623     m_pCurrentLine = pLine;
1624     m_aEditLoseFocusIdle.Start();
1625 }
1626 
IMPL_LINK_NOARG(CustomPropertyLine,EditLoseFocusHdl,weld::Widget &,void)1627 IMPL_LINK_NOARG(CustomPropertyLine, EditLoseFocusHdl, weld::Widget&, void)
1628 {
1629     if (!m_bTypeLostFocus)
1630         m_pParent->EditLoseFocus(this);
1631     else
1632         m_bTypeLostFocus = false;
1633 }
1634 
BoxLoseFocus(CustomPropertyLine * pLine)1635 void CustomPropertiesWindow::BoxLoseFocus(CustomPropertyLine* pLine)
1636 {
1637     m_pCurrentLine = pLine;
1638     m_aBoxLoseFocusIdle.Start();
1639 }
1640 
IMPL_LINK_NOARG(CustomPropertyLine,BoxLoseFocusHdl,weld::Widget &,void)1641 IMPL_LINK_NOARG(CustomPropertyLine, BoxLoseFocusHdl, weld::Widget&, void)
1642 {
1643     m_pParent->BoxLoseFocus(this);
1644 }
1645 
IMPL_LINK_NOARG(CustomPropertiesWindow,EditTimeoutHdl,Timer *,void)1646 IMPL_LINK_NOARG(CustomPropertiesWindow, EditTimeoutHdl, Timer *, void)
1647 {
1648     ValidateLine( m_pCurrentLine, false );
1649 }
1650 
IMPL_LINK_NOARG(CustomPropertiesWindow,BoxTimeoutHdl,Timer *,void)1651 IMPL_LINK_NOARG(CustomPropertiesWindow, BoxTimeoutHdl, Timer *, void)
1652 {
1653     ValidateLine( m_pCurrentLine, true );
1654 }
1655 
IsLineValid(CustomPropertyLine * pLine) const1656 bool CustomPropertiesWindow::IsLineValid( CustomPropertyLine* pLine ) const
1657 {
1658     bool bIsValid = true;
1659     pLine->m_bTypeLostFocus = false;
1660     auto nType = pLine->m_xTypeBox->get_active_id().toInt32();
1661     OUString sValue = pLine->m_xValueEdit->get_text();
1662     if ( sValue.isEmpty() )
1663         return true;
1664 
1665     sal_uInt32 nIndex = NUMBERFORMAT_ENTRY_NOT_FOUND;
1666     if ( Custom_Type_Number == nType )
1667         // tdf#116214 Scientific format allows to use also standard numbers
1668         nIndex = const_cast< SvNumberFormatter& >(
1669             m_aNumberFormatter ).GetFormatIndex( NF_SCIENTIFIC_000E00 );
1670     else if ( Custom_Type_Date == nType )
1671         nIndex = const_cast< SvNumberFormatter& >(
1672             m_aNumberFormatter).GetFormatIndex( NF_DATE_SYS_DDMMYYYY );
1673 
1674     if ( nIndex != NUMBERFORMAT_ENTRY_NOT_FOUND )
1675     {
1676         sal_uInt32 nTemp = nIndex;
1677         double fDummy = 0.0;
1678         bIsValid = const_cast< SvNumberFormatter& >(
1679             m_aNumberFormatter ).IsNumberFormat( sValue, nIndex, fDummy );
1680         if ( bIsValid && nTemp != nIndex )
1681             // sValue is a number but the format doesn't match the index
1682             bIsValid = false;
1683     }
1684 
1685     return bIsValid;
1686 }
1687 
ValidateLine(CustomPropertyLine * pLine,bool bIsFromTypeBox)1688 void CustomPropertiesWindow::ValidateLine( CustomPropertyLine* pLine, bool bIsFromTypeBox )
1689 {
1690     if (pLine && !IsLineValid(pLine))
1691     {
1692         if ( bIsFromTypeBox ) // LoseFocus of TypeBox
1693             pLine->m_bTypeLostFocus = true;
1694         std::unique_ptr<weld::MessageDialog> xMessageBox(Application::CreateMessageDialog(&m_rBody,
1695                                                          VclMessageType::Question, VclButtonsType::OkCancel, SfxResId(STR_SFX_QUERY_WRONG_TYPE)));
1696         if (xMessageBox->run() == RET_OK)
1697             pLine->m_xTypeBox->set_active_id(OUString::number(Custom_Type_Text));
1698         else
1699             pLine->m_xValueEdit->grab_focus();
1700     }
1701 }
1702 
SetVisibleLineCount(sal_uInt32 nCount)1703 void CustomPropertiesWindow::SetVisibleLineCount(sal_uInt32 nCount)
1704 {
1705     while (GetExistingLineCount() < nCount)
1706     {
1707         CreateNewLine();
1708     }
1709 }
1710 
AddLine(const OUString & sName,Any const & rAny)1711 void CustomPropertiesWindow::AddLine(const OUString& sName, Any const & rAny)
1712 {
1713     m_aCustomProperties.push_back(std::unique_ptr<CustomProperty>(new CustomProperty(sName, rAny)));
1714     ReloadLinesContent();
1715 }
1716 
CreateNewLine()1717 void CustomPropertiesWindow::CreateNewLine()
1718 {
1719     CustomPropertyLine* pNewLine = new CustomPropertyLine(this, &m_rBody);
1720     pNewLine->m_xNameBox->set_accessible_relation_labeled_by(&m_rHeaderAccName);
1721     pNewLine->m_xNameBox->set_accessible_name(m_rHeaderAccName.get_label());
1722     pNewLine->m_xTypeBox->set_accessible_relation_labeled_by(&m_rHeaderAccType);
1723     pNewLine->m_xTypeBox->set_accessible_name(m_rHeaderAccType.get_label());
1724     pNewLine->m_xValueEdit->set_accessible_relation_labeled_by(&m_rHeaderAccValue);
1725     pNewLine->m_xValueEdit->set_accessible_name(m_rHeaderAccValue.get_label());
1726 
1727     m_aCustomPropertiesLines.emplace_back( pNewLine );
1728 
1729     // this breaks online's jsdialogbuilder
1730     if (!comphelper::LibreOfficeKit::isActive()){
1731         // for ui-testing. Distinguish the elements in the lines
1732         sal_uInt16 nSize = m_aCustomPropertiesLines.size();
1733         pNewLine->m_xNameBox->set_buildable_name(
1734             pNewLine->m_xNameBox->get_buildable_name() + OUString::number(nSize));
1735         pNewLine->m_xTypeBox->set_buildable_name(
1736             pNewLine->m_xTypeBox->get_buildable_name() + OUString::number(nSize));
1737         pNewLine->m_xValueEdit->set_buildable_name(
1738             pNewLine->m_xValueEdit->get_buildable_name() + OUString::number(nSize));
1739         pNewLine->m_xRemoveButton->set_buildable_name(
1740             pNewLine->m_xRemoveButton->get_buildable_name() + OUString::number(nSize));
1741     }
1742 
1743     pNewLine->DoTypeHdl(*pNewLine->m_xTypeBox);
1744 }
1745 
AreAllLinesValid() const1746 bool CustomPropertiesWindow::AreAllLinesValid() const
1747 {
1748     bool bRet = true;
1749     for ( std::unique_ptr<CustomPropertyLine> const & pLine : m_aCustomPropertiesLines )
1750     {
1751         if ( !IsLineValid( pLine.get() ) )
1752         {
1753             bRet = false;
1754             break;
1755         }
1756     }
1757 
1758     return bRet;
1759 }
1760 
ClearAllLines()1761 void CustomPropertiesWindow::ClearAllLines()
1762 {
1763     for (auto& pLine : m_aCustomPropertiesLines)
1764     {
1765         pLine->Clear();
1766     }
1767     m_pCurrentLine = nullptr;
1768     m_aCustomProperties.clear();
1769     m_nScrollPos = 0;
1770 }
1771 
DoScroll(sal_Int32 nNewPos)1772 void CustomPropertiesWindow::DoScroll( sal_Int32 nNewPos )
1773 {
1774     StoreCustomProperties();
1775     m_nScrollPos += nNewPos;
1776     ReloadLinesContent();
1777 }
1778 
GetCustomProperties()1779 Sequence< beans::PropertyValue > CustomPropertiesWindow::GetCustomProperties()
1780 {
1781     StoreCustomProperties();
1782 
1783     Sequence< beans::PropertyValue > aPropertiesSeq(GetTotalLineCount());
1784     std::transform(
1785         m_aCustomProperties.begin(), m_aCustomProperties.end(), aPropertiesSeq.getArray(),
1786         [](const auto& el) { return comphelper::makePropertyValue(el->m_sName, el->m_aValue); });
1787 
1788     return aPropertiesSeq;
1789 }
1790 
CustomPropertiesTimeField(std::unique_ptr<weld::FormattedSpinButton> xTimeField)1791 CustomPropertiesTimeField::CustomPropertiesTimeField(std::unique_ptr<weld::FormattedSpinButton> xTimeField)
1792     : m_xTimeField(std::move(xTimeField))
1793     , m_xFormatter(new weld::TimeFormatter(*m_xTimeField))
1794     , m_isUTC(false)
1795 {
1796     m_xFormatter->SetExtFormat(ExtTimeFieldFormat::LongDuration);
1797     m_xFormatter->EnableEmptyField(false);
1798 }
1799 
get_value() const1800 tools::Time CustomPropertiesTimeField::get_value() const
1801 {
1802     return m_xFormatter->GetTime();
1803 }
1804 
set_value(const tools::Time & rTime)1805 void CustomPropertiesTimeField::set_value(const tools::Time& rTime)
1806 {
1807     m_xFormatter->SetTime(rTime);
1808 }
1809 
~CustomPropertiesTimeField()1810 CustomPropertiesTimeField::~CustomPropertiesTimeField()
1811 {
1812 }
1813 
CustomPropertiesDateField(SvtCalendarBox * pDateField)1814 CustomPropertiesDateField::CustomPropertiesDateField(SvtCalendarBox* pDateField)
1815     : m_xDateField(pDateField)
1816 {
1817     DateTime aDateTime(DateTime::SYSTEM);
1818     m_xDateField->set_date(aDateTime);
1819 }
1820 
set_visible(bool bVisible)1821 void CustomPropertiesDateField::set_visible(bool bVisible)
1822 {
1823     m_xDateField->set_visible(bVisible);
1824 }
1825 
get_date() const1826 Date CustomPropertiesDateField::get_date() const
1827 {
1828     return m_xDateField->get_date();
1829 }
1830 
set_date(const Date & rDate)1831 void CustomPropertiesDateField::set_date(const Date& rDate)
1832 {
1833     m_xDateField->set_date(rDate);
1834 }
1835 
~CustomPropertiesDateField()1836 CustomPropertiesDateField::~CustomPropertiesDateField()
1837 {
1838 }
1839 
StoreCustomProperties()1840 void CustomPropertiesWindow::StoreCustomProperties()
1841 {
1842     sal_uInt32 nDataModelPos = GetCurrentDataModelPosition();
1843 
1844     for (sal_uInt32 i = 0; nDataModelPos + i < GetTotalLineCount() && i < GetExistingLineCount(); i++)
1845     {
1846         CustomPropertyLine* pLine = m_aCustomPropertiesLines[i].get();
1847 
1848         OUString sPropertyName = pLine->m_xNameBox->get_active_text();
1849         if (!sPropertyName.isEmpty())
1850         {
1851             m_aCustomProperties[nDataModelPos + i]->m_sName = sPropertyName;
1852             auto nType = pLine->m_xTypeBox->get_active_id().toInt32();
1853             if (Custom_Type_Number == nType)
1854             {
1855                 double nValue = 0;
1856                 sal_uInt32 nIndex = m_aNumberFormatter.GetFormatIndex(NF_NUMBER_SYSTEM);
1857                 bool bIsNum = m_aNumberFormatter.
1858                     IsNumberFormat(pLine->m_xValueEdit->get_text(), nIndex, nValue);
1859                 if (bIsNum)
1860                     m_aCustomProperties[nDataModelPos + i]->m_aValue <<= nValue;
1861             }
1862             else if (Custom_Type_Boolean == nType)
1863             {
1864                 bool bValue = pLine->m_xYesNoButton->IsYesChecked();
1865                 m_aCustomProperties[nDataModelPos + i]->m_aValue <<= bValue;
1866             }
1867             else if (Custom_Type_Datetime == nType)
1868             {
1869                 Date aTmpDate = pLine->m_xDateField->get_date();
1870                 tools::Time aTmpTime = pLine->m_xTimeField->get_value();
1871                 util::DateTime const aDateTime(aTmpTime.GetNanoSec(),
1872                     aTmpTime.GetSec(), aTmpTime.GetMin(), aTmpTime.GetHour(),
1873                     aTmpDate.GetDay(), aTmpDate.GetMonth(), aTmpDate.GetYear(),
1874                     pLine->m_xTimeField->m_isUTC);
1875                 if (pLine->m_xDateField->m_TZ)
1876                 {
1877                     m_aCustomProperties[nDataModelPos + i]->m_aValue <<= util::DateTimeWithTimezone(
1878                         aDateTime, *pLine->m_xDateField->m_TZ);
1879                 }
1880                 else
1881                 {
1882                     m_aCustomProperties[nDataModelPos + i]->m_aValue <<= aDateTime;
1883                 }
1884             }
1885             else if (Custom_Type_Date == nType)
1886             {
1887                 Date aTmpDate = pLine->m_xDateField->get_date();
1888                 util::Date const aDate(aTmpDate.GetDay(), aTmpDate.GetMonth(),
1889                     aTmpDate.GetYear());
1890                 if (pLine->m_xDateField->m_TZ)
1891                 {
1892                     m_aCustomProperties[nDataModelPos + i]->m_aValue <<= util::DateWithTimezone(
1893                         aDate, *pLine->m_xDateField->m_TZ);
1894                 }
1895                 else
1896                 {
1897                     m_aCustomProperties[nDataModelPos + i]->m_aValue <<= aDate;
1898                 }
1899             }
1900             else if (Custom_Type_Duration == nType)
1901             {
1902                 m_aCustomProperties[nDataModelPos + i]->m_aValue <<= pLine->m_xDurationField->GetDuration();
1903             }
1904             else
1905             {
1906                 OUString sValue(pLine->m_xValueEdit->get_text());
1907                 m_aCustomProperties[nDataModelPos + i]->m_aValue <<= sValue;
1908             }
1909         }
1910     }
1911 }
1912 
SetCustomProperties(std::vector<std::unique_ptr<CustomProperty>> && rProperties)1913 void CustomPropertiesWindow::SetCustomProperties(std::vector< std::unique_ptr<CustomProperty> >&& rProperties)
1914 {
1915     m_aCustomProperties = std::move(rProperties);
1916     ReloadLinesContent();
1917 }
1918 
ReloadLinesContent()1919 void CustomPropertiesWindow::ReloadLinesContent()
1920 {
1921     double nTmpValue = 0;
1922     bool bTmpValue = false;
1923     OUString sTmpValue;
1924     util::DateTime aTmpDateTime;
1925     util::Date aTmpDate;
1926     util::DateTimeWithTimezone aTmpDateTimeTZ;
1927     util::DateWithTimezone aTmpDateTZ;
1928     util::Duration aTmpDuration;
1929     SvtSysLocale aSysLocale;
1930     const LocaleDataWrapper& rLocaleWrapper = aSysLocale.GetLocaleData();
1931     CustomProperties nType = Custom_Type_Unknown;
1932     OUString sValue;
1933 
1934     sal_uInt32 nDataModelPos = GetCurrentDataModelPosition();
1935     sal_uInt32 i = 0;
1936 
1937     for (; nDataModelPos + i < GetTotalLineCount() && i < GetExistingLineCount(); i++)
1938     {
1939         const OUString& rName = m_aCustomProperties[nDataModelPos + i]->m_sName;
1940         const css::uno::Any& rAny = m_aCustomProperties[nDataModelPos + i]->m_aValue;
1941 
1942         CustomPropertyLine* pLine = m_aCustomPropertiesLines[i].get();
1943         pLine->Clear();
1944 
1945         pLine->m_xNameBox->set_entry_text(rName);
1946         pLine->m_xLine->show();
1947 
1948         if (!rAny.hasValue())
1949         {
1950             pLine->m_xValueEdit->set_text(OUString());
1951         }
1952         else if (rAny >>= nTmpValue)
1953         {
1954             sal_uInt32 nIndex = m_aNumberFormatter.GetFormatIndex(NF_NUMBER_SYSTEM);
1955             m_aNumberFormatter.GetInputLineString(nTmpValue, nIndex, sValue);
1956             pLine->m_xValueEdit->set_text(sValue);
1957             nType = Custom_Type_Number;
1958         }
1959         else if (rAny >>= bTmpValue)
1960         {
1961             sValue = (bTmpValue ? rLocaleWrapper.getTrueWord() : rLocaleWrapper.getFalseWord());
1962             nType = Custom_Type_Boolean;
1963         }
1964         else if (rAny >>= sTmpValue)
1965         {
1966             pLine->m_xValueEdit->set_text(sTmpValue);
1967             nType = Custom_Type_Text;
1968         }
1969         else if (rAny >>= aTmpDate)
1970         {
1971             pLine->m_xDateField->set_date(Date(aTmpDate));
1972             nType = Custom_Type_Date;
1973         }
1974         else if (rAny >>= aTmpDateTime)
1975         {
1976             pLine->m_xDateField->set_date(Date(aTmpDateTime));
1977             pLine->m_xTimeField->set_value(tools::Time(aTmpDateTime));
1978             pLine->m_xTimeField->m_isUTC = aTmpDateTime.IsUTC;
1979             nType = Custom_Type_Datetime;
1980         }
1981         else if (rAny >>= aTmpDateTZ)
1982         {
1983             pLine->m_xDateField->set_date(Date(aTmpDateTZ.DateInTZ.Day,
1984                 aTmpDateTZ.DateInTZ.Month, aTmpDateTZ.DateInTZ.Year));
1985             pLine->m_xDateField->m_TZ = aTmpDateTZ.Timezone;
1986             nType = Custom_Type_Date;
1987         }
1988 
1989         else if (rAny >>= aTmpDateTimeTZ)
1990         {
1991             util::DateTime const& rDT(aTmpDateTimeTZ.DateTimeInTZ);
1992             pLine->m_xDateField->set_date(Date(rDT));
1993             pLine->m_xTimeField->set_value(tools::Time(rDT));
1994             pLine->m_xTimeField->m_isUTC = rDT.IsUTC;
1995             pLine->m_xDateField->m_TZ = aTmpDateTimeTZ.Timezone;
1996             nType = Custom_Type_Datetime;
1997         }
1998         else if (rAny >>= aTmpDuration)
1999         {
2000             nType = Custom_Type_Duration;
2001             pLine->m_xDurationField->SetDuration(aTmpDuration);
2002         }
2003 
2004         if (Custom_Type_Boolean == nType)
2005         {
2006             if (bTmpValue)
2007                 pLine->m_xYesNoButton->CheckYes();
2008             else
2009                 pLine->m_xYesNoButton->CheckNo();
2010         }
2011         pLine->m_xTypeBox->set_active_id(OUString::number(nType));
2012 
2013         pLine->DoTypeHdl(*pLine->m_xTypeBox);
2014     }
2015 
2016     // tdf#132667 - grab focus on the last inserted property
2017     if (i > 0 && m_aCustomProperties[nDataModelPos + i - 1]->m_sName.isEmpty())
2018     {
2019         CustomPropertyLine* pLine = m_aCustomPropertiesLines[i - 1].get();
2020         pLine->m_xNameBox->grab_focus();
2021     }
2022 
2023     while (nDataModelPos + i >= GetTotalLineCount() && i < GetExistingLineCount())
2024     {
2025         CustomPropertyLine* pLine = m_aCustomPropertiesLines[i].get();
2026         pLine->Hide();
2027         i++;
2028     }
2029 }
2030 
CustomPropertiesControl()2031 CustomPropertiesControl::CustomPropertiesControl()
2032     : m_nThumbPos(0)
2033 {
2034 }
2035 
Init(weld::Builder & rBuilder)2036 void CustomPropertiesControl::Init(weld::Builder& rBuilder)
2037 {
2038     m_xBox = rBuilder.weld_widget(u"box"_ustr);
2039     m_xBody = rBuilder.weld_container(u"properties"_ustr);
2040 
2041     m_xName = rBuilder.weld_label(u"name"_ustr);
2042     m_xType = rBuilder.weld_label(u"type"_ustr);
2043     m_xValue = rBuilder.weld_label(u"value"_ustr);
2044     m_xVertScroll = rBuilder.weld_scrolled_window(u"scroll"_ustr, true);
2045     m_xPropertiesWin.reset(new CustomPropertiesWindow(*m_xBody, *m_xName, *m_xType, *m_xValue));
2046 
2047     m_xBox->set_stack_background();
2048     m_xVertScroll->show();
2049 
2050     std::unique_ptr<CustomPropertyLine> xNewLine(new CustomPropertyLine(m_xPropertiesWin.get(), m_xBody.get()));
2051     Size aLineSize(xNewLine->m_xLine->get_preferred_size());
2052     m_xPropertiesWin->SetLineHeight(aLineSize.Height() + 6);
2053     m_xBody->set_size_request(aLineSize.Width() + 6, -1);
2054     auto nHeight = aLineSize.Height() * 8;
2055     m_xVertScroll->set_size_request(-1, nHeight + 6);
2056 
2057     m_xPropertiesWin->SetHeight(nHeight);
2058     m_xVertScroll->connect_size_allocate(LINK(this, CustomPropertiesControl, ResizeHdl));
2059 
2060     m_xName->set_size_request(xNewLine->m_xNameBox->get_preferred_size().Width(), -1);
2061     m_xType->set_size_request(xNewLine->m_xTypeBox->get_preferred_size().Width(), -1);
2062     m_xValue->set_size_request(xNewLine->m_xValueEdit->get_preferred_size().Width(), -1);
2063 
2064     m_xBody->move(xNewLine->m_xLine.get(), nullptr);
2065     xNewLine.reset();
2066 
2067     m_xPropertiesWin->SetRemovedHdl( LINK( this, CustomPropertiesControl, RemovedHdl ) );
2068 
2069     m_xVertScroll->vadjustment_set_lower(0);
2070     m_xVertScroll->vadjustment_set_upper(0);
2071     m_xVertScroll->vadjustment_set_page_size(0xFFFF);
2072 
2073     Link<weld::ScrolledWindow&,void> aScrollLink = LINK( this, CustomPropertiesControl, ScrollHdl );
2074     m_xVertScroll->connect_vadjustment_changed(aScrollLink);
2075 
2076     ResizeHdl(Size(-1, nHeight));
2077 }
2078 
IMPL_LINK(CustomPropertiesControl,ResizeHdl,const Size &,rSize,void)2079 IMPL_LINK(CustomPropertiesControl, ResizeHdl, const Size&, rSize, void)
2080 {
2081     int nHeight = rSize.Height() - 6;
2082     if (nHeight == m_xPropertiesWin->GetHeight())
2083         return;
2084     m_xPropertiesWin->SetHeight(nHeight);
2085     sal_Int32 nScrollOffset = m_xPropertiesWin->GetLineHeight();
2086     sal_Int32 nVisibleEntries = nHeight / nScrollOffset;
2087     m_xPropertiesWin->SetVisibleLineCount( nVisibleEntries );
2088     m_xVertScroll->vadjustment_set_page_increment( nVisibleEntries - 1 );
2089     m_xVertScroll->vadjustment_set_page_size( nVisibleEntries );
2090     m_xPropertiesWin->ReloadLinesContent();
2091 }
2092 
~CustomPropertiesControl()2093 CustomPropertiesControl::~CustomPropertiesControl()
2094 {
2095 }
2096 
IMPL_LINK(CustomPropertiesControl,ScrollHdl,weld::ScrolledWindow &,rScrollBar,void)2097 IMPL_LINK( CustomPropertiesControl, ScrollHdl, weld::ScrolledWindow&, rScrollBar, void )
2098 {
2099     sal_Int32 nOffset = m_xPropertiesWin->GetLineHeight();
2100     int nThumbPos = rScrollBar.vadjustment_get_value();
2101     nOffset *= ( m_nThumbPos - nThumbPos );
2102     m_nThumbPos = nThumbPos;
2103     m_xPropertiesWin->DoScroll( nOffset );
2104 }
2105 
IMPL_LINK_NOARG(CustomPropertiesControl,RemovedHdl,void *,void)2106 IMPL_LINK_NOARG(CustomPropertiesControl, RemovedHdl, void*, void)
2107 {
2108     auto nLineCount = m_xPropertiesWin->GetTotalLineCount();
2109     m_xVertScroll->vadjustment_set_upper(nLineCount + 1);
2110     if (m_xPropertiesWin->GetTotalLineCount() > m_xPropertiesWin->GetExistingLineCount())
2111     {
2112         m_xVertScroll->vadjustment_set_value(nLineCount - 1);
2113         ScrollHdl(*m_xVertScroll);
2114     }
2115 }
2116 
AddLine(Any const & rAny)2117 void CustomPropertiesControl::AddLine( Any const & rAny )
2118 {
2119     m_xPropertiesWin->AddLine( OUString(), rAny );
2120     auto nLineCount = m_xPropertiesWin->GetTotalLineCount();
2121     m_xVertScroll->vadjustment_set_upper(nLineCount + 1);
2122     if (m_xPropertiesWin->GetHeight() < nLineCount * m_xPropertiesWin->GetLineHeight())
2123     {
2124         m_xVertScroll->vadjustment_set_value(nLineCount + 1);
2125         ScrollHdl(*m_xVertScroll);
2126     }
2127 }
2128 
SetCustomProperties(std::vector<std::unique_ptr<CustomProperty>> && rProperties)2129 void CustomPropertiesControl::SetCustomProperties(std::vector< std::unique_ptr<CustomProperty> >&& rProperties)
2130 {
2131     m_xPropertiesWin->SetCustomProperties(std::move(rProperties));
2132     auto nLineCount = m_xPropertiesWin->GetTotalLineCount();
2133     m_xVertScroll->vadjustment_set_upper(nLineCount + 1);
2134 }
2135 
2136 // class SfxCustomPropertiesPage -----------------------------------------
SfxCustomPropertiesPage(weld::Container * pPage,weld::DialogController * pController,const SfxItemSet & rItemSet)2137 SfxCustomPropertiesPage::SfxCustomPropertiesPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rItemSet )
2138     : SfxTabPage(pPage, pController, u"sfx/ui/custominfopage.ui"_ustr, u"CustomInfoPage"_ustr, &rItemSet)
2139     , m_xPropertiesCtrl(new CustomPropertiesControl)
2140     , m_xAdd(m_xBuilder->weld_button(u"add"_ustr))
2141 {
2142     m_xPropertiesCtrl->Init(*m_xBuilder);
2143     m_xAdd->connect_clicked(LINK(this, SfxCustomPropertiesPage, AddHdl));
2144 }
2145 
~SfxCustomPropertiesPage()2146 SfxCustomPropertiesPage::~SfxCustomPropertiesPage()
2147 {
2148     m_xPropertiesCtrl.reset();
2149 }
2150 
IMPL_LINK_NOARG(SfxCustomPropertiesPage,AddHdl,weld::Button &,void)2151 IMPL_LINK_NOARG(SfxCustomPropertiesPage, AddHdl, weld::Button&, void)
2152 {
2153     // tdf#115853: reload current lines before adding a brand new one
2154     // indeed the info are deleted by ClearCustomProperties
2155     // each time SfxDocumentInfoItem destructor is called
2156     SfxDocumentInfoItem pInfo;
2157     const Sequence< beans::PropertyValue > aPropertySeq = m_xPropertiesCtrl->GetCustomProperties();
2158     for ( const auto& rProperty : aPropertySeq )
2159     {
2160         if ( !rProperty.Name.isEmpty() )
2161         {
2162             pInfo.AddCustomProperty( rProperty.Name, rProperty.Value );
2163         }
2164     }
2165 
2166     Any aAny;
2167     m_xPropertiesCtrl->AddLine(aAny);
2168 }
2169 
FillItemSet(SfxItemSet * rSet)2170 bool SfxCustomPropertiesPage::FillItemSet( SfxItemSet* rSet )
2171 {
2172     const SfxDocumentInfoItem* pItem = nullptr;
2173     SfxDocumentInfoItem* pInfo = nullptr;
2174     bool bMustDelete = false;
2175 
2176     if (const SfxItemSet* pItemSet = GetDialogExampleSet())
2177     {
2178         pItem = pItemSet->GetItemIfSet(SID_DOCINFO);
2179         if (!pItem)
2180             pInfo = const_cast<SfxDocumentInfoItem*>(&rSet->Get( SID_DOCINFO ));
2181         else
2182         {
2183             bMustDelete = true;
2184             pInfo = new SfxDocumentInfoItem( *pItem );
2185         }
2186     }
2187 
2188     if ( pInfo )
2189     {
2190         // If it's a CMIS document, we can't save custom properties
2191         if ( pInfo->isCmisDocument( ) )
2192         {
2193             if ( bMustDelete )
2194                 delete pInfo;
2195             return false;
2196         }
2197 
2198         pInfo->ClearCustomProperties();
2199         const Sequence< beans::PropertyValue > aPropertySeq = m_xPropertiesCtrl->GetCustomProperties();
2200         for ( const auto& rProperty : aPropertySeq )
2201         {
2202             if ( !rProperty.Name.isEmpty() )
2203                 pInfo->AddCustomProperty( rProperty.Name, rProperty.Value );
2204         }
2205     }
2206 
2207     if (pInfo)
2208     {
2209         rSet->Put(*pInfo);
2210         if ( bMustDelete )
2211             delete pInfo;
2212     }
2213     return true;
2214 }
2215 
Reset(const SfxItemSet * rItemSet)2216 void SfxCustomPropertiesPage::Reset( const SfxItemSet* rItemSet )
2217 {
2218     m_xPropertiesCtrl->ClearAllLines();
2219     const SfxDocumentInfoItem& rInfoItem = rItemSet->Get(SID_DOCINFO);
2220     std::vector< std::unique_ptr<CustomProperty> > aCustomProps = rInfoItem.GetCustomProperties();
2221     // tdf#123919 - sort custom document properties
2222     auto const sort = comphelper::string::NaturalStringSorter(
2223         comphelper::getProcessComponentContext(),
2224         Application::GetSettings().GetLanguageTag().getLocale());
2225     std::sort(aCustomProps.begin(), aCustomProps.end(),
2226               [&sort](const std::unique_ptr<CustomProperty>& rLHS,
2227                       const std::unique_ptr<CustomProperty>& rRHS) {
2228                   return sort.compare(rLHS->m_sName, rRHS->m_sName) < 0;
2229               });
2230     m_xPropertiesCtrl->SetCustomProperties(std::move(aCustomProps));
2231 }
2232 
DeactivatePage(SfxItemSet *)2233 DeactivateRC SfxCustomPropertiesPage::DeactivatePage( SfxItemSet* /*pSet*/ )
2234 {
2235     DeactivateRC nRet = DeactivateRC::LeavePage;
2236     if ( !m_xPropertiesCtrl->AreAllLinesValid() )
2237         nRet = DeactivateRC::KeepPage;
2238     return nRet;
2239 }
2240 
Create(weld::Container * pPage,weld::DialogController * pController,const SfxItemSet * rItemSet)2241 std::unique_ptr<SfxTabPage> SfxCustomPropertiesPage::Create(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet* rItemSet)
2242 {
2243     return std::make_unique<SfxCustomPropertiesPage>(pPage, pController, *rItemSet);
2244 }
2245 
CmisValue(weld::Widget * pParent,const OUString & aStr)2246 CmisValue::CmisValue(weld::Widget* pParent, const OUString& aStr)
2247     : m_xBuilder(Application::CreateBuilder(pParent, u"sfx/ui/cmisline.ui"_ustr))
2248     , m_xFrame(m_xBuilder->weld_frame(u"CmisFrame"_ustr))
2249     , m_xValueEdit(m_xBuilder->weld_entry(u"value"_ustr))
2250 {
2251     m_xValueEdit->show();
2252     m_xValueEdit->set_text(aStr);
2253 }
2254 
CmisDateTime(weld::Widget * pParent,const util::DateTime & aDateTime)2255 CmisDateTime::CmisDateTime(weld::Widget* pParent, const util::DateTime& aDateTime)
2256     : m_xBuilder(Application::CreateBuilder(pParent, u"sfx/ui/cmisline.ui"_ustr))
2257     , m_xFrame(m_xBuilder->weld_frame(u"CmisFrame"_ustr))
2258     , m_xDateField(new SvtCalendarBox(m_xBuilder->weld_menu_button(u"date"_ustr)))
2259     , m_xTimeField(m_xBuilder->weld_formatted_spin_button(u"time"_ustr))
2260     , m_xFormatter(new weld::TimeFormatter(*m_xTimeField))
2261 {
2262     m_xFormatter->SetExtFormat(ExtTimeFieldFormat::LongDuration);
2263     m_xFormatter->EnableEmptyField(false);
2264 
2265     m_xDateField->show();
2266     m_xTimeField->show();
2267     m_xDateField->set_date(Date(aDateTime));
2268     m_xFormatter->SetTime(tools::Time(aDateTime));
2269 }
2270 
CmisYesNo(weld::Widget * pParent,bool bValue)2271 CmisYesNo::CmisYesNo(weld::Widget* pParent, bool bValue)
2272     : m_xBuilder(Application::CreateBuilder(pParent, u"sfx/ui/cmisline.ui"_ustr))
2273     , m_xFrame(m_xBuilder->weld_frame(u"CmisFrame"_ustr))
2274     , m_xYesButton(m_xBuilder->weld_radio_button(u"yes"_ustr))
2275     , m_xNoButton(m_xBuilder->weld_radio_button(u"no"_ustr))
2276 {
2277     m_xYesButton->show();
2278     m_xNoButton->show();
2279     if (bValue)
2280         m_xYesButton->set_active(true);
2281     else
2282         m_xNoButton->set_active(true);
2283 }
2284 
2285 // struct CmisPropertyLine ---------------------------------------------
CmisPropertyLine(weld::Widget * pParent)2286 CmisPropertyLine::CmisPropertyLine(weld::Widget* pParent)
2287     : m_xBuilder(Application::CreateBuilder(pParent, u"sfx/ui/cmisline.ui"_ustr))
2288     , m_sType(CMIS_TYPE_STRING)
2289     , m_bUpdatable(false)
2290     , m_bRequired(false)
2291     , m_bMultiValued(false)
2292     , m_bOpenChoice(false)
2293     , m_xFrame(m_xBuilder->weld_frame(u"CmisFrame"_ustr))
2294     , m_xName(m_xBuilder->weld_label(u"name"_ustr))
2295     , m_xType(m_xBuilder->weld_label(u"type"_ustr))
2296 {
2297     m_xFrame->set_sensitive(true);
2298 }
2299 
~CmisPropertyLine()2300 CmisPropertyLine::~CmisPropertyLine( )
2301 {
2302 }
2303 
2304 // class CmisPropertiesWindow -----------------------------------------
2305 
CmisPropertiesWindow(std::unique_ptr<weld::Container> xParent)2306 CmisPropertiesWindow::CmisPropertiesWindow(std::unique_ptr<weld::Container> xParent)
2307     : m_xBox(std::move(xParent))
2308     , m_aNumberFormatter(::comphelper::getProcessComponentContext(),
2309                          Application::GetSettings().GetLanguageTag().getLanguageType())
2310 {
2311 }
2312 
~CmisPropertiesWindow()2313 CmisPropertiesWindow::~CmisPropertiesWindow()
2314 {
2315 }
2316 
ClearAllLines()2317 void CmisPropertiesWindow::ClearAllLines()
2318 {
2319     m_aCmisPropertiesLines.clear();
2320 }
2321 
AddLine(const OUString & sId,const OUString & sName,const OUString & sType,const bool bUpdatable,const bool bRequired,const bool bMultiValued,const bool bOpenChoice,Any &,Any const & rAny)2322 void CmisPropertiesWindow::AddLine( const OUString& sId, const OUString& sName,
2323                                     const OUString& sType, const bool bUpdatable,
2324                                     const bool bRequired, const bool bMultiValued,
2325                                     const bool bOpenChoice, Any& /*aChoices*/, Any const & rAny )
2326 {
2327     std::unique_ptr<CmisPropertyLine> pNewLine(new CmisPropertyLine(m_xBox.get()));
2328 
2329     pNewLine->m_sId = sId;
2330     pNewLine->m_sType = sType;
2331     pNewLine->m_bUpdatable = bUpdatable;
2332     pNewLine->m_bRequired = bRequired;
2333     pNewLine->m_bMultiValued = bMultiValued;
2334     pNewLine->m_bOpenChoice = bOpenChoice;
2335 
2336     if ( sType == CMIS_TYPE_INTEGER )
2337     {
2338         Sequence< sal_Int64 > seqValue;
2339         rAny >>= seqValue;
2340         sal_uInt32 nIndex = m_aNumberFormatter.GetFormatIndex( NF_NUMBER_SYSTEM );
2341         for (const auto& rValue : seqValue)
2342         {
2343             OUString sValue;
2344             m_aNumberFormatter.GetInputLineString( rValue, nIndex, sValue );
2345             std::unique_ptr<CmisValue> pValue(new CmisValue(m_xBox.get(), sValue));
2346             pValue->m_xValueEdit->set_editable(bUpdatable);
2347             pNewLine->m_aValues.push_back( std::move(pValue) );
2348         }
2349     }
2350     else if ( sType == CMIS_TYPE_DECIMAL )
2351     {
2352         Sequence< double > seqValue;
2353         rAny >>= seqValue;
2354         sal_uInt32 nIndex = m_aNumberFormatter.GetFormatIndex( NF_NUMBER_SYSTEM );
2355         for (const auto& rValue : seqValue)
2356         {
2357             OUString sValue;
2358             m_aNumberFormatter.GetInputLineString( rValue, nIndex, sValue );
2359             std::unique_ptr<CmisValue> pValue(new CmisValue(m_xBox.get(), sValue));
2360             pValue->m_xValueEdit->set_editable(bUpdatable);
2361             pNewLine->m_aValues.push_back( std::move(pValue) );
2362         }
2363 
2364     }
2365     else if ( sType == CMIS_TYPE_BOOL )
2366     {
2367         Sequence<sal_Bool> seqValue;
2368         rAny >>= seqValue;
2369         for (const auto& rValue : seqValue)
2370         {
2371             std::unique_ptr<CmisYesNo> pYesNo(new CmisYesNo(m_xBox.get(), rValue));
2372             pYesNo->m_xYesButton->set_sensitive( bUpdatable );
2373             pYesNo->m_xNoButton->set_sensitive( bUpdatable );
2374             pNewLine->m_aYesNos.push_back( std::move(pYesNo) );
2375         }
2376     }
2377     else if ( sType == CMIS_TYPE_STRING )
2378     {
2379         Sequence< OUString > seqValue;
2380         rAny >>= seqValue;
2381         for (const auto& rValue : seqValue)
2382         {
2383             std::unique_ptr<CmisValue> pValue(new CmisValue(m_xBox.get(), rValue));
2384             pValue->m_xValueEdit->set_editable(bUpdatable);
2385             pNewLine->m_aValues.push_back( std::move(pValue) );
2386         }
2387     }
2388     else if ( sType == CMIS_TYPE_DATETIME )
2389     {
2390         Sequence< util::DateTime > seqValue;
2391         rAny >>= seqValue;
2392         for (const auto& rValue : seqValue)
2393         {
2394             std::unique_ptr<CmisDateTime> pDateTime(new CmisDateTime(m_xBox.get(), rValue));
2395             pDateTime->m_xDateField->set_sensitive(bUpdatable);
2396             pDateTime->m_xTimeField->set_sensitive(bUpdatable);
2397             pNewLine->m_aDateTimes.push_back( std::move(pDateTime) );
2398         }
2399     }
2400     pNewLine->m_xName->set_label( sName );
2401     pNewLine->m_xName->show();
2402     pNewLine->m_xType->set_label( sType );
2403     pNewLine->m_xType->show();
2404 
2405     m_aCmisPropertiesLines.push_back( std::move(pNewLine) );
2406 }
2407 
GetCmisProperties() const2408 Sequence< document::CmisProperty > CmisPropertiesWindow::GetCmisProperties() const
2409 {
2410     Sequence< document::CmisProperty > aPropertiesSeq( m_aCmisPropertiesLines.size() );
2411     auto aPropertiesSeqRange = asNonConstRange(aPropertiesSeq);
2412     sal_Int32 i = 0;
2413     for ( auto& rxLine : m_aCmisPropertiesLines )
2414     {
2415         CmisPropertyLine* pLine = rxLine.get();
2416 
2417         aPropertiesSeqRange[i].Id = pLine->m_sId;
2418         aPropertiesSeqRange[i].Type = pLine->m_sType;
2419         aPropertiesSeqRange[i].Updatable = pLine->m_bUpdatable;
2420         aPropertiesSeqRange[i].Required = pLine->m_bRequired;
2421         aPropertiesSeqRange[i].OpenChoice = pLine->m_bOpenChoice;
2422         aPropertiesSeqRange[i].MultiValued = pLine->m_bMultiValued;
2423 
2424         OUString sPropertyName = pLine->m_xName->get_label();
2425         if ( !sPropertyName.isEmpty() )
2426         {
2427             aPropertiesSeqRange[i].Name = sPropertyName;
2428             OUString sType = pLine->m_xType->get_label();
2429             if ( CMIS_TYPE_DECIMAL == sType )
2430             {
2431                 sal_uInt32 nIndex = const_cast< SvNumberFormatter& >(
2432                     m_aNumberFormatter ).GetFormatIndex( NF_NUMBER_SYSTEM );
2433                 Sequence< double > seqValue( pLine->m_aValues.size( ) );
2434                 auto seqValueRange = asNonConstRange(seqValue);
2435                 sal_Int32 k = 0;
2436                 for ( const auto& rxValue : pLine->m_aValues )
2437                 {
2438                     double dValue = 0.0;
2439                     OUString sValue( rxValue->m_xValueEdit->get_text() );
2440                     bool bIsNum = const_cast< SvNumberFormatter& >( m_aNumberFormatter ).
2441                     IsNumberFormat( sValue, nIndex, dValue );
2442                     if ( bIsNum )
2443                         seqValueRange[k] = dValue;
2444                     ++k;
2445                 }
2446                 aPropertiesSeqRange[i].Value <<= seqValue;
2447             }
2448             else if ( CMIS_TYPE_INTEGER == sType )
2449             {
2450                 sal_uInt32 nIndex = const_cast< SvNumberFormatter& >(
2451                     m_aNumberFormatter ).GetFormatIndex( NF_NUMBER_SYSTEM );
2452                 Sequence< sal_Int64 > seqValue( pLine->m_aValues.size( ) );
2453                 auto seqValueRange = asNonConstRange(seqValue);
2454                 sal_Int32 k = 0;
2455                 for ( const auto& rxValue : pLine->m_aValues )
2456                 {
2457                     double dValue = 0;
2458                     OUString sValue( rxValue->m_xValueEdit->get_text() );
2459                     bool bIsNum = const_cast< SvNumberFormatter& >( m_aNumberFormatter ).
2460                     IsNumberFormat( sValue, nIndex, dValue );
2461                     if ( bIsNum )
2462                         seqValueRange[k] = static_cast<sal_Int64>(dValue);
2463                     ++k;
2464                 }
2465                 aPropertiesSeqRange[i].Value <<= seqValue;
2466             }
2467             else if ( CMIS_TYPE_BOOL == sType )
2468             {
2469                 Sequence<sal_Bool> seqValue( pLine->m_aYesNos.size( ) );
2470                 sal_Bool* pseqValue = seqValue.getArray();
2471                 sal_Int32 k = 0;
2472                 for ( const auto& rxYesNo : pLine->m_aYesNos )
2473                 {
2474                     bool bValue = rxYesNo->m_xYesButton->get_active();
2475                     pseqValue[k] = bValue;
2476                     ++k;
2477                 }
2478                 aPropertiesSeqRange[i].Value <<= seqValue;
2479 
2480             }
2481             else if ( CMIS_TYPE_DATETIME == sType )
2482             {
2483                 Sequence< util::DateTime > seqValue( pLine->m_aDateTimes.size( ) );
2484                 auto seqValueRange = asNonConstRange(seqValue);
2485                 sal_Int32 k = 0;
2486                 for ( const auto& rxDateTime : pLine->m_aDateTimes )
2487                 {
2488                     Date aTmpDate = rxDateTime->m_xDateField->get_date();
2489                     tools::Time aTmpTime = rxDateTime->m_xFormatter->GetTime();
2490                     util::DateTime aDateTime( aTmpTime.GetNanoSec(), aTmpTime.GetSec(),
2491                                               aTmpTime.GetMin(), aTmpTime.GetHour(),
2492                                               aTmpDate.GetDay(), aTmpDate.GetMonth(),
2493                                               aTmpDate.GetYear(), true );
2494                     seqValueRange[k] = aDateTime;
2495                     ++k;
2496                 }
2497                 aPropertiesSeqRange[i].Value <<= seqValue;
2498             }
2499             else
2500             {
2501                 Sequence< OUString > seqValue( pLine->m_aValues.size( ) );
2502                 auto seqValueRange = asNonConstRange(seqValue);
2503                 sal_Int32 k = 0;
2504                 for ( const auto& rxValue : pLine->m_aValues )
2505                 {
2506                     seqValueRange[k] = rxValue->m_xValueEdit->get_text();
2507                     ++k;
2508                 }
2509                 aPropertiesSeqRange[i].Value <<= seqValue;
2510             }
2511         }
2512         ++i;
2513     }
2514 
2515     return aPropertiesSeq;
2516 }
2517 
CmisPropertiesControl(weld::Builder & rBuilder)2518 CmisPropertiesControl::CmisPropertiesControl(weld::Builder& rBuilder)
2519     : m_aPropertiesWin(rBuilder.weld_container(u"CmisWindow"_ustr))
2520     , m_xScrolledWindow(rBuilder.weld_scrolled_window(u"CmisScroll"_ustr))
2521 {
2522     // set height to something small and force it to take the size
2523     // dictated by the other pages
2524     m_xScrolledWindow->set_size_request(-1, 42);
2525 }
2526 
ClearAllLines()2527 void CmisPropertiesControl::ClearAllLines()
2528 {
2529    m_aPropertiesWin.ClearAllLines();
2530 }
2531 
AddLine(const OUString & sId,const OUString & sName,const OUString & sType,const bool bUpdatable,const bool bRequired,const bool bMultiValued,const bool bOpenChoice,Any & aChoices,Any const & rAny)2532 void CmisPropertiesControl::AddLine( const OUString& sId, const OUString& sName,
2533                                      const OUString& sType, const bool bUpdatable,
2534                                      const bool bRequired, const bool bMultiValued,
2535                                      const bool bOpenChoice, Any& aChoices, Any const & rAny
2536                                      )
2537 {
2538     m_aPropertiesWin.AddLine( sId, sName, sType, bUpdatable, bRequired, bMultiValued,
2539                                bOpenChoice, aChoices, rAny );
2540 }
2541 
2542 // class SfxCmisPropertiesPage -----------------------------------------
SfxCmisPropertiesPage(weld::Container * pPage,weld::DialogController * pController,const SfxItemSet & rItemSet)2543 SfxCmisPropertiesPage::SfxCmisPropertiesPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rItemSet)
2544     : SfxTabPage(pPage, pController, u"sfx/ui/cmisinfopage.ui"_ustr, u"CmisInfoPage"_ustr, &rItemSet)
2545     , m_xPropertiesCtrl(new CmisPropertiesControl(*m_xBuilder))
2546 {
2547 }
2548 
~SfxCmisPropertiesPage()2549 SfxCmisPropertiesPage::~SfxCmisPropertiesPage()
2550 {
2551     m_xPropertiesCtrl.reset();
2552 }
2553 
FillItemSet(SfxItemSet * rSet)2554 bool SfxCmisPropertiesPage::FillItemSet( SfxItemSet* rSet )
2555 {
2556     const SfxDocumentInfoItem* pItem = nullptr;
2557     SfxDocumentInfoItem* pInfo = nullptr;
2558     bool bMustDelete = false;
2559 
2560     if (const SfxItemSet* pItemSet = GetDialogExampleSet())
2561     {
2562         pItem = pItemSet->GetItemIfSet(SID_DOCINFO);
2563         if (!pItem)
2564             pInfo = const_cast<SfxDocumentInfoItem*>(&rSet->Get( SID_DOCINFO ));
2565         else
2566         {
2567             bMustDelete = true;
2568             pInfo = new SfxDocumentInfoItem( *pItem );
2569         }
2570     }
2571 
2572     sal_Int32 modifiedNum = 0;
2573     if ( pInfo )
2574     {
2575         Sequence< document::CmisProperty > aOldProps = pInfo->GetCmisProperties( );
2576         Sequence< document::CmisProperty > aNewProps = m_xPropertiesCtrl->GetCmisProperties();
2577 
2578         std::vector< document::CmisProperty > changedProps;
2579         for ( sal_Int32 i = 0; i< aNewProps.getLength( ); ++i )
2580         {
2581             if ( aOldProps[i].Updatable && !aNewProps[i].Id.isEmpty( ) )
2582             {
2583                 if ( aOldProps[i].Type == CMIS_TYPE_DATETIME )
2584                 {
2585                     Sequence< util::DateTime > oldValue;
2586                     aOldProps[i].Value >>= oldValue;
2587                     // We only edit hours and minutes
2588                     // don't compare NanoSeconds and Seconds
2589                     for ( auto& rDateTime : asNonConstRange(oldValue) )
2590                     {
2591                         rDateTime.NanoSeconds = 0;
2592                         rDateTime.Seconds = 0;
2593                     }
2594                     Sequence< util::DateTime > newValue;
2595                     aNewProps[i].Value >>= newValue;
2596                     if ( oldValue != newValue )
2597                     {
2598                         modifiedNum++;
2599                         changedProps.push_back( aNewProps[i] );
2600                     }
2601                 }
2602                 else if ( aOldProps[i].Value != aNewProps[i].Value )
2603                 {
2604                     modifiedNum++;
2605                     changedProps.push_back( aNewProps[i] );
2606                 }
2607             }
2608         }
2609         Sequence< document::CmisProperty> aModifiedProps( comphelper::containerToSequence(changedProps) );
2610         pInfo->SetCmisProperties( aModifiedProps );
2611         rSet->Put( *pInfo );
2612         if ( bMustDelete )
2613             delete pInfo;
2614     }
2615 
2616     return modifiedNum;
2617 }
2618 
Reset(const SfxItemSet * rItemSet)2619 void SfxCmisPropertiesPage::Reset( const SfxItemSet* rItemSet )
2620 {
2621     m_xPropertiesCtrl->ClearAllLines();
2622     const SfxDocumentInfoItem& rInfoItem = rItemSet->Get(SID_DOCINFO);
2623     uno::Sequence< document::CmisProperty > aCmisProps = rInfoItem.GetCmisProperties();
2624     for ( auto& rCmisProp : asNonConstRange(aCmisProps) )
2625     {
2626         m_xPropertiesCtrl->AddLine(rCmisProp.Id,
2627                                    rCmisProp.Name,
2628                                    rCmisProp.Type,
2629                                    rCmisProp.Updatable,
2630                                    rCmisProp.Required,
2631                                    rCmisProp.MultiValued,
2632                                    rCmisProp.OpenChoice,
2633                                    rCmisProp.Choices,
2634                                    rCmisProp.Value);
2635     }
2636 }
2637 
DeactivatePage(SfxItemSet *)2638 DeactivateRC SfxCmisPropertiesPage::DeactivatePage( SfxItemSet* /*pSet*/ )
2639 {
2640     return DeactivateRC::LeavePage;
2641 }
2642 
Create(weld::Container * pPage,weld::DialogController * pController,const SfxItemSet * rItemSet)2643 std::unique_ptr<SfxTabPage> SfxCmisPropertiesPage::Create(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet* rItemSet)
2644 {
2645     return std::make_unique<SfxCmisPropertiesPage>(pPage, pController, *rItemSet);
2646 }
2647 
2648 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
2649