Coverage Report

Created: 2026-07-10 11:04

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/vcl/source/control/combobox.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 <vcl/toolkit/combobox.hxx>
21
22
#include <set>
23
24
#include <comphelper/string.hxx>
25
26
#include <vcl/builder.hxx>
27
#include <vcl/commandevent.hxx>
28
#include <vcl/event.hxx>
29
#include <vcl/rendercontext/SystemTextColorFlags.hxx>
30
#include <vcl/salnativewidgets.hxx>
31
#include <vcl/uitest/uiobject.hxx>
32
#include <vcl/vclevent.hxx>
33
34
#include <accessibility/vclxaccessibledropdowncombobox.hxx>
35
#include <accessibility/vclxaccessiblecombobox.hxx>
36
#include <listbox.hxx>
37
#include <comphelper/lok.hxx>
38
#include <tools/json_writer.hxx>
39
#include <o3tl/string_view.hxx>
40
41
struct ComboBoxBounds
42
{
43
    Point aSubEditPos;
44
    Size aSubEditSize;
45
46
    Point aButtonPos;
47
    Size aButtonSize;
48
};
49
50
static void lcl_GetSelectedEntries( ::std::set< sal_Int32 >& rSelectedPos, std::u16string_view rText, sal_Unicode cTokenSep, const ImplEntryList& rEntryList )
51
0
{
52
0
    if (rText.empty())
53
0
        return;
54
55
0
    sal_Int32 nIdx{0};
56
0
    do {
57
0
        const sal_Int32 nPos = rEntryList.FindEntry(comphelper::string::strip(o3tl::getToken(rText, 0, cTokenSep, nIdx), ' '));
58
0
        if ( nPos != LISTBOX_ENTRY_NOTFOUND )
59
0
            rSelectedPos.insert( nPos );
60
0
    } while (nIdx>=0);
61
0
}
62
63
ComboBox::ComboBox(vcl::Window *const pParent, WinBits const nStyle)
64
0
    : Edit( WindowType::COMBOBOX )
65
0
    , m_nDDHeight(0)
66
0
    , m_cMultiSep(0)
67
0
    , m_isDDAutoSize(false)
68
0
    , m_isSyntheticModify(false)
69
0
    , m_isKeyBoardModify(false)
70
0
    , m_isMatchCase(false)
71
0
    , m_nMaxWidthChars(0)
72
0
    , m_nWidthInChars(-1)
73
0
{
74
0
    ImplInitComboBoxData();
75
0
    ImplInit( pParent, nStyle );
76
0
    SetWidthInChars(-1);
77
0
}
Unexecuted instantiation: ComboBox::ComboBox(vcl::Window*, long)
Unexecuted instantiation: ComboBox::ComboBox(vcl::Window*, long)
78
79
ComboBox::~ComboBox()
80
0
{
81
0
    disposeOnce();
82
0
}
83
84
void ComboBox::dispose()
85
0
{
86
0
    m_pSubEdit.disposeAndClear();
87
88
0
    VclPtr< ImplListBox > pImplLB = m_pImplLB;
89
0
    m_pImplLB.reset();
90
0
    pImplLB.disposeAndClear();
91
92
0
    m_pFloatWin.disposeAndClear();
93
0
    m_pBtn.disposeAndClear();
94
0
    Edit::dispose();
95
0
}
96
97
void ComboBox::ImplInitComboBoxData()
98
0
{
99
0
    m_pSubEdit.disposeAndClear();
100
0
    m_pBtn              = nullptr;
101
0
    m_pImplLB           = nullptr;
102
0
    m_pFloatWin         = nullptr;
103
104
0
    m_nDDHeight         = 0;
105
0
    m_isDDAutoSize      = true;
106
0
    m_isSyntheticModify = false;
107
0
    m_isKeyBoardModify  = false;
108
0
    m_isMatchCase       = false;
109
0
    m_cMultiSep         = ';';
110
0
    m_nMaxWidthChars    = -1;
111
0
    m_nWidthInChars     = -1;
112
0
}
113
114
void ComboBox::ImplCalcEditHeight()
115
0
{
116
0
    sal_Int32 nLeft, nTop, nRight, nBottom;
117
0
    GetBorder( nLeft, nTop, nRight, nBottom );
118
0
    m_nDDHeight = static_cast<sal_uInt16>(m_pSubEdit->GetTextHeight() + nTop + nBottom + 4);
119
0
    if ( !IsDropDownBox() )
120
0
        m_nDDHeight += 4;
121
122
0
    const tools::Rectangle aCtrlRegion(Point(0, 0), Size(10, 10));
123
0
    tools::Rectangle aBoundRegion;
124
0
    tools::Rectangle aContentRegion;
125
0
    ImplControlValue aControlValue;
126
0
    const ControlType eType = IsDropDownBox() ? ControlType::Combobox : ControlType::Editbox;
127
128
0
    if( GetNativeControlRegion( eType, ControlPart::Entire,
129
0
                                aCtrlRegion,
130
0
                                ControlState::ENABLED,
131
0
                                aControlValue,
132
0
                                aBoundRegion, aContentRegion ) )
133
0
    {
134
0
        const tools::Long nNCHeight = aBoundRegion.GetHeight();
135
0
        if (m_nDDHeight < nNCHeight)
136
0
            m_nDDHeight = sal::static_int_cast<sal_uInt16>(nNCHeight);
137
0
    }
138
0
}
139
140
void ComboBox::ImplInit(vcl::Window* pParent, WinBits eStyle)
141
0
{
142
0
    const bool bNoBorder = (eStyle & WB_NOBORDER) != 0;
143
144
0
    if (!(eStyle & WB_DROPDOWN))
145
0
    {
146
0
        eStyle &= ~WB_BORDER;
147
0
        eStyle |= WB_NOBORDER;
148
0
    }
149
0
    else
150
0
    {
151
0
        if ( !bNoBorder )
152
0
            eStyle |= WB_BORDER;
153
0
    }
154
155
0
    Edit::ImplInit(pParent, eStyle);
156
0
    SetBackground();
157
158
    // DropDown ?
159
0
    WinBits eEditStyle = eStyle & (WB_LEFT | WB_RIGHT | WB_CENTER);
160
0
    WinBits eListStyle = eStyle;
161
0
    if (eStyle & WB_DROPDOWN)
162
0
    {
163
0
        m_pFloatWin = VclPtr<ImplListBoxFloatingWindow>::Create( this );
164
0
        if (!IsNativeControlSupported(ControlType::Pushbutton, ControlPart::Focus))
165
0
            m_pFloatWin->RequestDoubleBuffering(true);
166
0
        m_pFloatWin->SetAutoWidth( true );
167
0
        m_pFloatWin->SetPopupModeEndHdl(LINK(this, ComboBox, ImplPopupModeEndHdl));
168
169
0
        m_pBtn = VclPtr<ImplBtn>::Create( this, WB_NOLIGHTBORDER | WB_RECTSTYLE );
170
0
        ImplInitDropDownButton( m_pBtn );
171
0
        m_pBtn->SetMBDownHdl(LINK(this, ComboBox, ImplClickBtnHdl));
172
0
        m_pBtn->Show();
173
174
0
        eEditStyle |= WB_NOBORDER;
175
0
        eListStyle &= ~WB_BORDER;
176
0
        eListStyle |= WB_NOBORDER;
177
0
    }
178
0
    else
179
0
    {
180
0
        if ( !bNoBorder )
181
0
        {
182
0
            eEditStyle |= WB_BORDER;
183
0
            eListStyle &= ~WB_NOBORDER;
184
0
            eListStyle |= WB_BORDER;
185
0
        }
186
0
    }
187
188
0
    m_pSubEdit.reset(VclPtr<Edit>::Create(this, eEditStyle));
189
0
    m_pSubEdit->EnableRTL( false );
190
0
    SetSubEdit( m_pSubEdit );
191
0
    m_pSubEdit->SetPosPixel( Point() );
192
0
    EnableAutocomplete( true );
193
0
    m_pSubEdit->Show();
194
195
0
    vcl::Window* pLBParent = this;
196
0
    if (m_pFloatWin)
197
0
        pLBParent = m_pFloatWin;
198
0
    m_pImplLB = VclPtr<ImplListBox>::Create(pLBParent, eListStyle | WB_SIMPLEMODE | WB_AUTOHSCROLL);
199
0
    m_pImplLB->SetPosPixel( Point() );
200
0
    m_pImplLB->SetSelectHdl(LINK(this, ComboBox, ImplSelectHdl));
201
0
    m_pImplLB->SetCancelHdl( LINK(this, ComboBox, ImplCancelHdl));
202
0
    m_pImplLB->SetDoubleClickHdl(LINK(this, ComboBox, ImplDoubleClickHdl));
203
0
    m_pImplLB->SetSelectionChangedHdl(LINK(this, ComboBox, ImplSelectionChangedHdl));
204
0
    m_pImplLB->SetListItemSelectHdl(LINK(this, ComboBox, ImplListItemSelectHdl));
205
0
    m_pImplLB->Show();
206
207
0
    if (m_pFloatWin)
208
0
        m_pFloatWin->SetImplListBox(m_pImplLB);
209
0
    else
210
0
        GetMainWindow()->AllowGrabFocus( true );
211
212
0
    ImplCalcEditHeight();
213
214
0
    SetCompoundControl( true );
215
0
}
216
217
WinBits ComboBox::ImplInitStyle(WinBits eStyle)
218
0
{
219
0
    if (!(eStyle & WB_NOTABSTOP))
220
0
        eStyle |= WB_TABSTOP;
221
222
0
    if (!(eStyle & WB_NOGROUP))
223
0
        eStyle |= WB_GROUP;
224
225
0
    return eStyle;
226
0
}
227
228
void ComboBox::EnableAutocomplete( bool bEnable, bool bMatchCase )
229
0
{
230
0
    m_isMatchCase = bMatchCase;
231
232
0
    if ( bEnable )
233
0
        m_pSubEdit->SetAutocompleteHdl(LINK(this, ComboBox, ImplAutocompleteHdl));
234
0
    else
235
0
        m_pSubEdit->SetAutocompleteHdl( Link<Edit&,void>() );
236
0
}
237
238
bool ComboBox::IsAutocompleteEnabled() const
239
0
{
240
0
    return m_pSubEdit->GetAutocompleteHdl().IsSet();
241
0
}
242
243
IMPL_LINK_NOARG(ComboBox, ImplClickBtnHdl, void*, void)
244
0
{
245
0
    CallEventListeners( VclEventId::DropdownPreOpen );
246
0
    m_pSubEdit->GrabFocus();
247
0
    if (!m_pImplLB->GetEntryList().GetMRUCount())
248
0
        ImplUpdateFloatSelection();
249
0
    else
250
0
        m_pImplLB->SelectEntry( 0 , true );
251
0
    m_pBtn->SetPressed( true );
252
0
    SetSelection( Selection( 0, SELECTION_MAX ) );
253
0
    m_pFloatWin->StartFloat( true );
254
0
    CallEventListeners( VclEventId::DropdownOpen );
255
256
0
    ImplClearLayoutData();
257
0
    if (m_pImplLB)
258
0
        m_pImplLB->GetMainWindow()->ImplClearLayoutData();
259
0
}
260
261
IMPL_LINK_NOARG(ComboBox, ImplPopupModeEndHdl, FloatingWindow*, void)
262
0
{
263
0
    if (m_pFloatWin->IsPopupModeCanceled())
264
0
    {
265
0
        if (!m_pImplLB->GetEntryList().IsEntryPosSelected(
266
0
                    m_pFloatWin->GetPopupModeStartSaveSelection()))
267
0
        {
268
0
            m_pImplLB->SelectEntry(m_pFloatWin->GetPopupModeStartSaveSelection(), true);
269
0
            const bool bTravelSelect = m_pImplLB->IsTravelSelect();
270
0
            m_pImplLB->SetTravelSelect( true );
271
0
            Select();
272
0
            m_pImplLB->SetTravelSelect( bTravelSelect );
273
0
        }
274
0
    }
275
276
0
    ImplClearLayoutData();
277
0
    if (m_pImplLB)
278
0
        m_pImplLB->GetMainWindow()->ImplClearLayoutData();
279
280
0
    m_pBtn->SetPressed( false );
281
0
    CallEventListeners( VclEventId::DropdownClose );
282
0
}
283
284
IMPL_LINK(ComboBox, ImplAutocompleteHdl, Edit&, rEdit, void)
285
0
{
286
0
    const Selection aSel = rEdit.GetSelection();
287
288
0
    {
289
0
        const OUString aFullText = rEdit.GetText();
290
0
        const OUString aStartText = aFullText.copy( 0, static_cast<sal_Int32>(aSel.Max()) );
291
0
        sal_Int32 nStart = m_pImplLB->GetCurrentPos();
292
293
0
        if ( nStart == LISTBOX_ENTRY_NOTFOUND )
294
0
            nStart = 0;
295
296
0
        sal_Int32 nPos = LISTBOX_ENTRY_NOTFOUND;
297
0
        if (!m_isMatchCase)
298
0
        {
299
            // Try match case insensitive from current position
300
0
            nPos = m_pImplLB->GetEntryList().FindMatchingEntry(aStartText, nStart, true);
301
0
            if ( nPos == LISTBOX_ENTRY_NOTFOUND )
302
                // Try match case insensitive, but from start
303
0
                nPos = m_pImplLB->GetEntryList().FindMatchingEntry(aStartText, 0, true);
304
0
        }
305
306
0
        if ( nPos == LISTBOX_ENTRY_NOTFOUND )
307
            // Try match full from current position
308
0
            nPos = m_pImplLB->GetEntryList().FindMatchingEntry(aStartText, nStart, false);
309
0
        if ( nPos == LISTBOX_ENTRY_NOTFOUND )
310
            //  Match full, but from start
311
0
            nPos = m_pImplLB->GetEntryList().FindMatchingEntry(aStartText, 0, false);
312
313
0
        if ( nPos != LISTBOX_ENTRY_NOTFOUND )
314
0
        {
315
0
            const OUString aText = m_pImplLB->GetEntryList().GetEntryText( nPos );
316
0
            Selection aSelection( aText.getLength(), aStartText.getLength() );
317
0
            rEdit.SetText( aText, aSelection );
318
0
        }
319
0
    }
320
0
}
321
322
IMPL_LINK_NOARG(ComboBox, ImplSelectHdl, LinkParamNone*, void)
323
0
{
324
0
    const bool bPopup = IsInDropDown();
325
0
    bool bCallSelect = false;
326
0
    if (m_pImplLB->IsSelectionChanged() || bPopup)
327
0
    {
328
0
        OUString aText;
329
0
        if (IsMultiSelectionEnabled())
330
0
        {
331
0
            aText = m_pSubEdit->GetText();
332
333
            // remove all entries to which there is an entry, but which is not selected
334
0
            sal_Int32 nIndex = 0;
335
0
            while ( nIndex >= 0 )
336
0
            {
337
0
                const sal_Int32 nPrevIndex = nIndex;
338
0
                std::u16string_view aToken = o3tl::getToken(aText, 0, m_cMultiSep, nIndex );
339
0
                const sal_Int32 nTokenLen = aToken.size();
340
0
                aToken = comphelper::string::strip(aToken, ' ');
341
0
                const sal_Int32 nP = m_pImplLB->GetEntryList().FindEntry( aToken );
342
0
                if ((nP != LISTBOX_ENTRY_NOTFOUND) && (!m_pImplLB->GetEntryList().IsEntryPosSelected(nP)))
343
0
                {
344
0
                    aText = aText.replaceAt( nPrevIndex, nTokenLen, u"" );
345
0
                    nIndex = nIndex - nTokenLen;
346
0
                    sal_Int32 nSepCount=0;
347
0
                    if ((nPrevIndex+nSepCount < aText.getLength()) && (aText[nPrevIndex+nSepCount] == m_cMultiSep))
348
0
                    {
349
0
                        nIndex--;
350
0
                        ++nSepCount;
351
0
                    }
352
0
                    aText = aText.replaceAt( nPrevIndex, nSepCount, u"" );
353
0
                }
354
0
                aText = comphelper::string::strip(aText, ' ');
355
0
            }
356
357
            // attach missing entries
358
0
            ::std::set< sal_Int32 > aSelInText;
359
0
            lcl_GetSelectedEntries( aSelInText, aText, m_cMultiSep, m_pImplLB->GetEntryList() );
360
0
            const sal_Int32 nSelectedEntries = m_pImplLB->GetEntryList().GetSelectedEntryCount();
361
0
            for ( sal_Int32 n = 0; n < nSelectedEntries; n++ )
362
0
            {
363
0
                sal_Int32 nP = m_pImplLB->GetEntryList().GetSelectedEntryPos( n );
364
0
                if ( !aSelInText.count( nP ) )
365
0
                {
366
0
                    if (!aText.isEmpty() && (aText[aText.getLength()-1] != m_cMultiSep))
367
0
                        aText += OUStringChar(m_cMultiSep);
368
0
                    if ( !aText.isEmpty() )
369
0
                        aText += " ";   // slightly loosen
370
0
                    aText += m_pImplLB->GetEntryList().GetEntryText( nP ) +
371
0
                        OUStringChar(m_cMultiSep);
372
0
                }
373
0
            }
374
0
            aText = comphelper::string::stripEnd( aText, m_cMultiSep );
375
0
        }
376
0
        else
377
0
        {
378
0
            aText = m_pImplLB->GetEntryList().GetSelectedEntry( 0 );
379
0
        }
380
381
0
        m_pSubEdit->SetText( aText );
382
383
0
        switch (GetSettings().GetStyleSettings().GetComboBoxTextSelectionMode())
384
0
        {
385
0
            case ComboBoxTextSelectionMode::SelectText:
386
0
            {
387
0
                Selection aNewSelection(0, aText.getLength());
388
0
                if (IsMultiSelectionEnabled())
389
0
                    aNewSelection.Min() = aText.getLength();
390
0
                m_pSubEdit->SetSelection(aNewSelection);
391
0
                break;
392
0
            }
393
0
            case ComboBoxTextSelectionMode::CursorToStart:
394
0
            {
395
0
                Selection aNewSelection(0, 0);
396
0
                m_pSubEdit->SetSelection(aNewSelection);
397
0
                break;
398
0
            }
399
0
            case ComboBoxTextSelectionMode::CursorToEnd:
400
0
            {
401
0
                m_pSubEdit->SetCursorAtLast();
402
0
                break;
403
0
            }
404
0
            default:
405
0
                assert(false && "Unhandled ComboBoxTextSelectionMode case");
406
0
                break;
407
0
        }
408
409
0
        bCallSelect = true;
410
0
    }
411
412
    // #84652# Call GrabFocus and EndPopupMode before calling Select/Modify, but after changing the text
413
0
    bool bMenuSelect = bPopup && !m_pImplLB->IsTravelSelect() && (!IsMultiSelectionEnabled() || !m_pImplLB->GetSelectModifier());
414
0
    if (bMenuSelect)
415
0
    {
416
0
        m_pFloatWin->EndPopupMode();
417
0
        GrabFocus();
418
0
    }
419
420
0
    if ( bCallSelect )
421
0
    {
422
0
        m_isKeyBoardModify = !bMenuSelect;
423
0
        m_pSubEdit->SetModifyFlag();
424
0
        m_isSyntheticModify = true;
425
0
        Modify();
426
0
        m_isSyntheticModify = false;
427
0
        Select();
428
0
        m_isKeyBoardModify = false;
429
0
    }
430
0
}
431
432
bool ComboBox::IsSyntheticModify() const
433
0
{
434
0
    return m_isSyntheticModify;
435
0
}
436
437
bool ComboBox::IsModifyByKeyboard() const
438
0
{
439
0
    return m_isKeyBoardModify;
440
0
}
441
442
IMPL_LINK_NOARG(ComboBox, ImplListItemSelectHdl, LinkParamNone*, void)
443
0
{
444
0
    CallEventListeners(VclEventId::DropdownSelect);
445
0
}
446
447
IMPL_LINK_NOARG(ComboBox, ImplCancelHdl, LinkParamNone*, void)
448
0
{
449
0
    if (IsInDropDown())
450
0
        m_pFloatWin->EndPopupMode();
451
0
}
452
453
IMPL_LINK( ComboBox, ImplSelectionChangedHdl, sal_Int32, nChanged, void)
454
0
{
455
0
    if (!m_pImplLB->IsTrackingSelect())
456
0
    {
457
0
        if (!m_pSubEdit->IsReadOnly() && m_pImplLB->GetEntryList().IsEntryPosSelected(nChanged))
458
0
            m_pSubEdit->SetText(m_pImplLB->GetEntryList().GetEntryText(nChanged));
459
0
    }
460
0
}
461
462
IMPL_LINK_NOARG(ComboBox, ImplDoubleClickHdl, ImplListBoxWindow*, void)
463
0
{
464
0
    DoubleClick();
465
0
}
466
467
void ComboBox::ToggleDropDown()
468
0
{
469
0
    if( !IsDropDownBox() )
470
0
        return;
471
472
0
    if (m_pFloatWin->IsInPopupMode())
473
0
        m_pFloatWin->EndPopupMode();
474
0
    else
475
0
    {
476
0
        m_pSubEdit->GrabFocus();
477
0
        if (!m_pImplLB->GetEntryList().GetMRUCount())
478
0
            ImplUpdateFloatSelection();
479
0
        else
480
0
            m_pImplLB->SelectEntry( 0 , true );
481
0
        CallEventListeners( VclEventId::DropdownPreOpen );
482
0
        m_pBtn->SetPressed( true );
483
0
        SetSelection( Selection( 0, SELECTION_MAX ) );
484
0
        m_pFloatWin->StartFloat(true);
485
0
        CallEventListeners( VclEventId::DropdownOpen );
486
0
    }
487
0
}
488
489
void ComboBox::Select()
490
0
{
491
0
    ImplCallEventListenersAndHandler( VclEventId::ComboboxSelect, [this] () { m_SelectHdl.Call(*this); } );
492
0
}
493
494
void ComboBox::DoubleClick()
495
0
{
496
0
    ImplCallEventListenersAndHandler( VclEventId::ComboboxDoubleClick, [] () {} );
497
0
}
498
499
0
bool ComboBox::IsAutoSizeEnabled() const { return m_isDDAutoSize; }
500
501
void ComboBox::EnableAutoSize( bool bAuto )
502
0
{
503
0
    m_isDDAutoSize = bAuto;
504
0
    if (m_pFloatWin)
505
0
    {
506
0
        if (bAuto && !m_pFloatWin->GetDropDownLineCount())
507
0
        {
508
            // Adapt to GetListBoxMaximumLineCount here; was on fixed number of five before
509
0
            AdaptDropDownLineCountToMaximum();
510
0
        }
511
0
        else if ( !bAuto )
512
0
        {
513
0
            m_pFloatWin->SetDropDownLineCount(0);
514
0
        }
515
0
    }
516
0
}
517
518
void ComboBox::SetDropDownLineCount( sal_uInt16 nLines )
519
0
{
520
0
    if (m_pFloatWin)
521
0
        m_pFloatWin->SetDropDownLineCount(nLines);
522
0
}
523
524
void ComboBox::AdaptDropDownLineCountToMaximum()
525
0
{
526
    // Adapt to maximum allowed number.
527
    // Limit for LOK as we can't render outside of the dialog canvas.
528
0
    if (comphelper::LibreOfficeKit::isActive())
529
0
        SetDropDownLineCount(11);
530
0
    else
531
0
        SetDropDownLineCount(GetSettings().GetStyleSettings().GetListBoxMaximumLineCount());
532
0
}
533
534
sal_uInt16 ComboBox::GetDropDownLineCount() const
535
0
{
536
0
    sal_uInt16 nLines = 0;
537
0
    if (m_pFloatWin)
538
0
        nLines = m_pFloatWin->GetDropDownLineCount();
539
0
    return nLines;
540
0
}
541
542
void ComboBox::setPosSizePixel(tools::Long nX, tools::Long nY, tools::Long nWidth, tools::Long nHeight,
543
                               PosSizeFlags eFlags)
544
0
{
545
0
    if (IsDropDownBox() && (eFlags & PosSizeFlags::Size))
546
0
    {
547
0
        Size aPrefSz = m_pFloatWin->GetPrefSize();
548
0
        if ((eFlags & PosSizeFlags::Height) && (nHeight >= 2*m_nDDHeight))
549
0
            aPrefSz.setHeight( nHeight-m_nDDHeight );
550
0
        if (eFlags & PosSizeFlags::Width)
551
0
            aPrefSz.setWidth( nWidth );
552
0
        m_pFloatWin->SetPrefSize(aPrefSz);
553
554
0
        if (IsAutoSizeEnabled())
555
0
            nHeight = m_nDDHeight;
556
0
    }
557
558
0
    Edit::setPosSizePixel(nX, nY, nWidth, nHeight, eFlags);
559
0
}
560
561
void ComboBox::Resize()
562
0
{
563
0
    Control::Resize();
564
565
0
    if (m_pSubEdit)
566
0
    {
567
0
        Size aOutSz = GetOutputSizePixel();
568
0
        if( IsDropDownBox() )
569
0
        {
570
0
            ComboBoxBounds aBounds(calcComboBoxDropDownComponentBounds(aOutSz,
571
0
                GetWindow(GetWindowType::Border)->GetOutputSizePixel()));
572
0
            m_pSubEdit->SetPosSizePixel(aBounds.aSubEditPos, aBounds.aSubEditSize);
573
0
            m_pBtn->SetPosSizePixel(aBounds.aButtonPos, aBounds.aButtonSize);
574
0
        }
575
0
        else
576
0
        {
577
0
            m_pSubEdit->SetSizePixel(Size(aOutSz.Width(), m_nDDHeight));
578
0
            m_pImplLB->setPosSizePixel(0, m_nDDHeight,
579
0
                    aOutSz.Width(), aOutSz.Height() - m_nDDHeight);
580
0
            if ( !GetText().isEmpty() )
581
0
                ImplUpdateFloatSelection();
582
0
        }
583
0
    }
584
585
    // adjust the size of the FloatingWindow even when invisible
586
    // as KEY_PGUP/DOWN is being processed...
587
0
    if (m_pFloatWin)
588
0
        m_pFloatWin->SetSizePixel(m_pFloatWin->CalcFloatSize(m_pFloatWin->GetParentRect()));
589
0
}
590
591
0
bool ComboBox::IsDropDownBox() const { return m_pFloatWin != nullptr; }
592
593
void ComboBox::FillLayoutData() const
594
0
{
595
0
    mxLayoutData.emplace();
596
0
    AppendLayoutData( *m_pSubEdit );
597
0
    m_pSubEdit->SetLayoutDataParent( this );
598
0
    ImplListBoxWindow* rMainWindow = GetMainWindow();
599
0
    if (m_pFloatWin)
600
0
    {
601
        // dropdown mode
602
0
        if (m_pFloatWin->IsReallyVisible())
603
0
        {
604
0
            AppendLayoutData( *rMainWindow );
605
0
            rMainWindow->SetLayoutDataParent( this );
606
0
        }
607
0
    }
608
0
    else
609
0
    {
610
0
        AppendLayoutData( *rMainWindow );
611
0
        rMainWindow->SetLayoutDataParent( this );
612
0
    }
613
0
}
614
615
void ComboBox::StateChanged(StateChangedType eType)
616
0
{
617
0
    Edit::StateChanged(eType);
618
619
0
    if (eType == StateChangedType::ReadOnly)
620
0
    {
621
0
        m_pImplLB->SetReadOnly( IsReadOnly() );
622
0
        if (m_pBtn)
623
0
            m_pBtn->Enable( IsEnabled() && !IsReadOnly() );
624
0
    }
625
0
    else if (eType == StateChangedType::Enable)
626
0
    {
627
0
        m_pSubEdit->Enable( IsEnabled() );
628
0
        m_pImplLB->Enable( IsEnabled() && !IsReadOnly() );
629
0
        if (m_pBtn)
630
0
            m_pBtn->Enable( IsEnabled() && !IsReadOnly() );
631
0
        Invalidate();
632
0
    }
633
0
    else if (eType == StateChangedType::UpdateMode)
634
0
    {
635
0
        m_pImplLB->SetUpdateMode( IsUpdateMode() );
636
0
    }
637
0
    else if (eType == StateChangedType::Zoom)
638
0
    {
639
0
        m_pImplLB->SetZoom( GetZoom() );
640
0
        m_pSubEdit->SetZoom( GetZoom() );
641
0
        ImplCalcEditHeight();
642
0
        Resize();
643
0
    }
644
0
    else if (eType == StateChangedType::ControlFont)
645
0
    {
646
0
        m_pImplLB->SetControlFont( GetControlFont() );
647
0
        m_pSubEdit->SetControlFont( GetControlFont() );
648
0
        ImplCalcEditHeight();
649
0
        Resize();
650
0
    }
651
0
    else if (eType == StateChangedType::ControlForeground)
652
0
    {
653
0
        m_pImplLB->SetControlForeground( GetControlForeground() );
654
0
        m_pSubEdit->SetControlForeground( GetControlForeground() );
655
0
    }
656
0
    else if (eType == StateChangedType::ControlBackground)
657
0
    {
658
0
        m_pImplLB->SetControlBackground( GetControlBackground() );
659
0
        m_pSubEdit->SetControlBackground( GetControlBackground() );
660
0
    }
661
0
    else if (eType == StateChangedType::Style)
662
0
    {
663
0
        SetStyle( ImplInitStyle( GetStyle() ) );
664
0
        GetMainWindow()->EnableSort( ( GetStyle() & WB_SORT ) != 0 );
665
0
    }
666
0
    else if (eType == StateChangedType::Mirroring)
667
0
    {
668
0
        if (m_pBtn)
669
0
        {
670
0
            m_pBtn->EnableRTL( IsRTLEnabled() );
671
0
            ImplInitDropDownButton( m_pBtn );
672
0
        }
673
0
        m_pSubEdit->CompatStateChanged( StateChangedType::Mirroring );
674
0
        m_pImplLB->EnableRTL( IsRTLEnabled() );
675
0
        Resize();
676
0
    }
677
0
}
678
679
void ComboBox::DataChanged( const DataChangedEvent& rDCEvt )
680
0
{
681
0
    Control::DataChanged( rDCEvt );
682
683
0
    if ( !((rDCEvt.GetType() == DataChangedEventType::FONTS) ||
684
0
         (rDCEvt.GetType() == DataChangedEventType::FONTSUBSTITUTION) ||
685
0
         ((rDCEvt.GetType() == DataChangedEventType::SETTINGS) &&
686
0
          (rDCEvt.GetFlags() & AllSettingsFlags::STYLE))) )
687
0
        return;
688
689
0
    if (m_pBtn)
690
0
    {
691
0
        m_pBtn->GetOutDev()->SetSettings( GetSettings() );
692
0
        ImplInitDropDownButton( m_pBtn );
693
0
    }
694
0
    Resize();
695
0
    m_pImplLB->Resize(); // not called by ComboBox::Resize() if ImplLB is unchanged
696
697
0
    SetBackground();    // due to a hack in Window::UpdateSettings the background must be reset
698
                        // otherwise it will overpaint NWF drawn comboboxes
699
0
}
700
701
bool ComboBox::EventNotify( NotifyEvent& rNEvt )
702
0
{
703
0
    bool bDone = false;
704
0
    if ((rNEvt.GetType() == NotifyEventType::KEYINPUT)
705
0
        && (rNEvt.GetWindow() == m_pSubEdit)
706
0
        && !IsReadOnly())
707
0
    {
708
0
        KeyEvent aKeyEvt = *rNEvt.GetKeyEvent();
709
0
        const sal_uInt16 nKeyCode = aKeyEvt.GetKeyCode().GetCode();
710
0
        switch( nKeyCode )
711
0
        {
712
0
            case KEY_UP:
713
0
            case KEY_DOWN:
714
0
            case KEY_PAGEUP:
715
0
            case KEY_PAGEDOWN:
716
0
            {
717
0
                ImplUpdateFloatSelection();
718
0
                if ((nKeyCode == KEY_DOWN) && m_pFloatWin
719
0
                    && !m_pFloatWin->IsInPopupMode()
720
0
                    && aKeyEvt.GetKeyCode().IsMod2())
721
0
                {
722
0
                    CallEventListeners( VclEventId::DropdownPreOpen );
723
0
                    m_pBtn->SetPressed( true );
724
0
                    if (m_pImplLB->GetEntryList().GetMRUCount())
725
0
                        m_pImplLB->SelectEntry( 0 , true );
726
0
                    SetSelection( Selection( 0, SELECTION_MAX ) );
727
0
                    m_pFloatWin->StartFloat(false);
728
0
                    CallEventListeners( VclEventId::DropdownOpen );
729
0
                    bDone = true;
730
0
                }
731
0
                else if ((nKeyCode == KEY_UP) && m_pFloatWin
732
0
                        && m_pFloatWin->IsInPopupMode()
733
0
                        && aKeyEvt.GetKeyCode().IsMod2())
734
0
                {
735
0
                    m_pFloatWin->EndPopupMode();
736
0
                    bDone = true;
737
0
                }
738
0
                else
739
0
                {
740
0
                    bDone = m_pImplLB->ProcessKeyInput( aKeyEvt );
741
0
                }
742
0
            }
743
0
            break;
744
745
0
            case KEY_RETURN:
746
0
            {
747
0
                if ((rNEvt.GetWindow() == m_pSubEdit) && IsInDropDown())
748
0
                {
749
0
                    m_pImplLB->ProcessKeyInput( aKeyEvt );
750
0
                    bDone = true;
751
0
                }
752
0
            }
753
0
            break;
754
0
        }
755
0
    }
756
0
    else if ((rNEvt.GetType() == NotifyEventType::LOSEFOCUS) && m_pFloatWin)
757
0
    {
758
0
        if (m_pFloatWin->HasChildPathFocus())
759
0
            m_pSubEdit->GrabFocus();
760
0
        else if (m_pFloatWin->IsInPopupMode() && !HasChildPathFocus(true))
761
0
            m_pFloatWin->EndPopupMode();
762
0
    }
763
0
    else if( (rNEvt.GetType() == NotifyEventType::COMMAND) &&
764
0
             (rNEvt.GetCommandEvent()->GetCommand() == CommandEventId::Wheel) &&
765
0
             (rNEvt.GetWindow() == m_pSubEdit) )
766
0
    {
767
0
        MouseWheelBehaviour nWheelBehavior( GetSettings().GetMouseSettings().GetWheelBehavior() );
768
0
        if  (   ( nWheelBehavior == MouseWheelBehaviour::ALWAYS )
769
0
            ||  (   ( nWheelBehavior == MouseWheelBehaviour::FocusOnly )
770
0
                &&  HasChildPathFocus()
771
0
                )
772
0
            )
773
0
        {
774
0
            bDone = m_pImplLB->HandleWheelAsCursorTravel(*rNEvt.GetCommandEvent(), *this);
775
0
        }
776
0
        else
777
0
        {
778
0
            bDone = false;  // don't eat this event, let the default handling happen (i.e. scroll the context)
779
0
        }
780
0
    }
781
0
    else if ((rNEvt.GetType() == NotifyEventType::MOUSEBUTTONDOWN)
782
0
            && (rNEvt.GetWindow() == GetMainWindow()))
783
0
    {
784
0
        m_pSubEdit->GrabFocus();
785
0
    }
786
787
0
    return bDone || Edit::EventNotify( rNEvt );
788
0
}
789
790
void ComboBox::SetText( const OUString& rStr )
791
0
{
792
0
    CallEventListeners( VclEventId::ComboboxSetText );
793
794
0
    Edit::SetText( rStr );
795
0
    ImplUpdateFloatSelection();
796
0
}
797
798
void ComboBox::SetText( const OUString& rStr, const Selection& rNewSelection )
799
0
{
800
0
    CallEventListeners( VclEventId::ComboboxSetText );
801
802
0
    Edit::SetText( rStr, rNewSelection );
803
0
    ImplUpdateFloatSelection();
804
0
}
805
806
void ComboBox::Modify()
807
0
{
808
0
    if (!m_isSyntheticModify)
809
0
        ImplUpdateFloatSelection();
810
811
0
    Edit::Modify();
812
0
}
813
814
void ComboBox::ImplUpdateFloatSelection()
815
0
{
816
0
    if (!m_pImplLB || !m_pSubEdit)
817
0
        return;
818
819
    // move text in the ListBox into the visible region
820
0
    m_pImplLB->SetCallSelectionChangedHdl( false );
821
0
    if (!IsMultiSelectionEnabled())
822
0
    {
823
0
        OUString        aSearchStr( m_pSubEdit->GetText() );
824
0
        sal_Int32       nSelect = LISTBOX_ENTRY_NOTFOUND;
825
0
        bool        bSelect = true;
826
827
0
        if (m_pImplLB->GetCurrentPos() != LISTBOX_ENTRY_NOTFOUND)
828
0
        {
829
0
            OUString aCurrent = m_pImplLB->GetEntryList().GetEntryText(
830
0
                                    m_pImplLB->GetCurrentPos());
831
0
            if ( aCurrent == aSearchStr )
832
0
                nSelect = m_pImplLB->GetCurrentPos();
833
0
        }
834
835
0
        if ( nSelect == LISTBOX_ENTRY_NOTFOUND )
836
0
            nSelect = m_pImplLB->GetEntryList().FindEntry( aSearchStr );
837
0
        if ( nSelect == LISTBOX_ENTRY_NOTFOUND )
838
0
        {
839
0
            nSelect = m_pImplLB->GetEntryList().FindMatchingEntry( aSearchStr, 0, true );
840
0
            bSelect = false;
841
0
        }
842
843
0
        if( nSelect != LISTBOX_ENTRY_NOTFOUND )
844
0
        {
845
0
            if (!m_pImplLB->IsVisible(nSelect))
846
0
                m_pImplLB->ShowProminentEntry( nSelect );
847
0
            m_pImplLB->SelectEntry( nSelect, bSelect );
848
0
        }
849
0
        else
850
0
        {
851
0
            nSelect = m_pImplLB->GetEntryList().GetSelectedEntryPos( 0 );
852
0
            if( nSelect != LISTBOX_ENTRY_NOTFOUND )
853
0
                m_pImplLB->SelectEntry( nSelect, false );
854
0
            m_pImplLB->ResetCurrentPos();
855
0
        }
856
0
    }
857
0
    else
858
0
    {
859
0
        ::std::set< sal_Int32 > aSelInText;
860
0
        lcl_GetSelectedEntries(aSelInText, m_pSubEdit->GetText(), m_cMultiSep, m_pImplLB->GetEntryList());
861
0
        for (sal_Int32 n = 0; n < m_pImplLB->GetEntryList().GetEntryCount(); n++)
862
0
            m_pImplLB->SelectEntry( n, aSelInText.count( n ) != 0 );
863
0
    }
864
0
    m_pImplLB->SetCallSelectionChangedHdl( true );
865
0
}
866
867
sal_Int32 ComboBox::InsertEntry(const OUString& rStr, sal_Int32 const nPos)
868
0
{
869
0
    assert(nPos >= 0 && COMBOBOX_MAX_ENTRIES > m_pImplLB->GetEntryList().GetEntryCount());
870
871
0
    sal_Int32 nRealPos;
872
0
    if (nPos == COMBOBOX_APPEND)
873
0
        nRealPos = nPos;
874
0
    else
875
0
    {
876
0
        const sal_Int32 nMRUCount = m_pImplLB->GetEntryList().GetMRUCount();
877
0
        assert(nPos <= COMBOBOX_MAX_ENTRIES - nMRUCount);
878
0
        nRealPos = nPos + nMRUCount;
879
0
    }
880
881
0
    nRealPos = m_pImplLB->InsertEntry( nRealPos, rStr );
882
0
    nRealPos -= m_pImplLB->GetEntryList().GetMRUCount();
883
0
    CallEventListeners( VclEventId::ComboboxItemAdded, reinterpret_cast<void*>(static_cast<sal_IntPtr>(nRealPos)) );
884
0
    return nRealPos;
885
0
}
886
887
sal_Int32 ComboBox::InsertEntryWithImage(
888
        const OUString& rStr, const Image& rImage, sal_Int32 const nPos)
889
0
{
890
0
    assert(nPos >= 0 && COMBOBOX_MAX_ENTRIES > m_pImplLB->GetEntryList().GetEntryCount());
891
892
0
    sal_Int32 nRealPos;
893
0
    if (nPos == COMBOBOX_APPEND)
894
0
        nRealPos = nPos;
895
0
    else
896
0
    {
897
0
        const sal_Int32 nMRUCount = m_pImplLB->GetEntryList().GetMRUCount();
898
0
        assert(nPos <= COMBOBOX_MAX_ENTRIES - nMRUCount);
899
0
        nRealPos = nPos + nMRUCount;
900
0
    }
901
902
0
    nRealPos = m_pImplLB->InsertEntry( nRealPos, rStr, rImage );
903
0
    nRealPos -= m_pImplLB->GetEntryList().GetMRUCount();
904
0
    CallEventListeners( VclEventId::ComboboxItemAdded, reinterpret_cast<void*>(static_cast<sal_IntPtr>(nRealPos)) );
905
0
    return nRealPos;
906
0
}
907
908
void ComboBox::RemoveEntryAt(sal_Int32 const nPos)
909
0
{
910
0
    const sal_Int32 nMRUCount = m_pImplLB->GetEntryList().GetMRUCount();
911
0
    assert(nPos >= 0 && nPos <= COMBOBOX_MAX_ENTRIES - nMRUCount);
912
0
    m_pImplLB->RemoveEntry( nPos + nMRUCount );
913
0
    CallEventListeners( VclEventId::ComboboxItemRemoved, reinterpret_cast<void*>(static_cast<sal_IntPtr>(nPos)) );
914
0
}
915
916
void ComboBox::Clear()
917
0
{
918
0
    if (!m_pImplLB)
919
0
        return;
920
0
    m_pImplLB->Clear();
921
0
    CallEventListeners( VclEventId::ComboboxItemRemoved, reinterpret_cast<void*>(sal_IntPtr(-1)) );
922
0
}
923
924
Image ComboBox::GetEntryImage( sal_Int32 nPos ) const
925
0
{
926
0
    if (m_pImplLB->GetEntryList().HasEntryImage(nPos))
927
0
        return m_pImplLB->GetEntryList().GetEntryImage( nPos );
928
0
    return Image();
929
0
}
930
931
sal_Int32 ComboBox::GetEntryPos( std::u16string_view rStr ) const
932
0
{
933
0
    sal_Int32 nPos = m_pImplLB->GetEntryList().FindEntry( rStr );
934
0
    if ( nPos != LISTBOX_ENTRY_NOTFOUND )
935
0
        nPos -= m_pImplLB->GetEntryList().GetMRUCount();
936
0
    return nPos;
937
0
}
938
939
OUString ComboBox::GetEntry( sal_Int32 nPos ) const
940
0
{
941
0
    const sal_Int32 nMRUCount = m_pImplLB->GetEntryList().GetMRUCount();
942
0
    if (nPos < 0 || nPos > COMBOBOX_MAX_ENTRIES - nMRUCount)
943
0
        return OUString();
944
945
0
    return m_pImplLB->GetEntryList().GetEntryText( nPos + nMRUCount );
946
0
}
947
948
sal_Int32 ComboBox::GetEntryCount() const
949
0
{
950
0
    if (!m_pImplLB)
951
0
        return 0;
952
0
    return m_pImplLB->GetEntryList().GetEntryCount() - m_pImplLB->GetEntryList().GetMRUCount();
953
0
}
954
955
bool ComboBox::IsTravelSelect() const
956
0
{
957
0
    return m_pImplLB->IsTravelSelect();
958
0
}
959
960
bool ComboBox::IsInDropDown() const
961
0
{
962
    // when the dropdown is dismissed, first mbInPopupMode is set to false, and on the next event iteration then
963
    // mbPopupMode is set to false
964
0
    return m_pFloatWin && m_pFloatWin->IsInPopupMode() && m_pFloatWin->ImplIsInPrivatePopupMode();
965
0
}
966
967
bool ComboBox::IsMultiSelectionEnabled() const
968
0
{
969
0
    return m_pImplLB->IsMultiSelectionEnabled();
970
0
}
971
972
0
void ComboBox::SetSelectHdl(const Link<ComboBox&,void>& rLink) { m_SelectHdl = rLink; }
973
974
void ComboBox::SetEntryActivateHdl(const Link<Edit&,bool>& rLink)
975
0
{
976
0
    if (!m_pSubEdit)
977
0
        return;
978
0
    m_pSubEdit->SetActivateHdl(rLink);
979
0
}
980
981
Size ComboBox::GetOptimalSize() const
982
0
{
983
0
    return CalcMinimumSize();
984
0
}
985
986
tools::Long ComboBox::getMaxWidthScrollBarAndDownButton() const
987
0
{
988
0
    tools::Long nButtonDownWidth = 0;
989
990
0
    vcl::Window *pBorder = GetWindow( GetWindowType::Border );
991
0
    ImplControlValue aControlValue;
992
0
    tools::Rectangle aContent, aBound;
993
994
    // use the full extent of the control
995
0
    const tools::Rectangle aArea(Point(), pBorder->GetOutputSizePixel());
996
997
0
    if ( GetNativeControlRegion(ControlType::Combobox, ControlPart::ButtonDown,
998
0
        aArea, ControlState::NONE, aControlValue, aBound, aContent) )
999
0
    {
1000
0
        nButtonDownWidth = aContent.getOpenWidth();
1001
0
    }
1002
1003
0
    const tools::Long nScrollBarWidth = GetSettings().GetStyleSettings().GetScrollBarSize();
1004
1005
0
    return std::max(nScrollBarWidth, nButtonDownWidth);
1006
0
}
1007
1008
Size ComboBox::CalcMinimumSize() const
1009
0
{
1010
0
    Size aSz;
1011
1012
0
    if (!m_pImplLB)
1013
0
        return aSz;
1014
1015
0
    if (!IsDropDownBox())
1016
0
    {
1017
0
        aSz = m_pImplLB->CalcSize( m_pImplLB->GetEntryList().GetEntryCount() );
1018
0
        aSz.AdjustHeight(m_nDDHeight );
1019
0
    }
1020
0
    else
1021
0
    {
1022
0
        aSz.setHeight( Edit::CalcMinimumSizeForText(GetText()).Height() );
1023
1024
0
        if (m_nWidthInChars!= -1)
1025
0
            aSz.setWidth(m_nWidthInChars * approximate_digit_width());
1026
0
        else
1027
0
            aSz.setWidth(m_pImplLB->GetMaxEntryWidth());
1028
0
    }
1029
1030
0
    if (m_nMaxWidthChars != -1)
1031
0
    {
1032
0
        tools::Long nMaxWidth = m_nMaxWidthChars * approximate_char_width();
1033
0
        aSz.setWidth( std::min(aSz.Width(), nMaxWidth) );
1034
0
    }
1035
1036
0
    if (IsDropDownBox())
1037
0
        aSz.AdjustWidth(getMaxWidthScrollBarAndDownButton() );
1038
1039
0
    ComboBoxBounds aBounds(calcComboBoxDropDownComponentBounds(
1040
0
        Size(0xFFFF, 0xFFFF), Size(0xFFFF, 0xFFFF)));
1041
0
    aSz.AdjustWidth(aBounds.aSubEditPos.X()*2 );
1042
1043
0
    aSz.AdjustWidth(ImplGetExtraXOffset() * 2 );
1044
1045
0
    aSz = CalcWindowSize( aSz );
1046
0
    return aSz;
1047
0
}
1048
1049
Size ComboBox::CalcAdjustedSize( const Size& rPrefSize ) const
1050
0
{
1051
0
    Size aSz = rPrefSize;
1052
0
    sal_Int32 nLeft, nTop, nRight, nBottom;
1053
0
    static_cast<vcl::Window*>(const_cast<ComboBox *>(this))->GetBorder( nLeft, nTop, nRight, nBottom );
1054
0
    aSz.AdjustHeight( -(nTop+nBottom) );
1055
0
    if ( !IsDropDownBox() )
1056
0
    {
1057
0
        tools::Long nEntryHeight = CalcBlockSize( 1, 1 ).Height();
1058
0
        tools::Long nLines = aSz.Height() / nEntryHeight;
1059
0
        if ( nLines < 1 )
1060
0
            nLines = 1;
1061
0
        aSz.setHeight( nLines * nEntryHeight );
1062
0
        aSz.AdjustHeight(m_nDDHeight );
1063
0
    }
1064
0
    else
1065
0
    {
1066
0
        aSz.setHeight( m_nDDHeight );
1067
0
    }
1068
0
    aSz.AdjustHeight(nTop+nBottom );
1069
1070
0
    aSz = CalcWindowSize( aSz );
1071
0
    return aSz;
1072
0
}
1073
1074
Size ComboBox::CalcBlockSize( sal_uInt16 nColumns, sal_uInt16 nLines ) const
1075
0
{
1076
    // show ScrollBars where appropriate
1077
0
    const Size aMinSz = CalcMinimumSize();
1078
0
    Size aSz;
1079
1080
    // height
1081
0
    if ( nLines )
1082
0
    {
1083
0
        if ( !IsDropDownBox() )
1084
0
            aSz.setHeight( m_pImplLB->CalcSize( nLines ).Height() + m_nDDHeight );
1085
0
        else
1086
0
            aSz.setHeight( m_nDDHeight );
1087
0
    }
1088
0
    else
1089
0
        aSz.setHeight( aMinSz.Height() );
1090
1091
    // width
1092
0
    if ( nColumns )
1093
0
        aSz.setWidth( nColumns * approximate_char_width() );
1094
0
    else
1095
0
        aSz.setWidth( aMinSz.Width() );
1096
1097
0
    if ( IsDropDownBox() )
1098
0
        aSz.AdjustWidth(getMaxWidthScrollBarAndDownButton() );
1099
1100
0
    if ( !IsDropDownBox() )
1101
0
    {
1102
0
        if ( aSz.Width() < aMinSz.Width() )
1103
0
            aSz.AdjustHeight(GetSettings().GetStyleSettings().GetScrollBarSize() );
1104
0
        if ( aSz.Height() < aMinSz.Height() )
1105
0
            aSz.AdjustWidth(GetSettings().GetStyleSettings().GetScrollBarSize() );
1106
0
    }
1107
1108
0
    aSz.AdjustWidth(ImplGetExtraXOffset() * 2 );
1109
1110
0
    aSz = CalcWindowSize( aSz );
1111
0
    return aSz;
1112
0
}
1113
1114
tools::Long ComboBox::GetDropDownEntryHeight() const
1115
0
{
1116
0
    return m_pImplLB->GetEntryHeight();
1117
0
}
1118
1119
void ComboBox::GetMaxVisColumnsAndLines( sal_uInt16& rnCols, sal_uInt16& rnLines ) const
1120
0
{
1121
0
    const tools::Long nCharWidth = GetTextWidth(OUString(u'x'));
1122
0
    if ( !IsDropDownBox() )
1123
0
    {
1124
0
        const Size aOutSz = GetMainWindow()->GetOutputSizePixel();
1125
0
        rnCols = (nCharWidth > 0) ? static_cast<sal_uInt16>(aOutSz.Width()/nCharWidth) : 1;
1126
0
        rnLines = static_cast<sal_uInt16>(aOutSz.Height()/GetDropDownEntryHeight());
1127
0
    }
1128
0
    else
1129
0
    {
1130
0
        const Size aOutSz = m_pSubEdit->GetOutputSizePixel();
1131
0
        rnCols = (nCharWidth > 0) ? static_cast<sal_uInt16>(aOutSz.Width()/nCharWidth) : 1;
1132
0
        rnLines = 1;
1133
0
    }
1134
0
}
1135
1136
rtl::Reference<comphelper::OAccessible> ComboBox::CreateAccessible()
1137
0
{
1138
0
    const bool bIsDropDownBox = (GetStyle() & WB_DROPDOWN) == WB_DROPDOWN;
1139
0
    if (bIsDropDownBox)
1140
0
        return new VCLXAccessibleDropDownComboBox(this);
1141
1142
0
    return new VCLXAccessibleComboBox(this);
1143
0
}
1144
1145
void ComboBox::Draw(OutputDevice& rDev, const Point& rPos, SystemTextColorFlags eFlags)
1146
0
{
1147
0
    GetMainWindow()->ApplySettings(rDev);
1148
1149
0
    const Point aPos = rDev.LogicToPixel(rPos);
1150
0
    const Size aSize = GetSizePixel();
1151
0
    const vcl::Font aFont = GetMainWindow()->GetDrawPixelFont(&rDev);
1152
1153
0
    rDev.Push();
1154
0
    rDev.SetMapMode();
1155
0
    rDev.SetFont(aFont);
1156
0
    rDev.SetTextFillColor();
1157
1158
    // Border/Background
1159
0
    rDev.SetLineColor();
1160
0
    rDev.SetFillColor();
1161
0
    const bool bBorder = (GetStyle() & WB_BORDER);
1162
0
    const bool bBackground = IsControlBackground();
1163
0
    if ( bBorder || bBackground )
1164
0
    {
1165
0
        tools::Rectangle aRect( aPos, aSize );
1166
        // aRect.Top() += nEditHeight;
1167
0
        if ( bBorder )
1168
0
        {
1169
0
            ImplDrawFrame(&rDev, aRect);
1170
0
        }
1171
0
        if ( bBackground )
1172
0
        {
1173
0
            rDev.SetFillColor(GetControlBackground());
1174
0
            rDev.DrawRect(aRect);
1175
0
        }
1176
0
    }
1177
1178
    // contents
1179
0
    if ( !IsDropDownBox() )
1180
0
    {
1181
0
        const tools::Long nOnePixel = GetDrawPixel(&rDev, 1);
1182
0
        const tools::Long nTextHeight = rDev.GetTextHeight();
1183
0
        const tools::Long nEditHeight = nTextHeight + 6 * nOnePixel;
1184
0
        DrawTextFlags eTextStyle = DrawTextFlags::VCenter;
1185
1186
        // First, draw the edit part
1187
0
        Size aOrigSize(m_pSubEdit->GetSizePixel());
1188
0
        m_pSubEdit->SetSizePixel(Size(aSize.Width(), nEditHeight));
1189
0
        m_pSubEdit->Draw(rDev, aPos, eFlags);
1190
0
        m_pSubEdit->SetSizePixel(aOrigSize);
1191
1192
        // Second, draw the listbox
1193
0
        if ( GetStyle() & WB_CENTER )
1194
0
            eTextStyle |= DrawTextFlags::Center;
1195
0
        else if ( GetStyle() & WB_RIGHT )
1196
0
            eTextStyle |= DrawTextFlags::Right;
1197
0
        else
1198
0
            eTextStyle |= DrawTextFlags::Left;
1199
1200
0
        if (eFlags & SystemTextColorFlags::Mono)
1201
0
        {
1202
0
            rDev.SetTextColor(COL_BLACK);
1203
0
        }
1204
0
        else
1205
0
        {
1206
0
            if ( !IsEnabled() )
1207
0
            {
1208
0
                const StyleSettings& rStyleSettings = GetSettings().GetStyleSettings();
1209
0
                rDev.SetTextColor(rStyleSettings.GetDisableColor());
1210
0
            }
1211
0
            else
1212
0
            {
1213
0
                rDev.SetTextColor(GetTextColor());
1214
0
            }
1215
0
        }
1216
1217
0
        tools::Rectangle aClip( aPos, aSize );
1218
0
        rDev.IntersectClipRegion(aClip);
1219
0
        sal_Int32 nLines = static_cast<sal_Int32>( nTextHeight > 0 ? (aSize.Height()-nEditHeight)/nTextHeight : 1 );
1220
0
        if ( !nLines )
1221
0
            nLines = 1;
1222
0
        const sal_Int32 nTEntry = IsReallyVisible() ? m_pImplLB->GetTopEntry() : 0;
1223
1224
0
        tools::Rectangle aTextRect( aPos, aSize );
1225
1226
0
        aTextRect.AdjustLeft(3*nOnePixel );
1227
0
        aTextRect.AdjustRight( -(3*nOnePixel) );
1228
0
        aTextRect.AdjustTop(nEditHeight + nOnePixel );
1229
0
        aTextRect.SetBottom( aTextRect.Top() + nTextHeight );
1230
1231
        // the drawing starts here
1232
0
        for ( sal_Int32 n = 0; n < nLines; ++n )
1233
0
        {
1234
0
            rDev.DrawText(aTextRect, m_pImplLB->GetEntryList().GetEntryText(n + nTEntry),
1235
0
                          eTextStyle);
1236
0
            aTextRect.AdjustTop(nTextHeight );
1237
0
            aTextRect.AdjustBottom(nTextHeight );
1238
0
        }
1239
0
    }
1240
1241
0
    rDev.Pop();
1242
1243
    // Call Edit::Draw after restoring the MapMode...
1244
0
    if ( IsDropDownBox() )
1245
0
    {
1246
0
        const Size aOrigSize(m_pSubEdit->GetSizePixel());
1247
0
        m_pSubEdit->SetSizePixel(GetSizePixel());
1248
0
        m_pSubEdit->Draw(rDev, rPos, eFlags);
1249
0
        m_pSubEdit->SetSizePixel(aOrigSize);
1250
        // DD-Button ?
1251
0
    }
1252
0
}
1253
1254
void ComboBox::SetUserDrawHdl(const Link<UserDrawEvent*, void>& rLink)
1255
0
{
1256
0
    m_pImplLB->SetUserDrawHdl(rLink);
1257
0
}
1258
1259
void ComboBox::SetUserItemSize( const Size& rSz )
1260
0
{
1261
0
    GetMainWindow()->SetUserItemSize( rSz );
1262
0
}
1263
1264
void ComboBox::EnableUserDraw( bool bUserDraw )
1265
0
{
1266
0
    GetMainWindow()->EnableUserDraw( bUserDraw );
1267
0
}
1268
1269
bool ComboBox::IsUserDrawEnabled() const
1270
0
{
1271
0
    return GetMainWindow()->IsUserDrawEnabled();
1272
0
}
1273
1274
void ComboBox::DrawEntry(const UserDrawEvent& rEvt)
1275
0
{
1276
0
    GetMainWindow()->DrawEntry(*rEvt.GetRenderContext(), rEvt.GetItemId(), /*bDrawImage*/false, /*bDrawText*/false);
1277
0
}
1278
1279
void ComboBox::AddSeparator( sal_Int32 n )
1280
0
{
1281
0
    m_pImplLB->AddSeparator( n );
1282
0
}
1283
1284
void ComboBox::SetMRUEntries(const std::vector<OUString>& rEntries)
1285
0
{
1286
0
    m_pImplLB->SetMRUEntries(rEntries);
1287
0
}
1288
1289
std::vector<OUString> ComboBox::GetMRUEntries() const
1290
0
{
1291
0
    return m_pImplLB ? m_pImplLB->GetMRUEntries() : std::vector<OUString>();
1292
0
}
1293
1294
void ComboBox::SetMaxMRUCount( sal_Int32 n )
1295
0
{
1296
0
    m_pImplLB->SetMaxMRUCount( n );
1297
0
}
1298
1299
sal_Int32 ComboBox::GetMaxMRUCount() const
1300
0
{
1301
0
    return m_pImplLB ? m_pImplLB->GetMaxMRUCount() : 0;
1302
0
}
1303
1304
sal_uInt16 ComboBox::GetDisplayLineCount() const
1305
0
{
1306
0
    return m_pImplLB ? m_pImplLB->GetDisplayLineCount() : 0;
1307
0
}
1308
1309
void ComboBox::SetEntryData(sal_Int32 nPos, OUString* pNewData)
1310
0
{
1311
0
    m_pImplLB->SetEntryData( nPos + m_pImplLB->GetEntryList().GetMRUCount(), pNewData );
1312
0
}
1313
1314
OUString* ComboBox::GetEntryData(sal_Int32 nPos) const
1315
0
{
1316
0
    return m_pImplLB->GetEntryList().GetEntryData(
1317
0
            nPos + m_pImplLB->GetEntryList().GetMRUCount() );
1318
0
}
1319
1320
sal_Int32 ComboBox::GetTopEntry() const
1321
0
{
1322
0
    sal_Int32 nPos = GetEntryCount() ? m_pImplLB->GetTopEntry() : LISTBOX_ENTRY_NOTFOUND;
1323
0
    if (nPos < m_pImplLB->GetEntryList().GetMRUCount())
1324
0
        nPos = 0;
1325
0
    return nPos;
1326
0
}
1327
1328
tools::Rectangle ComboBox::GetDropDownPosSizePixel() const
1329
0
{
1330
0
    return m_pFloatWin
1331
0
        ? m_pFloatWin->GetWindowExtentsRelative(*this)
1332
0
        : tools::Rectangle();
1333
0
}
1334
1335
const Wallpaper& ComboBox::GetDisplayBackground() const
1336
0
{
1337
0
    if (!m_pSubEdit->IsBackground())
1338
0
        return Control::GetDisplayBackground();
1339
1340
0
    const Wallpaper& rBack = m_pSubEdit->GetBackground();
1341
0
    if( ! rBack.IsBitmap() &&
1342
0
        ! rBack.IsGradient() &&
1343
0
        rBack == Wallpaper(COL_TRANSPARENT)
1344
0
        )
1345
0
        return Control::GetDisplayBackground();
1346
0
    return rBack;
1347
0
}
1348
1349
sal_Int32 ComboBox::GetSelectedEntryCount() const
1350
0
{
1351
0
    return m_pImplLB->GetEntryList().GetSelectedEntryCount();
1352
0
}
1353
1354
sal_Int32 ComboBox::GetSelectedEntryPos( sal_Int32 nIndex ) const
1355
0
{
1356
0
    sal_Int32 nPos = m_pImplLB->GetEntryList().GetSelectedEntryPos( nIndex );
1357
0
    if ( nPos != LISTBOX_ENTRY_NOTFOUND )
1358
0
    {
1359
0
        if (nPos < m_pImplLB->GetEntryList().GetMRUCount())
1360
0
            nPos = m_pImplLB->GetEntryList().FindEntry(m_pImplLB->GetEntryList().GetEntryText(nPos));
1361
0
        nPos = sal::static_int_cast<sal_Int32>(nPos - m_pImplLB->GetEntryList().GetMRUCount());
1362
0
    }
1363
0
    return nPos;
1364
0
}
1365
1366
bool ComboBox::IsEntryPosSelected( sal_Int32 nPos ) const
1367
0
{
1368
0
    return m_pImplLB->GetEntryList().IsEntryPosSelected(
1369
0
            nPos + m_pImplLB->GetEntryList().GetMRUCount() );
1370
0
}
1371
1372
void ComboBox::SelectEntryPos( sal_Int32 nPos, bool bSelect)
1373
0
{
1374
0
    if (nPos < m_pImplLB->GetEntryList().GetEntryCount())
1375
0
        m_pImplLB->SelectEntry(
1376
0
            nPos + m_pImplLB->GetEntryList().GetMRUCount(), bSelect);
1377
0
}
1378
1379
void ComboBox::SetNoSelection()
1380
0
{
1381
0
    m_pImplLB->SetNoSelection();
1382
0
    m_pSubEdit->SetText(OUString());
1383
0
}
1384
1385
tools::Rectangle ComboBox::GetBoundingRectangle( sal_Int32 nItem ) const
1386
0
{
1387
0
    tools::Rectangle aRect = GetMainWindow()->GetBoundingRectangle( nItem );
1388
0
    const tools::Rectangle aOffset = GetMainWindow()->GetWindowExtentsRelative( *static_cast<vcl::Window*>(const_cast<ComboBox *>(this)) );
1389
0
    aRect.Move( aOffset.Left(), aOffset.Top() );
1390
0
    return aRect;
1391
0
}
1392
1393
void ComboBox::SetBorderStyle( WindowBorderStyle nBorderStyle )
1394
0
{
1395
0
    Window::SetBorderStyle( nBorderStyle );
1396
0
    if ( !IsDropDownBox() )
1397
0
    {
1398
0
        m_pSubEdit->SetBorderStyle(nBorderStyle);
1399
0
        m_pImplLB->SetBorderStyle( nBorderStyle );
1400
0
    }
1401
0
}
1402
1403
void ComboBox::SetHighlightColor( const Color& rColor )
1404
0
{
1405
0
    AllSettings aSettings(GetSettings());
1406
0
    StyleSettings aStyle(aSettings.GetStyleSettings());
1407
0
    aStyle.SetHighlightColor(rColor);
1408
0
    aSettings.SetStyleSettings(aStyle);
1409
0
    SetSettings(aSettings);
1410
1411
0
    AllSettings aSettingsSubEdit(m_pSubEdit->GetSettings());
1412
0
    StyleSettings aStyleSubEdit(aSettingsSubEdit.GetStyleSettings());
1413
0
    aStyleSubEdit.SetHighlightColor(rColor);
1414
0
    aSettingsSubEdit.SetStyleSettings(aStyleSubEdit);
1415
0
    m_pSubEdit->SetSettings(aSettings);
1416
1417
0
    m_pImplLB->SetHighlightColor(rColor);
1418
0
}
1419
1420
void ComboBox::SetHighlightTextColor( const Color& rColor )
1421
0
{
1422
0
    AllSettings aSettings(GetSettings());
1423
0
    StyleSettings aStyle(aSettings.GetStyleSettings());
1424
0
    aStyle.SetHighlightTextColor(rColor);
1425
0
    aSettings.SetStyleSettings(aStyle);
1426
0
    SetSettings(aSettings);
1427
1428
0
    AllSettings aSettingsSubEdit(m_pSubEdit->GetSettings());
1429
0
    StyleSettings aStyleSubEdit(aSettingsSubEdit.GetStyleSettings());
1430
0
    aStyleSubEdit.SetHighlightTextColor(rColor);
1431
0
    aSettingsSubEdit.SetStyleSettings(aStyleSubEdit);
1432
0
    m_pSubEdit->SetSettings(aSettings);
1433
1434
0
    m_pImplLB->SetHighlightTextColor(rColor);
1435
0
}
1436
1437
ImplListBoxWindow* ComboBox::GetMainWindow() const
1438
0
{
1439
0
    return m_pImplLB->GetMainWindow();
1440
0
}
1441
1442
tools::Long ComboBox::GetIndexForPoint( const Point& rPoint, sal_Int32& rPos ) const
1443
0
{
1444
0
    if( !HasLayoutData() )
1445
0
        FillLayoutData();
1446
1447
    // check whether rPoint fits at all
1448
0
    tools::Long nIndex = Control::GetIndexForPoint( rPoint );
1449
0
    if( nIndex != -1 )
1450
0
    {
1451
        // point must be either in main list window
1452
        // or in impl window (dropdown case)
1453
0
        ImplListBoxWindow* rMain = GetMainWindow();
1454
1455
        // convert coordinates to ImplListBoxWindow pixel coordinate space
1456
0
        Point aConvPoint = LogicToPixel( rPoint );
1457
0
        const AbsoluteScreenPixelPoint aConvPointAbs = OutputToAbsoluteScreenPixel( aConvPoint );
1458
0
        aConvPoint = rMain->AbsoluteScreenToOutputPixel( aConvPointAbs );
1459
0
        aConvPoint = rMain->PixelToLogic( aConvPoint );
1460
1461
        // try to find entry
1462
0
        sal_Int32 nEntry = rMain->GetEntryPosForPoint( aConvPoint );
1463
0
        if( nEntry == LISTBOX_ENTRY_NOTFOUND )
1464
0
            nIndex = -1;
1465
0
        else
1466
0
            rPos = nEntry;
1467
0
    }
1468
1469
    // get line relative index
1470
0
    if( nIndex != -1 )
1471
0
        nIndex = ToRelativeLineIndex( nIndex );
1472
1473
0
    return nIndex;
1474
0
}
1475
1476
ComboBoxBounds ComboBox::calcComboBoxDropDownComponentBounds(
1477
    const Size &rOutSz, const Size &rBorderOutSz) const
1478
0
{
1479
0
    ComboBoxBounds aBounds;
1480
1481
0
    tools::Long    nTop = 0;
1482
0
    const tools::Long nBottom = rOutSz.Height();
1483
1484
0
    vcl::Window *pBorder = GetWindow(GetWindowType::Border);
1485
0
    ImplControlValue aControlValue;
1486
0
    Point aPoint;
1487
0
    tools::Rectangle aContent, aBound;
1488
1489
    // use the full extent of the control
1490
0
    tools::Rectangle aArea( aPoint, rBorderOutSz );
1491
1492
0
    if (GetNativeControlRegion(ControlType::Combobox, ControlPart::ButtonDown,
1493
0
            aArea, ControlState::NONE, aControlValue, aBound, aContent) )
1494
0
    {
1495
        // convert back from border space to local coordinates
1496
0
        aPoint = pBorder->ScreenToOutputPixel(OutputToScreenPixel(aPoint));
1497
0
        aContent.Move(-aPoint.X(), -aPoint.Y());
1498
1499
0
        aBounds.aButtonPos = Point(aContent.Left(), nTop);
1500
0
        aBounds.aButtonSize = Size(aContent.getOpenWidth(), (nBottom-nTop));
1501
1502
        // adjust the size of the edit field
1503
0
        if (GetNativeControlRegion(ControlType::Combobox, ControlPart::SubEdit,
1504
0
                    aArea, ControlState::NONE, aControlValue, aBound, aContent) )
1505
0
        {
1506
            // convert back from border space to local coordinates
1507
0
            aContent.Move(-aPoint.X(), -aPoint.Y());
1508
1509
            // use the themes drop down size
1510
0
            aBounds.aSubEditPos = aContent.TopLeft();
1511
0
            aBounds.aSubEditSize = aContent.GetSize();
1512
0
        }
1513
0
        else
1514
0
        {
1515
            // use the themes drop down size for the button
1516
0
            aBounds.aSubEditSize = Size(rOutSz.Width() - aContent.getOpenWidth(), rOutSz.Height());
1517
0
        }
1518
0
    }
1519
0
    else
1520
0
    {
1521
0
        tools::Long nSBWidth = GetSettings().GetStyleSettings().GetScrollBarSize();
1522
0
        nSBWidth = CalcZoom( nSBWidth );
1523
0
        aBounds.aSubEditSize = Size(rOutSz.Width() - nSBWidth, rOutSz.Height());
1524
0
        aBounds.aButtonPos = Point(rOutSz.Width() - nSBWidth, nTop);
1525
0
        aBounds.aButtonSize = Size(nSBWidth, (nBottom-nTop));
1526
0
    }
1527
0
    return aBounds;
1528
0
}
1529
1530
void ComboBox::SetWidthInChars(sal_Int32 nWidthInChars)
1531
0
{
1532
0
    if (nWidthInChars != m_nWidthInChars)
1533
0
    {
1534
0
        m_nWidthInChars = nWidthInChars;
1535
0
        queue_resize();
1536
0
    }
1537
0
}
1538
1539
void ComboBox::setMaxWidthChars(sal_Int32 nWidth)
1540
0
{
1541
0
    if (nWidth != m_nMaxWidthChars)
1542
0
    {
1543
0
        m_nMaxWidthChars = nWidth;
1544
0
        queue_resize();
1545
0
    }
1546
0
}
1547
1548
bool ComboBox::set_property(const OUString &rKey, const OUString &rValue)
1549
0
{
1550
0
    if (rKey == "width-chars")
1551
0
        SetWidthInChars(rValue.toInt32());
1552
0
    else if (rKey == "max-width-chars")
1553
0
        setMaxWidthChars(rValue.toInt32());
1554
0
    else if (rKey == "can-focus")
1555
0
    {
1556
        // as far as I can see in Gtk, setting a ComboBox as can.focus means
1557
        // the focus gets stuck in it, so try here to behave like gtk does
1558
        // with the settings that work, i.e. can.focus of false doesn't
1559
        // set the hard WB_NOTABSTOP
1560
0
        WinBits eBits = GetStyle();
1561
0
        eBits &= ~(WB_TABSTOP|WB_NOTABSTOP);
1562
0
        if (toBool(rValue))
1563
0
            eBits |= WB_TABSTOP;
1564
0
        SetStyle(eBits);
1565
0
    }
1566
0
    else if (rKey == "placeholder-text")
1567
0
        SetPlaceholderText(rValue);
1568
0
    else
1569
0
        return Control::set_property(rKey, rValue);
1570
0
    return true;
1571
0
}
1572
1573
FactoryFunction ComboBox::GetUITestFactory() const
1574
0
{
1575
0
    return ComboBoxUIObject::create;
1576
0
}
1577
1578
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */