'****************************************************************************
'* Copyright (C) 2004 Peter Mortensen and Matthias Mann *
'* This file is part of MSQuant. *
'* *
'* MSQuant is distributed under the terms of *
'* the GNU General Public License. See src/COPYING.TXT or *
'* <http://www.gnu.org/licenses/gpl.txt> for details. *
'* *
'* MSQuant is free software; you can redistribute it *
'* and/or modify it under the terms of the GNU *
'* General Public License as published by the Free *
'* Software Foundation; either version 2 of the *
'* License, or (at your option) any later version. *
'* *
'* MSQuant is distributed in the hope that it will be *
'* useful, but WITHOUT ANY WARRANTY; without even the *
'* implied warranty of MERCHANTABILITY or FITNESS FOR *
'* A PARTICULAR PURPOSE. See the GNU General Public *
'* License for more details. *
'* *
'* You should have received a copy of the GNU General *
'* Public License along with MSQuant; if not, write to *
'* the Free Software Foundation, Inc., 59 Temple *
'* Place, Suite 330, Boston, MA 02111-1307 USA *
'* *
'* Purpose: provides sort class to be used to sort a ListView - by *
'* setting the property ListViewItemSorter to instance of *
'* this class. Clients specify a logical column, actual column *
'* is determined by an instance of a listviewColumnMapper. *
'* Clients can specify secondary columns/keys for the sort and *
'* specify datatype and direction (ascending/descending) for each *
'* key *
'* *
'****************************************************************************
'****************************************************************************
'* CEBI *
'* Software Development Group *
'* Peter Mortensen *
'* E-mail: NUKESPAMMERSdrmortensen@get2netZZZZZZ.dk *
'* WWW: http://www.cebi.sdu.dk/ *
'* *
'* Program for post-processing of result from search in mass *
'* spectrometric data. *
'* *
'* FILENAME: listviewColumnMapper.vb *
'* TYPE: VISUAL_BASIC *
'* *
'* CREATED: PM 2006-02-23 Vrs 1.0. *
'* UPDATED: PM 2005-xx-xx *
'* *
'****************************************************************************
'Future:
' 1. What if the column for a key does not exist in the current ListViewItem?
Option Strict On
Option Explicit On
Imports System.Collections.Generic 'For Dictionary and List.
'****************************************************************************
'* <placeholder for header> *
'****************************************************************************
Namespace CEBI_generalGUI 'Better name? Things in this name space are
' application independent stuff (bug fixes, extensions,
' generalisations) that could be used elsewhere. It is
' not even mass spec specific.
Public Enum sortingDirectionEnum
enumAscendingSort = 293
enumDescendingSort
End Enum 'sortingDirectionEnum
Public Enum datatypeEnum
enumText = 293
enumInteger
enumDecimalNumber
End Enum 'sortingDirectionEnum
'Changed PM_SORT_BY_SELECTION_AND_CHECKED 2008-12-09
Public Enum specialSortKeyEnum
enumBySelected = 2309
enumByChecked
End Enum 'sortingDirectionEnum
'****************************************************************************
'* <placeholder for header> *
'****************************************************************************
Public Class ListViewColumnSorter
Implements IComparer
Private Structure sortKeyStructure
Dim columnID As Integer
Dim physicalColumn As Integer 'Helper field. For caching.
' Value derived from columnID.
Dim sortDirection As sortingDirectionEnum
Dim dataType As datatypeEnum
'For convenience initialisation.
Public Sub New( _
ByRef aColumnID As Integer, _
ByVal aDataType As datatypeEnum, _
ByVal aSortDirection As sortingDirectionEnum)
columnID = aColumnID
dataType = aDataType
sortDirection = aSortDirection
End Sub 'New
End Structure 'sortKeyStructure
'Changed PM_COLUMNSORT_BY_KEYBOARD_FLATTEN 2008-11-08
'Private Const MENUPREFIX As String = "by "
Private Const MENUPREFIX As String = "Sort by "
'Changed PM_COLUMNSORT_BY_KEYBOARD 2008-11-07
Private Structure columnSortInfoStruct
Dim keyList As List(Of sortKeyStructure)
Dim useMenuKeyboardShortCut As Boolean
End Structure 'columnSortInfoStruct
Private mColumnMapper As listviewColumnMapper
'Changed PM_TYPESAFE_HASH 2008-11-06
'Private mCurrentKeyList As ArrayList
Private mCurrentKeyList3 As List(Of sortKeyStructure)
'Changed PM_COLUMNSORT_BY_KEYBOARD 2008-11-07
''Changed PM_REFACTOR 2008-11-06
'Private mColumnSortSpecificationHash As Dictionary( _
' Of Integer, List(Of sortKeyStructure))
Private mColumnSortSpecificationHash As Dictionary( _
Of Integer, columnSortInfoStruct)
Private mLatestPrimaryKey As Integer
'****************************************************************************
'* SUBROUTINE NAME: New *
'd$ <summary>Constructor</summary>
Public Sub New(ByRef anInColumnMapper As listviewColumnMapper)
MyBase.New() 'Is this necessary? Yes!
Trace.Assert(Not anInColumnMapper Is Nothing, _
"PIL ASSERT. anInColumnMapper is Nothing.")
mColumnMapper = anInColumnMapper
mColumnSortSpecificationHash = New Dictionary( _
Of Integer, columnSortInfoStruct)
mLatestPrimaryKey = -1 'Mark as uninitialised. To detect client errors.
End Sub 'Constructor.
'****************************************************************************
'* <placeholder for header> *
'****************************************************************************
Private Function reverseSortDirection( _
ByVal aSortingDirection3 As sortingDirectionEnum) _
As sortingDirectionEnum
Dim toReturn As sortingDirectionEnum = _
sortingDirectionEnum.enumAscendingSort 'Default if fall-through below.
Select Case aSortingDirection3
Case sortingDirectionEnum.enumAscendingSort
toReturn = sortingDirectionEnum.enumDescendingSort
Case sortingDirectionEnum.enumDescendingSort
toReturn = sortingDirectionEnum.enumAscendingSort
Case Else
Trace.Assert(False, _
"PIL ASSERT. Select Case never fall-through")
End Select
Return toReturn
End Function 'reverseSortDirection()
'Changed PM_SORT_BY_SELECTION_AND_CHECKED 2008-12-09
'****************************************************************************
'* <placeholder for header> *
'****************************************************************************
Private Sub checkKey( _
ByVal aKey As Integer, ByVal aDatatype As datatypeEnum)
If aKey = specialSortKeyEnum.enumByChecked Or _
aKey = specialSortKeyEnum.enumBySelected Then
'It is a client error to specify other than text for
'sorting by selected or checked. This is just an
'internal convention that we enforce as soon as possible - here!.
'Otherwise the error would happen much later, in the Compare()
'function during sorting.
Trace.Assert(aDatatype = datatypeEnum.enumText, _
"PIL ASSERT. Internal error. " & _
"Data type is not enumText for special key (value = " & _
aKey & ".")
End If
End Sub 'checkKey()
'****************************************************************************
'* <placeholder for header> *
'****************************************************************************
Public Sub addColumnPrimaryKey( _
ByVal aPrimaryKey As Integer, _
ByVal aDatatype As datatypeEnum, _
ByVal aSortingDirection2 As sortingDirectionEnum, _
ByVal aMenuKeyboardShortCut As Boolean)
'Changed PM_SORT_BY_SELECTION_AND_CHECKED 2008-12-09
checkKey(aPrimaryKey, aDatatype)
'Changed PM_ALTERNATE_SORT_DIRECTION 2008-11-08
'Note: we reverse the sort direction here at specification time. The
' reason is that we reverse in prepareSortAndSort(). In that way
' the first sort will be as specified.
Dim sortingDirection As sortingDirectionEnum = _
reverseSortDirection(aSortingDirection2)
'Changed PM_COLUMNSORT_BY_KEYBOARD 2008-11-07
'Dim keyList1 As List(Of sortKeyStructure) = _
' New List(Of sortKeyStructure)(10)
Dim someColumnSortInfo As columnSortInfoStruct
someColumnSortInfo.keyList = New List(Of sortKeyStructure)(10)
someColumnSortInfo.useMenuKeyboardShortCut = aMenuKeyboardShortCut
someColumnSortInfo.keyList.Add( _
New sortKeyStructure( _
aPrimaryKey, _
aDatatype, _
sortingDirection) _
)
mColumnSortSpecificationHash.Add( _
aPrimaryKey, someColumnSortInfo) 'Note: sub keys may be added later. In
' that case the key list in the hash will be updated.
mLatestPrimaryKey = aPrimaryKey
End Sub 'addColumnPrimaryKey()
'****************************************************************************
'* <placeholder for header> *
'****************************************************************************
Public Sub addColumnSecondaryKey( _
ByVal aSecondaryKey As Integer, _
ByVal aDatatype As datatypeEnum, _
ByVal aSortingDirection As sortingDirectionEnum)
'Note: the primary key is implied by the ***latest*** call
' to addColumnPrimaryKey()...
'Changed PM_SORT_BY_SELECTION_AND_CHECKED 2008-12-09
checkKey(aSecondaryKey, aDatatype)
'Detect errors in the client call sequence.
If mLatestPrimaryKey < 0 Then
Trace.Assert(False, _
"PIL ASSERT. " & _
"Client call error in addColumnSecondaryKey(): " & _
"addColumnPrimaryKey() has not been called.")
End If
'Changed PM_COLUMNSORT_BY_KEYBOARD 2008-11-07
'Dim keyList1 As List(Of sortKeyStructure) = _
' mColumnSortSpecificationHash(mLatestPrimaryKey)
Dim someColumnSortInfo As columnSortInfoStruct = _
mColumnSortSpecificationHash(mLatestPrimaryKey)
someColumnSortInfo.keyList.Add( _
New sortKeyStructure( _
aSecondaryKey, _
aDatatype, _
aSortingDirection) _
)
'Do we need to write back to the hash????
End Sub 'addColumnSecondaryKey()
'Changed PM_COLUMNSORT_BY_KEYBOARD 2008-11-06
'****************************************************************************
'* Returns list of menu items representing those columns that can *
'* be sorted. The client can use the returned list to add items to *
'* menus so it is possible to sort columns without using the mouse. *
'****************************************************************************
Public Function menuItemsForSortableColumns( _
ByRef anMenuToAddTo As System.Windows.Forms.MenuItem, _
ByRef aInHandlerFunction As EventHandler) _
As List(Of System.Windows.Forms.MenuItem)
'Note: perhaps we should order these menu items as the physical order
' of the columns. mColumnMapper knows about that order. Right
' now the order is undefined, but probably the same order as
' specified by the client.
Dim len As Integer = mColumnSortSpecificationHash.Count
Dim toReturn As List(Of System.Windows.Forms.MenuItem) = _
New List(Of System.Windows.Forms.MenuItem)(len)
Dim keyBoardShortcutCounter As Integer = 0
Dim hashEnumerator2 As Dictionary( _
Of Integer, columnSortInfoStruct).Enumerator = _
mColumnSortSpecificationHash.GetEnumerator()
While hashEnumerator2.MoveNext()
Dim curKey As Integer = hashEnumerator2.Current.Key
Dim curValue As columnSortInfoStruct = _
hashEnumerator2.Current.Value
'The primary key (for sorting) is the first in the list.
Dim columnSortKey As Integer = curValue.keyList(0).columnID
Dim inGUi As Boolean
Dim info As listViewColumnInfoStruct = _
mColumnMapper.getColumnInfo(columnSortKey, inGUi)
If inGUi Then
Dim someMenuItem As System.Windows.Forms.MenuItem = _
New System.Windows.Forms.MenuItem
someMenuItem.Tag = columnSortKey
someMenuItem.Text = MENUPREFIX & info.columnHeader
'Changed PM_COLUMNSORT_BY_KEYBOARD 2008-11-07
If curValue.useMenuKeyboardShortCut Then
Dim assignShortcut As Boolean = True
Dim sc As System.Windows.Forms.Shortcut
Select Case keyBoardShortcutCounter
Case 0
sc = Shortcut.Ctrl1
Case 1
sc = Shortcut.Ctrl2
Case 2
sc = Shortcut.Ctrl3
Case 3
sc = Shortcut.Ctrl4
Case 4
sc = Shortcut.Ctrl5
Case 5
sc = Shortcut.Ctrl6
Case 6
sc = Shortcut.Ctrl7
Case 7
sc = Shortcut.Ctrl8
Case 8
sc = Shortcut.Ctrl9
Case 9
sc = Shortcut.Ctrl0
Case Else
assignShortcut = False 'We have run out of
' keyboard shortcuts... Silently do not add it.
End Select
If assignShortcut Then
someMenuItem.Shortcut = sc
End If
keyBoardShortcutCounter += 1
End If
anMenuToAddTo.MenuItems.AddRange( _
New System.Windows.Forms.MenuItem() {someMenuItem})
'Wire handling of menu command selection to the handler
'function specified by the client.
AddHandler _
someMenuItem.Click, _
aInHandlerFunction
toReturn.Add(someMenuItem)
Else
Dim peter2 As Integer = 2 'For breakpoints. Defined, but
' not added to the GUI.
End If
End While 'Hash iteration.
'Dim someMI As System.Windows.Forms.MenuItem = _
' New System.Windows.Forms.MenuItem
'Private mColumnSortSpecificationHash As Dictionary( _
' Of Integer, List(Of sortKeyStructure))
Return toReturn
End Function 'menuItemsForSortableColumns()
'****************************************************************************
'* <placeholder for header> *
'****************************************************************************
Private Sub prepareSort( _
ByVal aKeylist2 As Generic.List(Of sortKeyStructure))
'Old:
' ByVal aKeylist As ArrayList
' Item type of aKeylist is: sortKeyStructure
'Cache the physical column number in the datastructure. Done here
'during a deep copy of the input list.
'Changed PM_TYPESAFE_HASH 2008-11-06
'mCurrentKeyList = New ArrayList
Dim inLen As Integer = aKeylist2.Count
mCurrentKeyList3 = New List(Of sortKeyStructure)(inLen)
Dim someKey As sortKeyStructure
For Each someKey In aKeylist2
Dim copiedKey As sortKeyStructure = someKey
copiedKey.physicalColumn = _
mColumnMapper.item2ColumnIndex(copiedKey.columnID)
mCurrentKeyList3.Add(copiedKey)
Next
End Sub 'prepareSort()
'****************************************************************************
'* <placeholder for header> *
'****************************************************************************
Public Sub prepareSortAndSort( _
ByVal aPrimaryKey As Integer, _
ByVal aListView As System.Windows.Forms.ListView)
'Dim keylist As List(Of sortKeyStructure) = Nothing
Dim columnSortInfo As columnSortInfoStruct
If mColumnSortSpecificationHash.TryGetValue(aPrimaryKey, columnSortInfo) Then
Dim doSort As Boolean = Not aListView Is Nothing
'Changed PM_ALTERNATE_SORT_DIRECTION 2008-11-08
'Note: we reverse the sort direction here at sort time. The reason
' is that we reverse at specification time. In that way
' the first sort will be as specified. Second sort will
' be reversed, etc.
'
If True Then 'Note: unconditional. We need to reverse even though we
' don't explicitly sort below. The sort will happen implicitly -
' when an instance of this class is assigned to
' the ListViewItemSorter property of the ListView (or a
' derived class).
'
Dim primaryKeyInfo As sortKeyStructure = columnSortInfo.keyList(0)
Dim newSortingDirection As sortingDirectionEnum = _
reverseSortDirection(primaryKeyInfo.sortDirection) 'Index 0
' contains the primary sort key.
'Write back.
primaryKeyInfo.sortDirection = newSortingDirection
columnSortInfo.keyList(0) = primaryKeyInfo
'mColumnSortSpecificationHash(aPrimaryKey) = columnSortInfo 'Needed??
End If 'Block. Alternating sort keys.
Me.prepareSort(columnSortInfo.keyList)
If doSort Then
aListView.Sort()
Else
Dim peter2 As Integer = 2 'We are not going to
' sort, at least not explicitly...
End If
Else
'We have not defined sorting for the column in question.
'Simply ignore the request. Should we throw a message
'box instead?
Dim peter1 As Integer = 1
End If
End Sub 'prepareSortAndSort
'****************************************************************************
'* <placeholder for header> *
'****************************************************************************
Private Function Compare( _
ByVal x As Object, ByVal y As Object) _
As Integer _
Implements System.Collections.IComparer.Compare
'Can we get rid of these casts??
Dim item1 As ListViewItem = CType(x, ListViewItem)
Dim item2 As ListViewItem = CType(y, ListViewItem)
Dim toReturn As Integer = 0 'In case
Dim someKey2 As sortKeyStructure
For Each someKey2 In mCurrentKeyList3
Dim physCol As Integer = someKey2.physicalColumn
If True Then 'Block. Early error detection.
'Under normal circumstances these two
'indices are always the same as all rows have
'the same number of columns. But it can
'happen that sorting is used during building
'up of the list (by a screen update?) and
'then a partly completed row can be compared
'against a full row. Clients should do specific
'things to avoid this situation. We detect it here
'if they don't...
Dim lastIndex1 As Integer = item1.SubItems.Count() - 1
Dim lastIndex2 As Integer = item2.SubItems.Count() - 1
Trace.Assert(physCol <= lastIndex1, _
"Index, " & physCol & ", is past last index in item1.SubItems, " & _
lastIndex1 & ".")
Trace.Assert(physCol <= lastIndex2, _
"Index, " & physCol & ", is past last index in item1.SubItems, " & _
lastIndex2 & ".")
End If
Dim str1 As String = Nothing
Dim str2 As String = Nothing
If someKey2.columnID = specialSortKeyEnum.enumBySelected Then
'Changed PM_SORT_BY_SELECTION_AND_CHECKED 2008-12-09.
'Note: any letters, but observe ascending or
' descending order.
If item1.Selected Then
str1 = "S"
Else
str1 = "AU"
End If
If item2.Selected Then
str2 = "S"
Else
str2 = "AU"
End If
Else
If someKey2.columnID = specialSortKeyEnum.enumByChecked Then
'Changed PM_SORT_BY_SELECTION_AND_CHECKED 2008-12-09
'Note: any letters, but observe ascending or
' descending order.
If item1.Checked Then
str1 = "C"
Else
str1 = "AN"
End If
If item2.Checked Then
str2 = "C"
Else
str2 = "AN"
End If
Else
str1 = item1.SubItems(physCol).Text
str2 = item2.SubItems(physCol).Text
If str1 = "..." Then
'Changed PM_QUANTCOLUMNS_SORT 2007-04-03
'str1 = "0"
str1 = "-1e-20"
End If
If str2 = "..." Then
'Changed PM_QUANTCOLUMNS_SORT 2007-04-03
'str2 = "0"
str2 = "-1e-20"
End If
'Changed PM_QUANTCOLUMNS_SORT 2007-04-03
If str1 = "NaN" Then
str1 = "-1e-22"
End If
If str2 = "NaN" Then
str2 = "-1e-22"
End If
End If
End If
'Dim compareResult As Integer = 0
Select Case someKey2.dataType
Case datatypeEnum.enumText
'Initially ascending, but we reverse the result later
'if descending is specified.
If str1 > str2 Then
toReturn = 1
Else
If str1 < str2 Then
toReturn = -1
Else
'Equal...
Dim peter1 As Integer = 1
End If
End If
Case datatypeEnum.enumInteger
Dim int1 As Integer = CInt(str1)
Dim int2 As Integer = CInt(str2)
'Initially ascending, but we reverse the result later
'if descending is specified.
If int1 > int2 Then
toReturn = 1
Else
If int1 < int2 Then
toReturn = -1
Else
'Equal...
Dim peter1 As Integer = 1
End If
End If
Case datatypeEnum.enumDecimalNumber
'Ignore any parenthesises around the number, e.g.: (47)
Dim startIndex1 As Integer = str1.IndexOf("(")
If startIndex1 >= 0 Then
Dim endIndex1 As Integer = str1.IndexOf(")")
If endIndex1 >= 0 Then
startIndex1 += 1 'Step over "("
Dim len1 As Integer = endIndex1 - startIndex1
str1 = str1.Substring(startIndex1, len1)
End If
End If
Dim startIndex2 As Integer = str2.IndexOf("(")
If startIndex2 >= 0 Then
Dim endIndex2 As Integer = str2.IndexOf(")")
If endIndex2 >= 0 Then
startIndex2 += 1 'Step over "("
Dim len2 As Integer = endIndex2 - startIndex2
str2 = str2.Substring(startIndex2, len2)
End If
End If
Dim d1 As Double = CDbl(str1)
Dim d2 As Double = CDbl(str2)
'Initially ascending, but we reverse the result later
'if descending is specified.
If d1 > d2 Then
toReturn = 1
Else
If d1 < d2 Then
toReturn = -1
Else
'Equal...
Dim peter1 As Integer = 1
End If
End If
Case Else
Trace.Assert(False, "PIL ASSERT. Select Case never fall-through")
End Select
If someKey2.sortDirection = _
sortingDirectionEnum.enumDescendingSort Then
toReturn = -toReturn
End If
If toReturn = 0 Then
'The two keys are equal. We must go to a lower
'level key (if there are any) - by continuing the loop.
Dim peter1 As Integer = 1
Else
Exit For 'Two current keys are not equal. We need not
'compare more keys. Stop.
End If
Next
Return toReturn
End Function 'Compare()
End Class 'ListViewColumnSorter
End Namespace 'CEBI_generalGUI
Generated by script codePublish.pl at 2009-01-05T15:20:59.