         title    Treeview

         .586
         .model   flat, STDCALL
         option   casemap: none   ; case sensitive

         include  TabTree.inc


.const
AppName        db  'Tree View',0
TreeClass      db  'SysTreeView32',0   
szRootPath     db  'C:\',0
szError3       db  'The above path for the tree was not found!',0

.data?
hInst          dd  ?       
hWndMain       dd  ?
hItem          dd  ?
iSeparatorPos  dd  ?
hTreeImageList dd  ?
hMyComputer    dd  ?
hDrives        dd  27 dup(?)
szDrvLet       db  27 dup(?)

.data
Loaded         dd  0
ExpCol         dd  0

.code

;==========================
; InitTreeView PROCEDURE
;==========================
InitTreeView proc hinst:DWORD, hwnd:DWORD
LOCAL    rect:RECT
LOCAL    lpsf:DWORD
LOCAL    sfi:SHFILEINFO

      INVOKE     GetClientRect, hwnd, addr rect     
         mov     eax, rect.right
         shr     eax, 1
         dec     eax

      INVOKE     CreateWindowEx, WS_EX_CLIENTEDGE, addr TreeClass, 0,\
                 WS_CHILD or WS_VISIBLE or TVS_HASLINES or TVS_HASBUTTONS or TVS_LINESATROOT,\
                 0, 25, 123, 100, hwnd, ID_TREEVIEW, hinst, NULL
        test     eax, eax     
          jz     Ret0
         mov     hWndTree, eax

;---------- [Get the shell image list for the tree view] ----------
      INVOKE     SHGetFileInfo, addr szRootPath, 0, addr sfi, sizeof SHFILEINFO,\
                 SHGFI_SYSICONINDEX or SHGFI_SMALLICON
        test     eax, eax
          jz     Ret0
         mov     hTreeImageList, eax 
      INVOKE     SendMessage, hWndTree, TVM_SETIMAGELIST, 0, eax

      INVOKE     SHGetDesktopFolder, addr lpsf
        test     eax, eax
          js     Ret0

;---------- [Set the tree colors] ----------
;      INVOKE     SendMessage, hWndTree, TVM_SETBKCOLOR, 0, 00000000h
;      INVOKE     SendMessage, hWndTree, TVM_SETTEXTCOLOR, 0, 00ffff00h
;      INVOKE     SendMessage, hWndTree, TVM_SETLINECOLOR, 0, 00ffff00h

;---------- [Fill the window with the root items] ----------
      INVOKE     UpdateTreeView, hWndTree, lpsf, 0, TVI_ROOT

;---------- [Free the shell folder pointer] ----------
         mov     eax, lpsf
         mov     eax,[eax]
      INVOKE     (IShellFolder ptr [eax]).Release, lpsf
      INVOKE     SetFocus, hWndTree
      INVOKE     SendMessage, hWndTree, TVM_SELECTITEM, TVGN_CARET, hMyComputer
      INVOKE     SendMessage, hWndTree, TVM_EXPAND, TVE_EXPAND, hMyComputer
         mov     Loaded, 44
         jmp     Ret0   

Ret0:
         xor     eax, eax

return:
         ret
InitTreeView endp

;-----------------------------------------------------------------------
UpdateTreeView proc uses ebx edx hwnd:DWORD, pshfParent, pidlFull, htreeParent
LOCAL    peidl:LPENUMIDLIST
LOCAL    pidl:DWORD
LOCAL    pidlFullCur:DWORD
LOCAL    ptvobj:LPTV_OBJDATA
LOCAL    tvis:TV_INSERTSTRUCT
LOCAL    sfi:SHFILEINFO
LOCAL    strret:STRRET
LOCAL    szWork[64]:BYTE
LOCAL    FirstItem:DWORD, dwAttr, Cnt, hItemI

         mov     edx, pshfParent
         mov     edx, [edx]
      INVOKE     (IShellFolder PTR [edx]).EnumObjects, pshfParent, hwnd, SHCONTF_FOLDERS or\
                 SHCONTF_NONFOLDERS or SHCONTF_INCLUDEHIDDEN, addr peidl
        test     eax, eax
          js     RetError
         mov     FirstItem, 1
         and     Cnt, 0
         mov     byte ptr szDrvLet, 0

ObjectLoop:
         mov     edx, peidl
         mov     edx, [edx]
      INVOKE     (IEnumIDList PTR [edx]).Next, peidl, 1, addr pidl, NULL
        test     eax, eax
          js     RetError
         cmp     eax, S_FALSE    ;no more items
          jz     RetOk

;---------- [Check to see if its a folder; if its a folder, add it to the tree] ----------
         mov     dwAttr, SFGAO_HASSUBFOLDER or SFGAO_FOLDER
         mov     edx, pshfParent
         mov     edx, [edx]
      INVOKE     (IShellFolder PTR [edx]).GetAttributesOf, pshfParent, 1, addr pidl, addr dwAttr
        test     eax, eax
          js     RetError

      .if dwAttr & (SFGAO_HASSUBFOLDER or SFGAO_FOLDER)
         .if dwAttr & SFGAO_FOLDER

            .if dwAttr == 20000000h
                  jmp     ObjectLoop
            .endif

;---------- [Save TV_OBJDATA] ----------
               mov     eax, pShellMalloc
               mov     eax, [eax]
            INVOKE     (IMalloc ptr [eax]).Alloc, pShellMalloc, sizeof TV_OBJDATA              
              test     eax, eax
                jz     RetError
               mov     ptvobj, eax                         
               mov     ebx, eax            
;DSPValue hwnd, pidlFull

            INVOKE     IDList_Combine, pidlFull, pidl  
               mov     (TV_OBJDATA PTR [ebx]).pidlFull, eax
               mov     pidlFullCur, eax
            INVOKE     IDList_CopyItem, pidl
               mov     (TV_OBJDATA PTR [ebx]).pidlRel, eax                    
               mov     eax, pshfParent
               mov     (TV_OBJDATA PTR [ebx]).pshfParent, eax
               mov     tvis.item.lParam, ebx
         
;---------- [Save tree item data] ----------
               mov     eax, htreeParent
               mov     tvis.hParent, eax
               mov     tvis.item.hItem, 0  
               mov     tvis.hInsertAfter, TVI_LAST
               mov     tvis.item.imask, TVIF_TEXT or TVIF_IMAGE or TVIF_SELECTEDIMAGE or TVIF_PARAM
            .if dwAttr & SFGAO_HASSUBFOLDER
                  mov     tvis.item.cChildren, TRUE
                   or     tvis.item.imask, TVIF_CHILDREN                         
            .endif

            INVOKE     SHGetFileInfo, pidlFullCur, 0, addr sfi, sizeof SHFILEINFO,
                       SHGFI_PIDL or SHGFI_DISPLAYNAME ; SHGFI_TYPENAME causes a problem
              test     eax,eax
                jz     RetError
               mov     tvis.item.cchTextMax, 255
               lea     eax, sfi.szDisplayName
               mov     tvis.item.pszText, eax

            INVOKE     lstrlen, eax
               sub     eax, 2
               mov     ebx, tvis.item.pszText
               add     ebx, eax
               mov     eax, [ebx]
            .if ax != '):' && !FirstItem && !Loaded
                  jmp     ObjectLoop
             .endif

            INVOKE     GetIcon, pidlFullCur, SHGFI_ICON
               mov     tvis.item.iImage, eax
            INVOKE     GetIcon, pidlFullCur, SHGFI_OPENICON 
               mov     tvis.item.iSelectedImage, eax

;---------- [Add item to the tree] ----------
            INVOKE     SendMessage, hwnd, TVM_INSERTITEM, 0, addr tvis
              test     eax, eax
                jz     RetError
               mov     hItemI, eax
            .if FirstItem == TRUE
                  mov     hMyComputer, eax
                  mov     FirstItem, 0
            .else

;---------- [Build the the drive list and drive handles] ----------
               INVOKE     lstrcpy, addr szWork, tvis.item.pszText
               INVOKE     lstrlen, addr szWork
                  sub     eax, 3
                  lea     ebx, szWork
                  add     ebx, eax
               .if word ptr [ebx+1] == '):'
                  INVOKE     lstrcpyn, addr szWork, ebx, 2
                     add     szWork, 32                             ; Convert to lower case
                  INVOKE     lstrcat, addr szDrvLet, addr szWork    ; szDrvLet = drive letters
                     .if byte ptr szWork > 96 && byte ptr szWork < 123  ; All drives
                           mov     ecx, hItemI
                           mov     eax, Cnt
                           dec     eax
                           mov     hDrives+[eax*4], ecx             ; Drive handles
                           inc     Cnt
                  .endif
               .endif
            .endif
         .endif     
      .endif
         mov     eax, pShellMalloc
         mov     eax, [eax]
      INVOKE     (IMalloc PTR [eax]).Free, pShellMalloc, pidl
         jmp     ObjectLoop

;!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
;RetOk:
         mov     eax, peidl
         mov     eax, [eax]
      INVOKE     (IEnumIDList PTR [eax]).Release, peidl

RetOk:
      INVOKE     lstrcat, addr szDrvLet, 0
         ret

RetError:
; Do Message
         ret
UpdateTreeView endp

;-----------------------------------------------------------------------
TreeViewNotify proc uses ebx edx hwnd:DWORD, pnmtv
LOCAL    pshf:DWORD
LOCAL    pidl:DWORD
LOCAL    ptvobj:LPTV_OBJDATA
LOCAL    strret:STRRET
LOCAL    tvhti:TV_HITTESTINFO
LOCAL    tvi:TV_ITEM
LOCAL    sfi:SHFILEINFO
LOCAL    pt:POINT

         mov     eax, pnmtv
         mov     ebx, (NM_TREEVIEW ptr [eax]).itemNew.lParam 
         mov     ptvobj, ebx
         mov     eax, (NM_TREEVIEW ptr [eax]).hdr.code
         cmp     eax, TVN_ITEMEXPANDING
          je     item_expanding
         cmp     eax, TVN_SELCHANGEDA
          je     sel_changed
         jmp     RetOk

;---------- [Item is expanding...] ----------
item_expanding:  
         mov     ebx, pnmtv
        push     (NM_TREEVIEW ptr [ebx]).action
         pop     ExpCol

        test     (NM_TREEVIEW ptr [ebx]).itemNew.state, TVIS_EXPANDEDONCE 
         jnz     RetOk

         mov     ebx, ptvobj         
         mov     edx, (TV_OBJDATA ptr [ebx]).pshfParent
         mov     edx, [edx]
      INVOKE     (IShellFolder PTR [edx]).BindToObject, (TV_OBJDATA ptr [ebx]).pshfParent, 
                 (TV_OBJDATA ptr [ebx]).pidlRel, 0, addr IID_IShellFolder, addr pshf 
        test     eax, eax
          js     RetError                  

         mov     ebx, pnmtv
         mov     edx, ptvobj
      INVOKE     UpdateTreeView, hWndTree, pshf, (TV_OBJDATA ptr [edx]).pidlFull, (NM_TREEVIEW ptr [ebx]).itemNew.hItem

      .if Loaded
            mov     ebx, pnmtv
         INVOKE     SendMessage, hWndTree, TVM_SORTCHILDREN, 0, (NM_TREEVIEW ptr [ebx]).itemNew.hItem
            mov     ebx, pnmtv
         INVOKE     SendMessage, hWndTree, TVM_ENSUREVISIBLE, 0, (NM_TREEVIEW ptr [ebx]).itemNew.hItem
      .endif

         mov     eax, pshf
         mov     eax, [eax]
      INVOKE     (IShellFolder ptr [eax]).Release, pshf
         jmp     RetOk              

;---------- [Update list view header to give full path name] ----------
sel_changed:
      INVOKE     SHGetDesktopFolder, addr pshf
        test     eax, eax
          js     RetError

         mov     ebx, pnmtv
      INVOKE     SendMessage, hWndTree, TVM_ENSUREVISIBLE, 0, (NM_TREEVIEW ptr [ebx]).itemNew.hItem

         mov     ebx, ptvobj
         mov     edx, (TV_OBJDATA ptr [ebx]).pidlFull
      INVOKE     SHGetPathFromIDList, edx, addr WorkBuff
        test     eax,eax
;          jz     RetError ;!!!!!!!!!!!!!!!!!!!!!!

;---------- [Display the Path] ----------
      .if Loaded && ExpCol != 1
         INVOKE     ReadTheFile
      .endif
         and     ExpCol, 0
         mov     eax, pshf
         mov     eax, [eax]
      INVOKE     (IShellFolder ptr [eax]).Release, pshf
         jmp     RetOk              

RetOk:
         mov     eax, TRUE
         ret

RetError:
         xor     eax, eax   
         ret
TreeViewNotify endp

;-----------------------------------------------------------------------
IDList_GetSize proc uses ebx pidl:DWORD
LOCAL    cbTotal:DWORD

         mov     cbTotal, 0
      .if pidl != 0
            mov     ebx, pidl

StartLoop:
          movsx     eax, (ITEMIDLIST PTR [ebx]).mkid.cb
           test     eax, eax
             jz     EndLoop
            add     cbTotal, eax
            add     ebx, eax
            jmp     StartLoop

EndLoop:                                   
      .endif       
         mov     eax, cbTotal    
         ret
IDList_GetSize endp

;-----------------------------------------------------------------------
IDList_CopyItem  proc uses ebx edx ecx edi esi pidl:DWORD
LOCAL    pidlNew:DWORD
LOCAL    sz:DWORD
      
         mov     ebx, pidl 
        movsx    edx, (ITEMIDLIST PTR [ebx]).mkid.cb
         add     edx, sizeof WORD
         mov     sz, edx
        
         mov     edx, pShellMalloc
         mov     edx, [edx]
      INVOKE     (IMalloc PTR [edx]).Alloc, pShellMalloc, sz
      
        test     eax, eax
          jz     Ret0
         mov     pidlNew, eax

         mov     esi, pidl
         mov     edi, pidlNew
         mov     ecx, sz
      .while (ecx)
            mov     al, byte ptr [esi]
            mov     byte ptr [edi], al       
            dec     ecx            
            inc     esi
            inc     edi
      .endw
      
RetOk: 
         mov     eax, pidlNew
         ret
      
Ret0:
         xor          eax, eax
         ret        
IDList_CopyItem  endp

;-----------------------------------------------------------------------
IDList_Combine proc uses esi edi ecx edx pidl1:DWORD, pidl2
LOCAL    pidlNew:DWORD
LOCAL    cb1:DWORD
LOCAL    cb2:DWORD
      
;---------- [Get length of 1st pidl (not including NULL terminating word)] ----------
      INVOKE     IDList_GetSize, pidl1
         mov     cb1, eax
      
;---------- [Get length of 2nd pidl (not including NULL terminating word)] ----------
      INVOKE     IDList_GetSize, pidl2
         mov     cb2, eax
      
;---------- [Get total length of resulting combined pidl (including NULL terminating word)] ----------
         mov     eax, sizeof ITEMIDLIST.mkid.cb
         add     eax, cb1
         add     eax, cb2  
        
;---------- [Allocate the memory] ----------
         mov     edx, pShellMalloc
         mov     edx, [edx]
      INVOKE     (IMalloc PTR [edx]).Alloc, pShellMalloc, eax
      
        test     eax, eax
          jz     RetError
         mov     pidlNew, eax
         mov     edi, eax
      
;---------- [Write 1st pidl to destination (pidlNew)] ----------
         mov     esi, pidl1
         mov     ecx, cb1
      .while (ecx)
            mov     al, byte ptr [esi]
            mov     byte ptr [edi], al       
            dec     ecx            
            inc     esi
            inc     edi
      .endw             

;---------- [Write 2nd pidl after 1st] ----------
         mov     esi, pidl2
         mov     ecx, cb2
      .while (ecx)
            mov     al, byte ptr [esi]
            mov     byte ptr [edi], al       
            dec     ecx            
            inc     esi
            inc     edi
      .endw             
      
;---------- [Add terminating word] ----------
         mov     word ptr [edi], 0
         mov     eax, pidlNew
         ret

RetError:
         int     3
         xor     eax, eax
         ret        
IDList_Combine   endp

;-----------------------------------------------------------------------
; dwType = SHGFI_OPENICON or SHGFI_ICON
GetIcon proc uses edx pidl:DWORD, dwType
LOCAL    shfi:SHFILEINFO
        
         mov     edx, SHGFI_PIDL or SHGFI_SYSICONINDEX or SHGFI_SMALLICON
          or     edx, dwType

;---------- [SHGetfileinfo requires full pidl] ----------
      INVOKE     SHGetFileInfo, pidl, 0, addr shfi, sizeof SHFILEINFO, edx
         mov     eax, shfi.iIcon
         ret
GetIcon endp

;========================================================================
; Expand Tree to proper branch converts a path to a tree handle PROCEDURE
;========================================================================
TreeBranch proc uses ebx esi  LoadPath:DWORD
LOCAL    tvi:TV_ITEM
LOCAL    LevelCnt:DWORD, Cnt, CntL, dwState, Match
LOCAL    szPath[256]:byte, szWork[64], szBuff[64]

      INVOKE     SendMessage, hWndTree, TVM_GETNEXTITEM, TVGN_ROOT, 0
      .if !eax
            ret
      .endif
         mov     hItem, eax
         and     CntL, 0
         and     Match, 0
      INVOKE     lstrcpy, addr szPath, LoadPath

;---------- [Get the first path level] ----------
         lea     esi, szPath
         mov     LevelCnt, 1
      .while (byte ptr[esi]) != 0
         .if byte ptr[esi] == '\'
               add     LevelCnt, 1
         .endif
         inc     esi
      .endw

         xor     eax, eax
         lea     esi, szPath
      .while (byte ptr[esi]) != 0
         .if byte ptr[esi] == '\'
               inc     eax
            INVOKE     lstrcpyn, addr szBuff, addr szPath, eax
               inc     esi
            INVOKE     lstrcpy, addr szPath, esi
            .break
         .endif
         inc     eax
         inc     esi
      .endw
         mov     dwState, TVIS_BOLD

;---------- [Get the drive handle from the drive letter] ----------
      .if byte ptr szBuff >= 'A' && byte ptr szBuff <= 'Z'
            add     byte ptr szBuff, 20h
      .endif
         and     Cnt, 0
      INVOKE     lstrlen, addr szDrvLet
         mov     ecx, eax
         xor     eax, eax
         mov     dl, byte ptr szBuff
      .while (ecx)
         .if dl == byte ptr szDrvLet+[eax]
            .break
         .endif
            inc     eax
            dec     ecx
      .endw
      .if ecx
         .if eax
               dec     eax
         .endif
            mov     edx, hDrives+[eax*4]
            mov     hItem, edx
         INVOKE     SendMessage, hWndTree, TVM_EXPAND, TVE_EXPAND, hItem
            jmp     NextLev
      .else
         INVOKE     MessageBox, NULL, addr szError3, addr szLoadPath, MB_OK or MB_ICONINFORMATION
            ret
      .endif
         xor     ebx, ebx

;---------- [Use the path to expand the tree] ----------
      .while (ebx < LevelCnt)
         .if byte ptr szPath == 0   ; Last level
               and     Match, 0
               mov     dwState, TVIS_SELECTED or TVIS_BOLD
         .endif

;------------------- [First item of tree] -------------------
         INVOKE     SendMessage, hWndTree, TVM_GETNEXTITEM, TVGN_CHILD, hItem
           test     eax, eax
            jz      ret_ok
            mov     hItem, eax
          MOVmd     tvi.hItem, hItem
            mov     tvi.imask, TVIF_TEXT
            lea     eax, szWork
            mov     tvi.pszText, eax
            mov     tvi.cchTextMax, 64
         INVOKE     SendMessage, hWndTree, TVM_GETITEM, NULL, addr tvi
         .if byte ptr szBuff+1 == ':'
            INVOKE     lstrlen, addr szWork
               sub     eax, 3
               lea     ebx, szWork
               add     ebx, eax
            .if word ptr [ebx+1] == '):'
               INVOKE     lstrcpyn, addr szWork, ebx, 3
            .endif
         .endif
         INVOKE     lstrcmpi, addr szWork, addr szBuff
         .if eax == 0
             MOVmd     tvi.hItem, hItem
               mov     tvi.imask, TVIF_STATE
             MOVmd     tvi.state, dwState ;TVIS_SELECTED
               mov     tvi.stateMask, 000000ffh
            INVOKE     SendMessage, hWndTree, TVM_SETITEM, NULL, addr tvi
            INVOKE     SendMessage, hWndTree, TVM_EXPAND, TVE_EXPAND, hItem
               inc     Match
               jmp     NextLev
         .endif

;------------------- [Greater then first item of tree] -------------------
         INVOKE     SendMessage, hWndTree, TVM_GETNEXTITEM, TVGN_NEXT, hItem
         .while (eax != 0) 
               mov     hItem, eax
             MOVmd     tvi.hItem, hItem
               mov     tvi.imask, TVIF_TEXT
               lea     eax, szWork
               mov     tvi.pszText, eax
               mov     tvi.cchTextMax, 64
            INVOKE     SendMessage, hWndTree, TVM_GETITEM, NULL, addr tvi
            .if byte ptr szBuff+1 == ':'
               INVOKE     lstrlen, addr szWork
                  sub     eax, 3
                  lea     ebx, szWork
                  add     ebx, eax
               .if word ptr [ebx+1] == '):'
                  INVOKE     lstrcpyn, addr szWork, ebx, 3
               .endif
            .endif
            INVOKE     lstrcmpi, addr szWork, addr szBuff
            .if eax == 0
                MOVmd     tvi.hItem, hItem
                  mov     tvi.imask, TVIF_STATE
                MOVmd     tvi.state, dwState ;TVIS_SELECTED
                  mov     tvi.stateMask, 000000ffh
               INVOKE     SendMessage, hWndTree, TVM_SETITEM, NULL, addr tvi
               INVOKE     SendMessage, hWndTree, TVM_EXPAND, TVE_EXPAND, hItem
                  inc     Match
               .break
            .endif
            INVOKE     SendMessage, hWndTree, TVM_GETNEXTITEM, TVGN_NEXT, hItem
         .endw

;------------------- [Get the directory for the next level] -------------------
NextLev:
         .if byte ptr szPath == 0 && Match
            INVOKE     SendMessage, hWndTree, TVM_SELECTITEM, TVGN_FIRSTVISIBLE, hItem
            INVOKE     SendMessage, hWndTree, TVM_SELECTITEM, TVGN_CARET, hItem
               jmp     ret_ok
         .elseif byte ptr szPath == 0 && !Match
            INVOKE     MessageBox, NULL, addr szError3, addr szLoadPath, MB_OK or MB_ICONINFORMATION
               jmp     ret_ok
         .endif
            mov     szBuff, 0
            xor     eax, eax
            lea     esi, szPath
         .while (byte ptr[esi]) != 0
            .if byte ptr[esi] == '\'
                  inc     eax
               INVOKE     lstrcpyn, addr szBuff, addr szPath, eax
                  inc     esi
               INVOKE     lstrcpy, addr szPath, esi
               .break
            .endif
            inc     eax
            inc     esi
         .endw
         .if byte ptr szBuff == 0
               jmp     ret_ok
         .endif
            add     CntL, 1
            mov     ebx, CntL
      .endw

ret_ok:
         ret
TreeBranch endp

END
;INVOKE     MessageBox, NULL, addr szPath, addr AppName, MB_OK
