PUBLIC oForm As Explorer
oForm = CREATEOBJECT("Explorer")
oForm.Visible=.T.
* end of main
DEFINE CLASS Explorer As Form
Width=480
Height=500
AutoCenter=.T.
Caption="Folders"
Backcolor=RGB(255,255,255)
ShowWindow=2
ADD OBJECT tree As ExplorerTree WITH;
Left=20, Top=10, Width=440, Height=440,;
StartPath="c:\"
ENDDEFINE
DEFINE CLASS ExplorerTree As OleControl
OleClass="MSComctlLib.TreeCtrl"
oRoot=NULL && root node
StartPath=""
hWindow=0
DirectoryWatch_Msg=0
DirectoryWatch_Id=0
* only top level of subfolders to be scanned
FolderEnum_MaxEnumLevel=0
* the FLL populates all following properties
* for each subfolder found
FolderEnum_StartPath=""
FolderEnum_Status=0
FolderList_Status=0
FolderEnum_FolderCount=0
FolderEnum_PathValid=.F.
FolderList_FolderIndex=0
FolderList_Foldername=""
FolderList_HasSubfolders=.F.
FolderList_FolderPath=""
FolderList_ParentPath=""
FolderList_FolderLevel=0
FolderList_FolderSFGAOAttributes=0
FolderList_FolderAttributes=0
FolderList_TypeName=""
PROCEDURE Init
SET LIBRARY TO TreeViewExtender.fll ADDITIVE
WITH THIS
.HideSelection=.F.
.PathSeparator="\"
.Style=3
.LineStyle=1
.LabelEdit=1
.Indentation=16
.BorderStyle=0
.Appearance=0
.Sorted=.T.
.hWindow = ThisForm.HWnd
* Windows Message ID to be sent to
* the window defined by THIS.hWindow
.DirectoryWatch_Msg =;
TVX_GetNotificationMessageId()
IF VERSION(5) >= 900 && VFP9+
.Anchor=15
BINDEVENT( .hWindow, .DirectoryWatch_Msg,;
THIS, "WinProc" )
ENDIF
* links TreeView control
* to the system image list
TVX_SetImageList( .hwnd )
.RefreshTree
ENDWITH
PROCEDURE Destroy
WITH THIS
TVX_UnregisterDirectoryWatch(;
.hWindow, .DirectoryWatch_Id )
ENDWITH
PROCEDURE RefreshTree
IF NOT EMPTY( THIS.StartPath )
THIS.StartPath = THIS.StartPath
ENDIF
PROCEDURE StartPath_ASSIGN( cPath As String )
WITH THIS
.StartPath = m.cPath
.Nodes.Clear
.oRoot = .Nodes.Add(,,;
UPPER(ALLTRIM(.StartPath)),;
"Scanning "+.StartPath+"..." )
nResult = TVX_ListFolders( .StartPath, THIS )
.oRoot.Key = UPPER(ALLTRIM(.FolderEnum_StartPath))
.oRoot.Text = .FolderEnum_StartPath
.oRoot.Sorted = .T.
.oRoot.Selected = .T.
.RegisterDirectoryWatch
ENDWITH
PROCEDURE RegisterDirectoryWatch
WITH THIS
TVX_UnregisterDirectoryWatch(;
.hWindow, .DirectoryWatch_Id )
IF .FolderEnum_PathValid
.DirectoryWatch_Id =;
TVX_RegisterDirectoryWatch(;
.FolderEnum_StartPath, .hWindow )
ENDIF
ENDWITH
PROCEDURE WinProc(hWindow as Integer, nMsgID as Integer,;
wParam as Integer, lParam as Integer)
* ignored for VFP versions before VFP9
* this message notifies about changes in folders
IF nMsgID = THIS.DirectoryWatch_Msg;
AND m.wParam = THIS.DirectoryWatch_Id
THIS.GetDirectoryChanges
ENDIF
RETURN 0
PROCEDURE GetDirectoryChanges
LOCAL cBuffer
IF NOT USED("csChanges")
* the cursor is for demo purpose only
CREATE CURSOR csChanges ( dirchangedata M )
ENDIF
DO WHILE .T.
* pulling latest changes while supply lasts
cBuffer = TVX_GetDirectoryChanges(;
THIS.hWindow, THIS.DirectoryWatch_Id )
IF EMPTY(m.cBuffer)
EXIT
ELSE
INSERT INTO csChanges (dirchangedata);
VALUES (m.cBuffer )
THIS.RescanTree( m.cBuffer )
ENDIF
ENDDO
PROCEDURE RescanTree( cBuffer )
LOCAL nActionCount, nActionIndex, cAction,;
cPath, cParentPath, cNewPath, oNode
cNewPath=""
nActionCount = ALINES( arrActions,;
m.cBuffer, CHR(13)+CHR(10) )
* scans changes from newest to oldest
FOR nActionIndex=nActionCount TO 1 STEP -1
cAction = SUBSTR(arrActions[nActionIndex], 1, 1)
cPath = SUBSTR(arrActions[nActionIndex], 2)
cPath = STRTRAN(m.cPath, "\\", "\") && a bug to be pinned
cParentPath = JUSTPATH( m.cPath )
DO CASE
CASE INLIST( m.cAction, "1", "3" )
* new folder detected
* rescanning the parent path
oNode = THIS.GetNodeByKey( m.cParentPath )
IF NOT ISNULL(m.oNode)
TVX_ListFolders( m.cParentPath, THIS )
ENDIF
CASE m.cAction="2"
* a folder has been deleted
* deleting existing node
oNode = THIS.GetNodeByKey( m.cPath )
IF NOT ISNULL(m.oNode)
THIS.Nodes.Remove( oNode.Key )
ENDIF
CASE m.cAction="4"
* a folder has been renamed - storing old name
* changing Name and Key of existing node
oNode = THIS.GetNodeByKey( m.cPath )
IF NOT ISNULL(m.oNode)
oNode.Text = JUSTFNAME(m.cNewPath)
oNode.Key = UPPER(ALLTRIM(m.cNewPath))
THIS.RemoveChildren( oNode )
TVX_ListFolders( m.cNewPath, THIS )
ENDIF
cNewPath=""
CASE m.cAction="5"
* a folder has been renamed - storing new name
cNewPath = m.cPath
ENDCASE
NEXT
PROCEDURE Expand( oNode )
IF NOT oNode.Checked
RETURN
ENDIF
THIS.RescanNode( m.oNode )
PROCEDURE RescanNode( oNode )
THIS.RemovePlaceholderNode( oNode )
TVX_ListFolders( oNode.Key, THIS )
IF NOT THIS.FolderEnum_PathValid
THIS.Nodes.Remove( m.oNode.Key )
ELSE
oNode.Selected=.T.
ENDIF
PROCEDURE FolderEnum_Status_ASSIGN( nNewStatus As Number )
WITH THIS
.FolderEnum_Status = m.nNewStatus
DO CASE
CASE .FolderEnum_Status = 1
* FLL begins enumerating folder.
* Do not clear TreeView nodes here, because
* an enumeration may start on expanding a node,
* not just on populating the whole tree
CASE .FolderEnum_Status = 2
* FLL has finished enumerating folders
CASE .FolderEnum_Status = 5
* FLL begins sending folderes data to the control
CASE .FolderEnum_Status = 6
* FLL has finished sending floders data to the control
ENDCASE
ENDWITH
PROCEDURE FolderList_Status_ASSIGN( nNewStatus As Number )
WITH THIS
.FolderList_Status = m.nNewStatus
DO CASE
CASE .FolderList_Status = 1
* FLL begins sending data for next folder
CASE .FolderList_Status = 2
* FLL has finished sending data for a folder
.AddFolder
ENDCASE
ENDWITH
PROCEDURE RemoveChildren( oNode )
DO WHILE oNode.Children > 0
THIS.Nodes.Remove( oNode.Child.Key )
ENDDO
FUNCTION GetNodeByKey( cKey As String )
LOCAL oNode
TRY
oNode = THIS.Nodes( UPPER(ALLTRIM(m.cKey)) )
CATCH
oNode=NULL
ENDTRY
RETURN m.oNode
FUNCTION NodeExists( cKey As String )
RETURN VARTYPE( THIS.GetNodeByKey(m.cKey) ) = "O"
PROCEDURE GetParentNode( cKey As String )
LOCAL oNode
IF EMPTY(m.cKey)
oNode = NULL
ELSE
TRY
oNode=THIS.Nodes( UPPER(ALLTRIM(m.cKey)) )
CATCH
oNode=NULL
ENDTRY
ENDIF
RETURN m.oNode
PROCEDURE AddPlaceholderNode( oNode )
LOCAL cKey, oPlaceholder
cKey = oNode.Key + "\.."
IF NOT THIS.NodeExists(m.cKey)
oPlaceholder = .Nodes.Add( oNode, 4, m.cKey, ".." )
DOEVENTS
ENDIF
oNode.Checked=.T.
PROCEDURE RemovePlaceholderNode( oNode )
LOCAL cKey
cKey = oNode.Key + "\.."
IF THIS.NodeExists(m.cKey)
THIS.Nodes.Remove( m.cKey )
ENDIF
oNode.Checked=.F.
PROCEDURE AddFolder
LOCAL oParentNode, oNode, ex As Exception
WITH THIS
oNode = .GetNodeByKey( .FolderList_FolderPath )
IF VARTYPE(m.oNode)="O"
oNode.Text = .FolderList_Foldername
RETURN
ENDIF
oParentNode = .GetParentNode( .FolderList_ParentPath )
IF ISNULL(oParentNode)
oParentNode = THIS.oRoot
ELSE
.RemovePlaceholderNode( oParentNode )
ENDIF
TRY
oNode = .Nodes.Add( oParentNode, 4,;
UPPER(ALLTRIM(.FolderList_FolderPath)),;
.FolderList_Foldername )
IF .FolderList_HasSubfolders
.AddPlaceholderNode( oNode )
ENDIF
oNode.Sorted=.T.
oNode.Selected=.T.
CATCH TO ex
* subfolders in ZIP files
* the routine is to be completed
ACTIVATE SCREEN
? ex.Message
? THIS.FolderList_FolderPath
ENDTRY
ENDWITH
ENDDEFINE
|
Note: this is an alpha version of the library. More testing and cleaning to be done. The interface may eventually change.
Library functions:
FUNCTION TVX_ListFolders ( cStartPath As String,
oVFPObject As Object ) As Number
For a given path, enumerates subfolders and sends the data to a TreeView control (2nd parameter). The target control should have a configured set of properties.
For each subfolder found within the start path, the FLL populates certain properties of TreeView control. VFP application uses these properties when adds tree nodes.
FUNCTION TVX_GetNotificationMessageId () As Number
Returns window message Id assigned to notify VFP application about folder changes. Do not replace the call with a constant value. This message id can vary through different computers (see RegisterWindowMessage API).
FUNCTION TVX_RegisterDirectoryWatch ( cStartPath As String,
nHWnd As Number ) As Number
The nHWnd must belong to a VFP form. The function returns the MonitorId to be used in subsequent calls to other library functions.
Upon this call, the FLL starts monitoring specified path, subfolders included, for any folder-related changes. Notifications of such changes are periodically sent to the form through a window message. In VFP9 the messages can be picked up with BINDEVENT() function. The WParam contains MonitorId value. The LParam contains the number of folder change records waiting.
This function can be called multiple times for the same nHWnd, each time with different start path; as well as multiple times for the same start path, each time with different HWnd.
FUNCTION TVX_GetDirectoryChanges ( nHWnd As Number,
MonitorId As Number ) As String
For given HWnd and MonitorId, returns a text buffer containing formatted list of recent folder changes happened within the monitored path. This function should be called in a cycle, until it returns an empty string. VFP application should interpret the folder changes: add, delete and modify TreeView nodes accordingly.
In VFP9 application, this function should be called upon receiving the notification message. While VFP8 application cannot capture window messages natively, it still can obtain folder changes through calling the TVX_GetDirectoryChanges() periodically.
PROCEDURE TVX_UnregisterDirectoryWatch ( nHWnd As Number,
nDirectoryWatchId As Number )
For given HWnd and MonitorId, closes folder changes monitor.
PROCEDURE TVX_SetImageList ( nHWnd As Number )
Links TreeView ActiveX control, specified by its HWnd, to the system image list, enabling system images in tree nodes. |