{----------------------------------------------------------------------------- The contents of this file are subject to the Mozilla Public License Version 1.1 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.mozilla.org/MPL/MPL-1.1.html Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either expressed or implied. See the License for the specific language governing rights and limitations under the License. The Original Code is: JvBrowseFolder.PAS, released on 2001-02-28. The Initial Developer of the Original Code is Sébastien Buysse [sbuysse att buypin dott com] Portions created by Sébastien Buysse are Copyright (C) 2001 Sébastien Buysse. All Rights Reserved. Contributor(s): Michael Beck [mbeck att bigfoot dott com] Roman Kovbasiouk [roko att users dott sourceforge dott net] Remko Bonte [remkobonte att myrealbox dott com] You may retrieve the latest version of this file at the Project JEDI's JVCL home page, located at http://jvcl.sourceforge.net Known Issues: -----------------------------------------------------------------------------} // $Id: JvBrowseFolder.pas 10612 2006-05-19 19:04:09Z jfudickar $ unit JvBrowseFolder; {$I jvcl.inc} {$I windowsonly.inc} interface {$IFDEF BCB6} // BCB6 needs the shtypes.h file to be included {$HPPEMIT '#include '} {$ENDIF BCB6} {$IFDEF BCB5} // BCB5 doesn't have the shtypes.h file, so we have to cope with it (*$HPPEMIT 'namespace shlobj_h'*) (*$HPPEMIT '{'*) (*$HPPEMIT '#include '*) (*$HPPEMIT '}'*) (*$HPPEMIT 'using namespace shlobj_h;'*) (*$HPPEMIT '#define _ITEMIDLIST shlobj_h::_ITEMIDLIST'*) {$ENDIF BCB5} uses {$IFDEF UNITVERSIONING} JclUnitVersioning, {$ENDIF UNITVERSIONING} {$IFDEF CLR} System.Text, System.Runtime.InteropServices, System.Security, {$ENDIF CLR} {$IFDEF VisualCLX} Qt, QWindows, {$ENDIF VisualCLX} Windows, Messages, ShlObj, Classes, JvBaseDlg; {$IFDEF CLR} type IUnknown = IInterface; {$ENDIF CLR} const { Interfaces from ShObjIdl.h } IID_IFolderFilterSite: TGUID = '{C0A651F5-B48B-11d2-B5ED-006097C686F6}'; SID_IFolderFilterSite = '{C0A651F5-B48B-11d2-B5ED-006097C686F6}'; type IFolderFilterSite = interface(IUnknown) [SID_IFolderFilterSite] function SetFilter(punk: IUnknown): HResult; stdcall; end; const IID_IFolderFilter: TGUID = '{9CC22886-DC8E-11d2-B1D0-00C04F8EEB3E}'; SID_IFolderFilter = '{9CC22886-DC8E-11d2-B1D0-00C04F8EEB3E}'; type IFolderFilter = interface(IUnknown) [SID_IFolderFilter] function ShouldShow(psf: IShellFolder; pidlFolder, pidlItem: PItemIDList): HResult; stdcall; function GetEnumFlags(psf: IShellFolder; pidlFolder: PItemIDList; const phWnd: THandle; var pgrfFlags: DWORD): HResult; stdcall; end; type { (rb) Stupid name, feel free to change :) } TJvBrowsableObjectClass = ( ocFolders, //SHCONTF_FOLDERS, ocNonFolders, //SHCONTF_NONFOLDERS, ocIncludeHidden, //SHCONTF_INCLUDEHIDDEN, ocInitOnFirstNext, //SHCONTF_INIT_ON_FIRST_NEXT, ocNetPrinterSrch, //SHCONTF_NETPRINTERSRCH, ocSharable, //SHCONTF_SHAREABLE, ocStorage //SHCONTF_STORAGE ); TJvBrowsableObjectClasses = set of TJvBrowsableObjectClass; TJvBrowseAcceptChange = procedure(Sender: TObject; const NewFolder: string; var Accept: Boolean) of object; TJvShouldShowEvent = procedure(Sender: TObject; const Item: string; var DoShow: Boolean) of object; TJvGetEnumFlagsEvent = procedure(Sender: TObject; const AFolder: string; var Flags: TJvBrowsableObjectClasses) of object; TJvDirChange = procedure(Sender: TObject; const Directory: string) of object; TJvValidateFailedEvent = procedure(Sender: TObject; const AEditText: string; var CanCloseDialog: Boolean) of object; TFromDirectory = ( fdNoSpecialFolder, { 0 } fdRootFolder, { 0 } fdRecycleBin, { CSIDL_BITBUCKET } fdControlPanel, { CSIDL_CONTROLS } fdDesktop, { CSIDL_DESKTOP } fdDesktopDirectory, { CSIDL_DESKTOPDIRECTORY } fdMyComputer, { CSIDL_DRIVES } fdFonts, { CSIDL_FONTS } fdNetHood, { CSIDL_NETHOOD } fdNetwork, { CSIDL_NETWORK } fdPersonal, { CSIDL_PERSONAL } fdPrinters, { CSIDL_PRINTERS } fdPrograms, { CSIDL_PROGRAMS } fdRecent, { CSIDL_RECENT } fdSendTo, { CSIDL_SENDTO } fdStartMenu, { CSIDL_STARTMENU } fdStartup, { CSIDL_STARTUP } fdTemplates, { CSIDL_TEMPLATES } fdStartUpNonLocalized, { CSIDL_ALTSTARTUP } fdCommonStartUpNonLocalized, { CSIDL_COMMON_ALTSTARTUP } fdCommonDocuments, { CSIDL_COMMON_DOCUMENTS } fdCommonFavorites, { CSIDL_COMMON_FAVORITES } fdCommonPrograms, { CSIDL_COMMON_PROGRAMS } fdCommonStartUp, { CSIDL_COMMON_STARTUP } fdCommonTemplates, { CSIDL_COMMON_TEMPLATES } fdCookies, { CSIDL_COOKIES } fdFavorites, { CSIDL_FAVORITES } fdHistory, { CSIDL_HISTORY } fdInternet, { CSIDL_INTERNET } fdMyMusic, { CSIDL_MYMUSIC } fdPrinthood, { CSIDL_PRINTHOOD } fdConnections, { CSIDL_CONNECTIONS } { Version 4.71 } fdAppData, { CSIDL_APPDATA } { Version 4.72 } fdInternetCache, { CSIDL_INTERNET_CACHE } { Version 5.00 } fdAdminTools, { CSIDL_ADMINTOOLS } fdCommonAdminTools, { CSIDL_COMMON_ADMINTOOLS } fdCommonAppData, { CSIDL_COMMON_APPDATA } fdLocalAppData, { CSIDL_LOCAL_APPDATA } fdMyPictures, { CSIDL_MYPICTURES } fdProfile, { CSIDL_PROFILE } fdProgramFiles, { CSIDL_PROGRAM_FILES } fdProgramFilesCommon, { CSIDL_PROGRAM_FILES_COMMON } fdSystem, { CSIDL_SYSTEM } fdWindows, { CSIDL_WINDOWS } { Version 6.00 } fdCDBurnArea, { CSIDL_CDBURN_AREA } fdCommonMusic, { CSIDL_COMMON_MUSIC } fdCommonPictures, { CSIDL_COMMON_PICTURES } fdCommonVideo, { CSIDL_COMMON_VIDEO } fdMyDocuments, { CSIDL_MYDOCUMENTS } fdMyVideo, { CSIDL_MYVIDEO } fdProfiles, { CSIDL_PROFILES } { Unknown version } fdResources, { CSIDL_RESOURCES } fdResourcesLocalized, fdCommonOEMLinks, { CSIDL_COMMON_OEM_LINKS } fdComputersNearMe { CSIDL_COMPUTERSNEARME } ); TJvFolderPos = (fpDefault, fpScreenCenter, fpFormCenter, fpTopLeft, fpTopRight, fpBottomLeft, fpBottomRight); TOptionsDirectory = (odBrowseForComputer, odOnlyDirectory, odOnlyPrinters, odNoBelowDomain, odSystemAncestorsOnly, odFileSystemDirectoryOnly, odStatusAvailable, odIncludeFiles, odIncludeUrls, odEditBox, odNewDialogStyle, odShareable, odUsageHint, odNoNewButtonFolder, odValidate); // (p3) shouldn't TOptionsDir be changed to T(Jv)OptionsDirectories? TOptionsDir = set of TOptionsDirectory; {$IFDEF COMPILER6_UP} TJvBrowseForFolderDialog = class(TJvCommonDialogF, IFolderFilter) {$ELSE} TJvBrowseForFolderDialog = class(TJvCommonDialogF, IFolderFilter, IUnknown) {$ENDIF COMPILER6_UP} private { Handle to the owner form of the dialog, used if Position = fpFormCenter } FOwnerWindow: THandle; { Handle to the MS "Browse for folder" dialog } FDialogWindow: THandle; FHelpContext: THelpContext; FTitle: string; FOptions: TOptionsDir; FUsedOptions: TOptionsDir; FDisplayName: string; FRootDirectory: TFromDirectory; FRootDirectoryPath: string; FDirectory: string; FPosition: TJvFolderPos; FPidl: PItemIDList; FStatusText: string; FHelpButtonHandle: THandle; FHelpButtonHeightDelta: Integer; FOnInit: TNotifyEvent; FOnChange: TJvDirChange; FOnAcceptChange: TJvBrowseAcceptChange; FOnShouldShow: TJvShouldShowEvent; FOnGetEnumFlags: TJvGetEnumFlagsEvent; FOnValidateFailed: TJvValidateFailedEvent; { For hooking the control } FDefWndProc: {$IFDEF CLR}TFNBFFCallBack{$ELSE}Pointer{$ENDIF}; FObjectInstance: {$IFDEF CLR}TFNWndProc{$ELSE}Pointer{$ENDIF}; FPositionSet: Boolean; // (p3) updates the status text. NOTE: doesn't work if odNewDialogStyle is true (MS limitation)!!! procedure UpdateStatusText(AText: string); procedure WMShowWindow(var Msg: TMessage); message WM_SHOWWINDOW; procedure WMSize(var Msg: TWMSize); message WM_SIZE; function GetRootDirectoryPath: string; function IsRootDirectoryPathStored: Boolean; procedure SetRootDirectory(const Value: TFromDirectory); procedure SetRootDirectoryPath(const Value: string); procedure SetOptions(const Value: TOptionsDir); protected { Messages from the browser } procedure DoInitialized; procedure DoIUnknown(const Unknown: IUnknown); procedure DoSelChanged(IDList: PItemIDList); function DoValidateFailed(AEditText: string): Integer; function DoValidateFailedW(AEditText: WideString): Integer; function DoShouldShow(const AItem: string): Boolean; function DoGetEnumFlags(const AFolder: string; var Flags: TJvBrowsableObjectClasses): Boolean; function GetOwnerWindow: THandle; procedure MainWndProc(var Msg: TMessage); procedure HookDialog; { IFolderFilter } function ShouldShow(psf: IShellFolder; pidlFolder, pidlItem: PItemIDList): HResult; stdcall; function GetEnumFlags(psf: IShellFolder; pidlFolder: PItemIDList; const phWnd: THandle; var pgrfFlags: DWORD): HResult; stdcall; public constructor Create(AOwner: TComponent); override; destructor Destroy; override; procedure DefaultHandler(var Msg); override; { Messages to the browser } procedure SetSelection(const APath: string); overload; procedure SetSelection(IDList: PItemIDList); overload; procedure SetStatusText(const AText: string); procedure SetStatusTextW(const AText: WideString); procedure SetOKEnabled(const Value: Boolean); procedure SetOKText(const AText: string); procedure SetOKTextW(const AText: WideString); procedure SetExpanded(const APath: string); overload; procedure SetExpandedW(const APath: WideString); procedure SetExpanded(IDList: PItemIDList); overload; property Pidl: PItemIDList read FPidl; property Handle: THandle read FDialogWindow; function Execute: Boolean; override; published property Directory: string read FDirectory write FDirectory; property DisplayName: string read FDisplayName write FDisplayName stored False; property HelpContext: THelpContext read FHelpContext write FHelpContext default 0; property Options: TOptionsDir read FOptions write SetOptions default [odStatusAvailable, odNewDialogStyle]; property Position: TJvFolderPos read FPosition write FPosition default fpScreenCenter; property RootDirectory: TFromDirectory read FRootDirectory write SetRootDirectory default fdNoSpecialFolder; property RootDirectoryPath: string read GetRootDirectoryPath write SetRootDirectoryPath stored IsRootDirectoryPathStored; property Title: string read FTitle write FTitle; property StatusText: string read FStatusText write FStatusText; property OnAcceptChange: TJvBrowseAcceptChange read FOnAcceptChange write FOnAcceptChange; property OnChange: TJvDirChange read FOnChange write FOnChange; property OnGetEnumFlags: TJvGetEnumFlagsEvent read FOnGetEnumFlags write FOnGetEnumFlags; property OnInitialized: TNotifyEvent read FOnInit write FOnInit; property OnShouldShow: TJvShouldShowEvent read FOnShouldShow write FOnShouldShow; property OnValidateFailed: TJvValidateFailedEvent read FOnValidateFailed write FOnValidateFailed; end; function BrowseForFolder(const ATitle: string; AllowCreate: Boolean; var ADirectory: string; AHelpContext: THelpContext = 0): Boolean; function BrowseForComputer(const ATitle: string; AllowCreate: Boolean; var ADirectory: string; AHelpContext: THelpContext = 0): Boolean; // (p3) moved from JvFileUtil, deprecated removed function BrowseDirectory(var AFolderName: string; const DlgText: string; AHelpContext: THelpContext): Boolean; // (p3) moved from JvFileUtil, deprecated removed function BrowseComputer(var AComputerName: string; const DlgText: string; AHelpContext: THelpContext): Boolean; {$IFDEF UNITVERSIONING} const UnitVersioning: TUnitVersionInfo = ( RCSfile: '$URL: https://jvcl.svn.sourceforge.net/svnroot/jvcl/tags/JVCL3_32/run/JvBrowseFolder.pas $'; Revision: '$Revision: 10612 $'; Date: '$Date: 2006-05-19 21:04:09 +0200 (ven., 19 mai 2006) $'; LogPath: 'JVCL\run' ); {$ENDIF UNITVERSIONING} implementation uses SysUtils, ActiveX, Controls, Forms, Consts, Graphics, {$IFNDEF CLR} JclShell, {$ENDIF ~CLR} JvJCLUtils, JvJVCLUtils, JvConsts, JvResources, JvTypes; {$IFDEF VisualCLX} type TMessage = Messages.TMessage; {$ENDIF VisualCLX} {$IFDEF CLR} // .NET loads the library on first access [SuppressUnmanagedCodeSecurity, DllImport('SHFolder.dll', CharSet = CharSet.Ansi, SetLastError = True, EntryPoint = 'SHGetFolderPathA')] function SHGetFolderPathProc(hWnd: HWND; CSIDL: Integer; hToken: THandle; dwFlags: DWORD; pszPath: StringBuilder): HResult; external; {$ELSE} type TSHGetFolderPathProc = function(hWnd: HWND; CSIDL: Integer; hToken: THandle; dwFlags: DWORD; pszPath: PAnsiChar): HResult; stdcall; var SHGetFolderPathProc: TSHGetFolderPathProc = nil; {$ENDIF CLR} const { Taken from ShlObj.h & ShObjIdl.h } BIF_RETURNFSANCESTORS = $0008; BIF_EDITBOX = $0010; // Add an editbox to the dialog BIF_VALIDATE = $0020; // insist on valid result (or CANCEL) BIF_NEWDIALOGSTYLE = $0040; // Use the new dialog layout with the ability to resize // Caller needs to call OleInitialize() before using this API BIF_BROWSEINCLUDEURLS = $0080; // Allow URLs to be displayed or entered. (Requires BIF_USENEWUI) BIF_UAHINT = $0100; // Add a UA hint to the dialog, in place of the edit box. // May not be combined with BIF_EDITBOX BIF_NONEWFOLDERBUTTON = $0200; // Do not add the "New Folder" button to the dialog. // Only applicable with BIF_NEWDIALOGSTYLE. BIF_BROWSEINCLUDEFILES = $4000; // Browsing for Everything BIF_SHAREABLE = $8000; // sharable resources displayed (remote shares, requires BIF_USENEWUI) SHCONTF_INIT_ON_FIRST_NEXT = $0100; // allow EnumObject() to return before validating enum SHCONTF_NETPRINTERSRCH = $0200; // hint that client is looking for printers SHCONTF_SHAREABLE = $0400; // hint that client is looking sharable resources (remote shares) SHCONTF_STORAGE = $0800; // include all items with accessible storage and their ancestors CSIDL_MYDOCUMENTS = $000C; // logical "My Documents" desktop icon CSIDL_MYMUSIC = $000D; // "My Music" folder CSIDL_MYVIDEO = $000E; // "My Videos" folder CSIDL_LOCAL_APPDATA = $001C; // \Local Settings\Applicaiton Data (non roaming) CSIDL_COMMON_APPDATA = $0023; // All Users\Application Data CSIDL_WINDOWS = $0024; // GetWindowsDirectory() CSIDL_SYSTEM = $0025; // GetSystemDirectory() CSIDL_PROGRAM_FILES = $0026; // C:\Program Files CSIDL_MYPICTURES = $0027; // C:\Program Files\My Pictures CSIDL_PROFILE = $0028; // USERPROFILE CSIDL_PROGRAM_FILES_COMMON = $002B; // C:\Program Files\Common CSIDL_COMMON_TEMPLATES = $002D; // All Users\Templates CSIDL_COMMON_DOCUMENTS = $002E; // All Users\Documents CSIDL_COMMON_ADMINTOOLS = $002F; // All Users\Start Menu\Programs\Administrative Tools CSIDL_ADMINTOOLS = $0030; // \Start Menu\Programs\Administrative Tools CSIDL_CONNECTIONS = $0031; // Network and Dial-up Connections CSIDL_COMMON_MUSIC = $0035; // All Users\My Music CSIDL_COMMON_PICTURES = $0036; // All Users\My Pictures CSIDL_COMMON_VIDEO = $0037; // All Users\My Video CSIDL_RESOURCES = $0038; // Resource Direcotry CSIDL_RESOURCES_LOCALIZED = $0039; // Localized Resource Direcotry CSIDL_COMMON_OEM_LINKS = $003A; // Links to All Users OEM specific apps CSIDL_CDBURN_AREA = $003B; // USERPROFILE\Local Settings\Application Data\Microsoft\CD Burning CSIDL_COMPUTERSNEARME = $003D; // Computers Near Me (computered from Workgroup membership) CSIDL_PROFILES = $003E; // ?? BFFM_SETOKTEXT = WM_USER + 105; // Unicode only BFFM_SETEXPANDED = WM_USER + 106; // Unicode only BFFM_IUNKNOWN = 5; // provides IUnknown to client. lParam: IUnknown* { TOptionsDirectory = (odBrowseForComputer, odOnlyDirectory, odOnlyPrinters, odNoBelowDomain, odSystemAncestorsOnly, odFileSystemDirectoryOnly, odStatusAvailable, odIncludeFiles, odIncludeUrls, odEditBox, odNewDialogStyle, odShareable, odUsageHint, odNoNewButtonFolder, odValidate); } { (rb) No idea why odOnlyDirectory is used? } COptionsDirectory: array [TOptionsDirectory] of Cardinal = ( BIF_BROWSEFORCOMPUTER, 0, BIF_BROWSEFORPRINTER, BIF_DONTGOBELOWDOMAIN, BIF_RETURNFSANCESTORS, BIF_RETURNONLYFSDIRS, BIF_STATUSTEXT, BIF_BROWSEINCLUDEFILES, BIF_BROWSEINCLUDEURLS, BIF_EDITBOX, BIF_NEWDIALOGSTYLE, BIF_SHAREABLE, BIF_UAHINT, BIF_NONEWFOLDERBUTTON, BIF_VALIDATE); { TJvBrowseObjectClass = (ocFolders, ocNonFolders, ocIncludeHidden, ocInitOnFirstNext, ocNetPrinterSrch, ocSharable, ocStorage) } CBrowseObjectClasses: array [TJvBrowsableObjectClass] of Cardinal = ( SHCONTF_FOLDERS, SHCONTF_NONFOLDERS, SHCONTF_INCLUDEHIDDEN, SHCONTF_INIT_ON_FIRST_NEXT, SHCONTF_NETPRINTERSRCH, SHCONTF_SHAREABLE, SHCONTF_STORAGE); function BrowseForFolder(const ATitle: string; AllowCreate: Boolean; var ADirectory: string; AHelpContext: THelpContext): Boolean; begin with TJvBrowseForFolderDialog.Create(nil) do try Position := fpScreenCenter; Directory := ADirectory; Title := ATitle; HelpContext := AHelpContext; if AllowCreate then Options := Options + [odNewDialogStyle] else Options := Options - [odNewDialogStyle]; Result := Execute; if Result then ADirectory := Directory; finally Free; end; end; function BrowseForComputer(const ATitle: string; AllowCreate: Boolean; var ADirectory: string; AHelpContext: THelpContext): Boolean; begin with TJvBrowseForFolderDialog.Create(nil) do try Position := fpScreenCenter; Directory := ADirectory; Title := ATitle; HelpContext := AHelpContext; if AllowCreate then Options := Options + [odNewDialogStyle] else Options := Options - [odNewDialogStyle]; Options := Options + [odBrowseForComputer]; RootDirectory := fdNetwork; Result := Execute; if Result then ADirectory := Directory; finally Free; end; end; function BrowseDirectory(var AFolderName: string; const DlgText: string; AHelpContext: THelpContext): Boolean; begin Result := BrowseForFolder(DlgText, True, AFolderName, AHelpContext); end; function BrowseComputer(var AComputerName: string; const DlgText: string; AHelpContext: THelpContext): Boolean; begin Result := BrowseForComputer(DlgText, True, AComputerName, AHelpContext); end; { From QDialogs.pas } function StrRetToString(PIDL: PItemIDList; StrRet: TStrRet): string; {$IFDEF CLR} begin case StrRet.uType of STRRET_CSTR: Result := Marshal.PtrToStringAnsi(StrRet.cStr); STRRET_OFFSET: Result := Marshal.PtrToStringAnsi(Marshal.ReadIntPtr(PIDL.mkid.abID, StrRet.uOffset - SizeOf(PIDL.mkid.cb)), PIDL.mkid.cb - StrRet.uOffset); STRRET_WSTR: Result := Marshal.PtrToStringBSTR(StrRet.pOleStr); end; end; {$ELSE} var P: PChar; begin case StrRet.uType of STRRET_CSTR: SetString(Result, StrRet.cStr, lStrLen(StrRet.cStr)); STRRET_OFFSET: begin P := @PIDL.mkid.abID[StrRet.uOffset - SizeOf(PIDL.mkid.cb)]; SetString(Result, P, PIDL.mkid.cb - StrRet.uOffset); end; STRRET_WSTR: Result := StrRet.pOleStr; end; end; {$ENDIF CLR} type TFromDirectoryData = record CSIDL: Cardinal; MinVersion: Cardinal; OnlyNT: Boolean; CanSimulate: Boolean; Alternative: TFromDirectory; end; const CSIDLLocations: array [TFromDirectory] of TFromDirectoryData = ( { fdNoSpecialFolder } (CSIDL: 0; MinVersion: 0; OnlyNT: False; CanSimulate: False; Alternative: fdNoSpecialFolder), { fdRootFolder } (CSIDL: 0; MinVersion: 0; OnlyNT: False; CanSimulate: False; Alternative: fdNoSpecialFolder), { fdRecycleBin } (CSIDL: CSIDL_BITBUCKET; MinVersion: 0; OnlyNT: False; CanSimulate: False; Alternative: fdNoSpecialFolder), { fdControlPanel } (CSIDL: CSIDL_CONTROLS; MinVersion: 0; OnlyNT: False; CanSimulate: False; Alternative: fdNoSpecialFolder), { fdDesktop } (CSIDL: CSIDL_DESKTOP; MinVersion: 0; OnlyNT: False; CanSimulate: False; Alternative: fdNoSpecialFolder), { fdDesktopDirectory } (CSIDL: CSIDL_DESKTOPDIRECTORY; MinVersion: 0; OnlyNT: False; CanSimulate: False; Alternative: fdNoSpecialFolder), { fdMyComputer } (CSIDL: CSIDL_DRIVES; MinVersion: 0; OnlyNT: False; CanSimulate: False; Alternative: fdNoSpecialFolder), { fdFonts } (CSIDL: CSIDL_FONTS; MinVersion: 0; OnlyNT: False; CanSimulate: False; Alternative: fdNoSpecialFolder), { fdNetHood } (CSIDL: CSIDL_NETHOOD; MinVersion: 0; OnlyNT: False; CanSimulate: False; Alternative: fdNoSpecialFolder), { fdNetwork } (CSIDL: CSIDL_NETWORK; MinVersion: 0; OnlyNT: False; CanSimulate: False; Alternative: fdNoSpecialFolder), { fdPersonal } (CSIDL: CSIDL_PERSONAL; MinVersion: 0; OnlyNT: False; CanSimulate: True; Alternative: fdNoSpecialFolder), { fdPrinters } (CSIDL: CSIDL_PRINTERS; MinVersion: 0; OnlyNT: False; CanSimulate: False; Alternative: fdNoSpecialFolder), { fdPrograms } (CSIDL: CSIDL_PROGRAMS; MinVersion: 0; OnlyNT: False; CanSimulate: False; Alternative: fdNoSpecialFolder), { fdRecent } (CSIDL: CSIDL_RECENT; MinVersion: 0; OnlyNT: False; CanSimulate: False; Alternative: fdNoSpecialFolder), { fdSendTo } (CSIDL: CSIDL_SENDTO; MinVersion: 0; OnlyNT: False; CanSimulate: False; Alternative: fdNoSpecialFolder), { fdStartMenu } (CSIDL: CSIDL_STARTMENU; MinVersion: 0; OnlyNT: False; CanSimulate: False; Alternative: fdNoSpecialFolder), { fdStartup } (CSIDL: CSIDL_STARTUP; MinVersion: 0; OnlyNT: False; CanSimulate: False; Alternative: fdNoSpecialFolder), { fdTemplates } (CSIDL: CSIDL_TEMPLATES; MinVersion: 0; OnlyNT: False; CanSimulate: False; Alternative: fdNoSpecialFolder), { fdStartUpNonLocalized } (CSIDL: CSIDL_ALTSTARTUP; MinVersion: 0; OnlyNT: False; CanSimulate: False; Alternative: fdNoSpecialFolder), { fdCommonStartUpNonLocalized } (CSIDL: CSIDL_COMMON_ALTSTARTUP; MinVersion: 0; OnlyNT: True; CanSimulate: False; Alternative: fdNoSpecialFolder), { fdCommonDocuments } (CSIDL: CSIDL_COMMON_DOCUMENTS; MinVersion: 0; OnlyNT: False; CanSimulate: True; Alternative: fdNoSpecialFolder), { fdCommonFavorites } (CSIDL: CSIDL_COMMON_FAVORITES; MinVersion: 0; OnlyNT: True; CanSimulate: False; Alternative: fdNoSpecialFolder), { fdCommonPrograms } (CSIDL: CSIDL_COMMON_PROGRAMS; MinVersion: 0; OnlyNT: True; CanSimulate: False; Alternative: fdNoSpecialFolder), { fdCommonStartUp } (CSIDL: CSIDL_COMMON_STARTUP; MinVersion: 0; OnlyNT: True; CanSimulate: False; Alternative: fdNoSpecialFolder), { fdCommonTemplates } (CSIDL: CSIDL_COMMON_TEMPLATES; MinVersion: 0; OnlyNT: True; CanSimulate: False; Alternative: fdNoSpecialFolder), { fdCookies } (CSIDL: CSIDL_COOKIES; MinVersion: 0; OnlyNT: False; CanSimulate: True; Alternative: fdNoSpecialFolder), { fdFavorites } (CSIDL: CSIDL_FAVORITES; MinVersion: 0; OnlyNT: False; CanSimulate: False; Alternative: fdNoSpecialFolder), { fdHistory } (CSIDL: CSIDL_HISTORY; MinVersion: 0; OnlyNT: False; CanSimulate: True; Alternative: fdNoSpecialFolder), { fdInternet } (CSIDL: CSIDL_INTERNET; MinVersion: 0; OnlyNT: False; CanSimulate: False; Alternative: fdNoSpecialFolder), { fdMyMusic } (CSIDL: CSIDL_MYMUSIC; MinVersion: 0; OnlyNT: False; CanSimulate: False; Alternative: fdNoSpecialFolder), { fdPrinthood } (CSIDL: CSIDL_PRINTHOOD; MinVersion: 0; OnlyNT: False; CanSimulate: False; Alternative: fdNoSpecialFolder), { fdConnections } (CSIDL: CSIDL_CONNECTIONS; MinVersion: 0; OnlyNT: False; CanSimulate: False; Alternative: fdNoSpecialFolder), { fdAppData } (CSIDL: CSIDL_APPDATA; MinVersion: $00040071; OnlyNT: False; CanSimulate: True; Alternative: fdNoSpecialFolder), { fdInternetCache } (CSIDL: CSIDL_INTERNET_CACHE; MinVersion: $00040072; OnlyNT: False; CanSimulate: True; Alternative: fdNoSpecialFolder), { fdAdminTools } (CSIDL: CSIDL_ADMINTOOLS; MinVersion: $00050000; OnlyNT: False; CanSimulate: True; Alternative: fdNoSpecialFolder), { fdCommonAdminTools } (CSIDL: CSIDL_COMMON_ADMINTOOLS; MinVersion: $00050000; OnlyNT: False; CanSimulate: True; Alternative: fdNoSpecialFolder), { fdCommonAppData } (CSIDL: CSIDL_COMMON_APPDATA; MinVersion: $00050000; OnlyNT: False; CanSimulate: True; Alternative: fdNoSpecialFolder), { fdLocalAppData } (CSIDL: CSIDL_LOCAL_APPDATA; MinVersion: $00050000; OnlyNT: False; CanSimulate: True; Alternative: fdNoSpecialFolder), { fdMyPictures } (CSIDL: CSIDL_MYPICTURES; MinVersion: $00050000; OnlyNT: False; CanSimulate: True; Alternative: fdPersonal), { fdProfile } (CSIDL: CSIDL_PROFILE; MinVersion: $00050000; OnlyNT: False; CanSimulate: False; Alternative: fdNoSpecialFolder), { fdProgramFiles } (CSIDL: CSIDL_PROGRAM_FILES; MinVersion: $00050000; OnlyNT: False; CanSimulate: True; Alternative: fdNoSpecialFolder), { fdProgramFilesCommon } (CSIDL: CSIDL_PROGRAM_FILES_COMMON; MinVersion: $00050000; OnlyNT: True; CanSimulate: True; Alternative: fdNoSpecialFolder), { fdSystem } (CSIDL: CSIDL_SYSTEM; MinVersion: $00050000; OnlyNT: False; CanSimulate: True; Alternative: fdNoSpecialFolder), { fdWindows } (CSIDL: CSIDL_WINDOWS; MinVersion: $00050000; OnlyNT: False; CanSimulate: True; Alternative: fdNoSpecialFolder), { fdCDBurnArea } (CSIDL: CSIDL_CDBURN_AREA; MinVersion: $00060000; OnlyNT: False; CanSimulate: False; Alternative: fdNoSpecialFolder), { fdCommonMusic } (CSIDL: CSIDL_COMMON_MUSIC; MinVersion: $00060000; OnlyNT: False; CanSimulate: False; Alternative: fdCommonDocuments), { fdCommonPictures } (CSIDL: CSIDL_COMMON_PICTURES; MinVersion: $00060000; OnlyNT: False; CanSimulate: False; Alternative: fdCommonDocuments), { fdCommonVideo } (CSIDL: CSIDL_COMMON_VIDEO; MinVersion: $00060000; OnlyNT: False; CanSimulate: False; Alternative: fdCommonDocuments), { fdMyDocuments } (CSIDL: CSIDL_MYDOCUMENTS; MinVersion: $00060000; OnlyNT: False; CanSimulate: False; Alternative: fdPersonal), { fdMyVideo } (CSIDL: CSIDL_MYVIDEO; MinVersion: $00060000; OnlyNT: False; CanSimulate: False; Alternative: fdPersonal), { fdProfiles } (CSIDL: CSIDL_PROFILES; MinVersion: $00060000; OnlyNT: False; CanSimulate: False; Alternative: fdNoSpecialFolder), { fdResources } (CSIDL: CSIDL_RESOURCES; MinVersion: $00060000; OnlyNT: False; CanSimulate: False; Alternative: fdNoSpecialFolder), { fdResourcesLocalized } (CSIDL: CSIDL_RESOURCES_LOCALIZED; MinVersion: $00060000; OnlyNT: False; CanSimulate: False; Alternative: fdNoSpecialFolder), { fdCommonOEMLinks } (CSIDL: CSIDL_COMMON_OEM_LINKS; MinVersion: $00060000; OnlyNT: False; CanSimulate: False; Alternative: fdNoSpecialFolder), { fdComputersNearMe } (CSIDL: CSIDL_COMPUTERSNEARME; MinVersion: $00060000; OnlyNT: False; CanSimulate: False; Alternative: fdNoSpecialFolder) ); {$IFDEF CLR} {$ELSE} procedure InitSHFolder; const SHFolderDll = 'SHFolder.dll'; var SHFolderHandle: HMODULE; begin { You never know, maybe someone does not have SHFolder.dll, thus load on request } SHFolderHandle := GetModuleHandle(SHFolderDll); if SHFolderHandle <> 0 then SHGetFolderPathProc := GetProcAddress(SHFolderHandle, 'SHGetFolderPathA'); end; {$ENDIF CLR} procedure GetCSIDLLocation(const ASpecialDirectory: TFromDirectory; var CSIDL: Cardinal; var APath: string); { This function is a bit overkill } var LSpecialDirectory: TFromDirectory; {$IFDEF CLR} Buffer: StringBuilder; {$ELSE} Buffer: PChar; {$ENDIF CLR} function IsOk: Boolean; begin with CSIDLLocations[LSpecialDirectory] do Result := (MinVersion <= GetShellVersion) and (not OnlyNT or (Win32Platform = VER_PLATFORM_WIN32_NT)); end; begin LSpecialDirectory := ASpecialDirectory; while (LSpecialDirectory <> fdNoSpecialFolder) and not CSIDLLocations[LSpecialDirectory].CanSimulate and not IsOk do LSpecialDirectory := CSIDLLocations[LSpecialDirectory].Alternative; if (LSpecialDirectory = fdNoSpecialFolder) or IsOk then begin CSIDL := CSIDLLocations[LSpecialDirectory].CSIDL; Exit; end; CSIDL := 0; {$IFDEF CLR} Buffer := StringBuilder.Create(MAX_PATH); Buffer.Length := MAX_PATH; if Succeeded(SHGetFolderPathProc(0, CSIDLLocations[LSpecialDirectory].CSIDL, 0, 0, Buffer)) then APath := Buffer.ToString() else APath := ''; {$ELSE} GetMem(Buffer, MAX_PATH); try if not Assigned(SHGetFolderPathProc) then InitSHFolder; if Assigned(SHGetFolderPathProc) and Succeeded(SHGetFolderPathProc(0, CSIDLLocations[LSpecialDirectory].CSIDL, 0, 0, Buffer)) then APath := Buffer else APath := ''; finally FreeMem(Buffer); end; {$ENDIF CLR} end; function CreateIDListFromPath(const APath: string): PItemIDList; var WS: WideString; Eaten, Flags: LongWord; IDesktopFolder: IShellFolder; begin { Returned value must be freed } Result := nil; if APath = '' then Exit; WS := APath; { MSDN : Since Flags is an in/out parameter, it should always be initialized } Flags := 0; if Failed(SHGetDesktopFolder(IDesktopFolder)) or Failed(IDesktopFolder.ParseDisplayName(0, nil, POleStr(WS), Eaten, Result, Flags)) then Result := nil; end; function CreateIDListFromCSIDL(const ASpecialDirectory: TFromDirectory): PItemIDList; var CSIDL: Cardinal; Path: string; begin { Returned value must be freed } Result := nil; if ASpecialDirectory = fdNoSpecialFolder then Exit; GetCSIDLLocation(ASpecialDirectory, CSIDL, Path); if CSIDL <> 0 then begin { MSDN: The calling application is responsible for freeing this pointer } { SHGetSpecialFolderLocation is shell v4.7 or later} if Failed(SHGetSpecialFolderLocation(0, CSIDL, Result)) then Result := nil; end else Result := CreateIDListFromPath(Path); end; function IDListToPath(IDList: PItemIDList): string; var IDesktopFolder: IShellFolder; StrRet: TStrRet; begin { Similar to SHGetPathFromIDList } if Succeeded(SHGetDesktopFolder(IDesktopFolder)) and Succeeded(IDesktopFolder.GetDisplayNameOf(IDList, SHGDN_NORMAL or SHGDN_FORPARSING, StrRet)) then { Result may be a GUID; Don't know whether these GUIDs are portable. Microsoft does recommend to return strings 'that are as close to the display names as possible'. But in this case display names aren't usable } Result := StrRetToString(IDList, StrRet) else Result := ''; (* These GUID's seem pretty portable, you can enter them at RootDirectoryPath or Directory, ie the "::{GUID}" part (only tested on Windows XP). ::{00020D75-0000-0000-C000-000000000046} - Inbox ::{20D04FE0-3AEA-1069-A2D8-08002B30309D} - CSIDL_DRIVES ::{208D2C60-3AEA-1069-A2D7-08002B30309D} - CSIDL_NETWORK, CSIDL_NETHOOD ::{21EC2020-3AEA-1069-A2DD-08002B30309D} - CSIDL_CONTROLS ::{2227A280-3AEA-1069-A2DE-08002B30309D} - CSIDL_PRINTERS, CSIDL_PRINTHOOD ::{450D8FBA-AD25-11D0-98A8-0800361B1103} - CSIDL_PERSONAL ::{645FF040-5081-101B-9F08-00AA002F954E} - CSIDL_BITBUCKET ::{7007ACC7-3202-11D1-AAD2-00805FC1270E} - CSIDL_CONNECTIONS ::{871C5380-42A0-1069-A2EA-08002B30309D} - CSIDL_INTERNET ::{D6277990-4C6A-11CF-8D87-00AA0060F5BF} - Scheduled Tasks *) end; function CSIDLToPath(const ASpecialDirectory: TFromDirectory): string; var CSIDL: Cardinal; IDList: PItemIDList; ShellMalloc: IMalloc; begin if ASpecialDirectory = fdNoSpecialFolder then begin Result := ''; Exit; end; GetCSIDLLocation(ASpecialDirectory, CSIDL, Result); if CSIDL = 0 then Exit; { SHGetSpecialFolderLocation is shell v4.7 or later} if Succeeded(SHGetSpecialFolderLocation(0, CSIDL, IDList)) then try Result := IDListToPath(IDList); finally if Succeeded(SHGetMalloc(ShellMalloc)) then ShellMalloc.Free(IDList); end else Result := ''; end; procedure SetDialogPos(AParentHandle, AWndHandle: THandle; Position: TJvFolderPos); var R, SR: TRect; begin if GetClientRect(AWndHandle, R) then begin //R.Right := R.Left + AWidth; //R.Bottom := R.Top + AHeight; SystemParametersInfo(SPI_GETWORKAREA, 0, @SR, 0); case Position of fpScreenCenter: begin R.Left := ((SR.Right - SR.Left - (R.Right - R.Left)) div 2); R.Top := (SR.Bottom - SR.Top - (R.Bottom - R.Top)) div 2; end; fpFormCenter: begin GetWindowRect(AParentHandle, SR); R.Left := SR.Left + ((SR.Right - SR.Left - (R.Right - R.Left)) div 2); R.Top := SR.Top + (SR.Bottom - SR.Top - (R.Bottom - R.Top)) div 2; end; fpTopLeft: begin R.Left := SR.Left; R.Top := SR.Top; end; fpTopRight: begin R.Top := SR.Top; R.Left := SR.Right - (R.Right - R.Left) - GetSystemMetrics(SM_CXFIXEDFRAME); end; fpBottomLeft: begin R.Top := SR.Bottom - (R.Bottom - R.Top) - GetSystemMetrics(SM_CYCAPTION) - -GetSystemMetrics(SM_CYFIXEDFRAME); R.Left := SR.Left; end; fpBottomRight: begin R.Top := SR.Bottom - (R.Bottom - R.Top) - GetSystemMetrics(SM_CYCAPTION) - GetSystemMetrics(SM_CYFIXEDFRAME); R.Left := SR.Right - (R.Right - R.Left) - GetSystemMetrics(SM_CXFIXEDFRAME); end; fpDefault: Exit; end; SetWindowPos(AWndHandle, 0, R.Left, R.Top, 0, 0, SWP_NOACTIVATE or SWP_NOSIZE or SWP_NOZORDER); end; end; //=== { TJvBrowseForFolderDialog } =========================================== function lpfnBrowseProc(Wnd: HWND; uMsg: UINT; lParam, lpData: LPARAM): Integer; stdcall; begin Result := 0; with TJvBrowseForFolderDialog(lpData) do begin FDialogWindow := Wnd; case uMsg of BFFM_INITIALIZED: DoInitialized; BFFM_SELCHANGED: DoSelChanged(PItemIDList(lParam)); BFFM_IUNKNOWN: DoIUnknown(IUnknown(lParam)); BFFM_VALIDATEFAILEDA: Result := DoValidateFailed(PChar(lParam)); BFFM_VALIDATEFAILEDW: Result := DoValidateFailedW(PWideChar(lParam)); end; end; end; constructor TJvBrowseForFolderDialog.Create(AOwner: TComponent); begin inherited Create(AOwner); FOptions := [odStatusAvailable, odNewDialogStyle]; FPosition := fpScreenCenter; // ahuser: changed from fpDefault - I think no one wants the dialog in the right bottom corner FRootDirectory := fdNoSpecialFolder; FObjectInstance := JvMakeObjectInstance(MainWndProc); end; destructor TJvBrowseForFolderDialog.Destroy; begin PidlFree(FPidl); JvFreeObjectInstance(FObjectInstance); inherited Destroy; end; procedure TJvBrowseForFolderDialog.DefaultHandler(var Msg); begin if FDialogWindow <> 0 then with TMessage(Msg) do Result := CallWindowProc(FDefWndProc, FDialogWindow, Msg, WParam, LParam) else inherited DefaultHandler(Msg); end; function TJvBrowseForFolderDialog.DoGetEnumFlags(const AFolder: string; var Flags: TJvBrowsableObjectClasses): Boolean; begin { (rb) Always return True? } Result := True; if Assigned(FOnGetEnumFlags) then FOnGetEnumFlags(Self, AFolder, Flags); end; procedure TJvBrowseForFolderDialog.DoInitialized; const SBtn = 'BUTTON'; HelpButtonId = $FFFF; var BtnHandle, BtnFont: THandle; BtnSize, WindowSize: TRect; begin { We can now change the position of the dialog - if it's not NewDialogStyle.. } FPositionSet := not (odNewDialogStyle in FUsedOptions); if FPositionSet then SetDialogPos(FOwnerWindow, FDialogWindow, Position); { ..Otherwise we have to delay the change until receive of WM_SHOWWINDOW, thus we need to hook the dialog; we also need to hook the dialog if there is a new help button on the dialog and the dialog is resizeable - ie NewDialogStyle } if not FPositionSet or ((FHelpContext <> 0) and (odNewDialogStyle in FUsedOptions)) then HookDialog; // [roko] Rx's code to insert Help button if FHelpContext <> 0 then begin { SomeBtnHandle is some button on the window; we need it to determine a useable height & width for the new help button } BtnHandle := FindWindowEx(FDialogWindow, 0, SBtn, nil); if BtnHandle <> 0 then begin GetWindowRect(BtnHandle, BtnSize); GetWindowRect(FDialogWindow, WindowSize); ScreenToClient(FDialogWindow, BtnSize.TopLeft); ScreenToClient(FDialogWindow, BtnSize.BottomRight); BtnFont := SendMessage(FDialogWindow, WM_GETFONT, 0, 0); { Note: BtnSize.Top = "Window.Height" - FHelpButtonHeightDelta, used in WM_SIZE } FHelpButtonHeightDelta := WindowSize.Bottom - WindowSize.Top - BtnSize.Top; { Remember the new buttons handle, because we need it, when the dialog is resized } FHelpButtonHandle := CreateWindow(SBtn, PChar(SHelpButton), WS_CHILD or WS_CLIPSIBLINGS or WS_VISIBLE or BS_PUSHBUTTON or WS_TABSTOP, 12, BtnSize.Top, BtnSize.Right - BtnSize.Left, BtnSize.Bottom - BtnSize.Top, FDialogWindow, HelpButtonId, HInstance, nil); if BtnFont <> 0 then SendMessage(FHelpButtonHandle, WM_SETFONT, BtnFont, MakeLParam(1, 0)); UpdateWindow(FDialogWindow); end; end; { Change directory (if possible) } if FDirectory <> '' then SetSelection(FDirectory); UpdateStatusText(FDirectory); if Assigned(FOnInit) then FOnInit(Self); end; procedure TJvBrowseForFolderDialog.DoIUnknown(const Unknown: IUnknown); var FolderFilterSite: IFolderFilterSite; begin if (Assigned(FOnGetEnumFlags) or Assigned(FOnShouldShow)) and Supports(Unknown, IID_IFolderFilterSite, FolderFilterSite) then begin FolderFilterSite.SetFilter(Self); FolderFilterSite := nil; end; end; procedure TJvBrowseForFolderDialog.DoSelChanged(IDList: PItemIDList); var // (p3) use buff array instead of string as this works better Buffer: array [0..MAX_PATH] of Char; Path: string; Accept: Boolean; SavePidl: PItemIDList; begin { Note : * If the location specified by the pidl parameter is not part of the file system, this function will fail. * If the pidl parameter specifies a shortcut, the pszPath will contain the path to the shortcut, not to the shortcut's target. (if not win XP ) Could also use IDListToPath } if SHGetPathFromIDList(IDList, Buffer) then Path := Buffer else Path := ''; SavePidl := FPidl; FPidl := IDList; try if Assigned(FOnAcceptChange) then begin Accept := True; FOnAcceptChange(Self, Path, Accept); SetOKEnabled(Accept); end; UpdateStatusText(Path); if Assigned(FOnChange) then FOnChange(Self, Path); finally FPidl := SavePidl; end; end; function TJvBrowseForFolderDialog.DoShouldShow(const AItem: string): Boolean; begin if Assigned(FOnShouldShow) then FOnShouldShow(Self, AItem, Result) else Result := True; end; function TJvBrowseForFolderDialog.DoValidateFailed(AEditText: string): Integer; var CanClose: Boolean; begin { Return zero to allow the dialog to be dismissed or nonzero to keep the dialog displayed. } if Assigned(FOnValidateFailed) then begin CanClose := True; FOnValidateFailed(Self, AEditText, CanClose); Result := Integer(not CanClose); end else Result := 0; // = Integer(False) end; function TJvBrowseForFolderDialog.DoValidateFailedW(AEditText: WideString): Integer; begin { Explicit conversion } Result := DoValidateFailed(PChar(string(AEditText))); end; function TJvBrowseForFolderDialog.Execute: Boolean; var dspName: array [0..MAX_PATH] of Char; BrowseInfo: TBrowseInfo; ShellVersion: Cardinal; ActiveWindow: HWND; WindowList: Pointer; Option: TOptionsDirectory; begin ShellVersion := GetShellVersion; if ShellVersion < $00040000 then raise EJVCLException.CreateRes(@RsEShellNotCompatible); FDialogWindow := 0; FOwnerWindow := GetOwnerWindow; FPositionSet := False; FHelpButtonHandle := 0; FHelpButtonHeightDelta := 0; Result := False; FillChar(BrowseInfo, SizeOf(BrowseInfo), #0); { FUsedOptions is a subset of FOptions; the options that actually can be used because of shell version limitations } FUsedOptions := FOptions; if ShellVersion < $00060000 then FUsedOptions := FUsedOptions - [odNoNewButtonFolder, odUsageHint]; if ShellVersion < $00050000 then FUsedOptions := FUsedOptions - [odIncludeUrls, odNewDialogStyle, odShareable]; if ShellVersion < $00040071 then FUsedOptions := FUsedOptions - [odIncludeFiles, odEditBox, odValidate]; for Option := Low(TOptionsDirectory) to High(TOptionsDirectory) do if Option in FUsedOptions then Inc(BrowseInfo.ulFlags, COptionsDirectory[Option]); BrowseInfo.hwndOwner := FOwnerWindow; BrowseInfo.pszDisplayName := dspName; BrowseInfo.lpfn := TFNBFFCallBack(@lpfnBrowseProc); BrowseInfo.lParam := Longint(Self); if (FStatusText = '') or not (odNewDialogStyle in FUsedOptions) then BrowseInfo.lpszTitle := Pointer(FTitle) else if FTitle = '' then BrowseInfo.lpszTitle := PChar(FStatusText) else BrowseInfo.lpszTitle := PChar(FTitle + Cr + FStatusText); if FRootDirectory = fdNoSpecialFolder then BrowseInfo.pidlRoot := CreateIDListFromPath(FRootDirectoryPath) else BrowseInfo.pidlRoot := CreateIDListFromCSIDL(FRootDirectory); try if odNewDialogStyle in FUsedOptions then CoInitialize(nil); try ActiveWindow := GetActiveWindow; WindowList := DisableTaskWindows(0); try if not PidlFree(FPidl) then begin Assert(False); // FPidl comes from shell, so PidlFree should never fail FPidl := nil; // in case building without assertions, need to ensure FPidl is nil end; FPidl := SHBrowseForFolder(BrowseInfo); finally EnableTaskWindows(WindowList); SetActiveWindow(ActiveWindow); end; Result := FPidl <> nil; if Result then begin FDisplayName := BrowseInfo.pszDisplayName; FDirectory := IDListToPath(FPidl); end; PidlFree(BrowseInfo.pidlRoot); finally FDialogWindow := 0; FOwnerWindow := 0; if odNewDialogStyle in FUsedOptions then CoUninitialize; end; except end; end; function TJvBrowseForFolderDialog.GetEnumFlags(psf: IShellFolder; pidlFolder: PItemIDList; const phWnd: THandle; var pgrfFlags: DWORD): HResult; var Flags: TJvBrowsableObjectClasses; Obj: TJvBrowsableObjectClass; begin { (rb) Don't know for sure if pgrfFlags is initialized } Flags := []; for Obj := Low(TJvBrowsableObjectClass) to High(TJvBrowsableObjectClass) do if pgrfFlags and CBrowseObjectClasses[Obj] = CBrowseObjectClasses[Obj] then Include(Flags, Obj); { This seems not to work ?? : } //if psf.GetDisplayNameOf(pidlFolder, SHGDN_NORMAL or SHGDN_FORPARSING, StrRet) <> S_OK then // Exit; try if DoGetEnumFlags(IDListToPath(pidlFolder), Flags) then Result := S_OK else Result := S_FALSE; except Result := E_UNEXPECTED; end; pgrfFlags := 0; for Obj := Low(TJvBrowsableObjectClass) to High(TJvBrowsableObjectClass) do if Obj in Flags then Inc(pgrfFlags, CBrowseObjectClasses[Obj]); end; function TJvBrowseForFolderDialog.GetOwnerWindow: THandle; var F: TCustomForm; begin // (Ralf Kaiser) Owner maybe a TDataModule if Owner is TControl then F := GetParentForm(TControl(Owner)) else F := nil; if F <> nil then {$IFDEF VCL} Result := F.Handle {$ENDIF VCL} {$IFDEF VisualCLX} Result := QWidget_winId(F.Handle) {$ENDIF VisualCLX} else if Owner is TWinControl then {$IFDEF VCL} Result := (Owner as TWinControl).Handle {$ENDIF VCL} {$IFDEF VisualCLX} Result := QWidget_winId((Owner as TWinControl).Handle) {$ENDIF VisualCLX} else if (Screen <> nil) and (Screen.ActiveCustomForm <> nil) then {$IFDEF VCL} Result := Screen.ActiveCustomForm.Handle {$ENDIF VCL} {$IFDEF VisualCLX} Result := QWidget_winId(Screen.ActiveCustomForm.Handle) {$ENDIF VisualCLX} else Result := GetForegroundWindow; end; function TJvBrowseForFolderDialog.GetRootDirectoryPath: string; begin if FRootDirectory = fdNoSpecialFolder then Result := FRootDirectoryPath else Result := CSIDLToPath(FRootDirectory); end; procedure TJvBrowseForFolderDialog.HookDialog; begin if FDialogWindow <> 0 then FDefWndProc := Pointer(SetWindowLong(FDialogWindow, GWL_WNDPROC, Longint(FObjectInstance))); end; function TJvBrowseForFolderDialog.IsRootDirectoryPathStored: Boolean; begin Result := (RootDirectory = fdNoSpecialFolder) and (FRootDirectoryPath > ''); end; procedure TJvBrowseForFolderDialog.MainWndProc(var Msg: TMessage); begin try Dispatch(Msg); except Application.HandleException(Self); end; end; procedure TJvBrowseForFolderDialog.SetExpanded(const APath: string); begin if FDialogWindow <> 0 then { Implicit conversion } SetExpandedW(APath); end; procedure TJvBrowseForFolderDialog.SetExpanded(IDList: PItemIDList); begin if FDialogWindow <> 0 then SendMessage(FDialogWindow, BFFM_SETEXPANDED, WPARAM(False), LPARAM(IDList)); end; procedure TJvBrowseForFolderDialog.SetExpandedW(const APath: WideString); begin if FDialogWindow <> 0 then SendMessage(FDialogWindow, BFFM_SETEXPANDED, WPARAM(True), LPARAM(PWideChar(APath))); end; procedure TJvBrowseForFolderDialog.SetOKEnabled(const Value: Boolean); begin if FDialogWindow <> 0 then SendMessage(FDialogWindow, BFFM_ENABLEOK, 0, LPARAM(Value)); end; procedure TJvBrowseForFolderDialog.SetOKText(const AText: string); begin if FDialogWindow <> 0 then { Implicit conversion } SetOKTextW(AText); end; procedure TJvBrowseForFolderDialog.SetOKTextW(const AText: WideString); begin if FDialogWindow <> 0 then SendMessage(FDialogWindow, BFFM_SETOKTEXT, 0, LPARAM(PWideChar(AText))); end; procedure TJvBrowseForFolderDialog.SetOptions(const Value: TOptionsDir); var AddedOptions, RemovedOptions: TOptionsDir; begin if FOptions = Value then Exit; AddedOptions := Value - (FOptions * Value); RemovedOptions := FOptions - (FOptions * Value); FOptions := Value; { Force correct options } if odIncludeUrls in AddedOptions then FOptions := FOptions + [odEditBox, odNewDialogStyle, odIncludeFiles]; if odShareable in AddedOptions then FOptions := FOptions + [odNewDialogStyle]; if odUsageHint in AddedOptions then FOptions := FOptions + [odNewDialogStyle] - [odEditBox]; if odValidate in AddedOptions then FOptions := FOptions + [odEditBox]; if odEditBox in AddedOptions then FOptions := FOptions - [odUsageHint]; if odEditBox in RemovedOptions then FOptions := FOptions - [odIncludeUrls, odValidate]; if odNewDialogStyle in RemovedOptions then FOptions := FOptions - [odIncludeUrls, odShareable, odUsageHint]; if odIncludeFiles in RemovedOptions then FOptions := FOptions - [odIncludeUrls]; { Last check } if odEditBox in FOptions then FOptions := FOptions - [odUsageHint] else FOptions := FOptions - [odIncludeUrls, odValidate]; if odUsageHint in FOptions then FOptions := FOptions - [odValidate, odEditBox]; end; procedure TJvBrowseForFolderDialog.SetRootDirectory( const Value: TFromDirectory); begin if (Value = fdNoSpecialFolder) and (FRootDirectory <> fdNoSpecialFolder) then FRootDirectoryPath := GetRootDirectoryPath; FRootDirectory := Value; end; procedure TJvBrowseForFolderDialog.SetRootDirectoryPath( const Value: string); begin FRootDirectory := fdNoSpecialFolder; FRootDirectoryPath := Value; end; procedure TJvBrowseForFolderDialog.SetSelection(const APath: string); begin if FDialogWindow <> 0 then SendMessage(FDialogWindow, BFFM_SETSELECTION, WPARAM(True), LPARAM(Pointer(APath))); end; procedure TJvBrowseForFolderDialog.SetSelection(IDList: PItemIDList); begin if FDialogWindow <> 0 then SendMessage(FDialogWindow, BFFM_SETSELECTION, WPARAM(False), LPARAM(IDList)); end; procedure TJvBrowseForFolderDialog.SetStatusText(const AText: string); begin if FDialogWindow <> 0 then SendMessage(FDialogWindow, BFFM_SETSTATUSTEXT, 0, LPARAM(Pointer(AText))); end; procedure TJvBrowseForFolderDialog.SetStatusTextW(const AText: WideString); begin if FDialogWindow <> 0 then SendMessage(FDialogWindow, BFFM_SETSTATUSTEXTW, 0, LPARAM(PWideChar(AText))); end; function TJvBrowseForFolderDialog.ShouldShow(psf: IShellFolder; pidlFolder, pidlItem: PItemIDList): HResult; var StrRet: TStrRet; begin psf.GetDisplayNameOf(pidlItem, SHGDN_NORMAL or SHGDN_FORPARSING, StrRet); try if DoShouldShow(StrRetToString(pidlItem, StrRet)) then Result := S_OK else Result := S_FALSE; except Result := E_UNEXPECTED; end; end; procedure TJvBrowseForFolderDialog.UpdateStatusText(AText: string); const cStatusLabel = $3743; var WindowRect, ItemRect: TRect; ItemHandle: THandle; LCanvas: TCanvas; begin if [odStatusAvailable, odNewDialogStyle] * FUsedOptions <> [odStatusAvailable] then Exit; if StatusText <> '' then AText := StatusText else begin ItemHandle := GetDlgItem(FDialogWindow, cStatusLabel); if ItemHandle <> 0 then begin GetWindowRect(FDialogWindow, WindowRect); GetWindowRect(ItemHandle, ItemRect); if Application.MainForm <> nil then LCanvas := Application.MainForm.Canvas else begin LCanvas := TCanvas.Create; LCanvas.Handle := GetDC(HWND_DESKTOP); end; AText := MinimizeFileName(AText, LCanvas, (WindowRect.Right - WindowRect.Left) - (ItemRect.Left - WindowRect.Left) * 2 - 8); if Application.MainForm = nil then LCanvas.Free; end; end; SetStatusText(AText); end; procedure TJvBrowseForFolderDialog.WMShowWindow(var Msg: TMessage); begin { If the dialog isn't resized, we won't get a WM_SIZE message. Thus we respond to the WM_SHOWWINDOW message } if not FPositionSet then SetDialogPos(FOwnerWindow, FDialogWindow, Position); FPositionSet := True; inherited; end; procedure TJvBrowseForFolderDialog.WMSize(var Msg: TWMSize); var BtnSize: TRect; WindowSize: TRect; begin inherited; if FHelpButtonHandle <> 0 then begin GetWindowRect(FHelpButtonHandle, BtnSize); GetWindowRect(FDialogWindow, WindowSize); ScreenToClient(FDialogWindow, BtnSize.TopLeft); ScreenToClient(FDialogWindow, BtnSize.BottomRight); SetWindowPos(FHelpButtonHandle, 0, BtnSize.Left, WindowSize.Bottom - WindowSize.Top - FHelpButtonHeightDelta, BtnSize.Right - BtnSize.Left, BtnSize.Bottom - BtnSize.Top, SWP_NOZORDER + SWP_NOACTIVATE); end; end; {$IFDEF UNITVERSIONING} initialization RegisterUnitVersion(HInstance, UnitVersioning); finalization UnregisterUnitVersion(HInstance); {$ENDIF UNITVERSIONING} end.