2 // This file is part of the aMule Project.
4 // Copyright (c) 2003-2011 aMule Team ( admin@amule.org / http://www.amule.org )
5 // Copyright (c) 2002 Merkur ( devs@emule-project.net / http://www.emule-project.net )
7 // Any parts of this program derived from the xMule, lMule or eMule project,
8 // or contributed by third-party developers are copyrighted by their
11 // This program is free software; you can redistribute it and/or modify
12 // it under the terms of the GNU General Public License as published by
13 // the Free Software Foundation; either version 2 of the License, or
14 // (at your option) any later version.
16 // This program is distributed in the hope that it will be useful,
17 // but WITHOUT ANY WARRANTY; without even the implied warranty of
18 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 // GNU General Public License for more details.
21 // You should have received a copy of the GNU General Public License
22 // along with this program; if not, write to the Free Software
23 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
26 #include <wx/menu.h> // Needed for wxMenu
27 #include <wx/fileconf.h> // Needed for wxConfig
28 #include <wx/tokenzr.h> // Needed for wxStringTokenizer
29 #include <wx/imaglist.h> // Needed for wxImageList
31 #include <common/MuleDebug.h> // Needed for MULE_VALIDATE_
32 #include <common/StringFunctions.h> // Needed for StrToLong
34 #include <common/MenuIDs.h>
36 #include "MuleListCtrl.h" // Interface declarations
37 #include "GetTickCount.h" // Needed for GetTickCount()
38 #include "OtherFunctions.h"
42 #include "pixmaps/sort_dn.xpm"
43 #include "pixmaps/sort_up.xpm"
44 #include "pixmaps/sort_dnx2.xpm"
45 #include "pixmaps/sort_upx2.xpm"
50 const int COL_SIZE_MIN
= 10;
51 #elif defined(__WINDOWS__ ) || defined(__WXMAC__) || defined(__WXCOCOA__)
52 const int COL_SIZE_MIN
= 0;
54 #error Need to define COL_SIZE_MIN for your OS
58 BEGIN_EVENT_TABLE(CMuleListCtrl
, MuleExtern::wxGenericListCtrl
)
59 EVT_LIST_COL_CLICK( -1, CMuleListCtrl::OnColumnLClick
)
60 EVT_LIST_COL_RIGHT_CLICK( -1, CMuleListCtrl::OnColumnRClick
)
61 EVT_LIST_ITEM_SELECTED(-1, CMuleListCtrl::OnItemSelected
)
62 EVT_LIST_ITEM_DESELECTED(-1, CMuleListCtrl::OnItemSelected
)
63 EVT_LIST_DELETE_ITEM(-1, CMuleListCtrl::OnItemDeleted
)
64 EVT_LIST_DELETE_ALL_ITEMS(-1, CMuleListCtrl::OnAllItemsDeleted
)
65 EVT_CHAR( CMuleListCtrl::OnChar
)
66 EVT_MENU_RANGE(MP_LISTCOL_1
, MP_LISTCOL_15
, CMuleListCtrl::OnMenuSelected
)
67 EVT_MOUSEWHEEL(CMuleListCtrl::OnMouseWheel
)
71 //! Shared list of arrow-pixmaps
72 static wxImageList
imgList(16, 16, true, 0);
75 CMuleListCtrl::CMuleListCtrl(wxWindow
*parent
, wxWindowID winid
, const wxPoint
& pos
, const wxSize
& size
, long style
, const wxValidator
& validator
, const wxString
& name
)
76 : MuleExtern::wxGenericListCtrl(parent
, winid
, pos
, size
, style
, validator
, name
)
83 if (imgList
.GetImageCount() == 0) {
84 imgList
.Add(wxBitmap(sort_dn_xpm
));
85 imgList
.Add(wxBitmap(sort_up_xpm
));
86 imgList
.Add(wxBitmap(sort_dnx2_xpm
));
87 imgList
.Add(wxBitmap(sort_upx2_xpm
));
90 // Default sort-order is to sort by the first column (asc).
91 m_sort_orders
.push_back(CColPair(0, 0));
93 SetImageList(&imgList
, wxIMAGE_LIST_SMALL
);
97 CMuleListCtrl::~CMuleListCtrl()
99 if (!m_name
.IsEmpty()) {
104 long CMuleListCtrl::InsertColumn(long col
, const wxString
& heading
, int format
, int width
, const wxString
& name
)
106 if (!name
.IsEmpty()) {
108 // Check for valid names
109 wxASSERT_MSG(name
.Find(wxT(':')) == wxNOT_FOUND
, wxT("Column name \"") + name
+ wxT("\" contains invalid characters!"));
110 wxASSERT_MSG(name
.Find(wxT(',')) == wxNOT_FOUND
, wxT("Column name \"") + name
+ wxT("\" contains invalid characters!"));
112 // Check for uniqueness of names.
113 for (ColNameList::const_iterator it
= m_column_names
.begin(); it
!= m_column_names
.end(); ++it
) {
114 if (name
== it
->name
) {
115 wxFAIL_MSG(wxT("Column name \"") + name
+ wxT("\" is not unique!"));
119 // Insert name at position col.
120 ColNameList::iterator it
= m_column_names
.begin();
121 while (it
!= m_column_names
.end() && it
->index
< col
) {
124 m_column_names
.insert(it
, ColNameEntry(col
, width
, name
));
125 while (it
!= m_column_names
.end()) {
131 return MuleExtern::wxGenericListCtrl::InsertColumn(col
, heading
, format
, width
);
134 void CMuleListCtrl::SaveSettings()
136 wxCHECK_RET(!m_name
.IsEmpty(), wxT("Cannot save settings for unnamed list"));
138 wxConfigBase
* cfg
= wxConfigBase::Get();
140 // Save sorting, column and order
142 for (CSortingList::iterator it
= m_sort_orders
.begin(); it
!= m_sort_orders
.end();) {
143 wxString columnName
= GetColumnName(it
->first
);
144 if (!columnName
.IsEmpty()) {
145 sortOrder
+= columnName
;
146 sortOrder
+= wxT(":");
147 sortOrder
+= it
->second
& SORT_DES
? wxT("1") : wxT("0");
148 sortOrder
+= wxT(":");
149 sortOrder
+= it
->second
& SORT_ALT
? wxT("1") : wxT("0");
150 if (++it
!= m_sort_orders
.end()) {
151 sortOrder
+= wxT(",");
158 cfg
->Write(wxT("/eMule/TableOrdering") + m_name
, sortOrder
);
160 // Save column widths. ATM this is also used to signify hidden columns.
162 for (int i
= 0; i
< GetColumnCount(); ++i
) {
163 wxString columnName
= GetColumnName(i
);
164 if (!columnName
.IsEmpty()) {
165 if (!buffer
.IsEmpty()) {
168 int currentwidth
= GetColumnWidth(i
);
169 int savedsize
= (m_column_sizes
.size() && (i
< (int) m_column_sizes
.size())) ? m_column_sizes
[i
] : 0;
170 buffer
<< columnName
<< wxT(":") << ((currentwidth
> 0) ? currentwidth
: (-1 * savedsize
));
174 cfg
->Write(wxT("/eMule/TableWidths") + m_name
, buffer
);
177 void CMuleListCtrl::ParseOldConfigEntries(const wxString
& sortOrders
, const wxString
& columnWidths
)
179 // Set sort order (including sort column)
180 wxStringTokenizer
tokens(sortOrders
, wxT(","));
181 while (tokens
.HasMoreTokens()) {
182 wxString token
= tokens
.GetNextToken();
185 unsigned long order
= 0;
187 if (token
.BeforeFirst(wxT(' ')).Strip(wxString::both
).ToLong(&column
)) {
188 if (token
.AfterFirst(wxT(' ')).Strip(wxString::both
).ToULong(&order
)) {
189 column
= GetNewColumnIndex(column
);
190 // Sanity checking, to avoid asserting if column count changes.
191 if (column
>= 0 && column
< GetColumnCount()) {
192 // Sanity checking, to avoid asserting if data-format changes.
193 if ((order
& ~SORTING_MASK
) == 0) {
194 // SetSorting will take care of duplicate entries
195 SetSorting(column
, order
);
204 wxStringTokenizer
tokenizer(columnWidths
, wxT(","));
205 while (tokenizer
.HasMoreTokens()) {
206 long idx
= GetNewColumnIndex(counter
++);
207 long width
= StrToLong(tokenizer
.GetNextToken());
209 SetColumnWidth(idx
, width
);
214 void CMuleListCtrl::LoadSettings()
216 wxCHECK_RET(!m_name
.IsEmpty(), wxT("Cannot load settings for unnamed list"));
218 wxConfigBase
* cfg
= wxConfigBase::Get();
220 // Load sort order (including sort-column)
221 m_sort_orders
.clear();
222 wxString sortOrders
= cfg
->Read(wxT("/eMule/TableOrdering") + m_name
, wxEmptyString
);
223 wxString columnWidths
= cfg
->Read(wxT("/eMule/TableWidths") + m_name
, wxEmptyString
);
225 // Prevent sorting from occuring when calling SetSorting
226 MuleListCtrlCompare sortFunc
= m_sort_func
;
229 if (columnWidths
.Find(wxT(':')) == wxNOT_FOUND
) {
230 // Old-style config entries...
231 ParseOldConfigEntries(sortOrders
, columnWidths
);
234 wxStringTokenizer
tokens(sortOrders
, wxT(","));
235 // Sort orders are stored in order primary, secondary, ...
236 // We want to apply them with SetSorting(), so we have to apply them in reverse order,
237 // so that the primary order is applied last and wins.
238 // Read them with tokenizer and store them in a list in reverse order.
239 CStringList tokenList
;
240 while (tokens
.HasMoreTokens()) {
241 tokenList
.push_front(tokens
.GetNextToken());
243 for (CStringList::iterator it
= tokenList
.begin(); it
!= tokenList
.end(); ++it
) {
244 wxString token
= *it
;
245 wxString name
= token
.BeforeFirst(wxT(':'));
246 long order
= StrToLong(token
.AfterFirst(wxT(':')).BeforeLast(wxT(':')));
247 long alt
= StrToLong(token
.AfterLast(wxT(':')));
248 int col
= GetColumnIndex(name
);
250 SetSorting(col
, (order
? SORT_DES
: 0) | (alt
? SORT_ALT
: 0));
255 wxStringTokenizer
tkz(columnWidths
, wxT(","));
256 while (tkz
.HasMoreTokens()) {
257 wxString token
= tkz
.GetNextToken();
258 wxString name
= token
.BeforeFirst(wxT(':'));
259 long width
= StrToLong(token
.AfterFirst(wxT(':')));
260 int col
= GetColumnIndex(name
);
262 if (col
>= (int) m_column_sizes
.size()) {
263 m_column_sizes
.resize(col
+ 1, 0);
265 m_column_sizes
[col
] = abs(width
);
266 SetColumnWidth(col
, (width
> 0) ? width
: 0);
271 // Must have at least one sort-order specified
272 if (m_sort_orders
.empty()) {
273 m_sort_orders
.push_back(CColPair(0, 0));
276 // Re-enable sorting and resort the contents (if any).
277 m_sort_func
= sortFunc
;
282 const wxString
& CMuleListCtrl::GetColumnName(int index
) const
284 for (ColNameList::const_iterator it
= m_column_names
.begin(); it
!= m_column_names
.end(); ++it
) {
285 if (it
->index
== index
) {
292 int CMuleListCtrl::GetColumnDefaultWidth(int index
) const
294 for (ColNameList::const_iterator it
= m_column_names
.begin(); it
!= m_column_names
.end(); ++it
) {
295 if (it
->index
== index
) {
296 return it
->defaultWidth
;
299 return wxLIST_AUTOSIZE
;
302 int CMuleListCtrl::GetColumnIndex(const wxString
& name
) const
304 for (ColNameList::const_iterator it
= m_column_names
.begin(); it
!= m_column_names
.end(); ++it
) {
305 if (it
->name
== name
) {
312 int CMuleListCtrl::GetNewColumnIndex(int oldindex
) const
314 wxStringTokenizer
oldcolumns(GetOldColumnOrder(), wxT(","), wxTOKEN_RET_EMPTY_ALL
);
316 while (oldcolumns
.HasMoreTokens()) {
317 wxString name
= oldcolumns
.GetNextToken();
319 return GetColumnIndex(name
);
326 long CMuleListCtrl::GetInsertPos(wxUIntPtr data
)
328 // Find the best place to position the item through a binary search
330 int Max
= GetItemCount();
332 // Only do this if there are any items and a sorter function.
333 // Otherwise insert at end.
334 if (Max
&& m_sort_func
) {
335 // This search will find the place to position the new item
336 // so it matches current sorting.
337 // The result will be the position the new item will have
338 // after insertion, which is the format expected by the InsertItem function.
339 // If the item equals another item it will be inserted after it.
341 int cur_pos
= ( Max
- Min
) / 2 + Min
;
342 int cmp
= CompareItems(data
, GetItemData(cur_pos
));
344 // Value is less than the one at the current pos
350 } while ((Min
!= Max
));
357 int CMuleListCtrl::CompareItems(wxUIntPtr item1
, wxUIntPtr item2
)
359 CSortingList::const_iterator it
= m_sort_orders
.begin();
360 for (; it
!= m_sort_orders
.end(); ++it
) {
361 int result
= m_sort_func(item1
, item2
, it
->first
| it
->second
);
367 // Ensure that different items are never considered equal.
368 return CmpAny(item1
, item2
);
371 int CMuleListCtrl::SortProc(wxUIntPtr item1
, wxUIntPtr item2
, long data
)
373 MuleSortData
* sortdata
= reinterpret_cast<MuleSortData
*>(data
);
374 const CSortingList
& orders
= sortdata
->m_sort_orders
;
376 CSortingList::const_iterator it
= orders
.begin();
377 for (; it
!= orders
.end(); ++it
) {
378 int result
= sortdata
->m_sort_func(item1
, item2
, it
->first
| it
->second
);
384 // Ensure that different items are never considered equal.
385 return CmpAny(item1
, item2
);
388 void CMuleListCtrl::SortList()
390 if (!IsSorting() && (m_sort_func
&& GetColumnCount())) {
394 MuleSortData
sortdata(m_sort_orders
, m_sort_func
);
396 // In many cases control already has correct order, and sorting causes nasty flickering.
397 // Make one pass through it to check if sorting is necessary at all.
398 int nrItems
= GetItemCount();
400 long lastItemdata
= 0;
402 lastItemdata
= GetItemData(0);
404 for (int i
= 1; i
< nrItems
; i
++) {
405 long nextItemdata
= GetItemData(i
);
406 if (SortProc(lastItemdata
, nextItemdata
, (long int)&sortdata
) > 0) {
407 // ok - we need to sort
411 lastItemdata
= nextItemdata
;
419 // Positions are likely to be invalid after sorting.
422 // Store the current selected items
423 ItemDataList selectedItems
= GetSelectedItems();
424 // Store the focused item
425 long pos
= GetNextItem( -1, wxLIST_NEXT_ALL
, wxLIST_STATE_FOCUSED
);
426 wxUIntPtr focused
= (pos
== -1) ? 0 : GetItemData(pos
);
428 SortItems(SortProc
, (long int)&sortdata
);
430 // Re-select the selected items.
431 for (unsigned i
= 0; i
< selectedItems
.size(); ++i
) {
432 long it_pos
= FindItem(-1, selectedItems
[i
]);
434 SetItemState(it_pos
, wxLIST_STATE_SELECTED
, wxLIST_STATE_SELECTED
);
438 // Set focus on item if any was focused
440 long it_pos
= FindItem(-1, focused
);
442 SetItemState(it_pos
, wxLIST_STATE_FOCUSED
, wxLIST_STATE_FOCUSED
);
450 CMuleListCtrl::ItemDataList
CMuleListCtrl::GetSelectedItems() const
452 // Create the initial vector with as many items as are selected
453 ItemDataList
list( GetSelectedItemCount() );
455 // Current item being located
456 unsigned int current
= 0;
458 long pos
= GetNextItem( -1, wxLIST_NEXT_ALL
, wxLIST_STATE_SELECTED
);
459 while ( pos
!= -1 ) {
460 wxASSERT( current
< list
.size() );
462 list
[ current
++ ] = GetItemData( pos
);
464 pos
= GetNextItem( pos
, wxLIST_NEXT_ALL
, wxLIST_STATE_SELECTED
);
471 void CMuleListCtrl::OnColumnRClick(wxListEvent
& evt
)
476 for ( int i
= 0; i
< GetColumnCount() && i
< 15; ++i
) {
479 menu
.AppendCheckItem(i
+ MP_LISTCOL_1
, item
.GetText() );
480 menu
.Check( i
+ MP_LISTCOL_1
, GetColumnWidth(i
) > COL_SIZE_MIN
);
483 PopupMenu(&menu
, evt
.GetPoint());
487 void CMuleListCtrl::OnMenuSelected( wxCommandEvent
& evt
)
489 unsigned int col
= evt
.GetId() - MP_LISTCOL_1
;
491 if (col
>= m_column_sizes
.size()) {
492 m_column_sizes
.resize(col
+ 1, 0);
495 if (GetColumnWidth(col
) > COL_SIZE_MIN
) {
496 m_column_sizes
[col
] = GetColumnWidth(col
);
497 SetColumnWidth(col
, 0);
499 int oldsize
= m_column_sizes
[col
];
500 SetColumnWidth(col
, (oldsize
> 0) ? oldsize
: GetColumnDefaultWidth(col
));
505 void CMuleListCtrl::OnColumnLClick(wxListEvent
& evt
)
507 // Stop if no sorter-function has been defined
510 } else if (evt
.GetColumn() == -1) {
511 // This happens if a user clicks past the last column header.
515 unsigned sort_order
= 0;
516 if (m_sort_orders
.front().first
== (unsigned)evt
.GetColumn()) {
517 // Same column as before, flip the sort-order
518 sort_order
= m_sort_orders
.front().second
;
520 if (sort_order
& SORT_DES
) {
521 if (AltSortAllowed(evt
.GetColumn())) {
522 sort_order
= (~sort_order
) & SORT_ALT
;
527 sort_order
= SORT_DES
| (sort_order
& SORT_ALT
);
530 m_sort_orders
.pop_front();
532 // Check if the column has already been set
533 CSortingList::iterator it
= m_sort_orders
.begin();
534 for (; it
!= m_sort_orders
.end(); ++it
) {
535 if ((unsigned)evt
.GetColumn() == it
->first
) {
536 sort_order
= it
->second
;
543 SetSorting(evt
.GetColumn(), sort_order
);
547 void CMuleListCtrl::ClearSelection()
549 if (GetSelectedItemCount()) {
550 long index
= GetNextItem(-1, wxLIST_NEXT_ALL
, wxLIST_STATE_SELECTED
);
551 while (index
!= -1) {
552 SetItemState(index
, 0, wxLIST_STATE_SELECTED
);
553 index
= GetNextItem(index
, wxLIST_NEXT_ALL
, wxLIST_STATE_SELECTED
);
559 bool CMuleListCtrl::AltSortAllowed(unsigned WXUNUSED(column
)) const
565 void CMuleListCtrl::SetSorting(unsigned column
, unsigned order
)
567 MULE_VALIDATE_PARAMS(column
< (unsigned)GetColumnCount(), wxT("Invalid column to sort by."));
568 MULE_VALIDATE_PARAMS(!(order
& ~SORTING_MASK
), wxT("Sorting order contains invalid data."));
570 if (!m_sort_orders
.empty()) {
571 SetColumnImage(m_sort_orders
.front().first
, -1);
573 CSortingList::iterator it
= m_sort_orders
.begin();
574 for (; it
!= m_sort_orders
.end(); ++it
) {
575 if (it
->first
== column
) {
576 m_sort_orders
.erase(it
);
582 m_sort_orders
.push_front(CColPair(column
, order
));
584 if (order
& SORT_DES
) {
585 SetColumnImage(column
, (order
& SORT_ALT
) ? 2 : 0);
587 SetColumnImage(column
, (order
& SORT_ALT
) ? 3 : 1);
594 bool CMuleListCtrl::IsItemSorted(long item
)
596 wxCHECK_MSG(m_sort_func
, true, wxT("No sort function specified!"));
597 wxCHECK_MSG((item
>= 0) && (item
< GetItemCount()), true, wxT("Invalid item"));
600 wxUIntPtr data
= GetItemData(item
);
602 // Check that the item before the current item is smaller (or equal)
604 sorted
&= (CompareItems(GetItemData(item
- 1), data
) <= 0);
607 // Check that the item after the current item is greater (or equal)
608 if (sorted
&& (item
< GetItemCount() - 1)) {
609 sorted
&= (CompareItems(GetItemData(item
+ 1), data
) >= 0);
616 void CMuleListCtrl::OnMouseWheel(wxMouseEvent
&event
)
618 // This enables scrolling with the mouse wheel
623 void CMuleListCtrl::SetColumnImage(unsigned col
, int image
)
626 item
.SetMask(wxLIST_MASK_IMAGE
);
627 item
.SetImage(image
);
628 SetColumn(col
, item
);
632 long CMuleListCtrl::CheckSelection(wxMouseEvent
& event
)
636 evt
.m_itemIndex
= HitTest(event
.GetPosition(), flags
);
638 return CheckSelection(evt
);
642 long CMuleListCtrl::CheckSelection(wxListEvent
& event
)
644 long item
= event
.GetIndex();
646 // Check if clicked item is selected. If not, unselect all and select it.
647 if ((item
!= -1) && !GetItemState(item
, wxLIST_STATE_SELECTED
)) {
650 SetItemState(item
, wxLIST_STATE_SELECTED
, wxLIST_STATE_SELECTED
);
657 wxString
CMuleListCtrl::GetTTSText(unsigned item
) const
659 MULE_VALIDATE_PARAMS(item
< (unsigned)GetItemCount(), wxT("Invalid row."));
660 MULE_VALIDATE_STATE((GetWindowStyle() & wxLC_OWNERDRAW
) == 0,
661 wxT("GetTTSText must be overwritten for owner-drawn lists."));
663 return GetItemText(item
);
667 void CMuleListCtrl::OnChar(wxKeyEvent
& evt
)
669 int key
= evt
.GetKeyCode();
671 // We prefer GetKeyCode() to GetUnicodeKey(), in order to work
672 // around a bug in the GetUnicodeKey(), that causes values to
673 // be returned untranslated. This means for instance, that if
674 // shift and '1' is pressed, the result is '1' rather than '!'
675 // (as it should be on my keyboard). This has been reported:
676 // http://sourceforge.net/tracker/index.php?func=detail&aid=1864810&group_id=9863&atid=109863
677 key
= evt
.GetUnicodeKey();
678 } else if (key
>= WXK_START
) {
679 // wxKeycodes are ignored, as they signify events such as the 'home'
680 // button. Unicoded chars are not checked as there is an overlap valid
681 // chars and the wx keycodes.
686 // We wish to avoid handling shortcuts, with the exception of 'select-all'.
687 if (evt
.AltDown() || evt
.ControlDown() || evt
.MetaDown()) {
688 if (evt
.CmdDown() && (evt
.GetKeyCode() == 0x01)) {
689 // Ctrl+a (Command+a on Mac) was pressed, select all items
690 for (int i
= 0; i
< GetItemCount(); ++i
) {
691 SetItemState(i
, wxLIST_STATE_SELECTED
, wxLIST_STATE_SELECTED
);
697 } else if (m_tts_time
+ 1500u < GetTickCount()) {
701 m_tts_time
= GetTickCount();
702 m_tts_text
.Append(wxTolower(key
));
704 // May happen if the subclass does not forward deletion events.
705 // Or rather when single-char-repeated (see below) wraps around, so don't assert.
706 if (m_tts_item
>= GetItemCount()) {
710 unsigned next
= (m_tts_item
== -1) ? 0 : m_tts_item
;
711 for (unsigned i
= 0, count
= GetItemCount(); i
< count
; ++i
) {
712 wxString text
= GetTTSText((next
+ i
) % count
).MakeLower();
714 if (text
.StartsWith(m_tts_text
)) {
717 m_tts_item
= (next
+ i
) % count
;
718 SetItemState(m_tts_item
, wxLIST_STATE_FOCUSED
| wxLIST_STATE_SELECTED
,
719 wxLIST_STATE_FOCUSED
| wxLIST_STATE_SELECTED
);
720 EnsureVisible(m_tts_item
);
726 if (m_tts_item
!= -1) {
727 // Crop the string so that it matches the old item (avoid typos).
728 wxString text
= GetTTSText(m_tts_item
).MakeLower();
730 // If the last key didn't result in a hit, then we skip the event.
731 if (!text
.StartsWith(m_tts_text
)) {
732 if ((m_tts_text
.Length() == 2) && (m_tts_text
[0] == m_tts_text
[1])) {
733 // Special case, single-char, repeated. This allows toggeling
734 // between items starting with a specific letter.
736 // Increment, so the next will be selected (or wrap around).
740 m_tts_text
.RemoveLast();
750 void CMuleListCtrl::OnItemSelected(wxListEvent
& evt
)
753 // Selection/Deselection that happened while sorting.
756 // We reset the current TTS session if the user manually changes the selection
757 if (m_tts_item
!= evt
.GetIndex()) {
760 // The item is changed so that the next TTS starts from the selected item.
761 m_tts_item
= evt
.GetIndex();
768 void CMuleListCtrl::OnItemDeleted(wxListEvent
& evt
)
770 if (evt
.GetIndex() <= m_tts_item
) {
778 void CMuleListCtrl::OnAllItemsDeleted(wxListEvent
& evt
)
786 void CMuleListCtrl::ResetTTS()
792 wxString
CMuleListCtrl::GetOldColumnOrder() const
794 return wxEmptyString
;
796 // File_checked_for_headers