Version 7.6.3.2-android, tag libreoffice-7.6.3.2-android
[LibreOffice.git] / svx / source / form / fmtextcontrolshell.cxx
blobe938a0ad140d89bead822143452194969e3acbaf
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/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 <comphelper/propertyvalue.hxx>
41 #include <cppuhelper/implbase.hxx>
42 #include <sfx2/app.hxx>
43 #include <sfx2/bindings.hxx>
44 #include <sfx2/dispatch.hxx>
45 #include <sfx2/msgpool.hxx>
46 #include <sfx2/msg.hxx>
47 #include <sfx2/objsh.hxx>
48 #include <sfx2/request.hxx>
49 #include <sfx2/sfxuno.hxx>
50 #include <sfx2/viewfrm.hxx>
51 #include <svl/eitem.hxx>
52 #include <svl/itempool.hxx>
53 #include <svl/ctloptions.hxx>
54 #include <svtools/stringtransfer.hxx>
55 #include <svl/whiter.hxx>
56 #include <toolkit/helper/vclunohelper.hxx>
57 #include <tools/debug.hxx>
58 #include <comphelper/diagnose_ex.hxx>
59 #include <sal/log.hxx>
60 #include <vcl/svapp.hxx>
61 #include <vcl/window.hxx>
63 #include <memory>
66 namespace svx
70 using namespace ::com::sun::star;
71 using namespace ::com::sun::star::uno;
72 using namespace ::com::sun::star::form;
73 using namespace ::com::sun::star::form::runtime;
74 using namespace ::com::sun::star::lang;
75 using namespace ::com::sun::star::frame;
76 using namespace ::com::sun::star::util;
77 using namespace ::com::sun::star::beans;
78 using namespace ::com::sun::star::container;
81 typedef sal_uInt16 WhichId;
84 static SfxSlotId pTextControlSlots[] =
86 SID_CLIPBOARD_FORMAT_ITEMS,
87 SID_CUT,
88 SID_COPY,
89 SID_PASTE,
90 SID_SELECTALL,
91 // SID_ATTR_TABSTOP, /* 2 */
92 SID_ATTR_CHAR_FONT,
93 SID_ATTR_CHAR_POSTURE,
94 SID_ATTR_CHAR_WEIGHT,
95 SID_ATTR_CHAR_SHADOWED,
96 SID_ATTR_CHAR_WORDLINEMODE,
97 SID_ATTR_CHAR_CONTOUR,
98 SID_ATTR_CHAR_STRIKEOUT,
99 SID_ATTR_CHAR_UNDERLINE,
100 SID_ATTR_CHAR_FONTHEIGHT,
101 SID_ATTR_CHAR_COLOR,
102 SID_ATTR_CHAR_KERNING,
103 SID_ATTR_CHAR_LANGUAGE, /* 20 */
104 SID_ATTR_CHAR_ESCAPEMENT,
105 SID_ATTR_PARA_ADJUST, /* 28 */
106 SID_ATTR_PARA_ADJUST_LEFT,
107 SID_ATTR_PARA_ADJUST_RIGHT,
108 SID_ATTR_PARA_ADJUST_CENTER,
109 SID_ATTR_PARA_ADJUST_BLOCK,
110 SID_ATTR_PARA_LINESPACE, /* 33 */
111 SID_ATTR_PARA_LINESPACE_10,
112 SID_ATTR_PARA_LINESPACE_15,
113 SID_ATTR_PARA_LINESPACE_20,
114 SID_ATTR_LRSPACE, /* 48 */
115 SID_ATTR_ULSPACE, /* 49 */
116 SID_ATTR_CHAR_AUTOKERN,
117 SID_SET_SUPER_SCRIPT,
118 SID_SET_SUB_SCRIPT,
119 SID_CHAR_DLG,
120 SID_PARA_DLG,
121 // SID_TEXTDIRECTION_LEFT_TO_RIGHT, /* 907 */
122 // SID_TEXTDIRECTION_TOP_TO_BOTTOM,
123 SID_ATTR_CHAR_SCALEWIDTH, /* 911 */
124 SID_ATTR_CHAR_RELIEF,
125 SID_ATTR_PARA_LEFT_TO_RIGHT, /* 950 */
126 SID_ATTR_PARA_RIGHT_TO_LEFT,
127 SID_ATTR_CHAR_OVERLINE,
131 // slots which we are not responsible for on the SfxShell level, but
132 // need to handle during the "paragraph attributes" and/or "character
133 // attributes" dialogs
134 static SfxSlotId pDialogSlots[] =
136 SID_ATTR_TABSTOP,
137 SID_ATTR_PARA_HANGPUNCTUATION,
138 SID_ATTR_PARA_FORBIDDEN_RULES,
139 SID_ATTR_PARA_SCRIPTSPACE,
140 SID_ATTR_CHAR_LATIN_LANGUAGE,
141 SID_ATTR_CHAR_CJK_LANGUAGE,
142 SID_ATTR_CHAR_CTL_LANGUAGE,
143 SID_ATTR_CHAR_LATIN_FONT,
144 SID_ATTR_CHAR_CJK_FONT,
145 SID_ATTR_CHAR_CTL_FONT,
146 SID_ATTR_CHAR_LATIN_FONTHEIGHT,
147 SID_ATTR_CHAR_CJK_FONTHEIGHT,
148 SID_ATTR_CHAR_CTL_FONTHEIGHT,
149 SID_ATTR_CHAR_LATIN_WEIGHT,
150 SID_ATTR_CHAR_CJK_WEIGHT,
151 SID_ATTR_CHAR_CTL_WEIGHT,
152 SID_ATTR_CHAR_LATIN_POSTURE,
153 SID_ATTR_CHAR_CJK_POSTURE,
154 SID_ATTR_CHAR_CTL_POSTURE,
155 SID_ATTR_CHAR_EMPHASISMARK,
159 typedef ::cppu::WeakImplHelper < css::awt::XFocusListener
160 > FmFocusListenerAdapter_Base;
161 class FmFocusListenerAdapter : public FmFocusListenerAdapter_Base
163 private:
164 IFocusObserver* m_pObserver;
165 Reference< css::awt::XWindow > m_xWindow;
167 public:
168 FmFocusListenerAdapter( const Reference< css::awt::XControl >& _rxControl, IFocusObserver* _pObserver );
170 // clean up the instance
171 void dispose();
173 protected:
174 virtual ~FmFocusListenerAdapter() override;
176 protected:
177 virtual void SAL_CALL focusGained( const css::awt::FocusEvent& e ) override;
178 virtual void SAL_CALL focusLost( const css::awt::FocusEvent& e ) override;
179 virtual void SAL_CALL disposing( const EventObject& Source ) override;
183 FmFocusListenerAdapter::FmFocusListenerAdapter( const Reference< css::awt::XControl >& _rxControl, IFocusObserver* _pObserver )
184 :m_pObserver( _pObserver )
185 ,m_xWindow( _rxControl, UNO_QUERY )
188 DBG_ASSERT( m_xWindow.is(), "FmFocusListenerAdapter::FmFocusListenerAdapter: invalid control!" );
189 osl_atomic_increment( &m_refCount );
193 if ( m_xWindow.is() )
194 m_xWindow->addFocusListener( this );
196 catch( const Exception& )
198 DBG_UNHANDLED_EXCEPTION("svx");
201 osl_atomic_decrement( &m_refCount );
205 FmFocusListenerAdapter::~FmFocusListenerAdapter()
207 acquire();
208 dispose();
213 void FmFocusListenerAdapter::dispose()
215 if ( m_xWindow.is() )
217 m_xWindow->removeFocusListener( this );
218 m_xWindow.clear();
223 void SAL_CALL FmFocusListenerAdapter::focusGained( const css::awt::FocusEvent& e )
225 if ( m_pObserver )
226 m_pObserver->focusGained( e );
230 void SAL_CALL FmFocusListenerAdapter::focusLost( const css::awt::FocusEvent& e )
232 if ( m_pObserver )
233 m_pObserver->focusLost( e );
237 void SAL_CALL FmFocusListenerAdapter::disposing( const EventObject& Source )
239 DBG_ASSERT( Source.Source == m_xWindow, "FmFocusListenerAdapter::disposing: where did this come from?" );
240 m_xWindow.clear();
243 typedef ::cppu::WeakImplHelper < css::awt::XMouseListener
244 > FmMouseListenerAdapter_Base;
245 class FmMouseListenerAdapter : public FmMouseListenerAdapter_Base
247 private:
248 IContextRequestObserver* m_pObserver;
249 Reference< css::awt::XWindow > m_xWindow;
251 public:
252 FmMouseListenerAdapter( const Reference< css::awt::XControl >& _rxControl, IContextRequestObserver* _pObserver );
254 // clean up the instance
255 void dispose();
257 protected:
258 virtual ~FmMouseListenerAdapter() override;
260 protected:
261 virtual void SAL_CALL mousePressed( const css::awt::MouseEvent& e ) override;
262 virtual void SAL_CALL mouseReleased( const css::awt::MouseEvent& e ) override;
263 virtual void SAL_CALL mouseEntered( const css::awt::MouseEvent& e ) override;
264 virtual void SAL_CALL mouseExited( const css::awt::MouseEvent& e ) override;
265 virtual void SAL_CALL disposing( const EventObject& Source ) override;
268 FmMouseListenerAdapter::FmMouseListenerAdapter( const Reference< css::awt::XControl >& _rxControl, IContextRequestObserver* _pObserver )
269 :m_pObserver( _pObserver )
270 ,m_xWindow( _rxControl, UNO_QUERY )
273 DBG_ASSERT( m_xWindow.is(), "FmMouseListenerAdapter::FmMouseListenerAdapter: invalid control!" );
274 osl_atomic_increment( &m_refCount );
278 if ( m_xWindow.is() )
279 m_xWindow->addMouseListener( this );
281 catch( const Exception& )
283 DBG_UNHANDLED_EXCEPTION("svx");
286 osl_atomic_decrement( &m_refCount );
290 FmMouseListenerAdapter::~FmMouseListenerAdapter()
292 acquire();
293 dispose();
298 void FmMouseListenerAdapter::dispose()
300 if ( m_xWindow.is() )
302 m_xWindow->removeMouseListener( this );
303 m_xWindow.clear();
308 void SAL_CALL FmMouseListenerAdapter::mousePressed( const css::awt::MouseEvent& _rEvent )
310 SolarMutexGuard aGuard;
311 // is this a request for a context menu?
312 if ( _rEvent.PopupTrigger )
314 if ( m_pObserver )
315 m_pObserver->contextMenuRequested();
320 void SAL_CALL FmMouseListenerAdapter::mouseReleased( const css::awt::MouseEvent& /*e*/ )
322 // not interested in
326 void SAL_CALL FmMouseListenerAdapter::mouseEntered( const css::awt::MouseEvent& /*e*/ )
328 // not interested in
332 void SAL_CALL FmMouseListenerAdapter::mouseExited( const css::awt::MouseEvent& /*e*/ )
334 // not interested in
338 void SAL_CALL FmMouseListenerAdapter::disposing( const EventObject& Source )
340 DBG_ASSERT( Source.Source == m_xWindow, "FmMouseListenerAdapter::disposing: where did this come from?" );
341 m_xWindow.clear();
345 //= FmTextControlShell
348 namespace
351 void lcl_translateUnoStateToItem( SfxSlotId _nSlot, const Any& _rUnoState, SfxItemSet& _rSet )
353 WhichId nWhich = _rSet.GetPool()->GetWhich( _nSlot );
354 if ( !_rUnoState.hasValue() )
356 if ( ( _nSlot != SID_CUT )
357 && ( _nSlot != SID_COPY )
358 && ( _nSlot != SID_PASTE )
361 _rSet.InvalidateItem( nWhich );
364 else
366 switch ( _rUnoState.getValueType().getTypeClass() )
368 case TypeClass_BOOLEAN:
370 bool bState = false;
371 _rUnoState >>= bState;
372 if ( _nSlot == SID_ATTR_PARA_SCRIPTSPACE )
373 _rSet.Put( SvxScriptSpaceItem( bState, nWhich ) );
374 else
375 _rSet.Put( SfxBoolItem( nWhich, bState ) );
377 break;
379 default:
381 Sequence< PropertyValue > aComplexState;
382 if ( _rUnoState >>= aComplexState )
384 if ( !aComplexState.hasElements() )
385 _rSet.InvalidateItem( nWhich );
386 else
388 SfxAllItemSet aAllItems( _rSet );
389 TransformParameters( _nSlot, aComplexState, aAllItems );
390 const SfxPoolItem* pTransformed = aAllItems.GetItem( nWhich );
391 OSL_ENSURE( pTransformed, "lcl_translateUnoStateToItem: non-empty parameter sequence leading to empty item?" );
392 if ( pTransformed )
393 _rSet.Put( *pTransformed );
394 else
395 _rSet.InvalidateItem( nWhich );
398 else
400 OSL_FAIL( "lcl_translateUnoStateToItem: invalid state!" );
408 OUString lcl_getUnoSlotName( SfxSlotId _nSlotId )
410 SfxSlotPool& rSlotPool = SfxSlotPool::GetSlotPool();
411 const SfxSlot* pSlot = rSlotPool.GetSlot( _nSlotId );
413 OUString sUnoName;
414 if ( pSlot )
416 sUnoName = pSlot->GetCommand();
418 else
420 // some hard-coded slots, which do not have a UNO name at SFX level, but which
421 // we nevertheless need to transport via UNO mechanisms, so we need a name
422 switch ( _nSlotId )
424 case SID_ATTR_PARA_HANGPUNCTUATION: sUnoName = ".uno:AllowHangingPunctuation"; break;
425 case SID_ATTR_PARA_FORBIDDEN_RULES: sUnoName = ".uno:ApplyForbiddenCharacterRules"; break;
426 case SID_ATTR_PARA_SCRIPTSPACE: sUnoName = ".uno:UseScriptSpacing"; break;
430 if (sUnoName.isEmpty())
432 SAL_WARN( "svx", "lcl_getUnoSlotName: invalid slot id, or invalid slot, or no UNO name! "
433 "(slot id: " << _nSlotId << ")");
435 return sUnoName;
439 bool lcl_determineReadOnly( const Reference< css::awt::XControl >& _rxControl )
441 bool bIsReadOnlyModel = true;
444 Reference< XPropertySet > xModelProps;
445 if ( _rxControl.is() )
446 xModelProps.set(_rxControl->getModel(), css::uno::UNO_QUERY);
447 Reference< XPropertySetInfo > xModelPropInfo;
448 if ( xModelProps.is() )
449 xModelPropInfo = xModelProps->getPropertySetInfo();
451 if ( !xModelPropInfo.is() || !xModelPropInfo->hasPropertyByName( FM_PROP_READONLY ) )
452 bIsReadOnlyModel = true;
453 else
455 bool bReadOnly = true;
456 xModelProps->getPropertyValue( FM_PROP_READONLY ) >>= bReadOnly;
457 bIsReadOnlyModel = bReadOnly;
460 catch( const Exception& )
462 DBG_UNHANDLED_EXCEPTION("svx");
464 return bIsReadOnlyModel;
468 vcl::Window* lcl_getWindow( const Reference< css::awt::XControl >& _rxControl )
470 vcl::Window* pWindow = nullptr;
473 Reference< css::awt::XWindowPeer > xControlPeer;
474 if ( _rxControl.is() )
475 xControlPeer = _rxControl->getPeer();
476 if ( xControlPeer.is() )
477 pWindow = VCLUnoHelper::GetWindow( xControlPeer );
479 catch( const Exception& )
481 DBG_UNHANDLED_EXCEPTION("svx");
484 return pWindow;
488 bool lcl_isRichText( const Reference< css::awt::XControl >& _rxControl )
490 if ( !_rxControl.is() )
491 return false;
493 bool bIsRichText = false;
496 Reference< XPropertySet > xModelProps( _rxControl->getModel(), UNO_QUERY );
497 Reference< XPropertySetInfo > xPSI;
498 if ( xModelProps.is() )
499 xPSI = xModelProps->getPropertySetInfo();
500 OUString sRichTextPropertyName = "RichText";
501 if ( xPSI.is() && xPSI->hasPropertyByName( sRichTextPropertyName ) )
503 OSL_VERIFY( xModelProps->getPropertyValue( sRichTextPropertyName ) >>= bIsRichText );
506 catch( const Exception& )
508 DBG_UNHANDLED_EXCEPTION("svx");
510 return bIsRichText;
515 FmTextControlShell::FmTextControlShell( SfxViewFrame* _pFrame )
516 :m_bActiveControl( false )
517 ,m_bActiveControlIsReadOnly( true )
518 ,m_bActiveControlIsRichText( false )
519 ,m_pViewFrame( _pFrame )
520 ,m_rBindings( _pFrame->GetBindings() )
521 ,m_aClipboardInvalidation("svx FmTextControlShell m_aClipboardInvalidation")
522 ,m_bNeedClipboardInvalidation( true )
524 m_aClipboardInvalidation.SetInvokeHandler( LINK( this, FmTextControlShell, OnInvalidateClipboard ) );
525 m_aClipboardInvalidation.SetTimeout( 200 );
529 FmTextControlShell::~FmTextControlShell()
531 dispose();
535 IMPL_LINK_NOARG( FmTextControlShell, OnInvalidateClipboard, Timer*, void )
537 if ( m_bNeedClipboardInvalidation )
539 SAL_INFO("svx.form", "invalidating clipboard slots" );
540 m_rBindings.Invalidate( SID_CUT );
541 m_rBindings.Invalidate( SID_COPY );
542 m_rBindings.Invalidate( SID_PASTE );
543 m_bNeedClipboardInvalidation = false;
548 void FmTextControlShell::transferFeatureStatesToItemSet( ControlFeatures& _rDispatchers, SfxAllItemSet& _rSet, bool _bTranslateLatin )
550 SfxItemPool& rPool = *_rSet.GetPool();
552 for (const auto& rFeature : _rDispatchers)
554 SfxSlotId nSlotId( rFeature.first );
555 #if OSL_DEBUG_LEVEL > 0
556 OUString sUnoSlotName;
557 if ( SfxGetpApp() )
558 sUnoSlotName = lcl_getUnoSlotName( nSlotId );
559 else
560 sUnoSlotName = "<unknown>";
561 OString sUnoSlotNameAscii = "\"" +
562 OUStringToOString( sUnoSlotName, RTL_TEXTENCODING_ASCII_US ) +
563 "\"";
564 #endif
566 if ( _bTranslateLatin )
568 // A rich text control offers a dispatcher for the "Font" slot/feature.
569 // Sadly, the semantics of the dispatches is that the feature "Font" depends
570 // on the current cursor position: If it's on latin text, it's the "latin font"
571 // which is set up at the control. If it's on CJK text, it's the "CJK font", and
572 // equivalent for "CTL font".
573 // The same holds for some other font related features/slots.
574 // Thus, we have separate dispatches for "Latin Font", "Latin Font Size", etc,
575 // which are only "virtual", in a sense that there exist no item with this id.
576 // So when we encounter such a dispatcher for, say, "Latin Font", we need to
577 // put an item into the set which has the "Font" id.
579 switch ( nSlotId )
581 case SID_ATTR_CHAR_LATIN_FONT: nSlotId = SID_ATTR_CHAR_FONT; break;
582 case SID_ATTR_CHAR_LATIN_FONTHEIGHT:nSlotId = SID_ATTR_CHAR_FONTHEIGHT; break;
583 case SID_ATTR_CHAR_LATIN_LANGUAGE: nSlotId = SID_ATTR_CHAR_LANGUAGE; break;
584 case SID_ATTR_CHAR_LATIN_POSTURE: nSlotId = SID_ATTR_CHAR_POSTURE; break;
585 case SID_ATTR_CHAR_LATIN_WEIGHT: nSlotId = SID_ATTR_CHAR_WEIGHT; break;
589 WhichId nWhich = rPool.GetWhich( nSlotId );
590 bool bIsInPool = rPool.IsInRange( nWhich );
591 if ( bIsInPool )
593 #if OSL_DEBUG_LEVEL > 0
594 bool bFeatureIsEnabled = rFeature.second->isFeatureEnabled();
595 OString sMessage = "found a feature state for " + sUnoSlotNameAscii;
596 if ( !bFeatureIsEnabled )
597 sMessage += " (disabled)";
598 SAL_INFO("svx.form", sMessage );
599 #endif
601 lcl_translateUnoStateToItem( nSlotId, rFeature.second->getFeatureState(), _rSet );
603 #if OSL_DEBUG_LEVEL > 0
604 else
606 SAL_WARN("svx.form", "found a feature state for " << sUnoSlotNameAscii << ", but could not translate it into an item!" );
608 #endif
613 void FmTextControlShell::executeAttributeDialog( AttributeSet _eSet, SfxRequest& rReq )
615 const SvxFontListItem* pFontList = dynamic_cast<const SvxFontListItem*>( m_pViewFrame->GetObjectShell()->GetItem( SID_ATTR_CHAR_FONTLIST ) );
616 DBG_ASSERT( pFontList, "FmTextControlShell::executeAttributeDialog: no font list item!" );
617 if ( !pFontList )
618 return;
620 rtl::Reference<SfxItemPool> pPool(EditEngine::CreatePool());
621 pPool->FreezeIdRanges();
622 std::optional< SfxItemSet > xPureItems(( SfxItemSet( *pPool ) ));
624 // put the current states of the items into the set
625 std::optional<SfxAllItemSet> xCurrentItems(( SfxAllItemSet( *xPureItems ) ));
626 transferFeatureStatesToItemSet( m_aControlFeatures, *xCurrentItems, false );
628 // additional items, which we are not responsible for at the SfxShell level,
629 // but which need to be forwarded to the dialog, anyway
630 ControlFeatures aAdditionalFestures;
631 fillFeatureDispatchers( m_xActiveControl, pDialogSlots, aAdditionalFestures );
632 transferFeatureStatesToItemSet( aAdditionalFestures, *xCurrentItems, true );
634 std::unique_ptr<SfxTabDialogController> xDialog;
635 if (_eSet == eCharAttribs)
636 xDialog = std::make_unique<TextControlCharAttribDialog>(rReq.GetFrameWeld(), *xCurrentItems, *pFontList);
637 else
638 xDialog = std::make_unique<TextControlParaAttribDialog>(rReq.GetFrameWeld(), *xCurrentItems);
639 if ( RET_OK == xDialog->run() )
641 const SfxItemSet& rModifiedItems = *xDialog->GetOutputItemSet();
642 for ( WhichId nWhich = pPool->GetFirstWhich(); nWhich <= pPool->GetLastWhich(); ++nWhich )
644 if ( rModifiedItems.GetItemState( nWhich ) == SfxItemState::SET )
646 SfxSlotId nSlotForItemSet = pPool->GetSlotId( nWhich );
647 const SfxPoolItem* pModifiedItem = rModifiedItems.GetItem( nWhich );
650 SfxSlotId nSlotForDispatcher = nSlotForItemSet;
651 switch ( nSlotForDispatcher )
653 case SID_ATTR_CHAR_FONT: nSlotForDispatcher = SID_ATTR_CHAR_LATIN_FONT; break;
654 case SID_ATTR_CHAR_FONTHEIGHT:nSlotForDispatcher = SID_ATTR_CHAR_LATIN_FONTHEIGHT; break;
655 case SID_ATTR_CHAR_LANGUAGE: nSlotForDispatcher = SID_ATTR_CHAR_LATIN_LANGUAGE; break;
656 case SID_ATTR_CHAR_POSTURE: nSlotForDispatcher = SID_ATTR_CHAR_LATIN_POSTURE; break;
657 case SID_ATTR_CHAR_WEIGHT: nSlotForDispatcher = SID_ATTR_CHAR_LATIN_WEIGHT; break;
660 // do we already have a dispatcher for this slot/feature?
661 ControlFeatures::const_iterator aFeaturePos = m_aControlFeatures.find( nSlotForDispatcher );
662 bool bFound = aFeaturePos != m_aControlFeatures.end( );
664 if ( !bFound )
666 aFeaturePos = aAdditionalFestures.find( nSlotForDispatcher );
667 bFound = aFeaturePos != aAdditionalFestures.end( );
670 if ( bFound )
672 Sequence< PropertyValue > aArgs;
673 // temporarily put the modified item into a "clean" set,
674 // and let TransformItems calc the respective UNO parameters
675 xPureItems->Put( *pModifiedItem );
676 TransformItems( nSlotForItemSet, *xPureItems, aArgs );
677 xPureItems->ClearItem( nWhich );
679 if ( ( nSlotForItemSet == SID_ATTR_PARA_HANGPUNCTUATION )
680 || ( nSlotForItemSet == SID_ATTR_PARA_FORBIDDEN_RULES )
681 || ( nSlotForItemSet == SID_ATTR_PARA_SCRIPTSPACE )
684 // these are no UNO slots, they need special handling since TransformItems cannot
685 // handle them
686 DBG_ASSERT( !aArgs.hasElements(), "FmTextControlShell::executeAttributeDialog: these are no UNO slots - are they?" );
688 const SfxBoolItem* pBoolItem = dynamic_cast<const SfxBoolItem*>( pModifiedItem );
689 DBG_ASSERT( pBoolItem, "FmTextControlShell::executeAttributeDialog: no bool item?!" );
690 if ( pBoolItem )
692 aArgs = { comphelper::makePropertyValue("Enable",
693 pBoolItem->GetValue()) };
697 // dispatch this
698 aFeaturePos->second->dispatch( aArgs );
700 #if OSL_DEBUG_LEVEL > 0
701 else
703 OUString sUnoSlotName = lcl_getUnoSlotName( nSlotForItemSet );
704 if ( sUnoSlotName.isEmpty() )
705 sUnoSlotName = "unknown (no SfxSlot)";
706 SAL_WARN( "svx", "FmTextControShell::executeAttributeDialog: Could not handle the following item:"
707 "\n SlotID: " << nSlotForItemSet
708 << "\n WhichID: " << nWhich
709 << "\n UNO name: " << sUnoSlotName );
711 #endif
714 rReq.Done( rModifiedItems );
717 xDialog.reset();
718 xCurrentItems.reset();
719 xPureItems.reset();
720 pPool.clear();
724 void FmTextControlShell::executeSelectAll( )
728 if ( m_xActiveTextComponent.is() )
730 sal_Int32 nTextLen = m_xActiveTextComponent->getText().getLength();
731 m_xActiveTextComponent->setSelection( css::awt::Selection( 0, nTextLen ) );
734 catch( const Exception& )
736 DBG_UNHANDLED_EXCEPTION("svx");
741 void FmTextControlShell::executeClipboardSlot( SfxSlotId _nSlot )
745 if ( m_xActiveTextComponent.is() )
747 switch ( _nSlot )
749 case SID_COPY:
750 case SID_CUT:
752 OUString sSelectedText( m_xActiveTextComponent->getSelectedText() );
753 ::svt::OStringTransfer::CopyString( sSelectedText, lcl_getWindow( m_xActiveControl ) );
754 if ( SID_CUT == _nSlot )
756 css::awt::Selection aSelection( m_xActiveTextComponent->getSelection() );
757 m_xActiveTextComponent->insertText( aSelection, OUString() );
760 break;
761 case SID_PASTE:
763 OUString sClipboardContent;
764 OSL_VERIFY( ::svt::OStringTransfer::PasteString( sClipboardContent, lcl_getWindow( m_xActiveControl ) ) );
765 css::awt::Selection aSelection( m_xActiveTextComponent->getSelection() );
766 m_xActiveTextComponent->insertText( aSelection, sClipboardContent );
768 break;
769 default:
770 OSL_FAIL( "FmTextControlShell::executeClipboardSlot: invalid slot!" );
774 catch( const Exception& )
776 DBG_UNHANDLED_EXCEPTION("svx");
781 void FmTextControlShell::ExecuteTextAttribute( SfxRequest& _rReq )
783 SfxSlotId nSlot = _rReq.GetSlot();
785 ControlFeatures::const_iterator aFeaturePos = m_aControlFeatures.find( nSlot );
786 if ( aFeaturePos == m_aControlFeatures.end() )
788 // special slots
789 switch ( nSlot )
791 case SID_CHAR_DLG:
792 executeAttributeDialog( eCharAttribs, _rReq );
793 break;
795 case SID_PARA_DLG:
796 executeAttributeDialog( eParaAttribs, _rReq );
797 break;
799 case SID_SELECTALL:
800 executeSelectAll();
801 break;
803 case SID_CUT:
804 case SID_COPY:
805 case SID_PASTE:
806 executeClipboardSlot( nSlot );
807 break;
809 default:
810 DBG_ASSERT( aFeaturePos != m_aControlFeatures.end(), "FmTextControShell::ExecuteTextAttribute: I have no such dispatcher, and cannot handle it at all!" );
811 return;
814 else
816 // slots which are dispatched to the control
818 switch ( nSlot )
820 case SID_ATTR_CHAR_STRIKEOUT:
821 case SID_ATTR_CHAR_UNDERLINE:
822 case SID_ATTR_CHAR_OVERLINE:
824 SfxItemSet aToggled( *_rReq.GetArgs() );
826 lcl_translateUnoStateToItem( nSlot, aFeaturePos->second->getFeatureState(), aToggled );
827 WhichId nWhich = aToggled.GetPool()->GetWhich( nSlot );
828 const SfxPoolItem* pItem = aToggled.GetItem( nWhich );
829 if ( ( SID_ATTR_CHAR_UNDERLINE == nSlot ) || ( SID_ATTR_CHAR_OVERLINE == nSlot ) )
831 const SvxTextLineItem* pTextLine = dynamic_cast<const SvxTextLineItem*>( pItem );
832 DBG_ASSERT( pTextLine, "FmTextControlShell::ExecuteTextAttribute: ooops - no underline/overline item!" );
833 if ( pTextLine )
835 FontLineStyle eTL = pTextLine->GetLineStyle();
836 if ( SID_ATTR_CHAR_UNDERLINE == nSlot ) {
837 aToggled.Put( SvxUnderlineItem( eTL == LINESTYLE_SINGLE ? LINESTYLE_NONE : LINESTYLE_SINGLE, nWhich ) );
838 } else {
839 aToggled.Put( SvxOverlineItem( eTL == LINESTYLE_SINGLE ? LINESTYLE_NONE : LINESTYLE_SINGLE, nWhich ) );
843 else
845 const SvxCrossedOutItem* pCrossedOut = dynamic_cast<const SvxCrossedOutItem*>( pItem );
846 DBG_ASSERT( pCrossedOut, "FmTextControlShell::ExecuteTextAttribute: ooops - no CrossedOut item!" );
847 if ( pCrossedOut )
849 FontStrikeout eFS = pCrossedOut->GetStrikeout();
850 aToggled.Put( SvxCrossedOutItem( eFS == STRIKEOUT_SINGLE ? STRIKEOUT_NONE : STRIKEOUT_SINGLE, nWhich ) );
854 Sequence< PropertyValue > aArguments;
855 TransformItems( nSlot, aToggled, aArguments );
856 aFeaturePos->second->dispatch( aArguments );
858 break;
860 case SID_ATTR_CHAR_FONTHEIGHT:
861 case SID_ATTR_CHAR_FONT:
862 case SID_ATTR_CHAR_POSTURE:
863 case SID_ATTR_CHAR_WEIGHT:
864 case SID_ATTR_CHAR_SHADOWED:
865 case SID_ATTR_CHAR_CONTOUR:
866 case SID_SET_SUPER_SCRIPT:
867 case SID_SET_SUB_SCRIPT:
869 const SfxItemSet* pArgs = _rReq.GetArgs();
870 Sequence< PropertyValue > aArgs;
871 if ( pArgs )
872 TransformItems( nSlot, *pArgs, aArgs );
873 aFeaturePos->second->dispatch( aArgs );
875 break;
877 default:
878 if ( aFeaturePos->second->isFeatureEnabled() )
879 aFeaturePos->second->dispatch();
880 break;
883 _rReq.Done();
887 void FmTextControlShell::GetTextAttributeState( SfxItemSet& _rSet )
889 SfxWhichIter aIter( _rSet );
890 sal_uInt16 nSlot = aIter.FirstWhich();
891 while ( nSlot )
893 if ( ( nSlot == SID_ATTR_PARA_LEFT_TO_RIGHT )
894 || ( nSlot == SID_ATTR_PARA_RIGHT_TO_LEFT )
897 if ( !SvtCTLOptions::IsCTLFontEnabled() )
899 _rSet.DisableItem( nSlot );
900 nSlot = aIter.NextWhich();
901 continue;
905 ControlFeatures::const_iterator aFeaturePos = m_aControlFeatures.find( nSlot );
906 if ( aFeaturePos != m_aControlFeatures.end() )
908 if ( aFeaturePos->second->isFeatureEnabled() )
909 lcl_translateUnoStateToItem( nSlot, aFeaturePos->second->getFeatureState(), _rSet );
910 else
911 _rSet.DisableItem( nSlot );
913 else
915 bool bDisable = false;
917 bool bNeedWriteableControl = false;
918 bool bNeedTextComponent = false;
919 bool bNeedSelection = false;
921 switch ( nSlot )
923 case SID_CHAR_DLG:
924 case SID_PARA_DLG:
925 bDisable |= m_aControlFeatures.empty();
926 bNeedWriteableControl = true;
927 break;
929 case SID_CUT:
930 bNeedSelection = true;
931 bNeedTextComponent = true;
932 bNeedWriteableControl = true;
933 SAL_INFO("svx.form", "need to invalidate again" );
934 m_bNeedClipboardInvalidation = true;
935 break;
937 case SID_PASTE:
939 vcl::Window* pActiveControlVCLWindow = lcl_getWindow( m_xActiveControl );
940 if ( pActiveControlVCLWindow )
942 TransferableDataHelper aDataHelper( TransferableDataHelper::CreateFromSystemClipboard( pActiveControlVCLWindow) );
943 bDisable |= !aDataHelper.HasFormat( SotClipboardFormatId::STRING );
945 else
946 bDisable = true;
948 bNeedTextComponent = true;
949 bNeedWriteableControl = true;
951 break;
953 case SID_COPY:
954 bNeedTextComponent = true;
955 bNeedSelection = true;
956 break;
958 case SID_SELECTALL:
959 bNeedTextComponent = true;
960 break;
962 default:
963 // slot is unknown at all
964 bDisable = true;
965 break;
967 SAL_WARN_IF( bNeedSelection && !bNeedTextComponent, "svx.form", "FmTextControlShell::GetTextAttributeState: bNeedSelection should imply bNeedTextComponent!" );
969 if ( !bDisable && bNeedWriteableControl )
970 bDisable |= !IsActiveControl( ) || m_bActiveControlIsReadOnly;
972 if ( !bDisable && bNeedTextComponent )
973 bDisable |= !m_xActiveTextComponent.is();
975 if ( !bDisable && bNeedSelection )
977 css::awt::Selection aSelection = m_xActiveTextComponent->getSelection();
978 bDisable |= aSelection.Min == aSelection.Max;
981 if ( bDisable )
982 _rSet.DisableItem( nSlot );
985 nSlot = aIter.NextWhich();
990 bool FmTextControlShell::IsActiveControl( bool _bCountRichTextOnly ) const
992 if ( _bCountRichTextOnly && !m_bActiveControlIsRichText )
993 return false;
995 return m_bActiveControl;
999 void FmTextControlShell::dispose()
1001 if ( IsActiveControl() )
1002 controlDeactivated();
1003 if ( isControllerListening() )
1004 stopControllerListening();
1008 void FmTextControlShell::designModeChanged()
1010 m_rBindings.Invalidate( pTextControlSlots );
1014 void FmTextControlShell::formActivated( const Reference< runtime::XFormController >& _rxController )
1016 #if OSL_DEBUG_LEVEL > 0
1017 SAL_INFO("svx.form", "0x" << OUString::number( reinterpret_cast<sal_IntPtr>(_rxController.get()), 16 ));
1018 #endif
1020 DBG_ASSERT( _rxController.is(), "FmTextControlShell::formActivated: invalid controller!" );
1021 if ( !_rxController.is() )
1022 return;
1024 // sometimes, a form controller notifies activations, even if it's already activated
1025 if ( m_xActiveController == _rxController )
1026 return;
1030 startControllerListening( _rxController );
1031 controlActivated( _rxController->getCurrentControl() );
1033 catch( const Exception& )
1035 DBG_UNHANDLED_EXCEPTION("svx");
1040 void FmTextControlShell::formDeactivated( const Reference< runtime::XFormController >& _rxController )
1042 SAL_INFO("svx.form", "0x" << OUString::number( reinterpret_cast<sal_IntPtr>(_rxController.get()), 16 ));
1044 if ( IsActiveControl() )
1045 controlDeactivated();
1046 if ( isControllerListening() )
1047 stopControllerListening();
1051 void FmTextControlShell::startControllerListening( const Reference< runtime::XFormController >& _rxController )
1053 OSL_PRECOND( _rxController.is(), "FmTextControlShell::startControllerListening: invalid controller!" );
1054 if ( !_rxController.is() )
1055 return;
1057 OSL_PRECOND( !isControllerListening(), "FmTextControlShell::startControllerListening: already listening!" );
1058 if ( isControllerListening() )
1059 stopControllerListening( );
1060 DBG_ASSERT( !isControllerListening(), "FmTextControlShell::startControllerListening: inconsistence!" );
1064 const Sequence< Reference< css::awt::XControl > > aControls( _rxController->getControls() );
1065 m_aControlObservers.resize( 0 );
1066 m_aControlObservers.reserve( aControls.getLength() );
1068 std::transform(aControls.begin(), aControls.end(), std::back_inserter(m_aControlObservers),
1069 [this](const Reference< css::awt::XControl >& rControl) -> FocusListenerAdapter {
1070 return FocusListenerAdapter( new FmFocusListenerAdapter( rControl, this ) ); });
1072 catch( const Exception& )
1074 DBG_UNHANDLED_EXCEPTION("svx");
1077 m_xActiveController = _rxController;
1081 void FmTextControlShell::stopControllerListening( )
1083 OSL_PRECOND( isControllerListening(), "FmTextControlShell::stopControllerListening: inconsistence!" );
1085 // dispose all listeners associated with the controls of the active controller
1086 for (auto& rpObserver : m_aControlObservers)
1088 rpObserver->dispose();
1091 FocusListenerAdapters().swap(m_aControlObservers);
1093 m_xActiveController.clear();
1097 void FmTextControlShell::implClearActiveControlRef()
1099 // no more features for this control
1100 for (auto& rFeature : m_aControlFeatures)
1102 rFeature.second->dispose();
1105 ControlFeatures().swap(m_aControlFeatures);
1107 if ( m_aContextMenuObserver )
1109 m_aContextMenuObserver->dispose();
1110 m_aContextMenuObserver = MouseListenerAdapter();
1113 if ( m_xActiveTextComponent.is() )
1115 SAL_INFO("svx.form", "stopping timer for clipboard invalidation" );
1116 m_aClipboardInvalidation.Stop();
1118 // no more active control
1119 m_xActiveControl.clear();
1120 m_xActiveTextComponent.clear();
1121 m_bActiveControlIsReadOnly = true;
1122 m_bActiveControlIsRichText = false;
1123 m_bActiveControl = false;
1127 void FmTextControlShell::controlDeactivated( )
1129 DBG_ASSERT( IsActiveControl(), "FmTextControlShell::controlDeactivated: no active control!" );
1131 m_bActiveControl = false;
1133 m_rBindings.Invalidate( pTextControlSlots );
1137 void FmTextControlShell::controlActivated( const Reference< css::awt::XControl >& _rxControl )
1139 // ensure that all knittings with the previously active control are lost
1140 if ( m_xActiveControl.is() )
1141 implClearActiveControlRef();
1142 DBG_ASSERT( m_aControlFeatures.empty(), "FmTextControlShell::controlActivated: should have no dispatchers when I'm here!" );
1144 #if OSL_DEBUG_LEVEL > 0
1146 Sequence< Reference< css::awt::XControl > > aActiveControls;
1147 if ( m_xActiveController.is() )
1148 aActiveControls = m_xActiveController->getControls();
1150 bool bFoundThisControl = false;
1152 const Reference< css::awt::XControl >* pControls = aActiveControls.getConstArray();
1153 const Reference< css::awt::XControl >* pControlsEnd = pControls + aActiveControls.getLength();
1154 for ( ; ( pControls != pControlsEnd ) && !bFoundThisControl; ++pControls )
1156 if ( *pControls == _rxControl )
1157 bFoundThisControl = true;
1159 DBG_ASSERT( bFoundThisControl, "FmTextControlShell::controlActivated: only controls which belong to the active controller can be activated!" );
1161 #endif
1162 // ask the control for dispatchers for our text-related slots
1163 fillFeatureDispatchers( _rxControl, pTextControlSlots, m_aControlFeatures );
1165 // remember this control
1166 m_xActiveControl = _rxControl;
1167 m_xActiveTextComponent.set(_rxControl, css::uno::UNO_QUERY);
1168 m_bActiveControlIsReadOnly = lcl_determineReadOnly( m_xActiveControl );
1169 m_bActiveControlIsRichText = lcl_isRichText( m_xActiveControl );
1171 // if we found a rich text control, we need context menu support
1172 if ( m_bActiveControlIsRichText )
1174 DBG_ASSERT( !m_aContextMenuObserver, "FmTextControlShell::controlActivated: already have an observer!" );
1175 m_aContextMenuObserver = MouseListenerAdapter( new FmMouseListenerAdapter( _rxControl, this ) );
1178 if ( m_xActiveTextComponent.is() )
1180 SAL_INFO("svx.form", "starting timer for clipboard invalidation" );
1181 m_aClipboardInvalidation.Start();
1184 m_bActiveControl = true;
1186 m_rBindings.Invalidate( pTextControlSlots );
1188 if ( m_pViewFrame )
1189 m_pViewFrame->UIFeatureChanged();
1191 // don't call the activation handler if we don't have any slots we can serve
1192 // The activation handler is used to put the shell on the top of the dispatcher stack,
1193 // so it's preferred when slots are distributed.
1194 // Note that this is a slight hack, to prevent that we grab slots from the SfxDispatcher
1195 // which should be served by other shells (e.g. Cut/Copy/Paste).
1196 // A real solution would be a forwarding-mechanism for slots: We should be on the top
1197 // if we're active, but if we cannot handle the slot, then we need to tell the dispatcher
1198 // to skip our shell, and pass the slot to the next one. However, this mechanism is not
1199 // not in place in SFX.
1200 // Another possibility would be to have dedicated shells for the slots which we might
1201 // or might not be able to serve. However, this could probably increase the number of
1202 // shells too much (In theory, nearly every slot could have an own shell then).
1204 // #i51621# / 2005-08-19 / frank.schoenheit@sun.com
1205 // bool bHaveAnyServeableSlots = m_xActiveTextComponent.is() || !m_aControlFeatures.empty();
1206 // LEM: not calling m_aControlActivatonHandler causes fdo#63695, so disable this hack for now.
1207 m_aControlActivationHandler.Call( nullptr );
1209 m_bNeedClipboardInvalidation = true;
1213 void FmTextControlShell::fillFeatureDispatchers(const Reference< css::awt::XControl >& _rxControl, SfxSlotId* _pZeroTerminatedSlots,
1214 ControlFeatures& _rDispatchers)
1216 Reference< XDispatchProvider > xProvider( _rxControl, UNO_QUERY );
1217 SfxApplication* pApplication = SfxGetpApp();
1218 DBG_ASSERT( pApplication, "FmTextControlShell::fillFeatureDispatchers: no SfxApplication!" );
1219 if ( xProvider.is() && pApplication )
1221 SfxSlotId* pSlots = _pZeroTerminatedSlots;
1222 while ( *pSlots )
1224 rtl::Reference<FmTextControlFeature> pDispatcher = implGetFeatureDispatcher( xProvider, pApplication, *pSlots );
1225 if ( pDispatcher )
1226 _rDispatchers.emplace( *pSlots, pDispatcher );
1228 ++pSlots;
1234 rtl::Reference<FmTextControlFeature> FmTextControlShell::implGetFeatureDispatcher( const Reference< XDispatchProvider >& _rxProvider, SfxApplication const * _pApplication, SfxSlotId _nSlot )
1236 OSL_PRECOND( _rxProvider.is() && _pApplication, "FmTextControlShell::implGetFeatureDispatcher: invalid arg(s)!" );
1237 URL aFeatureURL;
1238 aFeatureURL.Complete = lcl_getUnoSlotName( _nSlot );
1241 if ( !m_xURLTransformer.is() )
1243 m_xURLTransformer = util::URLTransformer::create( ::comphelper::getProcessComponentContext() );
1245 if ( m_xURLTransformer.is() )
1246 m_xURLTransformer->parseStrict( aFeatureURL );
1248 catch( const Exception& )
1250 DBG_UNHANDLED_EXCEPTION("svx");
1252 Reference< XDispatch > xDispatcher = _rxProvider->queryDispatch( aFeatureURL, OUString(), 0xFF );
1253 if ( xDispatcher.is() )
1254 return new FmTextControlFeature( xDispatcher, std::move(aFeatureURL), _nSlot, this );
1255 return nullptr;
1259 void FmTextControlShell::Invalidate( SfxSlotId _nSlot )
1261 m_rBindings.Invalidate( _nSlot );
1262 // despite this method being called "Invalidate", we also update here - this gives more immediate
1263 // feedback in the UI
1264 m_rBindings.Update( _nSlot );
1268 void FmTextControlShell::focusGained( const css::awt::FocusEvent& _rEvent )
1270 Reference< css::awt::XControl > xControl( _rEvent.Source, UNO_QUERY );
1272 #if OSL_DEBUG_LEVEL > 0
1273 SAL_INFO("svx.form", "0x" << OUString::number( reinterpret_cast<sal_IntPtr>(xControl.get()), 16 ));
1274 #endif
1276 DBG_ASSERT( xControl.is(), "FmTextControlShell::focusGained: suspicious focus event!" );
1277 if ( xControl.is() )
1278 controlActivated( xControl );
1282 void FmTextControlShell::focusLost( const css::awt::FocusEvent& _rEvent )
1284 Reference< css::awt::XControl > xControl( _rEvent.Source, UNO_QUERY );
1286 #if OSL_DEBUG_LEVEL > 0
1287 SAL_INFO("svx.form", "0x" << OUString::number( reinterpret_cast<sal_IntPtr>(xControl.get()), 16 ));
1288 #endif
1290 m_bActiveControl = false;
1294 void FmTextControlShell::ForgetActiveControl()
1296 implClearActiveControlRef();
1300 void FmTextControlShell::contextMenuRequested()
1302 m_rBindings.GetDispatcher()->ExecutePopup( "formrichtext" );
1309 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */