]> git.saurik.com Git - wxWidgets.git/blame_incremental - samples/listctrl/listtest.cpp
don't use wxVector<wxDataFormat> from wx/clipbrd.h as wxDataFormat definition is...
[wxWidgets.git] / samples / listctrl / listtest.cpp
... / ...
CommitLineData
1/////////////////////////////////////////////////////////////////////////////
2// Name: listctrl.cpp
3// Purpose: wxListCtrl sample
4// Author: Julian Smart
5// Modified by:
6// Created: 04/01/98
7// RCS-ID: $Id$
8// Copyright: (c) Julian Smart
9// Licence: wxWindows license
10/////////////////////////////////////////////////////////////////////////////
11
12// For compilers that support precompilation, includes "wx/wx.h".
13#include "wx/wxprec.h"
14
15#ifdef __BORLANDC__
16#pragma hdrstop
17#endif
18
19#ifndef WX_PRECOMP
20#include "wx/wx.h"
21#endif
22
23#if !defined(__WXMSW__) && !defined(__WXPM__)
24 #include "mondrian.xpm"
25#endif
26
27#ifndef __WXMSW__
28 #include "bitmaps/toolbrai.xpm"
29 #include "bitmaps/toolchar.xpm"
30 #include "bitmaps/tooldata.xpm"
31 #include "bitmaps/toolnote.xpm"
32 #include "bitmaps/tooltodo.xpm"
33 #include "bitmaps/toolchec.xpm"
34 #include "bitmaps/toolgame.xpm"
35 #include "bitmaps/tooltime.xpm"
36 #include "bitmaps/toolword.xpm"
37 #include "bitmaps/small1.xpm"
38#endif
39
40#include "wx/imaglist.h"
41#include "wx/listctrl.h"
42#include "wx/timer.h" // for wxStopWatch
43#include "wx/colordlg.h" // for wxGetColourFromUser
44#include "wx/settings.h"
45#include "wx/sysopt.h"
46
47#include "listtest.h"
48
49const wxChar *SMALL_VIRTUAL_VIEW_ITEMS[][2] =
50{
51 { _T("Cat"), _T("meow") },
52 { _T("Cow"), _T("moo") },
53 { _T("Crow"), _T("caw") },
54 { _T("Dog"), _T("woof") },
55 { _T("Duck"), _T("quack") },
56 { _T("Mouse"), _T("squeak") },
57 { _T("Owl"), _T("hoo") },
58 { _T("Pig"), _T("oink") },
59 { _T("Pigeon"), _T("coo") },
60 { _T("Sheep"), _T("baaah") },
61};
62
63
64BEGIN_EVENT_TABLE(MyFrame, wxFrame)
65 EVT_SIZE(MyFrame::OnSize)
66
67 EVT_MENU(LIST_QUIT, MyFrame::OnQuit)
68 EVT_MENU(LIST_ABOUT, MyFrame::OnAbout)
69 EVT_MENU(LIST_LIST_VIEW, MyFrame::OnListView)
70 EVT_MENU(LIST_REPORT_VIEW, MyFrame::OnReportView)
71 EVT_MENU(LIST_ICON_VIEW, MyFrame::OnIconView)
72 EVT_MENU(LIST_ICON_TEXT_VIEW, MyFrame::OnIconTextView)
73 EVT_MENU(LIST_SMALL_ICON_VIEW, MyFrame::OnSmallIconView)
74 EVT_MENU(LIST_SMALL_ICON_TEXT_VIEW, MyFrame::OnSmallIconTextView)
75 EVT_MENU(LIST_VIRTUAL_VIEW, MyFrame::OnVirtualView)
76 EVT_MENU(LIST_SMALL_VIRTUAL_VIEW, MyFrame::OnSmallVirtualView)
77
78 EVT_MENU(LIST_GOTO, MyFrame::OnGoTo)
79 EVT_MENU(LIST_FOCUS_LAST, MyFrame::OnFocusLast)
80 EVT_MENU(LIST_TOGGLE_FIRST, MyFrame::OnToggleFirstSel)
81 EVT_MENU(LIST_DESELECT_ALL, MyFrame::OnDeselectAll)
82 EVT_MENU(LIST_SELECT_ALL, MyFrame::OnSelectAll)
83 EVT_MENU(LIST_DELETE, MyFrame::OnDelete)
84 EVT_MENU(LIST_ADD, MyFrame::OnAdd)
85 EVT_MENU(LIST_EDIT, MyFrame::OnEdit)
86 EVT_MENU(LIST_DELETE_ALL, MyFrame::OnDeleteAll)
87 EVT_MENU(LIST_SORT, MyFrame::OnSort)
88 EVT_MENU(LIST_SET_FG_COL, MyFrame::OnSetFgColour)
89 EVT_MENU(LIST_SET_BG_COL, MyFrame::OnSetBgColour)
90 EVT_MENU(LIST_TOGGLE_MULTI_SEL, MyFrame::OnToggleMultiSel)
91 EVT_MENU(LIST_SHOW_COL_INFO, MyFrame::OnShowColInfo)
92 EVT_MENU(LIST_SHOW_SEL_INFO, MyFrame::OnShowSelInfo)
93 EVT_MENU(LIST_SHOW_VIEW_RECT, MyFrame::OnShowViewRect)
94#ifdef wxHAS_LISTCTRL_COLUMN_ORDER
95 EVT_MENU(LIST_SET_COL_ORDER, MyFrame::OnSetColOrder)
96 EVT_MENU(LIST_GET_COL_ORDER, MyFrame::OnGetColOrder)
97#endif // wxHAS_LISTCTRL_COLUMN_ORDER
98 EVT_MENU(LIST_FREEZE, MyFrame::OnFreeze)
99 EVT_MENU(LIST_THAW, MyFrame::OnThaw)
100 EVT_MENU(LIST_TOGGLE_LINES, MyFrame::OnToggleLines)
101 EVT_MENU(LIST_MAC_USE_GENERIC, MyFrame::OnToggleMacUseGeneric)
102
103 EVT_UPDATE_UI(LIST_SHOW_COL_INFO, MyFrame::OnUpdateShowColInfo)
104 EVT_UPDATE_UI(LIST_TOGGLE_MULTI_SEL, MyFrame::OnUpdateToggleMultiSel)
105END_EVENT_TABLE()
106
107BEGIN_EVENT_TABLE(MyListCtrl, wxListCtrl)
108 EVT_LIST_BEGIN_DRAG(LIST_CTRL, MyListCtrl::OnBeginDrag)
109 EVT_LIST_BEGIN_RDRAG(LIST_CTRL, MyListCtrl::OnBeginRDrag)
110 EVT_LIST_BEGIN_LABEL_EDIT(LIST_CTRL, MyListCtrl::OnBeginLabelEdit)
111 EVT_LIST_END_LABEL_EDIT(LIST_CTRL, MyListCtrl::OnEndLabelEdit)
112 EVT_LIST_DELETE_ITEM(LIST_CTRL, MyListCtrl::OnDeleteItem)
113 EVT_LIST_DELETE_ALL_ITEMS(LIST_CTRL, MyListCtrl::OnDeleteAllItems)
114 EVT_LIST_ITEM_SELECTED(LIST_CTRL, MyListCtrl::OnSelected)
115 EVT_LIST_ITEM_DESELECTED(LIST_CTRL, MyListCtrl::OnDeselected)
116 EVT_LIST_KEY_DOWN(LIST_CTRL, MyListCtrl::OnListKeyDown)
117 EVT_LIST_ITEM_ACTIVATED(LIST_CTRL, MyListCtrl::OnActivated)
118 EVT_LIST_ITEM_FOCUSED(LIST_CTRL, MyListCtrl::OnFocused)
119
120 EVT_LIST_COL_CLICK(LIST_CTRL, MyListCtrl::OnColClick)
121 EVT_LIST_COL_RIGHT_CLICK(LIST_CTRL, MyListCtrl::OnColRightClick)
122 EVT_LIST_COL_BEGIN_DRAG(LIST_CTRL, MyListCtrl::OnColBeginDrag)
123 EVT_LIST_COL_DRAGGING(LIST_CTRL, MyListCtrl::OnColDragging)
124 EVT_LIST_COL_END_DRAG(LIST_CTRL, MyListCtrl::OnColEndDrag)
125
126 EVT_LIST_CACHE_HINT(LIST_CTRL, MyListCtrl::OnCacheHint)
127
128#if USE_CONTEXT_MENU
129 EVT_CONTEXT_MENU(MyListCtrl::OnContextMenu)
130#endif
131 EVT_CHAR(MyListCtrl::OnChar)
132
133 EVT_RIGHT_DOWN(MyListCtrl::OnRightClick)
134END_EVENT_TABLE()
135
136IMPLEMENT_APP(MyApp)
137
138// number of items in list/report view
139static const int NUM_ITEMS = 10;
140
141// number of items in icon/small icon view
142static const int NUM_ICONS = 9;
143
144int wxCALLBACK MyCompareFunction(long item1, long item2, long WXUNUSED(sortData))
145{
146 // inverse the order
147 if (item1 < item2)
148 return -1;
149 if (item1 > item2)
150 return 1;
151
152 return 0;
153}
154
155// `Main program' equivalent, creating windows and returning main app frame
156bool MyApp::OnInit()
157{
158 if ( !wxApp::OnInit() )
159 return false;
160
161 // Create the main frame window
162 MyFrame *frame = new MyFrame(wxT("wxListCtrl Test"));
163
164 // Show the frame
165 frame->Show(true);
166
167 SetTopWindow(frame);
168
169 return true;
170}
171
172// My frame constructor
173MyFrame::MyFrame(const wxChar *title)
174 : wxFrame(NULL, wxID_ANY, title, wxDefaultPosition, wxSize(600, 500))
175{
176 m_listCtrl = NULL;
177 m_logWindow = NULL;
178 m_smallVirtual = false;
179
180 // Give it an icon
181 SetIcon( wxICON(mondrian) );
182
183 // Make an image list containing large icons
184 m_imageListNormal = new wxImageList(32, 32, true);
185 m_imageListSmall = new wxImageList(16, 16, true);
186
187#ifdef __WXMSW__
188 m_imageListNormal->Add( wxIcon(_T("icon1"), wxBITMAP_TYPE_ICO_RESOURCE) );
189 m_imageListNormal->Add( wxIcon(_T("icon2"), wxBITMAP_TYPE_ICO_RESOURCE) );
190 m_imageListNormal->Add( wxIcon(_T("icon3"), wxBITMAP_TYPE_ICO_RESOURCE) );
191 m_imageListNormal->Add( wxIcon(_T("icon4"), wxBITMAP_TYPE_ICO_RESOURCE) );
192 m_imageListNormal->Add( wxIcon(_T("icon5"), wxBITMAP_TYPE_ICO_RESOURCE) );
193 m_imageListNormal->Add( wxIcon(_T("icon6"), wxBITMAP_TYPE_ICO_RESOURCE) );
194 m_imageListNormal->Add( wxIcon(_T("icon7"), wxBITMAP_TYPE_ICO_RESOURCE) );
195 m_imageListNormal->Add( wxIcon(_T("icon8"), wxBITMAP_TYPE_ICO_RESOURCE) );
196 m_imageListNormal->Add( wxIcon(_T("icon9"), wxBITMAP_TYPE_ICO_RESOURCE) );
197
198 m_imageListSmall->Add( wxIcon(_T("iconsmall"), wxBITMAP_TYPE_ICO_RESOURCE) );
199
200#else
201 m_imageListNormal->Add( wxIcon( toolbrai_xpm ) );
202 m_imageListNormal->Add( wxIcon( toolchar_xpm ) );
203 m_imageListNormal->Add( wxIcon( tooldata_xpm ) );
204 m_imageListNormal->Add( wxIcon( toolnote_xpm ) );
205 m_imageListNormal->Add( wxIcon( tooltodo_xpm ) );
206 m_imageListNormal->Add( wxIcon( toolchec_xpm ) );
207 m_imageListNormal->Add( wxIcon( toolgame_xpm ) );
208 m_imageListNormal->Add( wxIcon( tooltime_xpm ) );
209 m_imageListNormal->Add( wxIcon( toolword_xpm ) );
210
211 m_imageListSmall->Add( wxIcon( small1_xpm) );
212#endif
213
214 // Make a menubar
215 wxMenu *menuFile = new wxMenu;
216 menuFile->Append(LIST_ABOUT, _T("&About"));
217 menuFile->AppendSeparator();
218 menuFile->Append(LIST_QUIT, _T("E&xit\tAlt-X"));
219
220 wxMenu *menuView = new wxMenu;
221 menuView->Append(LIST_LIST_VIEW, _T("&List view\tF1"));
222 menuView->Append(LIST_REPORT_VIEW, _T("&Report view\tF2"));
223 menuView->Append(LIST_ICON_VIEW, _T("&Icon view\tF3"));
224 menuView->Append(LIST_ICON_TEXT_VIEW, _T("Icon view with &text\tF4"));
225 menuView->Append(LIST_SMALL_ICON_VIEW, _T("&Small icon view\tF5"));
226 menuView->Append(LIST_SMALL_ICON_TEXT_VIEW, _T("Small icon &view with text\tF6"));
227 menuView->Append(LIST_VIRTUAL_VIEW, _T("&Virtual view\tF7"));
228 menuView->Append(LIST_SMALL_VIRTUAL_VIEW, _T("Small virtual vie&w\tF8"));
229#ifdef __WXMAC__
230 menuView->AppendCheckItem(LIST_MAC_USE_GENERIC, _T("Mac: Use Generic Control"));
231#endif
232
233 wxMenu *menuList = new wxMenu;
234 menuList->Append(LIST_GOTO, _T("&Go to item #3\tCtrl-3"));
235 menuList->Append(LIST_FOCUS_LAST, _T("&Make last item current\tCtrl-L"));
236 menuList->Append(LIST_TOGGLE_FIRST, _T("To&ggle first item\tCtrl-G"));
237 menuList->Append(LIST_DESELECT_ALL, _T("&Deselect All\tCtrl-D"));
238 menuList->Append(LIST_SELECT_ALL, _T("S&elect All\tCtrl-A"));
239 menuList->AppendSeparator();
240 menuList->Append(LIST_SHOW_COL_INFO, _T("Show &column info\tCtrl-C"));
241 menuList->Append(LIST_SHOW_SEL_INFO, _T("Show &selected items\tCtrl-S"));
242 menuList->Append(LIST_SHOW_VIEW_RECT, _T("Show &view rect"));
243#ifdef wxHAS_LISTCTRL_COLUMN_ORDER
244 menuList->Append(LIST_SET_COL_ORDER, _T("Se&t columns order\tShift-Ctrl-O"));
245 menuList->Append(LIST_GET_COL_ORDER, _T("Show&w columns order\tCtrl-O"));
246#endif // wxHAS_LISTCTRL_COLUMN_ORDER
247 menuList->AppendSeparator();
248 menuList->Append(LIST_SORT, _T("Sor&t\tCtrl-T"));
249 menuList->AppendSeparator();
250 menuList->Append(LIST_ADD, _T("&Append an item\tCtrl-P"));
251 menuList->Append(LIST_EDIT, _T("&Edit the item\tCtrl-E"));
252 menuList->Append(LIST_DELETE, _T("&Delete first item\tCtrl-X"));
253 menuList->Append(LIST_DELETE_ALL, _T("Delete &all items"));
254 menuList->AppendSeparator();
255 menuList->Append(LIST_FREEZE, _T("Free&ze\tCtrl-Z"));
256 menuList->Append(LIST_THAW, _T("Tha&w\tCtrl-W"));
257 menuList->AppendSeparator();
258 menuList->AppendCheckItem(LIST_TOGGLE_LINES, _T("Toggle &lines\tCtrl-I"));
259 menuList->Append(LIST_TOGGLE_MULTI_SEL, _T("&Multiple selection\tCtrl-M"),
260 _T("Toggle multiple selection"), true);
261
262 wxMenu *menuCol = new wxMenu;
263 menuCol->Append(LIST_SET_FG_COL, _T("&Foreground colour..."));
264 menuCol->Append(LIST_SET_BG_COL, _T("&Background colour..."));
265
266 wxMenuBar *menubar = new wxMenuBar;
267 menubar->Append(menuFile, _T("&File"));
268 menubar->Append(menuView, _T("&View"));
269 menubar->Append(menuList, _T("&List"));
270 menubar->Append(menuCol, _T("&Colour"));
271 SetMenuBar(menubar);
272
273 m_panel = new wxPanel(this, wxID_ANY);
274 m_logWindow = new wxTextCtrl(m_panel, wxID_ANY, wxEmptyString,
275 wxDefaultPosition, wxDefaultSize,
276 wxTE_READONLY | wxTE_MULTILINE | wxSUNKEN_BORDER);
277
278 m_logOld = wxLog::SetActiveTarget(new wxLogTextCtrl(m_logWindow));
279
280 RecreateList(wxLC_REPORT | wxLC_SINGLE_SEL);
281
282#if wxUSE_STATUSBAR
283 CreateStatusBar();
284#endif // wxUSE_STATUSBAR
285}
286
287MyFrame::~MyFrame()
288{
289 delete wxLog::SetActiveTarget(m_logOld);
290
291 delete m_imageListNormal;
292 delete m_imageListSmall;
293}
294
295void MyFrame::OnSize(wxSizeEvent& event)
296{
297 DoSize();
298
299 event.Skip();
300}
301
302void MyFrame::DoSize()
303{
304 if ( !m_logWindow )
305 return;
306
307 wxSize size = GetClientSize();
308 wxCoord y = (2*size.y)/3;
309 m_listCtrl->SetSize(0, 0, size.x, y);
310 m_logWindow->SetSize(0, y + 1, size.x, size.y - y);
311}
312
313bool MyFrame::CheckNonVirtual() const
314{
315 if ( !m_listCtrl->HasFlag(wxLC_VIRTUAL) )
316 return true;
317
318 // "this" == whatever
319 wxLogWarning(_T("Can't do this in virtual view, sorry."));
320
321 return false;
322}
323
324void MyFrame::OnQuit(wxCommandEvent& WXUNUSED(event))
325{
326 Close(true);
327}
328
329void MyFrame::OnAbout(wxCommandEvent& WXUNUSED(event))
330{
331 wxMessageDialog dialog(this, _T("List test sample\nJulian Smart (c) 1997"),
332 _T("About list test"), wxOK|wxCANCEL);
333
334 dialog.ShowModal();
335}
336
337void MyFrame::OnFreeze(wxCommandEvent& WXUNUSED(event))
338{
339 wxLogMessage(_T("Freezing the control"));
340
341 m_listCtrl->Freeze();
342}
343
344void MyFrame::OnThaw(wxCommandEvent& WXUNUSED(event))
345{
346 wxLogMessage(_T("Thawing the control"));
347
348 m_listCtrl->Thaw();
349}
350
351void MyFrame::OnToggleLines(wxCommandEvent& event)
352{
353 m_listCtrl->SetSingleStyle(wxLC_HRULES | wxLC_VRULES, event.IsChecked());
354}
355
356void MyFrame::OnToggleMacUseGeneric(wxCommandEvent& event)
357{
358 wxSystemOptions::SetOption(wxT("mac.listctrl.always_use_generic"), event.IsChecked());
359}
360
361void MyFrame::OnGoTo(wxCommandEvent& WXUNUSED(event))
362{
363 long index = 3;
364 m_listCtrl->SetItemState(index, wxLIST_STATE_FOCUSED, wxLIST_STATE_FOCUSED);
365
366 long sel = m_listCtrl->GetNextItem(-1, wxLIST_NEXT_ALL,
367 wxLIST_STATE_SELECTED);
368 if ( sel != -1 )
369 m_listCtrl->SetItemState(sel, 0, wxLIST_STATE_SELECTED);
370 m_listCtrl->SetItemState(index, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED);
371}
372
373void MyFrame::OnFocusLast(wxCommandEvent& WXUNUSED(event))
374{
375 long index = m_listCtrl->GetItemCount() - 1;
376 if ( index == -1 )
377 {
378 return;
379 }
380
381 m_listCtrl->SetItemState(index, wxLIST_STATE_FOCUSED, wxLIST_STATE_FOCUSED);
382 m_listCtrl->EnsureVisible(index);
383}
384
385void MyFrame::OnToggleFirstSel(wxCommandEvent& WXUNUSED(event))
386{
387 m_listCtrl->SetItemState(0, (~m_listCtrl->GetItemState(0, wxLIST_STATE_SELECTED) ) & wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED);
388}
389
390void MyFrame::OnDeselectAll(wxCommandEvent& WXUNUSED(event))
391{
392 if ( !CheckNonVirtual() )
393 return;
394
395 int n = m_listCtrl->GetItemCount();
396 for (int i = 0; i < n; i++)
397 m_listCtrl->SetItemState(i,0,wxLIST_STATE_SELECTED);
398}
399
400void MyFrame::OnSelectAll(wxCommandEvent& WXUNUSED(event))
401{
402 if ( !CheckNonVirtual() )
403 return;
404
405 int n = m_listCtrl->GetItemCount();
406 for (int i = 0; i < n; i++)
407 m_listCtrl->SetItemState(i,wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED);
408}
409
410// ----------------------------------------------------------------------------
411// changing listctrl modes
412// ----------------------------------------------------------------------------
413
414void MyFrame::RecreateList(long flags, bool withText)
415{
416 // we could avoid recreating it if we don't set/clear the wxLC_VIRTUAL
417 // style, but it is more trouble to do it than not
418#if 0
419 if ( !m_listCtrl || ((flags & wxLC_VIRTUAL) !=
420 (m_listCtrl->GetWindowStyleFlag() & wxLC_VIRTUAL)) )
421#endif
422 {
423 delete m_listCtrl;
424
425 m_listCtrl = new MyListCtrl(m_panel, LIST_CTRL,
426 wxDefaultPosition, wxDefaultSize,
427 flags |
428 wxSUNKEN_BORDER | wxLC_EDIT_LABELS);
429
430 switch ( flags & wxLC_MASK_TYPE )
431 {
432 case wxLC_LIST:
433 InitWithListItems();
434 break;
435
436 case wxLC_ICON:
437 InitWithIconItems(withText);
438 break;
439
440 case wxLC_SMALL_ICON:
441 InitWithIconItems(withText, true);
442 break;
443
444 case wxLC_REPORT:
445 if ( flags & wxLC_VIRTUAL )
446 InitWithVirtualItems();
447 else
448 InitWithReportItems();
449 break;
450
451 default:
452 wxFAIL_MSG( _T("unknown listctrl mode") );
453 }
454 }
455
456 DoSize();
457
458 m_logWindow->Clear();
459}
460
461void MyFrame::OnListView(wxCommandEvent& WXUNUSED(event))
462{
463 RecreateList(wxLC_LIST);
464}
465
466void MyFrame::InitWithListItems()
467{
468 for ( int i = 0; i < NUM_ITEMS; i++ )
469 {
470 m_listCtrl->InsertItem(i, wxString::Format(_T("Item %d"), i));
471 }
472}
473
474void MyFrame::OnReportView(wxCommandEvent& WXUNUSED(event))
475{
476 RecreateList(wxLC_REPORT);
477}
478
479void MyFrame::InitWithReportItems()
480{
481 m_listCtrl->SetImageList(m_imageListSmall, wxIMAGE_LIST_SMALL);
482
483 // note that under MSW for SetColumnWidth() to work we need to create the
484 // items with images initially even if we specify dummy image id
485 wxListItem itemCol;
486 itemCol.SetText(_T("Column 1"));
487 itemCol.SetImage(-1);
488 m_listCtrl->InsertColumn(0, itemCol);
489
490 itemCol.SetText(_T("Column 2"));
491 itemCol.SetAlign(wxLIST_FORMAT_CENTRE);
492 m_listCtrl->InsertColumn(1, itemCol);
493
494 itemCol.SetText(_T("Column 3"));
495 itemCol.SetAlign(wxLIST_FORMAT_RIGHT);
496 m_listCtrl->InsertColumn(2, itemCol);
497
498 // to speed up inserting we hide the control temporarily
499 m_listCtrl->Hide();
500
501 wxStopWatch sw;
502
503 for ( int i = 0; i < NUM_ITEMS; i++ )
504 {
505 m_listCtrl->InsertItemInReportView(i);
506 }
507
508 m_logWindow->WriteText(wxString::Format(_T("%d items inserted in %ldms\n"),
509 NUM_ITEMS, sw.Time()));
510 m_listCtrl->Show();
511
512 // we leave all mask fields to 0 and only change the colour
513 wxListItem item;
514 item.m_itemId = 0;
515 item.SetTextColour(*wxRED);
516 m_listCtrl->SetItem( item );
517
518 item.m_itemId = 2;
519 item.SetTextColour(*wxGREEN);
520 m_listCtrl->SetItem( item );
521 item.m_itemId = 4;
522 item.SetTextColour(*wxLIGHT_GREY);
523 item.SetFont(*wxITALIC_FONT);
524 item.SetBackgroundColour(*wxRED);
525 m_listCtrl->SetItem( item );
526
527 m_listCtrl->SetTextColour(*wxBLUE);
528
529 m_listCtrl->SetColumnWidth( 0, wxLIST_AUTOSIZE );
530 m_listCtrl->SetColumnWidth( 1, wxLIST_AUTOSIZE );
531 m_listCtrl->SetColumnWidth( 2, wxLIST_AUTOSIZE );
532
533 // Set images in columns
534 m_listCtrl->SetItemColumnImage(1, 1, 0);
535
536 wxListItem info;
537 info.SetImage(0);
538 info.SetId(3);
539 info.SetColumn(2);
540 m_listCtrl->SetItem(info);
541
542 // test SetItemFont too
543 m_listCtrl->SetItemFont(0, *wxITALIC_FONT);
544}
545
546void MyFrame::InitWithIconItems(bool withText, bool sameIcon)
547{
548 m_listCtrl->SetImageList(m_imageListNormal, wxIMAGE_LIST_NORMAL);
549 m_listCtrl->SetImageList(m_imageListSmall, wxIMAGE_LIST_SMALL);
550
551 for ( int i = 0; i < NUM_ICONS; i++ )
552 {
553 int image = sameIcon ? 0 : i;
554
555 if ( withText )
556 {
557 m_listCtrl->InsertItem(i, wxString::Format(_T("Label %d"), i),
558 image);
559 }
560 else
561 {
562 m_listCtrl->InsertItem(i, image);
563 }
564 }
565}
566
567void MyFrame::OnIconView(wxCommandEvent& WXUNUSED(event))
568{
569 RecreateList(wxLC_ICON, false);
570}
571
572void MyFrame::OnIconTextView(wxCommandEvent& WXUNUSED(event))
573{
574 RecreateList(wxLC_ICON);
575}
576
577void MyFrame::OnSmallIconView(wxCommandEvent& WXUNUSED(event))
578{
579 RecreateList(wxLC_SMALL_ICON, false);
580}
581
582void MyFrame::OnSmallIconTextView(wxCommandEvent& WXUNUSED(event))
583{
584 RecreateList(wxLC_SMALL_ICON);
585}
586
587void MyFrame::OnVirtualView(wxCommandEvent& WXUNUSED(event))
588{
589 m_smallVirtual = false;
590 RecreateList(wxLC_REPORT | wxLC_VIRTUAL);
591}
592
593void MyFrame::OnSmallVirtualView(wxCommandEvent& WXUNUSED(event))
594{
595 m_smallVirtual = true;
596 RecreateList(wxLC_REPORT | wxLC_VIRTUAL);
597}
598
599void MyFrame::InitWithVirtualItems()
600{
601 m_listCtrl->SetImageList(m_imageListSmall, wxIMAGE_LIST_SMALL);
602
603 if ( m_smallVirtual )
604 {
605 m_listCtrl->InsertColumn(0, _T("Animal"));
606 m_listCtrl->InsertColumn(1, _T("Sound"));
607 m_listCtrl->SetItemCount(WXSIZEOF(SMALL_VIRTUAL_VIEW_ITEMS));
608 }
609 else
610 {
611 m_listCtrl->InsertColumn(0, _T("First Column"));
612 m_listCtrl->InsertColumn(1, _T("Second Column"));
613 m_listCtrl->SetColumnWidth(0, 150);
614 m_listCtrl->SetColumnWidth(1, 150);
615 m_listCtrl->SetItemCount(1000000);
616 }
617}
618
619void MyFrame::OnSort(wxCommandEvent& WXUNUSED(event))
620{
621 wxStopWatch sw;
622
623 m_listCtrl->SortItems(MyCompareFunction, 0);
624
625 m_logWindow->WriteText(wxString::Format(_T("Sorting %d items took %ld ms\n"),
626 m_listCtrl->GetItemCount(),
627 sw.Time()));
628}
629
630void MyFrame::OnShowSelInfo(wxCommandEvent& WXUNUSED(event))
631{
632 int selCount = m_listCtrl->GetSelectedItemCount();
633 wxLogMessage(_T("%d items selected:"), selCount);
634
635 // don't show too many items
636 size_t shownCount = 0;
637
638 long item = m_listCtrl->GetNextItem(-1, wxLIST_NEXT_ALL,
639 wxLIST_STATE_SELECTED);
640 while ( item != -1 )
641 {
642 wxLogMessage(_T("\t%ld (%s)"),
643 item, m_listCtrl->GetItemText(item).c_str());
644
645 if ( ++shownCount > 10 )
646 {
647 wxLogMessage(_T("\t... more selected items snipped..."));
648 break;
649 }
650
651 item = m_listCtrl->GetNextItem(item, wxLIST_NEXT_ALL,
652 wxLIST_STATE_SELECTED);
653 }
654}
655
656void MyFrame::OnShowViewRect(wxCommandEvent& WXUNUSED(event))
657{
658 const wxRect r = m_listCtrl->GetViewRect();
659 wxLogMessage("View rect: (%d, %d)-(%d, %d)",
660 r.GetLeft(), r.GetTop(), r.GetRight(), r.GetBottom());
661}
662
663// ----------------------------------------------------------------------------
664// column order tests
665// ----------------------------------------------------------------------------
666
667#ifdef wxHAS_LISTCTRL_COLUMN_ORDER
668
669static wxString DumpIntArray(const wxArrayInt& a)
670{
671 wxString s("{ ");
672 const size_t count = a.size();
673 for ( size_t n = 0; n < count; n++ )
674 {
675 if ( n )
676 s += ", ";
677 s += wxString::Format("%lu", (unsigned long)a[n]);
678 }
679
680 s += " }";
681
682 return s;
683}
684
685void MyFrame::OnSetColOrder(wxCommandEvent& WXUNUSED(event))
686{
687 wxArrayInt order(3);
688 order[0] = 2;
689 order[1] = 0;
690 order[2] = 1;
691 if ( m_listCtrl->SetColumnsOrder(order) )
692 wxLogMessage("Column order set to %s", DumpIntArray(order));
693}
694
695void MyFrame::OnGetColOrder(wxCommandEvent& WXUNUSED(event))
696{
697 // show what GetColumnsOrder() returns
698 const wxArrayInt order = m_listCtrl->GetColumnsOrder();
699 wxString msg = "Columns order: " +
700 DumpIntArray(m_listCtrl->GetColumnsOrder()) + "\n";
701
702 int n;
703 const int count = m_listCtrl->GetColumnCount();
704
705 // show the results of GetColumnOrder() for each column
706 msg += "GetColumnOrder() results:\n";
707 for ( n = 0; n < count; n++ )
708 {
709 msg += wxString::Format(" %2d -> %2d\n",
710 n, m_listCtrl->GetColumnOrder(n));
711 }
712
713 // and the results of GetColumnIndexFromOrder() too
714 msg += "GetColumnIndexFromOrder() results:\n";
715 for ( n = 0; n < count; n++ )
716 {
717 msg += wxString::Format(" %2d -> %2d\n",
718 n, m_listCtrl->GetColumnIndexFromOrder(n));
719 }
720
721 wxLogMessage("%s", msg);
722}
723
724#endif // wxHAS_LISTCTRL_COLUMN_ORDER
725
726void MyFrame::OnShowColInfo(wxCommandEvent& WXUNUSED(event))
727{
728 int count = m_listCtrl->GetColumnCount();
729 wxLogMessage(wxT("%d columns:"), count);
730 for ( int c = 0; c < count; c++ )
731 {
732 wxLogMessage(wxT("\tcolumn %d has width %d"), c,
733 m_listCtrl->GetColumnWidth(c));
734 }
735}
736
737void MyFrame::OnUpdateShowColInfo(wxUpdateUIEvent& event)
738{
739 event.Enable( (m_listCtrl->GetWindowStyleFlag() & wxLC_REPORT) != 0 );
740}
741
742void MyFrame::OnToggleMultiSel(wxCommandEvent& WXUNUSED(event))
743{
744 long flags = m_listCtrl->GetWindowStyleFlag();
745 if ( flags & wxLC_SINGLE_SEL )
746 flags &= ~wxLC_SINGLE_SEL;
747 else
748 flags |= wxLC_SINGLE_SEL;
749
750 m_logWindow->WriteText(wxString::Format(wxT("Current selection mode: %sle\n"),
751 (flags & wxLC_SINGLE_SEL) ? _T("sing") : _T("multip")));
752
753 RecreateList(flags);
754}
755
756void MyFrame::OnUpdateToggleMultiSel(wxUpdateUIEvent& event)
757{
758 event.Check((m_listCtrl->GetWindowStyleFlag() & wxLC_SINGLE_SEL) == 0);
759}
760
761void MyFrame::OnSetFgColour(wxCommandEvent& WXUNUSED(event))
762{
763 m_listCtrl->SetForegroundColour(wxGetColourFromUser(this));
764 m_listCtrl->Refresh();
765}
766
767void MyFrame::OnSetBgColour(wxCommandEvent& WXUNUSED(event))
768{
769 m_listCtrl->SetBackgroundColour(wxGetColourFromUser(this));
770 m_listCtrl->Refresh();
771}
772
773void MyFrame::OnAdd(wxCommandEvent& WXUNUSED(event))
774{
775 m_listCtrl->InsertItem(m_listCtrl->GetItemCount(), _T("Appended item"));
776}
777
778void MyFrame::OnEdit(wxCommandEvent& WXUNUSED(event))
779{
780 long itemCur = m_listCtrl->GetNextItem(-1, wxLIST_NEXT_ALL,
781 wxLIST_STATE_FOCUSED);
782
783 if ( itemCur != -1 )
784 {
785 m_listCtrl->EditLabel(itemCur);
786 }
787 else
788 {
789 m_logWindow->WriteText(_T("No item to edit"));
790 }
791}
792
793void MyFrame::OnDelete(wxCommandEvent& WXUNUSED(event))
794{
795 if ( m_listCtrl->GetItemCount() )
796 {
797 m_listCtrl->DeleteItem(0);
798 }
799 else
800 {
801 m_logWindow->WriteText(_T("Nothing to delete"));
802 }
803}
804
805void MyFrame::OnDeleteAll(wxCommandEvent& WXUNUSED(event))
806{
807 wxStopWatch sw;
808
809 int itemCount = m_listCtrl->GetItemCount();
810
811 m_listCtrl->DeleteAllItems();
812
813 m_logWindow->WriteText(wxString::Format(_T("Deleting %d items took %ld ms\n"),
814 itemCount,
815 sw.Time()));
816}
817
818// MyListCtrl
819
820void MyListCtrl::OnCacheHint(wxListEvent& event)
821{
822 wxLogMessage( wxT("OnCacheHint: cache items %ld..%ld"),
823 event.GetCacheFrom(), event.GetCacheTo() );
824}
825
826void MyListCtrl::SetColumnImage(int col, int image)
827{
828 wxListItem item;
829 item.SetMask(wxLIST_MASK_IMAGE);
830 item.SetImage(image);
831 SetColumn(col, item);
832}
833
834void MyListCtrl::OnColClick(wxListEvent& event)
835{
836 int col = event.GetColumn();
837
838 // set or unset image
839 static bool x = false;
840 x = !x;
841 SetColumnImage(col, x ? 0 : -1);
842
843 wxLogMessage( wxT("OnColumnClick at %d."), col );
844}
845
846void MyListCtrl::OnColRightClick(wxListEvent& event)
847{
848 int col = event.GetColumn();
849 if ( col != -1 )
850 {
851 SetColumnImage(col, -1);
852 }
853
854 // Show popupmenu at position
855 wxMenu menu(wxT("Test"));
856 menu.Append(LIST_ABOUT, _T("&About"));
857 PopupMenu(&menu, event.GetPoint());
858
859 wxLogMessage( wxT("OnColumnRightClick at %d."), event.GetColumn() );
860}
861
862void MyListCtrl::LogColEvent(const wxListEvent& event, const wxChar *name)
863{
864 const int col = event.GetColumn();
865
866 wxLogMessage(wxT("%s: column %d (width = %d or %d)."),
867 name,
868 col,
869 event.GetItem().GetWidth(),
870 GetColumnWidth(col));
871}
872
873void MyListCtrl::OnColBeginDrag(wxListEvent& event)
874{
875 LogColEvent( event, wxT("OnColBeginDrag") );
876
877 if ( event.GetColumn() == 0 )
878 {
879 wxLogMessage(_T("Resizing this column shouldn't work."));
880
881 event.Veto();
882 }
883}
884
885void MyListCtrl::OnColDragging(wxListEvent& event)
886{
887 LogColEvent( event, wxT("OnColDragging") );
888}
889
890void MyListCtrl::OnColEndDrag(wxListEvent& event)
891{
892 LogColEvent( event, wxT("OnColEndDrag") );
893}
894
895void MyListCtrl::OnBeginDrag(wxListEvent& event)
896{
897 const wxPoint& pt = event.m_pointDrag;
898
899 int flags;
900 wxLogMessage( wxT("OnBeginDrag at (%d, %d), item %ld."),
901 pt.x, pt.y, HitTest(pt, flags) );
902}
903
904void MyListCtrl::OnBeginRDrag(wxListEvent& event)
905{
906 wxLogMessage( wxT("OnBeginRDrag at %d,%d."),
907 event.m_pointDrag.x, event.m_pointDrag.y );
908}
909
910void MyListCtrl::OnBeginLabelEdit(wxListEvent& event)
911{
912 wxLogMessage( wxT("OnBeginLabelEdit: %s"), event.m_item.m_text.c_str());
913
914 wxTextCtrl * const text = GetEditControl();
915 if ( !text )
916 {
917 wxLogMessage("BUG: started to edit but no edit control");
918 }
919 else
920 {
921 wxLogMessage("Edit control value: \"%s\"", text->GetValue());
922 }
923}
924
925void MyListCtrl::OnEndLabelEdit(wxListEvent& event)
926{
927 wxLogMessage( wxT("OnEndLabelEdit: %s"),
928 (
929 event.IsEditCancelled() ?
930 wxString("[cancelled]") :
931 event.m_item.m_text
932 ).c_str()
933 );
934}
935
936void MyListCtrl::OnDeleteItem(wxListEvent& event)
937{
938 LogEvent(event, _T("OnDeleteItem"));
939 wxLogMessage( wxT("Number of items when delete event is sent: %d"), GetItemCount() );
940}
941
942void MyListCtrl::OnDeleteAllItems(wxListEvent& event)
943{
944 LogEvent(event, _T("OnDeleteAllItems"));
945}
946
947void MyListCtrl::OnSelected(wxListEvent& event)
948{
949 LogEvent(event, _T("OnSelected"));
950
951 if ( GetWindowStyle() & wxLC_REPORT )
952 {
953 wxListItem info;
954 info.m_itemId = event.m_itemIndex;
955 info.m_col = 1;
956 info.m_mask = wxLIST_MASK_TEXT;
957 if ( GetItem(info) )
958 {
959 wxLogMessage(wxT("Value of the 2nd field of the selected item: %s"),
960 info.m_text.c_str());
961 }
962 else
963 {
964 wxFAIL_MSG(wxT("wxListCtrl::GetItem() failed"));
965 }
966 }
967}
968
969void MyListCtrl::OnDeselected(wxListEvent& event)
970{
971 LogEvent(event, _T("OnDeselected"));
972}
973
974void MyListCtrl::OnActivated(wxListEvent& event)
975{
976 LogEvent(event, _T("OnActivated"));
977}
978
979void MyListCtrl::OnFocused(wxListEvent& event)
980{
981 LogEvent(event, _T("OnFocused"));
982
983 event.Skip();
984}
985
986void MyListCtrl::OnListKeyDown(wxListEvent& event)
987{
988 long item;
989
990 switch ( event.GetKeyCode() )
991 {
992 case 'C': // colorize
993 {
994 wxListItem info;
995 info.m_itemId = event.GetIndex();
996 if ( info.m_itemId == -1 )
997 {
998 // no item
999 break;
1000 }
1001
1002 GetItem(info);
1003
1004 wxListItemAttr *attr = info.GetAttributes();
1005 if ( !attr || !attr->HasTextColour() )
1006 {
1007 info.SetTextColour(*wxCYAN);
1008
1009 SetItem(info);
1010
1011 RefreshItem(info.m_itemId);
1012 }
1013 }
1014 break;
1015
1016 case 'N': // next
1017 item = GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_FOCUSED);
1018 if ( item++ == GetItemCount() - 1 )
1019 {
1020 item = 0;
1021 }
1022
1023 wxLogMessage(_T("Focusing item %ld"), item);
1024
1025 SetItemState(item, wxLIST_STATE_FOCUSED, wxLIST_STATE_FOCUSED);
1026 EnsureVisible(item);
1027 break;
1028
1029 case 'R': // show bounding rectangle
1030 {
1031 item = event.GetIndex();
1032 wxRect r;
1033 if ( !GetItemRect(item, r) )
1034 {
1035 wxLogError(_T("Failed to retrieve rect of item %ld"), item);
1036 break;
1037 }
1038
1039 wxLogMessage(_T("Bounding rect of item %ld is (%d, %d)-(%d, %d)"),
1040 item, r.x, r.y, r.x + r.width, r.y + r.height);
1041 }
1042 break;
1043
1044 case '1': // show sub item bounding rectangle
1045 case '2':
1046 case '3':
1047 case '4': // this column is invalid but we want to test it too
1048 if ( InReportView() )
1049 {
1050 int subItem = event.GetKeyCode() - '1';
1051 item = event.GetIndex();
1052 wxRect r;
1053 if ( !GetSubItemRect(item, subItem, r) )
1054 {
1055 wxLogError(_T("Failed to retrieve rect of item %ld column %d"), item, subItem + 1);
1056 break;
1057 }
1058
1059 wxLogMessage(_T("Bounding rect of item %ld column %d is (%d, %d)-(%d, %d)"),
1060 item, subItem + 1,
1061 r.x, r.y, r.x + r.width, r.y + r.height);
1062 }
1063 break;
1064
1065 case 'U': // update
1066 if ( !IsVirtual() )
1067 break;
1068
1069 if ( m_updated != -1 )
1070 RefreshItem(m_updated);
1071
1072 m_updated = event.GetIndex();
1073 if ( m_updated != -1 )
1074 {
1075 // we won't see changes to this item as it's selected, update
1076 // the next one (or the first one if we're on the last item)
1077 if ( ++m_updated == GetItemCount() )
1078 m_updated = 0;
1079
1080 wxLogMessage("Updating colour of the item %ld", m_updated);
1081 RefreshItem(m_updated);
1082 }
1083 break;
1084
1085 case 'D': // delete
1086 item = GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
1087 while ( item != -1 )
1088 {
1089 DeleteItem(item);
1090
1091 wxLogMessage(_T("Item %ld deleted"), item);
1092
1093 // -1 because the indices were shifted by DeleteItem()
1094 item = GetNextItem(item - 1,
1095 wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
1096 }
1097 break;
1098
1099 case 'I': // insert
1100 if ( GetWindowStyle() & wxLC_REPORT )
1101 {
1102 if ( GetWindowStyle() & wxLC_VIRTUAL )
1103 {
1104 SetItemCount(GetItemCount() + 1);
1105 }
1106 else // !virtual
1107 {
1108 InsertItemInReportView(event.GetIndex());
1109 }
1110 }
1111 //else: fall through
1112
1113 default:
1114 LogEvent(event, _T("OnListKeyDown"));
1115
1116 event.Skip();
1117 }
1118}
1119
1120void MyListCtrl::OnChar(wxKeyEvent& event)
1121{
1122 wxLogMessage(_T("Got char event."));
1123
1124 switch ( event.GetKeyCode() )
1125 {
1126 case 'n':
1127 case 'N':
1128 case 'c':
1129 case 'C':
1130 case 'r':
1131 case 'R':
1132 case 'u':
1133 case 'U':
1134 case 'd':
1135 case 'D':
1136 case 'i':
1137 case 'I':
1138 // these are the keys we process ourselves
1139 break;
1140
1141 default:
1142 event.Skip();
1143 }
1144}
1145
1146void MyListCtrl::OnRightClick(wxMouseEvent& event)
1147{
1148 if ( !event.ControlDown() )
1149 {
1150 event.Skip();
1151 return;
1152 }
1153
1154 int flags;
1155 long subitem;
1156 long item = HitTest(event.GetPosition(), flags, &subitem);
1157
1158 wxString where;
1159 switch ( flags )
1160 {
1161 case wxLIST_HITTEST_ABOVE: where = _T("above"); break;
1162 case wxLIST_HITTEST_BELOW: where = _T("below"); break;
1163 case wxLIST_HITTEST_NOWHERE: where = _T("nowhere near"); break;
1164 case wxLIST_HITTEST_ONITEMICON: where = _T("on icon of"); break;
1165 case wxLIST_HITTEST_ONITEMLABEL: where = _T("on label of"); break;
1166 case wxLIST_HITTEST_ONITEMRIGHT: where = _T("right on"); break;
1167 case wxLIST_HITTEST_TOLEFT: where = _T("to the left of"); break;
1168 case wxLIST_HITTEST_TORIGHT: where = _T("to the right of"); break;
1169 default: where = _T("not clear exactly where on"); break;
1170 }
1171
1172 wxLogMessage(_T("Right double click %s item %ld, subitem %ld"),
1173 where.c_str(), item, subitem);
1174}
1175
1176void MyListCtrl::LogEvent(const wxListEvent& event, const wxChar *eventName)
1177{
1178 wxLogMessage(_T("Item %ld: %s (item text = %s, data = %ld)"),
1179 event.GetIndex(), eventName,
1180 event.GetText().c_str(), event.GetData());
1181}
1182
1183wxString MyListCtrl::OnGetItemText(long item, long column) const
1184{
1185 if ( GetItemCount() == WXSIZEOF(SMALL_VIRTUAL_VIEW_ITEMS) )
1186 {
1187 return SMALL_VIRTUAL_VIEW_ITEMS[item][column];
1188 }
1189 else // "big" virtual control
1190 {
1191 return wxString::Format(_T("Column %ld of item %ld"), column, item);
1192 }
1193}
1194
1195int MyListCtrl::OnGetItemColumnImage(long item, long column) const
1196{
1197 if (!column)
1198 return 0;
1199
1200 if (!(item % 3) && column == 1)
1201 return 0;
1202
1203 return -1;
1204}
1205
1206wxListItemAttr *MyListCtrl::OnGetItemAttr(long item) const
1207{
1208 // test to check that RefreshItem() works correctly: when m_updated is
1209 // set to some item and it is refreshed, we highlight the item
1210 if ( item == m_updated )
1211 {
1212 static wxListItemAttr s_attrHighlight(*wxRED, wxNullColour, wxNullFont);
1213 return &s_attrHighlight;
1214 }
1215
1216 return item % 2 ? NULL : (wxListItemAttr *)&m_attr;
1217}
1218
1219void MyListCtrl::InsertItemInReportView(int i)
1220{
1221 wxString buf;
1222 buf.Printf(_T("This is item %d"), i);
1223 long tmp = InsertItem(i, buf, 0);
1224 SetItemData(tmp, i);
1225
1226 buf.Printf(_T("Col 1, item %d"), i);
1227 SetItem(tmp, 1, buf);
1228
1229 buf.Printf(_T("Item %d in column 2"), i);
1230 SetItem(tmp, 2, buf);
1231}
1232
1233#if USE_CONTEXT_MENU
1234void MyListCtrl::OnContextMenu(wxContextMenuEvent& event)
1235{
1236 if (GetEditControl() == NULL)
1237 {
1238 wxPoint point = event.GetPosition();
1239 // If from keyboard
1240 if ( (point.x == -1) && (point.y == -1) )
1241 {
1242 wxSize size = GetSize();
1243 point.x = size.x / 2;
1244 point.y = size.y / 2;
1245 }
1246 else
1247 {
1248 point = ScreenToClient(point);
1249 }
1250 ShowContextMenu(point);
1251 }
1252 else
1253 {
1254 // the user is editing:
1255 // allow the text control to display its context menu
1256 // if it has one (it has on Windows) rather than display our one
1257 event.Skip();
1258 }
1259}
1260#endif
1261
1262void MyListCtrl::ShowContextMenu(const wxPoint& pos)
1263{
1264 wxMenu menu;
1265
1266 menu.Append(wxID_ABOUT, _T("&About"));
1267 menu.AppendSeparator();
1268 menu.Append(wxID_EXIT, _T("E&xit"));
1269
1270 PopupMenu(&menu, pos.x, pos.y);
1271}