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 <sal/config.h>
21
22 #include <string_view>
23
24 #include "wizardshell.hxx"
25
26 #include <com/sun/star/container/NoSuchElementException.hpp>
27 #include <com/sun/star/beans/XPropertySetInfo.hpp>
28 #include <com/sun/star/uno/XComponentContext.hpp>
29 #include <com/sun/star/ucb/AlreadyInitializedException.hpp>
30 #include <com/sun/star/ui/dialogs/XWizard.hpp>
31 #include <com/sun/star/ui/dialogs/XWizardController.hpp>
32 #include <com/sun/star/ui/dialogs/WizardButton.hpp>
33 #include <com/sun/star/util/InvalidStateException.hpp>
34
35 #include <comphelper/proparrhlp.hxx>
36 #include <cppuhelper/implbase.hxx>
37 #include <svtools/genericunodialog.hxx>
38 #include <toolkit/helper/vclunohelper.hxx>
39 #include <tools/diagnose_ex.h>
40 #include <osl/mutex.hxx>
41 #include <vcl/svapp.hxx>
42 #include <tools/urlobj.hxx>
43
44 using namespace ::com::sun::star;
45 using namespace ::svt::uno;
46
47 namespace {
48
49 using css::uno::Reference;
50 using css::uno::XInterface;
51 using css::uno::UNO_QUERY;
52 using css::uno::Any;
53 using css::uno::Sequence;
54 using css::ui::dialogs::XWizard;
55 using css::beans::XPropertySetInfo;
56 using css::uno::XComponentContext;
57 using css::beans::Property;
58 using css::lang::IllegalArgumentException;
59 using css::ucb::AlreadyInitializedException;
60 using css::ui::dialogs::XWizardController;
61 using css::ui::dialogs::XWizardPage;
62 using css::container::NoSuchElementException;
63 using css::util::InvalidStateException;
64 using css::awt::XWindow;
65
66 namespace WizardButton = css::ui::dialogs::WizardButton;
67
lcl_convertWizardButtonToWZB(const sal_Int16 i_nWizardButton)68 WizardButtonFlags lcl_convertWizardButtonToWZB( const sal_Int16 i_nWizardButton )
69 {
70 switch ( i_nWizardButton )
71 {
72 case WizardButton::NONE: return WizardButtonFlags::NONE;
73 case WizardButton::NEXT: return WizardButtonFlags::NEXT;
74 case WizardButton::PREVIOUS: return WizardButtonFlags::PREVIOUS;
75 case WizardButton::FINISH: return WizardButtonFlags::FINISH;
76 case WizardButton::CANCEL: return WizardButtonFlags::CANCEL;
77 case WizardButton::HELP: return WizardButtonFlags::HELP;
78 }
79 OSL_FAIL( "lcl_convertWizardButtonToWZB: invalid WizardButton constant!" );
80 return WizardButtonFlags::NONE;
81 }
82
83 typedef ::cppu::ImplInheritanceHelper < ::svt::OGenericUnoDialog
84 , ui::dialogs::XWizard
85 > Wizard_Base;
86 class Wizard;
87 typedef ::comphelper::OPropertyArrayUsageHelper< Wizard > Wizard_PBase;
88 class Wizard : public Wizard_Base
89 , public Wizard_PBase
90 {
91 public:
92 explicit Wizard( const css::uno::Reference< css::uno::XComponentContext >& i_rContext );
93
94 // lang::XServiceInfo
95 virtual OUString SAL_CALL getImplementationName() override;
96 virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override;
97
98 // beans::XPropertySet
99 virtual css::uno::Reference< beans::XPropertySetInfo > SAL_CALL getPropertySetInfo() override;
100 virtual ::cppu::IPropertyArrayHelper& SAL_CALL getInfoHelper() override;
101
102 // OPropertyArrayUsageHelper
103 virtual ::cppu::IPropertyArrayHelper* createArrayHelper( ) const override;
104
105 // ui::dialogs::XWizard
106 virtual OUString SAL_CALL getHelpURL() override;
107 virtual void SAL_CALL setHelpURL( const OUString& _helpurl ) override;
108 virtual css::uno::Reference< awt::XWindow > SAL_CALL getDialogWindow() override;
109 virtual css::uno::Reference< ui::dialogs::XWizardPage > SAL_CALL getCurrentPage( ) override;
110 virtual void SAL_CALL enableButton( ::sal_Int16 WizardButton, sal_Bool Enable ) override;
111 virtual void SAL_CALL setDefaultButton( ::sal_Int16 WizardButton ) override;
112 virtual sal_Bool SAL_CALL travelNext( ) override;
113 virtual sal_Bool SAL_CALL travelPrevious( ) override;
114 virtual void SAL_CALL enablePage( ::sal_Int16 PageID, sal_Bool Enable ) override;
115 virtual void SAL_CALL updateTravelUI( ) override;
116 virtual sal_Bool SAL_CALL advanceTo( ::sal_Int16 PageId ) override;
117 virtual sal_Bool SAL_CALL goBackTo( ::sal_Int16 PageId ) override;
118 virtual void SAL_CALL activatePath( ::sal_Int16 PathIndex, sal_Bool Final ) override;
119
120 // ui::dialogs::XExecutableDialog
121 virtual void SAL_CALL setTitle( const OUString& aTitle ) override;
122 virtual ::sal_Int16 SAL_CALL execute( ) override;
123
124 // lang::XInitialization
125 virtual void SAL_CALL initialize( const css::uno::Sequence< css::uno::Any >& aArguments ) override;
126
127 protected:
128 virtual ~Wizard() override;
129
130 protected:
131 virtual std::unique_ptr<weld::DialogController> createDialog(const css::uno::Reference<css::awt::XWindow>& rParent) override;
132
133 private:
134 css::uno::Sequence< css::uno::Sequence< sal_Int16 > > m_aWizardSteps;
135 css::uno::Reference< ui::dialogs::XWizardController > m_xController;
136 OUString m_sHelpURL;
137 };
138
Wizard(const Reference<XComponentContext> & _rxContext)139 Wizard::Wizard( const Reference< XComponentContext >& _rxContext )
140 :Wizard_Base( _rxContext )
141 {
142 }
143
lcl_getHelpURL(std::string_view sHelpId)144 OUString lcl_getHelpURL( std::string_view sHelpId )
145 {
146 OUStringBuffer aBuffer;
147 OUString aTmp(
148 OStringToOUString( sHelpId, RTL_TEXTENCODING_UTF8 ) );
149 INetURLObject aHID( aTmp );
150 if ( aHID.GetProtocol() == INetProtocol::NotValid )
151 aBuffer.append( INET_HID_SCHEME );
152 aBuffer.append( aTmp );
153 return aBuffer.makeStringAndClear();
154 }
155
~Wizard()156 Wizard::~Wizard()
157 {
158 if (m_xDialog)
159 {
160 ::osl::MutexGuard aGuard( m_aMutex );
161 if (m_xDialog)
162 {
163 m_sHelpURL = lcl_getHelpURL(m_xDialog->get_help_id());
164 destroyDialog();
165 }
166 }
167 }
168
lcl_checkPaths(const Sequence<Sequence<sal_Int16>> & i_rPaths,const Reference<XInterface> & i_rContext)169 void lcl_checkPaths( const Sequence< Sequence< sal_Int16 > >& i_rPaths, const Reference< XInterface >& i_rContext )
170 {
171 // need at least one path
172 if ( !i_rPaths.hasElements() )
173 throw IllegalArgumentException( OUString(), i_rContext, 2 );
174
175 // each path must be of length 1, at least
176 sal_Int32 i = 0;
177 for ( const Sequence< sal_Int16 >& rPath : i_rPaths )
178 {
179 if ( !rPath.hasElements() )
180 throw IllegalArgumentException( OUString(), i_rContext, 2 );
181
182 // page IDs must be in ascending order
183 auto pPageId = std::adjacent_find(rPath.begin(), rPath.end(), std::greater_equal<sal_Int16>());
184 if (pPageId != rPath.end())
185 {
186 throw IllegalArgumentException(
187 "Path " + OUString::number(i)
188 + ": invalid page ID sequence - each page ID must be greater than the previous one.",
189 i_rContext, 2 );
190 }
191 ++i;
192 }
193
194 // if we have one path, that's okay
195 if ( i_rPaths.getLength() == 1 )
196 return;
197
198 // if we have multiple paths, they must start with the same page id
199 const sal_Int16 nFirstPageId = i_rPaths[0][0];
200 if (std::any_of(i_rPaths.begin(), i_rPaths.end(),
201 [nFirstPageId](const Sequence< sal_Int16 >& rPath) { return rPath[0] != nFirstPageId; }))
202 throw IllegalArgumentException(
203 "All paths must start with the same page id.",
204 i_rContext, 2 );
205 }
206
initialize(const Sequence<Any> & i_Arguments)207 void SAL_CALL Wizard::initialize( const Sequence< Any >& i_Arguments )
208 {
209 ::osl::MutexGuard aGuard( m_aMutex );
210 if ( m_bInitialized )
211 throw AlreadyInitializedException( OUString(), *this );
212
213 if ( i_Arguments.getLength() != 2 )
214 throw IllegalArgumentException( OUString(), *this, -1 );
215
216 // the second argument must be a XWizardController, for each constructor
217 m_xController.set( i_Arguments[1], UNO_QUERY );
218 if ( !m_xController.is() )
219 throw IllegalArgumentException( OUString(), *this, 2 );
220
221 // the first arg is either a single path (short[]), or multiple paths (short[][])
222 Sequence< sal_Int16 > aSinglePath;
223 i_Arguments[0] >>= aSinglePath;
224 Sequence< Sequence< sal_Int16 > > aMultiplePaths;
225 i_Arguments[0] >>= aMultiplePaths;
226
227 if ( !aMultiplePaths.hasElements() )
228 {
229 aMultiplePaths.realloc(1);
230 aMultiplePaths[0] = aSinglePath;
231 }
232 lcl_checkPaths( aMultiplePaths, *this );
233 // if we survived this, the paths are valid, and we're done here ...
234 m_aWizardSteps = aMultiplePaths;
235
236 m_bInitialized = true;
237 }
238
lcl_getHelpId(const OUString & _rHelpURL)239 OString lcl_getHelpId( const OUString& _rHelpURL )
240 {
241 INetURLObject aHID( _rHelpURL );
242 if ( aHID.GetProtocol() == INetProtocol::Hid )
243 return OUStringToOString( aHID.GetURLPath(), RTL_TEXTENCODING_UTF8 );
244 else
245 return OUStringToOString( _rHelpURL, RTL_TEXTENCODING_UTF8 );
246 }
247
createDialog(const css::uno::Reference<css::awt::XWindow> & rParent)248 std::unique_ptr<weld::DialogController> Wizard::createDialog(const css::uno::Reference<css::awt::XWindow>& rParent)
249 {
250 auto xDialog = std::make_unique<WizardShell>(Application::GetFrameWeld(rParent), m_xController, m_aWizardSteps);
251 xDialog->set_help_id(lcl_getHelpId(m_sHelpURL));
252 xDialog->setTitleBase( m_sTitle );
253 return xDialog;
254 }
255
getImplementationName()256 OUString SAL_CALL Wizard::getImplementationName()
257 {
258 return "com.sun.star.comp.svtools.uno.Wizard";
259 }
260
getSupportedServiceNames()261 Sequence< OUString > SAL_CALL Wizard::getSupportedServiceNames()
262 {
263 return { "com.sun.star.ui.dialogs.Wizard" };
264 }
265
getPropertySetInfo()266 Reference< XPropertySetInfo > SAL_CALL Wizard::getPropertySetInfo()
267 {
268 return createPropertySetInfo( getInfoHelper() );
269 }
270
getInfoHelper()271 ::cppu::IPropertyArrayHelper& SAL_CALL Wizard::getInfoHelper()
272 {
273 return *getArrayHelper();
274 }
275
createArrayHelper() const276 ::cppu::IPropertyArrayHelper* Wizard::createArrayHelper( ) const
277 {
278 Sequence< Property > aProps;
279 describeProperties( aProps );
280 return new ::cppu::OPropertyArrayHelper( aProps );
281 }
282
getHelpURL()283 OUString SAL_CALL Wizard::getHelpURL()
284 {
285 SolarMutexGuard aSolarGuard;
286 ::osl::MutexGuard aGuard( m_aMutex );
287
288 if (!m_xDialog)
289 return m_sHelpURL;
290
291 return lcl_getHelpURL(m_xDialog->get_help_id());
292 }
293
setHelpURL(const OUString & i_HelpURL)294 void SAL_CALL Wizard::setHelpURL( const OUString& i_HelpURL )
295 {
296 SolarMutexGuard aSolarGuard;
297 ::osl::MutexGuard aGuard( m_aMutex );
298
299 if (!m_xDialog)
300 m_sHelpURL = i_HelpURL;
301 else
302 m_xDialog->set_help_id(lcl_getHelpId(i_HelpURL));
303 }
304
getDialogWindow()305 Reference< XWindow > SAL_CALL Wizard::getDialogWindow()
306 {
307 SolarMutexGuard aSolarGuard;
308 ::osl::MutexGuard aGuard( m_aMutex );
309
310 ENSURE_OR_RETURN( m_xDialog, "Wizard::getDialogWindow: illegal call (execution did not start, yet)!", nullptr );
311 return m_xDialog->getDialog()->GetXWindow();
312 }
313
enableButton(::sal_Int16 i_WizardButton,sal_Bool i_Enable)314 void SAL_CALL Wizard::enableButton( ::sal_Int16 i_WizardButton, sal_Bool i_Enable )
315 {
316 SolarMutexGuard aSolarGuard;
317 ::osl::MutexGuard aGuard( m_aMutex );
318
319 WizardShell* pWizardImpl = dynamic_cast<WizardShell*>(m_xDialog.get());
320 ENSURE_OR_RETURN_VOID( pWizardImpl, "Wizard::enableButtons: invalid dialog implementation!" );
321
322 pWizardImpl->enableButtons( lcl_convertWizardButtonToWZB( i_WizardButton ), i_Enable );
323 }
324
setDefaultButton(::sal_Int16 i_WizardButton)325 void SAL_CALL Wizard::setDefaultButton( ::sal_Int16 i_WizardButton )
326 {
327 SolarMutexGuard aSolarGuard;
328 ::osl::MutexGuard aGuard( m_aMutex );
329
330 WizardShell* pWizardImpl = dynamic_cast<WizardShell*>(m_xDialog.get());
331 ENSURE_OR_RETURN_VOID( pWizardImpl, "Wizard::setDefaultButton: invalid dialog implementation!" );
332
333 pWizardImpl->defaultButton( lcl_convertWizardButtonToWZB( i_WizardButton ) );
334 }
335
travelNext()336 sal_Bool SAL_CALL Wizard::travelNext( )
337 {
338 SolarMutexGuard aSolarGuard;
339 ::osl::MutexGuard aGuard( m_aMutex );
340
341 WizardShell* pWizardImpl = dynamic_cast<WizardShell*>(m_xDialog.get());
342 ENSURE_OR_RETURN_FALSE( pWizardImpl, "Wizard::travelNext: invalid dialog implementation!" );
343
344 return pWizardImpl->travelNext();
345 }
346
travelPrevious()347 sal_Bool SAL_CALL Wizard::travelPrevious( )
348 {
349 SolarMutexGuard aSolarGuard;
350 ::osl::MutexGuard aGuard( m_aMutex );
351
352 WizardShell* pWizardImpl = dynamic_cast<WizardShell*>(m_xDialog.get());
353 ENSURE_OR_RETURN_FALSE( pWizardImpl, "Wizard::travelPrevious: invalid dialog implementation!" );
354
355 return pWizardImpl->travelPrevious();
356 }
357
enablePage(::sal_Int16 i_PageID,sal_Bool i_Enable)358 void SAL_CALL Wizard::enablePage( ::sal_Int16 i_PageID, sal_Bool i_Enable )
359 {
360 SolarMutexGuard aSolarGuard;
361 ::osl::MutexGuard aGuard( m_aMutex );
362
363 WizardShell* pWizardImpl = dynamic_cast<WizardShell*>(m_xDialog.get());
364 ENSURE_OR_RETURN_VOID( pWizardImpl, "Wizard::enablePage: invalid dialog implementation!" );
365
366 if ( !pWizardImpl->knowsPage( i_PageID ) )
367 throw NoSuchElementException( OUString(), *this );
368
369 if ( i_PageID == pWizardImpl->getCurrentPage() )
370 throw InvalidStateException( OUString(), *this );
371
372 pWizardImpl->enablePage( i_PageID, i_Enable );
373 }
374
updateTravelUI()375 void SAL_CALL Wizard::updateTravelUI( )
376 {
377 SolarMutexGuard aSolarGuard;
378 ::osl::MutexGuard aGuard( m_aMutex );
379
380 WizardShell* pWizardImpl = dynamic_cast<WizardShell*>(m_xDialog.get());
381 ENSURE_OR_RETURN_VOID( pWizardImpl, "Wizard::updateTravelUI: invalid dialog implementation!" );
382
383 pWizardImpl->updateTravelUI();
384 }
385
advanceTo(::sal_Int16 i_PageId)386 sal_Bool SAL_CALL Wizard::advanceTo( ::sal_Int16 i_PageId )
387 {
388 SolarMutexGuard aSolarGuard;
389 ::osl::MutexGuard aGuard( m_aMutex );
390
391 WizardShell* pWizardImpl = dynamic_cast<WizardShell*>(m_xDialog.get());
392 ENSURE_OR_RETURN_FALSE( pWizardImpl, "Wizard::advanceTo: invalid dialog implementation!" );
393
394 return pWizardImpl->advanceTo( i_PageId );
395 }
396
goBackTo(::sal_Int16 i_PageId)397 sal_Bool SAL_CALL Wizard::goBackTo( ::sal_Int16 i_PageId )
398 {
399 SolarMutexGuard aSolarGuard;
400 ::osl::MutexGuard aGuard( m_aMutex );
401
402 WizardShell* pWizardImpl = dynamic_cast<WizardShell*>(m_xDialog.get());
403 ENSURE_OR_RETURN_FALSE( pWizardImpl, "Wizard::goBackTo: invalid dialog implementation!" );
404
405 return pWizardImpl->goBackTo( i_PageId );
406 }
407
getCurrentPage()408 Reference< XWizardPage > SAL_CALL Wizard::getCurrentPage( )
409 {
410 SolarMutexGuard aSolarGuard;
411 ::osl::MutexGuard aGuard( m_aMutex );
412
413 WizardShell* pWizardImpl = dynamic_cast<WizardShell*>(m_xDialog.get());
414 ENSURE_OR_RETURN( pWizardImpl, "Wizard::getCurrentPage: invalid dialog implementation!", Reference< XWizardPage >() );
415
416 return pWizardImpl->getCurrentWizardPage();
417 }
418
activatePath(::sal_Int16 i_PathIndex,sal_Bool i_Final)419 void SAL_CALL Wizard::activatePath( ::sal_Int16 i_PathIndex, sal_Bool i_Final )
420 {
421 SolarMutexGuard aSolarGuard;
422 ::osl::MutexGuard aGuard( m_aMutex );
423
424 if ( ( i_PathIndex < 0 ) || ( i_PathIndex >= m_aWizardSteps.getLength() ) )
425 throw NoSuchElementException( OUString(), *this );
426
427 WizardShell* pWizardImpl = dynamic_cast<WizardShell*>(m_xDialog.get());
428 ENSURE_OR_RETURN_VOID( pWizardImpl, "Wizard::activatePath: invalid dialog implementation!" );
429
430 pWizardImpl->activatePath( i_PathIndex, i_Final );
431 }
432
setTitle(const OUString & i_Title)433 void SAL_CALL Wizard::setTitle( const OUString& i_Title )
434 {
435 // simply disambiguate
436 Wizard_Base::OGenericUnoDialog::setTitle( i_Title );
437 }
438
execute()439 ::sal_Int16 SAL_CALL Wizard::execute( )
440 {
441 return Wizard_Base::OGenericUnoDialog::execute();
442 }
443 }
444
445 extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
com_sun_star_comp_svtools_uno_Wizard_get_implementation(css::uno::XComponentContext * context,css::uno::Sequence<css::uno::Any> const &)446 com_sun_star_comp_svtools_uno_Wizard_get_implementation(
447 css::uno::XComponentContext *context,
448 css::uno::Sequence<css::uno::Any> const &)
449 {
450 return cppu::acquire(new Wizard(context));
451 }
452
453 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
454