1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_svx.hxx"
26 
27 #include "fmprop.hrc"
28 #include "svx/fmresids.hrc"
29 #include "fmtextcontroldialogs.hxx"
30 #include "fmtextcontrolfeature.hxx"
31 #include "fmtextcontrolshell.hxx"
32 #include "editeng/crsditem.hxx"
33 #include "svx/dialmgr.hxx"
34 #include "editeng/editeng.hxx"
35 #include "editeng/eeitem.hxx"
36 #include "svx/fmglob.hxx"
37 #include "editeng/scriptspaceitem.hxx"
38 #include "svx/svxids.hrc"
39 #include "editeng/udlnitem.hxx"
40 
41 /** === begin UNO includes === **/
42 #include <com/sun/star/beans/XPropertySet.hpp>
43 #include <com/sun/star/awt/FontDescriptor.hpp>
44 #include <com/sun/star/frame/XDispatchProvider.hpp>
45 #include <com/sun/star/form/XForm.hpp>
46 #include <com/sun/star/container/XChild.hpp>
47 #include <com/sun/star/awt/XFocusListener.hpp>
48 #include <com/sun/star/awt/XMouseListener.hpp>
49 /** === end UNO includes === **/
50 
51 #include <comphelper/componentcontext.hxx>
52 #include <comphelper/processfactory.hxx>
53 #include <cppuhelper/implbase1.hxx>
54 #include <sfx2/app.hxx>
55 #include <sfx2/bindings.hxx>
56 #include <sfx2/dispatch.hxx>
57 #include <sfx2/msgpool.hxx>
58 #include <sfx2/objsh.hxx>
59 #include <sfx2/request.hxx>
60 #include <sfx2/sfxuno.hxx>
61 #include <sfx2/viewfrm.hxx>
62 #include <svl/eitem.hxx>
63 #include <svl/intitem.hxx>
64 #include <svl/itempool.hxx>
65 #include <svl/languageoptions.hxx>
66 #include <svtools/stringtransfer.hxx>
67 #include <svl/whiter.hxx>
68 #include <toolkit/helper/vclunohelper.hxx>
69 #include <tools/diagnose_ex.h>
70 #include <vcl/msgbox.hxx>
71 #include <vcl/outdev.hxx>
72 #include <vos/mutex.hxx>
73 
74 #include <memory>
75 
76 //........................................................................
77 namespace svx
78 {
79 //........................................................................
80 
81     using namespace ::com::sun::star;
82     using namespace ::com::sun::star::uno;
83     using namespace ::com::sun::star::awt;
84     using namespace ::com::sun::star::form;
85     using namespace ::com::sun::star::form::runtime;
86     using namespace ::com::sun::star::lang;
87     using namespace ::com::sun::star::frame;
88     using namespace ::com::sun::star::util;
89     using namespace ::com::sun::star::beans;
90     using namespace ::com::sun::star::container;
91 
92 	//====================================================================
93     typedef sal_uInt16 WhichId;
94 
95 	//====================================================================
96     static SfxSlotId pTextControlSlots[] =
97     {
98         SID_CLIPBOARD_FORMAT_ITEMS,
99         SID_CUT,
100         SID_COPY,
101         SID_PASTE,
102         SID_SELECTALL,
103 //        SID_ATTR_TABSTOP,           /* 2 */
104         SID_ATTR_CHAR_FONT,
105         SID_ATTR_CHAR_POSTURE,
106         SID_ATTR_CHAR_WEIGHT,
107         SID_ATTR_CHAR_SHADOWED,
108         SID_ATTR_CHAR_WORDLINEMODE,
109         SID_ATTR_CHAR_CONTOUR,
110         SID_ATTR_CHAR_STRIKEOUT,
111         SID_ATTR_CHAR_UNDERLINE,
112         SID_ATTR_CHAR_FONTHEIGHT,
113         SID_ATTR_CHAR_COLOR,
114         SID_ATTR_CHAR_KERNING,
115         SID_ATTR_CHAR_LANGUAGE,     /* 20 */
116         SID_ATTR_CHAR_ESCAPEMENT,
117         SID_ATTR_PARA_ADJUST,       /* 28 */
118         SID_ATTR_PARA_ADJUST_LEFT,
119         SID_ATTR_PARA_ADJUST_RIGHT,
120         SID_ATTR_PARA_ADJUST_CENTER,
121         SID_ATTR_PARA_ADJUST_BLOCK,
122         SID_ATTR_PARA_LINESPACE,    /* 33 */
123         SID_ATTR_PARA_LINESPACE_10,
124         SID_ATTR_PARA_LINESPACE_15,
125         SID_ATTR_PARA_LINESPACE_20,
126         SID_ATTR_LRSPACE,           /* 48 */
127         SID_ATTR_ULSPACE,           /* 49 */
128         SID_ATTR_CHAR_AUTOKERN,
129         SID_SET_SUPER_SCRIPT,
130         SID_SET_SUB_SCRIPT,
131         SID_CHAR_DLG,
132         SID_PARA_DLG,
133 //        SID_TEXTDIRECTION_LEFT_TO_RIGHT, /* 907 */
134 //        SID_TEXTDIRECTION_TOP_TO_BOTTOM,
135         SID_ATTR_CHAR_SCALEWIDTH,       /* 911 */
136         SID_ATTR_CHAR_RELIEF,
137         SID_ATTR_PARA_LEFT_TO_RIGHT,    /* 950 */
138         SID_ATTR_PARA_RIGHT_TO_LEFT,
139         SID_ATTR_CHAR_OVERLINE,
140         0
141     };
142 
143     // slots which we are not responsible for on the SfxShell level, but
144     // need to handle during the "paragraph attributes" and/or "character
145     // attributes" dialogs
146     static SfxSlotId pDialogSlots[] =
147     {
148         SID_ATTR_TABSTOP,
149         SID_ATTR_PARA_HANGPUNCTUATION,
150         SID_ATTR_PARA_FORBIDDEN_RULES,
151         SID_ATTR_PARA_SCRIPTSPACE,
152         SID_ATTR_CHAR_LATIN_LANGUAGE,
153         SID_ATTR_CHAR_CJK_LANGUAGE,
154         SID_ATTR_CHAR_CTL_LANGUAGE,
155         SID_ATTR_CHAR_LATIN_FONT,
156         SID_ATTR_CHAR_CJK_FONT,
157         SID_ATTR_CHAR_CTL_FONT,
158 		SID_ATTR_CHAR_LATIN_FONTHEIGHT,
159 		SID_ATTR_CHAR_CJK_FONTHEIGHT,
160 		SID_ATTR_CHAR_CTL_FONTHEIGHT,
161 		SID_ATTR_CHAR_LATIN_WEIGHT,
162 		SID_ATTR_CHAR_CJK_WEIGHT,
163 		SID_ATTR_CHAR_CTL_WEIGHT,
164 		SID_ATTR_CHAR_LATIN_POSTURE,
165 		SID_ATTR_CHAR_CJK_POSTURE,
166         SID_ATTR_CHAR_CTL_POSTURE,
167         SID_ATTR_CHAR_EMPHASISMARK,
168         0
169     };
170 
171 	//====================================================================
172 	//= FmFocusListenerAdapter
173 	//====================================================================
174     typedef ::cppu::WeakImplHelper1 <   XFocusListener
175                                     >   FmFocusListenerAdapter_Base;
176     class FmFocusListenerAdapter : public FmFocusListenerAdapter_Base
177     {
178     private:
179         IFocusObserver*         m_pObserver;
180         Reference< XWindow >    m_xWindow;
181 
182     public:
183         FmFocusListenerAdapter( const Reference< XControl >& _rxControl, IFocusObserver* _pObserver );
184 
185         // clean up the instance
186         void    dispose();
187 
188     protected:
189         ~FmFocusListenerAdapter();
190 
191     protected:
192         virtual void SAL_CALL focusGained( const FocusEvent& e ) throw (RuntimeException);
193         virtual void SAL_CALL focusLost( const FocusEvent& e ) throw (RuntimeException);
194         virtual void SAL_CALL disposing( const EventObject& Source ) throw (RuntimeException);
195     };
196 
197     //--------------------------------------------------------------------
DBG_NAME(FmFocusListenerAdapter)198     DBG_NAME( FmFocusListenerAdapter )
199     //--------------------------------------------------------------------
200     FmFocusListenerAdapter::FmFocusListenerAdapter( const Reference< XControl >& _rxControl, IFocusObserver* _pObserver )
201         :m_pObserver( _pObserver )
202         ,m_xWindow( _rxControl, UNO_QUERY )
203     {
204         DBG_CTOR( FmFocusListenerAdapter, NULL );
205 
206         DBG_ASSERT( m_xWindow.is(), "FmFocusListenerAdapter::FmFocusListenerAdapter: invalid control!" );
207         osl_incrementInterlockedCount( &m_refCount );
208         {
209             try
210             {
211                 if ( m_xWindow.is() )
212                     m_xWindow->addFocusListener( this );
213             }
214             catch( const Exception& )
215             {
216                 DBG_UNHANDLED_EXCEPTION();
217             }
218         }
219         osl_decrementInterlockedCount( &m_refCount );
220     }
221 
222     //--------------------------------------------------------------------
~FmFocusListenerAdapter()223     FmFocusListenerAdapter::~FmFocusListenerAdapter()
224     {
225         acquire();
226         dispose();
227 
228         DBG_DTOR( FmFocusListenerAdapter, NULL );
229     }
230 
231     //--------------------------------------------------------------------
dispose()232     void FmFocusListenerAdapter::dispose()
233     {
234         if ( m_xWindow.is() )
235         {
236             m_xWindow->removeFocusListener( this );
237             m_xWindow.clear();
238         }
239     }
240 
241     //--------------------------------------------------------------------
focusGained(const FocusEvent & e)242     void SAL_CALL FmFocusListenerAdapter::focusGained( const FocusEvent& e ) throw (RuntimeException)
243     {
244         if ( m_pObserver )
245             m_pObserver->focusGained( e );
246     }
247 
248     //--------------------------------------------------------------------
focusLost(const FocusEvent & e)249     void SAL_CALL FmFocusListenerAdapter::focusLost( const FocusEvent& e ) throw (RuntimeException)
250     {
251         if ( m_pObserver )
252             m_pObserver->focusLost( e );
253     }
254 
255     //--------------------------------------------------------------------
disposing(const EventObject & Source)256     void SAL_CALL FmFocusListenerAdapter::disposing( const EventObject& Source ) throw (RuntimeException)
257     {
258         (void)Source;
259         DBG_ASSERT( Source.Source == m_xWindow, "FmFocusListenerAdapter::disposing: where did this come from?" );
260         m_xWindow.clear();
261     }
262 
263 	//====================================================================
264 	//= FmMouseListenerAdapter
265 	//====================================================================
266     typedef ::cppu::WeakImplHelper1 <   XMouseListener
267                                     >   FmMouseListenerAdapter_Base;
268     class FmMouseListenerAdapter : public FmMouseListenerAdapter_Base
269     {
270     private:
271         IContextRequestObserver*  m_pObserver;
272         Reference< XWindow >    m_xWindow;
273 
274     public:
275         FmMouseListenerAdapter( const Reference< XControl >& _rxControl, IContextRequestObserver* _pObserver );
276 
277         // clean up the instance
278         void    dispose();
279 
280     protected:
281         ~FmMouseListenerAdapter();
282 
283     protected:
284         virtual void SAL_CALL mousePressed( const awt::MouseEvent& e ) throw (RuntimeException);
285         virtual void SAL_CALL mouseReleased( const awt::MouseEvent& e ) throw (RuntimeException);
286         virtual void SAL_CALL mouseEntered( const awt::MouseEvent& e ) throw (RuntimeException);
287         virtual void SAL_CALL mouseExited( const awt::MouseEvent& e ) throw (RuntimeException);
288         virtual void SAL_CALL disposing( const EventObject& Source ) throw (RuntimeException);
289     };
290 
291 	//====================================================================
292 	//= FmMouseListenerAdapter
293 	//====================================================================
294     //--------------------------------------------------------------------
DBG_NAME(FmMouseListenerAdapter)295     DBG_NAME( FmMouseListenerAdapter )
296     //--------------------------------------------------------------------
297     FmMouseListenerAdapter::FmMouseListenerAdapter( const Reference< XControl >& _rxControl, IContextRequestObserver* _pObserver )
298         :m_pObserver( _pObserver )
299         ,m_xWindow( _rxControl, UNO_QUERY )
300     {
301         DBG_CTOR( FmMouseListenerAdapter, NULL );
302 
303         DBG_ASSERT( m_xWindow.is(), "FmMouseListenerAdapter::FmMouseListenerAdapter: invalid control!" );
304         osl_incrementInterlockedCount( &m_refCount );
305         {
306             try
307             {
308                 if ( m_xWindow.is() )
309                     m_xWindow->addMouseListener( this );
310             }
311             catch( const Exception& )
312             {
313                 DBG_UNHANDLED_EXCEPTION();
314             }
315         }
316         osl_decrementInterlockedCount( &m_refCount );
317     }
318 
319     //--------------------------------------------------------------------
~FmMouseListenerAdapter()320     FmMouseListenerAdapter::~FmMouseListenerAdapter()
321     {
322         acquire();
323         dispose();
324 
325         DBG_DTOR( FmMouseListenerAdapter, NULL );
326     }
327 
328     //--------------------------------------------------------------------
dispose()329     void FmMouseListenerAdapter::dispose()
330     {
331         if ( m_xWindow.is() )
332         {
333             m_xWindow->removeMouseListener( this );
334             m_xWindow.clear();
335         }
336     }
337 
338     //--------------------------------------------------------------------
mousePressed(const awt::MouseEvent & _rEvent)339     void SAL_CALL FmMouseListenerAdapter::mousePressed( const awt::MouseEvent& _rEvent ) throw (::com::sun::star::uno::RuntimeException)
340     {
341         ::vos::OGuard aGuard( Application::GetSolarMutex() );
342 	    // is this a request for a context menu?
343 	    if ( _rEvent.PopupTrigger )
344         {
345             if ( m_pObserver )
346                 m_pObserver->contextMenuRequested( _rEvent );
347         }
348     }
349 
350     //--------------------------------------------------------------------
mouseReleased(const awt::MouseEvent &)351     void SAL_CALL FmMouseListenerAdapter::mouseReleased( const awt::MouseEvent& /*e*/ ) throw (::com::sun::star::uno::RuntimeException)
352     {
353         // not interested in
354     }
355 
356     //--------------------------------------------------------------------
mouseEntered(const awt::MouseEvent &)357     void SAL_CALL FmMouseListenerAdapter::mouseEntered( const awt::MouseEvent& /*e*/ ) throw (::com::sun::star::uno::RuntimeException)
358     {
359         // not interested in
360     }
361 
362     //--------------------------------------------------------------------
mouseExited(const awt::MouseEvent &)363     void SAL_CALL FmMouseListenerAdapter::mouseExited( const awt::MouseEvent& /*e*/ ) throw (::com::sun::star::uno::RuntimeException)
364     {
365         // not interested in
366     }
367 
368     //--------------------------------------------------------------------
disposing(const EventObject & Source)369     void SAL_CALL FmMouseListenerAdapter::disposing( const EventObject& Source ) throw (RuntimeException)
370     {
371         (void)Source;
372         DBG_ASSERT( Source.Source == m_xWindow, "FmMouseListenerAdapter::disposing: where did this come from?" );
373         m_xWindow.clear();
374     }
375 
376     //====================================================================
377     //= FmTextControlShell
378 	//====================================================================
379     //------------------------------------------------------------------------
380     namespace
381     {
382         //....................................................................
lcl_translateUnoStateToItem(SfxSlotId _nSlot,const Any & _rUnoState,SfxItemSet & _rSet)383         void lcl_translateUnoStateToItem( SfxSlotId _nSlot, const Any& _rUnoState, SfxItemSet& _rSet )
384         {
385             WhichId nWhich = _rSet.GetPool()->GetWhich( _nSlot );
386             if ( !_rUnoState.hasValue() )
387             {
388                 if  ( ( _nSlot != SID_CUT )
389                    && ( _nSlot != SID_COPY )
390                    && ( _nSlot != SID_PASTE )
391                     )
392                 {
393                     _rSet.InvalidateItem( nWhich );
394                 }
395             }
396             else
397             {
398                 switch ( _rUnoState.getValueType().getTypeClass() )
399                 {
400                 case TypeClass_BOOLEAN:
401                 {
402                     sal_Bool bState = sal_False;
403                     _rUnoState >>= bState;
404                     if ( _nSlot == SID_ATTR_PARA_SCRIPTSPACE )
405                         _rSet.Put( SvxScriptSpaceItem( bState, nWhich ) );
406                     else
407     				    _rSet.Put( SfxBoolItem( nWhich, bState ) );
408                 }
409                 break;
410 
411                 default:
412                 {
413                     Sequence< PropertyValue > aComplexState;
414                     if ( _rUnoState >>= aComplexState )
415                     {
416                         if ( !aComplexState.getLength() )
417                             _rSet.InvalidateItem( nWhich );
418                         else
419                         {
420                             SfxAllItemSet aAllItems( _rSet );
421                             TransformParameters( _nSlot, aComplexState, aAllItems );
422                             const SfxPoolItem* pTransformed = aAllItems.GetItem( nWhich );
423                             OSL_ENSURE( pTransformed, "lcl_translateUnoStateToItem: non-empty parameter sequence leading to empty item?" );
424                             if ( pTransformed )
425                                 _rSet.Put( *pTransformed );
426                             else
427                                 _rSet.InvalidateItem( nWhich );
428                         }
429                     }
430                     else
431                     {
432                         DBG_ERROR( "lcl_translateUnoStateToItem: invalid state!" );
433                     }
434                 }
435                 }
436             }
437         }
438 
439         //....................................................................
lcl_getUnoSlotName(SfxApplication &,SfxSlotId _nSlotId)440         ::rtl::OUString lcl_getUnoSlotName( SfxApplication&, SfxSlotId _nSlotId )
441         {
442             ::rtl::OUString sSlotUnoName;
443 
444             SfxSlotPool& rSlotPool = SfxSlotPool::GetSlotPool( NULL );
445             const SfxSlot* pSlot = rSlotPool.GetSlot( _nSlotId );
446 
447             const sal_Char* pAsciiUnoName = NULL;
448             if ( pSlot )
449             {
450                 pAsciiUnoName = pSlot->GetUnoName();
451             }
452             else
453             {
454                 // some hard-coded slots, which do not have a UNO name at SFX level, but which
455                 // we nevertheless need to transport via UNO mechanisms, so we need a name
456                 switch ( _nSlotId )
457                 {
458                 case SID_ATTR_PARA_HANGPUNCTUATION: pAsciiUnoName = "AllowHangingPunctuation"; break;
459                 case SID_ATTR_PARA_FORBIDDEN_RULES: pAsciiUnoName = "ApplyForbiddenCharacterRules"; break;
460                 case SID_ATTR_PARA_SCRIPTSPACE: pAsciiUnoName = "UseScriptSpacing"; break;
461                 }
462             }
463 
464             if ( pAsciiUnoName )
465             {
466                 sSlotUnoName = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( ".uno:" ) );
467                 sSlotUnoName += ::rtl::OUString::createFromAscii( pAsciiUnoName );
468             }
469 #if OSL_DEBUG_LEVEL > 0
470             else
471             {
472                 ::rtl::OString sMessage( "lcl_getUnoSlotName: invalid slot id, or invalid slot, or no UNO name!\n" );
473                 sMessage += "(slot id: ";
474                 sMessage += ::rtl::OString::valueOf( (sal_Int32)_nSlotId );
475                 sMessage += ")";
476                 DBG_ERROR( sMessage );
477             }
478 #endif
479             return sSlotUnoName;
480         }
481 
482         //....................................................................
lcl_determineReadOnly(const Reference<XControl> & _rxControl)483         bool lcl_determineReadOnly( const Reference< XControl >& _rxControl )
484         {
485             bool bIsReadOnlyModel = true;
486             try
487             {
488                 Reference< XPropertySet > xModelProps;
489                 if ( _rxControl.is() )
490                     xModelProps = xModelProps.query( _rxControl->getModel() );
491                 Reference< XPropertySetInfo > xModelPropInfo;
492                 if ( xModelProps.is() )
493                     xModelPropInfo = xModelProps->getPropertySetInfo();
494 
495                 if ( !xModelPropInfo.is() || !xModelPropInfo->hasPropertyByName( FM_PROP_READONLY ) )
496                     bIsReadOnlyModel = true;
497                 else
498                 {
499                     sal_Bool bReadOnly = sal_True;
500                     xModelProps->getPropertyValue( FM_PROP_READONLY ) >>= bReadOnly;
501                     bIsReadOnlyModel = bReadOnly;
502                 }
503             }
504             catch( const Exception& )
505             {
506                 DBG_UNHANDLED_EXCEPTION();
507             }
508             return bIsReadOnlyModel;
509         }
510 
511         //....................................................................
lcl_getWindow(const Reference<XControl> & _rxControl)512         static Window* lcl_getWindow( const Reference< XControl >& _rxControl )
513         {
514             Window* pWindow = NULL;
515             try
516             {
517                 Reference< XWindowPeer > xControlPeer;
518                 if ( _rxControl.is() )
519                     xControlPeer = _rxControl->getPeer();
520                 if ( xControlPeer.is() )
521                     pWindow = VCLUnoHelper::GetWindow( xControlPeer );
522             }
523             catch( const Exception& )
524             {
525                 DBG_UNHANDLED_EXCEPTION();
526             }
527 
528             return pWindow;
529         }
530 
531         //....................................................................
lcl_isRichText(const Reference<XControl> & _rxControl)532         bool lcl_isRichText( const Reference< XControl >& _rxControl )
533         {
534             if ( !_rxControl.is() )
535                 return false;
536 
537             bool bIsRichText = false;
538             try
539             {
540                 Reference< XPropertySet > xModelProps( _rxControl->getModel(), UNO_QUERY );
541                 Reference< XPropertySetInfo > xPSI;
542                 if ( xModelProps.is() )
543                     xPSI = xModelProps->getPropertySetInfo();
544                 ::rtl::OUString sRichTextPropertyName = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "RichText" ) );
545                 if ( xPSI.is() && xPSI->hasPropertyByName( sRichTextPropertyName ) )
546                 {
547                     OSL_VERIFY( xModelProps->getPropertyValue( sRichTextPropertyName ) >>= bIsRichText );
548                 }
549             }
550             catch( const Exception& )
551             {
552                 DBG_UNHANDLED_EXCEPTION();
553             }
554             return bIsRichText;
555         }
556     }
557 
558     //------------------------------------------------------------------------
FmTextControlShell(SfxViewFrame * _pFrame)559     FmTextControlShell::FmTextControlShell( SfxViewFrame* _pFrame )
560         :m_bActiveControl( false )
561         ,m_bActiveControlIsReadOnly( true )
562         ,m_bActiveControlIsRichText( false )
563         ,m_pViewFrame( _pFrame )
564         ,m_rBindings( _pFrame->GetBindings() )
565         ,m_bNeedClipboardInvalidation( true )
566     {
567         m_aClipboardInvalidation.SetTimeoutHdl( LINK( this, FmTextControlShell, OnInvalidateClipboard ) );
568         m_aClipboardInvalidation.SetTimeout( 200 );
569     }
570 
571     //------------------------------------------------------------------------
~FmTextControlShell()572     FmTextControlShell::~FmTextControlShell()
573     {
574         dispose();
575     }
576 
577     //------------------------------------------------------------------------
578     IMPL_LINK( FmTextControlShell, OnInvalidateClipboard, void*, /*_pNotInterestedIn*/ )
579     {
580         if ( m_bNeedClipboardInvalidation )
581         {
582             DBG_TRACE( "FmTextControlShell::ClipBoard: invalidating clipboard slots" );
583             m_rBindings.Invalidate( SID_CUT );
584             m_rBindings.Invalidate( SID_COPY );
585             m_rBindings.Invalidate( SID_PASTE );
586             m_bNeedClipboardInvalidation = false;
587         }
588         return 0L;
589     }
590 
591     //------------------------------------------------------------------------
transferFeatureStatesToItemSet(ControlFeatures & _rDispatchers,SfxAllItemSet & _rSet,bool _bTranslateLatin)592     void FmTextControlShell::transferFeatureStatesToItemSet( ControlFeatures& _rDispatchers, SfxAllItemSet& _rSet, bool _bTranslateLatin )
593     {
594         SfxItemPool& rPool = *_rSet.GetPool();
595 
596         for (   ControlFeatures::const_iterator aFeature = _rDispatchers.begin();
597                 aFeature != _rDispatchers.end();
598                 ++aFeature
599             )
600         {
601             SfxSlotId nSlotId( aFeature->first );
602             #if OSL_DEBUG_LEVEL > 0
603                 ::rtl::OUString sUnoSlotName;
604                 if ( SFX_APP() )
605                     sUnoSlotName = lcl_getUnoSlotName( *SFX_APP(), nSlotId );
606                 else
607                     sUnoSlotName = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "<unknown>" ) );
608                 ::rtl::OString sUnoSlotNameAscii( "\"" );
609                 sUnoSlotNameAscii += ::rtl::OString( sUnoSlotName.getStr(), sUnoSlotName.getLength(), RTL_TEXTENCODING_ASCII_US );
610                 sUnoSlotNameAscii += "\"";
611             #endif
612 
613             if ( _bTranslateLatin )
614             {
615                 // A rich text control offers a dispatcher for the "Font" slot/feature.
616                 // Sadly, the semantics of the dispatches is that the feature "Font" depends
617                 // on the current cursor position: If it's on latin text, it's the "latin font"
618                 // which is set up at the control. If it's on CJK text, it's the "CJK font", and
619                 // aequivalent for "CTL font".
620                 // The same holds for some other font related features/slots.
621                 // Thus, we have separate dispatches for "Latin Font", "Latin Font Size", etc,
622                 // which are only "virtual", in a sense that there exist no item with this id.
623                 // So when we encounter such a dispatcher for, say, "Latin Font", we need to
624                 // put an item into the set which has the "Font" id.
625 
626                 switch ( nSlotId )
627                 {
628                 case SID_ATTR_CHAR_LATIN_FONT:      nSlotId = SID_ATTR_CHAR_FONT; break;
629                 case SID_ATTR_CHAR_LATIN_FONTHEIGHT:nSlotId = SID_ATTR_CHAR_FONTHEIGHT; break;
630                 case SID_ATTR_CHAR_LATIN_LANGUAGE:  nSlotId = SID_ATTR_CHAR_LANGUAGE; break;
631                 case SID_ATTR_CHAR_LATIN_POSTURE:   nSlotId = SID_ATTR_CHAR_POSTURE; break;
632                 case SID_ATTR_CHAR_LATIN_WEIGHT:    nSlotId = SID_ATTR_CHAR_WEIGHT; break;
633                 }
634             }
635 
636             WhichId nWhich = rPool.GetWhich( nSlotId );
637             bool bIsInPool = rPool.IsInRange( nWhich );
638             if ( bIsInPool )
639             {
640                 #if OSL_DEBUG_LEVEL > 0
641                     bool bFeatureIsEnabled = aFeature->second->isFeatureEnabled();
642                     ::rtl::OString sMessage( "FmTextControlShell::transferFeatureStatesToItemSet: found a feature state for " );
643                     sMessage += sUnoSlotNameAscii;
644                     if ( !bFeatureIsEnabled )
645                         sMessage += " (disabled)";
646                     DBG_TRACE( sMessage );
647                 #endif
648 
649                 lcl_translateUnoStateToItem( nSlotId, aFeature->second->getFeatureState(), _rSet );
650             }
651             #if OSL_DEBUG_LEVEL > 0
652             else
653             {
654                 ::rtl::OString sMessage( "FmTextControlShell::transferFeatureStatesToItemSet: found a feature state for " );
655                 sMessage += sUnoSlotNameAscii;
656                 sMessage += ", but could not translate it into an item!";
657                 DBG_TRACE( sMessage );
658             }
659             #endif
660         }
661     }
662 
663     //------------------------------------------------------------------------
executeAttributeDialog(AttributeSet _eSet,SfxRequest & _rReq)664     void FmTextControlShell::executeAttributeDialog( AttributeSet _eSet, SfxRequest& _rReq )
665     {
666         const SvxFontListItem* pFontList = PTR_CAST( SvxFontListItem, m_pViewFrame->GetObjectShell()->GetItem( SID_ATTR_CHAR_FONTLIST ) );
667         DBG_ASSERT( pFontList, "FmTextControlShell::executeAttributeDialog: no font list item!" );
668         if ( !pFontList )
669             return;
670 
671         SfxItemPool* pPool = EditEngine::CreatePool();
672         pPool->FreezeIdRanges();
673         ::std::auto_ptr< SfxItemSet > pPureItems( new SfxItemSet( *pPool ) );
674 
675         // put the current states of the items into the set
676         ::std::auto_ptr< SfxAllItemSet > pCurrentItems( new SfxAllItemSet( *pPureItems ) );
677         transferFeatureStatesToItemSet( m_aControlFeatures, *pCurrentItems );
678 
679         // additional items, which we are not responsible for at the SfxShell level,
680         // but which need to be forwarded to the dialog, anyway
681         ControlFeatures aAdditionalFestures;
682         fillFeatureDispatchers( m_xActiveControl, pDialogSlots, aAdditionalFestures );
683         transferFeatureStatesToItemSet( aAdditionalFestures, *pCurrentItems, true );
684 
685         ::std::auto_ptr< SfxTabDialog > pDialog ( _eSet == eCharAttribs
686                                                 ? static_cast< SfxTabDialog* >( new TextControlCharAttribDialog( NULL, *pCurrentItems, *pFontList ) )
687                                                 : static_cast< SfxTabDialog* >( new TextControlParaAttribDialog( NULL, *pCurrentItems ) ) );
688         if ( RET_OK == pDialog->Execute() )
689         {
690             const SfxItemSet& rModifiedItems = *pDialog->GetOutputItemSet();
691             for ( WhichId nWhich = pPool->GetFirstWhich(); nWhich <= pPool->GetLastWhich(); ++nWhich )
692             {
693                 if ( rModifiedItems.GetItemState( nWhich ) == SFX_ITEM_SET )
694                 {
695                     SfxSlotId nSlotForItemSet = pPool->GetSlotId( nWhich );
696                     const SfxPoolItem* pModifiedItem = rModifiedItems.GetItem( nWhich );
697 
698 
699                     SfxSlotId nSlotForDispatcher = nSlotForItemSet;
700                     switch ( nSlotForDispatcher )
701                     {
702                         case SID_ATTR_CHAR_FONT:      nSlotForDispatcher = SID_ATTR_CHAR_LATIN_FONT; break;
703                         case SID_ATTR_CHAR_FONTHEIGHT:nSlotForDispatcher = SID_ATTR_CHAR_LATIN_FONTHEIGHT; break;
704                         case SID_ATTR_CHAR_LANGUAGE:  nSlotForDispatcher = SID_ATTR_CHAR_LATIN_LANGUAGE; break;
705                         case SID_ATTR_CHAR_POSTURE:   nSlotForDispatcher = SID_ATTR_CHAR_LATIN_POSTURE; break;
706                         case SID_ATTR_CHAR_WEIGHT:    nSlotForDispatcher = SID_ATTR_CHAR_LATIN_WEIGHT; break;
707                     }
708 
709                     // do we already have a dispatcher for this slot/feature?
710                     ControlFeatures::const_iterator aFeaturePos = m_aControlFeatures.find( nSlotForDispatcher );
711                     bool bFound = aFeaturePos != m_aControlFeatures.end( );
712 
713                     if ( !bFound )
714                     {
715                         aFeaturePos = aAdditionalFestures.find( nSlotForDispatcher );
716                         bFound = aFeaturePos != aAdditionalFestures.end( );
717                     }
718 
719                     if ( bFound )
720                     {
721                         Sequence< PropertyValue > aArgs;
722                         // temporarily put the modified item into a "clean" set,
723                         // and let TransformItems calc the respective UNO parameters
724                         pPureItems->Put( *pModifiedItem );
725                         TransformItems( nSlotForItemSet, *pPureItems, aArgs );
726                         pPureItems->ClearItem( nWhich );
727 
728                         if  (   ( nSlotForItemSet == SID_ATTR_PARA_HANGPUNCTUATION )
729                             ||  ( nSlotForItemSet == SID_ATTR_PARA_FORBIDDEN_RULES )
730                             ||  ( nSlotForItemSet == SID_ATTR_PARA_SCRIPTSPACE )
731                             )
732                         {
733                             // these are no UNO slots, they need special handling since TransformItems cannot
734                             // handle them
735                             DBG_ASSERT( aArgs.getLength() == 0, "FmTextControlShell::executeAttributeDialog: these are no UNO slots - are they?" );
736 
737                             const SfxBoolItem* pBoolItem = PTR_CAST( SfxBoolItem, pModifiedItem );
738                             DBG_ASSERT( pBoolItem, "FmTextControlShell::executeAttributeDialog: no bool item?!" );
739                             if ( pBoolItem )
740                             {
741                                 aArgs.realloc( 1 );
742                                 aArgs[ 0 ].Name = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Enable" ) );
743                                 aArgs[ 0 ].Value <<= (sal_Bool)pBoolItem->GetValue();
744                             }
745                         }
746 
747                         // dispatch this
748                         aFeaturePos->second->dispatch( aArgs );
749                     }
750                 #if OSL_DEBUG_LEVEL > 0
751                     else
752                     {
753                         ::rtl::OString sError( "FmTextControShell::executeAttributeDialog: Could not handle the following item:" );
754                         sError += "\n  SlotID: "; sError += ::rtl::OString::valueOf( (sal_Int32)nSlotForItemSet );
755                         sError += "\n  WhichID: "; sError += ::rtl::OString::valueOf( (sal_Int32)nWhich );
756                         sError += "\n  UNO name: ";
757 
758                         ::rtl::OUString sUnoSlotName = lcl_getUnoSlotName( *SFX_APP(), nSlotForItemSet );
759                         if ( sUnoSlotName.getLength() )
760                             sError += ::rtl::OString( sUnoSlotName.getStr(), sUnoSlotName.getLength(), RTL_TEXTENCODING_ASCII_US );
761                         else
762                             sError += "unknown (no SfxSlot)";
763                         DBG_ERROR( sError.getStr() );
764                     }
765                 #endif
766                 }
767             }
768             _rReq.Done( rModifiedItems );
769         }
770 
771         pDialog.reset();
772         pCurrentItems.reset();
773         pPureItems.reset();
774         SfxItemPool::Free(pPool);
775     }
776 
777     //------------------------------------------------------------------------
executeSelectAll()778     bool FmTextControlShell::executeSelectAll( )
779     {
780         try
781         {
782             if ( m_xActiveTextComponent.is() )
783             {
784                 sal_Int32 nTextLen = m_xActiveTextComponent->getText().getLength();
785                 m_xActiveTextComponent->setSelection( awt::Selection( 0, nTextLen ) );
786                 return true;
787             }
788         }
789         catch( const Exception& )
790         {
791             DBG_UNHANDLED_EXCEPTION();
792         }
793         return false;   // not handled
794     }
795 
796     //------------------------------------------------------------------------
executeClipboardSlot(SfxSlotId _nSlot)797     bool FmTextControlShell::executeClipboardSlot( SfxSlotId _nSlot )
798     {
799         try
800         {
801             if ( m_xActiveTextComponent.is() )
802             {
803                 switch ( _nSlot )
804                 {
805                 case SID_COPY:
806                 case SID_CUT:
807                 {
808                     ::rtl::OUString sSelectedText( m_xActiveTextComponent->getSelectedText() );
809                     ::svt::OStringTransfer::CopyString( sSelectedText, lcl_getWindow( m_xActiveControl ) );
810                     if ( SID_CUT == _nSlot )
811                     {
812                         awt::Selection aSelection( m_xActiveTextComponent->getSelection() );
813                         m_xActiveTextComponent->insertText( aSelection, ::rtl::OUString() );
814                     }
815                 }
816                 break;
817                 case SID_PASTE:
818                 {
819                     ::rtl::OUString sClipboardContent;
820                     OSL_VERIFY( ::svt::OStringTransfer::PasteString( sClipboardContent, lcl_getWindow( m_xActiveControl ) ) );
821                     awt::Selection aSelection( m_xActiveTextComponent->getSelection() );
822                     m_xActiveTextComponent->insertText( aSelection, sClipboardContent );
823                 }
824                 break;
825                 default:
826                     OSL_ENSURE( sal_False, "FmTextControlShell::executeClipboardSlot: invalid slot!" );
827                 }
828                 return true;
829             }
830         }
831         catch( const Exception& )
832         {
833             DBG_UNHANDLED_EXCEPTION();
834         }
835         return false;   // not handled
836     }
837 
838     //------------------------------------------------------------------------
ExecuteTextAttribute(SfxRequest & _rReq)839     void FmTextControlShell::ExecuteTextAttribute( SfxRequest& _rReq )
840     {
841 	    SfxSlotId nSlot = _rReq.GetSlot();
842 
843         ControlFeatures::const_iterator aFeaturePos = m_aControlFeatures.find( nSlot );
844         if ( aFeaturePos == m_aControlFeatures.end() )
845         {
846             // special slots
847             switch ( nSlot )
848             {
849             case SID_CHAR_DLG:
850                 executeAttributeDialog( eCharAttribs, _rReq );
851                 break;
852 
853             case SID_PARA_DLG:
854                 executeAttributeDialog( eParaAttribs, _rReq );
855                 break;
856 
857             case SID_SELECTALL:
858                 executeSelectAll();
859                 break;
860 
861             case SID_CUT:
862             case SID_COPY:
863             case SID_PASTE:
864                 executeClipboardSlot( nSlot );
865                 break;
866 
867             default:
868                 DBG_ASSERT( aFeaturePos != m_aControlFeatures.end(), "FmTextControShell::ExecuteTextAttribute: I have no such dispatcher, and cannot handle it at all!" );
869                 return;
870             }
871         }
872         else
873         {
874             // slots which are dispatched to the control
875 
876             switch ( nSlot )
877             {
878             case SID_ATTR_CHAR_STRIKEOUT:
879             case SID_ATTR_CHAR_UNDERLINE:
880             case SID_ATTR_CHAR_OVERLINE:
881             {
882                 SfxItemSet aToggled( *_rReq.GetArgs() );
883 
884                 lcl_translateUnoStateToItem( nSlot, aFeaturePos->second->getFeatureState(), aToggled );
885                 WhichId nWhich = aToggled.GetPool()->GetWhich( nSlot );
886                 const SfxPoolItem* pItem = aToggled.GetItem( nWhich );
887                 if ( ( SID_ATTR_CHAR_UNDERLINE == nSlot ) || ( SID_ATTR_CHAR_OVERLINE == nSlot ) )
888                 {
889                     const SvxOverlineItem* pTextLine = PTR_CAST( SvxOverlineItem, pItem );
890                     DBG_ASSERT( pTextLine, "FmTextControlShell::ExecuteTextAttribute: ooops - no underline/overline item!" );
891                     if ( pTextLine )
892                     {
893             		    FontUnderline eTL = pTextLine->GetLineStyle();
894 						if ( SID_ATTR_CHAR_UNDERLINE == nSlot ) {
895 	            		    aToggled.Put( SvxUnderlineItem( eTL == UNDERLINE_SINGLE ? UNDERLINE_NONE : UNDERLINE_SINGLE, nWhich ) );
896 						} else {
897 	            		    aToggled.Put( SvxOverlineItem( eTL == UNDERLINE_SINGLE ? UNDERLINE_NONE : UNDERLINE_SINGLE, nWhich ) );
898 						}
899                     }
900                 }
901                 else
902                 {
903                     const SvxCrossedOutItem* pCrossedOut = PTR_CAST( SvxCrossedOutItem, pItem );
904                     DBG_ASSERT( pCrossedOut, "FmTextControlShell::ExecuteTextAttribute: ooops - no CrossedOut item!" );
905                     if ( pCrossedOut )
906                     {
907             		    FontStrikeout eFS = pCrossedOut->GetStrikeout();
908             		    aToggled.Put( SvxCrossedOutItem( eFS == STRIKEOUT_SINGLE ? STRIKEOUT_NONE : STRIKEOUT_SINGLE, nWhich ) );
909                     }
910                 }
911 
912                 Sequence< PropertyValue > aArguments;
913                 TransformItems( nSlot, aToggled, aArguments );
914                 aFeaturePos->second->dispatch( aArguments );
915             }
916             break;
917 
918             case SID_ATTR_CHAR_FONTHEIGHT:
919             case SID_ATTR_CHAR_FONT:
920             case SID_ATTR_CHAR_POSTURE:
921             case SID_ATTR_CHAR_WEIGHT:
922             case SID_ATTR_CHAR_SHADOWED:
923             case SID_ATTR_CHAR_CONTOUR:
924             case SID_SET_SUPER_SCRIPT:
925             case SID_SET_SUB_SCRIPT:
926             {
927                 const SfxItemSet* pArgs = _rReq.GetArgs();
928                 Sequence< PropertyValue > aArgs;
929                 if ( pArgs )
930                     TransformItems( nSlot, *pArgs, aArgs );
931                 aFeaturePos->second->dispatch( aArgs );
932             }
933             break;
934 
935             default:
936                 if ( aFeaturePos->second->isFeatureEnabled() )
937                     aFeaturePos->second->dispatch();
938                 break;
939             }
940         }
941         _rReq.Done();
942     }
943 
944     //------------------------------------------------------------------------
GetTextAttributeState(SfxItemSet & _rSet)945     void FmTextControlShell::GetTextAttributeState( SfxItemSet& _rSet )
946     {
947 	    SfxWhichIter aIter( _rSet );
948 	    sal_uInt16 nSlot = aIter.FirstWhich();
949 	    while ( nSlot )
950 	    {
951             if  (   ( nSlot == SID_ATTR_PARA_LEFT_TO_RIGHT )
952                 ||  ( nSlot == SID_ATTR_PARA_RIGHT_TO_LEFT )
953                 )
954             {
955                 if ( !SvtLanguageOptions().IsCTLFontEnabled() )
956                 {
957                     _rSet.DisableItem( nSlot );
958                     nSlot = aIter.NextWhich();
959                     continue;
960                 }
961             }
962 
963             ControlFeatures::const_iterator aFeaturePos = m_aControlFeatures.find( nSlot );
964             if ( aFeaturePos != m_aControlFeatures.end() )
965             {
966                 if ( aFeaturePos->second->isFeatureEnabled() )
967                     lcl_translateUnoStateToItem( nSlot, aFeaturePos->second->getFeatureState(), _rSet );
968                 else
969                     _rSet.DisableItem( nSlot );
970             }
971             else
972             {
973                 bool bDisable = false;
974 
975                 bool bNeedWriteableControl = false;
976                 bool bNeedTextComponent = false;
977                 bool bNeedSelection = false;
978 
979                 switch ( nSlot )
980                 {
981                 case SID_CHAR_DLG:
982                 case SID_PARA_DLG:
983                     bDisable |= m_aControlFeatures.empty();
984                     bNeedWriteableControl = true;
985                     break;
986 
987                 case SID_CUT:
988                     bNeedSelection = true;
989                     bNeedTextComponent = true;
990                     bNeedWriteableControl = true;
991                     DBG_TRACE( "FmTextControlShell::ClipBoard: need to invalidate again" );
992                     m_bNeedClipboardInvalidation = true;
993                     break;
994 
995                 case SID_PASTE:
996                 {
997                     Window* pActiveControlVCLWindow = lcl_getWindow( m_xActiveControl );
998                     if ( pActiveControlVCLWindow )
999                     {
1000 		                TransferableDataHelper aDataHelper( TransferableDataHelper::CreateFromSystemClipboard( pActiveControlVCLWindow) );
1001                         bDisable |= !aDataHelper.HasFormat( SOT_FORMAT_STRING );
1002                     }
1003                     else
1004                         bDisable |= true;
1005 
1006                     bNeedTextComponent = true;
1007                     bNeedWriteableControl = true;
1008                 }
1009                 break;
1010 
1011                 case SID_COPY:
1012                     bNeedTextComponent = true;
1013                     bNeedSelection = true;
1014                     break;
1015 
1016                 case SID_SELECTALL:
1017                     bNeedTextComponent = true;
1018                     break;
1019 
1020                 default:
1021                     // slot is unknown at all
1022                     bDisable |= true;
1023                     break;
1024                 }
1025                 OSL_POSTCOND( !bNeedSelection || bNeedTextComponent, "FmTextControlShell::GetTextAttributeState: bNeedSelection should imply bNeedTextComponent!" );
1026 
1027                 if ( !bDisable && bNeedWriteableControl )
1028                     bDisable |= !IsActiveControl( ) || m_bActiveControlIsReadOnly;
1029 
1030                 if ( !bDisable && bNeedTextComponent )
1031                     bDisable |= !m_xActiveTextComponent.is();
1032 
1033                 if ( !bDisable && bNeedSelection )
1034                 {
1035                     awt::Selection aSelection = m_xActiveTextComponent->getSelection();
1036                     bDisable |= aSelection.Min == aSelection.Max;
1037                 }
1038 
1039                 if ( bDisable )
1040                     _rSet.DisableItem( nSlot );
1041             }
1042 
1043             nSlot = aIter.NextWhich();
1044 	    }
1045     }
1046 
1047     //------------------------------------------------------------------------
IsActiveControl(bool _bCountRichTextOnly) const1048     bool FmTextControlShell::IsActiveControl( bool _bCountRichTextOnly ) const
1049     {
1050         if ( _bCountRichTextOnly && !m_bActiveControlIsRichText )
1051             return false;
1052 
1053         return m_bActiveControl;
1054     }
1055 
1056     //------------------------------------------------------------------------
dispose()1057     void FmTextControlShell::dispose()
1058     {
1059         if ( IsActiveControl() )
1060             controlDeactivated();
1061         if ( isControllerListening() )
1062             stopControllerListening();
1063     }
1064 
1065     //------------------------------------------------------------------------
designModeChanged(bool)1066     void FmTextControlShell::designModeChanged( bool /*_bNewDesignMode*/ )
1067     {
1068         m_rBindings.Invalidate( pTextControlSlots );
1069     }
1070 
1071     //------------------------------------------------------------------------
formActivated(const Reference<XFormController> & _rxController)1072     void FmTextControlShell::formActivated( const Reference< XFormController >& _rxController )
1073     {
1074 #if OSL_DEBUG_LEVEL > 0
1075         ::rtl::OString sTrace( "FmTextControlShell::formActivated: 0x" );
1076         sTrace += ::rtl::OString::valueOf( (sal_IntPtr)_rxController.get(), 16 );
1077         DBG_TRACE( sTrace );
1078 #endif
1079 
1080         DBG_ASSERT( _rxController.is(), "FmTextControlShell::formActivated: invalid controller!" );
1081         if ( !_rxController.is() )
1082             return;
1083 
1084         // sometimes, a form controller notifies activations, even if it's already activated
1085         if ( m_xActiveController == _rxController )
1086             return;
1087 
1088         try
1089         {
1090             startControllerListening( _rxController );
1091             controlActivated( _rxController->getCurrentControl() );
1092         }
1093         catch( const Exception& )
1094         {
1095             DBG_UNHANDLED_EXCEPTION();
1096         }
1097     }
1098 
1099     //------------------------------------------------------------------------
formDeactivated(const Reference<XFormController> & _rxController)1100     void FmTextControlShell::formDeactivated( const Reference< XFormController >& _rxController )
1101     {
1102 #if OSL_DEBUG_LEVEL > 0
1103         ::rtl::OString sTrace( "FmTextControlShell::formDeactivated: 0x" );
1104         sTrace += ::rtl::OString::valueOf( (sal_IntPtr)_rxController.get(), 16 );
1105         DBG_TRACE( sTrace );
1106 #endif
1107         (void)_rxController;
1108 
1109         if ( IsActiveControl() )
1110             controlDeactivated();
1111         if ( isControllerListening() )
1112             stopControllerListening();
1113     }
1114 
1115     //------------------------------------------------------------------------
startControllerListening(const Reference<XFormController> & _rxController)1116     void FmTextControlShell::startControllerListening( const Reference< XFormController >& _rxController )
1117     {
1118         OSL_PRECOND( _rxController.is(), "FmTextControlShell::startControllerListening: invalid controller!" );
1119         if ( !_rxController.is() )
1120             return;
1121 
1122         OSL_PRECOND( !isControllerListening(), "FmTextControlShell::startControllerListening: already listening!" );
1123         if ( isControllerListening() )
1124             stopControllerListening( );
1125         DBG_ASSERT( !isControllerListening(), "FmTextControlShell::startControllerListening: inconsistence!" );
1126 
1127         try
1128         {
1129             Sequence< Reference< XControl > > aControls( _rxController->getControls() );
1130             m_aControlObservers.resize( 0 );
1131             m_aControlObservers.reserve( aControls.getLength() );
1132 
1133             const Reference< XControl >* pControls = aControls.getConstArray();
1134             const Reference< XControl >* pControlsEnd = pControls + aControls.getLength();
1135             for ( ; pControls != pControlsEnd; ++pControls )
1136             {
1137                 m_aControlObservers.push_back( FocusListenerAdapter( new FmFocusListenerAdapter( *pControls, this ) ) );
1138             }
1139         }
1140         catch( const Exception& )
1141         {
1142             DBG_UNHANDLED_EXCEPTION();
1143         }
1144 
1145         m_xActiveController = _rxController;
1146     }
1147 
1148     //------------------------------------------------------------------------
stopControllerListening()1149     void FmTextControlShell::stopControllerListening( )
1150     {
1151         OSL_PRECOND( isControllerListening(), "FmTextControlShell::stopControllerListening: inconsistence!" );
1152 
1153         // dispose all listeners associated with the controls of the active controller
1154         for (   FocusListenerAdapters::iterator aLoop = m_aControlObservers.begin();
1155                 aLoop != m_aControlObservers.end();
1156                 ++aLoop
1157             )
1158         {
1159             (*aLoop)->dispose();
1160         }
1161 
1162         FocusListenerAdapters aEmpty;
1163         m_aControlObservers.swap( aEmpty );
1164 
1165         m_xActiveController.clear();
1166     }
1167 
1168     //------------------------------------------------------------------------
implClearActiveControlRef()1169     void FmTextControlShell::implClearActiveControlRef()
1170     {
1171         // no more features for this control
1172         for (   ControlFeatures::iterator aLoop = m_aControlFeatures.begin();
1173                 aLoop != m_aControlFeatures.end();
1174                 ++aLoop
1175             )
1176         {
1177             aLoop->second->dispose();
1178         }
1179 
1180         ControlFeatures aEmpty;
1181         m_aControlFeatures.swap( aEmpty );
1182 
1183         if ( m_aContextMenuObserver.get() )
1184         {
1185             m_aContextMenuObserver->dispose();
1186             m_aContextMenuObserver = MouseListenerAdapter();
1187         }
1188 
1189         if ( m_xActiveTextComponent.is() )
1190         {
1191             DBG_TRACE( "FmTextControlShell::ClipBoard: stopping timer for clipboard invalidation" );
1192             m_aClipboardInvalidation.Stop();
1193         }
1194         // no more active control
1195         m_xActiveControl.clear();
1196         m_xActiveTextComponent.clear();
1197         m_bActiveControlIsReadOnly = true;
1198         m_bActiveControlIsRichText = false;
1199         m_bActiveControl = false;
1200     }
1201 
1202     //------------------------------------------------------------------------
controlDeactivated()1203     void FmTextControlShell::controlDeactivated( )
1204     {
1205         DBG_ASSERT( IsActiveControl(), "FmTextControlShell::controlDeactivated: no active control!" );
1206 
1207         m_bActiveControl = false;
1208 
1209         m_rBindings.Invalidate( pTextControlSlots );
1210     }
1211 
1212     //------------------------------------------------------------------------
controlActivated(const Reference<XControl> & _rxControl)1213     void FmTextControlShell::controlActivated( const Reference< XControl >& _rxControl )
1214     {
1215         // ensure that all knittings with the previously active control are lost
1216         if ( m_xActiveControl.is() )
1217             implClearActiveControlRef();
1218         DBG_ASSERT( m_aControlFeatures.empty(), "FmTextControlShell::controlActivated: should have no dispatchers when I'm here!" );
1219 
1220 #if OSL_DEBUG_LEVEL > 0
1221         {
1222             Sequence< Reference< XControl > > aActiveControls;
1223             if ( m_xActiveController.is() )
1224                 aActiveControls = m_xActiveController->getControls();
1225 
1226             bool bFoundThisControl = false;
1227 
1228             const Reference< XControl >* pControls = aActiveControls.getConstArray();
1229             const Reference< XControl >* pControlsEnd = pControls + aActiveControls.getLength();
1230             for ( ; ( pControls != pControlsEnd ) && !bFoundThisControl; ++pControls )
1231             {
1232                 if ( *pControls == _rxControl )
1233                     bFoundThisControl = true;
1234             }
1235             DBG_ASSERT( bFoundThisControl, "FmTextControlShell::controlActivated: only controls which belong to the active controller can be activated!" );
1236         }
1237 #endif
1238         // ask the control for dispatchers for our text-related slots
1239         fillFeatureDispatchers( _rxControl, pTextControlSlots, m_aControlFeatures );
1240 
1241         // remember this control
1242         m_xActiveControl = _rxControl;
1243         m_xActiveTextComponent = m_xActiveTextComponent.query( _rxControl );
1244         m_bActiveControlIsReadOnly = lcl_determineReadOnly( m_xActiveControl );
1245         m_bActiveControlIsRichText = lcl_isRichText( m_xActiveControl );
1246 
1247         // if we found a rich text control, we need context menu support
1248         if ( m_bActiveControlIsRichText )
1249         {
1250             DBG_ASSERT( NULL == m_aContextMenuObserver.get(), "FmTextControlShell::controlActivated: already have an observer!" );
1251             m_aContextMenuObserver = MouseListenerAdapter( new FmMouseListenerAdapter( _rxControl, this ) );
1252         }
1253 
1254         if ( m_xActiveTextComponent.is() )
1255         {
1256             DBG_TRACE( "FmTextControlShell::ClipBoard: starting timer for clipboard invalidation" );
1257             m_aClipboardInvalidation.Start();
1258         }
1259 
1260         m_bActiveControl = true;
1261 
1262     	m_rBindings.Invalidate( pTextControlSlots );
1263 
1264         if ( m_pViewFrame )
1265             m_pViewFrame->UIFeatureChanged();
1266 
1267         // don't call the activation handler if we don't have any slots we can serve
1268         // The activation handler is used to put the shell on the top of the dispatcher stack,
1269         // so it's preferred when slots are distributed.
1270         // Note that this is a slight hack, to prevent that we grab slots from the SfxDispatcher
1271         // which should be served by other shells (e.g. Cut/Copy/Paste).
1272         // A real solution would be a forwarding-mechanism for slots: We should be on the top
1273         // if we're active, but if we cannot handle the slot, then we need to tell the dispatcher
1274         // to skip our shell, and pass the slot to the next one. However, this mechanism is not
1275         // not in place in SFX.
1276         // Another possibility would be to have dedicated shells for the slots which we might
1277         // or might not be able to serve. However, this could probably increase the number of
1278         // shells too much (In theory, nearly every slot could have an own shell then).
1279         //
1280         // #i51621# / 2005-08-19 / frank.schoenheit@sun.com
1281         bool bHaveAnyServeableSlots = m_xActiveTextComponent.is() || !m_aControlFeatures.empty();
1282         if ( m_aControlActivationHandler.IsSet() && bHaveAnyServeableSlots )
1283             m_aControlActivationHandler.Call( NULL );
1284 
1285         m_bNeedClipboardInvalidation = true;
1286     }
1287 
1288     //------------------------------------------------------------------------
fillFeatureDispatchers(const Reference<XControl> _rxControl,SfxSlotId * _pZeroTerminatedSlots,ControlFeatures & _rDispatchers)1289     void FmTextControlShell::fillFeatureDispatchers(  const Reference< XControl > _rxControl, SfxSlotId* _pZeroTerminatedSlots,
1290             ControlFeatures& _rDispatchers )
1291     {
1292         Reference< XDispatchProvider > xProvider( _rxControl, UNO_QUERY );
1293         SfxApplication* pApplication = SFX_APP();
1294         DBG_ASSERT( pApplication, "FmTextControlShell::fillFeatureDispatchers: no SfxApplication!" );
1295         if ( xProvider.is() && pApplication )
1296         {
1297             SfxSlotId* pSlots = _pZeroTerminatedSlots;
1298             while ( *pSlots )
1299             {
1300                 FmTextControlFeature* pDispatcher = implGetFeatureDispatcher( xProvider, pApplication, *pSlots );
1301                 if ( pDispatcher )
1302                     _rDispatchers.insert( ControlFeatures::value_type( *pSlots, ControlFeature( pDispatcher ) ) );
1303 
1304                 ++pSlots;
1305             }
1306         }
1307     }
1308 
1309     //------------------------------------------------------------------------
impl_parseURL_nothrow(URL & _rURL)1310     void FmTextControlShell::impl_parseURL_nothrow( URL& _rURL )
1311     {
1312         try
1313         {
1314             if ( !m_xURLTransformer.is() )
1315             {
1316                 ::comphelper::ComponentContext aContext( ::comphelper::getProcessServiceFactory() );
1317                 aContext.createComponent( "com.sun.star.util.URLTransformer", m_xURLTransformer );
1318             }
1319             if ( m_xURLTransformer.is() )
1320                 m_xURLTransformer->parseStrict( _rURL );
1321         }
1322         catch( const Exception& )
1323         {
1324         	DBG_UNHANDLED_EXCEPTION();
1325         }
1326     }
1327 
1328     //------------------------------------------------------------------------
implGetFeatureDispatcher(const Reference<XDispatchProvider> & _rxProvider,SfxApplication * _pApplication,SfxSlotId _nSlot)1329     FmTextControlFeature* FmTextControlShell::implGetFeatureDispatcher( const Reference< XDispatchProvider >& _rxProvider, SfxApplication* _pApplication, SfxSlotId _nSlot )
1330     {
1331         OSL_PRECOND( _rxProvider.is() && _pApplication, "FmTextControlShell::implGetFeatureDispatcher: invalid arg(s)!" );
1332         URL aFeatureURL;
1333         aFeatureURL.Complete = lcl_getUnoSlotName( *_pApplication, _nSlot );
1334         impl_parseURL_nothrow( aFeatureURL );
1335         Reference< XDispatch > xDispatcher = _rxProvider->queryDispatch( aFeatureURL, ::rtl::OUString(), 0xFF );
1336         if ( xDispatcher.is() )
1337             return new FmTextControlFeature( xDispatcher, aFeatureURL, _nSlot, this );
1338         return NULL;
1339     }
1340 
1341     //------------------------------------------------------------------------
Invalidate(SfxSlotId _nSlot)1342     void FmTextControlShell::Invalidate( SfxSlotId _nSlot )
1343     {
1344     	m_rBindings.Invalidate( _nSlot );
1345         // despite this method being called "Invalidate", we also update here - this gives more immediate
1346         // feedback in the UI
1347     	m_rBindings.Update( _nSlot );
1348     }
1349 
1350     //------------------------------------------------------------------------
focusGained(const::com::sun::star::awt::FocusEvent & _rEvent)1351     void FmTextControlShell::focusGained( const ::com::sun::star::awt::FocusEvent& _rEvent )
1352     {
1353         Reference< XControl > xControl( _rEvent.Source, UNO_QUERY );
1354 
1355 #if OSL_DEBUG_LEVEL > 0
1356         ::rtl::OString sTrace( "FmTextControlShell::focusGained: 0x" );
1357         sTrace += ::rtl::OString::valueOf( (sal_IntPtr)xControl.get(), 16 );
1358         DBG_TRACE( sTrace );
1359 #endif
1360 
1361         DBG_ASSERT( xControl.is(), "FmTextControlShell::focusGained: suspicious focus event!" );
1362         if ( xControl.is() )
1363             controlActivated( xControl );
1364     }
1365 
1366     //------------------------------------------------------------------------
focusLost(const::com::sun::star::awt::FocusEvent & _rEvent)1367     void FmTextControlShell::focusLost( const ::com::sun::star::awt::FocusEvent& _rEvent )
1368     {
1369         Reference< XControl > xControl( _rEvent.Source, UNO_QUERY );
1370 
1371 #if OSL_DEBUG_LEVEL > 0
1372         ::rtl::OString sTrace( "FmTextControlShell::focusLost: 0x" );
1373         sTrace += ::rtl::OString::valueOf( (sal_IntPtr)xControl.get(), 16 );
1374         DBG_TRACE( sTrace );
1375 #endif
1376 
1377         m_bActiveControl = false;
1378     }
1379 
1380     //------------------------------------------------------------------------
ForgetActiveControl()1381     void FmTextControlShell::ForgetActiveControl()
1382     {
1383         implClearActiveControlRef();
1384     }
1385 
1386     //------------------------------------------------------------------------
contextMenuRequested(const awt::MouseEvent &)1387     void FmTextControlShell::contextMenuRequested( const awt::MouseEvent& /*_rEvent*/ )
1388     {
1389         m_rBindings.GetDispatcher()->ExecutePopup( SVX_RES( RID_FM_TEXTATTRIBUTE_MENU ) );
1390     }
1391 
1392 //........................................................................
1393 } // namespace svx
1394 //........................................................................
1395