VERSION 1.0 CLASS
BEGIN
  MultiUse = -1  'True
  Persistable = 0  'NotPersistable
  DataBindingBehavior = 0  'vbNone
  DataSourceBehavior  = 0  'vbNone
  MTSTransactionMode  = 0  'NotAnMTSObject
END
Attribute VB_Name = "ListBoxCopy"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = False
Attribute VB_PredeclaredId = False
Attribute VB_Exposed = False
Option Explicit

' This class provides a right-click Copy menu for use on Forms
' Note that the form needs to provide a Main menu with a Copy submenu
' (Used during standard menu editing for form)

Private Declare Function GlobalAlloc Lib "kernel32" (ByVal wFlags As Long, ByVal dwBytes As Long) As Long
Private Declare Function GlobalFree Lib "kernel32" (ByVal hMem As Long) As Long
Private Declare Function GlobalHandle Lib "kernel32" (wMem As Any) As Long
Private Declare Function GlobalLock Lib "kernel32" (ByVal hMem As Long) As Long
Private Declare Function GlobalSize Lib "kernel32" (ByVal hMem As Long) As Long
Private Declare Function GlobalUnlock Lib "kernel32" (ByVal hMem As Long) As Long

Private Const GMEM_MOVEABLE = &H2

Private Declare Function OpenClipboard Lib "user32" (ByVal hwnd As Long) As Long
Private Declare Function CloseClipboard Lib "user32" () As Long
Private Declare Function EmptyClipboard Lib "user32" () As Long
Private Declare Function SetClipboardData Lib "user32" (ByVal wFormat As Long, ByVal hMem As Long) As Long
Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (pDst As Any, pSrc As Any, ByVal ByteLen As Long)

Private Declare Function FindWindowA Lib "user32" _
    (ByVal lpClassName As String, ByVal lpWindowName As String) As Long

Private Const CF_TEXT = 1
Private Const CF_UNICODETEXT = 13

Private WithEvents m_Listbox As MSForms.ListBox
Attribute m_Listbox.VB_VarHelpID = -1
Private m_Form As MSForms.UserForm

Public Sub Initialize(f As MSForms.UserForm, l As MSForms.ListBox)
    Set m_Form = f
    Set m_Listbox = l
End Sub


Private Sub m_Listbox_KeyDown(ByVal KeyCode As MSForms.ReturnInteger, ByVal Shift As Integer)
    ' Trap Ctrl+C and treat as Edit Copy
    On Error GoTo Error_Block
    Dim t As Tracker: Set t = GStackTrace.Enter(TypeName(Me), "m_Listbox_KeyDown")
    Dim ShiftDown As Boolean
    Dim AltDown As Boolean
    Dim CtrlDown As Boolean
    
    ShiftDown = (Shift And vbShiftMask) > 0
    AltDown = (Shift And vbAltMask) > 0
    CtrlDown = (Shift And vbCtrlMask) > 0
   
    If KeyCode = vbKeyC And CtrlDown And Not ShiftDown And Not AltDown Then
        CopyListBoxSelectionsToClipboard m_Listbox
    ElseIf KeyCode = vbKeyA And CtrlDown And Not ShiftDown And Not AltDown Then
        ' Select all items
        Dim i As Integer
        For i = 0 To m_Listbox.ListCount - 1
            m_Listbox.Selected(i) = True
        Next
    End If
    Exit Sub

Error_Block:
    t.Report
End Sub

Private Sub UnicodeCopyToClipBoard(Msg As String)
    Dim t As Tracker: Set t = GStackTrace.Enter(TypeName(Me), "UnicodeCopyToClipBoard")
    Dim Result As Long
    Dim hGlb As Long
    Dim lpps As Long
    Dim hwndForm As Long
    
    hwndForm = FindWindowA("ThunderDframe", m_Form.Caption)

    ' We have to use API routines to ensure Unicode handled OK
    If OpenClipboard(hwndForm) Then
        EmptyClipboard
        
        Msg = Msg & vbNullChar
        hGlb = GlobalAlloc(GMEM_MOVEABLE, Len(Msg) * 2) ' Unicode is 2 bytes per char
        lpps = GlobalLock(hGlb)
        CopyMemory ByVal lpps, ByVal StrPtr(Msg), Len(Msg) * 2
        GlobalUnlock hGlb
        Result = SetClipboardData(CF_UNICODETEXT, hGlb)
        CloseClipboard
        
        'Note Clipboard does GlobalFree on hGlb
        If Result = vbNull Then
            err.Raise vbObjectError + 999, "UnicodeCopyToClipBoard", "Copy to clipboard failed!"
        End If
    End If

End Sub

Private Sub CopyListBoxSelectionsToClipboard(l As MSForms.ListBox)
    Dim x As Long
    Dim AccumulatedText As String
    
    With l
        For x = 0 To .ListCount - 1
            If .Selected(x) = True Then
                AccumulatedText = AccumulatedText & vbNewLine & .list(x)
            End If
        Next
    End With
    AccumulatedText = Mid$(AccumulatedText, Len(vbNewLine) + 1)
    
    If WindowsNTorGreater() Then
        UnicodeCopyToClipBoard AccumulatedText
    Else
        Clipboard.Clear
        Clipboard.SetText AccumulatedText, CF_TEXT
    End If
    
End Sub


Private Sub m_Listbox_MouseUp(ByVal Button As Integer, ByVal Shift As Integer, ByVal x As Single, ByVal y As Single)
    On Error GoTo Error_Block
    Dim t As Tracker: Set t = GStackTrace.Enter(TypeName(Me), "m_Listbox_MouseUp")
    ' See if right click and display appropriate menu if so
    Dim MenuChosen As Long
    
    If Button = vbRightButton Then
        Dim omenu As PopupMenu
        Set omenu = New PopupMenu
        MenuChosen = omenu.Popup("Copy")
        If MenuChosen = 1 Then
            CopyListBoxSelectionsToClipboard m_Listbox
        End If
    End If
    
    Exit Sub
    
Error_Block:
    t.Report
End Sub

