Coverage Report

Created: 2026-10-14 09:33

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/vcl/source/control/imivctl1.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 <limits.h>
22
#include <osl/diagnose.h>
23
#include <tools/debug.hxx>
24
#include <vcl/wall.hxx>
25
#include <vcl/help.hxx>
26
#include <vcl/decoview.hxx>
27
#include <vcl/event.hxx>
28
#include <vcl/salnativewidgets.hxx>
29
#include <vcl/svapp.hxx>
30
#include <vcl/vclevent.hxx>
31
#include <tools/poly.hxx>
32
#include <vcl/lineinfo.hxx>
33
#include <vcl/i18nhelp.hxx>
34
#include <vcl/mnemonic.hxx>
35
#include <vcl/settings.hxx>
36
#include <vcl/commandevent.hxx>
37
38
#include <ivctrl.hxx>
39
40
#include "imivctl.hxx"
41
42
#include <algorithm>
43
#include <memory>
44
#include <vcl/idle.hxx>
45
46
constexpr auto DRAWTEXT_FLAGS_ICON =
47
    DrawTextFlags::Center | DrawTextFlags::Top | DrawTextFlags::EndEllipsis |
48
    DrawTextFlags::Clip | DrawTextFlags::MultiLine | DrawTextFlags::WordBreak | DrawTextFlags::Mnemonic;
49
50
0
#define DRAWTEXT_FLAGS_SMALLICON (DrawTextFlags::Left|DrawTextFlags::EndEllipsis|DrawTextFlags::Clip)
51
52
0
#define EVENTID_ADJUST_SCROLLBARS       (reinterpret_cast<void*>(1))
53
54
SvxIconChoiceCtrl_Impl::SvxIconChoiceCtrl_Impl(
55
    SvtIconChoiceCtrl* pCurView,
56
    WinBits nWinStyle
57
) :
58
0
    aVerSBar( VclPtr<ScrollBar>::Create(pCurView, WB_DRAG | WB_VSCROLL) ),
59
0
    aHorSBar( VclPtr<ScrollBar>::Create(pCurView, WB_DRAG | WB_HSCROLL) ),
60
0
    aScrBarBox( VclPtr<ScrollBarBox>::Create(pCurView) ),
61
0
    aDocRectChangedIdle( "svtools::SvxIconChoiceCtrl_Impl aDocRectChangedIdle" ),
62
0
    aVisRectChangedIdle( "svtools::SvxIconChoiceCtrl_Impl aVisRectChangedIdle" ),
63
0
    aImageSize( 32 * pCurView->GetDPIScaleFactor(), 32 * pCurView->GetDPIScaleFactor()),
64
0
    pView(pCurView), nMaxVirtHeight(DEFAULT_MAX_VIRT_HEIGHT),
65
0
    nFlags(IconChoiceFlags::NONE), nUserEventAdjustScrBars(nullptr),
66
0
    pCurHighlightFrame(nullptr),
67
0
    pCursor(nullptr)
68
0
{
69
0
    SetStyle( nWinStyle );
70
0
    pImpCursor.reset( new IcnCursor_Impl( this ) );
71
0
    pGridMap.reset( new IcnGridMap_Impl( this ) );
72
73
0
    aVerSBar->SetScrollHdl( LINK( this, SvxIconChoiceCtrl_Impl, ScrollUpDownHdl ) );
74
0
    aHorSBar->SetScrollHdl( LINK( this, SvxIconChoiceCtrl_Impl, ScrollLeftRightHdl ) );
75
76
0
    nHorSBarHeight = aHorSBar->GetSizePixel().Height();
77
0
    nVerSBarWidth = aVerSBar->GetSizePixel().Width();
78
79
0
    aDocRectChangedIdle.SetPriority( TaskPriority::HIGH_IDLE );
80
0
    aDocRectChangedIdle.SetInvokeHandler(LINK(this,SvxIconChoiceCtrl_Impl,DocRectChangedHdl));
81
82
0
    aVisRectChangedIdle.SetPriority( TaskPriority::HIGH_IDLE );
83
0
    aVisRectChangedIdle.SetInvokeHandler(LINK(this,SvxIconChoiceCtrl_Impl,VisRectChangedHdl));
84
85
0
    Clear( true );
86
0
    Size gridSize;
87
0
    if (nWinStyle & WB_SMALLICON)
88
0
       gridSize = Size(-1, 32);
89
0
    else
90
0
       gridSize = Size(140, 70);
91
0
    if(pView->GetDPIScaleFactor() > 1)
92
0
    {
93
0
      gridSize.setHeight( gridSize.Height() * ( pView->GetDPIScaleFactor()) );
94
0
    }
95
0
    SetGrid(gridSize);
96
0
}
97
98
SvxIconChoiceCtrl_Impl::~SvxIconChoiceCtrl_Impl()
99
0
{
100
0
    Clear(false);
101
0
    CancelUserEvents();
102
0
    pImpCursor.reset();
103
0
    pGridMap.reset();
104
0
    aVerSBar.disposeAndClear();
105
0
    aHorSBar.disposeAndClear();
106
0
    aScrBarBox.disposeAndClear();
107
0
}
108
109
void SvxIconChoiceCtrl_Impl::Clear( bool bInCtor )
110
0
{
111
0
    pCurHighlightFrame = nullptr;
112
0
    CancelUserEvents();
113
0
    ShowCursor( false );
114
0
    bBoundRectsDirty = false;
115
0
    nMaxBoundHeight = 0;
116
117
0
    pCursor = nullptr;
118
0
    if( !bInCtor )
119
0
    {
120
0
        pImpCursor->Clear();
121
0
        pGridMap->Clear();
122
0
        aVirtOutputSize.setWidth( 0 );
123
0
        aVirtOutputSize.setHeight( 0 );
124
0
        Size aSize( pView->GetOutputSizePixel() );
125
0
        nMaxVirtHeight = aSize.Height() - nHorSBarHeight;
126
0
        if( nMaxVirtHeight <= 0 )
127
0
            nMaxVirtHeight = DEFAULT_MAX_VIRT_HEIGHT;
128
0
        maZOrderList.clear();
129
0
        SetOrigin( Point() );
130
0
        pView->Invalidate(InvalidateFlags::NoChildren);
131
0
    }
132
0
    AdjustScrollBars();
133
0
    maEntries.clear();
134
0
    DocRectChanged();
135
0
    VisRectChanged();
136
0
}
137
138
void SvxIconChoiceCtrl_Impl::SetStyle( WinBits nWinStyle )
139
0
{
140
0
    nWinBits = nWinStyle;
141
0
    nCurTextDrawFlags = DRAWTEXT_FLAGS_ICON;
142
0
    if( nWinBits & (WB_SMALLICON | WB_DETAILS) )
143
0
        nCurTextDrawFlags = DRAWTEXT_FLAGS_SMALLICON;
144
0
}
145
146
IMPL_LINK( SvxIconChoiceCtrl_Impl, ScrollUpDownHdl, ScrollBar*, pScrollBar, void )
147
0
{
148
    // arrow up: delta=-1; arrow down: delta=+1
149
0
    Scroll( 0, pScrollBar->GetDelta() );
150
0
}
151
152
IMPL_LINK( SvxIconChoiceCtrl_Impl, ScrollLeftRightHdl, ScrollBar*, pScrollBar, void )
153
0
{
154
    // arrow left: delta=-1; arrow right: delta=+1
155
0
    Scroll( pScrollBar->GetDelta(), 0 );
156
0
}
157
158
void SvxIconChoiceCtrl_Impl::FontModified()
159
0
{
160
0
    SetDefaultTextSize();
161
0
    ShowCursor( false );
162
0
    ShowCursor( true );
163
0
}
164
165
void SvxIconChoiceCtrl_Impl::InsertEntry( std::unique_ptr<SvxIconChoiceCtrlEntry> pEntry1, size_t nPos)
166
0
{
167
0
    auto pEntry = pEntry1.get();
168
169
0
    if ( nPos < maEntries.size() ) {
170
0
        maEntries.insert( maEntries.begin() + nPos, std::move(pEntry1) );
171
0
    } else {
172
0
        maEntries.push_back( std::move(pEntry1) );
173
0
    }
174
175
0
    maZOrderList.push_back( pEntry );
176
0
    pImpCursor->Clear();
177
178
    // don't set all bounding rectangles to
179
    // 'to be checked', but only the bounding rectangle of the new entry.
180
    // Thus, don't call InvalidateBoundingRect!
181
0
    pEntry->aRect.SetRight( LONG_MAX );
182
0
    FindBoundingRect(pEntry);
183
    // initial calculation w/o icon yet to ensure the background is filled
184
0
    if (nWinBits & WB_SMALLICON)
185
0
    {
186
0
        tools::Rectangle aTextRect = pView->GetTextRect( CalcMaxTextRect( pEntry ), pEntry->GetText(), nCurTextDrawFlags );
187
0
        pView->AdjustWidth(32 + aTextRect.GetSize().Width() + 2 * HOR_DIST_BMP_STRING );
188
0
    }
189
190
0
    tools::Rectangle aOutputArea(GetOutputRect());
191
0
    pGridMap->OccupyGrids(pEntry);
192
0
    if (!aOutputArea.Overlaps(pEntry->aRect))
193
0
        return; // is invisible
194
0
    pView->Invalidate(pEntry->aRect);
195
0
}
196
197
void SvxIconChoiceCtrl_Impl::RemoveEntry(size_t nPos)
198
0
{
199
0
    pImpCursor->Clear();
200
0
    maEntries.erase(maEntries.begin() + nPos);
201
0
    RecalcAllBoundingRectsSmart();
202
0
}
203
204
tools::Rectangle SvxIconChoiceCtrl_Impl::GetOutputRect() const
205
0
{
206
0
    Point aOrigin( pView->GetMapMode().GetOrigin() );
207
0
    aOrigin *= -1;
208
0
    return tools::Rectangle( aOrigin, aOutputSize );
209
0
}
210
211
void SvxIconChoiceCtrl_Impl::SelectEntry( SvxIconChoiceCtrlEntry* pEntry, bool bSelect,
212
    bool bAdd )
213
0
{
214
215
0
    if( !bAdd )
216
0
    {
217
0
        if ( !( nFlags & IconChoiceFlags::ClearingSelection ) )
218
0
        {
219
0
            nFlags |= IconChoiceFlags::ClearingSelection;
220
0
            DeselectAllBut( pEntry );
221
0
            nFlags &= ~IconChoiceFlags::ClearingSelection;
222
0
        }
223
0
    }
224
0
    if( pEntry->IsSelected() == bSelect )
225
0
        return;
226
227
0
    SvxIconViewFlags nEntryFlags = pEntry->GetFlags();
228
0
    if( bSelect )
229
0
    {
230
0
        nEntryFlags |= SvxIconViewFlags::SELECTED;
231
0
        pEntry->AssignFlags( nEntryFlags );
232
0
        pView->ClickIcon();
233
0
    }
234
0
    else
235
0
    {
236
0
        nEntryFlags &= ~SvxIconViewFlags::SELECTED;
237
0
        pEntry->AssignFlags( nEntryFlags );
238
0
        pView->ClickIcon();
239
0
    }
240
0
    EntrySelected( pEntry, bSelect );
241
0
}
242
243
void SvxIconChoiceCtrl_Impl::EntrySelected(SvxIconChoiceCtrlEntry* pEntry, bool bSelect)
244
0
{
245
    // make sure that the cursor is always placed
246
    // over the (only) selected entry. (But only if a cursor exists.)
247
0
    if (bSelect && pCursor && pEntry != pCursor)
248
0
    {
249
0
        SetCursor(pEntry);
250
0
    }
251
252
0
    ToTop(pEntry);
253
254
0
    if (pEntry == pCursor)
255
0
        ShowCursor(false);
256
0
    pView->Invalidate(CalcFocusRect(pEntry));
257
0
    if (pEntry == pCursor)
258
0
        ShowCursor(true);
259
260
    // #i101012# emit vcl event LISTBOX_SELECT only in case that the given entry is selected.
261
0
    if (bSelect)
262
0
    {
263
0
        CallEventListeners(VclEventId::ListboxSelect, pEntry);
264
0
    }
265
0
}
266
267
void SvxIconChoiceCtrl_Impl::ResetVirtSize()
268
0
{
269
0
    aVirtOutputSize.setWidth( 0 );
270
0
    aVirtOutputSize.setHeight( 0 );
271
0
    const size_t nCount = maEntries.size();
272
0
    for( size_t nCur = 0; nCur < nCount; nCur++ )
273
0
    {
274
0
        SvxIconChoiceCtrlEntry* pCur = maEntries[ nCur ].get();
275
0
        InvalidateBoundingRect(pCur->aRect);
276
0
    }
277
278
0
    if( !(nWinBits & (WB_NOVSCROLL | WB_NOHSCROLL)) )
279
0
    {
280
0
        Size aRealOutputSize( pView->GetOutputSizePixel() );
281
0
        if( aVirtOutputSize.Width() < aRealOutputSize.Width() ||
282
0
            aVirtOutputSize.Height() < aRealOutputSize.Height() )
283
0
        {
284
0
            sal_uLong nGridCount = IcnGridMap_Impl::GetGridCount(
285
0
                aRealOutputSize, static_cast<sal_uInt16>(nGridDX), static_cast<sal_uInt16>(nGridDY) );
286
0
            if( nGridCount < nCount )
287
0
                nMaxVirtHeight = aRealOutputSize.Height() - nHorSBarHeight;
288
0
        }
289
0
    }
290
291
0
    pImpCursor->Clear();
292
0
    pGridMap->Clear();
293
0
    VisRectChanged();
294
0
}
295
296
void SvxIconChoiceCtrl_Impl::AdjustVirtSize( const tools::Rectangle& rRect )
297
0
{
298
0
    tools::Long nHeightOffs = 0;
299
0
    tools::Long nWidthOffs = 0;
300
301
0
    if( aVirtOutputSize.Width() < (rRect.Right()+LROFFS_WINBORDER) )
302
0
        nWidthOffs = (rRect.Right()+LROFFS_WINBORDER) - aVirtOutputSize.Width();
303
304
0
    if( aVirtOutputSize.Height() < (rRect.Bottom()+TBOFFS_WINBORDER) )
305
0
        nHeightOffs = (rRect.Bottom()+TBOFFS_WINBORDER) - aVirtOutputSize.Height();
306
307
0
    if( !(nWidthOffs || nHeightOffs) )
308
0
        return;
309
310
0
    Range aRange;
311
0
    aVirtOutputSize.AdjustWidth(nWidthOffs );
312
0
    aRange.Max() = aVirtOutputSize.Width();
313
0
    aHorSBar->SetRange( aRange );
314
315
0
    aVirtOutputSize.AdjustHeight(nHeightOffs );
316
0
    aRange.Max() = aVirtOutputSize.Height();
317
0
    aVerSBar->SetRange( aRange );
318
319
0
    pImpCursor->Clear();
320
0
    pGridMap->OutputSizeChanged();
321
0
    AdjustScrollBars();
322
0
    DocRectChanged();
323
0
}
324
325
void SvxIconChoiceCtrl_Impl::Arrange(tools::Long nSetMaxVirtHeight)
326
0
{
327
0
    if ( nSetMaxVirtHeight != 0 )
328
0
        nMaxVirtHeight = nSetMaxVirtHeight;
329
0
    else
330
0
        nMaxVirtHeight = aOutputSize.Height();
331
332
0
    ImpArrange();
333
0
}
334
335
void SvxIconChoiceCtrl_Impl::ImpArrange()
336
0
{
337
0
    nFlags |= IconChoiceFlags::Arranging;
338
0
    ShowCursor( false );
339
0
    ResetVirtSize();
340
0
    bBoundRectsDirty = false;
341
0
    SetOrigin( Point() );
342
0
    VisRectChanged();
343
0
    RecalcAllBoundingRectsSmart();
344
0
    pView->Invalidate( InvalidateFlags::NoChildren );
345
0
    nFlags &= ~IconChoiceFlags::Arranging;
346
0
    ShowCursor( true );
347
0
}
348
349
void SvxIconChoiceCtrl_Impl::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect)
350
0
{
351
0
    if (!maEntries.size())
352
0
        return;
353
0
    if (!pCursor)
354
0
    {
355
        // set cursor to item with focus-flag
356
0
        bool bfound = false;
357
0
        for (sal_Int32 i = 0; i < pView->GetEntryCount() && !bfound; i++)
358
0
        {
359
0
            SvxIconChoiceCtrlEntry* pEntry = pView->GetEntry(i);
360
0
            if (pEntry->IsFocused())
361
0
            {
362
0
                pCursor = pEntry;
363
0
                bfound = true;
364
0
            }
365
0
        }
366
367
0
        if (!bfound)
368
0
            pCursor = maEntries[ 0 ].get();
369
0
    }
370
371
0
    size_t nCount = maZOrderList.size();
372
0
    if (!nCount)
373
0
        return;
374
375
0
    auto popIt = rRenderContext.ScopedPush(vcl::PushFlags::CLIPREGION);
376
0
    rRenderContext.SetClipRegion(vcl::Region(rRect));
377
378
0
    std::vector< SvxIconChoiceCtrlEntry* > aNewZOrderList;
379
0
    std::vector< SvxIconChoiceCtrlEntry* > aPaintedEntries;
380
381
0
    size_t nPos = 0;
382
0
    while(nCount)
383
0
    {
384
0
        SvxIconChoiceCtrlEntry* pEntry = maZOrderList[nPos];
385
0
        const tools::Rectangle& rBoundRect = GetEntryBoundRect(pEntry);
386
0
        if (rRect.Overlaps(rBoundRect))
387
0
        {
388
0
            PaintEntry(pEntry, rBoundRect.TopLeft(), rRenderContext);
389
            // set entries to Top if they are being repainted
390
0
            aPaintedEntries.push_back(pEntry);
391
0
        }
392
0
        else
393
0
            aNewZOrderList.push_back(pEntry);
394
395
0
        nCount--;
396
0
        nPos++;
397
0
    }
398
0
    maZOrderList = std::move( aNewZOrderList );
399
0
    maZOrderList.insert(maZOrderList.end(), aPaintedEntries.begin(), aPaintedEntries.end());
400
0
}
401
402
void SvxIconChoiceCtrl_Impl::RepaintSelectedEntries()
403
0
{
404
0
    tools::Rectangle aOutRect(GetOutputRect());
405
0
    for (SvxIconChoiceCtrlEntry* pEntry : maZOrderList)
406
0
    {
407
0
        if (pEntry->GetFlags() & SvxIconViewFlags::SELECTED)
408
0
        {
409
0
            const tools::Rectangle& rBoundRect = GetEntryBoundRect(pEntry);
410
0
            if (aOutRect.Overlaps(rBoundRect))
411
0
                pView->Invalidate(rBoundRect);
412
0
        }
413
0
    }
414
0
}
415
416
void SvxIconChoiceCtrl_Impl::InitScrollBarBox()
417
0
{
418
0
    aScrBarBox->SetSizePixel( Size(nVerSBarWidth-1, nHorSBarHeight-1) );
419
0
    Size aSize( pView->GetOutputSizePixel() );
420
0
    aScrBarBox->SetPosPixel( Point(aSize.Width()-nVerSBarWidth+1, aSize.Height()-nHorSBarHeight+1));
421
0
}
422
423
bool SvxIconChoiceCtrl_Impl::MouseButtonDown( const MouseEvent& rMEvt)
424
0
{
425
0
    bool bHandled = true;
426
0
    if( !(nWinBits & WB_NOPOINTERFOCUS) )
427
0
        pView->GrabFocus();
428
429
0
    Point aDocPos( rMEvt.GetPosPixel() );
430
0
    if(aDocPos.X()>=aOutputSize.Width() || aDocPos.Y()>=aOutputSize.Height())
431
0
        return false;
432
0
    ToDocPos( aDocPos );
433
0
    SvxIconChoiceCtrlEntry* pEntry = GetEntry( aDocPos );
434
0
    if( pEntry )
435
0
        MakeEntryVisible(pEntry);
436
437
0
    if( !pEntry )
438
0
        return false;
439
440
0
    if( rMEvt.GetClicks() == 2 )
441
0
    {
442
0
        DeselectAllBut( pEntry );
443
0
        SelectEntry( pEntry, true, false );
444
0
        pView->ClickIcon();
445
0
    }
446
0
    else
447
0
    {
448
        // Inplace-Editing ?
449
0
        if( rMEvt.IsMod2() )  // Alt?
450
0
        {
451
0
        }
452
0
        else
453
0
        {
454
0
            DeselectAllBut( pEntry );
455
0
            SetCursor( pEntry );
456
0
        }
457
0
    }
458
0
    return bHandled;
459
0
}
460
461
bool SvxIconChoiceCtrl_Impl::MouseMove( const MouseEvent& rMEvt )
462
0
{
463
0
    if( pView->IsTracking() )
464
0
        return false;
465
466
0
    SvxIconChoiceCtrlEntry* pEntry = nullptr;
467
0
    if (!rMEvt.IsLeaveWindow())
468
0
    {
469
0
        const Point aDocPos(pView->PixelToLogic(rMEvt.GetPosPixel()));
470
0
        pEntry = GetEntry(aDocPos);
471
0
    }
472
0
    SetEntryHighlightFrame(pEntry);
473
0
    return true;
474
0
}
475
476
void SvxIconChoiceCtrl_Impl::SetCursor_Impl(SvxIconChoiceCtrlEntry* pNewCursor)
477
0
{
478
0
    if( !pNewCursor )
479
0
        return;
480
481
0
    ShowCursor( false );
482
0
    MakeEntryVisible( pNewCursor );
483
0
    SetCursor( pNewCursor );
484
485
0
    SelectEntry( pCursor, true, false );
486
0
    CallEventListeners( VclEventId::ListboxSelect, pCursor );
487
0
}
488
489
bool SvxIconChoiceCtrl_Impl::KeyInput( const KeyEvent& rKEvt )
490
0
{
491
0
    bool bMod2 = rKEvt.GetKeyCode().IsMod2();
492
0
    sal_Unicode cChar = rKEvt.GetCharCode();
493
0
    sal_uLong nPos = sal_uLong(-1);
494
0
    if ( bMod2 && cChar && IsMnemonicChar( cChar, nPos ) )
495
0
    {
496
        // shortcut is clicked
497
0
        SvxIconChoiceCtrlEntry* pNewCursor = GetEntry( nPos );
498
0
        SvxIconChoiceCtrlEntry* pOldCursor = pCursor;
499
0
        if ( pNewCursor != pOldCursor )
500
0
            SetCursor_Impl(pNewCursor);
501
0
        return true;
502
0
    }
503
504
0
    if ( bMod2 )
505
        // no actions with <ALT>
506
0
        return false;
507
508
0
    bool bKeyUsed = true;
509
510
0
    SvxIconChoiceCtrlEntry* pNewCursor;
511
512
0
    sal_uInt16 nCode = rKEvt.GetKeyCode().GetCode();
513
0
    switch( nCode )
514
0
    {
515
0
        case KEY_UP:
516
0
        case KEY_PAGEUP:
517
0
            if( pCursor )
518
0
            {
519
0
                MakeEntryVisible( pCursor );
520
0
                if( nCode == KEY_UP || (rKEvt.GetKeyCode().IsMod1() && nCode == KEY_PAGEUP))
521
0
                    pNewCursor = pImpCursor->GoUpDown(pCursor,false);
522
0
                else
523
0
                    pNewCursor = pImpCursor->GoPageUpDown(pCursor,false);
524
0
                SetCursor_Impl(pNewCursor);
525
0
                if( !pNewCursor )
526
0
                {
527
0
                    tools::Rectangle aRect( GetEntryBoundRect( pCursor ) );
528
0
                    if( aRect.Top())
529
0
                    {
530
0
                        aRect.AdjustBottom( -(aRect.Top()) );
531
0
                        aRect.SetTop( 0 );
532
0
                        MakeVisible( aRect );
533
0
                    }
534
0
                }
535
0
            }
536
0
            break;
537
0
        case KEY_TAB:
538
0
            if (!rKEvt.GetKeyCode().IsMod1())
539
0
                bKeyUsed = false; // handle simple tab key without ctrl/cmd
540
0
            else if( pCursor )
541
0
            {
542
0
                MakeEntryVisible( pCursor );
543
0
                const bool bDown = !rKEvt.GetKeyCode().IsShift();
544
0
                pNewCursor = pImpCursor->GoUpDown(pCursor, bDown, true);
545
0
                SetCursor_Impl(pNewCursor);
546
0
                if( !pNewCursor )
547
0
                {
548
0
                    tools::Rectangle aRect( GetEntryBoundRect( pCursor ) );
549
0
                    if( aRect.Top())
550
0
                    {
551
0
                        aRect.AdjustBottom( -(aRect.Top()) );
552
0
                        aRect.SetTop( 0 );
553
0
                        MakeVisible( aRect );
554
0
                    }
555
0
                }
556
0
            }
557
0
            break;
558
559
0
        case KEY_DOWN:
560
0
        case KEY_PAGEDOWN:
561
0
            if( pCursor )
562
0
            {
563
0
                if( nCode == KEY_DOWN || (rKEvt.GetKeyCode().IsMod1() && nCode == KEY_PAGEDOWN) )
564
0
                    pNewCursor=pImpCursor->GoUpDown( pCursor,true );
565
0
                else
566
0
                    pNewCursor=pImpCursor->GoPageUpDown( pCursor,true );
567
0
                SetCursor_Impl(pNewCursor);
568
0
            }
569
0
            break;
570
571
0
        case KEY_RIGHT:
572
0
            if( pCursor )
573
0
            {
574
0
                pNewCursor=pImpCursor->GoLeftRight(pCursor,true );
575
0
                SetCursor_Impl(pNewCursor);
576
0
            }
577
0
            break;
578
579
0
        case KEY_LEFT:
580
0
            if( pCursor )
581
0
            {
582
0
                MakeEntryVisible( pCursor );
583
0
                pNewCursor = pImpCursor->GoLeftRight(pCursor,false );
584
0
                SetCursor_Impl(pNewCursor);
585
0
                if( !pNewCursor )
586
0
                {
587
0
                    tools::Rectangle aRect( GetEntryBoundRect(pCursor));
588
0
                    if( aRect.Left() )
589
0
                    {
590
0
                        aRect.AdjustRight( -(aRect.Left()) );
591
0
                        aRect.SetLeft( 0 );
592
0
                        MakeVisible( aRect );
593
0
                    }
594
0
                }
595
0
            }
596
0
            break;
597
598
0
        case KEY_F2:
599
0
            break;
600
601
0
        case KEY_F8:
602
0
            if( rKEvt.GetKeyCode().IsShift() )
603
0
            {
604
0
                if( nFlags & IconChoiceFlags::AddMode )
605
0
                    nFlags &= ~IconChoiceFlags::AddMode;
606
0
                else
607
0
                    nFlags |= IconChoiceFlags::AddMode;
608
0
            }
609
0
            else
610
0
                bKeyUsed = false;
611
0
            break;
612
613
0
        case KEY_SPACE:
614
0
            break;
615
0
        case KEY_ADD:
616
0
        case KEY_DIVIDE :
617
0
        case KEY_A:
618
0
        case KEY_SUBTRACT:
619
0
        case KEY_COMMA :
620
0
        case KEY_RETURN:
621
0
            bKeyUsed = false;
622
0
            break;
623
624
0
        case KEY_END:
625
0
            if( pCursor )
626
0
            {
627
0
                pNewCursor = maEntries.back().get();
628
0
                SetCursor_Impl(pNewCursor);
629
0
            }
630
0
            break;
631
632
0
        case KEY_HOME:
633
0
            if( pCursor )
634
0
            {
635
0
                pNewCursor = maEntries[ 0 ].get();
636
0
                SetCursor_Impl(pNewCursor);
637
0
            }
638
0
            break;
639
640
0
        default:
641
0
            bKeyUsed = false;
642
643
0
    }
644
0
    return bKeyUsed;
645
0
}
646
647
// recalculate TopLeft of scrollbars (but not their sizes!)
648
void SvxIconChoiceCtrl_Impl::PositionScrollBars( tools::Long nRealWidth, tools::Long nRealHeight )
649
0
{
650
    // horizontal scrollbar
651
0
    Point aPos( 0, nRealHeight );
652
0
    aPos.AdjustY( -nHorSBarHeight );
653
654
0
    if( aHorSBar->GetPosPixel() != aPos )
655
0
        aHorSBar->SetPosPixel( aPos );
656
657
    // vertical scrollbar
658
0
    aPos.setX( nRealWidth ); aPos.setY( 0 );
659
0
    aPos.AdjustX( -nVerSBarWidth );
660
0
    aPos.AdjustX( 1 );
661
0
    aPos.AdjustY( -1 );
662
663
0
    if( aVerSBar->GetPosPixel() != aPos )
664
0
        aVerSBar->SetPosPixel( aPos );
665
0
}
666
667
void SvxIconChoiceCtrl_Impl::AdjustScrollBars()
668
0
{
669
0
    tools::Long nVirtHeight = aVirtOutputSize.Height();
670
0
    tools::Long nVirtWidth = aVirtOutputSize.Width();
671
672
0
    Size aOSize( pView->GetOutputSizePixel() );
673
0
    tools::Long nRealHeight = aOSize.Height();
674
0
    tools::Long nRealWidth = aOSize.Width();
675
676
0
    PositionScrollBars( nRealWidth, nRealHeight );
677
678
0
    const MapMode& rMapMode = pView->GetMapMode();
679
0
    Point aOrigin( rMapMode.GetOrigin() );
680
681
0
    tools::Long nVisibleWidth;
682
0
    if( nRealWidth > nVirtWidth )
683
0
        nVisibleWidth = nVirtWidth + aOrigin.X();
684
0
    else
685
0
        nVisibleWidth = nRealWidth;
686
687
0
    tools::Long nVisibleHeight;
688
0
    if( nRealHeight > nVirtHeight )
689
0
        nVisibleHeight = nVirtHeight + aOrigin.Y();
690
0
    else
691
0
        nVisibleHeight = nRealHeight;
692
693
0
    bool bVerSBar = ( nWinBits & WB_VSCROLL ) != 0;
694
0
    bool bHorSBar = ( nWinBits & WB_HSCROLL ) != 0;
695
0
    bool bNoVerSBar = ( nWinBits & WB_NOVSCROLL ) != 0;
696
0
    bool bNoHorSBar = ( nWinBits & WB_NOHSCROLL ) != 0;
697
698
0
    sal_uInt16 nResult = 0;
699
0
    if( nVirtHeight )
700
0
    {
701
        // activate vertical scrollbar?
702
0
        if( !bNoVerSBar && (bVerSBar || ( nVirtHeight > nVisibleHeight)) )
703
0
        {
704
0
            nResult = 0x0001;
705
0
            nRealWidth -= nVerSBarWidth;
706
707
0
            if( nRealWidth > nVirtWidth )
708
0
                nVisibleWidth = nVirtWidth + aOrigin.X();
709
0
            else
710
0
                nVisibleWidth = nRealWidth;
711
0
        }
712
        // activate horizontal scrollbar?
713
0
        if( !bNoHorSBar && (bHorSBar || (nVirtWidth > nVisibleWidth)) )
714
0
        {
715
0
            nResult |= 0x0002;
716
0
            nRealHeight -= nHorSBarHeight;
717
718
0
            if( nRealHeight > nVirtHeight )
719
0
                nVisibleHeight = nVirtHeight + aOrigin.Y();
720
0
            else
721
0
                nVisibleHeight = nRealHeight;
722
723
            // do we need a vertical scrollbar after all?
724
0
            if( !(nResult & 0x0001) &&  // only if not already there
725
0
                ( !bNoVerSBar && ((nVirtHeight > nVisibleHeight) || bVerSBar)) )
726
0
            {
727
0
                nResult = 3; // both turned on
728
0
                nRealWidth -= nVerSBarWidth;
729
730
0
                if( nRealWidth > nVirtWidth )
731
0
                    nVisibleWidth = nVirtWidth + aOrigin.X();
732
0
                else
733
0
                    nVisibleWidth = nRealWidth;
734
0
            }
735
0
        }
736
0
    }
737
738
    // size vertical scrollbar
739
0
    tools::Long nThumb = aVerSBar->GetThumbPos();
740
0
    Size aSize( nVerSBarWidth, nRealHeight );
741
0
    aSize.AdjustHeight(2 );
742
0
    if( aSize != aVerSBar->GetSizePixel() )
743
0
        aVerSBar->SetSizePixel( aSize );
744
0
    aVerSBar->SetVisibleSize( nVisibleHeight );
745
0
    aVerSBar->SetPageSize( GetScrollBarPageSize( nVisibleHeight ));
746
747
0
    if( nResult & 0x0001 )
748
0
    {
749
0
        aVerSBar->SetThumbPos( nThumb );
750
0
        aVerSBar->Show();
751
0
    }
752
0
    else
753
0
    {
754
0
        aVerSBar->SetThumbPos( 0 );
755
0
        aVerSBar->Hide();
756
0
    }
757
758
    // size horizontal scrollbar
759
0
    nThumb = aHorSBar->GetThumbPos();
760
0
    aSize.setWidth( nRealWidth );
761
0
    aSize.setHeight( nHorSBarHeight );
762
0
    aSize.AdjustWidth( 1 );
763
0
    if( nResult & 0x0001 ) // vertical scrollbar?
764
0
    {
765
0
        aSize.AdjustWidth( 1 );
766
0
        nRealWidth++;
767
0
    }
768
0
    if( aSize != aHorSBar->GetSizePixel() )
769
0
        aHorSBar->SetSizePixel( aSize );
770
0
    aHorSBar->SetVisibleSize( nVisibleWidth );
771
0
    aHorSBar->SetPageSize( GetScrollBarPageSize(nVisibleWidth ));
772
0
    if( nResult & 0x0002 )
773
0
    {
774
0
        aHorSBar->SetThumbPos( nThumb );
775
0
        aHorSBar->Show();
776
0
    }
777
0
    else
778
0
    {
779
0
        aHorSBar->SetThumbPos( 0 );
780
0
        aHorSBar->Hide();
781
0
    }
782
783
0
    aOutputSize.setWidth( nRealWidth );
784
0
    if( nResult & 0x0002 ) // horizontal scrollbar ?
785
0
        nRealHeight++; // because lower border is clipped
786
0
    aOutputSize.setHeight( nRealHeight );
787
788
0
    if( (nResult & (0x0001|0x0002)) == (0x0001|0x0002) )
789
0
        aScrBarBox->Show();
790
0
    else
791
0
        aScrBarBox->Hide();
792
0
}
793
794
void SvxIconChoiceCtrl_Impl::Resize()
795
0
{
796
0
    InitScrollBarBox();
797
0
    aOutputSize = pView->GetOutputSizePixel();
798
0
    pImpCursor->Clear();
799
0
    pGridMap->OutputSizeChanged();
800
801
0
    const Size aSize = pView->GetOutputSizePixel();
802
0
    PositionScrollBars( aSize.Width(), aSize.Height() );
803
    // The scrollbars are shown/hidden asynchronously, so derived classes can
804
    // do an Arrange during Resize, without the scrollbars suddenly turning
805
    // on and off again.
806
    // If an event is already underway, we don't need to send a new one, at least
807
    // as long as there is only one event type.
808
0
    if ( ! nUserEventAdjustScrBars )
809
0
        nUserEventAdjustScrBars =
810
0
            Application::PostUserEvent( LINK( this, SvxIconChoiceCtrl_Impl, UserEventHdl),
811
0
                                        EVENTID_ADJUST_SCROLLBARS);
812
813
0
    VisRectChanged();
814
0
}
815
816
bool SvxIconChoiceCtrl_Impl::CheckHorScrollBar()
817
0
{
818
0
    if( maZOrderList.empty() || !aHorSBar->IsVisible() )
819
0
        return false;
820
0
    const MapMode& rMapMode = pView->GetMapMode();
821
0
    Point aOrigin( rMapMode.GetOrigin() );
822
0
    if(!( nWinBits & WB_HSCROLL) && !aOrigin.X() )
823
0
    {
824
0
        tools::Long nWidth = aOutputSize.Width();
825
0
        const size_t nCount = maZOrderList.size();
826
0
        tools::Long nMostRight = 0;
827
0
        for( size_t nCur = 0; nCur < nCount; nCur++ )
828
0
        {
829
0
            SvxIconChoiceCtrlEntry* pEntry = maZOrderList[ nCur ];
830
0
            tools::Long nRight = GetEntryBoundRect(pEntry).Right();
831
0
            if( nRight > nWidth )
832
0
                return false;
833
0
            if( nRight > nMostRight )
834
0
                nMostRight = nRight;
835
0
        }
836
0
        aHorSBar->Hide();
837
0
        aOutputSize.AdjustHeight(nHorSBarHeight );
838
0
        aVirtOutputSize.setWidth( nMostRight );
839
0
        aHorSBar->SetThumbPos( 0 );
840
0
        Range aRange;
841
0
        aRange.Max() = nMostRight - 1;
842
0
        aHorSBar->SetRange( aRange  );
843
0
        if( aVerSBar->IsVisible() )
844
0
        {
845
0
            Size aSize( aVerSBar->GetSizePixel());
846
0
            aSize.AdjustHeight(nHorSBarHeight );
847
0
            aVerSBar->SetSizePixel( aSize );
848
0
        }
849
0
        return true;
850
0
    }
851
0
    return false;
852
0
}
853
854
bool SvxIconChoiceCtrl_Impl::CheckVerScrollBar()
855
0
{
856
0
    if( maZOrderList.empty() || !aVerSBar->IsVisible() )
857
0
        return false;
858
0
    const MapMode& rMapMode = pView->GetMapMode();
859
0
    Point aOrigin( rMapMode.GetOrigin() );
860
0
    if(!( nWinBits & WB_VSCROLL) && !aOrigin.Y() )
861
0
    {
862
0
        tools::Long nDeepest = 0;
863
0
        tools::Long nHeight = aOutputSize.Height();
864
0
        const size_t nCount = maZOrderList.size();
865
0
        for( size_t nCur = 0; nCur < nCount; nCur++ )
866
0
        {
867
0
            SvxIconChoiceCtrlEntry* pEntry = maZOrderList[ nCur ];
868
0
            tools::Long nBottom = GetEntryBoundRect(pEntry).Bottom();
869
0
            if( nBottom > nHeight )
870
0
                return false;
871
0
            if( nBottom > nDeepest )
872
0
                nDeepest = nBottom;
873
0
        }
874
0
        aVerSBar->Hide();
875
0
        aOutputSize.AdjustWidth(nVerSBarWidth );
876
0
        aVirtOutputSize.setHeight( nDeepest );
877
0
        aVerSBar->SetThumbPos( 0 );
878
0
        Range aRange;
879
0
        aRange.Max() = nDeepest - 1;
880
0
        aVerSBar->SetRange( aRange  );
881
0
        if( aHorSBar->IsVisible() )
882
0
        {
883
0
            Size aSize( aHorSBar->GetSizePixel());
884
0
            aSize.AdjustWidth(nVerSBarWidth );
885
0
            aHorSBar->SetSizePixel( aSize );
886
0
        }
887
0
        return true;
888
0
    }
889
0
    return false;
890
0
}
891
892
893
// hides scrollbars if they're unnecessary
894
void SvxIconChoiceCtrl_Impl::CheckScrollBars()
895
0
{
896
0
    CheckVerScrollBar();
897
0
    if( CheckHorScrollBar() )
898
0
        CheckVerScrollBar();
899
0
    if( aVerSBar->IsVisible() && aHorSBar->IsVisible() )
900
0
        aScrBarBox->Show();
901
0
    else
902
0
        aScrBarBox->Hide();
903
0
}
904
905
906
void SvxIconChoiceCtrl_Impl::GetFocus()
907
0
{
908
0
    RepaintSelectedEntries();
909
0
    if( pCursor )
910
0
    {
911
0
        pCursor->SetFlags( SvxIconViewFlags::FOCUSED );
912
0
        ShowCursor( true );
913
0
    }
914
0
}
915
916
void SvxIconChoiceCtrl_Impl::LoseFocus()
917
0
{
918
0
    if( pCursor )
919
0
        pCursor->ClearFlags( SvxIconViewFlags::FOCUSED );
920
0
    ShowCursor( false );
921
922
//  HideFocus ();
923
//  pView->Invalidate ( aFocus.aRect );
924
925
0
    RepaintSelectedEntries();
926
0
}
927
928
// priorities of the emphasis:  bSelected
929
void SvxIconChoiceCtrl_Impl::PaintEmphasis(const tools::Rectangle& rTextRect,
930
                                           vcl::RenderContext& rRenderContext)
931
0
{
932
0
    const Color& rFillColor = rRenderContext.GetFont().GetFillColor();
933
0
    if (rFillColor == COL_TRANSPARENT)
934
0
        return;
935
936
    // draw text rectangle
937
0
    auto popIt = rRenderContext.ScopedPush(vcl::PushFlags::FILLCOLOR);
938
0
    rRenderContext.SetFillColor(rFillColor);
939
0
    rRenderContext.DrawRect(rTextRect);
940
0
}
941
942
943
void SvxIconChoiceCtrl_Impl::PaintItem(const tools::Rectangle& rRect,
944
    IcnViewFieldType eItem, const SvxIconChoiceCtrlEntry* pEntry, sal_uInt16 nPaintFlags,
945
    vcl::RenderContext& rRenderContext )
946
0
{
947
0
    if (eItem == IcnViewFieldType::Text)
948
0
    {
949
0
        if (nWinBits & WB_DETAILS)
950
0
        {
951
            // Vertically center text when the entry is text-only
952
0
            tools::Long nBoundingHeight(CalcBoundingHeight());
953
0
            tools::Long nStringHeight = GetItemSize(IcnViewFieldType::Text).Height();
954
0
            tools::Long nNewY = (nBoundingHeight - nStringHeight) / 2;
955
0
            Point aRectTL(rRect.TopLeft().getX(), rRect.TopLeft().getY() + nNewY);
956
0
            tools::Rectangle aTextRect(aRectTL, rRect.GetSize());
957
0
            rRenderContext.DrawText(aTextRect, pEntry->GetText(), nCurTextDrawFlags);
958
0
        }
959
0
        else
960
0
        {
961
0
            rRenderContext.DrawText(rRect, pEntry->GetText(), nCurTextDrawFlags);
962
0
        }
963
0
    }
964
0
    else
965
0
    {
966
0
        Point aPos(rRect.TopLeft());
967
0
        if (nPaintFlags & PAINTFLAG_HOR_CENTERED)
968
0
        {
969
0
            aPos.AdjustX((rRect.GetWidth() - aImageSize.Width()) / 2 );
970
0
            aPos.AdjustY( VER_DIST_BMP_STRING );
971
0
        }
972
0
        if (nPaintFlags & PAINTFLAG_VER_CENTERED)
973
0
        {
974
0
            aPos.AdjustX( HOR_DIST_BMP_STRING );
975
0
            Size aSize = pEntry->GetImage().GetSizePixel();
976
0
            aPos.AdjustY((rRect.GetHeight() - aSize.Height()) / 2 );
977
0
        }
978
0
        rRenderContext.DrawImage(aPos, pEntry->GetImage());
979
980
0
    }
981
0
}
982
983
void SvxIconChoiceCtrl_Impl::PaintEntry(SvxIconChoiceCtrlEntry* pEntry, const Point& rPos, vcl::RenderContext& rRenderContext)
984
0
{
985
0
    auto popIt = rRenderContext.ScopedPush(vcl::PushFlags::FONT | vcl::PushFlags::TEXTCOLOR);
986
987
0
    tools::Rectangle aTextRect(CalcTextRect(pEntry, &rPos));
988
0
    tools::Rectangle aBmpRect(CalcBmpRect(pEntry, &rPos));
989
990
0
    const bool bMouseHovered = pEntry == pCurHighlightFrame;
991
0
    const bool bSelected = pEntry->IsSelected();
992
993
0
    const StyleSettings& rSettings = rRenderContext.GetSettings().GetStyleSettings();
994
0
    vcl::Font aNewFont(rRenderContext.GetFont());
995
0
    if (bSelected)
996
#ifdef MACOSX
997
        // On macOS, selected tabs are drawn as default push buttons
998
        aNewFont.SetColor(rSettings.GetDefaultActionButtonPressedRolloverTextColor());
999
#else
1000
0
        aNewFont.SetColor(rSettings.GetTabHighlightTextColor());
1001
0
#endif
1002
0
    else if (bMouseHovered)
1003
0
        aNewFont.SetColor(rSettings.GetTabRolloverTextColor());
1004
0
    else
1005
0
        aNewFont.SetColor(rSettings.GetTabTextColor());
1006
0
    rRenderContext.SetFont(aNewFont);
1007
1008
0
    bool bResetClipRegion = false;
1009
0
    if (!rRenderContext.IsClipRegion() && (aVerSBar->IsVisible() || aHorSBar->IsVisible()))
1010
0
    {
1011
0
        tools::Rectangle aOutputArea(GetOutputRect());
1012
0
        if (aOutputArea.Overlaps(aTextRect) || aOutputArea.Overlaps(aBmpRect))
1013
0
        {
1014
0
            rRenderContext.SetClipRegion(vcl::Region(aOutputArea));
1015
0
            bResetClipRegion = true;
1016
0
        }
1017
0
    }
1018
1019
0
    bool bLargeIconMode = WB_ICON == ( nWinBits & VIEWMODE_MASK );
1020
0
    sal_uInt16 nBmpPaintFlags = PAINTFLAG_VER_CENTERED;
1021
0
    if (bLargeIconMode)
1022
0
        nBmpPaintFlags |= PAINTFLAG_HOR_CENTERED;
1023
0
    sal_uInt16 nTextPaintFlags = bLargeIconMode ? PAINTFLAG_HOR_CENTERED : PAINTFLAG_VER_CENTERED;
1024
1025
0
    tools::Rectangle aFocusRect(CalcFocusRect(pEntry));
1026
1027
0
    bool bNativeOK
1028
#ifdef MACOSX
1029
        = rRenderContext.IsNativeControlSupported(ControlType::Pushbutton, ControlPart::Entire);
1030
#else
1031
0
        = rRenderContext.IsNativeControlSupported(ControlType::TabItem, ControlPart::Entire);
1032
0
#endif
1033
0
    if (bNativeOK)
1034
0
    {
1035
0
        ControlState nState = ControlState::ENABLED;
1036
0
        ControlPart nPart(ControlPart::Entire);
1037
1038
#ifdef MACOSX
1039
        if (bSelected)
1040
        {
1041
            // Related: tdf#163008 draw the selected tab using a push button
1042
            // On macOS, more closely match the vertical tab style of the
1043
            // sidebar in the System Settings application. As of macOS Sequoia,
1044
            // the sidebar only shows the default style push button for the
1045
            // selected tab. The unselected tabs are only text and images so
1046
            // no native control needs to be drawn for unselected tabs.
1047
            nState |= ControlState::DEFAULT;
1048
1049
            // Allow the native push button to expand its height to match
1050
            // the focus rectangle's height.
1051
            PushButtonValue aControlValue;
1052
            aControlValue.mbSingleLine = false;
1053
            aControlValue.m_bFlatButton = true;
1054
1055
            // Eliminate artifacts when this entry becomes unselected by
1056
            // making the push button slightly narrower than the focus
1057
            // rectangle so that there is no antialiased pixels drawn
1058
            // outside the focus rectangle.
1059
            tools::Rectangle aButtonRect(aFocusRect);
1060
            if (aButtonRect.GetWidth() > 2)
1061
            {
1062
                aButtonRect.SetLeft(aButtonRect.Left() + 1);
1063
                aButtonRect.SetRight(aButtonRect.Right() - 1);
1064
            }
1065
1066
            bNativeOK = rRenderContext.DrawNativeControl(ControlType::Pushbutton, nPart,
1067
                                                         aButtonRect, nState, aControlValue, OUString());
1068
        }
1069
#else
1070
0
        if (bSelected)
1071
0
            nState |= ControlState::SELECTED;
1072
0
        if (pEntry->IsFocused())
1073
0
            nState |= ControlState::FOCUSED;
1074
0
        if (bMouseHovered)
1075
0
            nState |= ControlState::ROLLOVER;
1076
1077
0
        TabitemValue tiValue(aFocusRect, TabBarPosition::Left);
1078
#ifdef _WIN32
1079
        // ControlPart::MenuItem prevents drawing line around tabs under win
1080
        nPart = ControlPart::MenuItem;
1081
#endif
1082
0
        bNativeOK = rRenderContext.DrawNativeControl(ControlType::TabItem, nPart,
1083
0
                                                     aFocusRect, nState, tiValue, OUString());
1084
0
#endif
1085
0
    }
1086
1087
0
    if (!bNativeOK)
1088
0
    {
1089
0
        if (bSelected) {
1090
0
            rRenderContext.DrawSelectionBackground(aFocusRect, pView->GetBackgroundColor(),
1091
0
                                                    pView->HasFocus() ? 1 : 2, false);
1092
0
        } else
1093
0
            PaintEmphasis(aTextRect, rRenderContext);
1094
1095
0
        if (pEntry->IsFocused())
1096
0
            DrawFocusRect(rRenderContext, pEntry);
1097
1098
        // highlight mouse-hovered entry
1099
0
        if (bMouseHovered)
1100
0
            DrawHighlightFrame(rRenderContext, aFocusRect);
1101
0
    }
1102
1103
0
    PaintItem(aBmpRect, IcnViewFieldType::Image, pEntry, nBmpPaintFlags, rRenderContext);
1104
1105
    // Move text a bit to the right for native controls due to potential tab mark (applies to text-only entries)
1106
0
    if (bNativeOK && (nWinBits & WB_DETAILS))
1107
0
        aTextRect.SetPos(Point(aTextRect.GetPos().X() + TAB_MARK_WIDTH, aTextRect.GetPos().Y()));
1108
1109
0
    PaintItem(aTextRect, IcnViewFieldType::Text, pEntry, nTextPaintFlags, rRenderContext);
1110
1111
0
    if (bResetClipRegion)
1112
0
        rRenderContext.SetClipRegion();
1113
0
}
1114
1115
SvxIconChoiceCtrlEntry* SvxIconChoiceCtrl_Impl::GetEntry( const Point& rDocPos )
1116
0
{
1117
0
    CheckBoundingRects();
1118
    // search through z-order list from the end
1119
0
    size_t nCount = maZOrderList.size();
1120
0
    while( nCount )
1121
0
    {
1122
0
        nCount--;
1123
0
        SvxIconChoiceCtrlEntry* pEntry = maZOrderList[ nCount ];
1124
0
        tools::Rectangle aBoundingRect(GetEntryBoundRect(pEntry));
1125
0
        if( aBoundingRect.Contains( rDocPos ) )
1126
0
            return pEntry;
1127
0
    }
1128
0
    return nullptr;
1129
0
}
1130
1131
void SvxIconChoiceCtrl_Impl::MakeEntryVisible(SvxIconChoiceCtrlEntry* pEntry)
1132
0
{
1133
0
    const tools::Rectangle& rRect = GetEntryBoundRect( pEntry );
1134
0
    MakeVisible(rRect);
1135
0
}
1136
1137
const tools::Rectangle& SvxIconChoiceCtrl_Impl::GetEntryBoundRect( SvxIconChoiceCtrlEntry* pEntry )
1138
0
{
1139
0
    if( !IsBoundingRectValid( pEntry->aRect ))
1140
0
        FindBoundingRect( pEntry );
1141
0
    return pEntry->aRect;
1142
0
}
1143
1144
tools::Rectangle SvxIconChoiceCtrl_Impl::CalcBmpRect( SvxIconChoiceCtrlEntry* pEntry, const Point* pPos )
1145
0
{
1146
0
    tools::Rectangle aBound = GetEntryBoundRect( pEntry );
1147
0
    if( pPos )
1148
0
        aBound.SetPos( *pPos );
1149
0
    Point aPos( aBound.TopLeft() );
1150
1151
0
    switch( nWinBits & VIEWMODE_MASK )
1152
0
    {
1153
0
        case WB_ICON:
1154
0
        {
1155
0
            aPos.AdjustX(( aBound.GetWidth() - aImageSize.Width() ) / 2 );
1156
0
            return tools::Rectangle( aPos, aImageSize );
1157
0
        }
1158
1159
0
        case WB_DETAILS:
1160
0
            return tools::Rectangle(aPos, Size(0,0));
1161
0
        case WB_SMALLICON:
1162
0
            aPos.AdjustY(( aBound.GetHeight() - aImageSize.Height() ) / 2 );
1163
            //TODO: determine horizontal distance to bounding rectangle
1164
0
            return tools::Rectangle( aPos, aImageSize );
1165
1166
0
        default:
1167
0
            assert(false && "IconView: Viewmode not set");
1168
0
            return aBound;
1169
0
    }
1170
0
}
1171
1172
tools::Rectangle SvxIconChoiceCtrl_Impl::CalcTextRect( SvxIconChoiceCtrlEntry* pEntry,
1173
    const Point* pEntryPos)
1174
0
{
1175
0
    const tools::Rectangle aMaxTextRect( CalcMaxTextRect( pEntry ) );
1176
0
    tools::Rectangle aBound( GetEntryBoundRect( pEntry ) );
1177
0
    if( pEntryPos )
1178
0
        aBound.SetPos( *pEntryPos );
1179
1180
0
    const OUString aEntryText = pEntry->GetText();
1181
0
    tools::Rectangle aTextRect = pView->GetTextRect( aMaxTextRect, aEntryText, nCurTextDrawFlags );
1182
1183
0
    Size aTextSize( aTextRect.GetSize() );
1184
1185
0
    Point aPos( aBound.TopLeft() );
1186
0
    tools::Long nBoundWidth = aBound.GetWidth();
1187
0
    tools::Long nBoundHeight = aBound.GetHeight();
1188
1189
0
    switch( nWinBits & VIEWMODE_MASK )
1190
0
    {
1191
0
        case WB_ICON:
1192
0
            aPos.AdjustY(aImageSize.Height() );
1193
0
            aPos.AdjustY(VER_DIST_BMP_STRING );
1194
0
            aPos.AdjustX((nBoundWidth - aTextSize.Width()) / 2 );
1195
0
            break;
1196
1197
0
        case WB_DETAILS:
1198
0
            break;
1199
1200
0
        case WB_SMALLICON:
1201
0
            aPos.AdjustX(aImageSize.Width() );
1202
0
            aPos.AdjustX(HOR_DIST_BMP_STRING );
1203
0
            aPos.AdjustY((nBoundHeight - aTextSize.Height()) / 2 );
1204
            // final calculation of width after initially done on insert
1205
0
            long nNewWidth = aImageSize.Width() + aTextSize.Width() + 2*HOR_DIST_BMP_STRING;
1206
0
            nNewWidth = pView->AdjustWidth(nNewWidth);
1207
0
            pEntry->aRect.setWidth(nNewWidth);
1208
0
            break;
1209
0
    }
1210
0
    return tools::Rectangle( aPos, aTextSize );
1211
0
}
1212
1213
1214
tools::Long SvxIconChoiceCtrl_Impl::CalcBoundingWidth() const
1215
0
{
1216
0
    tools::Long nStringWidth = GetItemSize( IcnViewFieldType::Text ).Width();
1217
0
    tools::Long nWidth = 0;
1218
1219
0
    switch( nWinBits & VIEWMODE_MASK )
1220
0
    {
1221
0
        case WB_ICON:
1222
0
            nWidth = std::max( nStringWidth, aImageSize.Width() );
1223
0
            break;
1224
1225
0
        case WB_DETAILS:
1226
0
            nWidth = nStringWidth;
1227
0
            break;
1228
1229
0
        case WB_SMALLICON:
1230
0
            nWidth = aImageSize.Width();
1231
0
            nWidth += HOR_DIST_BMP_STRING;
1232
0
            nWidth += nStringWidth;
1233
0
            break;
1234
0
    }
1235
0
    return nWidth;
1236
0
}
1237
1238
tools::Long SvxIconChoiceCtrl_Impl::CalcBoundingHeight() const
1239
0
{
1240
0
    tools::Long nStringHeight = GetItemSize(IcnViewFieldType::Text).Height();
1241
0
    tools::Long nHeight = 0;
1242
1243
0
    switch( nWinBits & VIEWMODE_MASK )
1244
0
    {
1245
0
        case WB_ICON:
1246
0
            nHeight = aImageSize.Height();
1247
0
            nHeight += VER_DIST_BMP_STRING;
1248
0
            nHeight += nStringHeight;
1249
0
            break;
1250
1251
0
        case WB_DETAILS:
1252
0
            nHeight = nStringHeight + 2 * VERT_TEXT_PADDING;;
1253
0
            break;
1254
1255
0
        case WB_SMALLICON:
1256
0
            nHeight = std::max( aImageSize.Height(), nStringHeight );
1257
0
            break;
1258
0
    }
1259
0
    if( nHeight > nMaxBoundHeight )
1260
0
    {
1261
0
        const_cast<SvxIconChoiceCtrl_Impl*>(this)->nMaxBoundHeight = nHeight;
1262
0
        const_cast<SvxIconChoiceCtrl_Impl*>(this)->aHorSBar->SetLineSize( GetScrollBarLineSize() );
1263
0
        const_cast<SvxIconChoiceCtrl_Impl*>(this)->aVerSBar->SetLineSize( GetScrollBarLineSize() );
1264
0
    }
1265
0
    return nHeight;
1266
0
}
1267
1268
Size SvxIconChoiceCtrl_Impl::CalcBoundingSize() const
1269
0
{
1270
0
    return Size( CalcBoundingWidth(), CalcBoundingHeight() );
1271
0
}
1272
1273
void SvxIconChoiceCtrl_Impl::RecalcAllBoundingRectsSmart()
1274
0
{
1275
0
    nMaxBoundHeight = 0;
1276
0
    maZOrderList.clear();
1277
0
    size_t nCur;
1278
0
    SvxIconChoiceCtrlEntry* pEntry;
1279
0
    const size_t nCount = maEntries.size();
1280
1281
0
    for( nCur = 0; nCur < nCount; nCur++ )
1282
0
    {
1283
0
        pEntry = maEntries[ nCur ].get();
1284
0
        if( IsBoundingRectValid( pEntry->aRect ))
1285
0
        {
1286
0
            Size aBoundSize( pEntry->aRect.GetSize() );
1287
0
            if( aBoundSize.Height() > nMaxBoundHeight )
1288
0
                nMaxBoundHeight = aBoundSize.Height();
1289
0
        }
1290
0
        else
1291
0
            FindBoundingRect( pEntry );
1292
0
        maZOrderList.push_back( pEntry );
1293
0
    }
1294
0
    AdjustScrollBars();
1295
0
}
1296
1297
void SvxIconChoiceCtrl_Impl::FindBoundingRect( SvxIconChoiceCtrlEntry* pEntry )
1298
0
{
1299
0
    CalcBoundingSize();
1300
0
    Point aPos(pGridMap->GetGridRect(pGridMap->GetUnoccupiedGrid()).TopLeft());
1301
1302
0
    tools::Rectangle aGridRect(aPos, Size(nGridDX, nGridDY));
1303
0
    pEntry->aRect = aGridRect;
1304
0
    AdjustVirtSize( pEntry->aRect );
1305
0
    pGridMap->OccupyGrids( pEntry );
1306
0
}
1307
1308
1309
void SvxIconChoiceCtrl_Impl::SetCursor( SvxIconChoiceCtrlEntry* pEntry )
1310
0
{
1311
0
    if( pEntry == pCursor )
1312
0
    {
1313
0
        if (pCursor && !pCursor->IsSelected())
1314
0
            SelectEntry( pCursor, true );
1315
0
        return;
1316
0
    }
1317
0
    ShowCursor( false );
1318
0
    SvxIconChoiceCtrlEntry* pOldCursor = pCursor;
1319
0
    pCursor = pEntry;
1320
0
    if( pOldCursor )
1321
0
    {
1322
0
        pOldCursor->ClearFlags( SvxIconViewFlags::FOCUSED );
1323
0
        SelectEntry(pOldCursor, false); // deselect old cursor
1324
0
    }
1325
0
    if( pCursor )
1326
0
    {
1327
0
        ToTop( pCursor );
1328
0
        pCursor->SetFlags( SvxIconViewFlags::FOCUSED );
1329
0
        SelectEntry(pCursor, true);
1330
0
        ShowCursor( true );
1331
0
    }
1332
0
}
1333
1334
1335
void SvxIconChoiceCtrl_Impl::ShowCursor( bool bShow )
1336
0
{
1337
0
    if( !pCursor || !bShow || !pView->HasFocus() )
1338
0
    {
1339
0
        pView->HideFocus();
1340
0
        return;
1341
0
    }
1342
0
}
1343
1344
bool SvxIconChoiceCtrl_Impl::HandleScrollCommand( const CommandEvent& rCmd )
1345
0
{
1346
0
    tools::Rectangle aDocRect( Point(), aVirtOutputSize );
1347
0
    tools::Rectangle aVisRect( GetOutputRect() );
1348
0
    if( aVisRect.Contains( aDocRect ))
1349
0
        return false;
1350
0
    Size aDocSize( aDocRect.GetSize() );
1351
0
    Size aVisSize( aVisRect.GetSize() );
1352
0
    bool bHor = aDocSize.Width() > aVisSize.Width();
1353
0
    bool bVer = aDocSize.Height() > aVisSize.Height();
1354
1355
0
    tools::Long nScrollDX = 0, nScrollDY = 0;
1356
1357
0
    switch( rCmd.GetCommand() )
1358
0
    {
1359
0
        case CommandEventId::StartAutoScroll:
1360
0
        {
1361
0
            pView->EndTracking();
1362
0
            StartAutoScrollFlags nScrollFlags = StartAutoScrollFlags::NONE;
1363
0
            if( bHor )
1364
0
                nScrollFlags |= StartAutoScrollFlags::Horz;
1365
0
            if( bVer )
1366
0
                nScrollFlags |= StartAutoScrollFlags::Vert;
1367
0
            if( nScrollFlags != StartAutoScrollFlags::NONE )
1368
0
            {
1369
0
                pView->StartAutoScroll( nScrollFlags );
1370
0
                return true;
1371
0
            }
1372
0
        }
1373
0
        break;
1374
1375
0
        case CommandEventId::Wheel:
1376
0
        {
1377
0
            const CommandWheelData* pData = rCmd.GetWheelData();
1378
0
            if( pData && (CommandWheelMode::SCROLL == pData->GetMode()) && !pData->IsHorz() )
1379
0
            {
1380
0
                double nScrollLines = pData->GetScrollLines();
1381
0
                if( nScrollLines == COMMAND_WHEEL_PAGESCROLL )
1382
0
                {
1383
0
                    nScrollDY = GetScrollBarPageSize( aVisSize.Width() );
1384
0
                    if( pData->GetDelta() < 0 )
1385
0
                        nScrollDY *= -1;
1386
0
                }
1387
0
                else
1388
0
                {
1389
0
                    nScrollDY = pData->GetNotchDelta() * static_cast<tools::Long>(nScrollLines);
1390
0
                    nScrollDY *= GetScrollBarLineSize();
1391
0
                }
1392
0
            }
1393
0
        }
1394
0
        break;
1395
1396
0
        case CommandEventId::AutoScroll:
1397
0
        {
1398
0
            const CommandScrollData* pData = rCmd.GetAutoScrollData();
1399
0
            if( pData )
1400
0
            {
1401
0
                nScrollDX = pData->GetDeltaX() * GetScrollBarLineSize();
1402
0
                nScrollDY = pData->GetDeltaY() * GetScrollBarLineSize();
1403
0
            }
1404
0
        }
1405
0
        break;
1406
1407
0
        default: break;
1408
0
    }
1409
1410
0
    if( nScrollDX || nScrollDY )
1411
0
    {
1412
0
        aVisRect.AdjustTop( -nScrollDY );
1413
0
        aVisRect.AdjustBottom( -nScrollDY );
1414
0
        aVisRect.AdjustLeft( -nScrollDX );
1415
0
        aVisRect.AdjustRight( -nScrollDX );
1416
0
        MakeVisible( aVisRect );
1417
0
        return true;
1418
0
    }
1419
0
    return false;
1420
0
}
1421
1422
1423
void SvxIconChoiceCtrl_Impl::Command( const CommandEvent& rCEvt )
1424
0
{
1425
    // scroll mouse event?
1426
0
    if( (rCEvt.GetCommand() == CommandEventId::Wheel) ||
1427
0
        (rCEvt.GetCommand() == CommandEventId::StartAutoScroll) ||
1428
0
        (rCEvt.GetCommand() == CommandEventId::AutoScroll) )
1429
0
    {
1430
0
        if( HandleScrollCommand( rCEvt ) )
1431
0
            return;
1432
0
    }
1433
0
}
1434
1435
void SvxIconChoiceCtrl_Impl::ToTop( SvxIconChoiceCtrlEntry* pEntry )
1436
0
{
1437
0
    if( maZOrderList.empty() || pEntry == maZOrderList.back())
1438
0
        return;
1439
1440
0
    auto it = std::find(maZOrderList.begin(), maZOrderList.end(), pEntry);
1441
0
    if (it != maZOrderList.end())
1442
0
    {
1443
0
        maZOrderList.erase( it );
1444
0
        maZOrderList.push_back( pEntry );
1445
0
    }
1446
0
}
1447
1448
void SvxIconChoiceCtrl_Impl::ClipAtVirtOutRect( tools::Rectangle& rRect ) const
1449
0
{
1450
0
    if( rRect.Bottom() >= aVirtOutputSize.Height() )
1451
0
        rRect.SetBottom( aVirtOutputSize.Height() - 1 );
1452
0
    if( rRect.Right() >= aVirtOutputSize.Width() )
1453
0
        rRect.SetRight( aVirtOutputSize.Width() - 1 );
1454
0
    if( rRect.Top() < 0 )
1455
0
        rRect.SetTop( 0 );
1456
0
    if( rRect.Left() < 0 )
1457
0
        rRect.SetLeft( 0 );
1458
0
}
1459
1460
// rRect: area of the document (in document coordinates) that we want to make
1461
// visible
1462
// bScrBar == true: rectangle was calculated because of a scrollbar event
1463
1464
void SvxIconChoiceCtrl_Impl::MakeVisible( const tools::Rectangle& rRect, bool bScrBar )
1465
0
{
1466
0
    tools::Rectangle aVirtRect( rRect );
1467
0
    ClipAtVirtOutRect( aVirtRect );
1468
0
    Point aOrigin( pView->GetMapMode().GetOrigin() );
1469
    // convert to document coordinate
1470
0
    aOrigin *= -1;
1471
0
    tools::Rectangle aOutputArea( GetOutputRect() );
1472
0
    if( aOutputArea.Contains( aVirtRect ) )
1473
0
        return; // is already visible
1474
1475
0
    tools::Long nDy;
1476
0
    if( aVirtRect.Top() < aOutputArea.Top() )
1477
0
    {
1478
        // scroll up (nDy < 0)
1479
0
        nDy = aVirtRect.Top() - aOutputArea.Top();
1480
0
    }
1481
0
    else if( aVirtRect.Bottom() > aOutputArea.Bottom() )
1482
0
    {
1483
        // scroll down (nDy > 0)
1484
0
        nDy = aVirtRect.Bottom() - aOutputArea.Bottom();
1485
0
    }
1486
0
    else
1487
0
        nDy = 0;
1488
1489
0
    tools::Long nDx = 0;
1490
1491
    // no horizontal scrolling needed in list mode
1492
0
    if (!(nWinBits & WB_DETAILS))
1493
0
    {
1494
0
        if( aVirtRect.Left() < aOutputArea.Left() )
1495
0
        {
1496
            // scroll to the left (nDx < 0)
1497
0
            nDx = aVirtRect.Left() - aOutputArea.Left();
1498
0
        }
1499
0
        else if( aVirtRect.Right() > aOutputArea.Right() )
1500
0
        {
1501
            // scroll to the right (nDx > 0)
1502
0
            nDx = aVirtRect.Right() - aOutputArea.Right();
1503
0
        }
1504
0
    }
1505
1506
0
    aOrigin.AdjustX(nDx );
1507
0
    aOrigin.AdjustY(nDy );
1508
0
    aOutputArea.SetPos( aOrigin );
1509
1510
0
    pView->PaintImmediately();
1511
0
    ShowCursor(false);
1512
1513
    // invert origin for SV (so we can scroll/paint using document coordinates)
1514
0
    aOrigin *= -1;
1515
0
    SetOrigin( aOrigin );
1516
1517
0
    bool bScrollable = pView->GetBackground().IsScrollable();
1518
1519
0
    if (bScrollable)
1520
0
    {
1521
        // scroll in reverse direction!
1522
0
        pView->Scroll( -nDx, -nDy, aOutputArea,
1523
0
            ScrollFlags::NoChildren | ScrollFlags::UseClipRegion | ScrollFlags::Clip );
1524
0
    }
1525
0
    else
1526
0
        pView->Invalidate(InvalidateFlags::NoChildren);
1527
1528
0
    if( aHorSBar->IsVisible() || aVerSBar->IsVisible() )
1529
0
    {
1530
0
        if( !bScrBar )
1531
0
        {
1532
0
            aOrigin *= -1;
1533
            // correct thumbs
1534
0
            if(aHorSBar->IsVisible() && aHorSBar->GetThumbPos() != aOrigin.X())
1535
0
                aHorSBar->SetThumbPos( aOrigin.X() );
1536
0
            if(aVerSBar->IsVisible() && aVerSBar->GetThumbPos() != aOrigin.Y())
1537
0
                aVerSBar->SetThumbPos( aOrigin.Y() );
1538
0
        }
1539
0
    }
1540
1541
0
    ShowCursor(true);
1542
1543
    // check if we still need scrollbars
1544
0
    CheckScrollBars();
1545
0
    if (bScrollable)
1546
0
        pView->PaintImmediately();
1547
1548
    // If the requested area can not be made completely visible, the
1549
    // Vis-Rect-Changed handler is called in any case. This case may occur e.g.
1550
    // if only few pixels of the lower border are invisible, but a scrollbar has
1551
    // a larger line size.
1552
0
    VisRectChanged();
1553
0
}
1554
1555
void SvxIconChoiceCtrl_Impl::DeselectAllBut( SvxIconChoiceCtrlEntry const * pThisEntryNot )
1556
0
{
1557
    // TODO: work through z-order list, if necessary!
1558
1559
0
    size_t nCount = maEntries.size();
1560
0
    for( size_t nCur = 0; nCur < nCount; nCur++ )
1561
0
    {
1562
0
        SvxIconChoiceCtrlEntry* pEntry = maEntries[ nCur ].get();
1563
0
        if( pEntry != pThisEntryNot && pEntry->IsSelected() )
1564
0
            SelectEntry( pEntry, false, true );
1565
0
    }
1566
0
    nFlags &= ~IconChoiceFlags::AddMode;
1567
0
}
1568
1569
Size SvxIconChoiceCtrl_Impl::GetMinGrid() const
1570
0
{
1571
0
    Size aTextSize( pView->GetTextWidth( u"XXX"_ustr ), pView->GetTextHeight() );
1572
0
    if (nWinBits & WB_DETAILS)
1573
0
        return Size(aTextSize.Width(), aTextSize.Height());
1574
1575
0
    Size aMinSize( aImageSize );
1576
0
    aMinSize.AdjustWidth(2 * LROFFS_BOUND );
1577
0
    if( nWinBits & WB_ICON )
1578
0
    {
1579
0
        aMinSize.AdjustHeight(VER_DIST_BMP_STRING );
1580
0
        aMinSize.AdjustHeight(aTextSize.Height() );
1581
0
    }
1582
0
    else
1583
0
    {
1584
0
        aMinSize.AdjustWidth(HOR_DIST_BMP_STRING );
1585
0
        aMinSize.AdjustWidth(aTextSize.Width() );
1586
0
    }
1587
0
    return aMinSize;
1588
0
}
1589
1590
void SvxIconChoiceCtrl_Impl::SetGrid( const Size& rSize )
1591
0
{
1592
0
    Size aSize( rSize );
1593
0
    Size aMinSize( GetMinGrid() );
1594
0
    if( aSize.Width() < aMinSize.Width() )
1595
0
        aSize.setWidth( aMinSize.Width() );
1596
0
    if( aSize.Height() < aMinSize.Height() )
1597
0
        aSize.setHeight( aMinSize.Height() );
1598
1599
0
    nGridDX = aSize.Width();
1600
0
    nGridDY = aSize.Height();
1601
0
    SetDefaultTextSize();
1602
0
}
1603
1604
// Calculates the maximum size that the text rectangle may use within its
1605
// bounding rectangle.
1606
1607
tools::Rectangle SvxIconChoiceCtrl_Impl::CalcMaxTextRect( const SvxIconChoiceCtrlEntry* pEntry ) const
1608
0
{
1609
0
    assert(IsBoundingRectValid(pEntry->aRect) && "Bounding rect for entry hasn't been calculated yet.");
1610
0
    tools::Rectangle aBoundRect = pEntry->aRect;
1611
1612
0
    tools::Rectangle aBmpRect( const_cast<SvxIconChoiceCtrl_Impl*>(this)->CalcBmpRect(
1613
0
        const_cast<SvxIconChoiceCtrlEntry*>(pEntry) ) );
1614
0
    if( nWinBits & WB_ICON )
1615
0
    {
1616
0
        aBoundRect.SetTop( aBmpRect.Bottom() );
1617
0
        aBoundRect.AdjustTop(VER_DIST_BMP_STRING );
1618
0
        if( aBoundRect.Top() > aBoundRect.Bottom())
1619
0
            aBoundRect.SetTop( aBoundRect.Bottom() );
1620
0
        aBoundRect.AdjustLeft(LROFFS_BOUND );
1621
0
        aBoundRect.AdjustLeft( 1 );
1622
0
        aBoundRect.AdjustRight( -(LROFFS_BOUND) );
1623
0
        aBoundRect.AdjustRight( -1 );
1624
0
        if( aBoundRect.Left() > aBoundRect.Right())
1625
0
            aBoundRect.SetLeft( aBoundRect.Right() );
1626
0
    }
1627
0
    else if (nWinBits & WB_SMALLICON)
1628
0
    {
1629
0
        aBoundRect.SetLeft( aBmpRect.Right() );
1630
0
        aBoundRect.AdjustLeft(HOR_DIST_BMP_STRING );
1631
0
        aBoundRect.AdjustRight( -(LROFFS_BOUND) );
1632
0
        if( aBoundRect.Left() > aBoundRect.Right() )
1633
0
            aBoundRect.SetLeft( aBoundRect.Right() );
1634
0
        tools::Long nHeight = aBoundRect.GetSize().Height();
1635
0
        nHeight = nHeight - aDefaultTextSize.Height();
1636
0
        nHeight /= 2;
1637
0
        aBoundRect.AdjustTop(nHeight );
1638
0
        aBoundRect.AdjustBottom( -nHeight );
1639
0
        aBoundRect.setWidth(INT_MAX); // effective width calculated in CalcTextRect()
1640
0
    }
1641
0
    return aBoundRect;
1642
0
}
1643
1644
void SvxIconChoiceCtrl_Impl::SetDefaultTextSize()
1645
0
{
1646
0
    tools::Long nDY = nGridDY;
1647
0
    nDY -= aImageSize.Height();
1648
0
    nDY -= VER_DIST_BMP_STRING;
1649
0
    if (nDY <= 0)
1650
0
        nDY = 2;
1651
1652
0
    tools::Long nDX = nGridDX;
1653
0
    nDX -= 2 * LROFFS_BOUND;
1654
0
    nDX -= 2;
1655
0
    if (nDX <= 0)
1656
0
        nDX = 2;
1657
1658
0
    tools::Long nHeight = pView->GetTextHeight();
1659
0
    if (nDY < nHeight)
1660
0
        nDY = nHeight;
1661
0
    if(pView->GetDPIScaleFactor() > 1)
1662
0
    {
1663
0
      nDY*=2;
1664
0
    }
1665
0
    aDefaultTextSize = Size(nDX, nDY);
1666
0
}
1667
1668
// The deltas are the offsets by which the view is moved on the document.
1669
// left, up: offsets < 0
1670
// right, down: offsets > 0
1671
void SvxIconChoiceCtrl_Impl::Scroll( tools::Long nDeltaX, tools::Long nDeltaY )
1672
0
{
1673
0
    const MapMode& rMapMode = pView->GetMapMode();
1674
0
    Point aOrigin( rMapMode.GetOrigin() );
1675
    // convert to document coordinate
1676
0
    aOrigin *= -1;
1677
0
    aOrigin.AdjustY(nDeltaY );
1678
0
    aOrigin.AdjustX(nDeltaX );
1679
0
    tools::Rectangle aRect( aOrigin, aOutputSize );
1680
0
    MakeVisible( aRect, true/*bScrollBar*/ );
1681
0
}
1682
1683
1684
const Size& SvxIconChoiceCtrl_Impl::GetItemSize( IcnViewFieldType eItem ) const
1685
0
{
1686
0
    if (eItem == IcnViewFieldType::Text)
1687
0
        return aDefaultTextSize;
1688
0
    return aImageSize; // IcnViewFieldType::Image
1689
0
}
1690
1691
tools::Rectangle SvxIconChoiceCtrl_Impl::CalcFocusRect( SvxIconChoiceCtrlEntry* pEntry )
1692
0
{
1693
0
    tools::Rectangle aBoundRect( GetEntryBoundRect( pEntry ) );
1694
1695
    // Remove left margin
1696
0
    if (nWinBits & WB_DETAILS)
1697
0
        aBoundRect.SetPos(Point(0, aBoundRect.GetPos().Y()));
1698
1699
0
    return aBoundRect;
1700
0
}
1701
1702
IMPL_LINK_NOARG(SvxIconChoiceCtrl_Impl, VisRectChangedHdl, Timer *, void)
1703
0
{
1704
0
    aVisRectChangedIdle.Stop();
1705
0
}
1706
1707
IMPL_LINK_NOARG(SvxIconChoiceCtrl_Impl, DocRectChangedHdl, Timer *, void)
1708
0
{
1709
0
    aDocRectChangedIdle.Stop();
1710
0
}
1711
1712
void SvxIconChoiceCtrl_Impl::DrawFocusRect(vcl::RenderContext& rRenderContext,  SvxIconChoiceCtrlEntry* pEntry)
1713
0
{
1714
0
    const StyleSettings& rSettings = rRenderContext.GetSettings().GetStyleSettings();
1715
0
    tools::Rectangle aRect(CalcFocusRect(pEntry));
1716
0
    rRenderContext.SetFillColor(rSettings.GetMenuHighlightColor());
1717
0
    rRenderContext.SetTextColor(rSettings.GetMenuHighlightTextColor());
1718
0
    rRenderContext.DrawRect(aRect);
1719
0
}
1720
1721
bool SvxIconChoiceCtrl_Impl::IsMnemonicChar( sal_Unicode cChar, sal_uLong& rPos ) const
1722
0
{
1723
0
    bool bRet = false;
1724
0
    const vcl::I18nHelper& rI18nHelper = Application::GetSettings().GetUILocaleI18nHelper();
1725
0
    size_t nEntryCount = GetEntryCount();
1726
0
    for ( size_t i = 0; i < nEntryCount; ++i )
1727
0
    {
1728
0
        if ( rI18nHelper.MatchMnemonic( GetEntry( i )->GetText(), cChar ) )
1729
0
        {
1730
0
            bRet = true;
1731
0
            rPos = i;
1732
0
            break;
1733
0
        }
1734
0
    }
1735
1736
0
    return bRet;
1737
0
}
1738
1739
1740
IMPL_LINK(SvxIconChoiceCtrl_Impl, UserEventHdl, void*, nId, void )
1741
0
{
1742
0
    if( nId == EVENTID_ADJUST_SCROLLBARS )
1743
0
    {
1744
0
        nUserEventAdjustScrBars = nullptr;
1745
0
        AdjustScrollBars();
1746
0
    }
1747
0
}
1748
1749
void SvxIconChoiceCtrl_Impl::CancelUserEvents()
1750
0
{
1751
0
    if( nUserEventAdjustScrBars )
1752
0
    {
1753
0
        Application::RemoveUserEvent( nUserEventAdjustScrBars );
1754
0
        nUserEventAdjustScrBars = nullptr;
1755
0
    }
1756
0
}
1757
1758
SvxIconChoiceCtrlEntry* SvxIconChoiceCtrl_Impl::GetFirstSelectedEntry() const
1759
0
{
1760
0
    size_t nCount = maEntries.size();
1761
0
    for( size_t nCur = 0; nCur < nCount; nCur++ )
1762
0
    {
1763
0
        SvxIconChoiceCtrlEntry* pEntry = maEntries[ nCur ].get();
1764
0
        if( pEntry->IsSelected() )
1765
0
        {
1766
0
            return pEntry;
1767
0
        }
1768
0
    }
1769
0
    return nullptr;
1770
0
}
1771
1772
sal_Int32 SvxIconChoiceCtrl_Impl::GetEntryListPos( SvxIconChoiceCtrlEntry const * pEntry ) const
1773
0
{
1774
0
    auto it = std::find_if(maEntries.begin(), maEntries.end(),
1775
0
                           [pEntry](auto& rIt) { return rIt.get() == pEntry; });
1776
0
    if (it != maEntries.end())
1777
0
        return std::distance(maEntries.begin(), it);
1778
1779
0
    return -1;
1780
0
}
1781
1782
void SvxIconChoiceCtrl_Impl::InitSettings()
1783
0
{
1784
0
    const StyleSettings& rStyleSettings = pView->GetSettings().GetStyleSettings();
1785
1786
    // unit (from settings) is Point
1787
0
    vcl::Font aFont( rStyleSettings.GetFieldFont() );
1788
0
    aFont.SetColor( rStyleSettings.GetWindowTextColor() );
1789
0
    pView->SetPointFont( aFont );
1790
0
    SetDefaultTextSize();
1791
1792
0
    pView->SetTextColor( rStyleSettings.GetFieldTextColor() );
1793
0
    pView->SetTextFillColor();
1794
1795
0
    pView->SetBackground( rStyleSettings.GetFieldColor());
1796
1797
0
    tools::Long nScrBarSize = rStyleSettings.GetScrollBarSize();
1798
0
    if( nScrBarSize == nHorSBarHeight && nScrBarSize == nVerSBarWidth )
1799
0
        return;
1800
1801
0
    nHorSBarHeight = nScrBarSize;
1802
0
    Size aSize( aHorSBar->GetSizePixel() );
1803
0
    aSize.setHeight( nScrBarSize );
1804
0
    aHorSBar->Hide();
1805
0
    aHorSBar->SetSizePixel( aSize );
1806
1807
0
    nVerSBarWidth = nScrBarSize;
1808
0
    aSize = aVerSBar->GetSizePixel();
1809
0
    aSize.setWidth( nScrBarSize );
1810
0
    aVerSBar->Hide();
1811
0
    aVerSBar->SetSizePixel( aSize );
1812
1813
0
    Size aOSize(pView->GetOutputSizePixel());
1814
0
    PositionScrollBars( aOSize.Width(), aOSize.Height() );
1815
0
    AdjustScrollBars();
1816
0
}
1817
1818
bool SvxIconChoiceCtrl_Impl::RequestHelp( const HelpEvent& rHEvt )
1819
0
{
1820
0
    if ( !(rHEvt.GetMode() & HelpEventMode::QUICK ) )
1821
0
        return false;
1822
1823
0
    Point aPos( pView->ScreenToOutputPixel(rHEvt.GetMousePosPixel() ) );
1824
0
    aPos -= pView->GetMapMode().GetOrigin();
1825
0
    SvxIconChoiceCtrlEntry* pEntry = GetEntry( aPos );
1826
1827
0
    if ( !pEntry )
1828
0
        return false;
1829
1830
0
    OUString sQuickHelpText = pEntry->GetQuickHelpText();
1831
0
    tools::Rectangle aTextRect(CalcTextRect(pEntry, nullptr));
1832
0
    const OUString aEntryText = pEntry->GetText();
1833
0
    if ( ( !aTextRect.Contains( aPos ) || aEntryText.isEmpty() ) && sQuickHelpText.isEmpty() )
1834
0
        return false;
1835
1836
0
    tools::Rectangle aOptTextRect( aTextRect );
1837
0
    aOptTextRect.SetBottom( LONG_MAX );
1838
0
    DrawTextFlags nNewFlags = nCurTextDrawFlags;
1839
0
    nNewFlags &= ~DrawTextFlags( DrawTextFlags::Clip | DrawTextFlags::EndEllipsis );
1840
0
    aOptTextRect = pView->GetTextRect( aOptTextRect, aEntryText, nNewFlags );
1841
0
    if ( aOptTextRect != aTextRect || !sQuickHelpText.isEmpty() )
1842
0
    {
1843
        //aTextRect.Right() = aTextRect.Left() + aRealSize.Width() + 4;
1844
0
        Point aPt( aOptTextRect.TopLeft() );
1845
0
        aPt += pView->GetMapMode().GetOrigin();
1846
0
        aPt = pView->OutputToScreenPixel( aPt );
1847
        // subtract border of tooltip help
1848
0
        aPt.AdjustY( -1 );
1849
0
        aPt.AdjustX( -3 );
1850
0
        aOptTextRect.SetPos( aPt );
1851
0
        OUString sHelpText;
1852
0
        if ( !sQuickHelpText.isEmpty() )
1853
0
            sHelpText = sQuickHelpText;
1854
0
        else
1855
0
            sHelpText = aEntryText;
1856
0
        Help::ShowQuickHelp(pView, aOptTextRect, sHelpText, QuickHelpFlags::Left | QuickHelpFlags::VCenter);
1857
0
    }
1858
1859
0
    return true;
1860
0
}
1861
1862
void SvxIconChoiceCtrl_Impl::DrawHighlightFrame(vcl::RenderContext& rRenderContext,
1863
                                                const tools::Rectangle& rRect)
1864
0
{
1865
0
    const StyleSettings& rStyles = rRenderContext.GetSettings().GetStyleSettings();
1866
0
    Color aCol(rStyles.GetHighlightColor());
1867
0
    aCol.Merge(rStyles.GetFieldColor(), 50);
1868
0
    rRenderContext.SetFillColor(aCol);
1869
0
    rRenderContext.DrawRect(rRect);
1870
1871
0
    DecorationView aDecoView(&rRenderContext);
1872
0
    aDecoView.DrawHighlightFrame(rRect);
1873
0
}
1874
1875
void SvxIconChoiceCtrl_Impl::SetEntryHighlightFrame(SvxIconChoiceCtrlEntry* pEntry)
1876
0
{
1877
0
    if( pEntry == pCurHighlightFrame )
1878
0
        return;
1879
1880
0
    if (pCurHighlightFrame)
1881
0
    {
1882
0
        tools::Rectangle aInvalidationRect(GetEntryBoundRect(pCurHighlightFrame));
1883
0
        aInvalidationRect.expand(5);
1884
0
        pCurHighlightFrame = nullptr;
1885
0
        pView->Invalidate(aInvalidationRect);
1886
0
    }
1887
1888
0
    pCurHighlightFrame = pEntry;
1889
0
    if (pEntry)
1890
0
    {
1891
0
        tools::Rectangle aInvalidationRect(GetEntryBoundRect(pEntry));
1892
0
        aInvalidationRect.expand(5);
1893
0
        pView->Invalidate(aInvalidationRect);
1894
0
    }
1895
0
}
1896
1897
void SvxIconChoiceCtrl_Impl::SetOrigin( const Point& rPos )
1898
0
{
1899
0
    MapMode aMapMode( pView->GetMapMode() );
1900
0
    aMapMode.SetOrigin( rPos );
1901
0
    pView->SetMapMode( aMapMode );
1902
0
}
1903
1904
void SvxIconChoiceCtrl_Impl::CallEventListeners( VclEventId nEvent, void* pData )
1905
0
{
1906
0
    pView->CallImplEventListeners( nEvent, pData );
1907
0
}
1908
1909
1910
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */