1
2 #include <wx/colour.h>
3 #include <wx/log.h>
4 #include <wx/wupdlock.h>
5
6 #include "nonportable.h" //pulls in the SL_DUMMY_COL define if applicable
7 #include "settings.h"
8 #include "defines.h"
9 #include "iconimagelist.h"
10 #include "utils/customdialogs.h"
11 #include "uiutils.h"
12 #include "utils/sltipwin.h"
13 #include <lslutils/misc.h>
14 #include "utils/controls.h"
15
16 #include <algorithm>
17
18 #ifdef HAVE_WX29
wxBEGIN_EVENT_TABLE_TEMPLATE2(CustomVirtListCtrl,wxListCtrl,T,L)19 wxBEGIN_EVENT_TABLE_TEMPLATE2(CustomVirtListCtrl, wxListCtrl, T,L)
20 #else
21 BEGIN_EVENT_TABLE_TEMPLATE2(CustomVirtListCtrl, wxListCtrl, T,L)
22 #endif
23 #if wxUSE_TIPWINDOW
24 EVT_MOTION(CustomVirtListCtrl::OnMouseMotion)
25 EVT_TIMER(IDD_TIP_TIMER, CustomVirtListCtrl::OnTimer)
26 #endif
27 EVT_LIST_COL_BEGIN_DRAG(wxID_ANY, CustomVirtListCtrl::OnStartResizeCol)
28 EVT_LIST_COL_END_DRAG(wxID_ANY, CustomVirtListCtrl::OnEndResizeCol)
29 EVT_LEAVE_WINDOW(CustomVirtListCtrl::noOp)
30 EVT_LIST_ITEM_SELECTED ( wxID_ANY, CustomVirtListCtrl::OnSelected )
31 EVT_LIST_ITEM_DESELECTED ( wxID_ANY, CustomVirtListCtrl::OnDeselected )
32 EVT_LIST_DELETE_ITEM ( wxID_ANY, CustomVirtListCtrl::OnDeselected )
33 EVT_LIST_COL_CLICK ( wxID_ANY, CustomVirtListCtrl::OnColClick )
34 END_EVENT_TABLE()
35
36
37 template < class T, class L >
38 CustomVirtListCtrl<T,L>::CustomVirtListCtrl(wxWindow* parent, wxWindowID id, const wxPoint& pt, const wxSize& sz,
39 long style, const wxString& name, unsigned int sort_criteria_count,
40 CompareFunction func, bool highlight, UserActions::ActionType hlaction, bool periodic_sort, unsigned int periodic_sort_interval ):
41 wxListCtrl(parent, id, pt, sz, style | wxLC_VIRTUAL),
42 m_tiptimer(this, IDD_TIP_TIMER),
43 m_sort_timer(this, IDD_SORT_TIMER),
44 m_tiptext(wxEmptyString),
45 #if wxUSE_TIPWINDOW
46 m_tipwindow( 0 ),
47 m_controlPointer( 0 ),
48 #endif
49 m_columnCount( 0 ),
50 m_selected_index(-1),
51 m_prev_selected_index(-1),
52 m_last_mouse_pos( wxPoint(-1,-1) ),
53 m_name(name),
54 m_highlight(highlight),
55 m_highlightAction(hlaction),
56 m_bg_color( GetBackgroundColour() ),
57 m_dirty_sort(false),
58 m_sort_criteria_count( sort_criteria_count ),
59 m_comparator( this,m_sortorder, func ),
60 m_periodic_sort_timer_id( wxNewId() ),
61 m_periodic_sort_timer( this, m_periodic_sort_timer_id ),
62 m_periodic_sort( periodic_sort ),
63 m_periodic_sort_interval( periodic_sort_interval )
64 {
65 //dummy init , will later be replaced with loading from settings
66 for ( unsigned int i = 0; i < m_columnCount; ++i) {
67 m_column_map[i] = i;
68
69 }
70
71 SetImageList( &icons(), wxIMAGE_LIST_NORMAL );
72 SetImageList( &icons(), wxIMAGE_LIST_SMALL );
73 m_sortorder = sett().GetSortOrder( name );
74
75 StartTimer();
76 Connect( ListctrlDoSortEventType, wxCommandEventHandler( ThisType::OnSortEvent ), NULL, this );
77 }
78
79 template < class T, class L >
OnQuit(wxCommandEvent &)80 void CustomVirtListCtrl<T,L>::OnQuit( wxCommandEvent& /*data*/ )
81 {
82 m_periodic_sort_timer.Stop();
83 m_dirty_sort = false;
84 Clear();
85 }
86
87 template < class T, class L >
StartTimer()88 void CustomVirtListCtrl<T,L>::StartTimer()
89 {
90 if ( m_periodic_sort ) {
91 Connect( m_periodic_sort_timer_id, wxTimerEvent().GetEventType(), wxTimerEventHandler( ThisType::OnPeriodicSort ) );
92 #ifndef NDEBUG
93 bool started =
94 #endif
95 m_periodic_sort_timer.Start( m_periodic_sort_interval );
96 assert( started );
97 }
98 }
99
100 template < class T, class L >
StopTimer()101 void CustomVirtListCtrl<T,L>::StopTimer()
102 {
103 m_periodic_sort_timer.Stop();
104 Disconnect( m_periodic_sort_timer_id, wxTimerEvent().GetEventType(), wxTimerEventHandler( ThisType::OnPeriodicSort ) );
105 }
106
107 template < class T, class L >
~CustomVirtListCtrl()108 CustomVirtListCtrl<T,L>::~CustomVirtListCtrl()
109 {
110 StopTimer();
111 sett().SetSortOrder( m_name, m_sortorder );
112 }
113
114 template < class T, class L >
AddColumn(long i,int width,const wxString & label,const wxString & tip,bool modifiable)115 void CustomVirtListCtrl<T,L>::AddColumn(long i, int width, const wxString& label, const wxString& tip, bool modifiable)
116 {
117 m_columnCount++;
118 wxListCtrl::InsertColumn( i, label, wxLIST_FORMAT_LEFT, width);
119 colInfo temp( i, label, tip, modifiable, width );
120 m_colinfovec.push_back( temp );
121 }
122
123
124
125 template < class T, class L >
SaveSelection()126 void CustomVirtListCtrl<T,L>::SaveSelection()
127 {
128 ResetSelection();
129
130 long item = -1;
131 while ( true ) {
132 item = GetNextItem(item, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
133 if ( item == -1 )
134 break;
135 m_selected_data.push_back( m_data[item] );
136 }
137
138 }
139
140 template < class T, class L >
RestoreSelection()141 void CustomVirtListCtrl<T,L>::RestoreSelection()
142 {
143 while ( !m_selected_data.empty() ) {
144 SelectedDataType data = m_selected_data.back();
145 m_selected_data.pop_back();
146 int idx = GetIndexFromData( data );
147 SetItemState( idx, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED );
148 }
149 }
150
151 template < class T, class L >
ResetSelection()152 void CustomVirtListCtrl<T,L>::ResetSelection()
153 {
154 m_selected_data.clear();
155 }
156
157 template < class T, class L >
OnSelected(wxListEvent & event)158 void CustomVirtListCtrl<T,L>::OnSelected( wxListEvent& event )
159 {
160 m_selected_index = event.GetIndex();
161 event.Skip();
162 }
163
164 template < class T, class L >
OnDeselected(wxListEvent & event)165 void CustomVirtListCtrl<T,L>::OnDeselected( wxListEvent& event )
166 {
167 if ( m_selected_index == event.GetIndex() )
168 m_selected_index = -1;
169 }
170
171 template < class T, class L >
GetSelectedIndex() const172 long CustomVirtListCtrl<T,L>::GetSelectedIndex() const
173 {
174 return m_selected_index ;
175 }
176
177 template < class T, class L >
SelectAll()178 void CustomVirtListCtrl<T,L>::SelectAll()
179 {
180 for (long i = 0; i < GetItemCount() ; i++ ) {
181 SetItemState( i, wxLIST_STATE_SELECTED, -1 );
182 }
183 }
184
185 template < class T, class L >
SelectNone()186 void CustomVirtListCtrl<T,L>::SelectNone()
187 {
188 for (long i = 0; i < GetItemCount() ; i++ ) {
189 SetItemState( i, wxLIST_STATE_DONTCARE, -1 );
190 }
191 }
192 template < class T, class L >
SelectInverse()193 void CustomVirtListCtrl<T,L>::SelectInverse()
194 {
195 for (long i = 0; i < GetItemCount() ; i++ ) {
196 int state = GetItemState( i, -1 );
197 state = ( state == wxLIST_STATE_DONTCARE ? wxLIST_STATE_SELECTED : wxLIST_STATE_DONTCARE );
198 SetItemState( i, state, -1 );
199 }
200 }
201
202 template < class T, class L >
SetSelectedIndex(const long newindex)203 void CustomVirtListCtrl<T,L>::SetSelectedIndex(const long newindex)
204 {
205 m_selected_index = newindex;
206 }
207
208 template < class T, class L >
RefreshVisibleItems()209 void CustomVirtListCtrl<T,L>::RefreshVisibleItems()
210 {
211 if ( m_data.empty() )
212 return;
213 const long topItemIndex = GetTopItem();
214 const long range = topItemIndex + GetCountPerPage();
215 RefreshItems( topItemIndex, LSL::Util::Clamp( range, topItemIndex, (long) m_data.size() -1 ) );
216 }
217
218 template < class T, class L >
OnTimer(wxTimerEvent &)219 void CustomVirtListCtrl<T,L>::OnTimer(wxTimerEvent& /*unused*/ )
220 {
221 #if wxUSE_TIPWINDOW
222
223 if (!m_tiptext.empty()) {
224 m_tipwindow = new SLTipWindow(this, m_tiptext);
225 m_controlPointer = &m_tipwindow;
226 m_tipwindow->SetTipWindowPtr((wxTipWindow**)m_controlPointer);
227 m_tipwindow->SetBoundingRect(wxRect(1,1,50,50));
228 m_tiptext = wxEmptyString;
229 m_tiptimer.Start(m_tooltip_duration, wxTIMER_ONE_SHOT);
230 } else {
231 m_tiptext = wxEmptyString;
232 m_tiptimer.Stop();
233 if (m_controlPointer!= 0 && *m_controlPointer!= 0) {
234 m_tipwindow->Close();
235 m_tipwindow = 0;
236 }
237 }
238
239 #endif
240 }
241
242 template < class T, class L >
OnMouseMotion(wxMouseEvent & event)243 void CustomVirtListCtrl<T,L>::OnMouseMotion(wxMouseEvent& event)
244 {
245 m_sort_timer.Stop();
246 m_sort_timer.Start( m_sort_block_time , wxTIMER_ONE_SHOT );
247 #if wxUSE_TIPWINDOW
248 //we don't want to display the tooltip again until mouse has moved
249 if ( m_last_mouse_pos == event.GetPosition() )
250 return;
251
252 m_last_mouse_pos = event.GetPosition();
253
254 if (event.Leaving()) {
255 m_tiptext = wxEmptyString;
256 if (m_tipwindow) {
257 m_tipwindow->Close();
258 m_tipwindow = 0;
259 }
260 m_tiptimer.Stop();
261 } else {
262 if ( m_tiptimer.IsRunning() ) {
263 m_tiptimer.Stop();
264 }
265
266 wxPoint position = event.GetPosition();
267
268 int flag = wxLIST_HITTEST_ONITEM;
269
270 #ifdef HAVE_WX28
271 long subItem;
272 long item_hit = HitTest(position, flag, &subItem);
273 #else
274 long item_hit = HitTest(position, flag);
275 #endif
276 if (item_hit != wxNOT_FOUND && item_hit>=0 && item_hit<GetItemCount()) {
277 // we don't really need to recover from this if it fails
278 try {
279 SetTipWindowText(item_hit,m_last_mouse_pos);
280 m_tiptimer.Start(m_tooltip_delay, wxTIMER_ONE_SHOT);
281 } catch ( ... ) {
282 wxLogWarning( _T("Exception setting tooltip") );
283 }
284 }
285 }
286 #endif
287 }
288
289 template < class T, class L >
SetTipWindowText(const long,const wxPoint & position)290 void CustomVirtListCtrl<T,L>::SetTipWindowText( const long /*unused*/ , const wxPoint& position)
291 {
292 int column = getColumnFromPosition(position);
293 if (column >= int(m_colinfovec.size()) || column < 0) {
294 m_tiptext = wxEmptyString;
295 } else {
296 m_tiptimer.Start(m_tooltip_delay, wxTIMER_ONE_SHOT);
297 m_tiptext = TE(m_colinfovec[column].tip);
298 }
299 }
300
301 template < class T, class L >
getColumnFromPosition(wxPoint pos)302 int CustomVirtListCtrl<T,L>::getColumnFromPosition(wxPoint pos)
303 {
304 int x_pos = 0;
305 for (int i = 0; i < int(m_colinfovec.size()); ++i) {
306 x_pos += GetColumnWidth(i);
307 if (pos.x < x_pos)
308 return i;
309 }
310 return -1;
311 }
312
313 template < class T, class L >
OnStartResizeCol(wxListEvent & event)314 void CustomVirtListCtrl<T,L>::OnStartResizeCol(wxListEvent& event)
315 {
316 if (!m_colinfovec[event.GetColumn()].can_resize)
317 event.Veto();
318 }
319
320 template < class T, class L >
OnEndResizeCol(wxListEvent & event)321 void CustomVirtListCtrl<T,L>::OnEndResizeCol(wxListEvent& event)
322 {
323 int column = event.GetColumn();
324 int new_size = GetColumnWidth( column );
325 sett().SetColumnWidth( m_name, column, new_size );
326 sett().SaveSettings();
327
328 //let the event go further
329 event.Skip();
330 }
331
332 template < class T, class L >
SetColumnWidth(int col,int & width)333 bool CustomVirtListCtrl<T,L>::SetColumnWidth(int col, int& width)
334 {
335 assert( col < (long)m_columnCount );
336 assert( col >= 0 );
337 if ( sett().GetColumnWidth( m_name, col) != Settings::columnWidthUnset) {
338 width = sett().GetColumnWidth( m_name, col);
339 return wxListCtrl::SetColumnWidth( col, width );
340 } else {
341 sett().SetColumnWidth( m_name, col, width );
342 return wxListCtrl::SetColumnWidth( col, width );
343 }
344 }
345
346 template < class T, class L >
noOp(wxMouseEvent & event)347 void CustomVirtListCtrl<T,L>::noOp(wxMouseEvent& event)
348 {
349 m_tiptext = wxEmptyString;
350 // m_tiptimer.Stop();
351 // if (m_controlPointer!= 0 && *m_controlPointer!= 0)
352 // {
353 // m_tipwindow->Close();
354 // m_tipwindow = 0;
355 // }
356 event.Skip();
357 }
358
359 template < class T, class L >
HighlightItemUser(const wxString & name) const360 wxListItemAttr* CustomVirtListCtrl<T,L>::HighlightItemUser( const wxString& name ) const
361 {
362 static wxListItemAttr att;
363 if ( m_highlight && useractions().DoActionOnUser( m_highlightAction, name ) ) {
364 att.SetBackgroundColour( useractions().GetGroupHLColor( useractions().GetGroupOfUser( name ) ) );
365 return &att;
366 } else
367 return NULL;
368 }
369
370 template < class T, class L >
SetHighLightAction(UserActions::ActionType action)371 void CustomVirtListCtrl<T,L>::SetHighLightAction( UserActions::ActionType action )
372 {
373 m_highlightAction = action;
374 }
375
376 template < class T, class L >
MarkDirtySort()377 void CustomVirtListCtrl<T,L>::MarkDirtySort()
378 {
379 m_dirty_sort = true;
380 }
381
382 template < class T, class L >
CancelTooltipTimer()383 void CustomVirtListCtrl<T,L>::CancelTooltipTimer()
384 {
385 m_tiptimer.Stop();
386 }
387
388 template < class T, class L >
PopupMenu(wxMenu * menu,const wxPoint & pos)389 bool CustomVirtListCtrl<T,L>::PopupMenu(wxMenu* menu, const wxPoint& pos )
390 {
391 CancelTooltipTimer();
392 return wxListCtrl::PopupMenu( menu, pos );
393 }
394
395 template < class T, class L >
SortList(bool force)396 void CustomVirtListCtrl<T,L>::SortList( bool force )
397 {
398 if ( ( m_sort_timer.IsRunning() || !m_dirty_sort ) && !force ) {
399 return;
400 }
401
402 {
403 wxWindowUpdateLocker upd( this );
404 SelectionSaver<ThisType>(*this);
405 Sort();
406 m_dirty_sort = false;
407 }
408 RefreshVisibleItems();//needs to be out of locker scope
409 }
410
411 template < class T, class L >
Clear()412 void CustomVirtListCtrl<T,L>::Clear()
413 {
414 m_data.clear();
415 SetItemCount( 0 );
416 ResetSelection();
417 RefreshVisibleItems();
418 }
419
420 template < class T, class L >
GetDataFromIndex(const long index)421 typename CustomVirtListCtrl<T,L>::DataType CustomVirtListCtrl<T,L>::GetDataFromIndex ( const long index )
422 {
423 return m_data[index];
424 }
425
426 template < class T, class L >
GetDataFromIndex(const long index) const427 const typename CustomVirtListCtrl<T,L>::DataType CustomVirtListCtrl<T,L>::GetDataFromIndex ( const long index ) const
428 {
429 return m_data[index];
430 }
431
432 template < class T, class L >
GetSelectedData()433 typename CustomVirtListCtrl<T,L>::DataType CustomVirtListCtrl<T,L>::GetSelectedData()
434 {
435 return GetDataFromIndex( m_selected_index );
436 }
437
438 template < class T, class L >
ResetColumnSizes()439 void CustomVirtListCtrl<T,L>::ResetColumnSizes()
440 {
441 typename colInfoVec::const_iterator it = m_colinfovec.begin();
442 for ( ; it != m_colinfovec.end(); ++it ) {
443 int width = it->size;
444 SetColumnWidth( it->col_num, width );
445 }
446 }
447
448 template < class T, class L >
OnColClick(wxListEvent & event)449 void CustomVirtListCtrl<T,L>::OnColClick( wxListEvent& event )
450 {
451 if ( event.GetColumn() == -1 )
452 return;
453
454 const int evt_col = event.GetColumn();
455
456
457 m_sort_timer.Stop();//otherwise sorting will be way delayed
458
459 int old_sort_col = m_sortorder[0].col;
460
461 wxListItem col;
462 GetColumn( m_sortorder[0].col, col );
463 col.SetImage( icons().ICON_NONE );
464 SetColumn( m_sortorder[0].col, col );
465
466 unsigned int i = 0;
467 SortOrder::const_iterator it = m_sortorder.begin();
468 for ( ; it != m_sortorder.begin(); ++i, ++it ) {
469 if ( m_sortorder[i].col == evt_col )
470 break;
471 }
472
473 // for ( ; m_sortorder[i].col != event.GetColumn() && i < 4; ++i ) {}
474
475 i = LSL::Util::Clamp( i, (unsigned int)0, m_sort_criteria_count );
476
477 for ( ; i > 0; i--) {
478 m_sortorder[i] = m_sortorder[i-1];
479 }
480
481 m_sortorder[0].col = evt_col;
482 m_sortorder[0].direction *= -1;
483
484
485 GetColumn( m_sortorder[0].col, col );
486 //col.SetImage( ( m_sortorder[0].direction )?ICON_UP:ICON_DOWN );
487 col.SetImage( ( m_sortorder[0].direction > 0 )?icons().ICON_UP:icons().ICON_DOWN );
488 SetColumn( m_sortorder[0].col, col );
489
490 if ( (old_sort_col != m_sortorder[0].col) || m_dirty_sort) {
491 SortList( true );
492 } else { // O(n) instead of guaranteed worst case O(n*n)
493 ReverseOrder();
494 }
495 }
496
497 template < class T, class L >
OnSortEvent(wxCommandEvent & evt)498 void CustomVirtListCtrl<T,L>::OnSortEvent( wxCommandEvent& evt )
499 {
500 bool force = evt.GetInt() != 0;
501 SortList( force );
502 }
503
504 template < class T, class L >
GetColumn(int col,wxListItem & item) const505 bool CustomVirtListCtrl<T,L>::GetColumn(int col, wxListItem& item) const
506 {
507 return wxListCtrl::GetColumn( col, item );
508 }
509
510 template < class T, class L >
SetColumn(int col,wxListItem & item)511 bool CustomVirtListCtrl<T,L>::SetColumn(int col, wxListItem& item)
512 {
513 return wxListCtrl::SetColumn( col, item );
514 }
515
516 template < class T, class L >
ReverseOrder()517 void CustomVirtListCtrl<T,L>::ReverseOrder()
518 {
519 SaveSelection();
520 std::reverse( m_data.begin(), m_data.end() );
521 RefreshVisibleItems();
522 RestoreSelection();
523 }
524
525 template < class T, class L >
AddItem(const T & item)526 bool CustomVirtListCtrl<T,L>::AddItem( const T& item )
527 {
528 if ( GetIndexFromData( item ) != -1 )
529 return false;
530
531 m_data.push_back( item );
532 SetItemCount( m_data.size() );
533 RefreshItem( m_data.size() - 1 );
534 MarkDirtySort();
535 return true;
536 }
537
538 template < class T, class L >
RemoveItem(const T & item)539 bool CustomVirtListCtrl<T,L>::RemoveItem( const T& item )
540 {
541 int index = GetIndexFromData( item );
542 if ( (index >= 0) && (index<(long)m_data.size()) ) {
543 SelectionSaver<ThisType>(*this);
544 m_data.erase( m_data.begin() + index );
545 SetItemCount( m_data.size() );
546 if (index>(long)m_data.size()-1)
547 index--;
548 RefreshItems( index, m_data.size() -1 );
549 return true;
550 }
551 return false;
552 }
553
554 template < class T, class L >
OnGetItemText(long item,long column) const555 wxString CustomVirtListCtrl<T,L>::OnGetItemText(long item, long column) const
556 {
557 //assert( item < (long)m_data.size() );
558 //assert( column < m_columnCount );
559 column = LSL::Util::Clamp( column, long(0), long(m_columnCount) );
560 return asImp().GetItemText(item, column);
561 }
562
563 template < class T, class L >
OnGetItemColumnImage(long item,long column) const564 int CustomVirtListCtrl<T,L>::OnGetItemColumnImage(long item, long column) const
565 {
566 return asImp().GetItemColumnImage(item, column);
567 }
568
569 template < class T, class L >
OnGetItemAttr(long item) const570 wxListItemAttr* CustomVirtListCtrl<T,L>::OnGetItemAttr(long item) const
571 {
572 return asImp().GetItemAttr(item);
573 }
574
575 template < class T, class L >
OnPeriodicSort(wxTimerEvent &)576 void CustomVirtListCtrl<T,L>::OnPeriodicSort( wxTimerEvent& /*unused*/ )
577 {
578 SortList();
579 }
580