Coverage Report

Created: 2026-02-14 09:37

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/framework/source/uielement/generictoolbarcontroller.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
#include <framework/generictoolbarcontroller.hxx>
21
22
#include <com/sun/star/util/XURLTransformer.hpp>
23
#include <com/sun/star/frame/XDispatch.hpp>
24
#include <com/sun/star/frame/XFrame.hpp>
25
#include <com/sun/star/lang/DisposedException.hpp>
26
#include <com/sun/star/frame/status/ItemStatus.hpp>
27
#include <com/sun/star/frame/status/Visibility.hpp>
28
#include <com/sun/star/frame/ControlCommand.hpp>
29
30
#include <comphelper/propertyvalue.hxx>
31
#include <vcl/image.hxx>
32
#include <svl/imageitm.hxx>
33
#include <vcl/commandinfoprovider.hxx>
34
#include <vcl/svapp.hxx>
35
#include <vcl/vclevent.hxx>
36
#include <vcl/weld/weld.hxx>
37
#include <tools/urlobj.hxx>
38
#include <toolkit/helper/vclunohelper.hxx>
39
#include <strings.hrc>
40
#include <classes/fwkresid.hxx>
41
42
using namespace ::com::sun::star::uno;
43
using namespace ::com::sun::star::beans;
44
using namespace ::com::sun::star::lang;
45
using namespace ::com::sun::star::frame;
46
using namespace ::com::sun::star::frame::status;
47
48
namespace framework
49
{
50
51
static bool isEnumCommand( std::u16string_view rCommand )
52
0
{
53
0
    INetURLObject aURL( rCommand );
54
55
0
    return ( aURL.GetProtocol() == INetProtocol::Uno ) &&
56
0
           ( aURL.GetURLPath().indexOf( '.' ) != -1);
57
0
}
58
59
static OUString getEnumCommand( std::u16string_view rCommand )
60
0
{
61
0
    INetURLObject aURL( rCommand );
62
63
0
    OUString   aEnumCommand;
64
0
    OUString   aURLPath = aURL.GetURLPath();
65
0
    sal_Int32  nIndex   = aURLPath.indexOf( '.' );
66
0
    if (( nIndex > 0 ) && ( nIndex < aURLPath.getLength() ))
67
0
        aEnumCommand = aURLPath.copy( nIndex+1 );
68
69
0
    return aEnumCommand;
70
0
}
71
72
static OUString getMasterCommand( const OUString& rCommand )
73
0
{
74
0
    OUString aMasterCommand( rCommand );
75
0
    INetURLObject aURL( rCommand );
76
0
    if ( aURL.GetProtocol() == INetProtocol::Uno )
77
0
    {
78
0
        sal_Int32 nIndex = aURL.GetURLPath().indexOf( '.' );
79
0
        if ( nIndex )
80
0
        {
81
0
            aURL.SetURLPath( aURL.GetURLPath().subView( 0, nIndex ) );
82
0
            aMasterCommand = aURL.GetMainURL( INetURLObject::DecodeMechanism::NONE );
83
0
        }
84
0
    }
85
0
    return aMasterCommand;
86
0
}
87
88
GenericToolbarController::GenericToolbarController( const Reference< XComponentContext >&    rxContext,
89
                                                    const Reference< XFrame >&               rFrame,
90
                                                    ToolBox*                                 pToolbar,
91
                                                    ToolBoxItemId                            nID,
92
                                                    const OUString&                          aCommand ) :
93
0
    svt::ToolboxController( rxContext, rFrame, aCommand )
94
0
    ,   m_xToolbar( pToolbar )
95
0
    ,   m_nID( nID )
96
0
    ,   m_bEnumCommand( isEnumCommand( aCommand ))
97
0
    ,   m_bMirrored( false )
98
0
    ,   m_bMadeInvisible( false )
99
0
    ,   m_aEnumCommand( getEnumCommand( aCommand ))
100
0
{
101
0
    if ( m_bEnumCommand )
102
0
        addStatusListener( getMasterCommand( aCommand ) );
103
104
0
    addStatusListener( aCommand );
105
106
    // Initialization is done through ctor
107
0
    m_bInitialized = true;
108
0
}
109
110
GenericToolbarController::GenericToolbarController( const Reference< XComponentContext >&    rxContext,
111
                                                    const Reference< XFrame >&               rFrame,
112
                                                    weld::Toolbar&                           rToolbar,
113
                                                    const OUString&                          aCommand ) :
114
0
    GenericToolbarController( rxContext, rFrame, nullptr, ToolBoxItemId(0), aCommand )
115
0
{
116
0
    m_pToolbar = &rToolbar;
117
0
}
118
119
GenericToolbarController::~GenericToolbarController()
120
0
{
121
0
}
122
123
void SAL_CALL GenericToolbarController::dispose()
124
0
{
125
0
    SolarMutexGuard aSolarMutexGuard;
126
127
0
    svt::ToolboxController::dispose();
128
129
0
    m_pToolbar = nullptr;
130
0
    m_xToolbar.reset();
131
0
    m_nID = ToolBoxItemId(0);
132
0
}
133
134
void SAL_CALL GenericToolbarController::execute( sal_Int16 KeyModifier )
135
0
{
136
0
    Reference< XDispatch >       xDispatch;
137
0
    OUString                     aCommandURL;
138
139
0
    {
140
0
        SolarMutexGuard aSolarMutexGuard;
141
142
0
        if ( m_bDisposed )
143
0
            throw DisposedException();
144
145
0
        if ( !m_bInitialized || !m_xFrame.is() || m_aCommandURL.isEmpty() )
146
0
            return;
147
148
0
        URLToDispatchMap::iterator pIter = m_aListenerMap.find( m_aCommandURL );
149
150
0
        if ( pIter == m_aListenerMap.end() )
151
0
            return;
152
153
0
        aCommandURL = m_aCommandURL;
154
0
        xDispatch = pIter->second;
155
156
        // tdf#138234 If this toolbar is a floating window then
157
        // clicking on a button probably took the window focus. Let’s
158
        // give it back to the toolbar’s frame.
159
0
        if ( m_xFrame.is() && m_xFrame->getContainerWindow().is() )
160
0
            m_xFrame->getContainerWindow()->setFocus();
161
0
    }
162
163
0
    css::util::URL aTargetURL;
164
165
    // handle also command aliases
166
0
    auto aProperties = vcl::CommandInfoProvider::GetCommandProperties(m_aCommandURL,
167
0
        vcl::CommandInfoProvider::GetModuleIdentifier(m_xFrame));
168
0
    OUString sRealCommand = vcl::CommandInfoProvider::GetRealCommandForCommand(aProperties);
169
170
0
    aTargetURL.Complete = sRealCommand.isEmpty() ? aCommandURL : sRealCommand;
171
0
    if ( m_xUrlTransformer.is() )
172
0
        m_xUrlTransformer->parseStrict( aTargetURL );
173
174
    // Execute dispatch asynchronously
175
0
    ExecuteInfo* pExecuteInfo = new ExecuteInfo;
176
0
    pExecuteInfo->xDispatch     = std::move(xDispatch);
177
0
    pExecuteInfo->aTargetURL    = std::move(aTargetURL);
178
    // Add key modifier to argument list
179
0
    pExecuteInfo->aArgs = { comphelper::makePropertyValue(u"KeyModifier"_ustr, KeyModifier) };
180
181
0
    Application::PostUserEvent( LINK(nullptr, GenericToolbarController , ExecuteHdl_Impl), pExecuteInfo );
182
0
}
183
184
void GenericToolbarController::statusChanged( const FeatureStateEvent& Event )
185
0
{
186
0
    SolarMutexGuard aSolarMutexGuard;
187
188
0
    if ( m_bDisposed )
189
0
        return;
190
191
0
    if ( m_pToolbar )
192
0
    {
193
0
        m_pToolbar->set_item_sensitive(m_aCommandURL, Event.IsEnabled);
194
195
0
        bool        bValue;
196
0
        OUString    aStrValue;
197
0
        SfxImageItem aImageItem;
198
199
0
        if ( Event.State >>= bValue )
200
0
        {
201
            // Boolean, treat it as checked/unchecked
202
0
            m_pToolbar->set_item_active(m_aCommandURL, bValue);
203
0
        }
204
0
        else if ( Event.State >>= aStrValue )
205
0
        {
206
0
            m_pToolbar->set_item_label(m_aCommandURL, aStrValue);
207
0
        }
208
0
        else if ( aImageItem.PutValue( Event.State, 0 ) && aImageItem.IsMirrored() != m_bMirrored )
209
0
        {
210
0
            m_pToolbar->set_item_image_mirrored(m_aCommandURL, aImageItem.IsMirrored());
211
0
            auto xGraphic(vcl::CommandInfoProvider::GetXGraphicForCommand(m_aCommandURL, m_xFrame, m_pToolbar->get_icon_size()));
212
0
            m_pToolbar->set_item_image(m_aCommandURL, xGraphic);
213
0
            m_bMirrored = !m_bMirrored;
214
0
        }
215
0
        else
216
0
            m_pToolbar->set_item_active(m_aCommandURL, false);
217
218
0
        return;
219
0
    }
220
221
0
    if ( !m_xToolbar )
222
0
        return;
223
224
0
    m_xToolbar->EnableItem( m_nID, Event.IsEnabled );
225
226
0
    ToolBoxItemBits nItemBits = m_xToolbar->GetItemBits( m_nID );
227
0
    nItemBits &= ~ToolBoxItemBits::CHECKABLE;
228
0
    TriState eTri = TRISTATE_FALSE;
229
230
0
    bool            bValue;
231
0
    OUString        aStrValue;
232
0
    ItemStatus      aItemState;
233
0
    Visibility      aItemVisibility;
234
0
    ControlCommand  aControlCommand;
235
0
    SfxImageItem    aImageItem;
236
237
0
    if (( Event.State >>= bValue ) && !m_bEnumCommand )
238
0
    {
239
        // Boolean, treat it as checked/unchecked
240
0
        if ( m_bMadeInvisible )
241
0
            m_xToolbar->ShowItem( m_nID );
242
0
        m_xToolbar->CheckItem( m_nID, bValue );
243
0
        if ( bValue )
244
0
            eTri = TRISTATE_TRUE;
245
0
        nItemBits |= ToolBoxItemBits::CHECKABLE;
246
0
    }
247
0
    else if ( Event.State >>= aStrValue )
248
0
    {
249
0
        if ( m_bEnumCommand )
250
0
        {
251
0
            bValue = aStrValue == m_aEnumCommand;
252
253
0
            m_xToolbar->CheckItem( m_nID, bValue );
254
0
            if ( bValue )
255
0
                eTri = TRISTATE_TRUE;
256
0
            nItemBits |= ToolBoxItemBits::CHECKABLE;
257
0
        }
258
0
        else
259
0
        {
260
            // Replacement for place holders
261
0
            if ( aStrValue.startsWith("($1)") )
262
0
            {
263
0
                aStrValue = FwkResId(STR_UPDATEDOC) + " " + aStrValue.subView( 4 );
264
0
            }
265
0
            else if ( aStrValue.startsWith("($2)") )
266
0
            {
267
0
                aStrValue = FwkResId(STR_CLOSEDOC_ANDRETURN) + aStrValue.subView( 4 );
268
0
            }
269
0
            else if ( aStrValue.startsWith("($3)") )
270
0
            {
271
0
                aStrValue = FwkResId(STR_SAVECOPYDOC) + aStrValue.subView( 4 );
272
0
            }
273
0
            m_xToolbar->SetItemText( m_nID, aStrValue );
274
            // tdf#124267 strip mnemonic from tooltip
275
0
            m_xToolbar->SetQuickHelpText(m_nID, aStrValue.replaceFirst("~", ""));
276
0
        }
277
278
0
        if ( m_bMadeInvisible )
279
0
            m_xToolbar->ShowItem( m_nID );
280
0
    }
281
0
    else if (( Event.State >>= aItemState ) && !m_bEnumCommand )
282
0
    {
283
0
        eTri = TRISTATE_INDET;
284
0
        nItemBits |= ToolBoxItemBits::CHECKABLE;
285
0
        if ( m_bMadeInvisible )
286
0
            m_xToolbar->ShowItem( m_nID );
287
0
    }
288
0
    else if ( Event.State >>= aItemVisibility )
289
0
    {
290
0
        m_xToolbar->ShowItem( m_nID, aItemVisibility.bVisible );
291
0
        m_bMadeInvisible = !aItemVisibility.bVisible;
292
0
    }
293
0
    else if ( Event.State >>= aControlCommand )
294
0
    {
295
0
        if (aControlCommand.Command == "SetQuickHelpText")
296
0
        {
297
0
            for (NamedValue const& rArg : aControlCommand.Arguments)
298
0
            {
299
0
                if (rArg.Name == "HelpText")
300
0
                {
301
0
                    OUString aHelpText;
302
0
                    rArg.Value >>= aHelpText;
303
0
                    m_xToolbar->SetQuickHelpText(m_nID, aHelpText);
304
0
                    break;
305
0
                }
306
0
            }
307
0
        }
308
0
        if ( m_bMadeInvisible )
309
0
            m_xToolbar->ShowItem( m_nID );
310
0
    }
311
0
    else if ( aImageItem.PutValue( Event.State, 0 ) && aImageItem.IsMirrored() != m_bMirrored )
312
0
    {
313
0
        m_xToolbar->SetItemImageMirrorMode( m_nID, aImageItem.IsMirrored() );
314
0
        Image aImage( vcl::CommandInfoProvider::GetImageForCommand( m_aCommandURL, m_xFrame, m_xToolbar->GetImageSize() ));
315
0
        m_xToolbar->SetItemImage( m_nID, aImage );
316
0
        m_bMirrored = !m_bMirrored;
317
0
        if ( m_bMadeInvisible )
318
0
            m_xToolbar->ShowItem( m_nID );
319
0
    }
320
0
    else if ( m_bMadeInvisible )
321
0
        m_xToolbar->ShowItem( m_nID );
322
323
0
    m_xToolbar->SetItemState( m_nID, eTri );
324
0
    m_xToolbar->SetItemBits( m_nID, nItemBits );
325
0
}
326
327
IMPL_STATIC_LINK( GenericToolbarController, ExecuteHdl_Impl, void*, p, void )
328
0
{
329
0
   ExecuteInfo* pExecuteInfo = static_cast<ExecuteInfo*>(p);
330
0
   SolarMutexReleaser aReleaser;
331
0
   try
332
0
   {
333
        // Asynchronous execution as this can lead to our own destruction!
334
        // Framework can recycle our current frame and the layout manager disposes all user interface
335
        // elements if a component gets detached from its frame!
336
0
        pExecuteInfo->xDispatch->dispatch( pExecuteInfo->aTargetURL, pExecuteInfo->aArgs );
337
0
   }
338
0
   catch ( const Exception& )
339
0
   {
340
0
   }
341
342
0
   delete pExecuteInfo;
343
0
}
344
345
ImageOrientationController::ImageOrientationController(const Reference<XComponentContext>& rContext,
346
                                                       const Reference<XFrame>& rFrame,
347
                                                       const Reference<css::awt::XWindow>& rParentWindow,
348
                                                       const OUString& rModuleName)
349
0
    : ToolboxController(rContext, rFrame, u".uno:ImageOrientation"_ustr)
350
0
    , m_nRotationAngle(0_deg10)
351
0
    , m_bMirrored(false)
352
0
{
353
0
    m_sModuleName = rModuleName;
354
0
    m_xParentWindow = rParentWindow;
355
0
    initialize({});
356
0
    if (!m_pToolbar)
357
0
        VCLUnoHelper::GetWindow(getParent())->AddEventListener(LINK(this, ImageOrientationController, WindowEventListener));
358
0
}
359
360
void ImageOrientationController::dispose()
361
0
{
362
0
    ToolboxController::dispose();
363
0
    VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow(getParent());
364
0
    if (pWindow)
365
0
        pWindow->RemoveEventListener(LINK(this, ImageOrientationController, WindowEventListener));
366
0
}
367
368
IMPL_LINK(ImageOrientationController, WindowEventListener, VclWindowEvent&, rWindowEvent, void)
369
0
{
370
0
    if (m_bDisposed || rWindowEvent.GetId() != VclEventId::ToolboxItemAdded)
371
0
        return;
372
373
0
    ToolBox* pToolBox = static_cast<ToolBox*>(rWindowEvent.GetWindow());
374
0
    ToolBoxItemId nItemId = pToolBox->GetItemId(reinterpret_cast<sal_IntPtr>(rWindowEvent.GetData()));
375
0
    OUString aCommand = pToolBox->GetItemCommand(nItemId);
376
377
0
    if (vcl::CommandInfoProvider::IsMirrored(aCommand, getModuleName()))
378
0
        pToolBox->SetItemImageMirrorMode(nItemId, m_bMirrored);
379
0
    if (vcl::CommandInfoProvider::IsRotated(aCommand, getModuleName()))
380
0
        pToolBox->SetItemImageAngle(nItemId, m_nRotationAngle);
381
0
}
382
383
void ImageOrientationController::statusChanged(const css::frame::FeatureStateEvent& rEvent)
384
0
{
385
0
    if (m_bDisposed)
386
0
        throw DisposedException();
387
388
0
    SfxImageItem aItem;
389
0
    aItem.PutValue(rEvent.State, 0);
390
391
0
    if (m_bMirrored == aItem.IsMirrored() && m_nRotationAngle == aItem.GetRotation())
392
0
        return;
393
394
0
    m_bMirrored = aItem.IsMirrored();
395
0
    m_nRotationAngle = aItem.GetRotation();
396
397
0
    if (m_pToolbar)
398
0
    {
399
0
        for (int i = 0, nCount = m_pToolbar->get_n_items(); i < nCount; ++i)
400
0
        {
401
0
            OUString aCommand = m_pToolbar->get_item_ident(i);
402
0
            if (vcl::CommandInfoProvider::IsMirrored(aCommand, getModuleName()))
403
0
            {
404
0
                if (m_bMirrored)
405
0
                {
406
                    // Search for a specialized mirrored graphic
407
0
                    auto xRltbGraphic = vcl::CommandInfoProvider::GetXGraphicForCommand(
408
0
                        aCommand, m_xFrame, m_pToolbar->get_icon_size(),
409
0
                        vcl::ImageWritingDirection::RightLeftTopBottom);
410
0
                    if (xRltbGraphic)
411
0
                    {
412
0
                        m_pToolbar->set_item_image_mirrored(aCommand, false);
413
0
                        m_pToolbar->set_item_image(aCommand, xRltbGraphic);
414
0
                        continue;
415
0
                    }
416
0
                }
417
418
0
                m_pToolbar->set_item_image_mirrored(aCommand, m_bMirrored);
419
0
                auto xGraphic(vcl::CommandInfoProvider::GetXGraphicForCommand(
420
0
                    aCommand, m_xFrame, m_pToolbar->get_icon_size()));
421
0
                m_pToolbar->set_item_image(aCommand, xGraphic);
422
0
            }
423
0
        }
424
0
    }
425
0
    else
426
0
    {
427
0
        ToolBox* pToolBox = static_cast<ToolBox*>(VCLUnoHelper::GetWindow(getParent()));
428
0
        for (ToolBox::ImplToolItems::size_type i = 0; i < pToolBox->GetItemCount(); ++i)
429
0
        {
430
0
            ToolBoxItemId nItemId = pToolBox->GetItemId(i);
431
0
            OUString aCommand = pToolBox->GetItemCommand(nItemId);
432
433
0
            bool bCmdMirrored = vcl::CommandInfoProvider::IsMirrored(aCommand, getModuleName());
434
0
            if (bCmdMirrored && m_bMirrored)
435
0
            {
436
                // Search for a specialized mirrored graphic
437
0
                auto xRltbGraphic = vcl::CommandInfoProvider::GetXGraphicForCommand(
438
0
                    aCommand, m_xFrame, pToolBox->GetImageSize(),
439
0
                    vcl::ImageWritingDirection::RightLeftTopBottom);
440
0
                if (xRltbGraphic)
441
0
                {
442
0
                    pToolBox->SetItemImageMirrorMode(nItemId, false);
443
0
                    pToolBox->SetItemImageAngle(nItemId, m_nRotationAngle);
444
0
                    pToolBox->SetItemImage(nItemId, Image{ xRltbGraphic });
445
0
                    continue;
446
0
                }
447
0
            }
448
449
0
            bool bModified = false;
450
0
            if (bCmdMirrored)
451
0
            {
452
0
                pToolBox->SetItemImageMirrorMode(nItemId, m_bMirrored);
453
0
                bModified = true;
454
0
            }
455
0
            if (vcl::CommandInfoProvider::IsRotated(aCommand, getModuleName()))
456
0
            {
457
0
                pToolBox->SetItemImageAngle(nItemId, m_nRotationAngle);
458
0
                bModified = true;
459
0
            }
460
0
            if (bModified)
461
0
            {
462
0
                Image aImage(vcl::CommandInfoProvider::GetImageForCommand(aCommand, m_xFrame, pToolBox->GetImageSize()));
463
0
                pToolBox->SetItemImage(nItemId, aImage);
464
0
            }
465
0
        }
466
0
    }
467
0
}
468
469
} // namespace
470
471
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */