Browse For File |
Author: |
Message: |
Volv
Skinning Contest Winner
Posts: 1233 Reputation: 31
35 / /
Joined: Oct 2004
|
O.P. Browse For File
Heya,
How would I go about making a 'Browse for file' dialog which allows the user to navigate to a file of their choice on their PC and returns the path to the file.
Thanks in advance
- Volv
|
|
06-24-2006 02:04 AM |
|
|
matty
Scripting Guru
Posts: 8336 Reputation: 109
39 / /
Joined: Dec 2002
Status: Away
|
RE: Browse For File
This is taken from Choli's Translator, give all credits too him.
js code: var OFN_ENABLESIZING = 0x800000;
var OFN_EXPLORER = 0x80000;
var OFN_FILEMUSTEXIST = 0x1000;
var OFN_HIDEREADONLY = 0x4;
var OFN_LONGNAMES = 0x200000;
var OFN_PATHMUSTEXIST = 0x800;
function OnEvent_Initialize(MessengerStart)
{
var OpenFileName = Interop.Allocate(88);
var filter;
var s_filter;
var file;
var s_file;
var title;
var s_title;
var ret;
var tdata;
with (OpenFileName) {
WriteDWORD(0, Size); // lStructSize
WriteDWORD(4, 0); // hwndOwner
WriteDWORD(8, 0); // hInstance
filter = "Translation files (Lng_*.ini)|Lng_*.ini|All files (*.*)|*.*||";
s_filter = Interop.Allocate(2 * (filter.length + 1));
WriteMultiStringW(s_filter, filter);
WriteDWORD(12, s_filter.DataPtr); // lpstrFilter
WriteDWORD(16, 0); // lpstrCustomFilter
WriteDWORD(20, 0); // nMaxCustomFilter
WriteDWORD(24, 1); // nFilterIndex
file = "Lng_*.ini" + Space(256);
s_file = Interop.Allocate(2 * (file.length + 1));
s_file.WriteString(0, file);
WriteDWORD(28, s_file.DataPtr); // lpstrFile
WriteDWORD(32, file.length); // nMaxFile
WriteDWORD(36, 0); // lpstrFileTitle
WriteDWORD(40, 0); // nMaxFileTitle
WriteDWORD(44, 0); // lpstrInitialDir
title = "Select original file";
s_title = Interop.Allocate(2 * (title.length + 1));
s_title.WriteString(0, title);
WriteDWORD(48, s_title.DataPtr); // lpstrTitle
WriteDWORD(52, OFN_ENABLESIZING | OFN_EXPLORER | OFN_HIDEREADONLY | OFN_LONGNAMES | OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST); // flags
WriteWORD(56, 0); // nFileOffset
WriteWORD(58, 0); // nFileExtension
WriteDWORD(60, 0); // lpstrDefExt
WriteDWORD(64, 0); // lCustData
WriteDWORD(68, 0); // lpfnHook
WriteDWORD(72, 0); // lpTemplateName
WriteDWORD(76, 0); // pvReserved
WriteDWORD(80, 0); // dwReserved
WriteDWORD(84, 0); // FlagsEx
} with OpenFileName
ret = Interop.Call("comdlg32.dll", "GetOpenFileNameW", OpenFileName);
if (ret == 0) {
return;
}
}
function WriteMultiStringW (datablock, string) {
var pos = 0;
datablock.WriteString(0, string);
pos = string.indexOf("|", pos);
while (pos != -1) {
datablock.WriteWORD(2 * pos, 0);
pos = string.indexOf("|", pos + 1);
}
}
function Space (number) {
var i;
var s = "";
for (i = 0; i < number; i++) {
s += " ";
}
return s;
}
This post was edited on 04-11-2011 at 12:28 PM by matty.
|
|
06-24-2006 02:19 AM |
|
|
Volv
Skinning Contest Winner
Posts: 1233 Reputation: 31
35 / /
Joined: Oct 2004
|
O.P. RE: Browse For File
Thanks a lot Matty & Choli, works great
|
|
06-24-2006 05:16 AM |
|
|
Choli
Elite Member
Choli
Posts: 4714 Reputation: 42
43 / /
Joined: Jan 2003
|
RE: Browse For File
yeah, that's it
you can change the filter and put, for example:
"Text files|*.txt|All files|*.*||"
For more help, read this: GetOpenFileName and OPENFILENAME.
|
|
06-24-2006 09:32 AM |
|
|
CookieRevised
Elite Member
Posts: 15517 Reputation: 173
– / /
Joined: Jul 2003
Status: Away
|
RE: Browse For File
Since this recently came up again, I'll post my own variation/fix of the above code here.
Note the word 'variation', as this kind of code is very very generic and can be found all over the net already in various forms. It is simply a matter of filling a structure with the needed stuff and calling the proper Windows API, thus not realy something special.
Some small notes first:
- Choli's variation has some things in it which are specific for his Translator tool and should not be copied as-is. Nevertheless I have seen his code cropping up in various places and scripts without those needed changes. Aka, those copies will not always function properly.
- Matti has made a better variation in the post: Matti's reply to CommonDialog help
However, that too still contains bugs and one or two things which could be done better.
So here is my variation, based upon Matti's variation of Choli's snippet. It is of course different, but not that different to be completely 'new'. As such, I've also tried to keep most of the structure and variable names used in the previous variations. However, due to the change of order of the parameters and the change in format of the returned value of the function, you can't simply replace your old BrowseForFile function with this one though. You will need to change the order of the parameters in your code and change how you handle the return value in your code.
The code itself:
js code: /* FUNCION: BrowseForFile (CookieRevised variation)
Original code by Choli from the Messenger Plus! Translator script
Modified by Matti to be more versatile (eg: including "Save as" dialog function)
Modified by CookieRevised to optimize it a bit and fix some bugs
Description:
Opens a "Browse for file" dialog with the given settings
Parameters (all parameters are now optional):
save (boolean) / true is "Save as" dialog, false is "Open" dialog
title (string) / Dialog title
dir (string) / Start directory
file (string) / File to display as default
filter (string) / Filter message. Example: "JavaScript Files (*.JS)|*.JS|Text Files (*.TXT;*.LOG)|*.TXT;*.LOG|"
ext (string) / Default extension to append to the filename when no extension is specified
multiselect (boolean) / true to allow multiple file selection in an "Open" dialog
hwnd_owner (number) / Handle of the owner window, used to copy window icon etc.
Return value:
>>>A (string) array of full filepath names.<<<
Note that this will also be an array if just 1 file was selected (in that case an array with just 1 element).
If no files were selected or the dialog was canceled, the array will be empty.
I think this is more consistant and versatile across multiple implementations of the function.
*/
>>>function BrowseForFile(save, title, dir, file, filter, ext, multiselect, hwnd_owner) {<<<
// see http://msdn.microsoft.com/en-us/library/ms646839(v=vs.85).aspx
var OFN_OVERWRITEPROMPT = 0x2;
var OFN_HIDEREADONLY = 0x4;
>>>var OFN_NOCHANGEDIR = 0x8;<<<
var OFN_ALLOWMULTISELECT = 0x200;
var OFN_PATHMUSTEXIST = 0x800;
var OFN_FILEMUSTEXIST = 0x1000;
>>>var OFN_NONETWORKBUTTON = 0x20000;<<<
var OFN_EXPLORER = 0x80000;
var OFN_LONGNAMES = 0x200000;
var OFN_ENABLESIZING = 0x800000;
>>>var OFN_DONTADDTORECENT = 0x2000000;<<<
var OpenFileName = Interop.Allocate(88);
with (OpenFileName) {
save = save == true;
>>>multiselect = (multiselect == true) && !save;<<<
>>>hwnd_owner = hwnd_owner ? 1 * hwnd_owner : 0;<<<
>>>if (typeof filter !== "string") filter = "All Files (*.*)|*.*|";<<<
>>>var s_filter = Interop.Allocate(2 * (filter.length + 1) + 4);<<<
>>>s_filter.WriteBSTR(0, filter.replace(/\|/g, '\0'));<<<
if (typeof file !== "string") file = "";
>>>var s_file = Interop.Allocate(2 * (Math.max(file.length, 16383) + 1));<<<
s_file.WriteString(0, file);
if (typeof dir === "string") {
var s_dir = Interop.Allocate(2 * (dir.length + 1));
s_dir.WriteString(0, dir);
WriteDWORD(44, s_dir.DataPtr); // lpstrInitialDir
}
if (typeof title === "string") {
var s_title = Interop.Allocate(2 * (title.length + 1));
s_title.WriteString(0, title);
WriteDWORD(48, s_title.DataPtr); // lpstrTitle
}
if (typeof ext === "string") {
var s_ext = Interop.Allocate(2 * (ext.length + 1));
s_ext.WriteString(0, ext);
WriteDWORD(60, s_ext.DataPtr); // lpstrDefExt
}
WriteDWORD(0, Size); // lStructSize
WriteDWORD(4, hwnd_owner); // hwndOwner
>>>WriteDWORD(12, s_filter.DataPtr + 4); // lpstrFilter<<<
WriteDWORD(24, 1); // nFilterIndex
WriteDWORD(28, s_file.DataPtr); // lpstrFile
>>>WriteDWORD(32, s_file.Size / 2); // nMaxFile<<<
>>>WriteDWORD(52, OFN_DONTADDTORECENT | OFN_ENABLESIZING | OFN_EXPLORER | OFN_HIDEREADONLY | OFN_LONGNAMES | OFN_PATHMUSTEXIST | (save ? OFN_OVERWRITEPROMPT : OFN_FILEMUSTEXIST) | (multiselect ? OFN_ALLOWMULTISELECT : 0)); // Flags<<<
}
var result = Interop.Call("COMDLG32.DLL", (save ? "GetSaveFileNameW" : "GetOpenFileNameW"), OpenFileName);
if (result) {
var p_path = s_file.ReadString(0);
var p_files = new Array();
if (multiselect) {
var p_file = "";
var pos = 2 * (p_path.length + 1);
while (true) {
p_file = s_file.ReadString(pos);
if (p_file.length > 0) {
>>>p_files.push(p_path + (p_path.slice(-1) !== "\\" ? "\\" : "") + p_file);<<<
pos += 2 * (p_file.length + 1);
} else break;
}
}
>>>return p_files.length ? p_files : [p_path];<<<
} else {
>>>return [];<<<
}
}
Major changes highlighted
Examples:
js code: // Save File Dialog
// Opens up in C:\, file filters are 'Text Files' (*.txt and *.log) and 'All Files' (*.*).
var aFile = BrowseForFile(true, "Save file as", "C:\\", "test.txt", "Text Files (*.TXT;*.LOG)|*.TXT;*.LOG|All Files (*.*)|*.*|");
if (aFile.length) {
// User specified a valid save location. Do something useful here.
Debug.Trace(aFile[0])
} else {
// User didn't specify a file to save to or pressed Cancel.
}
js code: // Open File Dialog
// Opens up in C:\Windows, multiple selections are allowed, file filters are 'PNG Files' (*.png) and 'All Files' (*.*).
var aFile = BrowseForFile(false, "Select PNG", "C:\\Windows", null, "PNG Files (*.PNG)|*.PNG|All Files (*.*)|*.*|", null, true);
if (aFile.length) {
// User selected some files. Do something useful here.
for (var i in aFile) {
Debug.Trace(aFile[i])
}
} else {
// User didn't selected any files to open or pressed Cancel.
}
Stuff changed compared to Matti's and Choli's (also see highlights in the code):
- Made order of parameters a bit more logical, also in regards to optional paramater usage.
- Changed the format of the returned value of the function. Now it always return an array with the full filepath names of the selected file(s). I think this is much more versatile and consistant in regards to multiple file selections, looping, etc. It is also very easy now to check if a file is returned or not, no matter the amount of files (just check the length of the array being 0 or not).
thus beware, you can't simply replace your old BrowseForFile function with this one. See examples above.
- Fixed the behaviour of the parameters so that they are now all optional. They weren't before in the sense that the code would fail if the file or dir parameter were not specified (was a big bug in Matti's variation). This was solved by removing the Datablock.Size = 0 lines at the end of the function. They are not needed as datablocks will be removed automatically by the garbage collector of Plus! when the function ends anyways.
- Made hwnd_owner parameter also behave properly when not specified (was a potential bug). All parameters are now truely optional; you don't need to specify them.
- Added the optional multiselect parameter (boolean).
- Added some extra constants.
- Replaced Choli's WriteMultiStringW function with Datablock::WriteBSTR() function and thus highly optimized the parsing of the filter parameter.
- Removed Choli's Space() function.
- Removed all unneeded Datablock::WriteDWORD(x, 0); lines. They are not needed because a Datablock will always be zero-filled anyways when you allocate it.
- Added proper initial filepath name length check before writing to the filepath buffer. (was a potential bug)
- Increased the filepath buffer drastically. 256 like in Choli's code is way too small. Even 1024 characters is small-ish, especially considering multiple file selections. There is no reason not to have a massive buffer for this.
- Fixed nMaxFile calculation (size should include the null-character).
- Added OFN_DONTADDTORECENT flag. This prevents files from being added to Windows' Recent Documents list, which is otherwise highly annoying in many cases. If you explicitly have a reason to add the opened/saved files in that list, simply remove that flag again in the code (on the line starting with WriteDWORD(52, ...).
- Maybe some other tid bits not worth to mention.
This post was edited on 04-11-2011 at 08:43 PM by CookieRevised.
.-= A 'frrrrrrrituurrr' for Wacky =-.
|
|
04-11-2011 07:46 AM |
|
|
Choli
Elite Member
Choli
Posts: 4714 Reputation: 42
43 / /
Joined: Jan 2003
|
RE: Browse For File
Cookie... this thread is 5 years old...
quote: Originally posted by CookieRevised
256 like in Choli's code is way too small.
Sure, but if I remember well, I think that old versions of Windows (maybe 98, or 2000) don't allow more than that, do they?. Or something like that
|
|
04-11-2011 06:41 PM |
|
|
CookieRevised
Elite Member
Posts: 15517 Reputation: 173
– / /
Joined: Jul 2003
Status: Away
|
RE: Browse For File
quote: Originally posted by Choli
Cookie... this thread is 5 years old...
Yup, the '90s are coming back
Nah, but joking aside, as I said, the request to have a 'BrowseForFile' dialog pops up now and then and will continue to pop up in the future no doubt. And apparently people keep on referring to this thread and/or use code from this thread (which is main reason why I posted this here and not in that other thread). I found many links pointing to Matty's post here, even in very recent posts. So, nothing wrong with (finaly) updating it a bit in that case imho.
quote: Originally posted by Choli
quote: Originally posted by CookieRevised
256 like in Choli's code is way too small.
Sure, but if I remember well, I think that old versions of Windows (maybe 98, or 2000) don't allow more than that, do they?. Or something like that
Not exactly; +-256 (or rather 260, which is MAX_PATH) is the absolute required minimum (eg: when you are sure you don't return multiple selections and with short file names). It's the minimum required size to hold exactly one complete path and file name (in short name notation.... because in long name notation the name itself can even be MAX_PATH characters). So, even back in the old days, the buffer could be (and better should be) much much bigger to hold both the returned path and filename.
For your Translator tool this was never a real problem. But since people copy that code without changing anything in it and using it as-is, problems do arise though.
This post was edited on 04-11-2011 at 08:51 PM by CookieRevised.
.-= A 'frrrrrrrituurrr' for Wacky =-.
|
|
04-11-2011 08:13 PM |
|
|
Amec
Junior Member
Posts: 19
32 / /
Joined: Sep 2008
|
|
04-14-2011 06:54 PM |
|
|
matty
Scripting Guru
Posts: 8336 Reputation: 109
39 / /
Joined: Dec 2002
Status: Away
|
|
04-14-2011 07:06 PM |
|
|
CookieRevised
Elite Member
Posts: 15517 Reputation: 173
– / /
Joined: Jul 2003
Status: Away
|
RE: Browse For File
quote: Originally posted by Amec
quote: Originally posted by CookieRevised
js code: with (OpenFileName) {
Every time you use a with statement, a teddy bear cries.
http://www.yuiblog.com/blog/2006/04/11/with-statement-considered-harmful/
Yeah, well, you're 5 years to late....
Although I agree for 50%, it is mostly also a matter of schematics. There is essentially nothing wrong with using with() if you know what the code will do. In the code snippet it is also used for the exact purpose that with() statement was invented and there are enough readDWORD() statements to justify the use of with() imo.
May I also quote that very same page you linked to: quote: If you can’t read a program and be confident that you know what it is going to do, you can’t have confidence that it is going to work correctly. For this reason, the with statement should be avoided.
I can read the program and I am 100% confident in knowing what it is going to do and that it is going to work correctly.
Lastly, it is not my code: quote: Originally posted by CookieRevised
So here is my variation, based upon Matti's variation of Choli's snippet. It is of course different, but not that different to be completely 'new'. As such, I've also tried to keep most of the structure and variable names used in the previous variations.
Note that there are also other parts in the code which could be made shorter/smarter/cleaner/etc. But that was not the purpose of showing or updating that code.
This post was edited on 04-14-2011 at 07:53 PM by CookieRevised.
.-= A 'frrrrrrrituurrr' for Wacky =-.
|
|
04-14-2011 07:40 PM |
|
|
Pages: (2):
« First
[ 1 ]
2
»
Last »
|
|
|