Kewl Tip: Browse for Folder dialog in LotusScript
Category Technical
Bookmark :
How many times have you developed a robust Notes-based application, only to realize that what you really need to do at some point is to prompt the user for a folder/directory location - not a file, but a folder. You try using the NotesUIWorkspace,OpenFileDialog, you even try NotesUIWorkspace.Prompt - local browse option. But everything forces the user to choose a file. You wind up with some kludge where you are forced to ask the user to choose a file, and then you hack off the file name to get the directory name.
Well, I'm here to take you away from all that (at least all you Win32 users). How would you like to produce a nice folder dialog, like below?

Luckily I have my handy-dandy AllAPI.net API Guide to help me. It was a bit cryptic, and I needed to add a "Make new folder" button, so I did some more searching and found a few sites - this one seemed to be the most helpful.
Here is the code - I have commented it fairly well, and I will point out some highlights after the code itself.
Now I am not going to show all the declarations and such - you can download the LSS from here and look at it yourself.
The function code
There are a couple of main points I want to bring out in this code.
The GetForegroundWindow call is a Win32 C API call that returns a handle (long) to the program window that is in the foreground - in this case the Notes client. The handle is needed to pass to the SHBrowseForFolder function as a part of the BrowseInfo data type, so that the dialog has a "parent" window.
I wrote the getDirDialog function so that you can pass in your own title/instructions for the dialog. If you send an empty string in, then it will assign the default title of "Choose a folder for your attachment(s)".
Notice that when the stitle is passed into the lpszTitle parameter of the BrowseInfo data type it is passed via the lstrcat function. lstrcat is a C API function that takes two strings, munges them together, and then returns a memory pointer for where resulting string is stored. The lpszTitle parameter wants a memory pointer to the string, not the string itself, so this is the easiest way to get that.
The "ulflags" parameter in the BrowseInfo data type is a bit flag, so you use bitmasks to "flip" the bits you want. There are a ton of constants giving you a variety of parameters in the (Declarations); I am simply flipping the minimal bits to give you the dialog box pictured above.
The SHBrowseForFolder(bi) call returns a memory pointer to a complex data type. The If statement below the call parses the contents of that memory pointer using the SHGetPathFromIDList call, where it places the returned path in the sPath variable (which is a string variable set to MAX_PATH size). Then the memory is freed using the CoTaskMemFree call, and the null-terminated string in sPath is parsed to get the returned path in a LotusScript-digestable format.
Conclusion
I hope you find this call as useful as I have. Now you can create even more robust applications providing Win32 users the dialog boxes they have come to know in their OS, which helps make Notes more acceptable as a "standard" application.
Enjoy!
Rock
**Fun is a good thing but only when it spoils nothing better. - George Santayana
The function code
Function getDirDialog(stitle As String) As String REM unless otherwise noted, the functoin calls below came from Declarations (C API) Dim iNull As Integer, lpIDList As Long, lResult As Long Dim sPath As String Dim hwd As Long Dim bi As BrowseInfo ' user type declared in declarations On Error Goto errHandler hwd = GetForegroundWindow() ' returns the handle to the currently selected program window REM Set the owner/parent window bi.hWndOwner = hwd REM lstrcat appends the two strings and returns the memory address; ' even though we're using one string, we use lstrcat to get a pointer ' to a memory address, instead of using the string directly ' NOTE: You can change the text here if you want If stitle = "" Then stitle = "Choose a folder for your attachment(s)" bi.lpszTitle = lstrcat(stitle, "") REM this next parameter is a bit flag; we're flipping bits with the constants REM this flag ensures a return only if the user selected a directory bi.ulFlags = BIF_RETURNONLYFSDIRS REM this flag lets us have a "Make New Folder" button, resize the dlg, etc. bi.ulFlags = bi.ulFlags Or BIF_NEWDIALOGSTYLE REM if you DON'T want a "Make New Folder" button, you can uncomment the next flag ' bi.ulFlags = bi.ulFlags Or BIF_NONEWFOLDERBUTTON REM the rest of this simply preps all of the parameters and loads the dialog itself lpIDList = SHBrowseForFolder(bi) If lpIDList Then sPath = String$(MAX_PATH, 0) SHGetPathFromIDList lpIDList, sPath 'Get the path from the IDList CoTaskMemFree lpIDList 'free the block of memory iNull = Instr(sPath, Chr(0)) If iNull Then sPath = Left$(sPath, iNull - 1) End If End If REM return the path getDirDialog = spath getOut: Exit Function errHandler: On Error Goto 0 Error Err, Error$ & "(" & Err & ") [in " & Lsi_info(2) & ", line: " & Erl & "]" Resume getOut End Function
There are a couple of main points I want to bring out in this code.
The GetForegroundWindow call is a Win32 C API call that returns a handle (long) to the program window that is in the foreground - in this case the Notes client. The handle is needed to pass to the SHBrowseForFolder function as a part of the BrowseInfo data type, so that the dialog has a "parent" window.
I wrote the getDirDialog function so that you can pass in your own title/instructions for the dialog. If you send an empty string in, then it will assign the default title of "Choose a folder for your attachment(s)".
Notice that when the stitle is passed into the lpszTitle parameter of the BrowseInfo data type it is passed via the lstrcat function. lstrcat is a C API function that takes two strings, munges them together, and then returns a memory pointer for where resulting string is stored. The lpszTitle parameter wants a memory pointer to the string, not the string itself, so this is the easiest way to get that.
The "ulflags" parameter in the BrowseInfo data type is a bit flag, so you use bitmasks to "flip" the bits you want. There are a ton of constants giving you a variety of parameters in the (Declarations); I am simply flipping the minimal bits to give you the dialog box pictured above.
The SHBrowseForFolder(bi) call returns a memory pointer to a complex data type. The If statement below the call parses the contents of that memory pointer using the SHGetPathFromIDList call, where it places the returned path in the sPath variable (which is a string variable set to MAX_PATH size). Then the memory is freed using the CoTaskMemFree call, and the null-terminated string in sPath is parsed to get the returned path in a LotusScript-digestable format.
Conclusion
I hope you find this call as useful as I have. Now you can create even more robust applications providing Win32 users the dialog boxes they have come to know in their OS, which helps make Notes more acceptable as a "standard" application.
Enjoy!
Rock
**Fun is a good thing but only when it spoils nothing better. - George Santayana







Blog Roll









Comments
Posted by Newbs At 12:00:01 PM On 12/27/2005 | - Website - |
Excellent heads-up, Manfred. Thanks. Also, the best part of this solution is that it does work for the Mac client (well, I assume it does).
Thanks again!
Rock
Posted by Rock At 09:36:15 AM On 12/28/2005 | - Website - |
>>Excellent heads-up, Manfred.<<
Thank you very much.
@Jens:
Hallo Jens, hoffe Dir gehts gut! (Hi Jens, hope you are well!)
Thank you for pointing that out. Sure, you are right, there is a "New Folder button" in this dialog. I guess I'am getting old and need some eyeglasses soon.
Manfred
Posted by Manfred Dillmann At 04:49:03 AM On 12/29/2005 | - Website - |
See, it is very hard to invent something new - but I think I've done that. More later...
Rock
Posted by Rock At 06:46:40 PM On 12/27/2005 | - Website - |
Rock
Posted by Rock At 12:13:26 PM On 12/27/2005 | - Website - |
I have an application that searches the whole database then the result is put to a folder. But when the result gets bigger (1000+ documents) it starts to slowdown, what I mean is, the waiting time to display the documents from the folder took around 3-4 minutes. That kind of long waiting erritates my users who are lawyers.
Here's my code:
Dim db As notesdatabase
Dim docSearch As New notesdocument( s.currentdatabase )
Dim coll As notesdocumentcollection
Dim dateTime As New notesdatetime( Now )
On Error Goto errorHandling
Set db = s.currentdatabase
Call dateTime.AdjustDay(-10000)
' Display the search dialog box
If Not uiwork.dialogbox("DialogSearch", True, True, False, False, False, False, msgTitleSearch$, docSearch ) Then Exit Sub
' create search string
searchString$ = GetSearchCriteria( docSearch )
strMaxDocs = "100" ' max doc's returned from search
' Search
If searchString$ <> "" Then Set coll = db.Search( searchString$, dateTime, 0 )
' countFound
If coll Is Nothing Then countFound% = 0 Else countFound% = coll.count
' Exit if nothing found
If countFound% = 0 Then
'Print searchString$ ' debug
Msgbox msgNoDocumentsFound$, MB_ICONINFO, msgTitleMain$
Exit Sub
End If
Call coll.putallinfolder("Search")
uiwork.viewrefresh ' in case search initiated from folder
If IDOK = Msgbox ( msgFoundDocs$ & Cstr(countFound%) & " documents" & Chr(10) , MB_ICONINFORMATION + MB_OK, db.Title ) Then
End If
Exit Sub
errorHandling:
Print "Search.SearchFullTextAndFields reports:"
Print Cstr(Err) & " " & Error
Resume Next
P.S.
I hope to hear from you soon. Thank you in advance.
Posted by Mon Imperial At 02:44:13 AM On 02/06/2007 | - Website - |
Thanks for asking, yes I am well. Unfortunately, I did not get a session at LS06, but I could arrange to go nevertheless. I could bring you some glasses back home ...
Posted by Jens At 06:34:08 AM On 12/29/2005 | - Website - |
It has a function called GetDirDlg() that does the directory chooser.
Rock, I think there is an issue with the lstrcat function in your implementation, my titles do not show up correctly. In the Bookmark version they declare lpszTitle as string and that seems to work.
The constant BIF_NEWDIALOGSTYLE is missing from the bookmark example, but, it works fine when added.
Posted by Dwight Wilbanks At 07:44:45 PM On 02/20/2006 | - Website - |
I gotta give it to Rock here, both the implementation of the new folder button & the exposure/documenting of the other constants.
The version of directory browser that I have in my library was originally posted 6/2000 from here http://www-10.lotus.com/ldd/46dom.nsf/0/25136f17661d91d48525690700215e72?OpenDocument
I've upgraded my library code with rockys lss
Posted by Dwight Wilbanks At 06:34:34 PM On 12/29/2005 | - Website - |
stringArray = notesUIWorkspace.SaveFileDialog( directoriesOnly , [title$] , [filters$] , [initialDirectory$] , [initialFile$] )
a try. This method has the boolean parameter "directoriesOnly" (no files are displayed) and it returns the selected folder - that's what you want.
Sample:
Dim ws As New NotesUIWorkspace
Dim FolderChoice As Variant
FolderChoice = ws.SaveFileDialog( True )
Msgbox "Selected folder: " + FolderChoice(0)
OK, there is no "Make new folder" button and in this dialog and you have to open (instead of only select) the folder you desire - but apart from these limitations, it works.
Manfred
Posted by Manfred Dillmann At 07:29:18 AM On 12/28/2005 | - Website - |
Posted by Jens At 06:12:21 PM On 12/28/2005 | - Website - |
http://benpoole.com/80256B44004A7C14/articles/lotusscriptversionlocalbrowse
Posted by Ben Poole At 06:18:46 PM On 12/27/2005 | - Website - |