Bump version to 6.4-15
[LibreOffice.git] / svx / source / form / fmtextcontrolshell.cxx
blob8815d1a2be99751bbd9458b703a4329e99eba048
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
21 #include <fmprop.hxx>
22 #include <fmtextcontroldialogs.hxx>
23 #include <fmtextcontrolfeature.hxx>
24 #include <fmtextcontrolshell.hxx>
25 #include <editeng/crossedoutitem.hxx>
26 #include <editeng/editeng.hxx>
27 #include <editeng/eeitem.hxx>
28 #include <svx/fmglob.hxx>
29 #include <editeng/scriptspaceitem.hxx>
30 #include <svx/svxids.hrc>
31 #include <editeng/udlnitem.hxx>
33 #include <com/sun/star/beans/XPropertySet.hpp>
34 #include <com/sun/star/awt/FontDescriptor.hpp>
35 #include <com/sun/star/frame/XDispatchProvider.hpp>
36 #include <com/sun/star/form/XForm.hpp>
37 #include <com/sun/star/container/XChild.hpp>
38 #include <com/sun/star/awt/XFocusListener.hpp>
39 #include <com/sun/star/awt/XMouseListener.hpp>
40 #include <com/sun/star/awt/XWindow.hpp>
41 #include <com/sun/star/util/URLTransformer.hpp>
43 #include <comphelper/processfactory.hxx>
44 #include <cppuhelper/implbase.hxx>
45 #include <sfx2/app.hxx>
46 #include <sfx2/bindings.hxx>
47 #include <sfx2/dispatch.hxx>
48 #include <sfx2/msgpool.hxx>
49 #include <sfx2/msg.hxx>
50 #include <sfx2/objsh.hxx>
51 #include <sfx2/request.hxx>
52 #include <sfx2/sfxuno.hxx>
53 #include <sfx2/viewfrm.hxx>
54 #include <svl/eitem.hxx>
55 #include <svl/intitem.hxx>
56 #include <svl/itempool.hxx>
57 #include <svl/languageoptions.hxx>
58 #include <svtools/stringtransfer.hxx>
59 #include <svl/whiter.hxx>
60 #include <toolkit/helper/vclunohelper.hxx>
61 #include <tools/diagnose_ex.h>
62 #include <sal/log.hxx>
63 #include <vcl/outdev.hxx>
64 #include <vcl/svapp.hxx>
66 #include <memory>
69 namespace svx
73 using namespace ::com::sun::star;
74 using namespace ::com::sun::star::uno;
75 using namespace ::com::sun::star::form;
76 using namespace ::com::sun::star::form::runtime;
77 using namespace ::com::sun::star::lang;
78 using namespace ::com::sun::star::frame;
79 using namespace ::com::sun::star::util;
80 using namespace ::com::sun::star::beans;
81 using namespace ::com::sun::star::container;
84 typedef sal_uInt16 WhichId;
87 static SfxSlotId pTextControlSlots[] =
89 SID_CLIPBOARD_FORMAT_ITEMS,
90 SID_CUT,
91 SID_COPY,
92 SID_PASTE,
93 SID_SELECTALL,
94 // SID_ATTR_TABSTOP, /* 2 */
95 SID_ATTR_CHAR_FONT,
96 SID_ATTR_CHAR_POSTURE,
97 SID_ATTR_CHAR_WEIGHT,
98 SID_ATTR_CHAR_SHADOWED,
99 SID_ATTR_CHAR_WORDLINEMODE,
100 SID_ATTR_CHAR_CONTOUR,
101 SID_ATTR_CHAR_STRIKEOUT,
102 SID_ATTR_CHAR_UNDERLINE,
103 SID_ATTR_CHAR_FONTHEIGHT,
104 SID_ATTR_CHAR_COLOR,
105 SID_ATTR_CHAR_KERNING,
106 SID_ATTR_CHAR_LANGUAGE, /* 20 */
107 SID_ATTR_CHAR_ESCAPEMENT,
108 SID_ATTR_PARA_ADJUST, /* 28 */
109 SID_ATTR_PARA_ADJUST_LEFT,
110 SID_ATTR_PARA_ADJUST_RIGHT,
111 SID_ATTR_PARA_ADJUST_CENTER,
112 SID_ATTR_PARA_ADJUST_BLOCK,
113 SID_ATTR_PARA_LINESPACE, /* 33 */
114 SID_ATTR_PARA_LINESPACE_10,
115 SID_ATTR_PARA_LINESPACE_15,
116 SID_ATTR_PARA_LINESPACE_20,
117 SID_ATTR_LRSPACE, /* 48 */
118 SID_ATTR_ULSPACE, /* 49 */
119 SID_ATTR_CHAR_AUTOKERN,
120 SID_SET_SUPER_SCRIPT,
121 SID_SET_SUB_SCRIPT,
122 SID_CHAR_DLG,
123 SID_PARA_DLG,
124 // SID_TEXTDIRECTION_LEFT_TO_RIGHT, /* 907 */
125 // SID_TEXTDIRECTION_TOP_TO_BOTTOM,
126 SID_ATTR_CHAR_SCALEWIDTH, /* 911 */
127 SID_ATTR_CHAR_RELIEF,
128 SID_ATTR_PARA_LEFT_TO_RIGHT, /* 950 */
129 SID_ATTR_PARA_RIGHT_TO_LEFT,
130 SID_ATTR_CHAR_OVERLINE,
134 // slots which we are not responsible for on the SfxShell level, but
135 // need to handle during the "paragraph attributes" and/or "character
136 // attributes" dialogs
137 static SfxSlotId pDialogSlots[] =
139 SID_ATTR_TABSTOP,
140 SID_ATTR_PARA_HANGPUNCTUATION,
141 SID_ATTR_PARA_FORBIDDEN_RULES,
142 SID_ATTR_PARA_SCRIPTSPACE,
143 SID_ATTR_CHAR_LATIN_LANGUAGE,
144 SID_ATTR_CHAR_CJK_LANGUAGE,
145 SID_ATTR_CHAR_CTL_LANGUAGE,
146 SID_ATTR_CHAR_LATIN_FONT,
147 SID_ATTR_CHAR_CJK_FONT,
148 SID_ATTR_CHAR_CTL_FONT,
149 SID_ATTR_CHAR_LATIN_FONTHEIGHT,
150 SID_ATTR_CHAR_CJK_FONTHEIGHT,
151 SID_ATTR_CHAR_CTL_FONTHEIGHT,
152 SID_ATTR_CHAR_LATIN_WEIGHT,
153 SID_ATTR_CHAR_CJK_WEIGHT,
154 SID_ATTR_CHAR_CTL_WEIGHT,
155 SID_ATTR_CHAR_LATIN_POSTURE,
156 SID_ATTR_CHAR_CJK_POSTURE,
157 SID_ATTR_CHAR_CTL_POSTURE,
158 SID_ATTR_CHAR_EMPHASISMARK,
162 typedef ::cppu::WeakImplHelper < css::awt::XFocusListener
163 > FmFocusListenerAdapter_Base;
164 class FmFocusListenerAdapter : public FmFocusListenerAdapter_Base
166 private:
167 IFocusObserver* m_pObserver;
168 Reference< css::awt::XWindow > m_xWindow;
170 public:
171 FmFocusListenerAdapter( const Reference< css::awt::XControl >& _rxControl, IFocusObserver* _pObserver );
173 // clean up the instance
174 void dispose();
176 protected:
177 virtual ~FmFocusListenerAdapter() override;
179 protected:
180 virtual void SAL_CALL focusGained( const css::awt::FocusEvent& e ) override;
181 virtual void SAL_CALL focusLost( const css::awt::FocusEvent& e ) override;
182 virtual void SAL_CALL disposing( const EventObject& Source ) override;
186 FmFocusListenerAdapter::FmFocusListenerAdapter( const Reference< css::awt::XControl >& _rxControl, IFocusObserver* _pObserver )
187 :m_pObserver( _pObserver )
188 ,m_xWindow( _rxControl, UNO_QUERY )
191 DBG_ASSERT( m_xWindow.is(), "FmFocusListenerAdapter::FmFocusListenerAdapter: invalid control!" );
192 osl_atomic_increment( &m_refCount );
196 if ( m_xWindow.is() )
197 m_xWindow->addFocusListener( this );
199 catch( const Exception& )
201 DBG_UNHANDLED_EXCEPTION("svx");
204 osl_atomic_decrement( &m_refCount );
208 FmFocusListenerAdapter::~FmFocusListenerAdapter()
210 acquire();
211 dispose();
216 void FmFocusListenerAdapter::dispose()
218 if ( m_xWindow.is() )
220 m_xWindow->removeFocusListener( this );
221 m_xWindow.clear();
226 void SAL_CALL FmFocusListenerAdapter::focusGained( const css::awt::FocusEvent& e )
228 if ( m_pObserver )
229 m_pObserver->focusGained( e );
233 void SAL_CALL FmFocusListenerAdapter::focusLost( const css::awt::FocusEvent& e )
235 if ( m_pObserver )
236 m_pObserver->focusLost( e );
240 void SAL_CALL FmFocusListenerAdapter::disposing( const EventObject& Source )
242 DBG_ASSERT( Source.Source == m_xWindow, "FmFocusListenerAdapter::disposing: where did this come from?" );
243 m_xWindow.clear();
246 typedef ::cppu::WeakImplHelper < css::awt::XMouseListener
247 > FmMouseListenerAdapter_Base;
248 class FmMouseListenerAdapter : public FmMouseListenerAdapter_Base
250 private:
251 IContextRequestObserver* m_pObserver;
252 Reference< css::awt::XWindow > m_xWindow;
254 public:
255 FmMouseListenerAdapter( const Reference< css::awt::XControl >& _rxControl, IContextRequestObserver* _pObserver );
257 // clean up the instance
258 void dispose();
260 protected:
261 virtual ~FmMouseListenerAdapter() override;
263 protected:
264 virtual void SAL_CALL mousePressed( const css::awt::MouseEvent& e ) override;
265 virtual void SAL_CALL mouseReleased( const css::awt::MouseEvent& e ) override;
266 virtual void SAL_CALL mouseEntered( const css::awt::MouseEvent& e ) override;
267 virtual void SAL_CALL mouseExited( const css::awt::MouseEvent& e ) override;
268 virtual void SAL_CALL disposing( const EventObject& Source ) override;
271 FmMouseListenerAdapter::FmMouseListenerAdapter( const Reference< css::awt::XControl >& _rxControl, IContextRequestObserver* _pObserver )
272 :m_pObserver( _pObserver )
273 ,m_xWindow( _rxControl, UNO_QUERY )
276 DBG_ASSERT( m_xWindow.is(), "FmMouseListenerAdapter::FmMouseListenerAdapter: invalid control!" );
277 osl_atomic_increment( &m_refCount );
281 if ( m_xWindow.is() )
282 m_xWindow->addMouseListener( this );
284 catch( const Exception& )
286 DBG_UNHANDLED_EXCEPTION("svx");
289 osl_atomic_decrement( &m_refCount );
293 FmMouseListenerAdapter::~FmMouseListenerAdapter()
295 acquire();
296 dispose();
301 void FmMouseListenerAdapter::dispose()
303 if ( m_xWindow.is() )
305 m_xWindow->removeMouseListener( this );
306 m_xWindow.clear();
311 void SAL_CALL FmMouseListenerAdapter::mousePressed( const css::awt::MouseEvent& _rEvent )
313 SolarMutexGuard aGuard;
314 // is this a request for a context menu?
315 if ( _rEvent.PopupTrigger )
317 if ( m_pObserver )
318 m_pObserver->contextMenuRequested();
323 void SAL_CALL FmMouseListenerAdapter::mouseReleased( const css::awt::MouseEvent& /*e*/ )
325 // not interested in
329 void SAL_CALL FmMouseListenerAdapter::mouseEntered( const css::awt::MouseEvent& /*e*/ )
331 // not interested in
335 void SAL_CALL FmMouseListenerAdapter::mouseExited( const css::awt::MouseEvent& /*e*/ )
337 // not interested in
341 void SAL_CALL FmMouseListenerAdapter::disposing( const EventObject& Source )
343 DBG_ASSERT( Source.Source == m_xWindow, "FmMouseListenerAdapter::disposing: where did this come from?" );
344 m_xWindow.clear();
348 //= FmTextControlShell
351 namespace
354 void lcl_translateUnoStateToItem( SfxSlotId _nSlot, const Any& _rUnoState, SfxItemSet& _rSet )
356 WhichId nWhich = _rSet.GetPool()->GetWhich( _nSlot );
357 if ( !_rUnoState.hasValue() )
359 if ( ( _nSlot != SID_CUT )
360 && ( _nSlot != SID_COPY )
361 && ( _nSlot != SID_PASTE )
364 _rSet.InvalidateItem( nWhich );
367 else
369 switch ( _rUnoState.getValueType().getTypeClass() )
371 case TypeClass_BOOLEAN:
373 bool bState = false;
374 _rUnoState >>= bState;
375 if ( _nSlot == SID_ATTR_PARA_SCRIPTSPACE )
376 _rSet.Put( SvxScriptSpaceItem( bState, nWhich ) );
377 else
378 _rSet.Put( SfxBoolItem( nWhich, bState ) );
380 break;
382 default:
384 Sequence< PropertyValue > aComplexState;
385 if ( _rUnoState >>= aComplexState )
387 if ( !aComplexState.hasElements() )
388 _rSet.InvalidateItem( nWhich );
389 else
391 SfxAllItemSet aAllItems( _rSet );
392 TransformParameters( _nSlot, aComplexState, aAllItems );
393 const SfxPoolItem* pTransformed = aAllItems.GetItem( nWhich );
394 OSL_ENSURE( pTransformed, "lcl_translateUnoStateToItem: non-empty parameter sequence leading to empty item?" );
395 if ( pTransformed )
396 _rSet.Put( *pTransformed );
397 else
398 _rSet.InvalidateItem( nWhich );
401 else
403 OSL_FAIL( "lcl_translateUnoStateToItem: invalid state!" );
411 OUString lcl_getUnoSlotName( SfxSlotId _nSlotId )
413 OUString sSlotUnoName;
415 SfxSlotPool& rSlotPool = SfxSlotPool::GetSlotPool();
416 const SfxSlot* pSlot = rSlotPool.GetSlot( _nSlotId );
418 const sal_Char* pAsciiUnoName = nullptr;
419 if ( pSlot )
421 pAsciiUnoName = pSlot->GetUnoName();
423 else
425 // some hard-coded slots, which do not have a UNO name at SFX level, but which
426 // we nevertheless need to transport via UNO mechanisms, so we need a name
427 switch ( _nSlotId )
429 case SID_ATTR_PARA_HANGPUNCTUATION: pAsciiUnoName = "AllowHangingPunctuation"; break;
430 case SID_ATTR_PARA_FORBIDDEN_RULES: pAsciiUnoName = "ApplyForbiddenCharacterRules"; break;
431 case SID_ATTR_PARA_SCRIPTSPACE: pAsciiUnoName = "UseScriptSpacing"; break;
435 if ( pAsciiUnoName )
437 sSlotUnoName = ".uno:" + OUString::createFromAscii( pAsciiUnoName );
439 else
441 SAL_WARN( "svx", "lcl_getUnoSlotName: invalid slot id, or invalid slot, or no UNO name! "
442 "(slot id: " << _nSlotId << ")");
444 return sSlotUnoName;
448 bool lcl_determineReadOnly( const Reference< css::awt::XControl >& _rxControl )
450 bool bIsReadOnlyModel = true;
453 Reference< XPropertySet > xModelProps;
454 if ( _rxControl.is() )
455 xModelProps.set(_rxControl->getModel(), css::uno::UNO_QUERY);
456 Reference< XPropertySetInfo > xModelPropInfo;
457 if ( xModelProps.is() )
458 xModelPropInfo = xModelProps->getPropertySetInfo();
460 if ( !xModelPropInfo.is() || !xModelPropInfo->hasPropertyByName( FM_PROP_READONLY ) )
461 bIsReadOnlyModel = true;
462 else
464 bool bReadOnly = true;
465 xModelProps->getPropertyValue( FM_PROP_READONLY ) >>= bReadOnly;
466 bIsReadOnlyModel = bReadOnly;
469 catch( const Exception& )
471 DBG_UNHANDLED_EXCEPTION("svx");
473 return bIsReadOnlyModel;
477 vcl::Window* lcl_getWindow( const Reference< css::awt::XControl >& _rxControl )
479 vcl::Window* pWindow = nullptr;
482 Reference< css::awt::XWindowPeer > xControlPeer;
483 if ( _rxControl.is() )
484 xControlPeer = _rxControl->getPeer();
485 if ( xControlPeer.is() )
486 pWindow = VCLUnoHelper::GetWindow( xControlPeer ).get();
488 catch( const Exception& )
490 DBG_UNHANDLED_EXCEPTION("svx");
493 return pWindow;
497 bool lcl_isRichText( const Reference< css::awt::XControl >& _rxControl )
499 if ( !_rxControl.is() )
500 return false;
502 bool bIsRichText = false;
505 Reference< XPropertySet > xModelProps( _rxControl->getModel(), UNO_QUERY );
506 Reference< XPropertySetInfo > xPSI;
507 if ( xModelProps.is() )
508 xPSI = xModelProps->getPropertySetInfo();
509 OUString sRichTextPropertyName = "RichText";
510 if ( xPSI.is() && xPSI->hasPropertyByName( sRichTextPropertyName ) )
512 OSL_VERIFY( xModelProps->getPropertyValue( sRichTextPropertyName ) >>= bIsRichText );
515 catch( const Exception& )
517 DBG_UNHANDLED_EXCEPTION("svx");
519 return bIsRichText;
524 FmTextControlShell::FmTextControlShell( SfxViewFrame* _pFrame )
525 :m_bActiveControl( false )
526 ,m_bActiveControlIsReadOnly( true )
527 ,m_bActiveControlIsRichText( false )
528 ,m_pViewFrame( _pFrame )
529 ,m_rBindings( _pFrame->GetBindings() )
530 ,m_bNeedClipboardInvalidation( true )
532 m_aClipboardInvalidation.SetInvokeHandler( LINK( this, FmTextControlShell, OnInvalidateClipboard ) );
533 m_aClipboardInvalidation.SetTimeout( 200 );
537 FmTextControlShell::~FmTextControlShell()
539 dispose();
543 IMPL_LINK_NOARG( FmTextControlShell, OnInvalidateClipboard, Timer*, void )
545 if ( m_bNeedClipboardInvalidation )
547 SAL_INFO("svx.form", "invalidating clipboard slots" );
548 m_rBindings.Invalidate( SID_CUT );
549 m_rBindings.Invalidate( SID_COPY );
550 m_rBindings.Invalidate( SID_PASTE );
551 m_bNeedClipboardInvalidation = false;
556 void FmTextControlShell::transferFeatureStatesToItemSet( ControlFeatures& _rDispatchers, SfxAllItemSet& _rSet, bool _bTranslateLatin )
558 SfxItemPool& rPool = *_rSet.GetPool();
560 for (const auto& rFeature : _rDispatchers)
562 SfxSlotId nSlotId( rFeature.first );
563 #if OSL_DEBUG_LEVEL > 0
564 OUString sUnoSlotName;
565 if ( SfxGetpApp() )
566 sUnoSlotName = lcl_getUnoSlotName( nSlotId );
567 else
568 sUnoSlotName = "<unknown>";
569 OString sUnoSlotNameAscii = "\"" +
570 OString( sUnoSlotName.getStr(), sUnoSlotName.getLength(), RTL_TEXTENCODING_ASCII_US ) +
571 "\"";
572 #endif
574 if ( _bTranslateLatin )
576 // A rich text control offers a dispatcher for the "Font" slot/feature.
577 // Sadly, the semantics of the dispatches is that the feature "Font" depends
578 // on the current cursor position: If it's on latin text, it's the "latin font"
579 // which is set up at the control. If it's on CJK text, it's the "CJK font", and
580 // equivalent for "CTL font".
581 // The same holds for some other font related features/slots.
582 // Thus, we have separate dispatches for "Latin Font", "Latin Font Size", etc,
583 // which are only "virtual", in a sense that there exist no item with this id.
584 // So when we encounter such a dispatcher for, say, "Latin Font", we need to
585 // put an item into the set which has the "Font" id.
587 switch ( nSlotId )
589 case SID_ATTR_CHAR_LATIN_FONT: nSlotId = SID_ATTR_CHAR_FONT; break;
590 case SID_ATTR_CHAR_LATIN_FONTHEIGHT:nSlotId = SID_ATTR_CHAR_FONTHEIGHT; break;
591 case SID_ATTR_CHAR_LATIN_LANGUAGE: nSlotId = SID_ATTR_CHAR_LANGUAGE; break;
592 case SID_ATTR_CHAR_LATIN_POSTURE: nSlotId = SID_ATTR_CHAR_POSTURE; break;
593 case SID_ATTR_CHAR_LATIN_WEIGHT: nSlotId = SID_ATTR_CHAR_WEIGHT; break;
597 WhichId nWhich = rPool.GetWhich( nSlotId );
598 bool bIsInPool = rPool.IsInRange( nWhich );
599 if ( bIsInPool )
601 #if OSL_DEBUG_LEVEL > 0
602 bool bFeatureIsEnabled = rFeature.second->isFeatureEnabled();
603 OString sMessage = "found a feature state for " + sUnoSlotNameAscii;
604 if ( !bFeatureIsEnabled )
605 sMessage += " (disabled)";
606 SAL_INFO("svx.form", sMessage );
607 #endif
609 lcl_translateUnoStateToItem( nSlotId, rFeature.second->getFeatureState(), _rSet );
611 #if OSL_DEBUG_LEVEL > 0
612 else
614 SAL_WARN("svx.form", "found a feature state for " << sUnoSlotNameAscii << ", but could not translate it into an item!" );
616 #endif
621 void FmTextControlShell::executeAttributeDialog( AttributeSet _eSet, SfxRequest& rReq )
623 const SvxFontListItem* pFontList = dynamic_cast<const SvxFontListItem*>( m_pViewFrame->GetObjectShell()->GetItem( SID_ATTR_CHAR_FONTLIST ) );
624 DBG_ASSERT( pFontList, "FmTextControlShell::executeAttributeDialog: no font list item!" );
625 if ( !pFontList )
626 return;
628 SfxItemPool* pPool = EditEngine::CreatePool();
629 pPool->FreezeIdRanges();
630 std::unique_ptr< SfxItemSet > xPureItems( new SfxItemSet( *pPool ) );
632 // put the current states of the items into the set
633 std::unique_ptr<SfxAllItemSet> xCurrentItems( new SfxAllItemSet( *xPureItems ) );
634 transferFeatureStatesToItemSet( m_aControlFeatures, *xCurrentItems, false );
636 // additional items, which we are not responsible for at the SfxShell level,
637 // but which need to be forwarded to the dialog, anyway
638 ControlFeatures aAdditionalFestures;
639 fillFeatureDispatchers( m_xActiveControl, pDialogSlots, aAdditionalFestures );
640 transferFeatureStatesToItemSet( aAdditionalFestures, *xCurrentItems, true );
642 std::unique_ptr<SfxTabDialogController> xDialog;
643 if (_eSet == eCharAttribs)
644 xDialog = std::make_unique<TextControlCharAttribDialog>(rReq.GetFrameWeld(), *xCurrentItems, *pFontList);
645 else
646 xDialog = std::make_unique<TextControlParaAttribDialog>(rReq.GetFrameWeld(), *xCurrentItems);
647 if ( RET_OK == xDialog->run() )
649 const SfxItemSet& rModifiedItems = *xDialog->GetOutputItemSet();
650 for ( WhichId nWhich = pPool->GetFirstWhich(); nWhich <= pPool->GetLastWhich(); ++nWhich )
652 if ( rModifiedItems.GetItemState( nWhich ) == SfxItemState::SET )
654 SfxSlotId nSlotForItemSet = pPool->GetSlotId( nWhich );
655 const SfxPoolItem* pModifiedItem = rModifiedItems.GetItem( nWhich );
658 SfxSlotId nSlotForDispatcher = nSlotForItemSet;
659 switch ( nSlotForDispatcher )
661 case SID_ATTR_CHAR_FONT: nSlotForDispatcher = SID_ATTR_CHAR_LATIN_FONT; break;
662 case SID_ATTR_CHAR_FONTHEIGHT:nSlotForDispatcher = SID_ATTR_CHAR_LATIN_FONTHEIGHT; break;
663 case SID_ATTR_CHAR_LANGUAGE: nSlotForDispatcher = SID_ATTR_CHAR_LATIN_LANGUAGE; break;
664 case SID_ATTR_CHAR_POSTURE: nSlotForDispatcher = SID_ATTR_CHAR_LATIN_POSTURE; break;
665 case SID_ATTR_CHAR_WEIGHT: nSlotForDispatcher = SID_ATTR_CHAR_LATIN_WEIGHT; break;
668 // do we already have a dispatcher for this slot/feature?
669 ControlFeatures::const_iterator aFeaturePos = m_aControlFeatures.find( nSlotForDispatcher );
670 bool bFound = aFeaturePos != m_aControlFeatures.end( );
672 if ( !bFound )
674 aFeaturePos = aAdditionalFestures.find( nSlotForDispatcher );
675 bFound = aFeaturePos != aAdditionalFestures.end( );
678 if ( bFound )
680 Sequence< PropertyValue > aArgs;
681 // temporarily put the modified item into a "clean" set,
682 // and let TransformItems calc the respective UNO parameters
683 xPureItems->Put( *pModifiedItem );
684 TransformItems( nSlotForItemSet, *xPureItems, aArgs );
685 xPureItems->ClearItem( nWhich );
687 if ( ( nSlotForItemSet == SID_ATTR_PARA_HANGPUNCTUATION )
688 || ( nSlotForItemSet == SID_ATTR_PARA_FORBIDDEN_RULES )
689 || ( nSlotForItemSet == SID_ATTR_PARA_SCRIPTSPACE )
692 // these are no UNO slots, they need special handling since TransformItems cannot
693 // handle them
694 DBG_ASSERT( !aArgs.hasElements(), "FmTextControlShell::executeAttributeDialog: these are no UNO slots - are they?" );
696 const SfxBoolItem* pBoolItem = dynamic_cast<const SfxBoolItem*>( pModifiedItem );
697 DBG_ASSERT( pBoolItem, "FmTextControlShell::executeAttributeDialog: no bool item?!" );
698 if ( pBoolItem )
700 aArgs.realloc( 1 );
701 aArgs[ 0 ].Name = "Enable";
702 aArgs[ 0 ].Value <<= pBoolItem->GetValue();
706 // dispatch this
707 aFeaturePos->second->dispatch( aArgs );
709 #if OSL_DEBUG_LEVEL > 0
710 else
712 OUString sUnoSlotName = lcl_getUnoSlotName( nSlotForItemSet );
713 if ( sUnoSlotName.isEmpty() )
714 sUnoSlotName = "unknown (no SfxSlot)";
715 SAL_WARN( "svx", "FmTextControShell::executeAttributeDialog: Could not handle the following item:"
716 "\n SlotID: " << nSlotForItemSet
717 << "\n WhichID: " << nWhich
718 << "\n UNO name: " << sUnoSlotName );
720 #endif
723 rReq.Done( rModifiedItems );
726 xDialog.reset();
727 xCurrentItems.reset();
728 xPureItems.reset();
729 SfxItemPool::Free(pPool);
733 void FmTextControlShell::executeSelectAll( )
737 if ( m_xActiveTextComponent.is() )
739 sal_Int32 nTextLen = m_xActiveTextComponent->getText().getLength();
740 m_xActiveTextComponent->setSelection( css::awt::Selection( 0, nTextLen ) );
743 catch( const Exception& )
745 DBG_UNHANDLED_EXCEPTION("svx");
750 void FmTextControlShell::executeClipboardSlot( SfxSlotId _nSlot )
754 if ( m_xActiveTextComponent.is() )
756 switch ( _nSlot )
758 case SID_COPY:
759 case SID_CUT:
761 OUString sSelectedText( m_xActiveTextComponent->getSelectedText() );
762 ::svt::OStringTransfer::CopyString( sSelectedText, lcl_getWindow( m_xActiveControl ) );
763 if ( SID_CUT == _nSlot )
765 css::awt::Selection aSelection( m_xActiveTextComponent->getSelection() );
766 m_xActiveTextComponent->insertText( aSelection, OUString() );
769 break;
770 case SID_PASTE:
772 OUString sClipboardContent;
773 OSL_VERIFY( ::svt::OStringTransfer::PasteString( sClipboardContent, lcl_getWindow( m_xActiveControl ) ) );
774 css::awt::Selection aSelection( m_xActiveTextComponent->getSelection() );
775 m_xActiveTextComponent->insertText( aSelection, sClipboardContent );
777 break;
778 default:
779 OSL_FAIL( "FmTextControlShell::executeClipboardSlot: invalid slot!" );
783 catch( const Exception& )
785 DBG_UNHANDLED_EXCEPTION("svx");
790 void FmTextControlShell::ExecuteTextAttribute( SfxRequest& _rReq )
792 SfxSlotId nSlot = _rReq.GetSlot();
794 ControlFeatures::const_iterator aFeaturePos = m_aControlFeatures.find( nSlot );
795 if ( aFeaturePos == m_aControlFeatures.end() )
797 // special slots
798 switch ( nSlot )
800 case SID_CHAR_DLG:
801 executeAttributeDialog( eCharAttribs, _rReq );
802 break;
804 case SID_PARA_DLG:
805 executeAttributeDialog( eParaAttribs, _rReq );
806 break;
808 case SID_SELECTALL:
809 executeSelectAll();
810 break;
812 case SID_CUT:
813 case SID_COPY:
814 case SID_PASTE:
815 executeClipboardSlot( nSlot );
816 break;
818 default:
819 DBG_ASSERT( aFeaturePos != m_aControlFeatures.end(), "FmTextControShell::ExecuteTextAttribute: I have no such dispatcher, and cannot handle it at all!" );
820 return;
823 else
825 // slots which are dispatched to the control
827 switch ( nSlot )
829 case SID_ATTR_CHAR_STRIKEOUT:
830 case SID_ATTR_CHAR_UNDERLINE:
831 case SID_ATTR_CHAR_OVERLINE:
833 SfxItemSet aToggled( *_rReq.GetArgs() );
835 lcl_translateUnoStateToItem( nSlot, aFeaturePos->second->getFeatureState(), aToggled );
836 WhichId nWhich = aToggled.GetPool()->GetWhich( nSlot );
837 const SfxPoolItem* pItem = aToggled.GetItem( nWhich );
838 if ( ( SID_ATTR_CHAR_UNDERLINE == nSlot ) || ( SID_ATTR_CHAR_OVERLINE == nSlot ) )
840 const SvxTextLineItem* pTextLine = dynamic_cast<const SvxTextLineItem*>( pItem );
841 DBG_ASSERT( pTextLine, "FmTextControlShell::ExecuteTextAttribute: ooops - no underline/overline item!" );
842 if ( pTextLine )
844 FontLineStyle eTL = pTextLine->GetLineStyle();
845 if ( SID_ATTR_CHAR_UNDERLINE == nSlot ) {
846 aToggled.Put( SvxUnderlineItem( eTL == LINESTYLE_SINGLE ? LINESTYLE_NONE : LINESTYLE_SINGLE, nWhich ) );
847 } else {
848 aToggled.Put( SvxOverlineItem( eTL == LINESTYLE_SINGLE ? LINESTYLE_NONE : LINESTYLE_SINGLE, nWhich ) );
852 else
854 const SvxCrossedOutItem* pCrossedOut = dynamic_cast<const SvxCrossedOutItem*>( pItem );
855 DBG_ASSERT( pCrossedOut, "FmTextControlShell::ExecuteTextAttribute: ooops - no CrossedOut item!" );
856 if ( pCrossedOut )
858 FontStrikeout eFS = pCrossedOut->GetStrikeout();
859 aToggled.Put( SvxCrossedOutItem( eFS == STRIKEOUT_SINGLE ? STRIKEOUT_NONE : STRIKEOUT_SINGLE, nWhich ) );
863 Sequence< PropertyValue > aArguments;
864 TransformItems( nSlot, aToggled, aArguments );
865 aFeaturePos->second->dispatch( aArguments );
867 break;
869 case SID_ATTR_CHAR_FONTHEIGHT:
870 case SID_ATTR_CHAR_FONT:
871 case SID_ATTR_CHAR_POSTURE:
872 case SID_ATTR_CHAR_WEIGHT:
873 case SID_ATTR_CHAR_SHADOWED:
874 case SID_ATTR_CHAR_CONTOUR:
875 case SID_SET_SUPER_SCRIPT:
876 case SID_SET_SUB_SCRIPT:
878 const SfxItemSet* pArgs = _rReq.GetArgs();
879 Sequence< PropertyValue > aArgs;
880 if ( pArgs )
881 TransformItems( nSlot, *pArgs, aArgs );
882 aFeaturePos->second->dispatch( aArgs );
884 break;
886 default:
887 if ( aFeaturePos->second->isFeatureEnabled() )
888 aFeaturePos->second->dispatch();
889 break;
892 _rReq.Done();
896 void FmTextControlShell::GetTextAttributeState( SfxItemSet& _rSet )
898 SfxWhichIter aIter( _rSet );
899 sal_uInt16 nSlot = aIter.FirstWhich();
900 while ( nSlot )
902 if ( ( nSlot == SID_ATTR_PARA_LEFT_TO_RIGHT )
903 || ( nSlot == SID_ATTR_PARA_RIGHT_TO_LEFT )
906 if ( !SvtLanguageOptions().IsCTLFontEnabled() )
908 _rSet.DisableItem( nSlot );
909 nSlot = aIter.NextWhich();
910 continue;
914 ControlFeatures::const_iterator aFeaturePos = m_aControlFeatures.find( nSlot );
915 if ( aFeaturePos != m_aControlFeatures.end() )
917 if ( aFeaturePos->second->isFeatureEnabled() )
918 lcl_translateUnoStateToItem( nSlot, aFeaturePos->second->getFeatureState(), _rSet );
919 else
920 _rSet.DisableItem( nSlot );
922 else
924 bool bDisable = false;
926 bool bNeedWriteableControl = false;
927 bool bNeedTextComponent = false;
928 bool bNeedSelection = false;
930 switch ( nSlot )
932 case SID_CHAR_DLG:
933 case SID_PARA_DLG:
934 bDisable |= m_aControlFeatures.empty();
935 bNeedWriteableControl = true;
936 break;
938 case SID_CUT:
939 bNeedSelection = true;
940 bNeedTextComponent = true;
941 bNeedWriteableControl = true;
942 SAL_INFO("svx.form", "need to invalidate again" );
943 m_bNeedClipboardInvalidation = true;
944 break;
946 case SID_PASTE:
948 vcl::Window* pActiveControlVCLWindow = lcl_getWindow( m_xActiveControl );
949 if ( pActiveControlVCLWindow )
951 TransferableDataHelper aDataHelper( TransferableDataHelper::CreateFromSystemClipboard( pActiveControlVCLWindow) );
952 bDisable |= !aDataHelper.HasFormat( SotClipboardFormatId::STRING );
954 else
955 bDisable = true;
957 bNeedTextComponent = true;
958 bNeedWriteableControl = true;
960 break;
962 case SID_COPY:
963 bNeedTextComponent = true;
964 bNeedSelection = true;
965 break;
967 case SID_SELECTALL:
968 bNeedTextComponent = true;
969 break;
971 default:
972 // slot is unknown at all
973 bDisable = true;
974 break;
976 SAL_WARN_IF( bNeedSelection && !bNeedTextComponent, "svx.form", "FmTextControlShell::GetTextAttributeState: bNeedSelection should imply bNeedTextComponent!" );
978 if ( !bDisable && bNeedWriteableControl )
979 bDisable |= !IsActiveControl( ) || m_bActiveControlIsReadOnly;
981 if ( !bDisable && bNeedTextComponent )
982 bDisable |= !m_xActiveTextComponent.is();
984 if ( !bDisable && bNeedSelection )
986 css::awt::Selection aSelection = m_xActiveTextComponent->getSelection();
987 bDisable |= aSelection.Min == aSelection.Max;
990 if ( bDisable )
991 _rSet.DisableItem( nSlot );
994 nSlot = aIter.NextWhich();
999 bool FmTextControlShell::IsActiveControl( bool _bCountRichTextOnly ) const
1001 if ( _bCountRichTextOnly && !m_bActiveControlIsRichText )
1002 return false;
1004 return m_bActiveControl;
1008 void FmTextControlShell::dispose()
1010 if ( IsActiveControl() )
1011 controlDeactivated();
1012 if ( isControllerListening() )
1013 stopControllerListening();
1017 void FmTextControlShell::designModeChanged()
1019 m_rBindings.Invalidate( pTextControlSlots );
1023 void FmTextControlShell::formActivated( const Reference< runtime::XFormController >& _rxController )
1025 #if OSL_DEBUG_LEVEL > 0
1026 SAL_INFO("svx.form", "0x" << OUString::number( reinterpret_cast<sal_IntPtr>(_rxController.get()), 16 ));
1027 #endif
1029 DBG_ASSERT( _rxController.is(), "FmTextControlShell::formActivated: invalid controller!" );
1030 if ( !_rxController.is() )
1031 return;
1033 // sometimes, a form controller notifies activations, even if it's already activated
1034 if ( m_xActiveController == _rxController )
1035 return;
1039 startControllerListening( _rxController );
1040 controlActivated( _rxController->getCurrentControl() );
1042 catch( const Exception& )
1044 DBG_UNHANDLED_EXCEPTION("svx");
1049 void FmTextControlShell::formDeactivated( const Reference< runtime::XFormController >& _rxController )
1051 SAL_INFO("svx.form", "0x" << OUString::number( reinterpret_cast<sal_IntPtr>(_rxController.get()), 16 ));
1053 if ( IsActiveControl() )
1054 controlDeactivated();
1055 if ( isControllerListening() )
1056 stopControllerListening();
1060 void FmTextControlShell::startControllerListening( const Reference< runtime::XFormController >& _rxController )
1062 OSL_PRECOND( _rxController.is(), "FmTextControlShell::startControllerListening: invalid controller!" );
1063 if ( !_rxController.is() )
1064 return;
1066 OSL_PRECOND( !isControllerListening(), "FmTextControlShell::startControllerListening: already listening!" );
1067 if ( isControllerListening() )
1068 stopControllerListening( );
1069 DBG_ASSERT( !isControllerListening(), "FmTextControlShell::startControllerListening: inconsistence!" );
1073 Sequence< Reference< css::awt::XControl > > aControls( _rxController->getControls() );
1074 m_aControlObservers.resize( 0 );
1075 m_aControlObservers.reserve( aControls.getLength() );
1077 std::transform(aControls.begin(), aControls.end(), std::back_inserter(m_aControlObservers),
1078 [this](const Reference< css::awt::XControl >& rControl) -> FocusListenerAdapter {
1079 return FocusListenerAdapter( new FmFocusListenerAdapter( rControl, this ) ); });
1081 catch( const Exception& )
1083 DBG_UNHANDLED_EXCEPTION("svx");
1086 m_xActiveController = _rxController;
1090 void FmTextControlShell::stopControllerListening( )
1092 OSL_PRECOND( isControllerListening(), "FmTextControlShell::stopControllerListening: inconsistence!" );
1094 // dispose all listeners associated with the controls of the active controller
1095 for (auto& rpObserver : m_aControlObservers)
1097 rpObserver->dispose();
1100 FocusListenerAdapters aEmpty;
1101 m_aControlObservers.swap( aEmpty );
1103 m_xActiveController.clear();
1107 void FmTextControlShell::implClearActiveControlRef()
1109 // no more features for this control
1110 for (auto& rFeature : m_aControlFeatures)
1112 rFeature.second->dispose();
1115 ControlFeatures aEmpty;
1116 m_aControlFeatures.swap( aEmpty );
1118 if ( m_aContextMenuObserver.get() )
1120 m_aContextMenuObserver->dispose();
1121 m_aContextMenuObserver = MouseListenerAdapter();
1124 if ( m_xActiveTextComponent.is() )
1126 SAL_INFO("svx.form", "stopping timer for clipboard invalidation" );
1127 m_aClipboardInvalidation.Stop();
1129 // no more active control
1130 m_xActiveControl.clear();
1131 m_xActiveTextComponent.clear();
1132 m_bActiveControlIsReadOnly = true;
1133 m_bActiveControlIsRichText = false;
1134 m_bActiveControl = false;
1138 void FmTextControlShell::controlDeactivated( )
1140 DBG_ASSERT( IsActiveControl(), "FmTextControlShell::controlDeactivated: no active control!" );
1142 m_bActiveControl = false;
1144 m_rBindings.Invalidate( pTextControlSlots );
1148 void FmTextControlShell::controlActivated( const Reference< css::awt::XControl >& _rxControl )
1150 // ensure that all knittings with the previously active control are lost
1151 if ( m_xActiveControl.is() )
1152 implClearActiveControlRef();
1153 DBG_ASSERT( m_aControlFeatures.empty(), "FmTextControlShell::controlActivated: should have no dispatchers when I'm here!" );
1155 #if OSL_DEBUG_LEVEL > 0
1157 Sequence< Reference< css::awt::XControl > > aActiveControls;
1158 if ( m_xActiveController.is() )
1159 aActiveControls = m_xActiveController->getControls();
1161 bool bFoundThisControl = false;
1163 const Reference< css::awt::XControl >* pControls = aActiveControls.getConstArray();
1164 const Reference< css::awt::XControl >* pControlsEnd = pControls + aActiveControls.getLength();
1165 for ( ; ( pControls != pControlsEnd ) && !bFoundThisControl; ++pControls )
1167 if ( *pControls == _rxControl )
1168 bFoundThisControl = true;
1170 DBG_ASSERT( bFoundThisControl, "FmTextControlShell::controlActivated: only controls which belong to the active controller can be activated!" );
1172 #endif
1173 // ask the control for dispatchers for our text-related slots
1174 fillFeatureDispatchers( _rxControl, pTextControlSlots, m_aControlFeatures );
1176 // remember this control
1177 m_xActiveControl = _rxControl;
1178 m_xActiveTextComponent.set(_rxControl, css::uno::UNO_QUERY);
1179 m_bActiveControlIsReadOnly = lcl_determineReadOnly( m_xActiveControl );
1180 m_bActiveControlIsRichText = lcl_isRichText( m_xActiveControl );
1182 // if we found a rich text control, we need context menu support
1183 if ( m_bActiveControlIsRichText )
1185 DBG_ASSERT( nullptr == m_aContextMenuObserver.get(), "FmTextControlShell::controlActivated: already have an observer!" );
1186 m_aContextMenuObserver = MouseListenerAdapter( new FmMouseListenerAdapter( _rxControl, this ) );
1189 if ( m_xActiveTextComponent.is() )
1191 SAL_INFO("svx.form", "starting timer for clipboard invalidation" );
1192 m_aClipboardInvalidation.Start();
1195 m_bActiveControl = true;
1197 m_rBindings.Invalidate( pTextControlSlots );
1199 if ( m_pViewFrame )
1200 m_pViewFrame->UIFeatureChanged();
1202 // don't call the activation handler if we don't have any slots we can serve
1203 // The activation handler is used to put the shell on the top of the dispatcher stack,
1204 // so it's preferred when slots are distributed.
1205 // Note that this is a slight hack, to prevent that we grab slots from the SfxDispatcher
1206 // which should be served by other shells (e.g. Cut/Copy/Paste).
1207 // A real solution would be a forwarding-mechanism for slots: We should be on the top
1208 // if we're active, but if we cannot handle the slot, then we need to tell the dispatcher
1209 // to skip our shell, and pass the slot to the next one. However, this mechanism is not
1210 // not in place in SFX.
1211 // Another possibility would be to have dedicated shells for the slots which we might
1212 // or might not be able to serve. However, this could probably increase the number of
1213 // shells too much (In theory, nearly every slot could have an own shell then).
1215 // #i51621# / 2005-08-19 / frank.schoenheit@sun.com
1216 // bool bHaveAnyServeableSlots = m_xActiveTextComponent.is() || !m_aControlFeatures.empty();
1217 // LEM: not calling m_aControlActivatonHandler causes fdo#63695, so disable this hack for now.
1218 m_aControlActivationHandler.Call( nullptr );
1220 m_bNeedClipboardInvalidation = true;
1224 void FmTextControlShell::fillFeatureDispatchers(const Reference< css::awt::XControl >& _rxControl, SfxSlotId* _pZeroTerminatedSlots,
1225 ControlFeatures& _rDispatchers)
1227 Reference< XDispatchProvider > xProvider( _rxControl, UNO_QUERY );
1228 SfxApplication* pApplication = SfxGetpApp();
1229 DBG_ASSERT( pApplication, "FmTextControlShell::fillFeatureDispatchers: no SfxApplication!" );
1230 if ( xProvider.is() && pApplication )
1232 SfxSlotId* pSlots = _pZeroTerminatedSlots;
1233 while ( *pSlots )
1235 FmTextControlFeature* pDispatcher = implGetFeatureDispatcher( xProvider, pApplication, *pSlots );
1236 if ( pDispatcher )
1237 _rDispatchers.emplace( *pSlots, ControlFeature( pDispatcher ) );
1239 ++pSlots;
1245 FmTextControlFeature* FmTextControlShell::implGetFeatureDispatcher( const Reference< XDispatchProvider >& _rxProvider, SfxApplication const * _pApplication, SfxSlotId _nSlot )
1247 OSL_PRECOND( _rxProvider.is() && _pApplication, "FmTextControlShell::implGetFeatureDispatcher: invalid arg(s)!" );
1248 URL aFeatureURL;
1249 aFeatureURL.Complete = lcl_getUnoSlotName( _nSlot );
1252 if ( !m_xURLTransformer.is() )
1254 m_xURLTransformer = util::URLTransformer::create( ::comphelper::getProcessComponentContext() );
1256 if ( m_xURLTransformer.is() )
1257 m_xURLTransformer->parseStrict( aFeatureURL );
1259 catch( const Exception& )
1261 DBG_UNHANDLED_EXCEPTION("svx");
1263 Reference< XDispatch > xDispatcher = _rxProvider->queryDispatch( aFeatureURL, OUString(), 0xFF );
1264 if ( xDispatcher.is() )
1265 return new FmTextControlFeature( xDispatcher, aFeatureURL, _nSlot, this );
1266 return nullptr;
1270 void FmTextControlShell::Invalidate( SfxSlotId _nSlot )
1272 m_rBindings.Invalidate( _nSlot );
1273 // despite this method being called "Invalidate", we also update here - this gives more immediate
1274 // feedback in the UI
1275 m_rBindings.Update( _nSlot );
1279 void FmTextControlShell::focusGained( const css::awt::FocusEvent& _rEvent )
1281 Reference< css::awt::XControl > xControl( _rEvent.Source, UNO_QUERY );
1283 #if OSL_DEBUG_LEVEL > 0
1284 SAL_INFO("svx.form", "0x" << OUString::number( reinterpret_cast<sal_IntPtr>(xControl.get()), 16 ));
1285 #endif
1287 DBG_ASSERT( xControl.is(), "FmTextControlShell::focusGained: suspicious focus event!" );
1288 if ( xControl.is() )
1289 controlActivated( xControl );
1293 void FmTextControlShell::focusLost( const css::awt::FocusEvent& _rEvent )
1295 Reference< css::awt::XControl > xControl( _rEvent.Source, UNO_QUERY );
1297 #if OSL_DEBUG_LEVEL > 0
1298 SAL_INFO("svx.form", "0x" << OUString::number( reinterpret_cast<sal_IntPtr>(xControl.get()), 16 ));
1299 #endif
1301 m_bActiveControl = false;
1305 void FmTextControlShell::ForgetActiveControl()
1307 implClearActiveControlRef();
1311 void FmTextControlShell::contextMenuRequested()
1313 m_rBindings.GetDispatcher()->ExecutePopup( "formrichtext" );
1320 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */