Coverage Report

Created: 2026-03-31 11:00

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