Right-Click Listicon Header Detection

Share your advanced PureBasic knowledge/code with the community.
Xombie
Addict
Addict
Posts: 898
Joined: Thu Jul 01, 2004 2:51 am
Location: Tacoma, WA
Contact:

Right-Click Listicon Header Detection

Post by Xombie »

Code updated for 5.20+

Howdy,

I needed a way to detect right-clicks in the listview control header (for custom searching, menu popups, etc...) and searching the forum turned up nothing so I combed through the Internet for some help. Found some old VB code and converted it over.

The code is from: http://groups.google.com/group/microsof ... ef5b0575b8

From a guy named Chris Eastwood, apparently. Thanks, whoever you are :)

Code: Select all

;
#LVM_GETHEADER = 4127
;
Structure HDHITTESTINFO
  pt.POINT
  flags.l
  iItem.l
EndStructure

Enumeration ; Window IDs
  #WindowMain
EndEnumeration

Enumeration ; Menu IDs
  #MenuPrimary
  #MenuPrimaryTest
  #MenuSecondary
  #MenuSecondaryTest
EndEnumeration

Enumeration ; Gadget IDs
  #ListPrimary
  #ListSecondary
  #ButtonClose
EndEnumeration

Procedure.l WindowCallback(HandleWindow, Message, wParam, lParam)
  ; Main form callback procedure.
  HandleHeader.l
  ; Store the handle for the listview control's header.
  *NotifyHeader.NMHDR
  ; Notification message header.
  TestHit.HDHITTESTINFO
  ; Store information about header hit testing.
  IndexColumn.l
  ; Store the column index from our header.
  Protected iLoop.l
  ;
  Protected lResult.l
  ;
  lResult = #PB_ProcessPureBasicEvents
  ;
  If Message = #WM_NOTIFY
    ; Received a notify event.  Usually passed to the parent container for a control.
    *NotifyHeader = lParam
    ; Receive the message header information passed by the notify event.
    If *NotifyHeader\code = #NM_RCLICK
      ; Right-click event was received.
      GetCursorPos_(TestHit\pt)
      ; Retrieve the cursor position.
      ScreenToClient_(*NotifyHeader\hwndFrom, TestHit\pt)
      ; Translate our coordinates to screen coordinates.
      IndexColumn = SendMessage_(*NotifyHeader\hwndFrom, #HDM_HITTEST, 0, TestHit)
      ; Retrieve the column index from our header control.  The call uses the coordinate information we retrieved previously.
      If *NotifyHeader\hwndFrom = SendMessage_(GadgetID(#ListPrimary), #LVM_GETHEADER, 0, 0)
        ; Test if the header clicked is from the 'primary' listview control.  #LVM_GETHEADER returns the handle to the header
        ; control used in the listview control.  This is the same handle used as part of the NMHDR (*messageheader) variable.
        If IndexColumn = 0
          ; Right-clicked on the first column.
          HoldNumber.s = InputRequester("Number Search", "Please enter a number to search for (eg., '1', '2', '3' or '4')", "")
          ; For our test, prompt the user for a number used in the primary listview control.  We'll search for it.
          For iLoop = 0 To CountGadgetItems(#ListPrimary) - 1
            ; Loop through the number of items in our primary listview control.
            If HoldNumber = GetGadgetItemText(#ListPrimary, iLoop, 0)
              ; Check if the entered number is the same as the one in the listview.
              SetGadgetItemState(#ListPrimary, iLoop, #PB_ListIcon_Selected)
              ; Select the item.
              SendMessage_(GadgetID(#ListPrimary), #LVM_ENSUREVISIBLE, iLoop, #False)
              ; Make sure the newly selected item is visible.  Not useful in this example but only if we have many more items.
              Break
              ; We found the item.  Break from our loop.
            EndIf
            ;
          Next iLoop
          ;
        ElseIf IndexColumn = 1
          ; User right-clicked on the second column.
          DisplayPopupMenu(#MenuPrimary, WindowID(#WindowMain))
          ; Display a popup menu.
        EndIf
        ;
      ElseIf *NotifyHeader\hwndFrom = SendMessage_(GadgetID(#ListSecondary), #LVM_GETHEADER, 0, 0)
        ;
        If IndexColumn = 0
          ; Right-clicked on the first column.
          DisplayPopupMenu(#MenuSecondary, WindowID(#WindowMain))
          ; Display a popup menu.
        ElseIf IndexColumn = 2
          ; Right-clicked on the first column.
          HoldString.s = InputRequester("Text Search", "Please enter text to search in the 3rd column (eg. 'fo').", "")
          ; For our test, prompt the user for a number used in the primary listview control.  We'll search for it.
          For iLoop = 0 To CountGadgetItems(#ListSecondary) - 1
            ; Loop through the number of items in our primary listview control.
            If FindString(LCase(GetGadgetItemText(#ListSecondary, iLoop, 2)), LCase(HoldString), 1)
              ; Check if the 3rd column contains the text we search for.
              SetGadgetItemState(#ListSecondary, iLoop, #PB_ListIcon_Selected)
              ; Select the item.
              SendMessage_(GadgetID(#ListSecondary), #LVM_ENSUREVISIBLE, iLoop, #False)
              ; Make sure the newly selected item is visible.  Not useful in this example but only if we have many more items.
              Break
              ; We found the item.  Break from our loop.
            EndIf
            ;
          Next iLoop
          ;
        EndIf
        ;
      EndIf
      ;
    EndIf
    ;
  EndIf
  ;
  ProcedureReturn lResult
  ;
EndProcedure
;- Main Program Start
EventID.l
; Variable to hold the window message.
DoQuit.b
; Variable to control whether we quit the window or not.  Automatically set to #False.
If OpenWindow(#WindowMain, 0, 0, 400, 120, "Listview Header Test", #PB_Window_MinimizeGadget | #PB_Window_ScreenCentered | #PB_Window_SystemMenu | #PB_Window_TitleBar)
  ; Create the main window.
  ;
  ;  AdvancedGadgetEvents(#True)
  ; Enable advanced gadget events.
  ListIconGadget(#ListPrimary, 0, 0, 200, 100, "First", 50, #PB_ListIcon_AlwaysShowSelection | #PB_ListIcon_FullRowSelect | #PB_ListIcon_GridLines)
  ; Create our primary listview control.
  ListIconGadget(#ListSecondary, GadgetX(#ListPrimary) + GadgetWidth(#ListPrimary) + 1, GadgetY(#ListPrimary), 200, 100, "First", 50, #PB_ListIcon_AlwaysShowSelection | #PB_ListIcon_FullRowSelect | #PB_ListIcon_GridLines)
  ; Create our secondary listview control.
  ButtonGadget(#ButtonClose, GadgetX(#ListSecondary) + GadgetWidth(#ListSecondary) - 100, GadgetY(#ListSecondary) + GadgetHeight(#ListSecondary) + 1, 100, 20, "Close")
  ; Create our close button.
  ;
  SetWindowCallback(@WindowCallback())
  ; Set the main window callback.
  AddGadgetColumn(#ListPrimary, 1, "Second", 50)
  AddGadgetColumn(#ListPrimary, 2, "Third", 50)
  ; Add some dummy columns to our primary listicon.
  AddGadgetColumn(#ListSecondary, 1, "Second", 50)
  AddGadgetColumn(#ListSecondary, 2, "Third", 50)
  ; Add some dummy columns to our secondary listicon.
  If CreatePopupMenu(#MenuPrimary)
    ; Create the popup menu used on our primary listview.
    MenuItem(#MenuPrimaryTest, "Show Primary Header Info")
    ; Create a test sub-menu item.
  EndIf
  ;
  If CreatePopupMenu(#MenuSecondary)
    ; Create the popup menu used on our secondary listview.
    MenuItem(#MenuSecondaryTest, "Show Secondary Header Info")
    ; Create a test sub-menu item.
  EndIf
  ;
  AddGadgetItem(#ListPrimary, -1, "1" + Chr(10) + "One" + Chr(10) + "This")
  AddGadgetItem(#ListPrimary, -1, "2" + Chr(10) + "Two" + Chr(10) + "is")
  AddGadgetItem(#ListPrimary, -1, "3" + Chr(10) + "Three" + Chr(10) + "a")
  AddGadgetItem(#ListPrimary, -1, "4" + Chr(10) + "Four" + Chr(10) + "test.")
  ; Add some items to our primary listview control.
  AddGadgetItem(#ListSecondary, -1, "1978" + Chr(10) + "The" + Chr(10) + "quick")
  AddGadgetItem(#ListSecondary, -1, "2001" + Chr(10) + "brown" + Chr(10) + "fox")
  AddGadgetItem(#ListSecondary, -1, "2005" + Chr(10) + "jumped" + Chr(10) + "over")
  AddGadgetItem(#ListSecondary, -1, "2050" + Chr(10) + "the" + Chr(10) + "two")
  AddGadgetItem(#ListSecondary, -1, "3009" + Chr(10) + "lazy" + Chr(10) + "dogs.")
  ; Add some items to our secondary listview control.
  Repeat
    ;
    EventID = WaitWindowEvent()
    ;
    If EventID = #PB_Event_CloseWindow
      ; Close the program.
      DoQuit = #True
      ;
    ElseIf EventID = #PB_Event_Menu
      ; Menu Events
      If EventMenu() = #MenuPrimaryTest
        ;
        MessageRequester("Primary Test", "The user clicked the second column in the primary listview control.", #PB_MessageRequester_Ok)
        ;
      ElseIf EventMenu() = #MenuSecondaryTest
        ;
        MessageRequester("Secondary Test", "The user clicked the first column in the secondary listview control.", #PB_MessageRequester_Ok)
        ;
      EndIf
      ;
    ElseIf EventID = #PB_Event_Gadget
      ; Control Events
      If EventGadget() = #ButtonClose And EventType() = #PB_EventType_LeftClick : DoQuit = #True : EndIf
      ; Clicked the close button, close the window.
    EndIf
    ;
  Until DoQuit = #True
  ;
EndIf 
The example code creates a simple window with two listview controls. Right click on the headers and you'll get various things that happen. I just added a couple different responses so you can get an idea of how it would work. For example, in the first listview control, right-clicking on the first column brings up an inputrequester. Typing a number (1, 2, 3 or 4) will select the matching line. You can add code to handle something after that. Clicking the second column displays a simple popup menu. You could use the popup menu for custom filtering or anything else. Up to you. The third column in the first listview control does nothing.

Hope it's helpful to someone else. I added comments just in case. Not extremely descriptive but they give a little info.