1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
11  *   Licensed to the Apache Software Foundation (ASF) under one or more
12  *   contributor license agreements. See the NOTICE file distributed
13  *   with this work for additional information regarding copyright
14  *   ownership. The ASF licenses this file to you under the Apache
15  *   License, Version 2.0 (the "License"); you may not use this file
16  *   except in compliance with the License. You may obtain a copy of
17  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18  */
19 
20 
21 #include <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>
32 
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>
42 
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>
65 
66 #include <memory>
67 
68 
69 namespace svx
70 {
71 
72 
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;
82 
83 
84     typedef sal_uInt16 WhichId;
85 
86 
87     static SfxSlotId pTextControlSlots[] =
88     {
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,
131         0
132     };
133 
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[] =
138     {
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,
159         0
160     };
161 
162     typedef ::cppu::WeakImplHelper <   css::awt::XFocusListener
163                                     >   FmFocusListenerAdapter_Base;
164     class FmFocusListenerAdapter : public FmFocusListenerAdapter_Base
165     {
166     private:
167         IFocusObserver*         m_pObserver;
168         Reference< css::awt::XWindow >    m_xWindow;
169 
170     public:
171         FmFocusListenerAdapter( const Reference< css::awt::XControl >& _rxControl, IFocusObserver* _pObserver );
172 
173         // clean up the instance
174         void    dispose();
175 
176     protected:
177         virtual ~FmFocusListenerAdapter() override;
178 
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;
183     };
184 
185 
FmFocusListenerAdapter(const Reference<css::awt::XControl> & _rxControl,IFocusObserver * _pObserver)186     FmFocusListenerAdapter::FmFocusListenerAdapter( const Reference< css::awt::XControl >& _rxControl, IFocusObserver* _pObserver )
187         :m_pObserver( _pObserver )
188         ,m_xWindow( _rxControl, UNO_QUERY )
189     {
190 
191         DBG_ASSERT( m_xWindow.is(), "FmFocusListenerAdapter::FmFocusListenerAdapter: invalid control!" );
192         osl_atomic_increment( &m_refCount );
193         {
194             try
195             {
196                 if ( m_xWindow.is() )
197                     m_xWindow->addFocusListener( this );
198             }
199             catch( const Exception& )
200             {
201                 DBG_UNHANDLED_EXCEPTION("svx");
202             }
203         }
204         osl_atomic_decrement( &m_refCount );
205     }
206 
207 
~FmFocusListenerAdapter()208     FmFocusListenerAdapter::~FmFocusListenerAdapter()
209     {
210         acquire();
211         dispose();
212 
213     }
214 
215 
dispose()216     void FmFocusListenerAdapter::dispose()
217     {
218         if ( m_xWindow.is() )
219         {
220             m_xWindow->removeFocusListener( this );
221             m_xWindow.clear();
222         }
223     }
224 
225 
focusGained(const css::awt::FocusEvent & e)226     void SAL_CALL FmFocusListenerAdapter::focusGained( const css::awt::FocusEvent& e )
227     {
228         if ( m_pObserver )
229             m_pObserver->focusGained( e );
230     }
231 
232 
focusLost(const css::awt::FocusEvent & e)233     void SAL_CALL FmFocusListenerAdapter::focusLost( const css::awt::FocusEvent& e )
234     {
235         if ( m_pObserver )
236             m_pObserver->focusLost( e );
237     }
238 
239 
disposing(const EventObject & Source)240     void SAL_CALL FmFocusListenerAdapter::disposing( const EventObject& Source )
241     {
242         DBG_ASSERT( Source.Source == m_xWindow, "FmFocusListenerAdapter::disposing: where did this come from?" );
243         m_xWindow.clear();
244     }
245 
246     typedef ::cppu::WeakImplHelper <   css::awt::XMouseListener
247                                     >   FmMouseListenerAdapter_Base;
248     class FmMouseListenerAdapter : public FmMouseListenerAdapter_Base
249     {
250     private:
251         IContextRequestObserver*  m_pObserver;
252         Reference< css::awt::XWindow >    m_xWindow;
253 
254     public:
255         FmMouseListenerAdapter( const Reference< css::awt::XControl >& _rxControl, IContextRequestObserver* _pObserver );
256 
257         // clean up the instance
258         void    dispose();
259 
260     protected:
261         virtual ~FmMouseListenerAdapter() override;
262 
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;
269     };
270 
FmMouseListenerAdapter(const Reference<css::awt::XControl> & _rxControl,IContextRequestObserver * _pObserver)271     FmMouseListenerAdapter::FmMouseListenerAdapter( const Reference< css::awt::XControl >& _rxControl, IContextRequestObserver* _pObserver )
272         :m_pObserver( _pObserver )
273         ,m_xWindow( _rxControl, UNO_QUERY )
274     {
275 
276         DBG_ASSERT( m_xWindow.is(), "FmMouseListenerAdapter::FmMouseListenerAdapter: invalid control!" );
277         osl_atomic_increment( &m_refCount );
278         {
279             try
280             {
281                 if ( m_xWindow.is() )
282                     m_xWindow->addMouseListener( this );
283             }
284             catch( const Exception& )
285             {
286                 DBG_UNHANDLED_EXCEPTION("svx");
287             }
288         }
289         osl_atomic_decrement( &m_refCount );
290     }
291 
292 
~FmMouseListenerAdapter()293     FmMouseListenerAdapter::~FmMouseListenerAdapter()
294     {
295         acquire();
296         dispose();
297 
298     }
299 
300 
dispose()301     void FmMouseListenerAdapter::dispose()
302     {
303         if ( m_xWindow.is() )
304         {
305             m_xWindow->removeMouseListener( this );
306             m_xWindow.clear();
307         }
308     }
309 
310 
mousePressed(const css::awt::MouseEvent & _rEvent)311     void SAL_CALL FmMouseListenerAdapter::mousePressed( const css::awt::MouseEvent& _rEvent )
312     {
313         SolarMutexGuard aGuard;
314         // is this a request for a context menu?
315         if ( _rEvent.PopupTrigger )
316         {
317             if ( m_pObserver )
318                 m_pObserver->contextMenuRequested();
319         }
320     }
321 
322 
mouseReleased(const css::awt::MouseEvent &)323     void SAL_CALL FmMouseListenerAdapter::mouseReleased( const css::awt::MouseEvent& /*e*/ )
324     {
325         // not interested in
326     }
327 
328 
mouseEntered(const css::awt::MouseEvent &)329     void SAL_CALL FmMouseListenerAdapter::mouseEntered( const css::awt::MouseEvent& /*e*/ )
330     {
331         // not interested in
332     }
333 
334 
mouseExited(const css::awt::MouseEvent &)335     void SAL_CALL FmMouseListenerAdapter::mouseExited( const css::awt::MouseEvent& /*e*/ )
336     {
337         // not interested in
338     }
339 
340 
disposing(const EventObject & Source)341     void SAL_CALL FmMouseListenerAdapter::disposing( const EventObject& Source )
342     {
343         DBG_ASSERT( Source.Source == m_xWindow, "FmMouseListenerAdapter::disposing: where did this come from?" );
344         m_xWindow.clear();
345     }
346 
347 
348     //= FmTextControlShell
349 
350 
351     namespace
352     {
353 
lcl_translateUnoStateToItem(SfxSlotId _nSlot,const Any & _rUnoState,SfxItemSet & _rSet)354         void lcl_translateUnoStateToItem( SfxSlotId _nSlot, const Any& _rUnoState, SfxItemSet& _rSet )
355         {
356             WhichId nWhich = _rSet.GetPool()->GetWhich( _nSlot );
357             if ( !_rUnoState.hasValue() )
358             {
359                 if  ( ( _nSlot != SID_CUT )
360                    && ( _nSlot != SID_COPY )
361                    && ( _nSlot != SID_PASTE )
362                     )
363                 {
364                     _rSet.InvalidateItem( nWhich );
365                 }
366             }
367             else
368             {
369                 switch ( _rUnoState.getValueType().getTypeClass() )
370                 {
371                 case TypeClass_BOOLEAN:
372                 {
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 ) );
379                 }
380                 break;
381 
382                 default:
383                 {
384                     Sequence< PropertyValue > aComplexState;
385                     if ( _rUnoState >>= aComplexState )
386                     {
387                         if ( !aComplexState.hasElements() )
388                             _rSet.InvalidateItem( nWhich );
389                         else
390                         {
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 );
399                         }
400                     }
401                     else
402                     {
403                         OSL_FAIL( "lcl_translateUnoStateToItem: invalid state!" );
404                     }
405                 }
406                 }
407             }
408         }
409 
410 
lcl_getUnoSlotName(SfxSlotId _nSlotId)411         OUString lcl_getUnoSlotName( SfxSlotId _nSlotId )
412         {
413             OUString sSlotUnoName;
414 
415             SfxSlotPool& rSlotPool = SfxSlotPool::GetSlotPool();
416             const SfxSlot* pSlot = rSlotPool.GetSlot( _nSlotId );
417 
418             const sal_Char* pAsciiUnoName = nullptr;
419             if ( pSlot )
420             {
421                 pAsciiUnoName = pSlot->GetUnoName();
422             }
423             else
424             {
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 )
428                 {
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;
432                 }
433             }
434 
435             if ( pAsciiUnoName )
436             {
437                 sSlotUnoName = ".uno:" + OUString::createFromAscii( pAsciiUnoName );
438             }
439             else
440             {
441                 SAL_WARN( "svx", "lcl_getUnoSlotName: invalid slot id, or invalid slot, or no UNO name! "
442                         "(slot id: " << _nSlotId << ")");
443             }
444             return sSlotUnoName;
445         }
446 
447 
lcl_determineReadOnly(const Reference<css::awt::XControl> & _rxControl)448         bool lcl_determineReadOnly( const Reference< css::awt::XControl >& _rxControl )
449         {
450             bool bIsReadOnlyModel = true;
451             try
452             {
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();
459 
460                 if ( !xModelPropInfo.is() || !xModelPropInfo->hasPropertyByName( FM_PROP_READONLY ) )
461                     bIsReadOnlyModel = true;
462                 else
463                 {
464                     bool bReadOnly = true;
465                     xModelProps->getPropertyValue( FM_PROP_READONLY ) >>= bReadOnly;
466                     bIsReadOnlyModel = bReadOnly;
467                 }
468             }
469             catch( const Exception& )
470             {
471                 DBG_UNHANDLED_EXCEPTION("svx");
472             }
473             return bIsReadOnlyModel;
474         }
475 
476 
lcl_getWindow(const Reference<css::awt::XControl> & _rxControl)477         vcl::Window* lcl_getWindow( const Reference< css::awt::XControl >& _rxControl )
478         {
479             vcl::Window* pWindow = nullptr;
480             try
481             {
482                 Reference< css::awt::XWindowPeer > xControlPeer;
483                 if ( _rxControl.is() )
484                     xControlPeer = _rxControl->getPeer();
485                 if ( xControlPeer.is() )
486                     pWindow = VCLUnoHelper::GetWindow( xControlPeer ).get();
487             }
488             catch( const Exception& )
489             {
490                 DBG_UNHANDLED_EXCEPTION("svx");
491             }
492 
493             return pWindow;
494         }
495 
496 
lcl_isRichText(const Reference<css::awt::XControl> & _rxControl)497         bool lcl_isRichText( const Reference< css::awt::XControl >& _rxControl )
498         {
499             if ( !_rxControl.is() )
500                 return false;
501 
502             bool bIsRichText = false;
503             try
504             {
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 ) )
511                 {
512                     OSL_VERIFY( xModelProps->getPropertyValue( sRichTextPropertyName ) >>= bIsRichText );
513                 }
514             }
515             catch( const Exception& )
516             {
517                 DBG_UNHANDLED_EXCEPTION("svx");
518             }
519             return bIsRichText;
520         }
521     }
522 
523 
FmTextControlShell(SfxViewFrame * _pFrame)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 )
531     {
532         m_aClipboardInvalidation.SetInvokeHandler( LINK( this, FmTextControlShell, OnInvalidateClipboard ) );
533         m_aClipboardInvalidation.SetTimeout( 200 );
534     }
535 
536 
~FmTextControlShell()537     FmTextControlShell::~FmTextControlShell()
538     {
539         dispose();
540     }
541 
542 
IMPL_LINK_NOARG(FmTextControlShell,OnInvalidateClipboard,Timer *,void)543     IMPL_LINK_NOARG( FmTextControlShell, OnInvalidateClipboard, Timer*, void )
544     {
545         if ( m_bNeedClipboardInvalidation )
546         {
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;
552         }
553     }
554 
555 
transferFeatureStatesToItemSet(ControlFeatures & _rDispatchers,SfxAllItemSet & _rSet,bool _bTranslateLatin)556     void FmTextControlShell::transferFeatureStatesToItemSet( ControlFeatures& _rDispatchers, SfxAllItemSet& _rSet, bool _bTranslateLatin )
557     {
558         SfxItemPool& rPool = *_rSet.GetPool();
559 
560         for (const auto& rFeature : _rDispatchers)
561         {
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
573 
574             if ( _bTranslateLatin )
575             {
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.
586 
587                 switch ( nSlotId )
588                 {
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;
594                 }
595             }
596 
597             WhichId nWhich = rPool.GetWhich( nSlotId );
598             bool bIsInPool = rPool.IsInRange( nWhich );
599             if ( bIsInPool )
600             {
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
608 
609                 lcl_translateUnoStateToItem( nSlotId, rFeature.second->getFeatureState(), _rSet );
610             }
611 #if OSL_DEBUG_LEVEL > 0
612             else
613             {
614                 SAL_WARN("svx.form", "found a feature state for " << sUnoSlotNameAscii << ", but could not translate it into an item!" );
615             }
616 #endif
617         }
618     }
619 
620 
executeAttributeDialog(AttributeSet _eSet,SfxRequest & rReq)621     void FmTextControlShell::executeAttributeDialog( AttributeSet _eSet, SfxRequest& rReq )
622     {
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;
627 
628         SfxItemPool* pPool = EditEngine::CreatePool();
629         pPool->FreezeIdRanges();
630         std::unique_ptr< SfxItemSet > xPureItems( new SfxItemSet( *pPool ) );
631 
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 );
635 
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 );
641 
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() )
648         {
649             const SfxItemSet& rModifiedItems = *xDialog->GetOutputItemSet();
650             for ( WhichId nWhich = pPool->GetFirstWhich(); nWhich <= pPool->GetLastWhich(); ++nWhich )
651             {
652                 if ( rModifiedItems.GetItemState( nWhich ) == SfxItemState::SET )
653                 {
654                     SfxSlotId nSlotForItemSet = pPool->GetSlotId( nWhich );
655                     const SfxPoolItem* pModifiedItem = rModifiedItems.GetItem( nWhich );
656 
657 
658                     SfxSlotId nSlotForDispatcher = nSlotForItemSet;
659                     switch ( nSlotForDispatcher )
660                     {
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;
666                     }
667 
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( );
671 
672                     if ( !bFound )
673                     {
674                         aFeaturePos = aAdditionalFestures.find( nSlotForDispatcher );
675                         bFound = aFeaturePos != aAdditionalFestures.end( );
676                     }
677 
678                     if ( bFound )
679                     {
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 );
686 
687                         if  (   ( nSlotForItemSet == SID_ATTR_PARA_HANGPUNCTUATION )
688                             ||  ( nSlotForItemSet == SID_ATTR_PARA_FORBIDDEN_RULES )
689                             ||  ( nSlotForItemSet == SID_ATTR_PARA_SCRIPTSPACE )
690                             )
691                         {
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?" );
695 
696                             const SfxBoolItem* pBoolItem = dynamic_cast<const SfxBoolItem*>( pModifiedItem  );
697                             DBG_ASSERT( pBoolItem, "FmTextControlShell::executeAttributeDialog: no bool item?!" );
698                             if ( pBoolItem )
699                             {
700                                 aArgs.realloc( 1 );
701                                 aArgs[ 0 ].Name = "Enable";
702                                 aArgs[ 0 ].Value <<= pBoolItem->GetValue();
703                             }
704                         }
705 
706                         // dispatch this
707                         aFeaturePos->second->dispatch( aArgs );
708                     }
709                 #if OSL_DEBUG_LEVEL > 0
710                     else
711                     {
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 );
719                     }
720                 #endif
721                 }
722             }
723             rReq.Done( rModifiedItems );
724         }
725 
726         xDialog.reset();
727         xCurrentItems.reset();
728         xPureItems.reset();
729         SfxItemPool::Free(pPool);
730     }
731 
732 
executeSelectAll()733     void FmTextControlShell::executeSelectAll( )
734     {
735         try
736         {
737             if ( m_xActiveTextComponent.is() )
738             {
739                 sal_Int32 nTextLen = m_xActiveTextComponent->getText().getLength();
740                 m_xActiveTextComponent->setSelection( css::awt::Selection( 0, nTextLen ) );
741             }
742         }
743         catch( const Exception& )
744         {
745             DBG_UNHANDLED_EXCEPTION("svx");
746         }
747     }
748 
749 
executeClipboardSlot(SfxSlotId _nSlot)750     void FmTextControlShell::executeClipboardSlot( SfxSlotId _nSlot )
751     {
752         try
753         {
754             if ( m_xActiveTextComponent.is() )
755             {
756                 switch ( _nSlot )
757                 {
758                 case SID_COPY:
759                 case SID_CUT:
760                 {
761                     OUString sSelectedText( m_xActiveTextComponent->getSelectedText() );
762                     ::svt::OStringTransfer::CopyString( sSelectedText, lcl_getWindow( m_xActiveControl ) );
763                     if ( SID_CUT == _nSlot )
764                     {
765                         css::awt::Selection aSelection( m_xActiveTextComponent->getSelection() );
766                         m_xActiveTextComponent->insertText( aSelection, OUString() );
767                     }
768                 }
769                 break;
770                 case SID_PASTE:
771                 {
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 );
776                 }
777                 break;
778                 default:
779                     OSL_FAIL( "FmTextControlShell::executeClipboardSlot: invalid slot!" );
780                 }
781             }
782         }
783         catch( const Exception& )
784         {
785             DBG_UNHANDLED_EXCEPTION("svx");
786         }
787     }
788 
789 
ExecuteTextAttribute(SfxRequest & _rReq)790     void FmTextControlShell::ExecuteTextAttribute( SfxRequest& _rReq )
791     {
792         SfxSlotId nSlot = _rReq.GetSlot();
793 
794         ControlFeatures::const_iterator aFeaturePos = m_aControlFeatures.find( nSlot );
795         if ( aFeaturePos == m_aControlFeatures.end() )
796         {
797             // special slots
798             switch ( nSlot )
799             {
800             case SID_CHAR_DLG:
801                 executeAttributeDialog( eCharAttribs, _rReq );
802                 break;
803 
804             case SID_PARA_DLG:
805                 executeAttributeDialog( eParaAttribs, _rReq );
806                 break;
807 
808             case SID_SELECTALL:
809                 executeSelectAll();
810                 break;
811 
812             case SID_CUT:
813             case SID_COPY:
814             case SID_PASTE:
815                 executeClipboardSlot( nSlot );
816                 break;
817 
818             default:
819                 DBG_ASSERT( aFeaturePos != m_aControlFeatures.end(), "FmTextControShell::ExecuteTextAttribute: I have no such dispatcher, and cannot handle it at all!" );
820                 return;
821             }
822         }
823         else
824         {
825             // slots which are dispatched to the control
826 
827             switch ( nSlot )
828             {
829             case SID_ATTR_CHAR_STRIKEOUT:
830             case SID_ATTR_CHAR_UNDERLINE:
831             case SID_ATTR_CHAR_OVERLINE:
832             {
833                 SfxItemSet aToggled( *_rReq.GetArgs() );
834 
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 ) )
839                 {
840                     const SvxTextLineItem* pTextLine = dynamic_cast<const SvxTextLineItem*>( pItem  );
841                     DBG_ASSERT( pTextLine, "FmTextControlShell::ExecuteTextAttribute: ooops - no underline/overline item!" );
842                     if ( pTextLine )
843                     {
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 ) );
849                         }
850                     }
851                 }
852                 else
853                 {
854                     const SvxCrossedOutItem* pCrossedOut = dynamic_cast<const SvxCrossedOutItem*>( pItem  );
855                     DBG_ASSERT( pCrossedOut, "FmTextControlShell::ExecuteTextAttribute: ooops - no CrossedOut item!" );
856                     if ( pCrossedOut )
857                     {
858                         FontStrikeout eFS = pCrossedOut->GetStrikeout();
859                         aToggled.Put( SvxCrossedOutItem( eFS == STRIKEOUT_SINGLE ? STRIKEOUT_NONE : STRIKEOUT_SINGLE, nWhich ) );
860                     }
861                 }
862 
863                 Sequence< PropertyValue > aArguments;
864                 TransformItems( nSlot, aToggled, aArguments );
865                 aFeaturePos->second->dispatch( aArguments );
866             }
867             break;
868 
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:
877             {
878                 const SfxItemSet* pArgs = _rReq.GetArgs();
879                 Sequence< PropertyValue > aArgs;
880                 if ( pArgs )
881                     TransformItems( nSlot, *pArgs, aArgs );
882                 aFeaturePos->second->dispatch( aArgs );
883             }
884             break;
885 
886             default:
887                 if ( aFeaturePos->second->isFeatureEnabled() )
888                     aFeaturePos->second->dispatch();
889                 break;
890             }
891         }
892         _rReq.Done();
893     }
894 
895 
GetTextAttributeState(SfxItemSet & _rSet)896     void FmTextControlShell::GetTextAttributeState( SfxItemSet& _rSet )
897     {
898         SfxWhichIter aIter( _rSet );
899         sal_uInt16 nSlot = aIter.FirstWhich();
900         while ( nSlot )
901         {
902             if  (   ( nSlot == SID_ATTR_PARA_LEFT_TO_RIGHT )
903                 ||  ( nSlot == SID_ATTR_PARA_RIGHT_TO_LEFT )
904                 )
905             {
906                 if ( !SvtLanguageOptions().IsCTLFontEnabled() )
907                 {
908                     _rSet.DisableItem( nSlot );
909                     nSlot = aIter.NextWhich();
910                     continue;
911                 }
912             }
913 
914             ControlFeatures::const_iterator aFeaturePos = m_aControlFeatures.find( nSlot );
915             if ( aFeaturePos != m_aControlFeatures.end() )
916             {
917                 if ( aFeaturePos->second->isFeatureEnabled() )
918                     lcl_translateUnoStateToItem( nSlot, aFeaturePos->second->getFeatureState(), _rSet );
919                 else
920                     _rSet.DisableItem( nSlot );
921             }
922             else
923             {
924                 bool bDisable = false;
925 
926                 bool bNeedWriteableControl = false;
927                 bool bNeedTextComponent = false;
928                 bool bNeedSelection = false;
929 
930                 switch ( nSlot )
931                 {
932                 case SID_CHAR_DLG:
933                 case SID_PARA_DLG:
934                     bDisable |= m_aControlFeatures.empty();
935                     bNeedWriteableControl = true;
936                     break;
937 
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;
945 
946                 case SID_PASTE:
947                 {
948                     vcl::Window* pActiveControlVCLWindow = lcl_getWindow( m_xActiveControl );
949                     if ( pActiveControlVCLWindow )
950                     {
951                         TransferableDataHelper aDataHelper( TransferableDataHelper::CreateFromSystemClipboard( pActiveControlVCLWindow) );
952                         bDisable |= !aDataHelper.HasFormat( SotClipboardFormatId::STRING );
953                     }
954                     else
955                         bDisable = true;
956 
957                     bNeedTextComponent = true;
958                     bNeedWriteableControl = true;
959                 }
960                 break;
961 
962                 case SID_COPY:
963                     bNeedTextComponent = true;
964                     bNeedSelection = true;
965                     break;
966 
967                 case SID_SELECTALL:
968                     bNeedTextComponent = true;
969                     break;
970 
971                 default:
972                     // slot is unknown at all
973                     bDisable = true;
974                     break;
975                 }
976                 SAL_WARN_IF( bNeedSelection && !bNeedTextComponent, "svx.form", "FmTextControlShell::GetTextAttributeState: bNeedSelection should imply bNeedTextComponent!" );
977 
978                 if ( !bDisable && bNeedWriteableControl )
979                     bDisable |= !IsActiveControl( ) || m_bActiveControlIsReadOnly;
980 
981                 if ( !bDisable && bNeedTextComponent )
982                     bDisable |= !m_xActiveTextComponent.is();
983 
984                 if ( !bDisable && bNeedSelection )
985                 {
986                     css::awt::Selection aSelection = m_xActiveTextComponent->getSelection();
987                     bDisable |= aSelection.Min == aSelection.Max;
988                 }
989 
990                 if ( bDisable )
991                     _rSet.DisableItem( nSlot );
992             }
993 
994             nSlot = aIter.NextWhich();
995         }
996     }
997 
998 
IsActiveControl(bool _bCountRichTextOnly) const999     bool FmTextControlShell::IsActiveControl( bool _bCountRichTextOnly ) const
1000     {
1001         if ( _bCountRichTextOnly && !m_bActiveControlIsRichText )
1002             return false;
1003 
1004         return m_bActiveControl;
1005     }
1006 
1007 
dispose()1008     void FmTextControlShell::dispose()
1009     {
1010         if ( IsActiveControl() )
1011             controlDeactivated();
1012         if ( isControllerListening() )
1013             stopControllerListening();
1014     }
1015 
1016 
designModeChanged()1017     void FmTextControlShell::designModeChanged()
1018     {
1019         m_rBindings.Invalidate( pTextControlSlots );
1020     }
1021 
1022 
formActivated(const Reference<runtime::XFormController> & _rxController)1023     void FmTextControlShell::formActivated( const Reference< runtime::XFormController >& _rxController )
1024     {
1025 #if OSL_DEBUG_LEVEL > 0
1026         SAL_INFO("svx.form", "0x" << OUString::number( reinterpret_cast<sal_IntPtr>(_rxController.get()), 16 ));
1027 #endif
1028 
1029         DBG_ASSERT( _rxController.is(), "FmTextControlShell::formActivated: invalid controller!" );
1030         if ( !_rxController.is() )
1031             return;
1032 
1033         // sometimes, a form controller notifies activations, even if it's already activated
1034         if ( m_xActiveController == _rxController )
1035             return;
1036 
1037         try
1038         {
1039             startControllerListening( _rxController );
1040             controlActivated( _rxController->getCurrentControl() );
1041         }
1042         catch( const Exception& )
1043         {
1044             DBG_UNHANDLED_EXCEPTION("svx");
1045         }
1046     }
1047 
1048 
formDeactivated(const Reference<runtime::XFormController> & _rxController)1049     void FmTextControlShell::formDeactivated( const Reference< runtime::XFormController >& _rxController )
1050     {
1051         SAL_INFO("svx.form", "0x" << OUString::number( reinterpret_cast<sal_IntPtr>(_rxController.get()), 16 ));
1052 
1053         if ( IsActiveControl() )
1054             controlDeactivated();
1055         if ( isControllerListening() )
1056             stopControllerListening();
1057     }
1058 
1059 
startControllerListening(const Reference<runtime::XFormController> & _rxController)1060     void FmTextControlShell::startControllerListening( const Reference< runtime::XFormController >& _rxController )
1061     {
1062         OSL_PRECOND( _rxController.is(), "FmTextControlShell::startControllerListening: invalid controller!" );
1063         if ( !_rxController.is() )
1064             return;
1065 
1066         OSL_PRECOND( !isControllerListening(), "FmTextControlShell::startControllerListening: already listening!" );
1067         if ( isControllerListening() )
1068             stopControllerListening( );
1069         DBG_ASSERT( !isControllerListening(), "FmTextControlShell::startControllerListening: inconsistence!" );
1070 
1071         try
1072         {
1073             Sequence< Reference< css::awt::XControl > > aControls( _rxController->getControls() );
1074             m_aControlObservers.resize( 0 );
1075             m_aControlObservers.reserve( aControls.getLength() );
1076 
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 ) ); });
1080         }
1081         catch( const Exception& )
1082         {
1083             DBG_UNHANDLED_EXCEPTION("svx");
1084         }
1085 
1086         m_xActiveController = _rxController;
1087     }
1088 
1089 
stopControllerListening()1090     void FmTextControlShell::stopControllerListening( )
1091     {
1092         OSL_PRECOND( isControllerListening(), "FmTextControlShell::stopControllerListening: inconsistence!" );
1093 
1094         // dispose all listeners associated with the controls of the active controller
1095         for (auto& rpObserver : m_aControlObservers)
1096         {
1097             rpObserver->dispose();
1098         }
1099 
1100         FocusListenerAdapters aEmpty;
1101         m_aControlObservers.swap( aEmpty );
1102 
1103         m_xActiveController.clear();
1104     }
1105 
1106 
implClearActiveControlRef()1107     void FmTextControlShell::implClearActiveControlRef()
1108     {
1109         // no more features for this control
1110         for (auto& rFeature : m_aControlFeatures)
1111         {
1112             rFeature.second->dispose();
1113         }
1114 
1115         ControlFeatures aEmpty;
1116         m_aControlFeatures.swap( aEmpty );
1117 
1118         if ( m_aContextMenuObserver.get() )
1119         {
1120             m_aContextMenuObserver->dispose();
1121             m_aContextMenuObserver = MouseListenerAdapter();
1122         }
1123 
1124         if ( m_xActiveTextComponent.is() )
1125         {
1126             SAL_INFO("svx.form", "stopping timer for clipboard invalidation" );
1127             m_aClipboardInvalidation.Stop();
1128         }
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;
1135     }
1136 
1137 
controlDeactivated()1138     void FmTextControlShell::controlDeactivated( )
1139     {
1140         DBG_ASSERT( IsActiveControl(), "FmTextControlShell::controlDeactivated: no active control!" );
1141 
1142         m_bActiveControl = false;
1143 
1144         m_rBindings.Invalidate( pTextControlSlots );
1145     }
1146 
1147 
controlActivated(const Reference<css::awt::XControl> & _rxControl)1148     void FmTextControlShell::controlActivated( const Reference< css::awt::XControl >& _rxControl )
1149     {
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!" );
1154 
1155 #if OSL_DEBUG_LEVEL > 0
1156         {
1157             Sequence< Reference< css::awt::XControl > > aActiveControls;
1158             if ( m_xActiveController.is() )
1159                 aActiveControls = m_xActiveController->getControls();
1160 
1161             bool bFoundThisControl = false;
1162 
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 )
1166             {
1167                 if ( *pControls == _rxControl )
1168                     bFoundThisControl = true;
1169             }
1170             DBG_ASSERT( bFoundThisControl, "FmTextControlShell::controlActivated: only controls which belong to the active controller can be activated!" );
1171         }
1172 #endif
1173         // ask the control for dispatchers for our text-related slots
1174         fillFeatureDispatchers( _rxControl, pTextControlSlots, m_aControlFeatures );
1175 
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 );
1181 
1182         // if we found a rich text control, we need context menu support
1183         if ( m_bActiveControlIsRichText )
1184         {
1185             DBG_ASSERT( nullptr == m_aContextMenuObserver.get(), "FmTextControlShell::controlActivated: already have an observer!" );
1186             m_aContextMenuObserver = MouseListenerAdapter( new FmMouseListenerAdapter( _rxControl, this ) );
1187         }
1188 
1189         if ( m_xActiveTextComponent.is() )
1190         {
1191             SAL_INFO("svx.form", "starting timer for clipboard invalidation" );
1192             m_aClipboardInvalidation.Start();
1193         }
1194 
1195         m_bActiveControl = true;
1196 
1197         m_rBindings.Invalidate( pTextControlSlots );
1198 
1199         if ( m_pViewFrame )
1200             m_pViewFrame->UIFeatureChanged();
1201 
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).
1214 
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 );
1219 
1220         m_bNeedClipboardInvalidation = true;
1221     }
1222 
1223 
fillFeatureDispatchers(const Reference<css::awt::XControl> & _rxControl,SfxSlotId * _pZeroTerminatedSlots,ControlFeatures & _rDispatchers)1224     void FmTextControlShell::fillFeatureDispatchers(const Reference< css::awt::XControl >& _rxControl, SfxSlotId* _pZeroTerminatedSlots,
1225             ControlFeatures& _rDispatchers)
1226     {
1227         Reference< XDispatchProvider > xProvider( _rxControl, UNO_QUERY );
1228         SfxApplication* pApplication = SfxGetpApp();
1229         DBG_ASSERT( pApplication, "FmTextControlShell::fillFeatureDispatchers: no SfxApplication!" );
1230         if ( xProvider.is() && pApplication )
1231         {
1232             SfxSlotId* pSlots = _pZeroTerminatedSlots;
1233             while ( *pSlots )
1234             {
1235                 FmTextControlFeature* pDispatcher = implGetFeatureDispatcher( xProvider, pApplication, *pSlots );
1236                 if ( pDispatcher )
1237                     _rDispatchers.emplace( *pSlots, ControlFeature( pDispatcher ) );
1238 
1239                 ++pSlots;
1240             }
1241         }
1242     }
1243 
1244 
implGetFeatureDispatcher(const Reference<XDispatchProvider> & _rxProvider,SfxApplication const * _pApplication,SfxSlotId _nSlot)1245     FmTextControlFeature* FmTextControlShell::implGetFeatureDispatcher( const Reference< XDispatchProvider >& _rxProvider, SfxApplication const * _pApplication, SfxSlotId _nSlot )
1246     {
1247         OSL_PRECOND( _rxProvider.is() && _pApplication, "FmTextControlShell::implGetFeatureDispatcher: invalid arg(s)!" );
1248         URL aFeatureURL;
1249         aFeatureURL.Complete = lcl_getUnoSlotName( _nSlot );
1250         try
1251         {
1252             if ( !m_xURLTransformer.is() )
1253             {
1254                 m_xURLTransformer = util::URLTransformer::create( ::comphelper::getProcessComponentContext() );
1255             }
1256             if ( m_xURLTransformer.is() )
1257                 m_xURLTransformer->parseStrict( aFeatureURL );
1258         }
1259         catch( const Exception& )
1260         {
1261             DBG_UNHANDLED_EXCEPTION("svx");
1262         }
1263         Reference< XDispatch > xDispatcher = _rxProvider->queryDispatch( aFeatureURL, OUString(), 0xFF );
1264         if ( xDispatcher.is() )
1265             return new FmTextControlFeature( xDispatcher, aFeatureURL, _nSlot, this );
1266         return nullptr;
1267     }
1268 
1269 
Invalidate(SfxSlotId _nSlot)1270     void FmTextControlShell::Invalidate( SfxSlotId _nSlot )
1271     {
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 );
1276     }
1277 
1278 
focusGained(const css::awt::FocusEvent & _rEvent)1279     void FmTextControlShell::focusGained( const css::awt::FocusEvent& _rEvent )
1280     {
1281         Reference< css::awt::XControl > xControl( _rEvent.Source, UNO_QUERY );
1282 
1283 #if OSL_DEBUG_LEVEL > 0
1284         SAL_INFO("svx.form", "0x" << OUString::number( reinterpret_cast<sal_IntPtr>(xControl.get()), 16 ));
1285 #endif
1286 
1287         DBG_ASSERT( xControl.is(), "FmTextControlShell::focusGained: suspicious focus event!" );
1288         if ( xControl.is() )
1289             controlActivated( xControl );
1290     }
1291 
1292 
focusLost(const css::awt::FocusEvent & _rEvent)1293     void FmTextControlShell::focusLost( const css::awt::FocusEvent& _rEvent )
1294     {
1295         Reference< css::awt::XControl > xControl( _rEvent.Source, UNO_QUERY );
1296 
1297 #if OSL_DEBUG_LEVEL > 0
1298         SAL_INFO("svx.form", "0x" << OUString::number( reinterpret_cast<sal_IntPtr>(xControl.get()), 16 ));
1299 #endif
1300 
1301         m_bActiveControl = false;
1302     }
1303 
1304 
ForgetActiveControl()1305     void FmTextControlShell::ForgetActiveControl()
1306     {
1307         implClearActiveControlRef();
1308     }
1309 
1310 
contextMenuRequested()1311     void FmTextControlShell::contextMenuRequested()
1312     {
1313         m_rBindings.GetDispatcher()->ExecutePopup( "formrichtext" );
1314     }
1315 
1316 
1317 }
1318 
1319 
1320 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
1321