views:

129

answers:

2

There is a nice feature in the "Open Project..." dialog (see image below) in the NetBeans IDE which uses a custom icon for the folder symbols depending on the project type in that folder.

For example, if a folder contains a pom.xml file, the Maven project symbol appears.

Maybe there also an extension point in the Windows standard dialog or a shell view control which can be used to override the default folder icon.

All solutions I know so far need a system-wide change, but is there also a solution which works without system modifications and only for the current application?

alt text

Update: which VCL component would you suggest as a starting point for a custom dialog, could I use TShellTreeView or TcxShellTreeView?

+1  A: 

The IShellIconOverlay and the IShellIconOverlayIdentifier Interfaces are used to build overlay icons shell extensions , theses extensions are system wide not per application, in delphi both interfaces exists in the ShlObj unit.

check this link for an example

UPDATE

i think wich the Netbeans IDE dialog posted in your question, draw their own dialog box using custom icons and standard controls. you can gain the same effect building your own dialog box using the standard vcl controls.

RRUZ
The example says I must register the icon overlay handler in the registry - so it looks like overlays can not be defined only for the current application with these interfaces?
mjustin
@mjustin, theses interfaces are used in an Windows shell extension, so they are system wide.
RRUZ
+2  A: 

Descending from 'TCustomTreeView', TShellTreeView has support for images out of the box. An ImageList can be assigned to its Images property, and in its OnGetImageIndex event the index of the image in the list for the corresponding node can be supplied.

procedure TForm1.ShellTreeView1GetImageIndex(Sender: TObject; Node: TTreeNode);
begin
  if TShellFolder(Node.Data).DisplayName = 'RAD Studio' then
    Node.ImageIndex := 2;
end;


A downside of that is, all nodes will have to use the images in the imagelist, that is there won't be images from the system image list. The below example demonstrates how system images can be retrieved for nodes which won't be customized. It uses a custom image for 'RAD Studio' folder in the personal folder and uses system images for all other nodes. ImageList1 holds our custom images, ImageList2 is the one that is assigned to the 'Images' property of the 'ShellTreeView'.

type
  TForm1 = class(TForm)
    [...]
  private
    FShellImageList: THandle;
    [...]

uses
  shellapi, shellctrls, commctrl;

procedure TForm1.FormCreate(Sender: TObject);
var
  FileInfo: TSHFileInfo;
  ImageWidth, ImageHeight: Integer;
begin
  ShellTreeView1.Root := 'rfPersonal';

  FShellImageList := SHGetFileInfo('C:\', 0, FileInfo, SizeOf(FileInfo),
      SHGFI_SYSICONINDEX or SHGFI_SMALLICON);     //'//(pop SO formatting)
  ImageList_GetIconSize(FShellImageList, ImageWidth, ImageHeight);
  ImageList2.Width := ImageWidth;
  ImageList2.Height := ImageHeight;

  // Arbitrary count hopefully sufficient enough to be able to hold
  // system images. Note that this is a proof of concept, not to be
  // intended to be a working design.
  ImageList_SetImageCount(ImageList2.Handle, 255);

  // Make sure the width/height of ImageList1 is the same.
  // Set its size, populate, stretchdraw do whatever necessary..
end;

function GetShellImage(PIDL: PItemIDList; Open: Boolean): Integer;
var
  FileInfo: TSHFileInfo;
  Flags: Integer;
begin
  Flags := SHGFI_PIDL or SHGFI_SYSICONINDEX or SHGFI_SMALLICON;
  if Open then Flags := Flags or SHGFI_OPENICON;
  SHGetFileInfo(PChar(PIDL), 0, FileInfo, SizeOf(FileInfo), Flags);
  Result := FileInfo.iIcon;
end;

procedure TForm1.ShellTreeView1GetImageIndex(Sender: TObject; Node: TTreeNode);
var
  ImageIndex, SelectedIndex: Integer;
  Icon: TIcon;
begin
  if TShellFolder(Node.Data).DisplayName = 'RAD Studio' then begin
    Icon := TIcon.Create;
    try
      ImageList1.GetIcon(0, Icon);
      ImageIndex := ImageList_AddIcon(ImageList2.Handle, Icon.Handle);

      ImageList1.GetIcon(1, Icon);
      SelectedIndex := ImageList_AddIcon(ImageList2.Handle, Icon.Handle);
    finally
      Icon.Free;
    end;
  end else begin
    ImageIndex := GetShellImage(TShellFolder(Node.Data).AbsoluteID, False);
    SelectedIndex := GetShellImage(TShellFolder(Node.Data).AbsoluteID, True);

    ImageList_ReplaceIcon(ImageList2.Handle, ImageIndex,
        ImageList_GetIcon(FShellImageList, ImageIndex, 0));
    ImageList_ReplaceIcon(ImageList2.Handle, SelectedIndex,
        ImageList_GetIcon(FShellImageList, SelectedIndex, 0));
  end;
  Node.ImageIndex := ImageIndex;
  Node.SelectedIndex := SelectedIndex;
end;

As commented in the code, this should not be taken for a working design; Instead of an imagelist having lots of unused images, some kind of a lookup that matches 'image index' and 'system image list index' could be employed.

Sertac Akyuz