File requester with options (not a module tweak)

BlitzMax Forums/BlitzMax Module Tweaks/File requester with options (not a module tweak)

JoshK(Posted 2008) [#1]
This code can be imported into your program without screwing around with BRL.system. It could easily be modified to select multiple files. The optional hook function lets you add an options or help dialog to control how files are processed.

-Added optional owner gadget to make the file dialog a child of (requires MaxGUI, but can be commented out).

Strict

Import brl.system
Import brl.standardio
Import pub.win32
Import maxgui.maxgui

Private

Extern "win32"
	Function GetOpenFileNameA:Int(of:Byte Ptr)
	Function GetSaveFileNameA:Int(of:Byte Ptr)
	Function CommDlgExtendedError:Int()
	Function GetDlgItem(hDlg:Int,nIDDlgItem:Int)
	Function GetParent:Int(hwnd:Int)
	Function SetWindowTextA(hwnd:Int,text$z)
EndExtern

Public



'--------------------------------------------------
'Examples
'--------------------------------------------------
Rem
'Print RequestFile("Open Sesame","All Files:*;My File Format (*.mff):mff;Windows Bitmap (*.bmp):bmp",0,"c:/")
Print RequestFileEx("Open Sesame","All Files:*;My File Format (*.mff):mff;Windows Bitmap (*.bmp):bmp",1,"c:/",Null,Hook,"&Options")

Function Hook()
	Notify "Your stuff goes here."
EndFunction
EndRem
'--------------------------------------------------
'
'--------------------------------------------------


Rem
'=======================================================================
'Original unmolested version used for testing
'=======================================================================
Function RequestFile$(text$,exts$="",save_flag=False,path$="")
	Local result
	Local of:OPENFILENAME
	Local file$,dir$

	'Fix path
	path=path.Replace( "/","\" )
	Local i=path.FindLast( "\" )
	If i<>-1
		dir=path[..i]
		file=path[i+1..]
	Else
		file=path
	EndIf
	
	'Calculate extension string
	Local ext$,defext,p,q
	p=path.Find(".")
	If (p>-1)
		ext=","+path[p+1..].toLower()+","
		Local exs$=exts.toLower()
		exs=exs.Replace(":",":,")
		exs=exs.Replace(";",",;")
		p=exs.find(ext)
		If p>-1
			Local q=-1
			defext=1
			While True
				q=exs.find(";",q+1)
				If q>p Exit
				If q=-1 defext=0;Exit
				defext:+1
			Wend
		EndIf
	EndIf
	If exts
		If exts.Find(":")=-1
			exts="Files~0*."+exts
		Else
			exts=exts.Replace(":","~0*.")
		EndIf
		exts=exts.Replace(";","~0")
		exts=exts.Replace(",",";*.")+"~0"
	EndIf
	
	of=New OPENFILENAME
	of.lpstrTitle=text.tocstring()
	of.lpstrInitialDir=dir.tocstring()
	of.lpstrFilter=exts.tocstring()

	'Flags
	of.Flags=OFN_HIDEREADONLY|OFN_NOCHANGEDIR
	If save_flag
		of.lpstrDefExt=of.lpstrFilter
		of.Flags:|OFN_OVERWRITEPROMPT
	Else
		of.Flags:|OFN_FILEMUSTEXIST
	EndIf
	
	'Return string array
	Local lpstrFile:Byte[4096]
	of.lpstrFile=lpstrFile
	of.nMaxFile=4095

	Local cs:Byte Ptr
	cs=file.tocstring()
	MemCopy lpstrFile,cs,file.length
	MemFree cs
	
	If save_flag
		result=GetSaveFileNameA(of)
	Else
		result=GetOpenFileNameA(of)
	EndIf
	
	MemFree of.lpstrTitle
	MemFree of.lpstrInitialDir
	MemFree of.lpstrFilter
	
	If result Return String.FromCString(lpstrFile)
EndFunction
'=======================================================================
'
'=======================================================================
EndRem

'=======================================================================
'Whored out version
'=======================================================================
Function RequestFileEx$(text$,exts$="",save_flag=False,path$="",owner:TGadget=Null,hook()=Null,helpbuttontext$="Help")
	Local result
	Local of:OPENFILENAME
	Local file$,dir$

	'Fix path
	path=path.Replace( "/","\" )
	Local i=path.FindLast( "\" )
	If i<>-1
		dir=path[..i]
		file=path[i+1..]
	Else
		file=path
	EndIf
	
	'Calculate extension string
	Local ext$,defext,p,q
	p=path.Find(".")
	If (p>-1)
		ext=","+path[p+1..].toLower()+","
		Local exs$=exts.toLower()
		exs=exs.Replace(":",":,")
		exs=exs.Replace(";",",;")
		p=exs.find(ext)
		If p>-1
			Local q=-1
			defext=1
			While True
				q=exs.find(";",q+1)
				If q>p Exit
				If q=-1 defext=0;Exit
				defext:+1
			Wend
		EndIf
	EndIf
	If exts
		If exts.Find(":")=-1
			exts="Files~0*."+exts
		Else
			exts=exts.Replace(":","~0*.")
		EndIf
		exts=exts.Replace(";","~0")
		exts=exts.Replace(",",";*.")+"~0"
	EndIf
	
	of=New OPENFILENAME
	of.lpstrTitle=text.tocstring()
	of.lpstrInitialDir=dir.tocstring()
	of.lpstrFilter=exts.tocstring()
	
	If owner of.hwndowner=QueryGadget(owner,QUERY_HWND)
	
	'Flags
	of.Flags=OFN_HIDEREADONLY|OFN_NOCHANGEDIR
	If save_flag
		of.lpstrDefExt=of.lpstrFilter
		of.Flags:|OFN_OVERWRITEPROMPT
	Else
		of.Flags:|OFN_FILEMUSTEXIST
	EndIf
	
	'Add hook
	If hook
		RequestFileHook=hook
		RequestFileHelpButtonText=helpbuttontext
		of.lpfnHook=lpfnHook 
		of.Flags:|OFN_EXPLORER|OFN_ENABLEHOOK|OFN_SHOWHELP
	EndIf
	
	'Return string array
	Local lpstrFile:Byte[4096]
	
	Local cs:Byte Ptr
	cs=file.tocstring()
	MemCopy lpstrFile,cs,file.length
	MemFree cs
	
	of.lpstrFile=lpstrFile
	of.nMaxFile=4095
	
	If save_flag
		result=GetSaveFileNameA(of)
	Else
		result=GetOpenFileNameA(of)
	EndIf
	
	MemFree of.lpstrTitle
	MemFree of.lpstrInitialDir
	MemFree of.lpstrFilter
	
	If result Return String.FromCString(lpstrFile)
EndFunction


'=======================================================================
' ****ing Windows Madness©
'=======================================================================

Private

Global RequestFileHook()
Global RequestFileHelpButtonText$

Function lpfnHook:Int(hwnd:Int,uMsg:Int,lp:Int,pData:Byte Ptr) "win32"
	Local hdlg:Int
	Const CDN_HELP=-605
	Select uMsg
		Case WM_INITDIALOG
			hdlg=GetDlgItem(GetParent(hwnd),$040e)
			SetWindowTextA(hdlg,RequestFileHelpButtonText)
		Case WM_NOTIFY
			If pData<>Null
				Local o:OFNOTIFY=New OFNOTIFY
				MemCopy Varptr o.hwndFrom,pData,12
				If o.code=CDN_HELP
					hwnd=GetParent(hwnd)
					EnableWindow hwnd,0
					RequestFileHook()
					EnableWindow hwnd,1
					SetFocus hwnd
				EndIf
			EndIf
	EndSelect
	Return 0
EndFunction

Type NMHDR
	Field hwndFrom:Int
	Field idFrom:Int
	Field code:Int
EndType

Type OFNOTIFY Extends NMHDR
	Field lpOFN:Byte Ptr
	Field pszFile:Byte Ptr
EndType

Const OFN_READONLY                 =$00000001
Const OFN_OVERWRITEPROMPT          =$00000002
Const OFN_HIDEREADONLY             =$00000004
Const OFN_NOCHANGEDIR              =$00000008
Const OFN_SHOWHELP                 =$00000010
Const OFN_ENABLEHOOK               =$00000020
Const OFN_ENABLETEMPLATE           =$00000040
Const OFN_ENABLETEMPLATEHANDLE     =$00000080
Const OFN_NOVALIDATE               =$00000100
Const OFN_ALLOWMULTISELECT         =$00000200
Const OFN_EXTENSIONDIFFERENT       =$00000400
Const OFN_PATHMUSTEXIST            =$00000800
Const OFN_FILEMUSTEXIST            =$00001000
Const OFN_CREATEPROMPT             =$00002000
Const OFN_SHAREAWARE               =$00004000
Const OFN_NOREADONLYRETURN         =$00008000
Const OFN_NOTESTFILECREATE         =$00010000
Const OFN_NONETWORKBUTTON          =$00020000
Const OFN_NOLONGNAMES              =$00040000
Const OFN_EXPLORER                 =$00080000
Const OFN_NODEREFERENCELINKS       =$00100000
Const OFN_LONGNAMES                =$00200000

Const CDERR_DIALOGFAILURE			=$FFFF
Const CDERR_FINDRESFAILURE		=$0006
Const CDERR_INITIALIZATION		=$0002
Const CDERR_LOADRESFAILURE		=$0007
Const CDERR_LOADSTRFAILURE		=$0005
Const CDERR_LOCKRESFAILURE		=$0008
Const CDERR_MEMALLOCFAILURE		=$0009
Const CDERR_MEMLOCKFAILURE		=$000A
Const CDERR_NOHINSTANCE			=$0004
Const CDERR_NOHOOK				=$000B
Const CDERR_NOTEMPLATE			=$0003
Const CDERR_REGISTERMSGFAIL		=$000C
Const CDERR_STRUCTSIZE			=$0001
Const FNERR_BUFFERTOOSMALL		=$3003
Const FNERR_INVALIDFILENAME		=$3002
Const FNERR_SUBCLASSFAILURE		=$3001

Type OPENFILENAME
	Field lStructSize:Int=88
	Field hwndOwner:Int
	Field hInstance:Int=0
	Field lpstrFilter:Byte Ptr' File filter
	Field lpstrCustomFilter:Byte Ptr
	Field nMaxCustFilter:Int=0
	Field nFilterIndex:Int=0
	Field lpstrFile:Byte Ptr
	Field nMaxFile:Int
	Field lpstrFileTitle:Byte[4096] 
	Field nMaxFileTitle=4095
	Field lpstrInitialDir:Byte Ptr'start directory
	Field lpstrTitle:Byte Ptr' Dialog title
	Field Flags:Int=0
	Field nFileOffset:Short 
	Field nFileExtension:Short
	Field lpstrDefExt:Byte Ptr
	Field lCustData:Int
	Field lpfnHook:Byte Ptr=Null' Callback function
	Field lpTemplateName:Byte Ptr
	Field pvReserved:Byte Ptr' not sure if from here on is needed
	Field dwReserved:Int
	Field FlagsEx:Int
EndType

Public

'C++ stuff:
Rem
typedef struct tagOFN { 
  DWORD         lStructSize; 
  HWND          hwndOwner; 
  HINSTANCE     hInstance; 
  LPCTSTR       lpstrFilter; 
  LPTSTR        lpstrCustomFilter; 
  DWORD         nMaxCustFilter; 
  DWORD         nFilterIndex; 
  LPTSTR        lpstrFile; 
  DWORD         nMaxFile; 
  LPTSTR        lpstrFileTitle; 
  DWORD         nMaxFileTitle; 
  LPCTSTR       lpstrInitialDir; 
  LPCTSTR       lpstrTitle; 
  DWORD         Flags; 
  WORD          nFileOffset; 
  WORD          nFileExtension; 
  LPCTSTR       lpstrDefExt; 
  LPARAM        lCustData; 
  LPOFNHOOKPROC lpfnHook; 
  LPCTSTR       lpTemplateName; 
#If (_WIN32_WINNT >= =$0500)
  void *        pvReserved;
  DWORD         dwReserved;
  DWORD         FlagsEx;
#EndIf ' (_WIN32_WINNT >= =$0500)
} OPENFILENAME, *LPOPENFILENAME;
EndRem