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