Code::Blocks  SVN r11506
incremental_select_helper.cpp
Go to the documentation of this file.
1 
2 /*
3  * This file is part of the Code::Blocks IDE and licensed under the GNU Lesser General Public License, version 3
4  * http://www.gnu.org/licenses/lgpl-3.0.html
5  */
6 
7 #include "sdk_precomp.h"
8 
10 
11 #ifndef CB_PRECOMP
12  #include <wx/dialog.h>
13  #include <wx/sizer.h>
14  #include <wx/stattext.h>
15  #include <wx/textctrl.h>
16 #endif
17 
19 {
20 }
21 
23 {
24  return wxLIST_AUTOSIZE;
25 }
26 
28 {
29 }
30 
32 {
33  return m_indices.size();
34 }
35 
37 {
38  m_indices.clear();
39 }
40 
42 {
43  m_indices.push_back(index);
44 }
45 
47 {
48  if (index >= 0 && index < int(m_indices.size()))
49  return m_indices[index];
50  else
51  return wxNOT_FOUND;
52 }
53 
55  m_parent(parent),
56  m_list(nullptr),
57  m_text(nullptr),
58  m_iterator(iterator)
59 {
60  m_parent->PushEventHandler(this);
61  SetEvtHandlerEnabled(true);
62 }
63 
65 {
66 }
67 
69 {
70  m_list = list;
71  m_text = text;
72 
73  m_text->Connect(wxEVT_COMMAND_TEXT_UPDATED, (wxObjectEventFunction)&IncrementalSelectHandler::OnTextChanged,
74  nullptr, this);
75  m_text->Connect(wxEVT_KEY_DOWN, (wxObjectEventFunction)&IncrementalSelectHandler::OnKeyDown, nullptr, this);
76  m_list->Connect(wxEVT_KEY_DOWN, (wxObjectEventFunction)&IncrementalSelectHandler::OnKeyDown, nullptr, this);
77  m_list->Connect(wxEVT_COMMAND_LIST_ITEM_ACTIVATED, wxListEventHandler(IncrementalSelectHandler::OnItemActivated),
78  nullptr, this);
79 
81  FilterItems();
82 }
83 
85 {
86  m_list->Disconnect(wxEVT_COMMAND_LIST_ITEM_ACTIVATED, wxListEventHandler(IncrementalSelectHandler::OnItemActivated),
87  nullptr, this);
88  m_text->Disconnect(wxEVT_KEY_DOWN, (wxObjectEventFunction)&IncrementalSelectHandler::OnKeyDown, nullptr, this);
89  m_list->Disconnect(wxEVT_KEY_DOWN, (wxObjectEventFunction)&IncrementalSelectHandler::OnKeyDown, nullptr, this);
90  m_text->Disconnect(wxEVT_COMMAND_TEXT_UPDATED, (wxObjectEventFunction)&IncrementalSelectHandler::OnTextChanged,
91  nullptr, this);
92 
93  SetEvtHandlerEnabled(false);
94  window->RemoveEventHandler(this);
95 }
96 
98 {
99  FilterItems();
100  event.Skip();
101 }
102 
104 {
105  list.SetItemCount(iterator.GetFilteredCount());
106 
107  for (int ii = 0; ii < list.GetColumnCount(); ++ii)
108  {
109  int width = iterator.GetColumnWidth(ii);
110  if (width != -1)
111  list.SetColumnWidth(ii, width);
112  }
113 
114  if (iterator.GetFilteredCount() > 0)
116  list.Refresh();
117 }
118 
120 {
121  m_iterator->Reset();
122 
123  const wxString &inputPattern = m_text->GetValue().Lower();
124  if (inputPattern.empty())
125  {
126  int count = m_iterator->GetTotalCount();
127  for (int ii = 0; ii < count; ++ii)
128  m_iterator->AddIndex(ii);
129 
131  return;
132  }
133 
134  // We put a star before and after pattern to find search expression everywhere in path
135  // that is: if user enter "a", it will match "123a", "12a" or "a12".
136  wxString search(wxT("*") + inputPattern + wxT("*"));
137  bool isWord = !inputPattern.empty();
138  for (auto ch : inputPattern)
139  {
140  if (!wxIsalpha(ch))
141  {
142  isWord = false;
143  break;
144  }
145  }
146 
147  std::vector<int> indices, promoted;
148  indices.reserve(100);
149  promoted.reserve(100);
150 
151  for (int i = 0; i < m_iterator->GetTotalCount(); ++i)
152  {
153  wxString const &item = m_iterator->GetItemFilterString(i).Lower();
154  if (item.Matches(search.c_str()))
155  {
156  // If the search pattern doesn't contain non alpha characters and it matches at the start of the word in
157  // the item string then promote these items to the top of the list. The order is preserved.
158  if (isWord)
159  {
160  size_t pos = 0, newPos;
161  bool isPromoted = false;
162 
163  while ((newPos = item.find(inputPattern, pos)) != wxString::npos)
164  {
165  if (newPos == 0)
166  {
167  isPromoted = true;
168  break;
169  }
170 
171  if (!wxIsalpha(item[newPos - 1]))
172  {
173  isPromoted = true;
174  break;
175  }
176 
177  // Move one character forward to prevent the same string to be found again.
178  pos = newPos + 1;
179  }
180  if (isPromoted)
181  promoted.push_back(i);
182  else
183  indices.push_back(i);
184  }
185  else
186  indices.push_back(i);
187  }
188  }
189 
190  for (auto i : promoted)
191  m_iterator->AddIndex(i);
192  for (auto i : indices)
193  m_iterator->AddIndex(i);
194 
196 }
197 
198 #if !wxCHECK_VERSION(3, 0, 0)
199  typedef int wxStandardID;
200 #endif
201 
202 static wxStandardID KeyDownAction(wxKeyEvent& event, int &selected, int selectedMax)
203 {
204  // now, adjust position from key input
205  switch (event.GetKeyCode())
206  {
207  case WXK_RETURN:
208  case WXK_NUMPAD_ENTER:
209  return wxID_OK;
210 
211  case WXK_ESCAPE:
212  return wxID_CANCEL;
213 
214  case WXK_UP:
215  case WXK_NUMPAD_UP:
216  if (selected)
217  selected--;
218  break;
219 
220  case WXK_DOWN:
221  case WXK_NUMPAD_DOWN:
222  selected++;
223  break;
224 
225  case WXK_PAGEUP:
226  case WXK_NUMPAD_PAGEUP:
227  selected -= 10;
228  break;
229 
230  case WXK_PAGEDOWN:
231  case WXK_NUMPAD_PAGEDOWN:
232  selected += 10;
233  break;
234 
235  case WXK_HOME:
237  selected = 0;
238  else
239  event.Skip();
240  break;
241 
242  case WXK_END:
244  selected = selectedMax;
245  else
246  event.Skip();
247  break;
248 
249  default:
250  event.Skip();
251  break;
252  }
253 
254  // Clamp value below 0 and above Max
255  if (selected < 0)
256  selected = 0;
257  else if (selected > selectedMax)
258  selected = selectedMax;
259 
260  return wxID_LOWEST;
261 }
262 
264 {
266  int selectedMax = m_list->GetItemCount() - 1;
267 
268  wxStandardID result = KeyDownAction(event, selected, selectedMax);
269  if (result != wxID_LOWEST)
270  m_parent->EndModal(result);
271  else if (selectedMax >= 0)
272  {
274  m_list->EnsureVisible(selected);
275  }
276 }
277 
279 {
280  m_parent->EndModal(wxID_OK);
281 }
282 
284 {
286  if (index == -1)
287  return wxNOT_FOUND;
288  else
289  return m_iterator->GetUnfilteredIndex(index);
290 }
291 
293  long style, const wxValidator &validator, const wxString &name) :
294  wxListCtrl(parent, winid, pos, size, style, validator, name)
295 {
296 }
297 
298 wxString IncrementalListCtrl::OnGetItemText(long item, long column) const
299 {
300  return m_Iterator->GetDisplayText(item, column);
301 }
302 
304 {
305  m_Iterator = iterator;
306 }
307 
308 
310  m_items(items), m_columnWidth(300)
311 {
312 }
313 
315 {
316  return m_items.size();
317 }
319 {
320  return m_items[index];
321 }
323 {
324  return m_items[m_indices[index]];
325 }
327 {
328  return m_columnWidth;
329 }
331 {
332  int length = 0;
333  wxString longest;
334 
335  for (const auto &item : m_items)
336  {
337  int itemLength = item.length();
338  if (length < itemLength)
339  {
340  longest = item;
341  length = itemLength;
342  }
343  }
344 
345  if (length > 0)
346  {
347  int yTemp;
348  list.GetTextExtent(longest, &m_columnWidth, &yTemp);
349  // just to be safe if the longest string is made of thin letters.
350  m_columnWidth += 50;
351  }
352  else
353  m_columnWidth = 300;
354 }
355 
356 const long ID_TEXTCTRL1 = wxNewId();
357 const long ID_RESULT_LIST = wxNewId();
358 
359 BEGIN_EVENT_TABLE(IncrementalSelectDialog, wxDialog)
360 END_EVENT_TABLE()
361 
363  const wxString &title, const wxString &message) :
364  m_handler(this, iterator)
365 {
366  BuildContent(parent, iterator, title, message);
367 
368  m_handler.Init(m_resultList, m_text);
369 }
370 
372 {
373  m_handler.DeInit(this);
374 }
375 
377  const wxString &message)
378 {
379  Create(parent, wxID_ANY, title, wxDefaultPosition, wxDefaultSize,
381  wxBoxSizer *sizer = new wxBoxSizer(wxVERTICAL);
382  wxStaticText *labelCtrl = new wxStaticText(this, wxID_ANY, message, wxDefaultPosition, wxDefaultSize, 0,
383  _T("wxID_ANY"));
384  sizer->Add(labelCtrl, 0, wxTOP|wxLEFT|wxRIGHT|wxEXPAND, 5);
386  _T("ID_TEXTCTRL1"));
387  m_text->SetFocus();
388  sizer->Add(m_text, 0, wxTOP|wxLEFT|wxRIGHT|wxEXPAND, 5);
391  wxDefaultValidator, _T("ID_RESULT_LIST"));
392  m_resultList->SetMinSize(wxSize(500,300));
393  sizer->Add(m_resultList, 1, wxALL|wxEXPAND, 5);
394  SetSizer(sizer);
395  sizer->Fit(this);
396  sizer->SetSizeHints(this);
397 
398  // Add first column
399  wxListItem column;
400  column.SetId(0);
401  column.SetText( _("Column") );
402  column.SetWidth(300);
403  m_resultList->InsertColumn(0, column);
404  m_resultList->SetIterator(iterator);
405 }
406 
408 {
409  return m_handler.GetSelection();
410 }
wxSize Fit(wxWindow *window)
virtual const wxString & GetItemFilterString(int index) const =0
#define wxMAXIMIZE_BOX
virtual int GetFilteredCount() const =0
int GetKeyCode() const
virtual int GetUnfilteredIndex(int index) const =0
bool Matches(const wxString &mask) const
int wxNewId()
int GetUnfilteredIndex(int index) const override
const wxValidator wxDefaultValidator
long GetNextItem(long item, int geometry=wxLIST_NEXT_ALL, int state=wxLIST_STATE_DONTCARE) const
wxString Lower() const
size_t length() const
IncrementalSelectIterator * m_Iterator
virtual int GetColumnWidth(int column) const
#define wxLC_REPORT
#define wxTE_PROCESS_ENTER
void BuildContent(wxWindow *parent, IncrementalSelectIterator *iterator, const wxString &title, const wxString &message)
wxString GetDisplayText(int index, int column) const override
void SetWidth(int width)
wxCStrData c_str() const
#define wxLIST_STATE_SELECTED
int GetColumnWidth(int column) const override
virtual void AddIndex(int index)=0
#define _T(string)
virtual wxString GetDisplayText(int index, int column) const =0
#define wxHSCROLL
void SetItemCount(long count)
void Init(wxListCtrl *list, wxTextCtrl *text)
#define wxT(string)
#define wxNOT_FOUND
void SetId(long id)
bool empty() const
size_t find(const wxString &str, size_t nStart=0) const
IncrementalSelectArrayIterator(const wxArrayString &items)
const long ID_TEXTCTRL1
virtual void Reset()=0
bool SetItemState(long item, long state, long stateMask)
void SetIterator(IncrementalSelectIterator *iterator)
null_pointer_t nullptr
Definition: nullptr.cpp:16
virtual int GetTotalCount() const =0
IncrementalListCtrl(wxWindow *parent, wxWindowID winid=wxID_ANY, const wxPoint &pos=wxDefaultPosition, const wxSize &size=wxDefaultSize, long style=wxLC_ICON, const wxValidator &validator=wxDefaultValidator, const wxString &name=wxListCtrlNameStr)
wxSizerItem * Add(wxWindow *window, const wxSizerFlags &flags)
const wxSize wxDefaultSize
const wxPoint wxDefaultPosition
wxStandardID
const wxString & GetItemFilterString(int index) const override
#define wxDEFAULT_DIALOG_STYLE
int GetItemCount() const
wxString wxEmptyString
#define wxLC_SINGLE_SEL
void FilterItemsFinalize(wxListCtrl &list, IncrementalSelectIterator &iterator)
IncrementalSelectHandler(wxDialog *parent, IncrementalSelectIterator *iterator)
const wxString & _(const wxString &string)
wxEventType wxEVT_KEY_DOWN
#define wxLC_NO_HEADER
Class that implements a virtual list control that uses an IncrementalSelectIterator to populate the l...
bool wxIsalpha(const wxUniChar &c)
static const size_t npos
int GetColumnCount() const
#define wxCLOSE_BOX
virtual void CalcColumnWidth(wxListCtrl &list)
void OnTextChanged(wxCommandEvent &event)
const long ID_RESULT_LIST
void SetSizeHints(wxWindow *window)
#define wxLC_VIRTUAL
IncrementalSelectIterator * m_iterator
void CalcColumnWidth(wxListCtrl &list) override
void OnItemActivated(wxListEvent &event)
bool SetColumnWidth(int col, int width)
void SetText(const wxString &text)
Simple incremental select dialog that shows a single column and doesn&#39;t have much ui elements...
wxString OnGetItemText(long item, long column) const override
#define wxRESIZE_BORDER
int wxWindowID
bool wxGetKeyState(wxKeyCode key)
bool EnsureVisible(long item)
#define wxVSCROLL
static wxStandardID KeyDownAction(wxKeyEvent &event, int &selected, int selectedMax)