Coverage Report

Created: 2026-08-14 10:22

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/vcl/source/control/quickselectionengine.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/quickselectionengine.hxx>
21
#include <vcl/event.hxx>
22
#include <vcl/timer.hxx>
23
#include <vcl/i18nhelp.hxx>
24
#include <vcl/svapp.hxx>
25
#include <vcl/settings.hxx>
26
#include <sal/log.hxx>
27
28
#include <optional>
29
30
namespace vcl
31
{
32
33
    struct QuickSelectionEngine_Data
34
    {
35
        ISearchableStringList&              rEntryList;
36
        OUString                            sCurrentSearchString;
37
        ::std::optional< sal_Unicode >    aSingleSearchChar;
38
        Timer                               aSearchTimeout;
39
40
        explicit QuickSelectionEngine_Data( ISearchableStringList& _entryList )
41
0
            :rEntryList( _entryList )
42
0
            ,aSearchTimeout( "vcl::QuickSelectionEngine_Data aSearchTimeout" )
43
0
        {
44
0
            aSearchTimeout.SetTimeout( 2500 );
45
0
            aSearchTimeout.SetInvokeHandler( LINK( this, QuickSelectionEngine_Data, SearchStringTimeout ) );
46
0
        }
47
48
        ~QuickSelectionEngine_Data()
49
0
        {
50
0
            aSearchTimeout.Stop();
51
0
        }
52
53
        DECL_LINK( SearchStringTimeout, Timer*, void );
54
    };
55
56
    namespace
57
    {
58
        void lcl_reset( QuickSelectionEngine_Data& _data )
59
0
        {
60
0
            _data.sCurrentSearchString.clear();
61
0
            _data.aSingleSearchChar.reset();
62
0
            _data.aSearchTimeout.Stop();
63
0
        }
64
    }
65
66
    IMPL_LINK_NOARG( QuickSelectionEngine_Data, SearchStringTimeout, Timer*, void )
67
0
    {
68
0
        lcl_reset( *this );
69
0
    }
70
71
    static StringEntryIdentifier findMatchingEntry( const OUString& _searchString, QuickSelectionEngine_Data const & _engineData )
72
0
    {
73
0
        const vcl::I18nHelper& rI18nHelper = Application::GetSettings().GetLocaleI18nHelper();
74
        // TODO: do we really need the Window's settings here? The original code used it ...
75
76
0
        OUString sEntryText;
77
        // get the "current + 1" entry
78
0
        StringEntryIdentifier pSearchEntry = _engineData.rEntryList.CurrentEntry( sEntryText );
79
0
        if ( pSearchEntry )
80
0
            pSearchEntry = _engineData.rEntryList.NextEntry( pSearchEntry, sEntryText );
81
        // loop 'til we find another matching entry
82
0
        StringEntryIdentifier pStartedWith = pSearchEntry;
83
0
        while ( pSearchEntry )
84
0
        {
85
0
            if ( rI18nHelper.MatchString( _searchString, sEntryText ) )
86
0
                break;
87
88
0
            pSearchEntry = _engineData.rEntryList.NextEntry( pSearchEntry, sEntryText );
89
0
            if ( pSearchEntry == pStartedWith )
90
0
                pSearchEntry = nullptr;
91
0
        }
92
93
0
        return pSearchEntry;
94
0
    }
95
96
    QuickSelectionEngine::QuickSelectionEngine( ISearchableStringList& _entryList )
97
0
        :m_pData( new QuickSelectionEngine_Data( _entryList ) )
98
0
    {
99
0
    }
100
101
    QuickSelectionEngine::~QuickSelectionEngine()
102
0
    {
103
0
    }
104
105
    bool QuickSelectionEngine::HandleKeyEvent( const KeyEvent& _keyEvent )
106
0
    {
107
0
        sal_Unicode c = _keyEvent.GetCharCode();
108
109
0
        if ( ( c >= 32 ) && ( c != 127 ) && !_keyEvent.GetKeyCode().IsMod2() )
110
0
        {
111
0
            m_pData->sCurrentSearchString += OUStringChar(c);
112
0
            SAL_INFO( "vcl", "QuickSelectionEngine::HandleKeyEvent: searching for " << m_pData->sCurrentSearchString );
113
114
0
            if ( m_pData->sCurrentSearchString.getLength() == 1 )
115
0
            {   // first character in the search -> remember
116
0
                m_pData->aSingleSearchChar = c;
117
0
            }
118
0
            else if ( m_pData->sCurrentSearchString.getLength() > 1 )
119
0
            {
120
0
                if ( !!m_pData->aSingleSearchChar && ( *m_pData->aSingleSearchChar != c ) )
121
                    // we already have a "single char", but the current one is different -> reset
122
0
                    m_pData->aSingleSearchChar.reset();
123
0
            }
124
125
0
            OUString aSearchTemp( m_pData->sCurrentSearchString );
126
127
0
            StringEntryIdentifier pMatchingEntry = findMatchingEntry( aSearchTemp, *m_pData );
128
0
            SAL_INFO( "vcl", "QuickSelectionEngine::HandleKeyEvent: found " << pMatchingEntry );
129
0
            if ( !pMatchingEntry && (aSearchTemp.getLength() > 1) && !!m_pData->aSingleSearchChar )
130
0
            {
131
                // if there's only one letter in the search string, use a different search mode
132
0
                aSearchTemp = OUString(*m_pData->aSingleSearchChar);
133
0
                pMatchingEntry = findMatchingEntry( aSearchTemp, *m_pData );
134
0
            }
135
136
0
            if ( pMatchingEntry )
137
0
            {
138
0
                m_pData->rEntryList.SelectEntry( pMatchingEntry );
139
0
                m_pData->aSearchTimeout.Start();
140
0
            }
141
0
            else
142
0
            {
143
0
                lcl_reset( *m_pData );
144
0
            }
145
146
0
            return true;
147
0
        }
148
0
        return false;
149
0
    }
150
151
    void QuickSelectionEngine::Reset()
152
0
    {
153
0
        lcl_reset( *m_pData );
154
0
    }
155
156
} // namespace vcl
157
158
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */