Coverage Report

Created: 2025-11-16 09:57

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