1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_vcl.hxx"
26 
27 #include "vcl/quickselectionengine.hxx"
28 #include "vcl/event.hxx"
29 #include "vcl/timer.hxx"
30 #include "vcl/i18nhelp.hxx"
31 #include "vcl/svapp.hxx"
32 
33 #include <boost/optional.hpp>
34 
35 //........................................................................
36 namespace vcl
37 {
38 //........................................................................
39 
40 	//====================================================================
41 	//= QuickSelectionEngine_Data
42 	//====================================================================
43     struct QuickSelectionEngine_Data
44     {
45         ISearchableStringList&              rEntryList;
46         String                              sCurrentSearchString;
47         ::boost::optional< sal_Unicode >    aSingleSearchChar;
48         Timer                               aSearchTimeout;
49 
QuickSelectionEngine_Datavcl::QuickSelectionEngine_Data50         QuickSelectionEngine_Data( ISearchableStringList& _entryList )
51             :rEntryList( _entryList )
52             ,sCurrentSearchString()
53             ,aSingleSearchChar()
54             ,aSearchTimeout()
55         {
56 	        aSearchTimeout.SetTimeout( 2500 );
57 	        aSearchTimeout.SetTimeoutHdl( LINK( this, QuickSelectionEngine_Data, SearchStringTimeout ) );
58         }
59 
~QuickSelectionEngine_Datavcl::QuickSelectionEngine_Data60         ~QuickSelectionEngine_Data()
61         {
62 	        aSearchTimeout.Stop();
63         }
64 
65 	    DECL_LINK( SearchStringTimeout, Timer* );
66     };
67 
68     //--------------------------------------------------------------------
69     namespace
70     {
lcl_reset(QuickSelectionEngine_Data & _data)71         static void lcl_reset( QuickSelectionEngine_Data& _data )
72         {
73             _data.sCurrentSearchString.Erase();
74             _data.aSingleSearchChar.reset();
75             _data.aSearchTimeout.Stop();
76         }
77     }
78 
79 	//--------------------------------------------------------------------
80     IMPL_LINK( QuickSelectionEngine_Data, SearchStringTimeout, Timer*, /*EMPTYARG*/ )
81     {
82         lcl_reset( *this );
83 	    return 1;
84     }
85 
86 	//--------------------------------------------------------------------
findMatchingEntry(const String & _searchString,QuickSelectionEngine_Data & _engineData)87     static StringEntryIdentifier findMatchingEntry( const String& _searchString, QuickSelectionEngine_Data& _engineData )
88     {
89         const vcl::I18nHelper& rI18nHelper = Application::GetSettings().GetLocaleI18nHelper();
90             // TODO: do we really need the Window's settings here? The original code used it ...
91 
92         String sEntryText;
93         // get the "current + 1" entry
94         StringEntryIdentifier pSearchEntry = _engineData.rEntryList.CurrentEntry( sEntryText );
95         if ( pSearchEntry )
96             pSearchEntry = _engineData.rEntryList.NextEntry( pSearchEntry, sEntryText );
97         // loop 'til we find another matching entry
98         StringEntryIdentifier pStartedWith = pSearchEntry;
99         while ( pSearchEntry )
100         {
101             if ( rI18nHelper.MatchString( _searchString, sEntryText ) != 0 )
102                 break;
103 
104             pSearchEntry = _engineData.rEntryList.NextEntry( pSearchEntry, sEntryText );
105             if ( pSearchEntry == pStartedWith )
106                 pSearchEntry = NULL;
107         }
108 
109         return pSearchEntry;
110     }
111 
112     //====================================================================
113 	//= QuickSelectionEngine
114 	//====================================================================
115 	//--------------------------------------------------------------------
QuickSelectionEngine(ISearchableStringList & _entryList)116     QuickSelectionEngine::QuickSelectionEngine( ISearchableStringList& _entryList )
117         :m_pData( new QuickSelectionEngine_Data( _entryList ) )
118     {
119     }
120 
121 	//--------------------------------------------------------------------
~QuickSelectionEngine()122     QuickSelectionEngine::~QuickSelectionEngine()
123     {
124     }
125 
126     //--------------------------------------------------------------------
HandleKeyEvent(const KeyEvent & _keyEvent)127     bool QuickSelectionEngine::HandleKeyEvent( const KeyEvent& _keyEvent )
128     {
129         xub_Unicode c = _keyEvent.GetCharCode();
130 
131         if ( ( c >= 32 ) && ( c != 127 ) && !_keyEvent.GetKeyCode().IsMod2() )
132         {
133             m_pData->sCurrentSearchString += c;
134             OSL_TRACE( "QuickSelectionEngine::HandleKeyEvent: searching for %s", ByteString( m_pData->sCurrentSearchString, RTL_TEXTENCODING_UTF8 ).GetBuffer() );
135 
136             if ( m_pData->sCurrentSearchString.Len() == 1 )
137             {   // first character in the search -> remmeber
138                 m_pData->aSingleSearchChar.reset( c );
139             }
140             else if ( m_pData->sCurrentSearchString.Len() > 1 )
141             {
142                 if ( !!m_pData->aSingleSearchChar && ( *m_pData->aSingleSearchChar != c ) )
143                     // we already have a "single char", but the current one is different -> reset
144                     m_pData->aSingleSearchChar.reset();
145             }
146 
147             XubString aSearchTemp( m_pData->sCurrentSearchString );
148 
149             StringEntryIdentifier pMatchingEntry = findMatchingEntry( aSearchTemp, *m_pData );
150             OSL_TRACE( "QuickSelectionEngine::HandleKeyEvent: found %p", pMatchingEntry );
151             if ( !pMatchingEntry && ( aSearchTemp.Len() > 1 ) && !!m_pData->aSingleSearchChar )
152             {
153                 // if there's only one letter in the search string, use a different search mode
154                 aSearchTemp = *m_pData->aSingleSearchChar;
155                 pMatchingEntry = findMatchingEntry( aSearchTemp, *m_pData );
156             }
157 
158             if ( pMatchingEntry )
159             {
160                 m_pData->rEntryList.SelectEntry( pMatchingEntry );
161                 m_pData->aSearchTimeout.Start();
162             }
163             else
164             {
165                 lcl_reset( *m_pData );
166             }
167 
168             return true;
169         }
170         return false;
171     }
172 
173     //--------------------------------------------------------------------
Reset()174     void QuickSelectionEngine::Reset()
175     {
176         lcl_reset( *m_pData );
177     }
178 
179 //........................................................................
180 } // namespace vcl
181 //........................................................................
182