ListView Columns

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
4 messages Options
Reply | Threaded
Open this post in threaded view
|

ListView Columns

Theo Pronk
I would like to be able to sort a multiple column list view
progressively column by column depending on which headers are pressed.

Eg 3 columns: name age sex
If I press heading "name" then "sex" then "age" the order should be,
name by sex by age.

The sort order block within each column allows the ability to sort by
that column or a multiple of columns but only in a predefined way.

I would like to have a dynamic way of determining which column to sort
by. To do this I need to get feedback each time a heading is pressed. Ie
   the question is: how do I know "age" has been pressed?

Any help would be appreciated.

Thanks
Theo


Reply | Threaded
Open this post in threaded view
|

Re: ListView Columns

Bill Dargel
Theo wrote:
>
> I would like to be able to sort a multiple column list view
> progressively column by column depending on which headers are pressed.
>
> Eg 3 columns: name age sex
> If I press heading "name" then "sex" then "age" the order should be,
> name by sex by age.

Just to be clear. I take this to mean that the primary sort is the last
column selected? So in your example, it would be first sorted by age,
and then within the same age sorted by sex, and finally within the same
age and sex sorted by name?

I just wanted to point out that this would occur naturally if sorting
would just preserve the existing order for equal entries. Unfortunately
this is not the case for the quicksort algorithm used by
SortedCollection. There have been a number of times that I wished it
were otherwise. :-(

But, if I misunderstood your order of column selections, then there
would also need to be some user action to indicate that they were
starting over and selecting the primary sort column.

Anyway, as far as doing what you want to do ... Check out
ListView>>lvnColumnClick: which calls ListView>>sortOnColumn: which
calls ListPresenter>>beSorted: for places to override or hook in. You'll
need to keep track of what columns have been clicked, and then use an
appropriate composite sort block.

-------------------------------------------
Bill Dargel            [hidden email]
Shoshana Technologies
100 West Joy Road, Ann Arbor, MI 48105  USA


Reply | Threaded
Open this post in threaded view
|

Re: ListView Columns

Christopher J. Demers
"Bill Dargel" <[hidden email]> wrote in message
news:[hidden email]...

> Theo wrote:
> >
> > I would like to be able to sort a multiple column list view
> > progressively column by column depending on which headers are pressed.
> >
> > Eg 3 columns: name age sex
> > If I press heading "name" then "sex" then "age" the order should be,
> > name by sex by age.
>
> Just to be clear. I take this to mean that the primary sort is the last
> column selected? So in your example, it would be first sorted by age,
> and then within the same age sorted by sex, and finally within the same
> age and sex sorted by name?
>
> I just wanted to point out that this would occur naturally if sorting
> would just preserve the existing order for equal entries. Unfortunately
> this is not the case for the quicksort algorithm used by
> SortedCollection. There have been a number of times that I wished it
> were otherwise. :-(

I think Bob Jarvis has granted your wish (see StableSortedCollection, he
also includes a list presenter for it):
http://www.nls.net/mp/jarvis/Bob/DolphinGoodies.htm

I have used the StableSortedCollection (Thanks Bob!), but not the Presenter
yet.

Chris


Reply | Threaded
Open this post in threaded view
|

Re: ListView Columns

Bill Dargel
Christopher J. Demers wrote:
> I think Bob Jarvis has granted your wish (see StableSortedCollection, he
> also includes a list presenter for it):
> http://www.nls.net/mp/jarvis/Bob/DolphinGoodies.htm
>
> I have used the StableSortedCollection (Thanks Bob!), but not the Presenter
> yet.

Thanks for the pointer Chris!

I've been checking it out, and have some fixes and further caveats for
you, Theo, or anyone else.

I've stuck the added and changed code at the end of this message.

I noticed a potential bounds problem with the indices into tempArray,
while inspecting the code. Finally came up with the setup shown in
#testTempArrayLargeEnough, that causes the error to surface. The key is
having 'firstIndex' no longer be 1, and a large enough #addAll: that the
space at the beginning of the collection gets left. The fix then is in
the attached #sortFrom:to: where tempArray simply needs to be
instantiated from #basicSize, rather than #size.

I tried out using the StableListPresenter by hacking
MethodBrowser>>createComponents. Only found that the list no longer
sorted at all. The fix to StableListPresenter>>list: is attached. It's
that age-old gotcha of the value answered from #addAll: being the added
collection, and not the modified receiver collection. Just needed a
#yourself.

BTW, the two tests #testStability1 and #testStability2 suffered from the
same #addAll: usage problem and were not really testing what they
appeared to be testing. (One could substitute SortedCollection for
StableSortedCollection, and they still would pass). The fixed tests
below fail on SortedCollection, but pass for StableSortedCollection.

Anyway, with the fix to StableListPresenter>>list: the hacked
MethodBrowser started doing useful things. If you clicked on the 'Class'
column, and then on the 'Selector' column for instance, the entries with
the same selector would have a sub-sort on class.

But, after playing with it for a while, I realized that the 'preserved
order' was actually being reversed. In other words, if you sorted on
class A-Z, and then sorted by selectors, the classes within equal
selectors would be Z-A.

Some debugging and playing with variations of the stability tests (once
they'd been fixed) revealed what the issue is. The correct operation of
StableSortedCollection depends on the sortBlock answering true for equal
values. But the MethodBrowser has a sort that is "a selector < b
selector". The historical Smalltalk sortBlock was (and default in
SortedCollection still is in Dolphin) "a <= b". Though now the ANSI
standard says it should be "a < b".

The StableSortedCollection will need to be modified to correctly handle
"<" style sortBlocks. To transparently handle both "<" or "<=" styles,
it would need to do a check on "sortBlock value: someElement value:
someElement" and see if it was true or false for the equal case, and
then act accordingly.

I've already spent too much time today playing with this, rather than
getting done the work that I should be doing. So saying, I probably
won't do any more on it without a specific need arising.

I wonder if Andy and Blair could be enticed into picking up on it? ;-)
It would be nice if in particular the MethodBrowser could do secondary
sorts on the other columns.

regards,
-Bill

------------------------
!StableSortedCollectionTest methodsFor!

testTempArrayLargeEnough
        | collection errors last |
        collection := StableSortedCollection new.
        collection addAll: (100 to: 200).
        60 timesRepeat: [collection removeFirst].
        collection addAll: (130 to: 200).
        last := nil.
        errors := 0.
        collection do:
                        [:each |
                        (last notNil and: [last > each]) ifTrue: [errors := errors + 1].
                        last := each].
        self assert: errors = 0! !
!StableSortedCollectionTest categoriesFor:
#testTempArrayLargeEnough!public!unit tests! !


sortFrom: start to: stop
        "Private - Sort elements start through stop of self to be nondescending
according to sortBlock."

        "wod - Fix bug where tempArray needs to be initialized to basicSize.
Otherwise in may not be large enough when the internal 'firstIndex' is
something other than 1."

        tempArray := Array new: self basicSize.
        self mergeSortFrom: start to: stop.
        tempArray := nil! !
!StableSortedCollection categoriesFor: #sortFrom:to:!private!sorting! !

!StableListPresenter methodsFor!

list: aSequenceableCollection
        "Set the contents of the receiver to be aSequenceableCollection"

        "wod - fix bug so that the list is the StableSortedCollection and not
the original aSequenceableCollection."

        | list |
        list := self isSorted
                                ifTrue:
                                        [(StableSortedCollection new sortBlock: sortBlock)
                                                addAll: aSequenceableCollection;
                                                yourself]
                                ifFalse: [aSequenceableCollection].
        ^self model list: list! !
!StableListPresenter categoriesFor: #list:!accessing!public! !

!StableSortedCollectionTest methodsFor!

testStability1
        "Verify that the sort is stable"

        "wod - fix bug so that it is actually verifying the
StableSortedCollection"

        | testData col lastTestData errs |
        testData := OrderedCollection new.
        testData addAll: ((100 to: 1 by: -1) collect: [:n | SortTestData new
value: n]).
        testData addAll: ((100 to: 1 by: -1) collect: [:n | SortTestData new
value: n]).
        testData addAll: ((100 to: 1 by: -1) collect: [:n | SortTestData new
value: n]).
        1 to: testData size do: [:i | (testData at: i) order: i].
        col := StableSortedCollection new sortBlock: [:v1 :v2 | v1 value <= v2
value].
        col addAll: testData.
        lastTestData := nil.
        errs := col inject: 0
                                into:
                                        [:tot :aSortTestData |
                                        lastTestData notNil
                                                ifTrue:
                                                        [lastTestData value = aSortTestData value
                                                                ifTrue: [lastTestData order > aSortTestData order ifTrue: [tot
:= tot + 1]]].
                                        lastTestData := aSortTestData.
                                        tot].
        self should: [errs = 0]! !
!StableSortedCollectionTest categoriesFor: #testStability1!public!unit
tests! !

!StableSortedCollectionTest methodsFor!

testStability2
        "Verify that the sort is stable"

        "wod - fix bug so that it is actually verifying the
StableSortedCollection"

        | testData col lastTestData errs |
        testData := OrderedCollection new.
        (25 to: 1 by: -1)
                do: [:i | 1 to: 100 do: [:j | testData add: (SortTestData new value:
i)]].
        1 to: testData size do: [:i | (testData at: i) order: i].
        col := StableSortedCollection new sortBlock: [:v1 :v2 | v1 value <= v2
value].
        col addAll: testData.
        lastTestData := nil.
        errs := col inject: 0
                                into:
                                        [:tot :aSortTestData |
                                        lastTestData notNil
                                                ifTrue:
                                                        [lastTestData value = aSortTestData value
                                                                ifTrue: [lastTestData order > aSortTestData order ifTrue: [tot
:= tot + 1]]].
                                        lastTestData := aSortTestData.
                                        tot].
        self should: [errs = 0]! !
!StableSortedCollectionTest categoriesFor: #testStability2!public!unit
tests! !

-------------------------------------------
Bill Dargel            [hidden email]
Shoshana Technologies
100 West Joy Road, Ann Arbor, MI 48105  USA