1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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 .
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/scriptspaceitem.hxx>
28 #include <svx/svxids.hrc>
29 #include <editeng/udlnitem.hxx>
31 #include <com/sun/star/beans/XPropertySet.hpp>
32 #include <com/sun/star/frame/XDispatchProvider.hpp>
33 #include <com/sun/star/container/XChild.hpp>
34 #include <com/sun/star/awt/XFocusListener.hpp>
35 #include <com/sun/star/awt/XMouseListener.hpp>
36 #include <com/sun/star/awt/XWindow.hpp>
37 #include <com/sun/star/util/URLTransformer.hpp>
39 #include <comphelper/processfactory.hxx>
40 #include <cppuhelper/implbase.hxx>
41 #include <sfx2/app.hxx>
42 #include <sfx2/bindings.hxx>
43 #include <sfx2/dispatch.hxx>
44 #include <sfx2/msgpool.hxx>
45 #include <sfx2/msg.hxx>
46 #include <sfx2/objsh.hxx>
47 #include <sfx2/request.hxx>
48 #include <sfx2/sfxuno.hxx>
49 #include <sfx2/viewfrm.hxx>
50 #include <svl/eitem.hxx>
51 #include <svl/itempool.hxx>
52 #include <svl/languageoptions.hxx>
53 #include <svtools/stringtransfer.hxx>
54 #include <svl/whiter.hxx>
55 #include <toolkit/helper/vclunohelper.hxx>
56 #include <tools/debug.hxx>
57 #include <tools/diagnose_ex.h>
58 #include <sal/log.hxx>
59 #include <vcl/svapp.hxx>
60 #include <vcl/window.hxx>
69 using namespace ::com::sun::star
;
70 using namespace ::com::sun::star::uno
;
71 using namespace ::com::sun::star::form
;
72 using namespace ::com::sun::star::form::runtime
;
73 using namespace ::com::sun::star::lang
;
74 using namespace ::com::sun::star::frame
;
75 using namespace ::com::sun::star::util
;
76 using namespace ::com::sun::star::beans
;
77 using namespace ::com::sun::star::container
;
80 typedef sal_uInt16 WhichId
;
83 static SfxSlotId pTextControlSlots
[] =
85 SID_CLIPBOARD_FORMAT_ITEMS
,
90 // SID_ATTR_TABSTOP, /* 2 */
92 SID_ATTR_CHAR_POSTURE
,
94 SID_ATTR_CHAR_SHADOWED
,
95 SID_ATTR_CHAR_WORDLINEMODE
,
96 SID_ATTR_CHAR_CONTOUR
,
97 SID_ATTR_CHAR_STRIKEOUT
,
98 SID_ATTR_CHAR_UNDERLINE
,
99 SID_ATTR_CHAR_FONTHEIGHT
,
101 SID_ATTR_CHAR_KERNING
,
102 SID_ATTR_CHAR_LANGUAGE
, /* 20 */
103 SID_ATTR_CHAR_ESCAPEMENT
,
104 SID_ATTR_PARA_ADJUST
, /* 28 */
105 SID_ATTR_PARA_ADJUST_LEFT
,
106 SID_ATTR_PARA_ADJUST_RIGHT
,
107 SID_ATTR_PARA_ADJUST_CENTER
,
108 SID_ATTR_PARA_ADJUST_BLOCK
,
109 SID_ATTR_PARA_LINESPACE
, /* 33 */
110 SID_ATTR_PARA_LINESPACE_10
,
111 SID_ATTR_PARA_LINESPACE_15
,
112 SID_ATTR_PARA_LINESPACE_20
,
113 SID_ATTR_LRSPACE
, /* 48 */
114 SID_ATTR_ULSPACE
, /* 49 */
115 SID_ATTR_CHAR_AUTOKERN
,
116 SID_SET_SUPER_SCRIPT
,
120 // SID_TEXTDIRECTION_LEFT_TO_RIGHT, /* 907 */
121 // SID_TEXTDIRECTION_TOP_TO_BOTTOM,
122 SID_ATTR_CHAR_SCALEWIDTH
, /* 911 */
123 SID_ATTR_CHAR_RELIEF
,
124 SID_ATTR_PARA_LEFT_TO_RIGHT
, /* 950 */
125 SID_ATTR_PARA_RIGHT_TO_LEFT
,
126 SID_ATTR_CHAR_OVERLINE
,
130 // slots which we are not responsible for on the SfxShell level, but
131 // need to handle during the "paragraph attributes" and/or "character
132 // attributes" dialogs
133 static SfxSlotId pDialogSlots
[] =
136 SID_ATTR_PARA_HANGPUNCTUATION
,
137 SID_ATTR_PARA_FORBIDDEN_RULES
,
138 SID_ATTR_PARA_SCRIPTSPACE
,
139 SID_ATTR_CHAR_LATIN_LANGUAGE
,
140 SID_ATTR_CHAR_CJK_LANGUAGE
,
141 SID_ATTR_CHAR_CTL_LANGUAGE
,
142 SID_ATTR_CHAR_LATIN_FONT
,
143 SID_ATTR_CHAR_CJK_FONT
,
144 SID_ATTR_CHAR_CTL_FONT
,
145 SID_ATTR_CHAR_LATIN_FONTHEIGHT
,
146 SID_ATTR_CHAR_CJK_FONTHEIGHT
,
147 SID_ATTR_CHAR_CTL_FONTHEIGHT
,
148 SID_ATTR_CHAR_LATIN_WEIGHT
,
149 SID_ATTR_CHAR_CJK_WEIGHT
,
150 SID_ATTR_CHAR_CTL_WEIGHT
,
151 SID_ATTR_CHAR_LATIN_POSTURE
,
152 SID_ATTR_CHAR_CJK_POSTURE
,
153 SID_ATTR_CHAR_CTL_POSTURE
,
154 SID_ATTR_CHAR_EMPHASISMARK
,
158 typedef ::cppu::WeakImplHelper
< css::awt::XFocusListener
159 > FmFocusListenerAdapter_Base
;
160 class FmFocusListenerAdapter
: public FmFocusListenerAdapter_Base
163 IFocusObserver
* m_pObserver
;
164 Reference
< css::awt::XWindow
> m_xWindow
;
167 FmFocusListenerAdapter( const Reference
< css::awt::XControl
>& _rxControl
, IFocusObserver
* _pObserver
);
169 // clean up the instance
173 virtual ~FmFocusListenerAdapter() override
;
176 virtual void SAL_CALL
focusGained( const css::awt::FocusEvent
& e
) override
;
177 virtual void SAL_CALL
focusLost( const css::awt::FocusEvent
& e
) override
;
178 virtual void SAL_CALL
disposing( const EventObject
& Source
) override
;
182 FmFocusListenerAdapter::FmFocusListenerAdapter( const Reference
< css::awt::XControl
>& _rxControl
, IFocusObserver
* _pObserver
)
183 :m_pObserver( _pObserver
)
184 ,m_xWindow( _rxControl
, UNO_QUERY
)
187 DBG_ASSERT( m_xWindow
.is(), "FmFocusListenerAdapter::FmFocusListenerAdapter: invalid control!" );
188 osl_atomic_increment( &m_refCount
);
192 if ( m_xWindow
.is() )
193 m_xWindow
->addFocusListener( this );
195 catch( const Exception
& )
197 DBG_UNHANDLED_EXCEPTION("svx");
200 osl_atomic_decrement( &m_refCount
);
204 FmFocusListenerAdapter::~FmFocusListenerAdapter()
212 void FmFocusListenerAdapter::dispose()
214 if ( m_xWindow
.is() )
216 m_xWindow
->removeFocusListener( this );
222 void SAL_CALL
FmFocusListenerAdapter::focusGained( const css::awt::FocusEvent
& e
)
225 m_pObserver
->focusGained( e
);
229 void SAL_CALL
FmFocusListenerAdapter::focusLost( const css::awt::FocusEvent
& e
)
232 m_pObserver
->focusLost( e
);
236 void SAL_CALL
FmFocusListenerAdapter::disposing( const EventObject
& Source
)
238 DBG_ASSERT( Source
.Source
== m_xWindow
, "FmFocusListenerAdapter::disposing: where did this come from?" );
242 typedef ::cppu::WeakImplHelper
< css::awt::XMouseListener
243 > FmMouseListenerAdapter_Base
;
244 class FmMouseListenerAdapter
: public FmMouseListenerAdapter_Base
247 IContextRequestObserver
* m_pObserver
;
248 Reference
< css::awt::XWindow
> m_xWindow
;
251 FmMouseListenerAdapter( const Reference
< css::awt::XControl
>& _rxControl
, IContextRequestObserver
* _pObserver
);
253 // clean up the instance
257 virtual ~FmMouseListenerAdapter() override
;
260 virtual void SAL_CALL
mousePressed( const css::awt::MouseEvent
& e
) override
;
261 virtual void SAL_CALL
mouseReleased( const css::awt::MouseEvent
& e
) override
;
262 virtual void SAL_CALL
mouseEntered( const css::awt::MouseEvent
& e
) override
;
263 virtual void SAL_CALL
mouseExited( const css::awt::MouseEvent
& e
) override
;
264 virtual void SAL_CALL
disposing( const EventObject
& Source
) override
;
267 FmMouseListenerAdapter::FmMouseListenerAdapter( const Reference
< css::awt::XControl
>& _rxControl
, IContextRequestObserver
* _pObserver
)
268 :m_pObserver( _pObserver
)
269 ,m_xWindow( _rxControl
, UNO_QUERY
)
272 DBG_ASSERT( m_xWindow
.is(), "FmMouseListenerAdapter::FmMouseListenerAdapter: invalid control!" );
273 osl_atomic_increment( &m_refCount
);
277 if ( m_xWindow
.is() )
278 m_xWindow
->addMouseListener( this );
280 catch( const Exception
& )
282 DBG_UNHANDLED_EXCEPTION("svx");
285 osl_atomic_decrement( &m_refCount
);
289 FmMouseListenerAdapter::~FmMouseListenerAdapter()
297 void FmMouseListenerAdapter::dispose()
299 if ( m_xWindow
.is() )
301 m_xWindow
->removeMouseListener( this );
307 void SAL_CALL
FmMouseListenerAdapter::mousePressed( const css::awt::MouseEvent
& _rEvent
)
309 SolarMutexGuard aGuard
;
310 // is this a request for a context menu?
311 if ( _rEvent
.PopupTrigger
)
314 m_pObserver
->contextMenuRequested();
319 void SAL_CALL
FmMouseListenerAdapter::mouseReleased( const css::awt::MouseEvent
& /*e*/ )
325 void SAL_CALL
FmMouseListenerAdapter::mouseEntered( const css::awt::MouseEvent
& /*e*/ )
331 void SAL_CALL
FmMouseListenerAdapter::mouseExited( const css::awt::MouseEvent
& /*e*/ )
337 void SAL_CALL
FmMouseListenerAdapter::disposing( const EventObject
& Source
)
339 DBG_ASSERT( Source
.Source
== m_xWindow
, "FmMouseListenerAdapter::disposing: where did this come from?" );
344 //= FmTextControlShell
350 void lcl_translateUnoStateToItem( SfxSlotId _nSlot
, const Any
& _rUnoState
, SfxItemSet
& _rSet
)
352 WhichId nWhich
= _rSet
.GetPool()->GetWhich( _nSlot
);
353 if ( !_rUnoState
.hasValue() )
355 if ( ( _nSlot
!= SID_CUT
)
356 && ( _nSlot
!= SID_COPY
)
357 && ( _nSlot
!= SID_PASTE
)
360 _rSet
.InvalidateItem( nWhich
);
365 switch ( _rUnoState
.getValueType().getTypeClass() )
367 case TypeClass_BOOLEAN
:
370 _rUnoState
>>= bState
;
371 if ( _nSlot
== SID_ATTR_PARA_SCRIPTSPACE
)
372 _rSet
.Put( SvxScriptSpaceItem( bState
, nWhich
) );
374 _rSet
.Put( SfxBoolItem( nWhich
, bState
) );
380 Sequence
< PropertyValue
> aComplexState
;
381 if ( _rUnoState
>>= aComplexState
)
383 if ( !aComplexState
.hasElements() )
384 _rSet
.InvalidateItem( nWhich
);
387 SfxAllItemSet
aAllItems( _rSet
);
388 TransformParameters( _nSlot
, aComplexState
, aAllItems
);
389 const SfxPoolItem
* pTransformed
= aAllItems
.GetItem( nWhich
);
390 OSL_ENSURE( pTransformed
, "lcl_translateUnoStateToItem: non-empty parameter sequence leading to empty item?" );
392 _rSet
.Put( *pTransformed
);
394 _rSet
.InvalidateItem( nWhich
);
399 OSL_FAIL( "lcl_translateUnoStateToItem: invalid state!" );
407 OUString
lcl_getUnoSlotName( SfxSlotId _nSlotId
)
409 OUString sSlotUnoName
;
411 SfxSlotPool
& rSlotPool
= SfxSlotPool::GetSlotPool();
412 const SfxSlot
* pSlot
= rSlotPool
.GetSlot( _nSlotId
);
414 const char* pAsciiUnoName
= nullptr;
417 pAsciiUnoName
= pSlot
->GetUnoName();
421 // some hard-coded slots, which do not have a UNO name at SFX level, but which
422 // we nevertheless need to transport via UNO mechanisms, so we need a name
425 case SID_ATTR_PARA_HANGPUNCTUATION
: pAsciiUnoName
= "AllowHangingPunctuation"; break;
426 case SID_ATTR_PARA_FORBIDDEN_RULES
: pAsciiUnoName
= "ApplyForbiddenCharacterRules"; break;
427 case SID_ATTR_PARA_SCRIPTSPACE
: pAsciiUnoName
= "UseScriptSpacing"; break;
433 sSlotUnoName
= ".uno:" + OUString::createFromAscii( pAsciiUnoName
);
437 SAL_WARN( "svx", "lcl_getUnoSlotName: invalid slot id, or invalid slot, or no UNO name! "
438 "(slot id: " << _nSlotId
<< ")");
444 bool lcl_determineReadOnly( const Reference
< css::awt::XControl
>& _rxControl
)
446 bool bIsReadOnlyModel
= true;
449 Reference
< XPropertySet
> xModelProps
;
450 if ( _rxControl
.is() )
451 xModelProps
.set(_rxControl
->getModel(), css::uno::UNO_QUERY
);
452 Reference
< XPropertySetInfo
> xModelPropInfo
;
453 if ( xModelProps
.is() )
454 xModelPropInfo
= xModelProps
->getPropertySetInfo();
456 if ( !xModelPropInfo
.is() || !xModelPropInfo
->hasPropertyByName( FM_PROP_READONLY
) )
457 bIsReadOnlyModel
= true;
460 bool bReadOnly
= true;
461 xModelProps
->getPropertyValue( FM_PROP_READONLY
) >>= bReadOnly
;
462 bIsReadOnlyModel
= bReadOnly
;
465 catch( const Exception
& )
467 DBG_UNHANDLED_EXCEPTION("svx");
469 return bIsReadOnlyModel
;
473 vcl::Window
* lcl_getWindow( const Reference
< css::awt::XControl
>& _rxControl
)
475 vcl::Window
* pWindow
= nullptr;
478 Reference
< css::awt::XWindowPeer
> xControlPeer
;
479 if ( _rxControl
.is() )
480 xControlPeer
= _rxControl
->getPeer();
481 if ( xControlPeer
.is() )
482 pWindow
= VCLUnoHelper::GetWindow( xControlPeer
);
484 catch( const Exception
& )
486 DBG_UNHANDLED_EXCEPTION("svx");
493 bool lcl_isRichText( const Reference
< css::awt::XControl
>& _rxControl
)
495 if ( !_rxControl
.is() )
498 bool bIsRichText
= false;
501 Reference
< XPropertySet
> xModelProps( _rxControl
->getModel(), UNO_QUERY
);
502 Reference
< XPropertySetInfo
> xPSI
;
503 if ( xModelProps
.is() )
504 xPSI
= xModelProps
->getPropertySetInfo();
505 OUString sRichTextPropertyName
= "RichText";
506 if ( xPSI
.is() && xPSI
->hasPropertyByName( sRichTextPropertyName
) )
508 OSL_VERIFY( xModelProps
->getPropertyValue( sRichTextPropertyName
) >>= bIsRichText
);
511 catch( const Exception
& )
513 DBG_UNHANDLED_EXCEPTION("svx");
520 FmTextControlShell::FmTextControlShell( SfxViewFrame
* _pFrame
)
521 :m_bActiveControl( false )
522 ,m_bActiveControlIsReadOnly( true )
523 ,m_bActiveControlIsRichText( false )
524 ,m_pViewFrame( _pFrame
)
525 ,m_rBindings( _pFrame
->GetBindings() )
526 ,m_bNeedClipboardInvalidation( true )
528 m_aClipboardInvalidation
.SetInvokeHandler( LINK( this, FmTextControlShell
, OnInvalidateClipboard
) );
529 m_aClipboardInvalidation
.SetTimeout( 200 );
533 FmTextControlShell::~FmTextControlShell()
539 IMPL_LINK_NOARG( FmTextControlShell
, OnInvalidateClipboard
, Timer
*, void )
541 if ( m_bNeedClipboardInvalidation
)
543 SAL_INFO("svx.form", "invalidating clipboard slots" );
544 m_rBindings
.Invalidate( SID_CUT
);
545 m_rBindings
.Invalidate( SID_COPY
);
546 m_rBindings
.Invalidate( SID_PASTE
);
547 m_bNeedClipboardInvalidation
= false;
552 void FmTextControlShell::transferFeatureStatesToItemSet( ControlFeatures
& _rDispatchers
, SfxAllItemSet
& _rSet
, bool _bTranslateLatin
)
554 SfxItemPool
& rPool
= *_rSet
.GetPool();
556 for (const auto& rFeature
: _rDispatchers
)
558 SfxSlotId
nSlotId( rFeature
.first
);
559 #if OSL_DEBUG_LEVEL > 0
560 OUString sUnoSlotName
;
562 sUnoSlotName
= lcl_getUnoSlotName( nSlotId
);
564 sUnoSlotName
= "<unknown>";
565 OString sUnoSlotNameAscii
= "\"" +
566 OString( sUnoSlotName
.getStr(), sUnoSlotName
.getLength(), RTL_TEXTENCODING_ASCII_US
) +
570 if ( _bTranslateLatin
)
572 // A rich text control offers a dispatcher for the "Font" slot/feature.
573 // Sadly, the semantics of the dispatches is that the feature "Font" depends
574 // on the current cursor position: If it's on latin text, it's the "latin font"
575 // which is set up at the control. If it's on CJK text, it's the "CJK font", and
576 // equivalent for "CTL font".
577 // The same holds for some other font related features/slots.
578 // Thus, we have separate dispatches for "Latin Font", "Latin Font Size", etc,
579 // which are only "virtual", in a sense that there exist no item with this id.
580 // So when we encounter such a dispatcher for, say, "Latin Font", we need to
581 // put an item into the set which has the "Font" id.
585 case SID_ATTR_CHAR_LATIN_FONT
: nSlotId
= SID_ATTR_CHAR_FONT
; break;
586 case SID_ATTR_CHAR_LATIN_FONTHEIGHT
:nSlotId
= SID_ATTR_CHAR_FONTHEIGHT
; break;
587 case SID_ATTR_CHAR_LATIN_LANGUAGE
: nSlotId
= SID_ATTR_CHAR_LANGUAGE
; break;
588 case SID_ATTR_CHAR_LATIN_POSTURE
: nSlotId
= SID_ATTR_CHAR_POSTURE
; break;
589 case SID_ATTR_CHAR_LATIN_WEIGHT
: nSlotId
= SID_ATTR_CHAR_WEIGHT
; break;
593 WhichId nWhich
= rPool
.GetWhich( nSlotId
);
594 bool bIsInPool
= rPool
.IsInRange( nWhich
);
597 #if OSL_DEBUG_LEVEL > 0
598 bool bFeatureIsEnabled
= rFeature
.second
->isFeatureEnabled();
599 OString sMessage
= "found a feature state for " + sUnoSlotNameAscii
;
600 if ( !bFeatureIsEnabled
)
601 sMessage
+= " (disabled)";
602 SAL_INFO("svx.form", sMessage
);
605 lcl_translateUnoStateToItem( nSlotId
, rFeature
.second
->getFeatureState(), _rSet
);
607 #if OSL_DEBUG_LEVEL > 0
610 SAL_WARN("svx.form", "found a feature state for " << sUnoSlotNameAscii
<< ", but could not translate it into an item!" );
617 void FmTextControlShell::executeAttributeDialog( AttributeSet _eSet
, SfxRequest
& rReq
)
619 const SvxFontListItem
* pFontList
= dynamic_cast<const SvxFontListItem
*>( m_pViewFrame
->GetObjectShell()->GetItem( SID_ATTR_CHAR_FONTLIST
) );
620 DBG_ASSERT( pFontList
, "FmTextControlShell::executeAttributeDialog: no font list item!" );
624 rtl::Reference
<SfxItemPool
> pPool(EditEngine::CreatePool());
625 pPool
->FreezeIdRanges();
626 std::optional
< SfxItemSet
> xPureItems(( SfxItemSet( *pPool
) ));
628 // put the current states of the items into the set
629 std::optional
<SfxAllItemSet
> xCurrentItems(( SfxAllItemSet( *xPureItems
) ));
630 transferFeatureStatesToItemSet( m_aControlFeatures
, *xCurrentItems
, false );
632 // additional items, which we are not responsible for at the SfxShell level,
633 // but which need to be forwarded to the dialog, anyway
634 ControlFeatures aAdditionalFestures
;
635 fillFeatureDispatchers( m_xActiveControl
, pDialogSlots
, aAdditionalFestures
);
636 transferFeatureStatesToItemSet( aAdditionalFestures
, *xCurrentItems
, true );
638 std::unique_ptr
<SfxTabDialogController
> xDialog
;
639 if (_eSet
== eCharAttribs
)
640 xDialog
= std::make_unique
<TextControlCharAttribDialog
>(rReq
.GetFrameWeld(), *xCurrentItems
, *pFontList
);
642 xDialog
= std::make_unique
<TextControlParaAttribDialog
>(rReq
.GetFrameWeld(), *xCurrentItems
);
643 if ( RET_OK
== xDialog
->run() )
645 const SfxItemSet
& rModifiedItems
= *xDialog
->GetOutputItemSet();
646 for ( WhichId nWhich
= pPool
->GetFirstWhich(); nWhich
<= pPool
->GetLastWhich(); ++nWhich
)
648 if ( rModifiedItems
.GetItemState( nWhich
) == SfxItemState::SET
)
650 SfxSlotId nSlotForItemSet
= pPool
->GetSlotId( nWhich
);
651 const SfxPoolItem
* pModifiedItem
= rModifiedItems
.GetItem( nWhich
);
654 SfxSlotId nSlotForDispatcher
= nSlotForItemSet
;
655 switch ( nSlotForDispatcher
)
657 case SID_ATTR_CHAR_FONT
: nSlotForDispatcher
= SID_ATTR_CHAR_LATIN_FONT
; break;
658 case SID_ATTR_CHAR_FONTHEIGHT
:nSlotForDispatcher
= SID_ATTR_CHAR_LATIN_FONTHEIGHT
; break;
659 case SID_ATTR_CHAR_LANGUAGE
: nSlotForDispatcher
= SID_ATTR_CHAR_LATIN_LANGUAGE
; break;
660 case SID_ATTR_CHAR_POSTURE
: nSlotForDispatcher
= SID_ATTR_CHAR_LATIN_POSTURE
; break;
661 case SID_ATTR_CHAR_WEIGHT
: nSlotForDispatcher
= SID_ATTR_CHAR_LATIN_WEIGHT
; break;
664 // do we already have a dispatcher for this slot/feature?
665 ControlFeatures::const_iterator aFeaturePos
= m_aControlFeatures
.find( nSlotForDispatcher
);
666 bool bFound
= aFeaturePos
!= m_aControlFeatures
.end( );
670 aFeaturePos
= aAdditionalFestures
.find( nSlotForDispatcher
);
671 bFound
= aFeaturePos
!= aAdditionalFestures
.end( );
676 Sequence
< PropertyValue
> aArgs
;
677 // temporarily put the modified item into a "clean" set,
678 // and let TransformItems calc the respective UNO parameters
679 xPureItems
->Put( *pModifiedItem
);
680 TransformItems( nSlotForItemSet
, *xPureItems
, aArgs
);
681 xPureItems
->ClearItem( nWhich
);
683 if ( ( nSlotForItemSet
== SID_ATTR_PARA_HANGPUNCTUATION
)
684 || ( nSlotForItemSet
== SID_ATTR_PARA_FORBIDDEN_RULES
)
685 || ( nSlotForItemSet
== SID_ATTR_PARA_SCRIPTSPACE
)
688 // these are no UNO slots, they need special handling since TransformItems cannot
690 DBG_ASSERT( !aArgs
.hasElements(), "FmTextControlShell::executeAttributeDialog: these are no UNO slots - are they?" );
692 const SfxBoolItem
* pBoolItem
= dynamic_cast<const SfxBoolItem
*>( pModifiedItem
);
693 DBG_ASSERT( pBoolItem
, "FmTextControlShell::executeAttributeDialog: no bool item?!" );
697 aArgs
[ 0 ].Name
= "Enable";
698 aArgs
[ 0 ].Value
<<= pBoolItem
->GetValue();
703 aFeaturePos
->second
->dispatch( aArgs
);
705 #if OSL_DEBUG_LEVEL > 0
708 OUString sUnoSlotName
= lcl_getUnoSlotName( nSlotForItemSet
);
709 if ( sUnoSlotName
.isEmpty() )
710 sUnoSlotName
= "unknown (no SfxSlot)";
711 SAL_WARN( "svx", "FmTextControShell::executeAttributeDialog: Could not handle the following item:"
712 "\n SlotID: " << nSlotForItemSet
713 << "\n WhichID: " << nWhich
714 << "\n UNO name: " << sUnoSlotName
);
719 rReq
.Done( rModifiedItems
);
723 xCurrentItems
.reset();
729 void FmTextControlShell::executeSelectAll( )
733 if ( m_xActiveTextComponent
.is() )
735 sal_Int32 nTextLen
= m_xActiveTextComponent
->getText().getLength();
736 m_xActiveTextComponent
->setSelection( css::awt::Selection( 0, nTextLen
) );
739 catch( const Exception
& )
741 DBG_UNHANDLED_EXCEPTION("svx");
746 void FmTextControlShell::executeClipboardSlot( SfxSlotId _nSlot
)
750 if ( m_xActiveTextComponent
.is() )
757 OUString
sSelectedText( m_xActiveTextComponent
->getSelectedText() );
758 ::svt::OStringTransfer::CopyString( sSelectedText
, lcl_getWindow( m_xActiveControl
) );
759 if ( SID_CUT
== _nSlot
)
761 css::awt::Selection
aSelection( m_xActiveTextComponent
->getSelection() );
762 m_xActiveTextComponent
->insertText( aSelection
, OUString() );
768 OUString sClipboardContent
;
769 OSL_VERIFY( ::svt::OStringTransfer::PasteString( sClipboardContent
, lcl_getWindow( m_xActiveControl
) ) );
770 css::awt::Selection
aSelection( m_xActiveTextComponent
->getSelection() );
771 m_xActiveTextComponent
->insertText( aSelection
, sClipboardContent
);
775 OSL_FAIL( "FmTextControlShell::executeClipboardSlot: invalid slot!" );
779 catch( const Exception
& )
781 DBG_UNHANDLED_EXCEPTION("svx");
786 void FmTextControlShell::ExecuteTextAttribute( SfxRequest
& _rReq
)
788 SfxSlotId nSlot
= _rReq
.GetSlot();
790 ControlFeatures::const_iterator aFeaturePos
= m_aControlFeatures
.find( nSlot
);
791 if ( aFeaturePos
== m_aControlFeatures
.end() )
797 executeAttributeDialog( eCharAttribs
, _rReq
);
801 executeAttributeDialog( eParaAttribs
, _rReq
);
811 executeClipboardSlot( nSlot
);
815 DBG_ASSERT( aFeaturePos
!= m_aControlFeatures
.end(), "FmTextControShell::ExecuteTextAttribute: I have no such dispatcher, and cannot handle it at all!" );
821 // slots which are dispatched to the control
825 case SID_ATTR_CHAR_STRIKEOUT
:
826 case SID_ATTR_CHAR_UNDERLINE
:
827 case SID_ATTR_CHAR_OVERLINE
:
829 SfxItemSet
aToggled( *_rReq
.GetArgs() );
831 lcl_translateUnoStateToItem( nSlot
, aFeaturePos
->second
->getFeatureState(), aToggled
);
832 WhichId nWhich
= aToggled
.GetPool()->GetWhich( nSlot
);
833 const SfxPoolItem
* pItem
= aToggled
.GetItem( nWhich
);
834 if ( ( SID_ATTR_CHAR_UNDERLINE
== nSlot
) || ( SID_ATTR_CHAR_OVERLINE
== nSlot
) )
836 const SvxTextLineItem
* pTextLine
= dynamic_cast<const SvxTextLineItem
*>( pItem
);
837 DBG_ASSERT( pTextLine
, "FmTextControlShell::ExecuteTextAttribute: ooops - no underline/overline item!" );
840 FontLineStyle eTL
= pTextLine
->GetLineStyle();
841 if ( SID_ATTR_CHAR_UNDERLINE
== nSlot
) {
842 aToggled
.Put( SvxUnderlineItem( eTL
== LINESTYLE_SINGLE
? LINESTYLE_NONE
: LINESTYLE_SINGLE
, nWhich
) );
844 aToggled
.Put( SvxOverlineItem( eTL
== LINESTYLE_SINGLE
? LINESTYLE_NONE
: LINESTYLE_SINGLE
, nWhich
) );
850 const SvxCrossedOutItem
* pCrossedOut
= dynamic_cast<const SvxCrossedOutItem
*>( pItem
);
851 DBG_ASSERT( pCrossedOut
, "FmTextControlShell::ExecuteTextAttribute: ooops - no CrossedOut item!" );
854 FontStrikeout eFS
= pCrossedOut
->GetStrikeout();
855 aToggled
.Put( SvxCrossedOutItem( eFS
== STRIKEOUT_SINGLE
? STRIKEOUT_NONE
: STRIKEOUT_SINGLE
, nWhich
) );
859 Sequence
< PropertyValue
> aArguments
;
860 TransformItems( nSlot
, aToggled
, aArguments
);
861 aFeaturePos
->second
->dispatch( aArguments
);
865 case SID_ATTR_CHAR_FONTHEIGHT
:
866 case SID_ATTR_CHAR_FONT
:
867 case SID_ATTR_CHAR_POSTURE
:
868 case SID_ATTR_CHAR_WEIGHT
:
869 case SID_ATTR_CHAR_SHADOWED
:
870 case SID_ATTR_CHAR_CONTOUR
:
871 case SID_SET_SUPER_SCRIPT
:
872 case SID_SET_SUB_SCRIPT
:
874 const SfxItemSet
* pArgs
= _rReq
.GetArgs();
875 Sequence
< PropertyValue
> aArgs
;
877 TransformItems( nSlot
, *pArgs
, aArgs
);
878 aFeaturePos
->second
->dispatch( aArgs
);
883 if ( aFeaturePos
->second
->isFeatureEnabled() )
884 aFeaturePos
->second
->dispatch();
892 void FmTextControlShell::GetTextAttributeState( SfxItemSet
& _rSet
)
894 SfxWhichIter
aIter( _rSet
);
895 sal_uInt16 nSlot
= aIter
.FirstWhich();
898 if ( ( nSlot
== SID_ATTR_PARA_LEFT_TO_RIGHT
)
899 || ( nSlot
== SID_ATTR_PARA_RIGHT_TO_LEFT
)
902 if ( !SvtLanguageOptions().IsCTLFontEnabled() )
904 _rSet
.DisableItem( nSlot
);
905 nSlot
= aIter
.NextWhich();
910 ControlFeatures::const_iterator aFeaturePos
= m_aControlFeatures
.find( nSlot
);
911 if ( aFeaturePos
!= m_aControlFeatures
.end() )
913 if ( aFeaturePos
->second
->isFeatureEnabled() )
914 lcl_translateUnoStateToItem( nSlot
, aFeaturePos
->second
->getFeatureState(), _rSet
);
916 _rSet
.DisableItem( nSlot
);
920 bool bDisable
= false;
922 bool bNeedWriteableControl
= false;
923 bool bNeedTextComponent
= false;
924 bool bNeedSelection
= false;
930 bDisable
|= m_aControlFeatures
.empty();
931 bNeedWriteableControl
= true;
935 bNeedSelection
= true;
936 bNeedTextComponent
= true;
937 bNeedWriteableControl
= true;
938 SAL_INFO("svx.form", "need to invalidate again" );
939 m_bNeedClipboardInvalidation
= true;
944 vcl::Window
* pActiveControlVCLWindow
= lcl_getWindow( m_xActiveControl
);
945 if ( pActiveControlVCLWindow
)
947 TransferableDataHelper
aDataHelper( TransferableDataHelper::CreateFromSystemClipboard( pActiveControlVCLWindow
) );
948 bDisable
|= !aDataHelper
.HasFormat( SotClipboardFormatId::STRING
);
953 bNeedTextComponent
= true;
954 bNeedWriteableControl
= true;
959 bNeedTextComponent
= true;
960 bNeedSelection
= true;
964 bNeedTextComponent
= true;
968 // slot is unknown at all
972 SAL_WARN_IF( bNeedSelection
&& !bNeedTextComponent
, "svx.form", "FmTextControlShell::GetTextAttributeState: bNeedSelection should imply bNeedTextComponent!" );
974 if ( !bDisable
&& bNeedWriteableControl
)
975 bDisable
|= !IsActiveControl( ) || m_bActiveControlIsReadOnly
;
977 if ( !bDisable
&& bNeedTextComponent
)
978 bDisable
|= !m_xActiveTextComponent
.is();
980 if ( !bDisable
&& bNeedSelection
)
982 css::awt::Selection aSelection
= m_xActiveTextComponent
->getSelection();
983 bDisable
|= aSelection
.Min
== aSelection
.Max
;
987 _rSet
.DisableItem( nSlot
);
990 nSlot
= aIter
.NextWhich();
995 bool FmTextControlShell::IsActiveControl( bool _bCountRichTextOnly
) const
997 if ( _bCountRichTextOnly
&& !m_bActiveControlIsRichText
)
1000 return m_bActiveControl
;
1004 void FmTextControlShell::dispose()
1006 if ( IsActiveControl() )
1007 controlDeactivated();
1008 if ( isControllerListening() )
1009 stopControllerListening();
1013 void FmTextControlShell::designModeChanged()
1015 m_rBindings
.Invalidate( pTextControlSlots
);
1019 void FmTextControlShell::formActivated( const Reference
< runtime::XFormController
>& _rxController
)
1021 #if OSL_DEBUG_LEVEL > 0
1022 SAL_INFO("svx.form", "0x" << OUString::number( reinterpret_cast<sal_IntPtr
>(_rxController
.get()), 16 ));
1025 DBG_ASSERT( _rxController
.is(), "FmTextControlShell::formActivated: invalid controller!" );
1026 if ( !_rxController
.is() )
1029 // sometimes, a form controller notifies activations, even if it's already activated
1030 if ( m_xActiveController
== _rxController
)
1035 startControllerListening( _rxController
);
1036 controlActivated( _rxController
->getCurrentControl() );
1038 catch( const Exception
& )
1040 DBG_UNHANDLED_EXCEPTION("svx");
1045 void FmTextControlShell::formDeactivated( const Reference
< runtime::XFormController
>& _rxController
)
1047 SAL_INFO("svx.form", "0x" << OUString::number( reinterpret_cast<sal_IntPtr
>(_rxController
.get()), 16 ));
1049 if ( IsActiveControl() )
1050 controlDeactivated();
1051 if ( isControllerListening() )
1052 stopControllerListening();
1056 void FmTextControlShell::startControllerListening( const Reference
< runtime::XFormController
>& _rxController
)
1058 OSL_PRECOND( _rxController
.is(), "FmTextControlShell::startControllerListening: invalid controller!" );
1059 if ( !_rxController
.is() )
1062 OSL_PRECOND( !isControllerListening(), "FmTextControlShell::startControllerListening: already listening!" );
1063 if ( isControllerListening() )
1064 stopControllerListening( );
1065 DBG_ASSERT( !isControllerListening(), "FmTextControlShell::startControllerListening: inconsistence!" );
1069 Sequence
< Reference
< css::awt::XControl
> > aControls( _rxController
->getControls() );
1070 m_aControlObservers
.resize( 0 );
1071 m_aControlObservers
.reserve( aControls
.getLength() );
1073 std::transform(aControls
.begin(), aControls
.end(), std::back_inserter(m_aControlObservers
),
1074 [this](const Reference
< css::awt::XControl
>& rControl
) -> FocusListenerAdapter
{
1075 return FocusListenerAdapter( new FmFocusListenerAdapter( rControl
, this ) ); });
1077 catch( const Exception
& )
1079 DBG_UNHANDLED_EXCEPTION("svx");
1082 m_xActiveController
= _rxController
;
1086 void FmTextControlShell::stopControllerListening( )
1088 OSL_PRECOND( isControllerListening(), "FmTextControlShell::stopControllerListening: inconsistence!" );
1090 // dispose all listeners associated with the controls of the active controller
1091 for (auto& rpObserver
: m_aControlObservers
)
1093 rpObserver
->dispose();
1096 FocusListenerAdapters().swap(m_aControlObservers
);
1098 m_xActiveController
.clear();
1102 void FmTextControlShell::implClearActiveControlRef()
1104 // no more features for this control
1105 for (auto& rFeature
: m_aControlFeatures
)
1107 rFeature
.second
->dispose();
1110 ControlFeatures().swap(m_aControlFeatures
);
1112 if ( m_aContextMenuObserver
)
1114 m_aContextMenuObserver
->dispose();
1115 m_aContextMenuObserver
= MouseListenerAdapter();
1118 if ( m_xActiveTextComponent
.is() )
1120 SAL_INFO("svx.form", "stopping timer for clipboard invalidation" );
1121 m_aClipboardInvalidation
.Stop();
1123 // no more active control
1124 m_xActiveControl
.clear();
1125 m_xActiveTextComponent
.clear();
1126 m_bActiveControlIsReadOnly
= true;
1127 m_bActiveControlIsRichText
= false;
1128 m_bActiveControl
= false;
1132 void FmTextControlShell::controlDeactivated( )
1134 DBG_ASSERT( IsActiveControl(), "FmTextControlShell::controlDeactivated: no active control!" );
1136 m_bActiveControl
= false;
1138 m_rBindings
.Invalidate( pTextControlSlots
);
1142 void FmTextControlShell::controlActivated( const Reference
< css::awt::XControl
>& _rxControl
)
1144 // ensure that all knittings with the previously active control are lost
1145 if ( m_xActiveControl
.is() )
1146 implClearActiveControlRef();
1147 DBG_ASSERT( m_aControlFeatures
.empty(), "FmTextControlShell::controlActivated: should have no dispatchers when I'm here!" );
1149 #if OSL_DEBUG_LEVEL > 0
1151 Sequence
< Reference
< css::awt::XControl
> > aActiveControls
;
1152 if ( m_xActiveController
.is() )
1153 aActiveControls
= m_xActiveController
->getControls();
1155 bool bFoundThisControl
= false;
1157 const Reference
< css::awt::XControl
>* pControls
= aActiveControls
.getConstArray();
1158 const Reference
< css::awt::XControl
>* pControlsEnd
= pControls
+ aActiveControls
.getLength();
1159 for ( ; ( pControls
!= pControlsEnd
) && !bFoundThisControl
; ++pControls
)
1161 if ( *pControls
== _rxControl
)
1162 bFoundThisControl
= true;
1164 DBG_ASSERT( bFoundThisControl
, "FmTextControlShell::controlActivated: only controls which belong to the active controller can be activated!" );
1167 // ask the control for dispatchers for our text-related slots
1168 fillFeatureDispatchers( _rxControl
, pTextControlSlots
, m_aControlFeatures
);
1170 // remember this control
1171 m_xActiveControl
= _rxControl
;
1172 m_xActiveTextComponent
.set(_rxControl
, css::uno::UNO_QUERY
);
1173 m_bActiveControlIsReadOnly
= lcl_determineReadOnly( m_xActiveControl
);
1174 m_bActiveControlIsRichText
= lcl_isRichText( m_xActiveControl
);
1176 // if we found a rich text control, we need context menu support
1177 if ( m_bActiveControlIsRichText
)
1179 DBG_ASSERT( !m_aContextMenuObserver
, "FmTextControlShell::controlActivated: already have an observer!" );
1180 m_aContextMenuObserver
= MouseListenerAdapter( new FmMouseListenerAdapter( _rxControl
, this ) );
1183 if ( m_xActiveTextComponent
.is() )
1185 SAL_INFO("svx.form", "starting timer for clipboard invalidation" );
1186 m_aClipboardInvalidation
.Start();
1189 m_bActiveControl
= true;
1191 m_rBindings
.Invalidate( pTextControlSlots
);
1194 m_pViewFrame
->UIFeatureChanged();
1196 // don't call the activation handler if we don't have any slots we can serve
1197 // The activation handler is used to put the shell on the top of the dispatcher stack,
1198 // so it's preferred when slots are distributed.
1199 // Note that this is a slight hack, to prevent that we grab slots from the SfxDispatcher
1200 // which should be served by other shells (e.g. Cut/Copy/Paste).
1201 // A real solution would be a forwarding-mechanism for slots: We should be on the top
1202 // if we're active, but if we cannot handle the slot, then we need to tell the dispatcher
1203 // to skip our shell, and pass the slot to the next one. However, this mechanism is not
1204 // not in place in SFX.
1205 // Another possibility would be to have dedicated shells for the slots which we might
1206 // or might not be able to serve. However, this could probably increase the number of
1207 // shells too much (In theory, nearly every slot could have an own shell then).
1209 // #i51621# / 2005-08-19 / frank.schoenheit@sun.com
1210 // bool bHaveAnyServeableSlots = m_xActiveTextComponent.is() || !m_aControlFeatures.empty();
1211 // LEM: not calling m_aControlActivatonHandler causes fdo#63695, so disable this hack for now.
1212 m_aControlActivationHandler
.Call( nullptr );
1214 m_bNeedClipboardInvalidation
= true;
1218 void FmTextControlShell::fillFeatureDispatchers(const Reference
< css::awt::XControl
>& _rxControl
, SfxSlotId
* _pZeroTerminatedSlots
,
1219 ControlFeatures
& _rDispatchers
)
1221 Reference
< XDispatchProvider
> xProvider( _rxControl
, UNO_QUERY
);
1222 SfxApplication
* pApplication
= SfxGetpApp();
1223 DBG_ASSERT( pApplication
, "FmTextControlShell::fillFeatureDispatchers: no SfxApplication!" );
1224 if ( xProvider
.is() && pApplication
)
1226 SfxSlotId
* pSlots
= _pZeroTerminatedSlots
;
1229 rtl::Reference
<FmTextControlFeature
> pDispatcher
= implGetFeatureDispatcher( xProvider
, pApplication
, *pSlots
);
1231 _rDispatchers
.emplace( *pSlots
, pDispatcher
);
1239 rtl::Reference
<FmTextControlFeature
> FmTextControlShell::implGetFeatureDispatcher( const Reference
< XDispatchProvider
>& _rxProvider
, SfxApplication
const * _pApplication
, SfxSlotId _nSlot
)
1241 OSL_PRECOND( _rxProvider
.is() && _pApplication
, "FmTextControlShell::implGetFeatureDispatcher: invalid arg(s)!" );
1243 aFeatureURL
.Complete
= lcl_getUnoSlotName( _nSlot
);
1246 if ( !m_xURLTransformer
.is() )
1248 m_xURLTransformer
= util::URLTransformer::create( ::comphelper::getProcessComponentContext() );
1250 if ( m_xURLTransformer
.is() )
1251 m_xURLTransformer
->parseStrict( aFeatureURL
);
1253 catch( const Exception
& )
1255 DBG_UNHANDLED_EXCEPTION("svx");
1257 Reference
< XDispatch
> xDispatcher
= _rxProvider
->queryDispatch( aFeatureURL
, OUString(), 0xFF );
1258 if ( xDispatcher
.is() )
1259 return new FmTextControlFeature( xDispatcher
, aFeatureURL
, _nSlot
, this );
1264 void FmTextControlShell::Invalidate( SfxSlotId _nSlot
)
1266 m_rBindings
.Invalidate( _nSlot
);
1267 // despite this method being called "Invalidate", we also update here - this gives more immediate
1268 // feedback in the UI
1269 m_rBindings
.Update( _nSlot
);
1273 void FmTextControlShell::focusGained( const css::awt::FocusEvent
& _rEvent
)
1275 Reference
< css::awt::XControl
> xControl( _rEvent
.Source
, UNO_QUERY
);
1277 #if OSL_DEBUG_LEVEL > 0
1278 SAL_INFO("svx.form", "0x" << OUString::number( reinterpret_cast<sal_IntPtr
>(xControl
.get()), 16 ));
1281 DBG_ASSERT( xControl
.is(), "FmTextControlShell::focusGained: suspicious focus event!" );
1282 if ( xControl
.is() )
1283 controlActivated( xControl
);
1287 void FmTextControlShell::focusLost( const css::awt::FocusEvent
& _rEvent
)
1289 Reference
< css::awt::XControl
> xControl( _rEvent
.Source
, UNO_QUERY
);
1291 #if OSL_DEBUG_LEVEL > 0
1292 SAL_INFO("svx.form", "0x" << OUString::number( reinterpret_cast<sal_IntPtr
>(xControl
.get()), 16 ));
1295 m_bActiveControl
= false;
1299 void FmTextControlShell::ForgetActiveControl()
1301 implClearActiveControlRef();
1305 void FmTextControlShell::contextMenuRequested()
1307 m_rBindings
.GetDispatcher()->ExecutePopup( "formrichtext" );
1314 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */