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
21 #include <sal/macros.h>
22
23 #include <fmprop.hxx>
24 #include <fmPropBrw.hxx>
25 #include <svx/strings.hrc>
26 #include <fmservs.hxx>
27 #include <fmshimp.hxx>
28 #include <fmpgeimp.hxx>
29
30 #include <svx/dialmgr.hxx>
31 #include <svx/fmpage.hxx>
32 #include <svx/fmshell.hxx>
33 #include <svx/fmview.hxx>
34 #include <svx/sdrpagewindow.hxx>
35 #include <svx/svdpagv.hxx>
36 #include <svx/svxids.hrc>
37
38 #include <com/sun/star/awt/XControlContainer.hpp>
39 #include <com/sun/star/beans/XPropertySet.hpp>
40 #include <com/sun/star/form/XForm.hpp>
41 #include <com/sun/star/form/FormComponentType.hpp>
42 #include <com/sun/star/form/inspection/DefaultFormComponentInspectorModel.hpp>
43 #include <com/sun/star/frame/Frame.hpp>
44 #include <com/sun/star/inspection/ObjectInspector.hpp>
45 #include <com/sun/star/inspection/XObjectInspectorUI.hpp>
46 #include <com/sun/star/inspection/DefaultHelpProvider.hpp>
47 #include <com/sun/star/lang/XServiceInfo.hpp>
48 #include <com/sun/star/util/VetoException.hpp>
49
50 #include <comphelper/processfactory.hxx>
51 #include <comphelper/property.hxx>
52 #include <comphelper/sequence.hxx>
53 #include <comphelper/types.hxx>
54 #include <cppuhelper/component_context.hxx>
55 #include <o3tl/deleter.hxx>
56 #include <sfx2/bindings.hxx>
57 #include <sfx2/childwin.hxx>
58 #include <sfx2/objitem.hxx>
59 #include <sfx2/objsh.hxx>
60 #include <comphelper/diagnose_ex.hxx>
61 #include <unotools/confignode.hxx>
62 #include <vcl/stdtext.hxx>
63 #include <vcl/svapp.hxx>
64 #include <vcl/weld.hxx>
65 #include <vcl/weldutils.hxx>
66
67 using namespace ::com::sun::star;
68 using namespace ::com::sun::star::beans;
69 using namespace ::com::sun::star::container;
70 using namespace ::com::sun::star::form;
71 using namespace ::com::sun::star::form::inspection;
72 using namespace ::com::sun::star::frame;
73 using namespace ::com::sun::star::inspection;
74 using namespace ::com::sun::star::lang;
75 using namespace ::com::sun::star::uno;
76 using namespace ::com::sun::star::util;
77 using namespace ::svxform;
78 using ::com::sun::star::awt::XWindow;
79
80 //= FmPropBrwMgr
SFX_IMPL_MODELESSDIALOGCONTOLLER(FmPropBrwMgr,SID_FM_SHOW_PROPERTIES)81 SFX_IMPL_MODELESSDIALOGCONTOLLER(FmPropBrwMgr, SID_FM_SHOW_PROPERTIES)
82
83 FmPropBrwMgr::FmPropBrwMgr( vcl::Window* _pParent, sal_uInt16 _nId,
84 SfxBindings* _pBindings, const SfxChildWinInfo* _pInfo)
85 :SfxChildWindow(_pParent, _nId)
86 {
87 std::shared_ptr<FmPropBrw> xControl(new FmPropBrw(::comphelper::getProcessComponentContext(), _pBindings,
88 this, _pParent->GetFrameWeld(), _pInfo), o3tl::default_delete<FmPropBrw>());
89 SetController(std::move(xControl));
90 static_cast<FmPropBrw*>(GetController().get())->Initialize( _pInfo );
91 }
92
GetUIHeadlineName(sal_Int16 nClassId,const Any & aUnoObj)93 static OUString GetUIHeadlineName(sal_Int16 nClassId, const Any& aUnoObj)
94 {
95 TranslateId pClassNameResourceId;
96
97 switch ( nClassId )
98 {
99 case FormComponentType::TEXTFIELD:
100 {
101 Reference< XInterface > xIFace;
102 aUnoObj >>= xIFace;
103 pClassNameResourceId = RID_STR_PROPTITLE_EDIT;
104 if (xIFace.is())
105 { // we have a chance to check if it's a formatted field model
106 Reference< XServiceInfo > xInfo(xIFace, UNO_QUERY);
107 if (xInfo.is() && (xInfo->supportsService(FM_SUN_COMPONENT_FORMATTEDFIELD)))
108 pClassNameResourceId = RID_STR_PROPTITLE_FORMATTED;
109 else if (!xInfo.is())
110 {
111 // couldn't distinguish between formatted and edit with the service name, so try with the properties
112 Reference< XPropertySet > xProps(xIFace, UNO_QUERY);
113 if (xProps.is())
114 {
115 Reference< XPropertySetInfo > xPropsInfo = xProps->getPropertySetInfo();
116 if (xPropsInfo.is() && xPropsInfo->hasPropertyByName(FM_PROP_FORMATSSUPPLIER))
117 pClassNameResourceId = RID_STR_PROPTITLE_FORMATTED;
118 }
119 }
120 }
121 }
122 break;
123
124 case FormComponentType::COMMANDBUTTON:
125 pClassNameResourceId = RID_STR_PROPTITLE_PUSHBUTTON; break;
126 case FormComponentType::RADIOBUTTON:
127 pClassNameResourceId = RID_STR_PROPTITLE_RADIOBUTTON; break;
128 case FormComponentType::CHECKBOX:
129 pClassNameResourceId = RID_STR_PROPTITLE_CHECKBOX; break;
130 case FormComponentType::LISTBOX:
131 pClassNameResourceId = RID_STR_PROPTITLE_LISTBOX; break;
132 case FormComponentType::COMBOBOX:
133 pClassNameResourceId = RID_STR_PROPTITLE_COMBOBOX; break;
134 case FormComponentType::GROUPBOX:
135 pClassNameResourceId = RID_STR_PROPTITLE_GROUPBOX; break;
136 case FormComponentType::IMAGEBUTTON:
137 pClassNameResourceId = RID_STR_PROPTITLE_IMAGEBUTTON; break;
138 case FormComponentType::FIXEDTEXT:
139 pClassNameResourceId = RID_STR_PROPTITLE_FIXEDTEXT; break;
140 case FormComponentType::GRIDCONTROL:
141 pClassNameResourceId = RID_STR_PROPTITLE_DBGRID; break;
142 case FormComponentType::FILECONTROL:
143 pClassNameResourceId = RID_STR_PROPTITLE_FILECONTROL; break;
144 case FormComponentType::DATEFIELD:
145 pClassNameResourceId = RID_STR_PROPTITLE_DATEFIELD; break;
146 case FormComponentType::TIMEFIELD:
147 pClassNameResourceId = RID_STR_PROPTITLE_TIMEFIELD; break;
148 case FormComponentType::NUMERICFIELD:
149 pClassNameResourceId = RID_STR_PROPTITLE_NUMERICFIELD; break;
150 case FormComponentType::CURRENCYFIELD:
151 pClassNameResourceId = RID_STR_PROPTITLE_CURRENCYFIELD; break;
152 case FormComponentType::PATTERNFIELD:
153 pClassNameResourceId = RID_STR_PROPTITLE_PATTERNFIELD; break;
154 case FormComponentType::IMAGECONTROL:
155 pClassNameResourceId = RID_STR_PROPTITLE_IMAGECONTROL; break;
156 case FormComponentType::HIDDENCONTROL:
157 pClassNameResourceId = RID_STR_PROPTITLE_HIDDEN; break;
158 case FormComponentType::SCROLLBAR:
159 pClassNameResourceId = RID_STR_PROPTITLE_SCROLLBAR; break;
160 case FormComponentType::SPINBUTTON:
161 pClassNameResourceId = RID_STR_PROPTITLE_SPINBUTTON; break;
162 case FormComponentType::NAVIGATIONBAR:
163 pClassNameResourceId = RID_STR_PROPTITLE_NAVBAR; break;
164 case FormComponentType::CONTROL:
165 default:
166 pClassNameResourceId = RID_STR_CONTROL; break;
167 }
168
169 return SvxResId(pClassNameResourceId);
170 }
171
FmPropBrw(const Reference<XComponentContext> & _xORB,SfxBindings * _pBindings,SfxChildWindow * _pMgr,weld::Window * _pParent,const SfxChildWinInfo * _pInfo)172 FmPropBrw::FmPropBrw(const Reference< XComponentContext >& _xORB, SfxBindings* _pBindings,
173 SfxChildWindow* _pMgr, weld::Window* _pParent, const SfxChildWinInfo* _pInfo)
174 : SfxModelessDialogController(_pBindings, _pMgr, _pParent, u"svx/ui/formpropertydialog.ui"_ustr, u"FormPropertyDialog"_ustr)
175 , SfxControllerItem(SID_FM_PROPERTY_CONTROL, *_pBindings)
176 , m_bInitialStateChange(true)
177 , m_pParent(_pParent)
178 , m_nAsyncGetFocusId(nullptr)
179 , m_xDialogBox(m_xBuilder->weld_box(u"dialog-vbox1"_ustr))
180 , m_xContainer(m_xBuilder->weld_container(u"container"_ustr))
181 , m_xORB(_xORB)
182 {
183 m_xContainer->set_size_request(m_xContainer->get_approximate_digit_width() * 72, m_xContainer->get_text_height() * 20);
184
185 try
186 {
187 // create a frame wrapper for myself
188 m_xMeAsFrame = Frame::create(m_xORB);
189
190 // transport the container area of this dialog to be the container window of the frame
191 css::uno::Reference<css::awt::XWindow> xFrameContainerWindow(new weld::TransportAsXWindow(m_xContainer.get()));
192 m_xMeAsFrame->initialize(xFrameContainerWindow);
193 m_xMeAsFrame->setName(u"form property browser"_ustr);
194 }
195 catch (const Exception&)
196 {
197 OSL_FAIL("FmPropBrw::FmPropBrw: could not create/initialize my frame!");
198 m_xMeAsFrame.clear();
199 }
200
201 if ( _pInfo )
202 m_sLastActivePage = _pInfo->aExtraString;
203 }
204
~FmPropBrw()205 FmPropBrw::~FmPropBrw()
206 {
207 if (m_nAsyncGetFocusId)
208 {
209 Application::RemoveUserEvent(m_nAsyncGetFocusId);
210 m_nAsyncGetFocusId = nullptr;
211 }
212
213 if (m_xBrowserController.is())
214 implDetachController();
215 try
216 {
217 // remove our own properties from the component context. We cannot ensure that the component context
218 // is freed (there might be refcount problems :-\), so at least ensure the context itself
219 // does hold the objects anymore
220 Reference<XNameContainer> xName(m_xInspectorContext,uno::UNO_QUERY);
221 if ( xName.is() )
222 {
223 const OUString pProps[] = { u"ContextDocument"_ustr
224 , u"DialogParentWindow"_ustr
225 , u"ControlContext"_ustr
226 , u"ControlShapeAccess"_ustr };
227 for (const auto & i : pProps)
228 xName->removeByName( i );
229 }
230 }
231 catch (const Exception& )
232 {
233 DBG_UNHANDLED_EXCEPTION("svx");
234 }
235 ::SfxControllerItem::dispose();
236 }
237
getCurrentPage() const238 OUString FmPropBrw::getCurrentPage() const
239 {
240 OUString sCurrentPage;
241 try
242 {
243 if ( m_xBrowserController.is() )
244 {
245 OSL_VERIFY( m_xBrowserController->getViewData() >>= sCurrentPage );
246 }
247
248 if ( sCurrentPage.isEmpty() )
249 sCurrentPage = m_sLastActivePage;
250 }
251 catch( const Exception& )
252 {
253 TOOLS_WARN_EXCEPTION("svx.form", "caught an exception while retrieving the current page!");
254 }
255 return sCurrentPage;
256 }
257
implDetachController()258 void FmPropBrw::implDetachController()
259 {
260 m_sLastActivePage = getCurrentPage();
261
262 implSetNewSelection( InterfaceBag() );
263
264 if ( m_xMeAsFrame.is() )
265 {
266 try
267 {
268 m_xMeAsFrame->setComponent(nullptr, nullptr);
269 }
270 catch( const Exception& )
271 {
272 TOOLS_WARN_EXCEPTION("svx.form", "caught an exception while resetting the component!");
273 }
274 }
275
276 // we attached a frame to the controller manually, so we need to manually tell it that it's detached, too
277 if ( m_xBrowserController.is() )
278 {
279 m_xBrowserController->attachFrame( nullptr );
280 }
281
282 m_xBrowserController.clear();
283 m_xInspectorModel.clear();
284 m_xMeAsFrame.clear();
285 }
286
Close()287 void FmPropBrw::Close()
288 {
289 // suspend the controller (it is allowed to veto)
290 if ( m_xMeAsFrame.is() )
291 {
292 try
293 {
294 Reference< XController > xController( m_xMeAsFrame->getController() );
295 if ( xController.is() && !xController->suspend( true ) )
296 return;
297 }
298 catch( const Exception& )
299 {
300 TOOLS_WARN_EXCEPTION("svx.form", "caught an exception while asking the controller!");
301 }
302 }
303
304 implDetachController();
305
306 // remember our bindings: while we're closed, we're deleted, too, so accessing the bindings after this
307 // would be deadly
308 // 10/19/00 - 79321 - FS
309 SfxBindings& rBindings = SfxControllerItem::GetBindings();
310
311 SfxModelessDialogController::Close();
312
313 rBindings.Invalidate(SID_FM_CTL_PROPERTIES);
314 rBindings.Invalidate(SID_FM_PROPERTIES);
315 }
316
implIsReadOnlyModel() const317 bool FmPropBrw::implIsReadOnlyModel() const
318 {
319 try
320 {
321 if ( m_xInspectorModel.is() )
322 return m_xInspectorModel->getIsReadOnly();
323 return false;
324 }
325 catch( const Exception& )
326 {
327 DBG_UNHANDLED_EXCEPTION("svx");
328 }
329 return true;
330 }
331
332
implSetNewSelection(const InterfaceBag & _rSelection)333 void FmPropBrw::implSetNewSelection( const InterfaceBag& _rSelection )
334 {
335 if ( !m_xBrowserController.is() )
336 return;
337
338 try
339 {
340 Reference< XObjectInspector > xInspector( m_xBrowserController, UNO_QUERY_THROW );
341
342 // tell it the objects to inspect
343 xInspector->inspect( comphelper::containerToSequence(_rSelection) );
344 }
345 catch( const VetoException& )
346 {
347 return;
348 }
349 catch( const Exception& )
350 {
351 TOOLS_WARN_EXCEPTION("svx.form", "");
352 return;
353 }
354
355 // set the new title according to the selected object
356 OUString sTitle;
357
358 if ( _rSelection.empty() )
359 {
360 sTitle = SvxResId(RID_STR_NO_PROPERTIES);
361 }
362 else if ( _rSelection.size() > 1 )
363 {
364 // no form component and (no form or no name) -> Multiselection
365 sTitle = SvxResId(RID_STR_PROPERTIES_CONTROL) +
366 SvxResId(RID_STR_PROPTITLE_MULTISELECT);
367 }
368 else
369 {
370 Reference< XPropertySet > xSingleSelection( *_rSelection.begin(), UNO_QUERY);
371 if ( ::comphelper::hasProperty( FM_PROP_CLASSID, xSingleSelection ) )
372 {
373 sal_Int16 nClassID = FormComponentType::CONTROL;
374 xSingleSelection->getPropertyValue( FM_PROP_CLASSID ) >>= nClassID;
375
376 sTitle = SvxResId(RID_STR_PROPERTIES_CONTROL) +
377 GetUIHeadlineName(nClassID, Any(xSingleSelection));
378 }
379 else if ( Reference< XForm >( xSingleSelection, UNO_QUERY ).is() )
380 sTitle = SvxResId(RID_STR_PROPERTIES_FORM);
381 }
382
383 if ( implIsReadOnlyModel() )
384 sTitle += SvxResId(RID_STR_READONLY_VIEW);
385
386 m_xDialog->set_title(sTitle);
387 }
388
FillInfo(SfxChildWinInfo & rInfo) const389 void FmPropBrw::FillInfo( SfxChildWinInfo& rInfo ) const
390 {
391 rInfo.bVisible = false;
392 rInfo.aExtraString = getCurrentPage();
393 }
394
IMPL_LINK_NOARG(FmPropBrw,OnAsyncGetFocus,void *,void)395 IMPL_LINK_NOARG( FmPropBrw, OnAsyncGetFocus, void*, void )
396 {
397 m_xDialogBox->child_grab_focus();
398 m_nAsyncGetFocusId = nullptr;
399 }
400
401 namespace
402 {
lcl_shouldEnableHelpSection(const Reference<XComponentContext> & _rxContext)403 bool lcl_shouldEnableHelpSection( const Reference< XComponentContext >& _rxContext )
404 {
405 ::utl::OConfigurationTreeRoot aConfiguration(
406 ::utl::OConfigurationTreeRoot::createWithComponentContext(
407 _rxContext,
408 u"/org.openoffice.Office.Common/Forms/PropertyBrowser/"_ustr ) );
409
410 bool bEnabled = false;
411 OSL_VERIFY( aConfiguration.getNodeValue( u"DirectHelp"_ustr ) >>= bEnabled );
412 return bEnabled;
413 }
414 }
415
impl_createPropertyBrowser_throw(FmFormShell * _pFormShell)416 void FmPropBrw::impl_createPropertyBrowser_throw( FmFormShell* _pFormShell )
417 {
418 // the document in which we live
419 Reference< XInterface > xDocument;
420 if ( _pFormShell && _pFormShell->GetObjectShell() )
421 xDocument = _pFormShell->GetObjectShell()->GetModel();
422
423 // the context of the controls in our document
424 Reference< awt::XControlContainer > xControlContext;
425 if ( _pFormShell && _pFormShell->GetFormView() )
426 {
427 SdrPageView* pPageView = _pFormShell->GetFormView()->GetSdrPageView();
428
429 if(pPageView)
430 {
431 SdrPageWindow* pPageWindow = pPageView->GetPageWindow(0);
432
433 if(pPageWindow)
434 {
435 xControlContext = pPageWindow->GetControlContainer();
436 }
437 }
438 }
439
440 // the default parent window for message boxes
441 Reference< XWindow > xParentWindow(m_xDialog->GetXWindow());
442
443 // the mapping from control models to control shapes
444 Reference< XMap > xControlMap;
445 FmFormPage* pFormPage = _pFormShell ? _pFormShell->GetCurPage() : nullptr;
446 if ( pFormPage )
447 xControlMap = pFormPage->GetImpl().getControlToShapeMap();
448
449 // our own component context
450
451 // a ComponentContext for the
452 ::cppu::ContextEntry_Init aHandlerContextInfo[] =
453 {
454 ::cppu::ContextEntry_Init( u"ContextDocument"_ustr, Any( xDocument ) ),
455 ::cppu::ContextEntry_Init( u"DialogParentWindow"_ustr, Any( xParentWindow ) ),
456 ::cppu::ContextEntry_Init( u"ControlContext"_ustr, Any( xControlContext ) ),
457 ::cppu::ContextEntry_Init( u"ControlShapeAccess"_ustr, Any( xControlMap ) )
458 };
459 m_xInspectorContext.set(
460 ::cppu::createComponentContext( aHandlerContextInfo, SAL_N_ELEMENTS( aHandlerContextInfo ),
461 m_xORB ) );
462
463 bool bEnableHelpSection = lcl_shouldEnableHelpSection( m_xORB );
464
465 // an object inspector model
466 m_xInspectorModel =
467 bEnableHelpSection
468 ? DefaultFormComponentInspectorModel::createWithHelpSection( m_xInspectorContext, 3, 5 )
469 : DefaultFormComponentInspectorModel::createDefault( m_xInspectorContext );
470
471 // an object inspector
472 m_xBrowserController =
473 ObjectInspector::createWithModel(
474 m_xInspectorContext, m_xInspectorModel
475 );
476
477 if ( !m_xBrowserController.is() )
478 {
479 ShowServiceNotAvailableError(m_pParent, u"com.sun.star.inspection.ObjectInspector", true);
480 }
481 else
482 {
483 m_xBrowserController->attachFrame( Reference<XFrame>(m_xMeAsFrame,UNO_QUERY_THROW) );
484 }
485
486 if ( bEnableHelpSection )
487 {
488 Reference< XObjectInspector > xInspector( m_xBrowserController, UNO_QUERY_THROW );
489 Reference< XObjectInspectorUI > xInspectorUI( xInspector->getInspectorUI() );
490 DefaultHelpProvider::create( m_xInspectorContext, xInspectorUI );
491 }
492 }
493
494
impl_ensurePropertyBrowser_nothrow(FmFormShell * _pFormShell)495 void FmPropBrw::impl_ensurePropertyBrowser_nothrow( FmFormShell* _pFormShell )
496 {
497 // the document in which we live
498 Reference< XInterface > xDocument;
499 SfxObjectShell* pObjectShell = _pFormShell ? _pFormShell->GetObjectShell() : nullptr;
500 if ( pObjectShell )
501 xDocument = pObjectShell->GetModel();
502 if ( ( xDocument == m_xLastKnownDocument ) && m_xBrowserController.is() )
503 // nothing to do
504 return;
505
506 try
507 {
508 // clean up any previous instances of the object inspector
509 if ( m_xMeAsFrame.is() )
510 m_xMeAsFrame->setComponent( nullptr, nullptr );
511 else
512 ::comphelper::disposeComponent( m_xBrowserController );
513 m_xBrowserController.clear();
514 m_xInspectorModel.clear();
515
516 // and create a new one
517 impl_createPropertyBrowser_throw( _pFormShell );
518 }
519 catch( const Exception& )
520 {
521 DBG_UNHANDLED_EXCEPTION("svx");
522 }
523 m_xLastKnownDocument = xDocument;
524 }
525
526
StateChangedAtToolBoxControl(sal_uInt16 nSID,SfxItemState eState,const SfxPoolItem * pState)527 void FmPropBrw::StateChangedAtToolBoxControl(sal_uInt16 nSID, SfxItemState eState, const SfxPoolItem* pState)
528 {
529 if (!pState || SID_FM_PROPERTY_CONTROL != nSID)
530 return;
531
532 try
533 {
534 if (eState >= SfxItemState::DEFAULT)
535 {
536 FmFormShell* pShell = dynamic_cast<FmFormShell*>( static_cast<const SfxObjectItem*>(pState)->GetShell() );
537 InterfaceBag aSelection;
538 if ( pShell )
539 pShell->GetImpl()->getCurrentSelection_Lock(aSelection);
540
541 impl_ensurePropertyBrowser_nothrow( pShell );
542
543 // set the new object to inspect
544 implSetNewSelection( aSelection );
545
546 // if this is the first time we're here, some additional things need to be done ...
547 if ( m_bInitialStateChange )
548 {
549 // if we're just newly created, we want to have the focus
550 m_nAsyncGetFocusId = Application::PostUserEvent(LINK(this, FmPropBrw, OnAsyncGetFocus));
551
552 // and additionally, we want to show the page which was active during
553 // our previous incarnation
554 if ( !m_sLastActivePage.isEmpty() )
555 {
556 try
557 {
558 if ( m_xBrowserController.is() )
559 m_xBrowserController->restoreViewData( Any( m_sLastActivePage ) );
560 }
561 catch( const Exception& )
562 {
563 TOOLS_WARN_EXCEPTION("svx.form",
564 "caught an exception while setting the initial page!");
565 }
566 }
567
568 m_bInitialStateChange = false;
569 }
570
571 }
572 else
573 {
574 implSetNewSelection( InterfaceBag() );
575 }
576 }
577 catch (Exception&)
578 {
579 TOOLS_WARN_EXCEPTION("svx.form", "");
580 }
581 }
582
583 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
584