Unit SystemTreeView

-----------------------------------------------------------------------------} { This is BETA software. It is not guaranteed to do ANYTHING useful at all. } { I'm looking for useful feedback on problems that exist and major elements } { missing. Please read the "WORKING NOTES" section below for more information. } { -----------------------------------------------------------------------------} { -----------------------------------------------------------------------------} { R E A D T H I S N O W ! ! ! } { -----------------------------------------------------------------------------} { I hope that got your attention. This component requires a unit that } { provides access to the Win95/NT 4.0 Shell Namespace API. Delphi 2.0 did not } { include this import, but the v2.01 update does. Also, Mr. Pat Ritchey was } { kind enough to write one, which I think was the basis for Borland's (same } { bugs in both units, must be the same). Regardless of which you use, you } { will have to do some extra work because both contain errors that will } { prevent this component from working properly. To implement these changes, } { read the ShellFix.txt file that should have been included with this file. } { If you use Delphi 3, you will not need to worry about any of this. No bugs. } { -----------------------------------------------------------------------------} { -----------------------------------------------------------------------------} { A tree view control that acts as the tree in the Windows 95 Explorer. } { Copyright 1996, Brad Stowers. All Rights Reserved. } { This component can be freely used and distributed in commercial and private } { environments, provied this notice is not modified in any way. } { -----------------------------------------------------------------------------} { Feel free to contact me if you have any questions, comments or suggestions } { at bstowers@pobox.com. } { You can always find the latest version of this component at: } { http://www.pobox.com/~bstowers/delphi/ } { -----------------------------------------------------------------------------} { Date last modified: December 18, 1997 } { -----------------------------------------------------------------------------} { -----------------------------------------------------------------------------} { TSystemTreeView v0.70 } { -----------------------------------------------------------------------------} { } { Description: } { A tree view control that acts as the tree in the Windows 95 Explorer. } { } { -----------------------------------------------------------------------------} { } { WORKING NOTES: } { * I really need to rip all the namespace stuff out and put it in it's own } { class so that you could use it however you wanted, not just in this comp.} { * The included test project creates a TSystemTreeView object on the fly so } { that you don't have to install the component in your library. I'm } { fairly confident that the component is stable, but if you don't want to } { install, you don't have to just to see the demo. } { * Drag/Drop is begin worked on. } { * DisplayContextMenu doesn't work for root tree objects. Desktop doesn't } { have one, and I fixed that, but don't know what's wrong with the others. } { * If anyone knows how to determine the type of object you have given it's } { PIDL, I'd love to know it. Right now, if it's not a file system object, } { you just get it's name. I need to determine other types like the } { printer folder, control panel folder, network servers, network } { workgroups, etc. } { } { -----------------------------------------------------------------------------} { } { Beta Revision History: } { 0.00: + Initial alpha release to limited audience. } { 0.01: + Cleaned up the mess in the USES clauses and the test project. } { 0.02: + Updated to use ShlObj, available inthe Delphi 2.01 update. See } { NOTE above. Edit the DEFINES.INC file to control which namespace } { import unit you want to use. If $D201 is defined, you use } { Borland's, otherwise Pat Ritchey's. Also made some more changes to } { ShellObj to make it more compatible with Borland's ShlObj version. } { If you use Borland's, context menus won't work. See the comments in } { the DisplayContextMenu method for an explanation. } { + Context menus (right click) are working, but need a good round of } { testing. } { + Added some stuff to the demo project to help with testing. } { + Can now change RootFolder property during execution. Would blow up } { before if changed after initial assignment. } { rfRecycleBin still blows up... don't know why. It doesn't like } { the EnumObjects call in EnumerateFolders. } { + A lot of the code got rewritten to make context menus, icons, etc. } { work. A the Golden Rule of PIDLs is that the SH* functions take a } { fully qualified path PIDL, and the IShellFolder interface methods } { take a relative PIDL. I was using relative PIDLs everywhere. The } { result is that more things work now, but the code is probably even } { less stable than before. Bang on it. } { 0.03: + Finally got the damn icon problem fixed. The PIDL wrapper class } { was hosing me up somehow. Don't know what was wrong with it, but } { its pointer would disappear for some reason. Getting rid of it } { cleared up the problem. So much for OOP making my life easier. :) } { + Got ShowRoot working. Thanks to John Cardinal for that. } { + General code clean up. } { + While Mr. Ritchey acknowledged the declaration errors in his } { ShellObj unit, he has never answered my request to included a fixed } { version of it with this control. So, for now, I'm just going to } { include directions on how to fix everything and not worry about. } { See the SHELLFIX.TXT file. } { 0.10: + I Feel reasonably confident in the component now. All core } { functionality seems to be in place and working. } { + Added ability to rename folders. Thanks to Thomas AW Brown. } { + Fixed problem that would occur if you installed the component in } { Delphi (i.e. Component | Install). By default, the Items property } { is "stored", so all everything in it is written to the DFM file } { that the component is on and that includes the Data property of } { each of the tree nodes. This a pointer to memory we allocate, so } { having the pointer value stored wouldn't do a whole lot of good, } { would it? See the redeclaration of Items below. } { + Added a new unit, FileChange, which provides a TThread descendant } { that knows how to watch directories for changes in structure. It } { hasn't been well tested, so I have included it only inside $IFDEFs. } { It should automatically cause the selected node to be updated if } { another app makes changes to that directory's subdir structure. } { For example, compile and run the demo. Select a node that is a } { directory somewhere on your hard drive. Start explorer and select } { that same node in it. Add a new folder to the selected directory } { in Explorer. The demo app should automatically update the node to } { reflect the change. If you have problems with it, comment out the } { $DEFINE FILECHANGES line below. } { 0.11 + Deleted the $I DEFINES.INC file in demo app. Not needed any more. } { + Added GetNodePath. Returns full path given a TTreeNode. Returns } { '' if it is not a file system type node (i.e. control panel). } { + If selected node were deleted from another app, it could get pretty } { ugly. Fixed. } { + Added palette resource icon for component. Thanks to Basri Kanca } { for it. } { + Added three new functions, RenameNode, DeleteNode, AddNewNode. } { 0.20 + This unit requires complete boolean eval to be turned off. I've } { added the appropriate compiler switches to do this in case it is } { turned on in your project. } { + First crack at a TSystemListView component to compliment the tree } { view. It is way far from being as functionally complete as the tree } { view. Mostly it's to see if it works for everyone at it's simplest } { form. } { 0.21 + DisplayContextMenu doesn't work for root tree objects. Desktop } { doesn't have one, and I fixed that, but don't know what's wrong } { with the others. } { + Moved all strings into resource file (ErrorMsgs.r32) except for the } { debugging "mem leak" string. Doesn't seem to like loading strings } { in an exitproc (finalization section). } { + Could never make up my mind about whether to go with message boxes } { or exceptions, so I just added a public property that can be used } { to set your preference. I prefer message boxes since there is no } { good way to catch the exceptions in an app, so that is the default. } { + Added all the file system report info stuff for list view items } { (size, type, date). If anyone knows how to determine the type of } { object you have given it's PIDL, I'd love to know it. Right now, } { if it's not a file system object, you just get it's name. } { 0.30 + New feature: Set the value of CustomDir to a valid directory, and } { then set RootFolder to rfCustom, and the tree will allow you to } { browse from that directory as the root. Also, if you don't want } { the root to read something like 'c:\win\system', then use } { CustomDirCaption to override it. } { + Now using SHGetDataFromIDList, which is missing from SHLOBJ.PAS. } { See step five in ShellFix.txt. } { + If PopupMenu property is has a menu, it is displayed instead of the } { context menu. } { + TSystemListView now descends form TCustomListView as it should. } { + Fixed bug. I overrode the Change method of the tree, but forgot to } { call inherited method. This broke the OnChangeEvent - never called. } { + Fixed bug that caused serious problems for projects with one of } { these saved on a form. When you tried to reopen the form after it } { was closed, it would cause many strange things to happen, usually } { just a lockup. See the Loaded and CreateWnd methods for the fix. } { If you want detailed description, email me and I will explain it. } { + New feature: ShowFiles property. Allows the tree to display items } { other than folders in it. I guess ShowFiles isn't the best } { description, since also includes things like printers in the } { printers folder. Anyone got a better name for it? } { + TSystemListView object has some serious problems, so I've disabled } { it in test app until I get time to work on it. I strongly suggest } { you stay away from it in this version, unless you want to help me } { fix it. To reenable it in the test app, take out the NOLISTVIEW } { define at the top of TESTING.PAS. } { 0.40 + At this point, TSystemListView is just an experiment. If you have } { problems with it, I will try to help, but I will not devote much } { time to it. I'm mainly interested in the tree view. Now, if you } { have problem AND a fix, I'll be more than happy to hear about it. } { + Somehow I ended up with two versions of v0.30. I don't have any } { idea how it happened, and I've given up trying to merge the two } { versions and just gone back to the one with the most functionality. } { + Got rid of the SHGetDataFromIDList call. Only works correctly on } { NT 4 with service pack 2, so it's not really a viable alternative } { for this. } { + Sorting revisited. Thanks to Detlef Meister } { (Meister@rz.fhtw-berlin.de) for sending me some good code to start } { with for this. I think sorting is working very well now, but that } { is of course only on my machines. Please try it out, and especially} { pay attention to see if it sorts in the same order as Explorer. If } { you find that sorting is causing terrible problems for some reason, } { you can turn it off by undefining STV_DOSORTING below. If all goes } { well with it, I'll add it to the list view next. } { + Added read only Selection property. Returns the path name of the } { selected node (if it is a file system object). Working on making } { it read/write, but it's a pretty hairy problem. } { + CustomDir didn't like UNC names (\\somecomputer\somedirectory). } { Working a little better now, but still not right. } { + Many people have asked how to just display drives, i.e. RootFolder } { set to rfDrives but without the printers, control panels, etc. } { There is no direct support for this at the API level } { (SHGetSpecialFolderLocation), but I think I have found a way to do } { it. Try the new rfFileSystem root type. Let me know if you have } { any problems with it. It should have My Computer as the root node } { and then any device that has physical storage capability as child } { nodes. If you are interested, search for SFGAO_FILESYSANCESTOR to } { see what I'm doing. } { 0.50 + ShowRoot didn't work as it should (as TListView does). } { + rfFileSystem roots wouldn't expand farther than the first level. } { + Some serious problems with EAccessViolations after fiddling with } { the component for a while (change RootFolder, etc). I think I have } { this all fixed up now. AddRefs and Releases weren't quite right. } { + Selection property is now read and WRITE. That means you can do } { something like MyTree.Selection := 'c:\windows\system'; and it will } { select that node. Note that this only works with file/directory } { stuff, there is no way to do something like MyTree.Selection := } { Control Panel; Also note that the assignment value is relative to } { the root type. So, if you want to select a directory when } { RootFolder is rfDesktop or rfDrives, you pass the full pathname. } { If RootFolder was rfPrograms and your programs directory was } { 'c:\win\start menu\programs' and you wanted to select } { 'c:\win\start menu\programs\some folder\subfolder', you would do: } { MyTree.Selection := 'some folder\subfolder'; } { Many thanks to Raymond Chen for his help on walking a PIDL (similar } { to walking a dog, but without the pooper-scooper). } { + Added OnAddListItem and OnCreateColumns to the TSystemListView } { component. OnCreateColumns is called when ColumnType is set to } { ctUser. The old columns are removed and then the event is fired. } { The event is responsible for creating the columns it needs. } { OnAddListItem is called every time an item is added to the listview.} { It passes the new TListItem, which has the Caption property set to } { the name of the new item. You should set the ColumnType property } { to ctUser and create the columns you need in the OnCreateColumns } { event. Then, in the OnAddListItem event, you would add the data } { for these new columns by using the passed TListItem's SubItems.Add } { method. } { + Updated for compatibility with Delphi 3. } { 0.60 + Well, it compiles under Delphi 3, but it sure doesn't work. I will } { get to when I can. Sorry. } { + Added TSystemListView.GetItemAttrs public method so you can get the } { attrs of an item to do stuff like testing for if it is a folder: } { if (SLV.GetItemAttrs(SLF.Selected) and SFGAO_FOLDER) <> 0 then } { // It's a folder } { + Added TSystemListView.GetFullPath public method to get an item with } { it's complete path information. This obviously doesn't apply for } { things like printers and such. } { + Sorting in the TSystemListView should now work. Set SortType to } { something other than stNone. } { 0.65 + Yes, it's true; I FINALLY got it working under Delphi 3. I thought } { I was just really thick skulled, but turns out that Delphi 3 freaks } { out when you try to store COM object references in dynamically } { allocated records. I switched the record structure over to a class,} { and all works just fine... Go figure. Needed to be done anyway. } { Now I need to flesh out the new class so it's more OOP-ish. } { 0.70 + Turns out I was thick-skulled after all (see 0.65 notes). I was } { allocating the memory with GetMem, and that of course does not } { initialize the memory (i.e. make sure the pointer variables are set } { to NIL). So, I assigned something to the IShellFolder variable, } { and Delphi 3 checks the existing value and tries to Release it if } { it is not NIL. Well, it's just trash initially, so off it goes } { into the weeds... I could go back to the dynamic allocation method } { and use the Initialize function to clear it all, but the class is } { better anyway. } { + General clean-up. } { + Fixed bug in RenameNode method. It updated the relative PIDL, but } { not the fully qualifed PIDL. Thanks to Frank Kroeger } { (FKroeger@tkc.de) for finding and fixing this. } { + Shared and link overlay images now work for the tree and list. HUGE } { thanks go to Peter Ruskin (PeterRuskin@classic.msn.com) and Lars } { Randers (lranders@post2.tele.dk) who both for provided this code. } { + Added ShowFolders property to TSystemListView. } { + Files weren't shown in folders that had no subfolders (ShowFiles } { property set to TRUE). } { + Fixed problem that would cause bizarre access violations if you did } { anything that caused the window to be recreated. For example, if } { you changed the BorderStyle property, you would get this because } { the window handle has to be destroyed and recreated. This caused } { all the pointers to get freed, but then the pointer values were } { restored when the window was recreated -- data is gone, just a load } { of invalid pointers. } { + Images were screwed up in Control Panel and Printers folder. } { + Overriden Expand method never called inherited. Broke OnExpanded } { event. } { } {------------------------------------------------------------------------------} { } { ITEMS NEXT ON THE TO-DO LIST: } { } { * During node expansion, sometimes the treeview is blank. This is } { apparently a new "feature" of of the Delphi 3 VCL. Have to find a way } { to stop it. } { * Double click in TSystemListView does not automatically updated the } { SystemTreeView with the new location. } { * A few more bugs still in my email. } { * CMF_CANRENAME flag for context menu. Can't just add it as it will cause } { OLE errors, i.e. system won't handle it. I need to determine if I can } { use "magic numbers" to interpret rename and other unhandled commands to } { to catch them and process them myself. } { * Extension filtering for list view and tree view when showing files. } { * Use GetTickCount to decide if I should show the hourglass or not in } { EnumerateFolders methods. } { * Change GetItemAttrs so it uses a nice Delphi set instead of API values. } { * Modification of the system context menu instead of just replacing it. } { * Do away with ShowFolders/Files and ShowHiddenFolders/Files properties } { and replace with a set that includes all the stanadard stuff: hidden, } { system, read-only, etc. } { } {------------------------------------------------------------------------------

Classes

TFolderItemData - A TFolderItem instance is stored in each node's Data property.
TSystemComboBox -
TSystemListView -
TSystemTreeView -

Functions

Register -

Types

TColumnType
TRootFolder
TSCBType
TSLVAddListItem
TSLVCreateColumns

Constants

MEMLEAK_STR

Variables


Functions


procedure Register;


Types


TColumnType = (ctFileSystem, ctMachine, ctControlPanel, ctPrinters, ctDUNet,
     ctNetwork, ctUser, ctUnknown);

TRootFolder = (rfDesktop, rfRecycleBin, rfControlPanel, rfDesktopDir,
     rfDrives, rfFavoriteURLs, rfFonts, rfNetHood, rfNetHoodDir, rfDocumentDir,
     rfPrinters, rfPrograms, rfRecentDir, rfSendTo, rfStartMenu, rfStartup,
     rfTemplates, rfFileSystem, rfCustom);
If you change the order of these, you have to change the order of the FOLDERID constants below in GetFolderID function.
TSCBType = (scbSystem, scbDrives);

TSLVAddListItem = procedure(Sender: TObject; AItem: TListItem) of object
this stuff can't be saved with the form. It has to be read every time for obvious reasons.
TSLVCreateColumns = TNotifyEvent

Constants

MEMLEAK_STR = 'Memory Leak Detected. Not all folder data was freed.'


Variables