Announcement

Collapse
No announcement yet.

How to recursively build a treeview of folders and files.

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

  • How to recursively build a treeview of folders and files.

    Has anyone tried to do this; build a treeview of folders and files recursively?
    I'm having a bit of trouble in making more nodes under nodes...

    Code:
    #COMPILE EXE
    #DIM ALL
    
    #INCLUDE "WIN32API.INC"
    
    GLOBAL GL_hDlg          AS DWORD
    GLOBAL GL_hFont_1       AS DWORD
    
    GLOBAL GL_COUNTER       AS DWORD
    GLOBAL RECURSION_DEPTH  AS DWORD
    
     ENUM CONTROLS SINGULAR
      ENUM_CONTROLS_STARTING_NUMBER = 100
      TREEVIEW_1
    '  LABEL_1
     END ENUM
    
     ENUM MESSAGES SINGULAR
      ENUM_STARTING_MESSAGESS_NUMBER = %WM_APP
      START_APPLICATION
      DIALOG_END
     END ENUM
    
    '------------------------------------------------------------------------------
     FUNCTION PBMAIN () AS LONG
    
      OPEN "out.txt" FOR OUTPUT AS #1
    
      FONT NEW "Courier New", 36, 0, %ANSI_CHARSET TO GL_hFont_1
    
      MAIN_DIALOG
    
      CLOSE #1
    
     END FUNCTION
    '------------------------------------------------------------------------------
     FUNCTION MAIN_DIALOG()AS LONG
    
      LOCAL DIALOG_HEIGHT     AS LONG
      LOCAL DIALOG_WIDTH      AS LONG
      LOCAL DIALOG_STYLE      AS LONG
      LOCAL DIALOG_STYLE_EX   AS LONG
      LOCAL LISTBOX_STYLE     AS LONG
      LOCAL LISTBOX_STYLE_EX  AS LONG
    
      DIALOG_HEIGHT = 600
      DIALOG_WIDTH  = 1000
    
      DIALOG_STYLE    = %WS_POPUP OR %WS_CAPTION OR %WS_SYSMENU OR %WS_MINIMIZEBOX OR %WS_VISIBLE OR %DS_MODALFRAME OR %DS_3DLOOK OR %DS_NOFAILCREATE OR %DS_SETFONT OR %WS_THICKFRAME OR %DS_CENTER
      DIALOG_STYLE_EX = %WS_EX_CONTROLPARENT OR %WS_EX_LEFT OR %WS_EX_LTRREADING OR %WS_EX_RIGHTSCROLLBAR
    
      DIALOG NEW PIXELS, %HWND_DESKTOP, "Recursive File Catalog", 10, 10, DIALOG_WIDTH, DIALOG_HEIGHT, DIALOG_STYLE, DIALOG_STYLE_EX, TO GL_hDlg
    
      'CONTROL ADD LABEL, GL_hDlg, %LABEL_1, "", 10, (DIALOG_HEIGHT/2), DIALOG_WIDTH-20, (DIALOG_HEIGHT/2), %WS_CHILD OR %WS_VISIBLE OR %SS_CENTER, %WS_EX_LEFT OR %WS_EX_LTRREADING
      'CONTROL SET FONT GL_hDlg, %LABEL_1, hFont_1
    
      CONTROL ADD TREEVIEW, GL_hDlg, %TREEVIEW_1, "", 10, 10, DIALOG_WIDTH-20, DIALOG_HEIGHT-20
    
      DIALOG SHOW MODAL GL_hDlg, CALL MAIN_DIALOG_CALLBACK
    
    
     END FUNCTION
    '------------------------------------------------------------------------------
     CALLBACK FUNCTION MAIN_DIALOG_CALLBACK
    
      LOCAL DIALOG_WIDTH  AS LONG
      LOCAL DIALOG_HEIGHT AS LONG
    
        SELECT CASE AS LONG CB.MSG
    
          CASE %WM_INITDIALOG
            DIALOG POST GL_hDlg, %START_APPLICATION,0,0
    
          CASE %START_APPLICATION
            CATALOG_FILES(EXE.PATH$, "*")
    
    
          CASE %WM_SIZING
            'DIALOG GET CLIENT GL_hDlg TO DIALOG_WIDTH, DIALOG_HEIGHT
            'CONTROL SET SIZE  GL_hDlg, %TREEVIEW_1, DIALOG_WIDTH-20, DIALOG_HEIGHT - 20
    
          CASE %DIALOG_END
    
    
          CASE %WM_TIMER
          CASE %WM_CLOSE
          CASE %WM_DESTROY
    
        END SELECT
     END FUNCTION
    
    '------------------------------------------------------------------------------
     FUNCTION CATALOG_FILES(BYVAL FILE_DRIVE_PATH AS STRING, FILE_EXTENTION AS STRING) AS LONG
    
        LOCAL hRoot   AS DWORD
        LOCAL hParent AS DWORD
    
        LOCAL SEARCH_HANDLE      AS DWORD               ' Search handle
        LOCAL STRUCT_FIND_DATA   AS WIN32_FIND_DATA     ' FindFirstFile structure
        LOCAL CURRENT_PATH       AS ASCIIZ * %MAX_PATH  ' What to search for
        LOCAL cFileName          AS ASCIIZ * %MAX_PATH  ' What to search for
        LOCAL FILE_NAME          AS STRING
        LOCAL FULL_FILE_NAME     AS STRING
        LOCAL COUNTER            AS LONG
        LOCAL STRUCT_SystemTime  AS SYSTEMTIME
        LOCAL FILE_TAIL          AS STRING
        LOCAL FILE_ATTR          AS LONG
    
        INCR RECURSION_DEPTH
    
        TREEVIEW INSERT ITEM  GL_hDlg, %TREEVIEW_1, 0, %TVI_LAST, 0, 0, FILE_DRIVE_PATH TO hRoot
    
        PRINT #1, ""
        PRINT #1, STRING$(RECURSION_DEPTH, $TAB) + FILE_DRIVE_PATH
    
        CURRENT_PATH  = FILE_DRIVE_PATH & "*." + FILE_EXTENTION
        SEARCH_HANDLE = FindFirstFile(CURRENT_PATH, STRUCT_FIND_DATA)  'GET A SEARCH_HANDLE FOR FindNextFile API CALLS
    
        IF SEARCH_HANDLE <> %INVALID_HANDLE_VALUE THEN                 'IF IT IS A DIRECTORY CALL THIS FUNCTION RECURSIVLY
            DO
                IF (STRUCT_FIND_DATA.dwFileAttributes AND %FILE_ATTRIBUTE_DIRECTORY) <> %FILE_ATTRIBUTE_DIRECTORY THEN  'THIS IS A FILE NOT A DIRECTORY
    
                   cFileName = STRUCT_FIND_DATA.cFileName
    
                   TREEVIEW INSERT ITEM GL_hDlg, %TREEVIEW_1, hRoot, %TVI_LAST, 0, 0, cFileName TO hParent
                   PRINT #1, STRING$(RECURSION_DEPTH, $TAB) + FILE_DRIVE_PATH + cFileName
    
    
                END IF
            LOOP WHILE FindNextFile(SEARCH_HANDLE, STRUCT_FIND_DATA)
    
            CALL FindClose(SEARCH_HANDLE)
        END IF
    
    
        CURRENT_PATH  = FILE_DRIVE_PATH & "*"
        SEARCH_HANDLE = FindFirstFile(CURRENT_PATH, STRUCT_FIND_DATA)
    
    
    
       'THIS IS JUST DIRECTORY NAMES THAT MAKE A RECURSIVE CALL TO THE FUNCTION
        IF SEARCH_HANDLE <> %INVALID_HANDLE_VALUE THEN
            DO
                IF (STRUCT_FIND_DATA.dwFileAttributes AND %FILE_ATTRIBUTE_DIRECTORY) = %FILE_ATTRIBUTE_DIRECTORY AND (STRUCT_FIND_DATA.dwFileAttributes AND %FILE_ATTRIBUTE_HIDDEN) = 0 THEN  ' If dirs, but not hidden
    
                    'THIS IS INSIDE THE DIRECTORY LOOP HERE
                    'TEXT_TO_SCREEN STR$(TIMER)
    
                    IF STRUCT_FIND_DATA.cFileName <> "." AND STRUCT_FIND_DATA.cFileName <> ".." THEN          ' Not these..
                      CALL CATALOG_FILES(FILE_DRIVE_PATH & RTRIM$(STRUCT_FIND_DATA.cFileName, CHR$(0)) & "\", FILE_EXTENTION)
                    END IF
    
                END IF
            LOOP WHILE FindNextFile(SEARCH_HANDLE, STRUCT_FIND_DATA)
            CALL FindClose(SEARCH_HANDLE)  'WIN API FindClose
        END IF
    
       DECR RECURSION_DEPTH
    
     END FUNCTION
    '------------------------------------------------------------------------------

  • #2
    Hi David!

    It MUST be recursively?

    Comment


    • #3
      And, must the full file/folder list be generated in full?

      A TreeView generally only presents a fixed set of files and folders, depending which branches are expanded, like this ...

      Code:
      'Compilable Example:
      'Starting with c:\, this example displays all subfolders.  Each subfolder is
      'checked to see if it has children. If so, it is given a a place-holder child
      'node so that the + sign is displayed in front of the subfolder node.
      'When a node is expanded, the place-holder is removed and the tree is populated
      'real-time with subfolders of the node.  When a folder is collapsed, no action
      'is taken. The next time it is expanded, the subfolder list be be freshened.
      'When a folder is selected, the files in the folder are displayed in the listbox.
      #Compiler PBWin 9, PBWin 10
      #Compile Exe
      #Dim All
      %Unicode=1
      #Include "Win32API.inc"
      #Include "CommCtrl.inc"
      %IDC_TreeView = 500 : %IDC_ListBox = 501
      Global hDlg As Dword
      
      Function PBMain() As Long
         Local hItem As Dword
         Dialog New Pixels, 0, "File/Folder Browser",300,300,370,330, %WS_OverlappedWindow To hDlg
         Control Add ListBox, hDlg, %IDC_ListBox, ,  190, 15, 170, 320
         Control Add Treeview, hDlg, %IDC_TreeView, "TopLeft",  10, 15, 170, 300
         Treeview Insert Item hDlg, %IDC_TreeView, 0, %TVI_Last, 1,1,"c:\" To hItem
         Treeview Insert Item hDlg, %IDC_TreeView, hItem, %TVI_Last, 1,4,"n/a" To hItem
         Dialog Show Modal hDlg Call DlgProc
      End Function
      
      CallBack Function DlgProc() As Long
         Select Case Cb.Msg
            Case %WM_InitDialog
               Local hNode As Dword
               Treeview Get Root hDlg, %IDC_TreeView To hNode
               DisplayChildren hNode
               Treeview Set Expanded hDlg, %IDC_TreeView, hNode, %True
            Case %WM_Notify
               Select Case Cb.NmId
                  Case %IDC_TreeView
                     Select Case Cb.NmCode
                        Case %TVN_ItemExpanding
                           Local N As NM_TreeView Ptr
                           N = Cb.LParam
                           DisplayChildren @N.ItemNew.hItem
                        Case %TVN_SelChanged
                           DisplayFiles
                     End Select
               End Select
         End Select
      End Function
      
      Sub DisplayChildren (hParent As Dword)
         Local hChild, hTemp As Dword
         Local ChildDIR, ParentDIR, temp As String
         Local FullPath As String
      
         'get text of parent node that is is expanding
         Treeview Get Text hDlg, %IDC_TreeView, hParent To ParentDIR
         ParentDIR = Trim$(ParentDIR, "\")           'this turns c:\ into c:
      
         'remove all children to ensure currency of display and directory structure
         Do
            Treeview Get Child hDlg, %IDC_TreeView, hParent To hChild
            If hChild Then Treeview Delete hDlg, %IDC_TreeView, hChild
         Loop While hChild
      
         'add nodes for each subfolder
         ChildDir = Dir$(FullTreePath(hParent) + "\*.*", Only %SubDir)
         While Len(ChildDIR)
            Treeview Insert Item hDlg, %IDC_TreeView, hParent, %TVI_Last, 0, 0, ChildDIR To hTemp
            ChildDIR = Dir$
         Wend
      
         'add dummy child to each tree element whose folder has subfolders (causes TreeView to display + sign)
         Treeview Get Child hDlg, %IDC_TreeView, hParent To hChild
         While hChild
            FullPath = FullTreePath(hChild)
            temp = Dir$(FullPath + "\*.*", Only %SubDir)
            If Len(temp) Then Treeview Insert Item hDlg, %IDC_TreeView, hChild, %TVI_Last, 0, 0, "n/a" To hTemp
            Treeview Get Next hDlg, %IDC_TreeView, hChild To hChild
         Wend
      End Sub
      
      Function FullTreePath(ByVal hNode As Dword) As String
         'get full directory path for hNode
         Local hRoot As Dword
         Local FullPath, temp As String
         Do
            Treeview Get Text hDlg, %IDC_TreeView, hNode To temp
            FullPath = "\" + temp + FullPath
            Treeview Get Parent hDlg, %IDC_TreeView, hNode To hNode
         Loop Until hNode = 0
         Function = LTrim$(FullPath, "\")
      End Function
      
      Sub DisplayFiles
         Local temp As String
         Local hNode As Dword
         ListBox Reset hDlg, %IDC_ListBox
         ListBox Add hDlg, %IDC_ListBox, "<no files>"
         Treeview Get Select hDlg, %IDC_Treeview To hNode
         temp = Dir$(FullTreePath(hNode)+"\*.*")
         If Len(temp) Then ListBox Reset hDlg, %IDC_ListBox
         Do
            If Len(temp) Then ListBox Add hDlg, %IDC_ListBox, temp
            temp = Dir$
         Loop While Len(temp)
      End Sub
      
      'gbs_00526
      'Date: 03-10-2012
      ... where hunting for the newly exposed files/folders is done on demand.

      Comment


      • #4
        Do you really need to populate the whole thing initially?
        Wouldn't it be more responsive if you just create the next lower level when a branch is selected?
        That would mean only ever reading one directory and adding a limited number of nodes at a time.

        Edit; I see Gary has gone into a lot more detail while I was composing the above.

        Comment


        • #5
          "It MUST be recursively?" That is the way my current stuff works - figured it would be a good way to go.

          "Do you really need to populate the whole thing initially?" You all do have a good point there.

          Actually it hadn't occurred to me that you could do that, go back and add branches. I guess I don't have my head around treeview yet.




          Comment


          • #6
            Nice code Gary!

            Comment


            • #7
              This is what i was trying to do - except it fails after around 18,500

              Code:
              #COMPILE EXE
              #DIM ALL
              
              #INCLUDE "WIN32API.INC"
              
              GLOBAL GL_hDlg          AS DWORD
              GLOBAL GL_hFont_1       AS DWORD
              
              GLOBAL GL_COUNTER       AS DWORD
              GLOBAL RECURSION_DEPTH  AS DWORD
              
               ENUM CONTROLS SINGULAR
                ENUM_CONTROLS_STARTING_NUMBER = 100
                TREEVIEW_1
              '  LABEL_1
               END ENUM
              
               ENUM MESSAGES SINGULAR
                ENUM_STARTING_MESSAGESS_NUMBER = %WM_APP
                START_APPLICATION
                DIALOG_END
               END ENUM
              
              '------------------------------------------------------------------------------
               FUNCTION PBMAIN () AS LONG
              
                OPEN "out.txt" FOR OUTPUT AS #1
              
                FONT NEW "Courier New", 36, 0, %ANSI_CHARSET TO GL_hFont_1
              
                MAIN_DIALOG
              
                CLOSE #1
              
               END FUNCTION
              '------------------------------------------------------------------------------
               FUNCTION MAIN_DIALOG()AS LONG
              
                LOCAL DIALOG_HEIGHT     AS LONG
                LOCAL DIALOG_WIDTH      AS LONG
                LOCAL DIALOG_STYLE      AS LONG
                LOCAL DIALOG_STYLE_EX   AS LONG
                LOCAL LISTBOX_STYLE     AS LONG
                LOCAL LISTBOX_STYLE_EX  AS LONG
              
                DIALOG_HEIGHT = 600
                DIALOG_WIDTH  = 1000
              
                DIALOG_STYLE    = %WS_POPUP OR %WS_CAPTION OR %WS_SYSMENU OR %WS_MINIMIZEBOX OR %WS_VISIBLE OR %DS_MODALFRAME OR %DS_3DLOOK OR %DS_NOFAILCREATE OR %DS_SETFONT OR %WS_THICKFRAME OR %DS_CENTER
                DIALOG_STYLE_EX = %WS_EX_CONTROLPARENT OR %WS_EX_LEFT OR %WS_EX_LTRREADING OR %WS_EX_RIGHTSCROLLBAR
              
                DIALOG NEW PIXELS, %HWND_DESKTOP, "Recursive File Catalog", 10, 10, DIALOG_WIDTH, DIALOG_HEIGHT, DIALOG_STYLE, DIALOG_STYLE_EX, TO GL_hDlg
              
                'CONTROL ADD LABEL, GL_hDlg, %LABEL_1, "", 10, (DIALOG_HEIGHT/2), DIALOG_WIDTH-20, (DIALOG_HEIGHT/2), %WS_CHILD OR %WS_VISIBLE OR %SS_CENTER, %WS_EX_LEFT OR %WS_EX_LTRREADING
                'CONTROL SET FONT GL_hDlg, %LABEL_1, hFont_1
              
                CONTROL ADD TREEVIEW, GL_hDlg, %TREEVIEW_1, "", 10, 10, DIALOG_WIDTH-20, DIALOG_HEIGHT-20
              
                DIALOG SHOW MODAL GL_hDlg, CALL MAIN_DIALOG_CALLBACK
              
              
               END FUNCTION
              
              '------------------------------------------------------------------------------
               CALLBACK FUNCTION MAIN_DIALOG_CALLBACK
              
                LOCAL DIALOG_WIDTH  AS LONG
                LOCAL DIALOG_HEIGHT AS LONG
              
                  SELECT CASE AS LONG CB.MSG
              
                    CASE %WM_INITDIALOG
                      DIALOG POST GL_hDlg, %START_APPLICATION,0,0
              
                    CASE %START_APPLICATION
              '        CATALOG_FILES(EXE.PATH$, "*", 0)
                      CATALOG_FILES("C:\", "*", 0)
              
                      'SCREW_AROUND
              
              
                    CASE %WM_SIZING
                      DIALOG GET CLIENT GL_hDlg TO DIALOG_WIDTH, DIALOG_HEIGHT
                      CONTROL SET SIZE  GL_hDlg, %TREEVIEW_1, DIALOG_WIDTH-20, DIALOG_HEIGHT - 20
              
                    CASE %DIALOG_END
              
              
                    CASE %WM_TIMER
                    CASE %WM_CLOSE
                    CASE %WM_DESTROY
              
                  END SELECT
               END FUNCTION
              
              '------------------------------------------------------------------------------
               FUNCTION CATALOG_FILES(BYVAL FILE_DRIVE_PATH AS STRING, FILE_EXTENTION AS STRING, THE_ROOT AS DWORD) AS LONG
              
                 INCR GL_COUNTER
              
              
                 ' LOCAL hRoot   AS DWORD
                  LOCAL hParent AS DWORD
              
                  LOCAL SEARCH_HANDLE      AS DWORD               ' Search handle
                  LOCAL STRUCT_FIND_DATA   AS WIN32_FIND_DATA     ' FindFirstFile structure
                  LOCAL CURRENT_PATH       AS ASCIIZ * %MAX_PATH  ' What to search for
                  LOCAL cFileName          AS ASCIIZ * %MAX_PATH  ' What to search for
                  LOCAL FILE_NAME          AS STRING
                  LOCAL FULL_FILE_NAME     AS STRING
                  LOCAL COUNTER            AS LONG
                  LOCAL STRUCT_SystemTime  AS SYSTEMTIME
                  LOCAL FILE_TAIL          AS STRING
                  LOCAL FILE_ATTR          AS LONG
                  LOCAL THE_SUB_ROOT       AS DWORD
                  LOCAL THE_SUB_SUB_ROOT       AS DWORD
              
              
                  TREEVIEW INSERT ITEM GL_hDlg, %TREEVIEW_1,  THE_ROOT, %TVI_LAST, 0, 0, FORMAT$(GL_COUNTER, "0000000") + " " + FILE_DRIVE_PATH TO THE_SUB_ROOT               '0 puts it at the root
              
                  INCR RECURSION_DEPTH
              
              
                  PRINT #1, ""
                  PRINT #1, STRING$(RECURSION_DEPTH, $TAB) + FILE_DRIVE_PATH
              
                  CURRENT_PATH  = FILE_DRIVE_PATH & "*." + FILE_EXTENTION
                  SEARCH_HANDLE = FindFirstFile(CURRENT_PATH, STRUCT_FIND_DATA)  'GET A SEARCH_HANDLE FOR FindNextFile API CALLS
              
                  IF SEARCH_HANDLE <> %INVALID_HANDLE_VALUE THEN                 'IF IT IS A DIRECTORY CALL THIS FUNCTION RECURSIVLY
                      DO
                          IF (STRUCT_FIND_DATA.dwFileAttributes AND %FILE_ATTRIBUTE_DIRECTORY) <> %FILE_ATTRIBUTE_DIRECTORY THEN  'THIS IS A FILE NOT A DIRECTORY
              
                             cFileName = STRUCT_FIND_DATA.cFileName
              
                              TREEVIEW INSERT ITEM GL_hDlg, %TREEVIEW_1, THE_SUB_ROOT, %TVI_LAST, 0, 0, FORMAT$(RECURSION_DEPTH,"000") + " - " + cFileName TO THE_SUB_SUB_ROOT
              
                             PRINT #1, STRING$(RECURSION_DEPTH, $TAB) + FILE_DRIVE_PATH + cFileName
              
              
                          END IF
                      LOOP WHILE FindNextFile(SEARCH_HANDLE, STRUCT_FIND_DATA)
              
                       TREEVIEW SET EXPANDED GL_hDlg, %TREEVIEW_1,  THE_SUB_ROOT, %True
              
                      CALL FindClose(SEARCH_HANDLE)
                  END IF
              
                  TREEVIEW SET EXPANDED GL_hDlg, %TREEVIEW_1,  THE_ROOT, %True
              
              
                  CURRENT_PATH  = FILE_DRIVE_PATH & "*"
                  SEARCH_HANDLE = FindFirstFile(CURRENT_PATH, STRUCT_FIND_DATA)
              
              
              
                 'THIS IS JUST DIRECTORY NAMES THAT MAKE A RECURSIVE CALL TO THE FUNCTION
                  IF SEARCH_HANDLE <> %INVALID_HANDLE_VALUE THEN
                      DO
                          IF (STRUCT_FIND_DATA.dwFileAttributes AND %FILE_ATTRIBUTE_DIRECTORY) = %FILE_ATTRIBUTE_DIRECTORY AND (STRUCT_FIND_DATA.dwFileAttributes AND %FILE_ATTRIBUTE_HIDDEN) = 0 THEN  ' If dirs, but not hidden
              
                              IF STRUCT_FIND_DATA.cFileName <> "." AND STRUCT_FIND_DATA.cFileName <> ".." THEN          ' Not these..
                                CALL CATALOG_FILES(FILE_DRIVE_PATH & RTRIM$(STRUCT_FIND_DATA.cFileName, CHR$(0)) & "\", FILE_EXTENTION, THE_SUB_ROOT)
                              END IF
              
                          END IF
                      LOOP WHILE FindNextFile(SEARCH_HANDLE, STRUCT_FIND_DATA)
                      CALL FindClose(SEARCH_HANDLE)  'WIN API FindClose
                  END IF
              
                 DECR RECURSION_DEPTH
              
               END FUNCTION
              '------------------------------------------------------------------------------

              Comment


              • #8
                This is similar to what you are looking for, old code but should be servicable... https://forum.powerbasic.com/forum/u...cement?t=23778
                <b>George W. Bleck</b>
                <img src='http://www.blecktech.com/myemail.gif'>

                Comment

                Working...
                X