I need an 'Open folder' dialog with possibility to manually enter path - delphi

I use FileCtrl.SelectDirectory to show a 'open folder' dialog. However, I am unhappy with it because it doesn't allow the user to enter a path from where to start the browsing.
For example, if the user already has the path in clipboard it should be able to enter it into my dialog instead of wasting 12 seconds to navigate (open) lots of folders until it gets there.
I have found this code which seems to do EXACTLY what FileCtrl.SelectDirectory does. i hopped it will allow me to configure the dialog more. It doesn't.
So, how do I show a editbox in the SelectDirectory where the user can enter the path?
The solution that I have now, is my own dialog box. It is build from zero using TDirectory and TListBox. Very handy. BUT it looks so obsole because it uses Embarcadero's file management controls (TDirectory, TListBox) and we all know how dull they look like.
To make it clear: I would like something like FileCtrl.SelectDirectory but with an extact TEdit or a crumbar where the user can enter its path (if he has any).
Example:

Passing sdShowEdit to FileCtrl.SelectDirectory adds an edit box that you can paste a directory into.
FileCtrl.SelectDirectory('Caption', 'C:\', Dir, [sdNewUI, sdShowEdit]);

If you use the overloaded version of SelectDirectory() that has a Root parameter, it calls SHBrowseForFolder() internally (the other overload displays a custom VCL Win3.1-style dialog instead). If you assign an initial value to the variable that you pass to the Directory parameter, it gets passed to SHBrowseForFolder() as the initial selected folder. You can also specify the sdShowEdit flag in the Options parameter. However, the edit box is not meant for entering full paths. But, if you call SHBrowseForFolder() directly, you can provide your own callback function for it, so when the dialog sends you a BFFM_VALIDATEFAILED event for instance, you can grab the text from the dialog's edit box and send the dialog window a BFFM_SETSELECTION message to navigate to the correct path.
What you are really asking for is the customization provided by the Vista+ IFileDialog dialog instead. You can use the IFileDialogCustomize interface to add custom controls to the dialog, such as edit boxes and buttons, and then implement the IFileDialogControlEvents interface to know when various actions occur on those controls, like button clicks. You can use that to check your custom edit box, or the clipboard, for a valid path and if detected then tell the dialog to navigate to that path via the IFileDialog.SetFolder() method.

TJvDirectoryEdit from Jedi VCS does that. Look it up.
Here are some pictures of it:

If I understand correctly I think this could be your solution.
procedure TForm1.Button1Click(Sender: TObject);
var
opendialog : Topendialog;
begin
openDialog := TOpenDialog.Create(self);
openDialog.InitialDir := GetCurrentDir; {This can also be what is on the clipboard}
openDialog.Options := [ofFileMustExist];
openDialog.Filter := 'Text Document |*.txt'; {This is the type of file the user must open}
openDialog.FilterIndex := 1;
opendialog.execute;
end;
This code creates and shows a simple open dialog.
The path the user then selected is:
opendialog.filename

#davea's answer is ok but it only shows the old (WinXP) dialog style.
So, this is the code I use now. On Win Vista and up it shows the new style dialog and the old style on Win XP:
{$WARN SYMBOL_PLATFORM OFF}
{$IFDEF MSWindows}
function SelectAFolder(VAR Folder: string; CONST Options: TFileDialogOptions= [fdoPickFolders, fdoForceFileSystem, fdoPathMustExist, fdoDefaultNoMiniMode]): Boolean; { Keywords: FolderDialog, BrowseForFolder} { Works with UNC paths }
VAR Dlg: TFileOpenDialog;
begin
{ Win Vista and up }
if OS_IsWindowsVistaUp then
begin
Dlg:= TFileOpenDialog.Create(NIL); { Class for Vista and newer Windows operating systems style file open dialogs }
TRY
Dlg.Options := Options;
Dlg.DefaultFolder := Folder;
Dlg.FileName := Folder;
Result := Dlg.Execute;
if Result
then Folder:= Dlg.FileName;
FINALLY
FreeAndNil(Dlg);
END;
end
else
{ Win XP or down }
Result:= vcl.FileCtrl.SelectDirectory('', ExtractFileDrive(Folder), Folder, [sdNewUI, sdShowEdit, sdNewFolder], nil);
if Result
then Folder:= Trail(Folder);
end;
{$ENDIF}
{$WARN SYMBOL_PLATFORM On}
{ Keywords: FolderDialog, BrowseForFolder}

Related

File Open Dialog with Preview in Delphi 10.3

I changed for Delphi 10.3 and its default TOpenDialog contains a preview pane. I made some searches and found the IFileDialogCustomize interface provided by Microsoft to customize standard WinAPI dialogs. I know I have to use the OnSelectionChange event handler to modify the picture of the pane. The big question for me is : how can I access the preview pane image by IFileDialogCustomize? What is the ItemID for this? I couldn't find any answer to this question on the net. Somebody know the answer? Then please share with me and the community! :)
I replaced some code fragments by ... for the sake of brevity, because these are trivial or app dependent sections.
procedure TMainWindow.OnSelectionChange( Sender : TObject );
var
dc : HDC;
aBMP : TBitmap;
function isSelectedFilePreviewAble : boolean;
begin
result := ...;
end;
functon getPreviewPictureDC : HDC;
var
iCustomize : IFileDialogCustomize;
h : THandle;
begin
if OpenDialog1.QueryInterface( IFileDialogCustomize, iCustomize ) = S_OK then
begin
h := iCustomize.??? this is the missing code fragment
result := GetDC( h );
end else
result := 0;
end;
procedure generatePreviewPicture;
begin
...
end;
begin
dc := getPreviewPictureDC;
if ( dc <> 0 ) then
begin
aBMP := TBitmap.Create;
try
if ( isSelectedFilePreviewAble ) then
generatePreviewPicture;
StretchBlt( aBMP.Handle, ...);
finally
aBMP.Free;
ReleaseDC( dc );
end;
end;
end;
I made some searches and found the IFileDialogCustomize interface provided by Microsoft to customize standard WinAPI dialogs.
First, IFileDialogCustomize does not "customize standard WinAPI dialogs". It customizes only IFileOpenDialog and IFileSaveDialog dialogs, no others.
Second, TOpenDialog primarily uses the legacy Win32 API GetOpenFileName() function. On Windows Vista+, GetOpenFileName() uses IFileOpenDialog internally with basic options enabled, so that legacy apps can still have a modern look.
Although, under the following conditions, TOpenDialog will instead use IFileOpenDialog directly rather than using GetOpenFileName():
Win32MajorVersion is >= 6 (Vista+)
UseLatestCommonDialogs is True
StyleServices.Enabled is True
TOpenDialog.Template is nil
TOpenDialog.OnIncludeItem, TOpenDialog.OnClose, and TOpenDialog.OnShow are unassigned.
But even so, TOpenDialog still does not give you access to its internal IFileOpenDialog interface, when it is used.
If you really want to access the dialog's IFileOpenDialog and thus its IFileDialogCustomize, you need to use TFileOpenDialog instead of TOpenDialog (just know that dialog won't work on XP and earlier systems, if you still need to support them).
The big question for me is : how can I access the preview pane image by IFileDialogCustomize?
You don't. The preview pane is not a dialog customization, so it can't be accessed via IFileDialogCustomize. Even if you could get a control ID for the preview pane (which you can't), there is no function of IFileDialogCustomize that would allow you to access the preview pane's HWND or HDC, or otherwise alter the content of the preview pane in any way. The preview pane is an integral and private component of IFileDialog for any file type that supports previews. It is not something that you can access and draw on directly. IFileOpenDialog itself will update the preview pane as needed when the user selects a file that has (or lacks) a preview to display.
My boss want to show previews for our own file formats.
The correct way to handle that on Vista+ is to create a Preview Handler for your custom file types. Then, any Shell component that wants to display previews of your files, including IFileOpenDialog, can use your handler.

CHM file not displaying correctly when Delphi VCL style active

My Delphi application includes a help file that the user can call from anywhere in the application (well... that is, for all the parts I've written so far...)
It also includes the ability for the user to switch from the regular style to another VCL style from a list.
When no style is applied, the help file displays normally like this :
But as soon as a VCL style is active, the Help file does not display correctly anymore, like this :
Is this due to the way I declare the HelpFile on main Form creation like this (path being a global variable pointing to the main exe folder):
Application.HelpFile := path+'Help\D.R.A.M.A. 2.0 Help.chm';
or is this a known problem that can not be solved ?
SIDE NOTE : the help is called on helpContext should that be important to mention and the HtmlHelpViewer is added to the uses clause.
This answer was taken from https://forums.embarcadero.com/thread.jspa?threadID=227785 and I've confirmed works very well.
Drop a TApplicationEvents component onto the applications main form.
Implement the OnHelp event of that component as this:
function TfmMain.ApplicationEvents1Help(Command: Word; Data: NativeInt; var CallHelp: Boolean): Boolean;
begin
CloseHelpWnd;
Result := ShellExecute(0,'open','hh.exe',
PWideChar('-mapid '+IntToStr(Data)
+' ms-its:'+Application.HelpFile),
nil,SW_SHOW) = 32;
CallHelp := false;
end;
On the main form, implement the CloseHelpWnd method as this:
procedure TfmMain.CloseHelpWnd;
var
HlpWind: HWND;
const
HelpTitle = 'Your help file title';
begin
HlpWind := FindWindow('HH Parent',HelpTitle);
if HlpWind <> 0 then PostMessage(HlpWind,WM_Close,0,0);
end;
You would replace 'Your help file title' with the title of your help file. This is the window caption title when you open the help file directly.
In the FormDestroy event for the main form, include a call to
CloseHelpWnd;
So far we've not seen any issues with the above method, and because we are running the help file in a separate process, it is not affected by the VCL Styles problems evident in Delphi 10.2 Tokyo.
NOTE: It does not have to be the applications main form, but it must be a form that is created before the help system is needed and remains instantiated while the application is running. In our case, we did it on a common resources form and then all programs we rebuilt with the new form had the help problem resolved.
NOTE: You still need to set the Application.HelpFile property as normal, but you don't need to include the HtmlHelpViewer unit in the Uses clause.

how to get the selected text from acropdf component to an edit directly with Delphi 7

I study preparing a dictionary programme with delphi. So far I have solved my problems about Word documents but I've got some problem about PDF documents.
I imported and installed the AcroPdf component with Delphi 7 and I want to get the word (or text) which was selected by dblclicking by user from pdf document which was viewed by the ACROPDF component in Delphi. If I can get it I'll send it the dictionary database directly.
If you help me I'll be glad. Thank you...
Remzi MAKAK
The following shows one way to get the selected text from a Pdf document which is
open in Adobe Acrobat Professional (v.8, English version).
Update The original version of this answer neglected to check the Boolean result of calling MenuItemExecute and specified the wrong argument to it. Both these points are fixed in the updated version of this answer. It turned out that the reason the call to MenuItemExecute was failing was that it is essential to call BringToFront on the Acrobat document before trying to copy the text selected in to to the clipboard.
Create a new Delphi VCL project.
In D7's IDE go to Projects | Import Type Library, and in the Import Type Library pop-up, scroll down until you see something like "Acrobat (Version 1.0) in the list of files, and
"TAcroApp, TAcroAVDoc..." in the Class names box. That is the one you need to import. Click the Create unit button/
In the project's main form file
a. Make sure it USES the Acrobat_Tlb.Pas unit from step 2. You may need to add the path to wherever you saved Acrobat_Tlb.Pas to the SearchPath of your project.
b. Drop a TButton on the form, name it btnGetSel. Drop a TEdit on the form and name it edSelection
Edit the source code of your main form unit as shown below.
Set a debugger breakpoint on Acrobat.MenuItemExecute('File->Copy'); Do not set a breakpoint within the GetSelection procedure as this is likely to defeat the call to BringToFront in it.
Close any running instance of Adobe Acrobat. Check in Task Manager that there are no hidden instances of it running. The reason for these step is to make sure that when you run your app, it "talks" to the instance of Acrobat that it starts, not another one.
Compile and run your app. Once the app and Acrobat are open, switch to Acrobat, select some text, switch back to your app and click the btnGetSel button.
Code:
uses ... Acrobat_Tlb, ClipBrd;
TDefaultForm = class(TForm)
[...]
private
FFileName: String;
procedure GetSelection;
public
Acrobat : CAcroApp;
PDDoc : CAcroPDDoc;
AVDoc : CAcroAVDoc;
end;
[...]
procedure TDefaultForm.FormCreate(Sender: TObject);
begin
// Adjust the following path to suit your system. My application is
// in a folder on drive D:
FFileName := ExtractfilePath(Application.ExeName) + 'Printed.Pdf';
Acrobat := CoAcroApp.Create;
Acrobat.Show;
AVDoc := CoAcroAVDoc.Create;
AVDoc.Open(FileName, FileName); // := Acrobat.GetAVDoc(0) as CAcroAVDoc; //
PDDoc := AVDoc.GetPDDoc as CAcroPDDoc;
end;
procedure TDefaultForm.btnGetSelClick(Sender: TObject);
begin
GetSelection;
end;
procedure TDefaultForm.GetSelection;
begin
// call this once some text is selected in Acrobat
edSelection.Text := '';
if AVDoc.BringToFront then // NB: This call to BringToFront is essential for the call to MenuItemExecute('Copy') to succeed
Caption := 'BringToFront ok'
else
Caption := 'BringToFront failed';
if Acrobat.MenuItemExecute('Copy') then
Caption := 'Copy ok'
else
Caption := 'BringToFront failed';
Sleep(100); // Normally I would avoid ever calling Sleep in a Delphi
// App's main thread. In this case, it is to allow Acrobat time to transfer the selected
// text to the clipboard before we attempt to read it.
try
edSelection.Text := Clipboard.AsText;
except
end;
end;

Delphi open file with standard windows GUI window

I want to be able to open files in Delphi with a Windows GUI where you can scroll through the folders etc. I have already done this with Matlab with a single function that (after selecting the file) returns a string of the path. You could event specify which extension the be shown. Is this kind of function available in delphi and how should I use it.
you can use the TOpenDialog component which is part of the Dialogs unit. you can create in runtime or drop this component from the Dialogs palette.
if you drop the component to your form you can use in this way
OpenDialog1.Filter := 'Only Text files (*.txt)|*.txt';
if OpenDialog1.Execute then
//do you stuff here
or if you create the component in runtime
Var
OpenDialog1 : TOpenDialog;
begin
OpenDialog1:=TOpenDialog.Create(nil);
try
OpenDialog1.Filter := 'Only Text files (*.txt)|*.txt';
if OpenDialog1.Execute then
ShowMessage('Selected File '+OpenDialog1.FileName);
finally
OpenDialog1.Free;
end;
end;
That's available via TOpenDialog which encapsulates the relevant Windows functionality.
Drop a TOpenDialog component on your form. Then you can call OpenDialog1.Execute to show the Windows dialog.

A way to redirect keypresses from TWebBrowser to the ParentForm

First question. Help format it, if needed, please.
Context
I have a TWebBrowser in the main form that is used to behave like it was a printer.
So I load some HTML text in it as user do some commands in the real printer...
I want the user to be able to click and select text from the WebBrowser.
Problem
When the user clicks in the WebBrowser some of the shortcuts registered from the actions don't work anymore. For example, there is an action with shortcut F7. If the user clicks in the WebBrowser and presses F7, it does not invoke my shortcut.
I know that this is by design of the WebBrowser.
So, I thought: I want to send every key combination back to the form.
The question is: How?
If it was another control, I could use a perform(WM_KeyDown,...) in the OnKeyDown event.
Alternatives or suggestions would be appreciated too. I am very tired these past 2 days, so I could be missing something.
Derivate TWebBrowser with an implementation of IDocHostUIHandler or use the famous EmbeddedWB
Implement the interface with an event OnTranslateAccelerator called in TranslateAccelerator
Set the event on your brwoser instance
Detect your key(s) like this:
function TBrowserPageIE.DoTranslateAccelerator(const lpMsg: PMSG; const pguidCmdGroup: PGUID; const nCmdID: DWORD): HRESULT;
begin
result := S_FALSE;
if lpMsg.message = WM_KEYDOWN then begin
if lpMsg.wParam = VK_F7 then begin
// do something here...
result := S_OK;
end;
end;
end;
An option that I tested and worked is to trap the key_code on HTML/javascript and then send that to the form using changing the document title. I will let it here hoping that help someone...
You will need add the javascript to trap the keys in the Header of the HTML page like this:
<script = ''javascript''>
function keypresed() {
var tecla=window.event.keyCode;
document.title = "Command"+tecla;
event.keyCode=0;
event.returnValue=false;
}
document.onkeydown=keypresed;
</script>
Then in Webbrowser you use the onTitleChangeEvent to use the key.
var
s:string;
begin
if Copy(Text,0,7) = 'Command' then
begin
//get the key...
s:= Copy(Text,8,Length(Text));
// if before the webbrowser get the focus edit1 was the focused control, you will need remove that focus first...
dummy.setfocus;
edit1.setfocus;
//perform keydown
keybd_event(StrToInt(s), 1,0,0)
end;
end;
Well, this can be used to perform any other custom command. :)

Resources