Coverage Report

Created: 2026-04-09 11:41

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/svx/source/stbctrls/pszctrl.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 <sal/config.h>
21
22
#include <comphelper/propertyvalue.hxx>
23
#include <vcl/commandevent.hxx>
24
#include <vcl/event.hxx>
25
#include <vcl/fieldvalues.hxx>
26
#include <vcl/status.hxx>
27
#include <vcl/image.hxx>
28
#include <vcl/settings.hxx>
29
#include <vcl/svapp.hxx>
30
#include <vcl/weld/Builder.hxx>
31
#include <vcl/weld/Menu.hxx>
32
#include <vcl/weld/weld.hxx>
33
#include <vcl/weld/weldutils.hxx>
34
#include <svl/stritem.hxx>
35
#include <svl/ptitem.hxx>
36
#include <sfx2/module.hxx>
37
#include <svx/dialmgr.hxx>
38
#include <svx/statusitem.hxx>
39
#include <svx/strings.hrc>
40
#include <svl/intitem.hxx>
41
#include <sal/log.hxx>
42
#include <tools/fldunit.hxx>
43
44
#include <svx/pszctrl.hxx>
45
46
0
#define PAINT_OFFSET    5
47
48
#include <editeng/sizeitem.hxx>
49
#include "stbctrls.h"
50
51
#include <svx/svxids.hrc>
52
#include <bitmaps.hlst>
53
#include <unotools/localedatawrapper.hxx>
54
55
#include <com/sun/star/beans/PropertyValue.hpp>
56
57
/*  [Description]
58
59
    Function used to create a text representation of
60
    a metric value
61
62
    nVal is the metric value in the unit eUnit.
63
64
    [cross reference]
65
66
    <SvxPosSizeStatusBarControl::Paint(const UserDrawEvent&)>
67
*/
68
69
OUString SvxPosSizeStatusBarControl::GetMetricStr_Impl( tools::Long nVal ) const
70
0
{
71
    // deliver and set the Metric of the application
72
0
    FieldUnit eOutUnit = SfxModule::GetModuleFieldUnit( getFrameInterface() );
73
74
0
    OUString sMetric;
75
0
    const sal_Unicode cSep = Application::GetSettings().GetLocaleDataWrapper().getNumDecimalSep()[0];
76
0
    sal_Int64 nConvVal = vcl::ConvertValue( nVal * 100, 0, 0, FieldUnit::MM_100TH, eOutUnit );
77
78
0
    if ( nConvVal < 0 && ( nConvVal / 100 == 0 ) )
79
0
        sMetric += "-";
80
0
    sMetric += OUString::number(nConvVal / 100);
81
82
0
    if( FieldUnit::NONE != eOutUnit )
83
0
    {
84
0
        sMetric += OUStringChar(cSep);
85
0
        sal_Int64 nFract = nConvVal % 100;
86
87
0
        if ( nFract < 0 )
88
0
            nFract *= -1;
89
0
        if ( nFract < 10 )
90
0
            sMetric += "0";
91
0
        sMetric += OUString::number(nFract);
92
0
    }
93
94
0
    return sMetric;
95
0
}
96
97
98
SFX_IMPL_STATUSBAR_CONTROL(SvxPosSizeStatusBarControl, SvxSizeItem);
99
100
namespace {
101
102
class FunctionPopup_Impl
103
{
104
    std::unique_ptr<weld::Builder> m_xBuilder;
105
    std::unique_ptr<weld::Menu> m_xMenu;
106
    sal_uInt32        m_nSelected;
107
    static sal_uInt16 id_to_function(std::u16string_view rIdent);
108
    static OUString function_to_id(sal_uInt16 nFunc);
109
public:
110
    explicit FunctionPopup_Impl(sal_uInt32 nCheckEncoded);
111
    OUString Execute(weld::Window* pParent, const tools::Rectangle& rRect)
112
0
    {
113
0
        return m_xMenu->popup_at_rect(pParent, rRect);
114
0
    }
115
    sal_uInt32 GetSelected(std::u16string_view curident) const;
116
};
117
118
}
119
120
sal_uInt16 FunctionPopup_Impl::id_to_function(std::u16string_view rIdent)
121
0
{
122
0
    if (rIdent == u"avg")
123
0
        return PSZ_FUNC_AVG;
124
0
    else if (rIdent == u"counta")
125
0
        return PSZ_FUNC_COUNT2;
126
0
    else if (rIdent == u"count")
127
0
        return PSZ_FUNC_COUNT;
128
0
    else if (rIdent == u"max")
129
0
        return PSZ_FUNC_MAX;
130
0
    else if (rIdent == u"min")
131
0
        return PSZ_FUNC_MIN;
132
0
    else if (rIdent == u"sum")
133
0
        return PSZ_FUNC_SUM;
134
0
    else if (rIdent == u"selection")
135
0
        return PSZ_FUNC_SELECTION_COUNT;
136
0
    else if (rIdent == u"none")
137
0
        return PSZ_FUNC_NONE;
138
0
    return 0;
139
0
}
140
141
OUString FunctionPopup_Impl::function_to_id(sal_uInt16 nFunc)
142
0
{
143
0
    switch (nFunc)
144
0
    {
145
0
        case PSZ_FUNC_AVG:
146
0
            return u"avg"_ustr;
147
0
        case PSZ_FUNC_COUNT2:
148
0
            return u"counta"_ustr;
149
0
        case PSZ_FUNC_COUNT:
150
0
            return u"count"_ustr;
151
0
        case PSZ_FUNC_MAX:
152
0
            return u"max"_ustr;
153
0
        case PSZ_FUNC_MIN:
154
0
            return u"min"_ustr;
155
0
        case PSZ_FUNC_SUM:
156
0
            return u"sum"_ustr;
157
0
        case PSZ_FUNC_SELECTION_COUNT:
158
0
            return u"selection"_ustr;
159
0
        case PSZ_FUNC_NONE:
160
0
            return u"none"_ustr;
161
0
    }
162
0
    return {};
163
0
}
164
165
FunctionPopup_Impl::FunctionPopup_Impl(sal_uInt32 nCheckEncoded)
166
0
    : m_xBuilder(Application::CreateBuilder(nullptr, u"svx/ui/functionmenu.ui"_ustr))
167
0
    , m_xMenu(m_xBuilder->weld_menu(u"menu"_ustr))
168
0
    , m_nSelected(nCheckEncoded)
169
0
{
170
0
    for ( sal_uInt16 nCheck = 1; nCheck < 32; ++nCheck )
171
0
        if ( nCheckEncoded & (1u << nCheck) )
172
0
            m_xMenu->set_active(function_to_id(nCheck), true);
173
0
}
174
175
sal_uInt32 FunctionPopup_Impl::GetSelected(std::u16string_view curident) const
176
0
{
177
0
    sal_uInt32 nSelected = m_nSelected;
178
0
    sal_uInt16 nCurItemId = id_to_function(curident);
179
0
    if ( nCurItemId == PSZ_FUNC_NONE )
180
0
        nSelected = ( 1 << PSZ_FUNC_NONE );
181
0
    else
182
0
    {
183
0
        nSelected &= (~( 1u << PSZ_FUNC_NONE )); // Clear the "None" bit
184
0
        nSelected ^= ( 1u << nCurItemId ); // Toggle the bit corresponding to nCurItemId
185
0
        if ( !nSelected )
186
0
            nSelected = ( 1u << PSZ_FUNC_NONE );
187
0
    }
188
0
    return nSelected;
189
0
}
190
191
struct SvxPosSizeStatusBarControl_Impl
192
193
/*  [Description]
194
195
    This implementation-structure of the class SvxPosSizeStatusBarControl
196
    is done for the un-linking of the changes of the exported interface such as
197
    the toning down of symbols that are visible externally.
198
199
    One instance exists for each SvxPosSizeStatusBarControl-instance
200
    during its life time
201
*/
202
203
{
204
    Point     aPos;       // valid when a position is shown
205
    Size      aSize;      // valid when a size is shown
206
    OUString  aStr;       // valid when a text is shown
207
    bool      bPos;       // show position ?
208
    bool      bSize;      // set size ?
209
    bool      bTable;     // set table index ?
210
    bool      bHasMenu;   // set StarCalc popup menu ?
211
    sal_uInt32  nFunctionSet;  // the selected StarCalc functions encoded in 32 bits
212
    Image     aPosImage;  // Image to show the position
213
    Image     aSizeImage; // Image to show the size
214
};
215
216
/*  [Description]
217
218
    Ctor():
219
    Create an instance of the implementation class,
220
    load the images for the position and size
221
*/
222
223
#define STR_POSITION ".uno:Position"
224
#define STR_TABLECELL ".uno:StateTableCell"
225
#define STR_FUNC ".uno:StatusBarFunc"
226
227
SvxPosSizeStatusBarControl::SvxPosSizeStatusBarControl( sal_uInt16 _nSlotId,
228
                                                        sal_uInt16 _nId,
229
                                                        StatusBar& rStb ) :
230
0
    SfxStatusBarControl( _nSlotId, _nId, rStb ),
231
0
    pImpl( new SvxPosSizeStatusBarControl_Impl )
232
0
{
233
0
    pImpl->bPos = false;
234
0
    pImpl->bSize = false;
235
0
    pImpl->bTable = false;
236
0
    pImpl->bHasMenu = false;
237
0
    pImpl->nFunctionSet = 0;
238
0
    pImpl->aPosImage = Image(StockImage::Yes, RID_SVXBMP_POSITION);
239
0
    pImpl->aSizeImage = Image(StockImage::Yes, RID_SVXBMP_SIZE);
240
241
0
    addStatusListener( u"" STR_POSITION ""_ustr);         // SID_ATTR_POSITION
242
0
    addStatusListener( u"" STR_TABLECELL ""_ustr);   // SID_TABLE_CELL
243
0
    addStatusListener( u"" STR_FUNC ""_ustr);    // SID_PSZ_FUNCTION
244
0
    ImplUpdateItemText();
245
0
}
246
247
/*  [Description]
248
249
    Dtor():
250
    remove the pointer to the implementation class, so that the timer is stopped
251
252
*/
253
254
SvxPosSizeStatusBarControl::~SvxPosSizeStatusBarControl()
255
0
{
256
0
}
257
258
259
/*  [Description]
260
261
    SID_PSZ_FUNCTION activates the popup menu for Calc:
262
263
    Status overview
264
    Depending on the type of the item, a special setting is enabled, the others disabled.
265
266
                NULL/Void   SfxPointItem    SvxSizeItem     SfxStringItem
267
    ------------------------------------------------------------------------
268
    Position    sal_False                                       FALSE
269
    Size        FALSE                       TRUE            FALSE
270
    Text        sal_False                       sal_False           TRUE
271
272
*/
273
274
void SvxPosSizeStatusBarControl::StateChangedAtStatusBarControl( sal_uInt16 nSID, SfxItemState eState,
275
                                               const SfxPoolItem* pState )
276
0
{
277
    // Because the combi-controller, always sets the current Id as HelpId
278
    // first clean the cached HelpText
279
0
    GetStatusBar().SetHelpText( GetId(), u""_ustr );
280
281
0
    switch ( nSID )
282
0
    {
283
0
        case SID_ATTR_POSITION : GetStatusBar().SetHelpId( GetId(), u"" STR_POSITION ""_ustr ); break;
284
0
        case SID_TABLE_CELL: GetStatusBar().SetHelpId( GetId(), u"" STR_TABLECELL ""_ustr ); break;
285
0
        case SID_PSZ_FUNCTION: GetStatusBar().SetHelpId( GetId(), u"" STR_FUNC ""_ustr ); break;
286
0
        default: break;
287
0
    }
288
289
0
    if ( nSID == SID_PSZ_FUNCTION )
290
0
    {
291
0
        if ( eState == SfxItemState::DEFAULT )
292
0
        {
293
0
            pImpl->bHasMenu = true;
294
0
            if ( auto pUInt32Item = dynamic_cast< const SfxUInt32Item* >(pState) )
295
0
                pImpl->nFunctionSet = pUInt32Item->GetValue();
296
0
        }
297
0
        else
298
0
            pImpl->bHasMenu = false;
299
0
    }
300
0
    else if ( SfxItemState::DEFAULT != eState )
301
0
    {
302
        // don't switch to empty display before an empty state was
303
        // notified for all display types
304
305
0
        if ( nSID == SID_TABLE_CELL )
306
0
            pImpl->bTable = false;
307
0
        else if ( nSID == SID_ATTR_POSITION )
308
0
            pImpl->bPos = false;
309
0
        else if ( nSID == GetSlotId() )     // controller is registered for SID_ATTR_SIZE
310
0
            pImpl->bSize = false;
311
0
        else
312
0
        {
313
0
            SAL_WARN( "svx.stbcrtls","unknown slot id");
314
0
        }
315
0
    }
316
0
    else if ( auto pPointItem = dynamic_cast<const SfxPointItem*>( pState) )
317
0
    {
318
        // show position
319
0
        pImpl->aPos = pPointItem->GetValue();
320
0
        pImpl->bPos = true;
321
0
        pImpl->bTable = false;
322
0
    }
323
0
    else if ( auto pSizeItem = dynamic_cast<const SvxSizeItem*>( pState) )
324
0
    {
325
        // show size
326
0
        pImpl->aSize = pSizeItem->GetSize();
327
0
        pImpl->bSize = true;
328
0
        pImpl->bTable = false;
329
0
    }
330
0
    else if ( auto pStatusItem = dynamic_cast<const SvxStatusItem*>( pState) )
331
0
    {
332
        // show string (table cell or different)
333
0
        pImpl->aStr = pStatusItem->GetValue();
334
0
        pImpl->bTable = true;
335
0
        pImpl->bPos = false;
336
0
        pImpl->bSize = false;
337
0
        if (!pImpl->aStr.isEmpty())
338
0
        {
339
0
            OUString sTip;
340
0
            switch (pStatusItem->GetCategory())
341
0
            {
342
0
                case StatusCategory::TableCell:
343
0
                    sTip = SvxResId(RID_SVXSTR_TABLECELL_HINT);
344
0
                    break;
345
0
                case StatusCategory::Section:
346
0
                    sTip = SvxResId(RID_SVXSTR_SECTION_HINT);
347
0
                    break;
348
0
                case StatusCategory::TableOfContents:
349
0
                    sTip = SvxResId(RID_SVXSTR_TOC_HINT);
350
0
                    break;
351
0
                case StatusCategory::Numbering:
352
0
                    sTip = SvxResId(RID_SVXSTR_NUMBERING_HINT);
353
0
                    break;
354
0
                case StatusCategory::ListStyle:
355
0
                    sTip = SvxResId(RID_SVXSTR_LIST_STYLE_HINT);
356
0
                    break;
357
0
                case StatusCategory::Formula:
358
0
                    sTip = SvxResId(RID_SVXSTR_FORMULA_HINT);
359
0
                    break;
360
0
                case StatusCategory::RowColumn:
361
0
                    sTip = SvxResId(RID_SVXSTR_ROW_COLUMN_HINT);
362
0
                    break;
363
0
                case StatusCategory::NONE:
364
0
                    break;
365
0
            }
366
0
            GetStatusBar().SetQuickHelpText(GetId(), sTip);
367
0
        }
368
0
    }
369
0
    else if ( auto pStringItem = dynamic_cast<const SfxStringItem*>( pState) )
370
0
    {
371
0
        SAL_WARN( "svx.stbcrtls", "this should be a SvxStatusItem not a SfxStringItem" );
372
        // show string (table cell or different)
373
0
        pImpl->aStr = pStringItem->GetValue();
374
0
        pImpl->bTable = true;
375
0
        pImpl->bPos = false;
376
0
        pImpl->bSize = false;
377
0
    }
378
0
    else
379
0
    {
380
0
        SAL_WARN( "svx.stbcrtls", "invalid item type" );
381
0
        pImpl->bPos = false;
382
0
        pImpl->bSize = false;
383
0
        pImpl->bTable = false;
384
0
    }
385
386
0
    GetStatusBar().SetItemData( GetId(), nullptr );
387
388
0
    ImplUpdateItemText();
389
0
}
390
391
392
/*  [Description]
393
394
    execute popup menu, when the status enables this
395
*/
396
397
void SvxPosSizeStatusBarControl::Command( const CommandEvent& rCEvt )
398
0
{
399
0
    if ( rCEvt.GetCommand() == CommandEventId::ContextMenu && pImpl->bHasMenu )
400
0
    {
401
0
        sal_uInt32 nSelect = pImpl->nFunctionSet;
402
0
        if (!nSelect)
403
0
            nSelect = ( 1 << PSZ_FUNC_NONE );
404
0
        tools::Rectangle aRect(rCEvt.GetMousePosPixel(), Size(1,1));
405
0
        weld::Window* pParent = weld::GetPopupParent(GetStatusBar(), aRect);
406
0
        FunctionPopup_Impl aMenu(nSelect);
407
0
        OUString sIdent = aMenu.Execute(pParent, aRect);
408
0
        if (!sIdent.isEmpty())
409
0
        {
410
0
            nSelect = aMenu.GetSelected(sIdent);
411
0
            if (nSelect)
412
0
            {
413
0
                if (nSelect == (1 << PSZ_FUNC_NONE))
414
0
                    nSelect = 0;
415
416
0
                css::uno::Any a;
417
0
                SfxUInt32Item aItem( SID_PSZ_FUNCTION, nSelect );
418
0
                aItem.QueryValue( a );
419
0
                css::uno::Sequence< css::beans::PropertyValue > aArgs{ comphelper::makePropertyValue(
420
0
                    u"StatusBarFunc"_ustr, a) };
421
0
                execute( u".uno:StatusBarFunc"_ustr, aArgs );
422
0
            }
423
0
        }
424
0
    }
425
0
    else
426
0
        SfxStatusBarControl::Command( rCEvt );
427
0
}
428
429
430
/*  [Description]
431
432
    Depending on the type to be shown, the value us shown. First the
433
    rectangle is repainted (removed).
434
*/
435
436
void SvxPosSizeStatusBarControl::Paint( const UserDrawEvent& rUsrEvt )
437
0
{
438
0
    vcl::RenderContext* pDev = rUsrEvt.GetRenderContext();
439
440
0
    const tools::Rectangle& rRect = rUsrEvt.GetRect();
441
0
    StatusBar& rBar = GetStatusBar();
442
0
    Point aItemPos = rBar.GetItemTextPos( GetId() );
443
0
    auto popIt = pDev->ScopedPush(vcl::PushFlags::LINECOLOR | vcl::PushFlags::FILLCOLOR);
444
0
    pDev->SetLineColor();
445
0
    pDev->SetFillColor( pDev->GetBackground().GetColor() );
446
447
0
    if ( pImpl->bPos || pImpl->bSize )
448
0
    {
449
        // count the position for showing the size
450
0
        tools::Long nSizePosX =
451
0
            rRect.Left() + rRect.GetWidth() / 2 + PAINT_OFFSET;
452
        // draw position
453
0
        Point aPnt = rRect.TopLeft();
454
0
        aPnt.AdjustX(PAINT_OFFSET );
455
        // vertically centered
456
0
        const tools::Long nSizePosY =
457
0
            (rRect.GetHeight() - pImpl->aPosImage.GetSizePixel().Height()) / 2;
458
0
        aPnt.AdjustY( nSizePosY );
459
460
0
        pDev->DrawImage( aPnt, pImpl->aPosImage );
461
0
        aPnt.AdjustX(pImpl->aPosImage.GetSizePixel().Width() );
462
0
        aPnt.AdjustX(PAINT_OFFSET );
463
0
        OUString aStr = GetMetricStr_Impl( pImpl->aPos.X()) + " / " +
464
0
            GetMetricStr_Impl( pImpl->aPos.Y());
465
0
        tools::Rectangle aRect(aPnt, Point(nSizePosX, rRect.Bottom()));
466
0
        pDev->DrawRect(aRect);
467
0
        vcl::Region aOrigRegion(pDev->GetClipRegion());
468
0
        pDev->SetClipRegion(vcl::Region(aRect));
469
0
        pDev->DrawText(aPnt, aStr);
470
0
        pDev->SetClipRegion(aOrigRegion);
471
472
        // draw the size, when available
473
0
        aPnt.setX( nSizePosX );
474
475
0
        if ( pImpl->bSize )
476
0
        {
477
0
            pDev->DrawImage( aPnt, pImpl->aSizeImage );
478
0
            aPnt.AdjustX(pImpl->aSizeImage.GetSizePixel().Width() );
479
0
            Point aDrwPnt = aPnt;
480
0
            aPnt.AdjustX(PAINT_OFFSET );
481
0
            aStr = GetMetricStr_Impl( pImpl->aSize.Width() ) + " x " +
482
0
                GetMetricStr_Impl( pImpl->aSize.Height() );
483
0
            aRect = tools::Rectangle(aDrwPnt, rRect.BottomRight());
484
0
            pDev->DrawRect(aRect);
485
0
            aOrigRegion = pDev->GetClipRegion();
486
0
            pDev->SetClipRegion(vcl::Region(aRect));
487
0
            pDev->DrawText(aPnt, aStr);
488
0
            pDev->SetClipRegion(aOrigRegion);
489
0
        }
490
0
        else
491
0
            pDev->DrawRect( tools::Rectangle( aPnt, rRect.BottomRight() ) );
492
0
    }
493
0
    else if ( pImpl->bTable )
494
0
    {
495
0
        pDev->DrawRect( rRect );
496
0
        pDev->DrawText( Point(
497
0
            rRect.Left() + rRect.GetWidth() / 2 - pDev->GetTextWidth( pImpl->aStr ) / 2,
498
0
            aItemPos.Y() ), pImpl->aStr );
499
0
    }
500
0
    else
501
0
    {
502
        // Empty display if neither size nor table position are available.
503
        // Date/Time are no longer used (#65302#).
504
0
        pDev->DrawRect( rRect );
505
0
    }
506
0
}
507
508
void SvxPosSizeStatusBarControl::ImplUpdateItemText()
509
0
{
510
    //  set only strings as text at the statusBar, so that the Help-Tips
511
    //  can work with the text, when it is too long for the statusBar
512
0
    OUString aText;
513
0
    int nCharsWidth = -1;
514
0
    if ( pImpl->bPos || pImpl->bSize )
515
0
    {
516
0
        aText = GetMetricStr_Impl( pImpl->aPos.X()) + " / " +
517
0
            GetMetricStr_Impl( pImpl->aPos.Y());
518
        // widest X/Y string looks like "-999,99"
519
0
        nCharsWidth = 1 + 6 + 3 + 6; // icon + x + slash + y
520
0
        if ( pImpl->bSize )
521
0
        {
522
0
            aText += " " + GetMetricStr_Impl( pImpl->aSize.Width() ) + " x " +
523
0
                GetMetricStr_Impl( pImpl->aSize.Height() );
524
0
            nCharsWidth += 1 + 1 + 4 + 3 + 4; // icon + space + w + x + h
525
0
        }
526
0
    }
527
0
    else if ( pImpl->bTable )
528
0
       aText = pImpl->aStr;
529
530
0
    GetStatusBar().SetItemText( GetId(), aText, nCharsWidth );
531
0
}
532
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */