Page 1 of 1

ComboBox Sort

Posted: Sun May 22, 2011 11:33 pm
by offsides
I'm not sure where to put this as it's hardly "advanced" enough for "Tips and Tricks". So I'll stick it here.

I needed some way to ensure that, after additions to a combobox, it would be sorted. If the list is already sorted, one way to do this is to insert a new item according to an on-the-fly scan of the list. What to do if the list isn't sorted?

From what I can see, this isn't a built-in PB feature so I wrote this one. I couldn't see offloading the list into an array, sorting that, then shoving them back in, especially for only a dozen items, or so. Also, the combobox is already an array, of sorts, so I figured to just sort it directly.

This is based on the slow but compact code bubble sort. For the number of items normally found in a combobox, though, it's fast enough. To make it ignore case, you could sort Ucased items.

If there's snazzier ways to do this or I've overlooked this feature in PB, let me know.

Thanks,
Bill

Code: Select all

Declare SortComboBox(BoxID)

#ComboBox = 1
#ButtonSort = 2

If OpenWindow(0, 0, 0, 230, 120, "Combo Sort example...", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
  ComboBoxGadget(1, 10, 40, 200, 21, #PB_ComboBox_Editable)
  ButtonGadget(#ButtonSort, 20, 80, 50, 25, "Sort")
  ;Generate some words
  For i = 1 To 40 ;Number of items in combo box
    a$ = ""
    For j = 1 To 10 ;Number of characters per word
      a$ = a$ + Chr(Random(25) + 65) ; Make "words"
    Next
    AddGadgetItem(#ComboBox, -1, a$); Load up with words
  Next  
  
  Repeat
    Event = WaitWindowEvent()   
    Select Event 
      Case #PB_Event_Gadget
        Select EventGadget()
           Case #ButtonSort: SortComboBox(#ComboBox)        
        EndSelect
    EndSelect
  Until Event = #PB_Event_CloseWindow
  End
EndIf
  

Procedure SortComboBox(BoxID)
  ;Sort combobox using the bubble sort
  Start = ElapsedMilliseconds()
  N = CountGadgetItems(BoxID)
  For i = 1 To N-1 ; Loop cycles around N-1 times (because of the J+1 thing.
    For J = 0 To N-2 ; -2 because of the J+1 thing and starting at 0
      C$ = GetGadgetItemText(BoxID, J)
      N$ = GetGadgetItemText(BoxID, J+1)
      If C$ > N$           
        SetGadgetItemText(BoxID, J, N$)
        SetGadgetItemText(BoxID, J+1, C$)            
      EndIf
    Next
  Next    
  Time.l = ElapsedMilliseconds()-Start
  MessageRequester("Elapsed Time", Str(Time) + " ms")
EndProcedure

Re: ComboBox Sort

Posted: Mon May 23, 2011 9:06 am
by JustinJack

Code: Select all

Procedure AddSortedComboItem( numCombo, myString.s )
  retVal = 0
  If IsGadget(numCombo)
    NewList myItems.s()
    
    ; Get the current text in the box.
    cText.s = GetGadgetText(numCombo)
    
    ; Count items
    numItems = CountGadgetItems(numCombo)
    If numItems > 0
      numItems - 1
      AddElement(myItems())
      myItems() = myString
      For i = 0 To numItems
        AddElement(myItems())
        myItems() = GetGadgetItemText(numCombo, i)
      Next
      SortList(myItems(), #PB_Sort_Ascending)
      ResetList(myItems())
      ClearGadgetItems(numCombo)
      myState = -1
      ctr = 0
      newItemIndex = -1
      While NextElement(myItems())
        AddGadgetItem(numCombo, -1, myItems())
        If myItems() = cText
          myState = ctr
        ElseIf myItems() = myString
          newItemIndex = ctr
        EndIf
        ctr + 1
      Wend
      SetGadgetState(numCombo, myState)
      retVal = newItemIndex
      
    Else ; Box is empty, add and select a string.
      AddGadgetItem(numCombo, -1, myString)
      SetGadgetState(numCombo, 0)
    EndIf
    FreeList(myItems())
  EndIf
  ProcedureReturn retVal
EndProcedure



OpenWindow(0, 0, 0, 500, 500, "Combobox test", #PB_Window_ScreenCentered|#PB_Window_SystemMenu)
ComboBoxGadget(3, 10, 100, 100, 20)
StringGadget(1, 10, 10, 100, 20, "", #PB_String_UpperCase)
ButtonGadget(2, 10, 30, 100, 20, "Add")
Repeat
  myEvent = WaitWindowEvent()
  Select myEvent
    Case #PB_Event_Gadget
      If EventGadget() = 2 And EventType() = #PB_EventType_LeftClick
        AddSortedComboItem(3, GetGadgetText(1))
        SetGadgetText(1, "")
      EndIf
    Case #PB_Event_CloseWindow
      okToClose = 1
  EndSelect
Until okToClose <> 0
End okToClose
Here's a quick and easy that I do it. Just call AddSortedComboItem()
and it will insert the item perserving what's currently selected. You shouldn't even see it blink.

Re: ComboBox Sort

Posted: Mon May 23, 2011 9:10 am
by JustinJack
btw..I've done things similar to this where I walk a linked-list of 42k items and only populate items matching some text in the combobox, sorted alphabetically....it's very, very fast so I don't hesitate to use this method.

Re: ComboBox Sort

Posted: Mon May 23, 2011 8:12 pm
by offsides
Justin

I'm a rank amateur with PB so yours is the first glimpse at lists. Thanks.

Here's another approach for a combo that is already sorted, like a directory, or an empty one. I recycled some of your code. (Actually, I'd lost track of this so had to invent it again. Your example was just in time.)

Code: Select all

Procedure AddSortedComboItem( numCombo, myString$ )
    n = CountGadgetItems(numCombo)
    If n > 0 ;Need more than 1 or nothing to sort.
      ;numItems - 1
      MyString$ = GetGadgetText(1)
      For i = 0 To n
        If UCase(MyString$) < UCase(GetGadgetItemText(3, i)) ;It's less so insert, move rest down
          AddGadgetItem(numCombo, i, myString$)
          ProcedureReturn
        EndIf        
        If i = n
          AddGadgetItem(numCombo, -1, myString$) ;Reached end w/o insert
        EndIf
      Next
    Else
      AddGadgetItem(numCombo, -1, myString$) ;The first one
    EndIf  
EndProcedure

OpenWindow(0, 0, 0, 500, 500, "Combobox test", #PB_Window_ScreenCentered|#PB_Window_SystemMenu)
ComboBoxGadget(3, 10, 100, 100, 20)
StringGadget(1, 10, 10, 100, 20, "") 
ButtonGadget(2, 10, 30, 100, 20, "Add")
Repeat
  myEvent = WaitWindowEvent()
  Select myEvent
    Case #PB_Event_Gadget
      If EventGadget() = 2 And EventType() = #PB_EventType_LeftClick
        AddSortedComboItem(3, GetGadgetText(1))
        SetGadgetText(1, "")
      EndIf
    Case #PB_Event_CloseWindow
      okToClose = 1
  EndSelect
Until okToClose <> 0
End okToClose
All great stuff, thanks again.
Bill

Re: ComboBox Sort

Posted: Mon May 23, 2011 9:55 pm
by JustinJack
Bill,

One of the greatest features (in my opinion) of PureBasic is it's handling of Linked Lists. They're amazing for the loading of absolutly huge structured datasets. Plus, you can pass a pointer to a structure that CONTAINS a list within to a thread or to anything, and there goes your dataset, all passed along nicely!

Ex:

Code: Select all

Structure CoolList
  itemName.s
  itemNumber.s
  itemID.i
EndStructure

Structure passStruct
  MyString.s
  List myList.CoolList()
Endstructure

StructOne.passStruct
myThread = CreateThread(@ThreadProc(), @StructOne)
Now if you had ThreadProc() as:

Code: Select all

Procedure ThreadProc( *lpPassStruct.passStruct )
  if *lpPassStruct <> 0
  resetlist(*lpPassStruct\myList())
  while nextelement(*lpPassStruct\myList())
    debug *lpPassStruct\myList()\itemName
    debug *lpPassStruct\myList()\itemNumber
  wend
  endif
EndProcedure
That would display all the list elements in the thread...
I love them! I use them over Arrays whenever possible!

Re: ComboBox Sort

Posted: Tue May 24, 2011 3:09 pm
by Shardik
In Windows and MacOS X you don't need to reinvent the wheel for sorting ComboBox items. :wink:
In Windows you only need to add the flag #CBS_SORT when defining the ComboBoxGadget. The
only drawback is that you can't use AddGadgetItem() to add items but

Code: Select all

SendMessage_(GadgetID(x), #CB_ADDSTRING, 0, EntryText$)
if you want the new entries to be sorted automatically.

For Windows take a look into these examples:
FluidByte: http://www.purebasic.fr/german/viewtopi ... 31&start=4
Alireza: http://www.purebasic.fr/english/viewtop ... 20&start=7

For editable ComboBoxes in MacOS X I have presented this example:
http://www.purebasic.fr/english/viewtop ... 19&t=46018

Re: ComboBox Sort

Posted: Tue May 31, 2011 5:14 am
by offsides
Shardik,
(I read the book about you, many years ago. Wow!)

I apologize for not responding before now. I thought this thread was finished.
Thanks much for your reply. I will save your example and use it.

Bill