Code archives/Miscellaneous/[MaxGUI] GUITools class

This code has been declared by its author to be Public Domain code.

Download source code

[MaxGUI] GUITools class by Jake L.2006
Module with a collection of useful functions (own work and community code) to make working with MaxGUI easier.

Leave me a mail for requests, comments and bugreports.
SuperStrict

Rem
BBDoc: MaxGUI Tools and useful functions
EndRem
Module fuw.guitools

ModuleInfo "Version: 0.8"
ModuleInfo "OS: Win32 / MacOS / Linux"
ModuleInfo "History: 0.8 - Removed GadgetClass() and ClearTreeView() as they are now supported in MaxGUI.MaxGUI."
ModuleInfo "History: 0.7 - Added RequestString and TreeViewKidsList"
ModuleInfo "History: 0.6 - Added ListViewSwapItems"
ModuleInfo "History: 0.5 - Added GadgetGetD/GadgetSetD"
ModuleInfo "History: 0.4 - TreeviewNodeFind and others added"
ModuleInfo "History: 0.3 - some bugfixes, added GadgetGet$/GadgetSet$"
ModuleInfo "History: 0.2 - multifile-requester (Win32 only)"
ModuleInfo "HISTORY: 0.1 - some treeview helpers to start with"


Import maxgui.drivers
Import brl.retro
Import brl.eventqueue

Private

Rem
BBDoc: Float to string, cutting of precision digits
EndRem
Function _TrimFloat$(value:Double,places:Int=2)
	Return Left(String(value),Instr(String(value),".") + places)
End Function

Const MAX_BUFFER_SIZE:Int = 8192

Const OFN_ALLOWMULTISELECT:Int = 512
Const OFN_CREATEPROMPT:Int = $2000
Const OFN_ENABLEHOOK:Int = 32
Const OFN_ENABLESIZING:Int = $800000
Const OFN_ENABLETEMPLATE:Int = 64
Const OFN_ENABLETEMPLATEHANDLE:Int = 128
Const OFN_EXPLORER:Int = $80000
Const OFN_EXTENSIONDIFFERENT:Int = $400
Const OFN_FILEMUSTEXIST:Int = $1000
Const OFN_HIDEREADONLY:Int = 4
Const OFN_LONGNAMES:Int = $200000
Const OFN_NOCHANGEDIR:Int = 8
Const OFN_NODEREFERENCELINKS:Int = $100000
Const OFN_NOLONGNAMES:Int = $40000
Const OFN_NONETWORKBUTTON:Int = $20000
Const OFN_NOREADONLYRETURN:Int = $8000
Const OFN_NOTESTFILECREATE:Int = $10000
Const OFN_NOVALIDATE:Int = 256
Const OFN_OVERWRITEPROMPT:Int = 2
Const OFN_PATHMUSTEXIST:Int = $800
Const OFN_READONLY:Int = 1
Const OFN_SHAREAWARE:Int = $4000
Const OFN_SHOWHELP:Int = 16
Const OFN_SHAREFALLTHROUGH:Int = 2
Const OFN_SHARENOWARN:Int = 1
Const OFN_SHAREWARN:Int = 0

Type TOpenFileNameA
	Field lStructSize:Int
	Field hwndOwner:Int
	Field hInstance:Int
	Field lpstrFilter:Byte Ptr
	Field lpstrCustomFilter:Int
	Field nMaxCustFilter:Int
	Field nFilterIndex:Int
	Field lpstrFile:Byte Ptr
	Field nMaxFile:Int
	Field lpstrFileTitle:Byte Ptr
	Field nMaxFileTitle:Int
	Field lpstrInitialDir:Byte Ptr
	Field lpstrTitle:Byte Ptr
	Field flags:Int
	Field nFileOffset:Short
	Field nFileExtension:Short
	Field lpstrDefExt:Byte Ptr
	Field lCustData:Int
	Field lpfnHook:Byte Ptr
	Field lpTemplateName:Byte Ptr
EndType

Extern "Win32"
	Function GetOpenFileName:Int( of:Byte Ptr) = "GetOpenFileNameA@4"	
EndExtern

Public

Rem
BBDoc: get a node's level
EndRem
Function TreeViewNodeLevel%(NODE:TGadget)
		Assert NODE<>Null,"TreeViewNodeLevel: node=Null!"
	Local lvl%
	While NODE.parent And GadgetClass(NODE)<>GADGET_TREEVIEW
		NODE=NODE.parent
		lvl:+1
	Wend
	Return lvl
End Function

Rem
BBDoc: get the subindex of a node
EndRem
Function TreeViewNodeIndex%(NODE:TGadget)
		Assert NODE<>Null,"TreeViewNodeIndex: node=Null!"
		Assert NODE.parent<>Null, "TreeViewNodeIndex: node is not a child!"
	Local idx%
	Local Link:TLink=NODE.parent.kids.FirstLink()
	While TGadget(Link.value())<>NODE
		Link=Link.NextLink()
		idx:+1
	Wend
	Return idx
End Function

Rem
BBDoc: Return names of node's kids as stringarray
EndRem
Function TreeViewKidsList$[](node:TGadget)
	Assert NODE<>Null,"TreeViewNodeIndex: node=Null!"
	Local cnt%=node.kids.Count()
	Local res$[]=New String[cnt]
	Local Link:TLink=NODE.kids.FirstLink()
	Local i%
	While Link<>Null
		res[i]=TGadget(Link.Value()).name
		Link=Link.NextLink()
		i:+1
	Wend
	Return res
End Function


Rem
BBDoc: returns the node by a parentnodes subindex
EndRem
Function TreeViewNodeAtIndex:TGadget(NODE:TGadget,index%)
		Assert NODE<>Null,"TreeViewNodeAtIndex: node=Null!"
	Local Link:TLink=NODE.kids.FirstLink()
	While Link<>Null
		If index<=0 Then Return TGadget(Link.value())
		index:-1
		Link=Link.NextLink()
	Wend
	Return Null
End Function

Rem
BBDoc: This function will swap node's captions but cannot swap icons unless you provide them
EndRem
Function TreeViewSwapNodes (node1:TGadget,node2:TGadget,icon1%=-1,icon2%=-1)
	?Debug
		Assert node1<>Null And node2<>Null,"TreeViewSwapNodes: Null not allowed"
	?
	Local temp$=node1.name
	Local tobj:Object=node1.context
	ModifyTreeViewNode (node1,node2.name,icon2)
	ModifyTreeViewNode (node2,temp,icon1)
	node1.context=node2.context
	node2.context=tobj
End Function

Rem
BBDoc: Search a treeview starting from node to find a children by name (and optional level)
EndRem
Function TreeViewNodeFind:TGadget (NODE:TGadget,txt$,lvl%=-1)
	Local Link:TLink=NODE.kids.FirstLink()
	Local n:TGadget
	While Link<>Null
		n=TGadget(Link.value())
		If n.name=txt And (lvl=-1 Or TreeViewNodeLevel(n)=lvl) Then Return n
		Link=Link.NextLink()
	Wend
End Function

Rem
BBDoc: Find a listitem by extra content
EndRem
Function GetGadgetItemByExtra% (listgadget:TGadget,extra:Object,compare_string%=False)
	Local ex:Object
	For Local i%=0 Until CountGadgetItems(listgadget)
		ex=GadgetItemExtra(listgadget,i)
		If ex=extra Or (compare_string And String(ex)=String(extra))
			Return i
		End If
	Next
	Return -1
End Function 

Rem
BBDoc: Swap two listitems
EndRem
Function ListViewSwapItems (lstview:TGadget,idx1%,idx2%)
	Local tmp$=GadgetItemText (lstview,idx1)
	Local tmpflags%=GadgetItemFlags (lstview,idx1)
	Local tmpextra:Object=GadgetItemExtra(lstview,idx1)
	Local tmptip$=lstView.ItemTip(idx1)
	Local tmpicon%=GadgetItemIcon(lstview,idx1)

	Local si%=SelectedGadgetItem(lstView)
	ModifyGadgetItem (lstview,idx1,GadgetItemText(lstview,idx2),GadgetItemFlags(lstview,idx2),GadgetItemIcon(lstview,idx2),..
					  lstView.ItemTip(idx2),GadgetItemExtra(lstview,idx2))
	ModifyGadgetItem (lstview,idx2,tmp,tmpflags,tmpicon,tmptip,tmpextra)
	If si=idx1 Then SelectGadgetItem(lstView,idx2) ElseIf si=idx2 Then SelectGadgetItem(lstView,idx1)
End Function

 Rem
 BBDoc: Create a textfield with label
 EndRem
Function AddTextField:TGadget (parent:TGadget,x%,y%,w%,caption$="",text$="")
	Local G:TGadget=CreateTextField (x,y,w,20,parent)
	
	If text Then SetGadgetText (G,text)
	If caption
		Local lbl:TGadget=CreateLabel (caption,x-105,y+3,100,20,parent,LABEL_RIGHT)
	EndIf
	Return G
End Function

Rem
BBDoc: Create a checkbox and set it's state
EndRem
Function AddCheckbox:TGadget (parent:TGadget,x%,y%,w%,caption$,checked%=False)
	Local G:TGadget=CreateButton (caption,x,y,w,20,parent,BUTTON_CHECKBOX)
	SetButtonState(G,checked)
End Function

Rem
BBDoc: Request multiple files
About: Done by Blitzmax community, slightly changed the way files are returned
EndRem
Function RequestFiles:String[]( text:String, exts:String = Null, path:String = Null)
	?MacOs | Linux	
	Local res:String = RequestFile( test, exts, False, path)
	If res.length <= 0 Then Return Null
	Return [res]
	?
	?Win32	
	Global hwndFocus:Int
	
	' prepare filename / path (ripped from BRL's RequestFile())
	Local File:String, Dir:String
	path = path.Replace( "/","\" )	
	Local i:Int = path.FindLast( "\" )
	If i <> -1 Then
		Dir = path[..i]
		File = path[i+1..]
	Else
		File = path
	EndIf
	' calculate default index of extension in extension list from path name
	Local ext:String, defext:Int,p:Int,q:Int
	p = path.Find(".")
	If (p>-1) Then
		ext = "," + path[p+1..].toLower() + ","
		Local exs:String = exts.toLower()
		exs = exs.Replace(":",":,")
		exs = exs.Replace(";",",;")
		p = exs.find(ext)
		If p >-1 Then
			Local q:Int = -1
			defext = 1
			While True
				q = exs.find(";",q+1)
				If q > p Then Exit
				If q = -1 Then 
					defext = 0
					Exit
				EndIf
				defext :+ 1
			Wend
		EndIf
	EndIf
	If exts Then
		If exts.Find(":") = -1 Then
			exts = "Files~0*." + exts
		Else
			exts = exts.Replace(":","~0*.")
		EndIf
		exts = exts.Replace(";","~0")
		exts = exts.Replace(",",";*.") + "~0"
	EndIf
	
	' allocate cstrings
	Local textp:Byte Ptr = text.ToCString()
	Local extsp:Byte Ptr = exts.ToCString()
	Local dirp:Byte Ptr
	If Dir.length > 0 Then dirp = Dir.ToCString()
	
	' prepare file buffer
	Local Buf:Byte[MAX_BUFFER_SIZE]
	memcpy_( Buf, File, File.length)
	
	' initialize dialog options
	Local of:TOpenFileNameA = New TOpenFileNameA
	of.lStructSize = SizeOf(TOpenFileNameA)
	of.hwndOwner = GetActiveWindow()
	of.lpstrTitle = textp
	of.lpstrFilter = extsp
	of.nFilterIndex = defext
	of.lpstrFile = Buf
	of.lpstrInitialDir = dirp
	of.nMaxFile = Buf.length
	of.flags = OFN_HIDEREADONLY | OFN_NOCHANGEDIR | OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_ALLOWMULTISELECT | OFN_EXPLORER ' | OFN_LONGNAMES
	
	' display dialog
	hwndFocus = GetFocus()
	Local n:Int = GetOpenFileName( of)
	SetFocus( hwndFocus)
	
	' free cstrings
	MemFree textp
	MemFree extsp
	If dirp Then MemFree dirp

	' failure ?
	If n <= 0 Then Return Null
	
	' count the number of files
	Local s:Byte Ptr = Buf
	Local count:Int = 0
	While s[0] <> 0
		If s[1] = 0 Then
			count :+ 1
			s :+ 2
			If s[0] = 0 Then Exit
		EndIf
		s :+ 1
	Wend
	If count <= 0 Then Return Null
	
	' extract filenames into String array
	If count = 1 Then 
		'MARK: im following RequestFile() convention here, and returing "\" path seperators #1
		'Return [ String.FromCString( buf).Replace( "\", "/") ]
		Return [ String.FromCString( Buf) ]
	Else
		Local result:String[] = New String[count]	
		s = Buf
		For Local i:Int = 0 Until count
			result[i] = String.FromCString( s)
			s :+ result[i].length + 1
			If i>0 Then result[i] = result[0]+"\"+result[i]
		Next	
		'MARK: im following RequestFile() convention here, and returing "\" path seperators #2
		'result[0] = result[0].Replace( "\", "/") + "/"
		result=Result[1..]
		Return result
	EndIf
EndFunction

Rem
BBDoc: Return a gadget's value as Double
EndRem
Function GadgetGetD! (G:TGadget)
	Return Double(GadgetGet(G))
End Function

Rem
BBDoc: Set a gadget's value from Double
EndRem
Function GadgetSetD (G:TGadget,d!)
	SetGadgetText (G,_TrimFloat(d))
End Function

Rem
BBDoc: Return a gadget's value as Float
EndRem
Function GadgetGetF# (G:TGadget)
		Select GadgetClass(G)
			Case GADGET_TEXTFIELD
				Return Float(GadgetText(G))
		End Select
End Function

Rem
BBDoc: Return a gadget's value as Int
EndRem
Function GadgetGetI% (G:TGadget)
		Select GadgetClass(G)
			Case GADGET_TEXTFIELD
				Return Int(GadgetText(G))
		End Select
End Function

Rem
BBDoc: Return a gadget's value as string
About: Not quite useful yet, but other gadget-types could be added
EndRem
Function GadgetGet$ (G:TGadget)
	Select GadgetClass(G)
			Case GADGET_TEXTFIELD
				Return GadgetText(G)
		End Select
End Function

Rem
BBDoc: Set a gadget's value from Float
EndRem	
Function GadgetSetF (G:TGadget,f#)
		SetGadgetText(G,_TrimFloat(f))
End Function

Rem
BBDoc: Set a gadget's value from Int
EndRem		
Function GadgetSetI (G:TGadget,i%)
		SetGadgetText(G,i)
End Function

Rem
BBDoc: Set a gadget's value from string
About: Not quite useful yet, but other gadget-types could be added
EndRem	
Function GadgetSet (G:TGadget,s$)
		SetGadgetText(G,s)
End Function

Rem
BBDoc: A DialogBox
EndRem	
Function RequestString$(caption$,text$)
	Local ww%=220
	Local wh%=30
	Local wx%=Desktop().width/2-ww/2
	Local wy%=Desktop().height/2-wh/2
	Local win:TGadget=CreateWindow(caption,wx,wy,ww,wh,Null,WINDOW_TITLEBAR|WINDOW_TOOL|WINDOW_CLIENTCOORDS)
	Local txt:TGadget=CreateTextField(5,5,ww-100,20,win)
		SetGadgetText (txt,text)
	Local ok:TGadget=CreateButton("OK",ww-85,5,80,20,win)
	
	ActivateGadget(txt)
	Repeat
		Select WaitEvent()
			Case EVENT_WINDOWCLOSE
				FreeGadget(win)
				Return text
			Case EVENT_GADGETACTION
				If EventSource()=ok
					text=GadgetText(txt)
					FreeGadget(win)
					Return text
				End If
		End Select
	Forever
End Function

Rem
BBDoc: Absolute to relative paths
About: Copied from Blitzmax community 
EndRem
Function ExtractRelativePath:String( src:String, dest:String )
	
	src=ExtractDir(RealPath(src))
	dest=RealPath(dest)
	
	Local count:Int=0
	While src<>Left(dest,Len(src))
		count:+1
		Local i:Int=src.FindLast("/")
		src=src[..i]
	Wend
	
	dest=dest.Replace(src,"")
	dest=dest[1..]
	For Local i:Int=1 To count
		dest="../"+dest
	Next
	
	Return dest
	
End Function

Comments

WendellM2006
Treeviews aren't my thing right now, but if/when I need them, I'll be happy that you shared this <g> – thanks!


DavidDC2007
There are a few safety checks missing from the above code.

For example, in TreeViewNodeIndex, the code should be testing link <> Null before calling link.Value().


Jake L.2007
You're right. I will update my post with the latest version soon. Also added a few functions since the first version.

EDIT: Updated with new latest version.


andre722007
When I try to compile the module I get an error: Module does not match commandline module ...
Can anybody help me what to do now?

Thanks


Jake L.2007
You should copy the code into a file called "guitools.bmx" and put that in the path <your Blitzmax>/mod/fuw.mod/guitools.mod/

compile it using BMK.exe (from Blitzmax/Bin):

bmk -makemods fuw.guitools

Jake


andre722007
Thanks, but that what I tried out...
Also I rebuild all modules with the IDE - but in my guitools.mod folder is after building also only the guitools.bmx module, no builded binarys.

However, bmk reports no error but for sure the commands like RequestString$ are not recognized ...


Jake L.2007
You should have some binary code after compiling with bmk, so something must be wrong with your directory structure I think.

I copy&pasted the code from my existing mod which I use the whole day....it should definitely work. Are you using windows?


andre722007
Yes I'm using Windows XP.
I've installed some others mods like bah and fmc - and after a lot of questions and great support I've no more trouble compiling mods.

The matter with the GUITool Class is that I get no error code - it seems that anything whould be fine but the binaries are not available.
Is there a way to get the binary code from somewhere?


Jake L.2007
Well, you should be able to compile this mod, as I am. Don't know why it don't work on your side, but copy&paste the code to whereever you like (plain bmx, your own mod etc..) and use it, if you want.


xlsior2008
With the latest 1.30 SVN version, I'm getting the following error when compiling this module:

Compiling:guitools.bmx
Compile Error: Unable to convert from 'Int()' to 'Int'
[c:/code/blitzmax/mod/fuw.mod/guitools.mod/guitools.bmx;108;3]




markcw2008
The error is saying that a method is being incorrectly treated as a field. You have to add the brackets like so.
Function GadgetClass%(G:TGadget)
	?Win32
		Return TWin32Gadget(G).class()
	?MacOs
		Return TNSGadget(G).class()
	?Linux
		Return TFLWidget(G).fltype
	?
End Function



xlsior2008
Thanks, that did the trick.


SebHoll2008
GadgetClass() is a built-in MaxGUI function now so you can just remove that declaration. Also, the one given here does not work with MaxGUI.Win32MaxGUIEx and may report incorrect values.


SebHoll2009
Jake: Just a note to say that I've updated the code archive entry with the now officially supported commands (ClearTreeView() and GadgetClass()) removed.


Jake L.2009
Thanks for keeping this up to date and shame on me that I don't. I haven't used MaxGUI for a long time, so I missed the ongoing development.


Code Archives Forum