xref: /core/cui/source/options/optpath.cxx (revision dfaa7d4d)
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 <svx/svxdlg.hxx>
21 #include <sfx2/filedlghelper.hxx>
22 #include <sfx2/app.hxx>
23 #include <svl/aeitem.hxx>
24 #include <svtools/svtabbx.hxx>
25 #include <svtools/treelistentry.hxx>
26 #include <tools/urlobj.hxx>
27 #include <vcl/svapp.hxx>
28 #include <unotools/defaultoptions.hxx>
29 #include <unotools/localfilehelper.hxx>
30 #include <unotools/pathoptions.hxx>
31 #include <unotools/moduleoptions.hxx>
32 #include <unotools/viewoptions.hxx>
33 
34 #include <optpath.hxx>
35 #include <dialmgr.hxx>
36 #include <strings.hrc>
37 #include <comphelper/configuration.hxx>
38 #include <comphelper/processfactory.hxx>
39 #include <comphelper/string.hxx>
40 #include <com/sun/star/uno/Exception.hpp>
41 #include <com/sun/star/beans/XPropertySet.hpp>
42 #include <com/sun/star/beans/PropertyAttribute.hpp>
43 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
44 #include <com/sun/star/ui/dialogs/ExecutableDialogResults.hpp>
45 #include <com/sun/star/ui/dialogs/XAsynchronousExecutableDialog.hpp>
46 #include <com/sun/star/ui/dialogs/FolderPicker.hpp>
47 #include <com/sun/star/ui/dialogs/FilePicker.hpp>
48 #include <com/sun/star/ui/dialogs/TemplateDescription.hpp>
49 #include <com/sun/star/util/thePathSettings.hpp>
50 #include <officecfg/Office/Common.hxx>
51 #include "optHeaderTabListbox.hxx"
52 #include <vcl/help.hxx>
53 #include <tools/diagnose_ex.h>
54 #include <sal/log.hxx>
55 
56 using namespace css;
57 using namespace css::beans;
58 using namespace css::lang;
59 using namespace css::ui::dialogs;
60 using namespace css::uno;
61 using namespace svx;
62 
63 // define ----------------------------------------------------------------
64 
65 #define TAB_WIDTH_MIN   10
66 #define ITEMID_TYPE       1
67 #define ITEMID_PATH       2
68 
69 #define POSTFIX_INTERNAL    "_internal"
70 #define POSTFIX_USER        "_user"
71 #define POSTFIX_WRITABLE    "_writable"
72 #define VAR_ONE             "%1"
73 #define IODLG_CONFIGNAME    "FilePicker_Save"
74 
75 // struct OptPath_Impl ---------------------------------------------------
76 
77 struct OptPath_Impl
78 {
79     SvtDefaultOptions           m_aDefOpt;
80     Image                       m_aLockImage;
81     OUString                    m_sMultiPathDlg;
82     Reference< css::util::XPathSettings >   m_xPathSettings;
83 
84     OptPath_Impl(const Image& rLockImage, const OUString& rMultiPathDlg)
85         : m_aLockImage(rLockImage)
86         , m_sMultiPathDlg(rMultiPathDlg)
87     {
88     }
89 };
90 
91 // struct PathUserData_Impl ----------------------------------------------
92 
93 struct PathUserData_Impl
94 {
95     sal_uInt16      nRealId;
96     SfxItemState    eState;
97     OUString        sUserPath;
98     OUString        sWritablePath;
99 
100     explicit PathUserData_Impl( sal_uInt16 nId ) :
101         nRealId( nId ), eState( SfxItemState::UNKNOWN ) {}
102 };
103 
104 struct Handle2CfgNameMapping_Impl
105 {
106     sal_uInt16      m_nHandle;
107     const char* m_pCfgName;
108 };
109 
110 static Handle2CfgNameMapping_Impl const Hdl2CfgMap_Impl[] =
111 {
112     { SvtPathOptions::PATH_AUTOCORRECT, "AutoCorrect" },
113     { SvtPathOptions::PATH_AUTOTEXT,    "AutoText" },
114     { SvtPathOptions::PATH_BACKUP,      "Backup" },
115     { SvtPathOptions::PATH_GALLERY,     "Gallery" },
116     { SvtPathOptions::PATH_GRAPHIC,     "Graphic" },
117     { SvtPathOptions::PATH_TEMP,        "Temp" },
118     { SvtPathOptions::PATH_TEMPLATE,    "Template" },
119     { SvtPathOptions::PATH_WORK,        "Work" },
120     { SvtPathOptions::PATH_DICTIONARY,        "Dictionary" },
121     { SvtPathOptions::PATH_CLASSIFICATION, "Classification" },
122 #if OSL_DEBUG_LEVEL > 1
123     { SvtPathOptions::PATH_LINGUISTIC,        "Linguistic" },
124 #endif
125     { USHRT_MAX, nullptr }
126 };
127 
128 static OUString getCfgName_Impl( sal_uInt16 _nHandle )
129 {
130     OUString sCfgName;
131     sal_uInt16 nIndex = 0;
132     while ( Hdl2CfgMap_Impl[ nIndex ].m_nHandle != USHRT_MAX )
133     {
134         if ( Hdl2CfgMap_Impl[ nIndex ].m_nHandle == _nHandle )
135         {
136             // config name found
137             sCfgName = OUString::createFromAscii( Hdl2CfgMap_Impl[ nIndex ].m_pCfgName );
138             break;
139         }
140         ++nIndex;
141     }
142 
143     return sCfgName;
144 }
145 
146 #define MULTIPATH_DELIMITER     ';'
147 
148 static OUString Convert_Impl( const OUString& rValue )
149 {
150     OUString aReturn;
151     if (rValue.isEmpty())
152         return aReturn;
153 
154     sal_Int32 nPos = 0;
155 
156     for (;;)
157     {
158         OUString aValue = rValue.getToken( 0, MULTIPATH_DELIMITER, nPos );
159         INetURLObject aObj( aValue );
160         if ( aObj.GetProtocol() == INetProtocol::File )
161             aReturn += aObj.PathToFileName();
162         if ( nPos < 0 )
163             break;
164         aReturn += OUStringLiteral1(MULTIPATH_DELIMITER);
165     }
166 
167     return aReturn;
168 }
169 
170 // functions -------------------------------------------------------------
171 
172 bool IsMultiPath_Impl( const sal_uInt16 nIndex )
173 {
174 #if OSL_DEBUG_LEVEL > 1
175     return ( SvtPathOptions::PATH_AUTOCORRECT == nIndex ||
176              SvtPathOptions::PATH_AUTOTEXT == nIndex ||
177              SvtPathOptions::PATH_BASIC == nIndex ||
178              SvtPathOptions::PATH_GALLERY == nIndex ||
179              SvtPathOptions::PATH_TEMPLATE == nIndex );
180 #else
181     return ( SvtPathOptions::PATH_AUTOCORRECT == nIndex ||
182              SvtPathOptions::PATH_AUTOTEXT == nIndex ||
183              SvtPathOptions::PATH_BASIC == nIndex ||
184              SvtPathOptions::PATH_GALLERY == nIndex ||
185              SvtPathOptions::PATH_TEMPLATE == nIndex ||
186              SvtPathOptions::PATH_LINGUISTIC == nIndex ||
187              SvtPathOptions::PATH_DICTIONARY == nIndex  );
188 #endif
189 }
190 
191 // class SvxPathTabPage --------------------------------------------------
192 
193 SvxPathTabPage::SvxPathTabPage(vcl::Window* pParent, const SfxItemSet& rSet)
194     : SfxTabPage( pParent, "OptPathsPage", "cui/ui/optpathspage.ui", &rSet)
195     , pImpl( new OptPath_Impl(get<FixedImage>("lock")->GetImage(),
196         get<FixedText>("editpaths")->GetText()) )
197     , xDialogListener ( new ::svt::DialogClosedListener() )
198 {
199     get(m_pStandardBtn, "default");
200     get(m_pPathBtn, "edit");
201     get(m_pPathCtrl, "paths");
202 
203     m_pStandardBtn->SetClickHdl(LINK(this, SvxPathTabPage, StandardHdl_Impl));
204     m_pPathBtn->SetClickHdl( LINK( this, SvxPathTabPage, PathHdl_Impl ) );
205 
206     Size aControlSize(236 , 147);
207     aControlSize = LogicToPixel(aControlSize, MapMode(MapUnit::MapAppFont));
208     m_pPathCtrl->set_width_request(aControlSize.Width());
209     m_pPathCtrl->set_height_request(aControlSize.Height());
210     WinBits nBits = WB_SORT | WB_HSCROLL | WB_CLIPCHILDREN | WB_TABSTOP;
211     pPathBox = VclPtr<svx::OptHeaderTabListBox>::Create( *m_pPathCtrl, nBits );
212 
213     HeaderBar &rBar = pPathBox->GetTheHeaderBar();
214     rBar.SetSelectHdl( LINK( this, SvxPathTabPage, HeaderSelect_Impl ) );
215     rBar.SetEndDragHdl( LINK( this, SvxPathTabPage, HeaderEndDrag_Impl ) );
216 
217     rBar.InsertItem( ITEMID_TYPE, get<FixedText>("type")->GetText(),
218                             0,
219                             HeaderBarItemBits::LEFT | HeaderBarItemBits::VCENTER | HeaderBarItemBits::CLICKABLE | HeaderBarItemBits::UPARROW );
220     rBar.InsertItem( ITEMID_PATH, get<FixedText>("path")->GetText(),
221                             0,
222                             HeaderBarItemBits::LEFT | HeaderBarItemBits::VCENTER );
223 
224     long nWidth1 = rBar.GetTextWidth(rBar.GetItemText(ITEMID_TYPE));
225     long nWidth2 = rBar.GetTextWidth(rBar.GetItemText(ITEMID_PATH));
226 
227     long aTabs[] = {0, 0, 0};
228     aTabs[1] = nWidth1 + 12;
229     aTabs[2] = aTabs[1] + nWidth2 + 12;
230     pPathBox->SetTabs(SAL_N_ELEMENTS(aTabs), aTabs, MapUnit::MapPixel);
231 
232     pPathBox->SetDoubleClickHdl( LINK( this, SvxPathTabPage, DoubleClickPathHdl_Impl ) );
233     pPathBox->SetSelectHdl( LINK( this, SvxPathTabPage, PathSelect_Impl ) );
234     pPathBox->SetSelectionMode( SelectionMode::Multiple );
235     pPathBox->SetHighlightRange();
236 
237     xDialogListener->SetDialogClosedLink( LINK( this, SvxPathTabPage, DialogClosedHdl ) );
238 }
239 
240 
241 SvxPathTabPage::~SvxPathTabPage()
242 {
243     disposeOnce();
244 }
245 
246 void SvxPathTabPage::dispose()
247 {
248     if ( pPathBox )
249     {
250         for ( sal_uLong i = 0; i < pPathBox->GetEntryCount(); ++i )
251             delete static_cast<PathUserData_Impl*>(pPathBox->GetEntry(i)->GetUserData());
252         pPathBox.disposeAndClear();
253     }
254     pImpl.reset();
255     m_pPathCtrl.clear();
256     m_pStandardBtn.clear();
257     m_pPathBtn.clear();
258     SfxTabPage::dispose();
259 }
260 
261 VclPtr<SfxTabPage> SvxPathTabPage::Create( TabPageParent pParent,
262                                            const SfxItemSet* rAttrSet )
263 {
264     return VclPtr<SvxPathTabPage>::Create( pParent.pParent, *rAttrSet );
265 }
266 
267 bool SvxPathTabPage::FillItemSet( SfxItemSet* )
268 {
269     for ( sal_uLong i = 0; i < pPathBox->GetEntryCount(); ++i )
270     {
271         PathUserData_Impl* pPathImpl = static_cast<PathUserData_Impl*>(pPathBox->GetEntry(i)->GetUserData());
272         sal_uInt16 nRealId = pPathImpl->nRealId;
273         if ( pPathImpl->eState == SfxItemState::SET )
274             SetPathList( nRealId, pPathImpl->sUserPath, pPathImpl->sWritablePath );
275     }
276     return true;
277 }
278 
279 void SvxPathTabPage::Reset( const SfxItemSet* )
280 {
281     pPathBox->Clear();
282 
283     HeaderBar &rBar = pPathBox->GetTheHeaderBar();
284     long nWidth1 = rBar.GetTextWidth(rBar.GetItemText(1));
285     long nWidth2 = rBar.GetTextWidth(rBar.GetItemText(2));
286 
287     for( sal_uInt16 i = 0; i <= sal_uInt16(SvtPathOptions::PATH_CLASSIFICATION); ++i )
288     {
289         // only writer uses autotext
290         if ( i == SvtPathOptions::PATH_AUTOTEXT
291             && !SvtModuleOptions().IsModuleInstalled( SvtModuleOptions::EModule::WRITER ) )
292             continue;
293 
294         const char* pId = nullptr;
295 
296         switch (i)
297         {
298             case SvtPathOptions::PATH_AUTOCORRECT:
299                 pId = RID_SVXSTR_KEY_AUTOCORRECT_DIR;
300                 break;
301             case SvtPathOptions::PATH_AUTOTEXT:
302                 pId = RID_SVXSTR_KEY_GLOSSARY_PATH;
303                 break;
304             case SvtPathOptions::PATH_BACKUP:
305                 pId = RID_SVXSTR_KEY_BACKUP_PATH;
306                 break;
307             case SvtPathOptions::PATH_GALLERY:
308                 pId = RID_SVXSTR_KEY_GALLERY_DIR;
309                 break;
310             case SvtPathOptions::PATH_GRAPHIC:
311                 pId = RID_SVXSTR_KEY_GRAPHICS_PATH;
312                 break;
313             case SvtPathOptions::PATH_TEMP:
314                 pId = RID_SVXSTR_KEY_TEMP_PATH;
315                 break;
316             case SvtPathOptions::PATH_TEMPLATE:
317                 pId = RID_SVXSTR_KEY_TEMPLATE_PATH;
318                 break;
319             case SvtPathOptions::PATH_DICTIONARY:
320                 pId = RID_SVXSTR_KEY_DICTIONARY_PATH;
321                 break;
322             case SvtPathOptions::PATH_CLASSIFICATION:
323                 pId = RID_SVXSTR_KEY_CLASSIFICATION_PATH;
324                 break;
325 #if OSL_DEBUG_LEVEL > 1
326             case SvtPathOptions::PATH_LINGUISTIC:
327                 pId = RID_SVXSTR_KEY_LINGUISTIC_DIR;
328                 break;
329 #endif
330             case SvtPathOptions::PATH_WORK:
331                 pId = RID_SVXSTR_KEY_WORK_PATH;
332                 break;
333         }
334 
335         if (pId)
336         {
337             OUString aStr(CuiResId(pId));
338 
339             nWidth1 = std::max(nWidth1, pPathBox->GetTextWidth(aStr));
340             aStr += "\t";
341             OUString sInternal, sUser, sWritable;
342             bool bReadOnly = false;
343             GetPathList( i, sInternal, sUser, sWritable, bReadOnly );
344             OUString sTmpPath = sUser;
345             if ( !sTmpPath.isEmpty() && !sWritable.isEmpty() )
346                 sTmpPath += OUStringLiteral1(MULTIPATH_DELIMITER);
347             sTmpPath += sWritable;
348             const OUString aValue = Convert_Impl( sTmpPath );
349             nWidth2 = std::max(nWidth2, pPathBox->GetTextWidth(aValue));
350             aStr += aValue;
351             SvTreeListEntry* pEntry = pPathBox->InsertEntry( aStr );
352             if ( bReadOnly )
353             {
354                 pPathBox->SetCollapsedEntryBmp( pEntry, pImpl->m_aLockImage );
355             }
356             PathUserData_Impl* pPathImpl = new PathUserData_Impl(i);
357             pPathImpl->sUserPath = sUser;
358             pPathImpl->sWritablePath = sWritable;
359             pEntry->SetUserData( pPathImpl );
360         }
361 
362     }
363 
364     long aTabs[] = {0, 0, 0};
365     aTabs[1] = nWidth1 + 12;
366     aTabs[2] = aTabs[1] + nWidth2 + 12;
367     pPathBox->SetTabs(SAL_N_ELEMENTS(aTabs), aTabs, MapUnit::MapPixel);
368 
369     PathSelect_Impl( nullptr );
370 }
371 
372 void SvxPathTabPage::FillUserData()
373 {
374     HeaderBar &rBar = pPathBox->GetTheHeaderBar();
375 
376     OUString aUserData = OUString::number( rBar.GetItemSize( ITEMID_TYPE ) ) + ";";
377     HeaderBarItemBits nBits = rBar.GetItemBits( ITEMID_TYPE );
378     bool bUp = ( ( nBits & HeaderBarItemBits::UPARROW ) == HeaderBarItemBits::UPARROW );
379     aUserData += bUp ? OUString("1") : OUString("0");
380     SetUserData( aUserData );
381 }
382 
383 IMPL_LINK_NOARG(SvxPathTabPage, PathSelect_Impl, SvTreeListBox*, void)
384 {
385     sal_uInt16 nSelCount = 0;
386     SvTreeListEntry* pEntry = pPathBox->FirstSelected();
387 
388     //the entry image indicates whether the path is write protected
389     Image aEntryImage;
390     if(pEntry)
391         aEntryImage = SvTreeListBox::GetCollapsedEntryBmp( pEntry );
392     bool bEnable = !aEntryImage;
393     while ( pEntry && ( nSelCount < 2 ) )
394     {
395         nSelCount++;
396         pEntry = pPathBox->NextSelected( pEntry );
397     }
398 
399     m_pPathBtn->Enable( 1 == nSelCount && bEnable);
400     m_pStandardBtn->Enable( nSelCount > 0 && bEnable);
401 }
402 
403 IMPL_LINK_NOARG(SvxPathTabPage, StandardHdl_Impl, Button*, void)
404 {
405     SvTreeListEntry* pEntry = pPathBox->FirstSelected();
406     while ( pEntry )
407     {
408         PathUserData_Impl* pPathImpl = static_cast<PathUserData_Impl*>(pEntry->GetUserData());
409         OUString aOldPath = pImpl->m_aDefOpt.GetDefaultPath( pPathImpl->nRealId );
410 
411         if ( !aOldPath.isEmpty() )
412         {
413             OUString sInternal, sUser, sWritable, sTemp;
414             bool bReadOnly = false;
415             GetPathList( pPathImpl->nRealId, sInternal, sUser, sWritable, bReadOnly );
416 
417             sal_Int32 nOldPos = 0;
418             do
419             {
420                 bool bFound = false;
421                 const OUString sOnePath = aOldPath.getToken( 0, MULTIPATH_DELIMITER, nOldPos );
422                 if ( !sInternal.isEmpty() )
423                 {
424                     sal_Int32 nInternalPos = 0;
425                     do
426                     {
427                         if ( sInternal.getToken( 0, MULTIPATH_DELIMITER, nInternalPos ) == sOnePath )
428                             bFound = true;
429                     }
430                     while ( !bFound && nInternalPos >= 0 );
431                 }
432                 if ( !bFound )
433                 {
434                     if ( !sTemp.isEmpty() )
435                         sTemp += OUStringLiteral1(MULTIPATH_DELIMITER);
436                     sTemp += sOnePath;
437                 }
438             }
439             while ( nOldPos >= 0 );
440 
441             OUString sUserPath, sWritablePath;
442             if ( !sTemp.isEmpty() )
443             {
444                 sal_Int32 nNextPos = 0;
445                 for (;;)
446                 {
447                     const OUString sToken = sTemp.getToken( 0, MULTIPATH_DELIMITER, nNextPos );
448                     if ( nNextPos<0 )
449                     {
450                         // Last token need a different handling
451                         sWritablePath = sToken;
452                         break;
453                     }
454                     if ( !sUserPath.isEmpty() )
455                         sUserPath += OUStringLiteral1(MULTIPATH_DELIMITER);
456                     sUserPath += sToken;
457                 }
458             }
459             pPathBox->SetEntryText( Convert_Impl( sTemp ), pEntry, 1 );
460             pPathImpl->eState = SfxItemState::SET;
461             pPathImpl->sUserPath = sUserPath;
462             pPathImpl->sWritablePath = sWritablePath;
463         }
464         pEntry = pPathBox->NextSelected( pEntry );
465     }
466 }
467 
468 
469 void SvxPathTabPage::ChangeCurrentEntry( const OUString& _rFolder )
470 {
471     SvTreeListEntry* pEntry = pPathBox->GetCurEntry();
472     if ( !pEntry )
473     {
474         SAL_WARN( "cui.options", "SvxPathTabPage::ChangeCurrentEntry(): no entry" );
475         return;
476     }
477 
478     OUString sInternal, sUser, sWritable;
479     PathUserData_Impl* pPathImpl = static_cast<PathUserData_Impl*>(pEntry->GetUserData());
480     bool bReadOnly = false;
481     GetPathList( pPathImpl->nRealId, sInternal, sUser, sWritable, bReadOnly );
482     sUser = pPathImpl->sUserPath;
483     sWritable = pPathImpl->sWritablePath;
484 
485     // old path is an URL?
486     INetURLObject aObj( sWritable );
487     bool bURL = ( aObj.GetProtocol() != INetProtocol::NotValid );
488     INetURLObject aNewObj( _rFolder );
489     aNewObj.removeFinalSlash();
490 
491     // then the new path also an URL else system path
492     OUString sNewPathStr = bURL ? _rFolder : aNewObj.getFSysPath( FSysStyle::Detect );
493 
494     bool bChanged =
495 #ifdef UNX
496 // Unix is case sensitive
497         ( sNewPathStr != sWritable );
498 #else
499         !sNewPathStr.equalsIgnoreAsciiCase( sWritable );
500 #endif
501 
502     if ( bChanged )
503     {
504         pPathBox->SetEntryText( Convert_Impl( sNewPathStr ), pEntry, 1 );
505         sal_uInt16 nPos = static_cast<sal_uInt16>(pPathBox->GetModel()->GetAbsPos( pEntry ));
506         pPathImpl = static_cast<PathUserData_Impl*>(pPathBox->GetEntry(nPos)->GetUserData());
507         pPathImpl->eState = SfxItemState::SET;
508         pPathImpl->sWritablePath = sNewPathStr;
509         if ( SvtPathOptions::PATH_WORK == pPathImpl->nRealId )
510         {
511             // Remove view options entry so the new work path
512             // will be used for the next open dialog.
513             SvtViewOptions aDlgOpt( EViewType::Dialog, IODLG_CONFIGNAME );
514             aDlgOpt.Delete();
515             // Reset also last used dir in the sfx application instance
516             SfxApplication *pSfxApp = SfxGetpApp();
517             pSfxApp->ResetLastDir();
518 
519             // Set configuration flag to notify file picker that it's necessary
520             // to take over the path provided.
521             std::shared_ptr< comphelper::ConfigurationChanges > batch(
522                 comphelper::ConfigurationChanges::create());
523             officecfg::Office::Common::Path::Info::WorkPathChanged::set(
524                 true, batch);
525             batch->commit();
526         }
527     }
528 }
529 
530 
531 IMPL_LINK_NOARG(SvxPathTabPage, DoubleClickPathHdl_Impl, SvTreeListBox*, bool)
532 {
533     PathHdl_Impl(nullptr);
534     return false;
535 }
536 
537 IMPL_LINK_NOARG(SvxPathTabPage, PathHdl_Impl, Button*, void)
538 {
539     SvTreeListEntry* pEntry = pPathBox->GetCurEntry();
540     sal_uInt16 nPos = ( pEntry != nullptr ) ? static_cast<PathUserData_Impl*>(pEntry->GetUserData())->nRealId : 0;
541     OUString sInternal, sUser, sWritable;
542     bool bPickFile = false;
543     if ( pEntry )
544     {
545         PathUserData_Impl* pPathImpl = static_cast<PathUserData_Impl*>(pEntry->GetUserData());
546         bool bReadOnly = false;
547         GetPathList( pPathImpl->nRealId, sInternal, sUser, sWritable, bReadOnly );
548         sUser = pPathImpl->sUserPath;
549         sWritable = pPathImpl->sWritablePath;
550         bPickFile = pPathImpl->nRealId == SvtPathOptions::PATH_CLASSIFICATION;
551     }
552 
553     if(pEntry && !(!SvTreeListBox::GetCollapsedEntryBmp(pEntry)))
554         return;
555 
556     if ( IsMultiPath_Impl( nPos ) )
557     {
558         SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create();
559         ScopedVclPtr<AbstractSvxMultiPathDialog> pMultiDlg(
560             pFact->CreateSvxMultiPathDialog( this ));
561 
562         OUString sPath( sUser );
563         if ( !sPath.isEmpty() )
564             sPath += OUStringLiteral1(MULTIPATH_DELIMITER);
565         sPath += sWritable;
566         pMultiDlg->SetPath( sPath );
567 
568         const OUString sPathName = SvTabListBox::GetEntryText( pEntry, 0 );
569         const OUString sNewTitle = pImpl->m_sMultiPathDlg.replaceFirst( VAR_ONE, sPathName );
570         pMultiDlg->SetTitle( sNewTitle );
571 
572         if ( pMultiDlg->Execute() == RET_OK && pEntry )
573         {
574             sUser.clear();
575             sWritable.clear();
576             OUString sFullPath;
577             OUString sNewPath = pMultiDlg->GetPath();
578             if ( !sNewPath.isEmpty() )
579             {
580                 sal_Int32 nNextPos = 0;
581                 for (;;)
582                 {
583                     const OUString sToken(sNewPath.getToken( 0, MULTIPATH_DELIMITER, nNextPos ));
584                     if ( nNextPos<0 )
585                     {
586                         // Last token need a different handling
587                         sWritable = sToken;
588                         break;
589                     }
590                     if ( !sUser.isEmpty() )
591                         sUser += OUStringLiteral1(MULTIPATH_DELIMITER);
592                     sUser += sToken;
593                 }
594                 sFullPath = sUser;
595                 if ( !sFullPath.isEmpty() )
596                     sFullPath += OUStringLiteral1(MULTIPATH_DELIMITER);
597                 sFullPath += sWritable;
598             }
599 
600             pPathBox->SetEntryText( Convert_Impl( sFullPath ), pEntry, 1 );
601             // save modified flag
602             PathUserData_Impl* pPathImpl = static_cast<PathUserData_Impl*>(pEntry->GetUserData());
603             pPathImpl->eState = SfxItemState::SET;
604             pPathImpl->sUserPath = sUser;
605             pPathImpl->sWritablePath = sWritable;
606         }
607     }
608     else if (pEntry && !bPickFile)
609     {
610         try
611         {
612             Reference < XComponentContext > xContext( ::comphelper::getProcessComponentContext() );
613             xFolderPicker = FolderPicker::create(xContext);
614 
615             INetURLObject aURL( sWritable, INetProtocol::File );
616             xFolderPicker->setDisplayDirectory( aURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ) );
617 
618             Reference< XAsynchronousExecutableDialog > xAsyncDlg( xFolderPicker, UNO_QUERY );
619             if ( xAsyncDlg.is() )
620                 xAsyncDlg->startExecuteModal( xDialogListener.get() );
621             else
622             {
623                 short nRet = xFolderPicker->execute();
624                 if (ExecutableDialogResults::OK != nRet)
625                     return;
626 
627                 OUString sFolder(xFolderPicker->getDirectory());
628                 ChangeCurrentEntry(sFolder);
629             }
630         }
631         catch( Exception& )
632         {
633             SAL_WARN( "cui.options", "SvxPathTabPage::PathHdl_Impl: exception from folder picker" );
634         }
635     }
636     else if (pEntry)
637     {
638         try
639         {
640             uno::Reference<uno::XComponentContext> xComponentContext(comphelper::getProcessComponentContext());
641             uno::Reference<ui::dialogs::XFilePicker3> xFilePicker = ui::dialogs::FilePicker::createWithMode(xComponentContext, ui::dialogs::TemplateDescription::FILEOPEN_SIMPLE);
642             xFilePicker->appendFilter(OUString(), "*.xml");
643             if (xFilePicker->execute() == ui::dialogs::ExecutableDialogResults::OK)
644             {
645                 uno::Sequence<OUString> aPathSeq(xFilePicker->getSelectedFiles());
646                 ChangeCurrentEntry(aPathSeq[0]);
647             }
648         }
649         catch (const uno::Exception&)
650         {
651             DBG_UNHANDLED_EXCEPTION("cui.options", "exception from file picker");
652         }
653     }
654 }
655 
656 
657 IMPL_LINK( SvxPathTabPage, HeaderSelect_Impl, HeaderBar*, pBar, void )
658 {
659     if (!pBar || pBar->GetCurItemId() != ITEMID_TYPE)
660         return;
661 
662     HeaderBarItemBits nBits = pBar->GetItemBits(ITEMID_TYPE);
663     bool bUp = ( ( nBits & HeaderBarItemBits::UPARROW ) == HeaderBarItemBits::UPARROW );
664     SvSortMode eMode = SortAscending;
665 
666     if ( bUp )
667     {
668         nBits &= ~HeaderBarItemBits::UPARROW;
669         nBits |= HeaderBarItemBits::DOWNARROW;
670         eMode = SortDescending;
671     }
672     else
673     {
674         nBits &= ~HeaderBarItemBits::DOWNARROW;
675         nBits |= HeaderBarItemBits::UPARROW;
676     }
677     pBar->SetItemBits( ITEMID_TYPE, nBits );
678     SvTreeList* pModel = pPathBox->GetModel();
679     pModel->SetSortMode( eMode );
680     pModel->Resort();
681 }
682 
683 
684 IMPL_LINK( SvxPathTabPage, HeaderEndDrag_Impl, HeaderBar*, pBar, void )
685 {
686     if (!pBar || !pBar->GetCurItemId())
687         return;
688 
689     if ( !pBar->IsItemMode() )
690     {
691         Size aSz;
692         sal_uInt16 nTabs = pBar->GetItemCount();
693         long nTmpSz = 0;
694         long nWidth = pBar->GetItemSize(ITEMID_TYPE);
695         long nBarWidth = pBar->GetSizePixel().Width();
696 
697         if(nWidth < TAB_WIDTH_MIN)
698             pBar->SetItemSize( ITEMID_TYPE, TAB_WIDTH_MIN);
699         else if ( ( nBarWidth - nWidth ) < TAB_WIDTH_MIN )
700             pBar->SetItemSize( ITEMID_TYPE, nBarWidth - TAB_WIDTH_MIN );
701 
702         for ( sal_uInt16 i = 1; i <= nTabs; ++i )
703         {
704             long _nWidth = pBar->GetItemSize(i);
705             aSz.setWidth(  _nWidth + nTmpSz );
706             nTmpSz += _nWidth;
707             pPathBox->SetTab( i, PixelToLogic( aSz, MapMode(MapUnit::MapAppFont) ).Width() );
708         }
709     }
710 }
711 
712 IMPL_LINK( SvxPathTabPage, DialogClosedHdl, DialogClosedEvent*, pEvt, void )
713 {
714     if (RET_OK == pEvt->DialogResult)
715     {
716         assert(xFolderPicker.is() && "SvxPathTabPage::DialogClosedHdl(): no folder picker");
717         OUString sURL = xFolderPicker->getDirectory();
718         ChangeCurrentEntry( sURL );
719     }
720 }
721 
722 void SvxPathTabPage::GetPathList(
723     sal_uInt16 _nPathHandle, OUString& _rInternalPath,
724     OUString& _rUserPath, OUString& _rWritablePath, bool& _rReadOnly )
725 {
726     OUString sCfgName = getCfgName_Impl( _nPathHandle );
727 
728     try
729     {
730         // load PathSettings service if necessary
731         if ( !pImpl->m_xPathSettings.is() )
732         {
733             Reference< XComponentContext > xContext = comphelper::getProcessComponentContext();
734             pImpl->m_xPathSettings = css::util::thePathSettings::get( xContext );
735         }
736 
737         // load internal paths
738         Any aAny = pImpl->m_xPathSettings->getPropertyValue(
739             sCfgName + POSTFIX_INTERNAL);
740         Sequence< OUString > aPathSeq;
741         if ( aAny >>= aPathSeq )
742         {
743             long i, nCount = aPathSeq.getLength();
744             const OUString* pPaths = aPathSeq.getConstArray();
745 
746             for ( i = 0; i < nCount; ++i )
747             {
748                 if ( !_rInternalPath.isEmpty() )
749                     _rInternalPath += ";";
750                 _rInternalPath += pPaths[i];
751             }
752         }
753         // load user paths
754         aAny = pImpl->m_xPathSettings->getPropertyValue(
755             sCfgName + POSTFIX_USER);
756         if ( aAny >>= aPathSeq )
757         {
758             long i, nCount = aPathSeq.getLength();
759             const OUString* pPaths = aPathSeq.getConstArray();
760 
761             for ( i = 0; i < nCount; ++i )
762             {
763                 if ( !_rUserPath.isEmpty() )
764                     _rUserPath += ";";
765                 _rUserPath += pPaths[i];
766             }
767         }
768         // then the writable path
769         aAny = pImpl->m_xPathSettings->getPropertyValue(
770             sCfgName + POSTFIX_WRITABLE);
771         OUString sWritablePath;
772         if ( aAny >>= sWritablePath )
773             _rWritablePath = sWritablePath;
774 
775         // and the readonly flag
776         Reference< XPropertySetInfo > xInfo = pImpl->m_xPathSettings->getPropertySetInfo();
777         Property aProp = xInfo->getPropertyByName(sCfgName);
778         _rReadOnly = ( ( aProp.Attributes & PropertyAttribute::READONLY ) == PropertyAttribute::READONLY );
779     }
780     catch( const Exception& )
781     {
782         OSL_FAIL( "SvxPathTabPage::GetPathList(): caught an exception!" );
783     }
784 }
785 
786 
787 void SvxPathTabPage::SetPathList(
788     sal_uInt16 _nPathHandle, const OUString& _rUserPath, const OUString& _rWritablePath )
789 {
790     OUString sCfgName = getCfgName_Impl( _nPathHandle );
791 
792     try
793     {
794         // load PathSettings service if necessary
795         if ( !pImpl->m_xPathSettings.is() )
796         {
797             Reference< XComponentContext > xContext = comphelper::getProcessComponentContext();
798             pImpl->m_xPathSettings = css::util::thePathSettings::get( xContext );
799         }
800 
801         // save user paths
802         const sal_Int32 nCount = comphelper::string::getTokenCount(_rUserPath, MULTIPATH_DELIMITER);
803         Sequence< OUString > aPathSeq( nCount );
804         OUString* pArray = aPathSeq.getArray();
805         sal_Int32 nPos = 0;
806         for ( sal_Int32 i = 0; i < nCount; ++i )
807             pArray[i] = _rUserPath.getToken( 0, MULTIPATH_DELIMITER, nPos );
808         Any aValue( aPathSeq );
809         pImpl->m_xPathSettings->setPropertyValue(
810             sCfgName + POSTFIX_USER, aValue);
811 
812         // then the writable path
813         aValue <<= _rWritablePath;
814         pImpl->m_xPathSettings->setPropertyValue(
815             sCfgName + POSTFIX_WRITABLE, aValue);
816     }
817     catch( const Exception& e )
818     {
819         SAL_WARN("cui.options", "caught: " << e);
820     }
821 }
822 
823 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
824