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