MaxGUI Upgrade

BlitzMax Forums/BlitzMax GUI Programming/MaxGUI Upgrade

The forum is locked. These are the original blitzbasic.com forums, kept so they can still be read. No new topics, replies or edits can be made here. To join in, head over to the Blitz3D+ forums.

You can have a look through this code if you want. I have basically just written a wrapper for the maxgui implimentation plus a german posters code for the stringtable gadget. Id like to rewrite it at some point. Was kind of bolted on.

You'll have to go in and figure it out, but here is an example of how to use it.

Import "../../global/libs/ggui.bmx"

'create an update list. This lets you store all gadgets that need updating
Local updatelist:gupdatelist = New gupdatelist


'### create a window ###
Global window:ggadget = gwindow.create("A window",[10,10,400,300])
'add a window to the update list
updatelist.addgadget(window)
'add update hook to window gadget. this basically tells the gadget to go to a specified function upon updating
window.setupdatehook(MyEvents)


'### create a stringtable ###
'this function uses the gwindow.insiderect() this basically returns a 4 dimensional array of the client area of the gadget
Global stringtable:gstringtable = gstringtable.create(window.insiderect(),window)
'add some columns
stringtable.addcolumn("column1",100)
stringtable.addcolumn("column2",100)
stringtable.addcolumn("column3",100)
'add some rows
stringtable.addrow(["row0 col0","row0 col1","row0 col2"])
stringtable.addrow(["row1 col0","row1 col1","row1 col2"])
stringtable.addrow(["row2 col0","row2 col1","row2 col2"])
'add a stringtable to the update list
updatelist.addgadget(stringtable)
'add update hook to stringtable gadget. this basically tells the gadget to go to a specified function upon updating
stringtable.setupdatehook(MyEvents)


'main loop
Repeat
	'wait for an event
	WaitEvent()

	'check to see if no events belong to items in the update list
	If updatelist.update() = False
		'put all non ggui events here. eg timers and whatnot!
	End If
Forever

Function MyEvents(ngadget:ggadget)
	'see who this event belongs too
	Select ngadget
		Case window
			Select EventID()
				Case EVENT_WINDOWCLOSE
					'window closed so end
					End
			End Select
			
		Case stringtable
			Select EventID()
				Case EVENT_GADGETSELECT
					'selection changed on stringtable
					Notify "stringtable row changed to "+EventData()
			End Select
	End Select
End Function



Obviously not as nice as having it built in but its acceptable!!!



Here are the 3 source files

ggui.bmx
Strict

Import BRL.Win32MaxGUI
Import BRL.EventQueue
Import brl.linkedlist
Import "tdriveinfo.bmx"
Import "multicolumnlistbox.bmx"

Type gupdatelist
	Global list:TList = CreateList()

	Field gadgets:TList = CreateList()
	
	Method New:Int()
		gupdatelist.list.addlast(list)
	End Method
	
	Method addgadget:Int(ngadget:ggadget)
		gadgets.addlast(ngadget)
	End Method
	
	Method removegadget:Int(ngadget:ggadget)
		gadgets.remove(ngadget)
	End Method
	
	Method Update:Int()
		Local temp_gadget:ggadget
		
		For temp_gadget = EachIn gadgets
			If temp_gadget.update() Return True
		Next
	End Method
End Type

Type ggadget Abstract
	Field gadgets:ggadget[]
	Field gadget:tgadget
	Field items:gitem[]
	Field string1:String
	Field string2:String
	Field string3:String
	Field string4:String
	Field int1:Int
	Field int2:Int
	Field int3:Int
	Field int4:Int
	
	Field _hookupdate:Int(ngadget:ggadget)
	
	Method selecteditem:Int()
		Return SelectedGadgetItem(gadget)
	End Method
	
	Method countitems:Int()
		Return CountGadgetItems(gadget)
	End Method
	
	Method removeitem:Int(nitem:Int)
		Return SelectedGadgetItem(gadget)
	End Method
	
	Method selecteditemtext:String()
		Return GadgetItemText(gadget,SelectedGadgetItem(gadget))
	End Method

	Method defaultupdate:Int()
		Return False
	End Method
	
	Method setlayout:Int(nleft:Int,nright:Int,ntop:Int,nbottom:Int)
		Return SetGadgetLayout(gadget,nleft,nright,ntop,nbottom)
	End Method

	Method disabled:Int()
		Return GadgetDisabled(gadget)
	End Method
	
	Method enabled:Int()
		Return GadgetDisabled(gadget) = False
	End Method
	
	Method setint1:Int(nint:Int)
		int1 = nint
	End Method

	Method setint2:Int(nint:Int)
		int2 = nint
	End Method
	
	Method setint3:Int(nint:Int)
		int3 = nint
	End Method
	
	Method setint4:Int(nint:Int)
		int4 = nint
	End Method
	
	Method setstring1:Int(nstring:String)
		string1 = nstring
	End Method
	
	Method setstring2:Int(nstring:String)
		string2 = nstring
	End Method
	
	Method setstring3:Int(nstring:String)
		string3 = nstring
	End Method
	
	Method setstring4:Int(nstring:String)
		string4 = nstring
	End Method

	Method getint1:Int()
		Return int1
	End Method
	
	Method getint2:Int()
		Return int2
	End Method
	
	Method getint3:Int()
		Return int3
	End Method
	
	Method getint4:Int()
		Return int4
	End Method
	
	Method getstring1:String()
		Return string1
	End Method
	
	Method getstring2:String()
		Return string2
	End Method
	
	Method getstring3:String()
		Return string3
	End Method
	
	Method getstring4:String()
		Return string4
	End Method

	Method update:Int()
		'this function should return true if it wants to halt gui updates
		Local temp_return:Int
		If _hookupdate <> Null And hastgadget(tgadget(EventSource()))
			temp_return = _hookupdate(Self)
			If temp_return Return temp_return
		End If
	End Method
	
	Method hastgadget:Int(ngadget:tgadget)
		If gadget = ngadget Return True
	End Method
	
	Method getgadget:tgadget()
		Return gadget
	End Method
	
	Method getclient:tgadget()
		Return gadget
	End Method
	
	Method cleargadgets:Int()
		Local temp_gadget:ggadget
		Local temp_updatelist:gupdatelist
		
		'clear each sub gadget
		For temp_gadget = EachIn gadgets
			'recurse clear
			temp_gadget.cleargadgets()
			
			'remove the gadget
			FreeGadget(temp_gadget.gadget)
			
			'remove existance of gadget
			For temp_updatelist = EachIn gupdatelist.list
				temp_updatelist.removegadget(temp_gadget)
			Next
		Next
		
		'clear teh list of gadgets
		gadgets = Null
	End Method
	
	Method cleartext:Int()
		SetGadgetText(gadget,"")
	End Method
	
	Method clearitems:Int()
		ClearGadgetItems(gadget)
	End Method
	
	Method disableitem:Int(nitem:Int)
		DisableGadgetItem(gadget,nitem)
	End Method
	
	Method enableitem:Int(nitem:Int)
		EnableGadgetItem(gadget,nitem)
	End Method
	
	Method finditem:gitem(ntext:String)
		'call to update from all items
		Local temp_return:gitem
		Local temp_item:gitem
		For temp_item = EachIn items
			temp_return = temp_item.finditem(ntext)
			If temp_return Return temp_return
		Next
	End Method
	
	Method additem:gitem(ntext:String,nicon:Int=-1,nextra:Object=Null,nid:String="")
		'create new item
		Local temp_item:gitem = New gitem
		temp_item.id = nid
		temp_item.text = ntext
		temp_item.extra = nextra
		temp_item.parent = Null
		'save item into array
		Local temp_slot:Int = items.length
		items = items[..temp_slot+1]
		items[temp_slot] = temp_item
		
		AddGadgetItem(gadget,ntext,0,nicon,Null)
		
		Return temp_item
	End Method
	
	Method redraw:Int()
		RedrawGadget(gadget)
	End Method
	
	Method setupdatehook:Int(nhook:Int(ngadget:ggadget))
		_hookupdate = nhook
	End Method
	
	Method show:Int()
		ShowGadget(Gadget)
	End Method
	
	Method hide:Int()
		HideGadget(gadget)
	End Method
	
	Method enable:Int()
		EnableGadget(gadget)
	End Method
	
	Method disable:Int()
		DisableGadget(gadget)
	End Method

	Method insidex:Int()
		Return 0
	End Method
	
	Method insidey:Int()
		Return 0
	End Method
	
	Method gettext:String()
		Return GadgetText(gadget)
	End Method
	
	Method settext:Int(ntext:String)
		SetGadgetText(gadget,ntext)
	End Method
	
	Method addtext:Int(ntext:String)
		SetGadgetText(gadget,GadgetText(gadget)+ntext)
	End Method
	
	Method insidewidth:Int()
		Return ClientWidth(gadget)
	End Method
	
	Method insideheight:Int()
		Return ClientHeight(gadget)
	End Method
	
	Method outsidex:Int()
		Return GadgetX(gadget)
	End Method
	
	Method outsidey:Int()
		Return GadgetY(gadget)
	End Method
	
	Method outsidewidth:Int()
		Return GadgetWidth(gadget)
	End Method
	
	Method outsideheight:Int()
		Return GadgetHeight(gadget)
	End Method
	
	Method insiderect:Int[](noffsetx:Int=0,noffsety:Int=0,noffsetw:Int=0,noffseth:Int=0)
		Return [0+noffsetx,0+noffsety,ClientWidth(gadget)+noffsetw,ClientHeight(gadget)+noffseth]
	End Method
	
	Method outsiderect:Int[](noffsetx:Int=0,noffsety:Int=0,noffsetw:Int=0,noffseth:Int=0)
		Return [GadgetX(gadget)+noffsetx,GadgetY(gadget)+noffsety,GadgetWidth(gadget)+noffsetw,GadgetHeight(gadget)+noffseth]
	End Method
	
	Method topedge:Int()
		Return GadgetY(gadget)
	End Method
	
	Method bottomedge:Int()
		Return GadgetY(gadget) + GadgetHeight(gadget)
	End Method
	
	Method leftedge:Int()
		Return GadgetX(gadget)
	End Method
	
	Method rightedge:Int()
		Return GadgetX(gadget) + GadgetWidth(gadget)
	End Method
	
	Method setposition:Int(nx:Int,ny:Int)
		SetGadgetShape(gadget,nx,ny,GadgetWidth(gadget),GadgetHeight(gadget))
	End Method
	
	Method setsize:Int(nwidth:Int,nheight:Int)
		SetGadgetShape(gadget,GadgetX(gadget),GadgetY(gadget),nwidth,nheight)
	End Method
	
	Method setshape(nx:Int,ny:Int,nwidth:Int,nheight:Int)
		SetGadgetShape(gadget,nx,ny,nwidth,nheight)
	End Method
	
	Method addgadget:Int(ngadget:ggadget)
		Local temp_slot:Int = gadgets.length
		gadgets = gadgets[..temp_slot+1]
		gadgets[temp_slot] = ngadget
		Return temp_slot
	End Method
End Type

Type gwindow Extends ggadget
	Field client:tgadget

	Method hastgadget:Int(ngadget:tgadget)
		If client = ngadget Or gadget = ngadget Return True
	End Method

	Method getclient:tgadget()
		Return client
	End Method

	Function create:gwindow(ntitle:String,nrect:Int[],nparent:ggadget=Null,nstyle:Int=WINDOW_TITLEBAR)
		Local window:gwindow = New gwindow
		Local temp_parent:tgadget = Null
		If nparent <> Null temp_parent = nparent.getclient()
		window.gadget = CreateWindow(ntitle,nrect[0],nrect[1],nrect[2],nrect[3],temp_parent,nstyle)
		window.client = CreatePanel(0,0,ClientWidth(window.gadget),ClientHeight(window.gadget),window.gadget)
		SetGadgetLayout(window.client,EDGE_ALIGNED,EDGE_ALIGNED,EDGE_ALIGNED,EDGE_ALIGNED)
		Return window
	End Function
End Type

Type gtoolwindow Extends ggadget
	Field client:tgadget
	
	Method hastgadget:Int(ngadget:tgadget)
		If client = ngadget Or gadget = ngadget Return True
	End Method
	
	Method getclient:tgadget()
		Return client
	End Method

	Function create:gtoolwindow(ntitle:String,nrect:Int[],nparent:ggadget=Null,nstyle:Int=WINDOW_TITLEBAR | WINDOW_TOOL)
		Local window:gtoolwindow = New gtoolwindow
		Local temp_parent:tgadget = Null
		If nparent <> Null temp_parent = nparent.getclient()
		window.gadget = CreateWindow(ntitle,nrect[0],nrect[1],nrect[2],nrect[3],temp_parent,nstyle)
		window.client = CreatePanel(0,0,ClientWidth(window.gadget),ClientHeight(window.gadget),window.gadget)
		SetGadgetLayout(window.client,EDGE_ALIGNED,EDGE_ALIGNED,EDGE_ALIGNED,EDGE_ALIGNED)
		Return window
	End Function
End Type

Type gtabber Extends ggadget
	Field selected:Int = -1
	Field icons:ticonstrip
	Field tabs:gtab[]

	Method update:Int()
		'this function should return true if it wants to halt gui updates
		Local temp_return:Int
		Local temp_i:Int
		If _hookupdate <> Null 
			temp_return = _hookupdate(Self)
			If temp_return Return temp_return
		End If			
		'scan through all gadgets until a return was matched
		If gadgets.length > 0
			For temp_i = 0 Until gadgets.length
				If gadgets[temp_i].update() Return True
			Next
		End If
		'scan through all tabs until a return was matched
		If tabs.length > 0
			For temp_i = 0 Until tabs.length
				If tabs[temp_i].update() Return True
			Next
		End If	
	End Method

	Method addtab:gtab(ntitle:String,ntip:String="",nicon:Int=-1)
		'create a new tab ggadget
		Local tab:gtab = New gtab
		'setup the tab
		tab.slot = tabs.length
		tab.gadget = CreatePanel(0,0,ClientWidth(gadget),ClientHeight(gadget),gadget)
		SetGadgetLayout(tab.gadget,EDGE_ALIGNED,EDGE_ALIGNED,EDGE_ALIGNED,EDGE_ALIGNED)
		'resize tabs array for this new tab
		tabs = tabs[..tab.slot+1]
		tabs[tab.slot] = tab
		'add tab to tabber
		AddGadgetItem(gadget,ntitle,0,nicon,ntip)
		'hide the tab unless it is only current active tab in tabber
		If tabs.length > 1 
			tab.hide()
		Else
			selected = 0
		End If
		Return tab
	End Method
	
	Method changetab:Int(ntab:Int)
		If ntab > tabs.length Or ntab = selected Return False
		Local temp_i:Int
		tabs[selected].hide()
		tabs[ntab].show()
		selected = ntab
	End Method

	Function create:gtabber(nrect[],nparent:ggadget=Null,nicons:Object=Null)
		Local tabber:gtabber = New gtabber
		Local temp_parent:tgadget = Null
		If nparent <> Null temp_parent = nparent.getclient()
		tabber.gadget = CreateTabber(nrect[0],nrect[1],nrect[2],nrect[3],temp_parent)
		Local iconstrip:ticonstrip = ticonstrip(nicons)
		If iconstrip = Null
			tabber.icons = LoadIconStrip(String(nicons))
		Else
			tabber.icons = ticonstrip(nicons)
		End If
		If tabber.icons SetGadgetIconStrip(tabber.gadget,tabber.icons)
		SetGadgetLayout(tabber.gadget,EDGE_ALIGNED,EDGE_ALIGNED,EDGE_ALIGNED,EDGE_ALIGNED)
		Return tabber
	End Function
End Type

Type glabel Extends ggadget
	Function create:glabel(ntitle:String,nrect[],nparent:ggadget=Null,nalign:Int = LABEL_CENTER)
		Local label:glabel = New glabel
		Local temp_parent:tgadget = Null
		
		'setup parent gadget
		If nparent <> Null temp_parent = nparent.getclient()
		
		'create gadgets
		label.gadget = CreateLabel(ntitle,nrect[0],nrect[1],nrect[2],nrect[3],temp_parent,nalign)
		
		'return label
		Return label
	End Function
End Type

Type ggroup Extends ggadget
	Function create:ggroup(ntitle:String,nrect[],nparent:ggadget=Null)
		Local group:ggroup = New ggroup
		Local temp_parent:tgadget = Null
		
		'setup parent gadget
		If nparent <> Null temp_parent = nparent.getclient()
		
		'create gadgets
		group.gadget = CreatePanel(nrect[0],nrect[1],nrect[2],nrect[3],temp_parent,PANEL_GROUP,ntitle)
		
		'return group
		Return group
	End Function
End Type

Type gtab Extends ggadget
	Field slot:Int
End Type

Type gfilebrowser Extends ggadget
	Field listbox:tgadget
	Field textfield:tgadget
	Field path:String
	Field rootpath:String
	Field icons:ticonstrip
	Field filter:String[]
	
	Method getgadget:tgadget()
		Return listbox
	End Method
	
	Method selectedpath:String()
		Local temp_slot:Int = SelectedGadgetItem(listbox)
		If temp_slot = -1
			Return rootpath+path
		Else
			Return rootpath+path+"/"+GadgetItemText(listbox,temp_slot)
		End If
	End Method
	
	Method hastgadget:Int(ngadget:tgadget)
		If listbox = ngadget Or textfield = ngadget Or gadget = ngadget Return True
	End Method
	
	Method update:Int()
		Local temp_itemtext:String
		Local temp_pos:Int
	
		Select EventSource()
			Case listbox
				Select EventID()
					Case EVENT_GADGETACTION
						'the listbox was double left clicked
						temp_itemtext = GadgetItemText(listbox,EventData())
						If temp_itemtext = ".."
							'go back a folder
							If path.length
								temp_pos = path.findlast("/")
								changepath(path[0..temp_pos])
							End If
						Else
							'go forward a folder
							If path.length
								If FileType(rootpath+path + "/" + temp_itemtext) = FILETYPE_DIR
									changepath(path + "/" + temp_itemtext)
								End If
							Else
								If FileType(rootpath+path + temp_itemtext) = FILETYPE_DIR
									changepath(path + temp_itemtext)
								End If
							End If
						End If
					Case EVENT_GADGETSELECT
						'the list box was single left clicked
				End Select
		End Select
		
		super.update()
	End Method
	
	Method changepath:Int(npath:String)
		If npath.length npath = StripSlash(npath)
		path = npath
		SetGadgetText(textfield,path)
		populatelist()
	End Method
	
	Method populatelist:Int()
		Local temp_dir:String[]
		Local temp_file:String
		Local temp_ext1:String
		Local temp_ext2:String
		Local temp_list:TList
		Local temp_drive:tdriveinfo
		Local temp_ok:Int
		
		'this function will clear teh list and populate it
		ClearGadgetItems(listbox)
		
		'add back
		If path.length AddGadgetItem(listbox,"..",0,3)
		
		'check for path scan or for drive scan
		If rootpath.length = 0 And path.length = 0
			'load drives on the computer
			temp_list = GetAllDrives(-1,False)
			'add drives
			For temp_drive = EachIn temp_list
				AddGadgetItem(listbox,temp_drive.label.replace("",""),0,0)
			Next
		Else
			'load contents for the directory
			temp_dir = LoadDir(rootpath+path,True)
			
			'add folders
			For temp_file = EachIn temp_dir
				If FileType(rootpath+path+"/"+temp_file) = FILETYPE_DIR
					AddGadgetItem(listbox,temp_file,0,1)
				End If
			Next
			
			'add files
			For temp_file = EachIn temp_dir
				If FileType(rootpath+path+"/"+temp_file) = FILETYPE_FILE
					temp_ok = False
					If filter = Null
						temp_ok = True
					Else
						temp_ext1 = ExtractExt(temp_file).tolower()
						For temp_ext2 = EachIn filter
							If temp_ext1 = temp_ext2
								temp_ok = True
								Exit
							End If
						Next
					End If
					If temp_ok AddGadgetItem(listbox,temp_file,0,2)
				End If
			Next
		End If
	End Method
	
	Function create:gfilebrowser(nrect[],nparent:ggadget=Null,nicons:Object=Null,nrootpath:String="",npath:String="",nfilter:String[]=Null)
		Local temp_filebrowser:gfilebrowser= New gfilebrowser
		Local temp_parent:tgadget = Null
		If nparent <> Null temp_parent = nparent.getclient()

		'create main bounding / clipping box
		temp_filebrowser.gadget = CreatePanel(nrect[0],nrect[1],nrect[2],nrect[3],temp_parent)
		temp_filebrowser.textfield = CreateTextField(0,0,ClientWidth(temp_filebrowser.gadget),20,temp_filebrowser.gadget)
		temp_filebrowser.listbox = CreateListBox(0,20,ClientWidth(temp_filebrowser.gadget),ClientHeight(temp_filebrowser.gadget)-20,temp_filebrowser.gadget)
		DisableGadget(temp_filebrowser.textfield)
		SetGadgetLayout(temp_filebrowser.textfield,EDGE_ALIGNED,EDGE_ALIGNED,EDGE_ALIGNED,EDGE_CENTERED)
		SetGadgetLayout(temp_filebrowser.listbox,EDGE_ALIGNED,EDGE_ALIGNED,EDGE_ALIGNED,EDGE_ALIGNED)
				
		'setup icon strip
		Local iconstrip:ticonstrip = ticonstrip(nicons)
		If iconstrip = Null
			temp_filebrowser.icons = LoadIconStrip(String(nicons))
		Else
			temp_filebrowser.icons = ticonstrip(nicons)
		End If
		If temp_filebrowser.icons SetGadgetIconStrip(temp_filebrowser.listbox,temp_filebrowser.icons)
		
		'fix details
		If nrootpath.length nrootpath = RealPath(nrootpath)
		If nfilter
			Local temp_i:Int
			For temp_i = 0 Until nfilter.length
				nfilter[temp_i] = nfilter[temp_i].tolower()
			Next
		End If
		
		'setup browsing details
		temp_filebrowser.filter = nfilter
		temp_filebrowser.rootpath = nrootpath
		temp_filebrowser.changepath(npath)
		
		Return temp_filebrowser
	End Function
End Type


Type gstringtable Extends ggadget
	Field icons:ticonstrip
	
	Field totalcolumns:Int
	Field totalrows:Int
	
	Method findentirerow:Int(ncells:String[],nwild:String="*")
		If ncells.length < totalcolumns Return -1
		'this function will attempt to find a row match
		Local temp_row:Int
		Local temp_column:Int
		Local temp_debugcell:String
		Local temp_debugarray:String
		
		For temp_row = 0 Until totalrows
			For temp_column = 0 Until totalcolumns
				If ncells[temp_column] = nwild Or ncells[temp_column] = GetListBoxItemText(gadget,temp_row,temp_column+1)
					'check to see if end of row for return
					If temp_column = totalcolumns - 1 Return temp_row
				Else
					'break this row as match not found
					Exit
				End If
			Next
		Next
		
		Return -1
	End Method
	
	Method removeitem:Int(nrow:Int)
		'see if there was a selected row
		Local temp_row:Int = SelectedGadgetItem(gadget)
		
		'update selection before deleting
		If temp_row > -1
			If temp_row = totalrows - 1 And totalrows > 1
				'move selection up
				temp_row :- 1
			End If
		End If
		
		'remove teh row
		RemoveGadgetItem(gadget,nrow)
		SetListBoxColumnWidth(gadget,0,0)
		
		'decrease number of rows
		totalrows :- 1
		
		'move the selection
		If totalrows
			SelectGadgetItem(gadget,temp_row)
		End If
	End Method
	
	Method findrow:Int(nstring:String,ncolumn:Int=-1,ncase:Int=True)
		'this will scan the stringtable for a matching string text
		Local temp_row:Int
		Local temp_column:Int
		
		If ncase = False
			nstring = nstring.tolower()
			
			For temp_row = 0 Until totalrows
				If ncolumn = -1
					'scan all columns
					For temp_column = 1 To totalcolumns
						If GetListBoxItemText(gadget,temp_row,temp_column).tolower() = nstring Return temp_row
					Next
				Else
					'scan speciffic column
					If GetListBoxItemText(gadget,temp_row,ncolumn+1).tolower() = nstring Return temp_row
				End If
			Next
		Else
			For temp_row = 0 Until totalrows
				If ncolumn = -1
					'scan all columns
					For temp_column = 1 To totalcolumns
						If GetListBoxItemText(gadget,temp_row,temp_column) = nstring Return temp_row
					Next
				Else
					'scan speciffic column
					If GetListBoxItemText(gadget,temp_row,ncolumn+1) = nstring Return temp_row
				End If
			Next
		End If
		
		Return -1
	End Method
	
	Method clearitems:Int()
		ClearGadgetItems(gadget)
		totalrows = 0
	End Method
	
	Method countrows:Int()
		Return totalrows
	End Method
	
	Method countcolumns:Int()
		Return totalcolumns
	End Method
	
	Method getcelltext:String(ncolumn:Int,nrow:Int)
		Return GetListBoxItemText(gadget,nrow,ncolumn+1)
	End Method
	
	Method setcelltext:String(ncolumn:Int,nrow:Int,ntext:String)
		Local temp_result:Int = ModifyListBoxItem(gadget,nrow,ncolumn+1,ntext)
		SetListBoxColumnWidth(gadget,0,0)
		Return temp_result
	End Method
	
	Method addcolumn:Int(ntitle:String,nwidth:Int)
		Local temp_slot:Int = totalcolumns+1
		'adds a column to the stringtable
		AddListBoxColumn(gadget,temp_slot,nwidth)
		SetListBoxHeading(gadget,temp_slot,ntitle)
		totalcolumns :+ 1
		SetListBoxColumnWidth(gadget,0,0)
		Return temp_slot - 1
	End Method
	
	Method addrow:Int(ndata:String[])
		Local temp_slot:Int = totalrows
		Local temp_end:Int = ndata.length
		Local temp_column:Int
		
		'make sure not adding row past column total
		If temp_end > totalcolumns temp_end = totalcolumns
		
		'add the row
		AddGadgetItem(gadget,temp_slot)
		totalrows :+ 1
		
		For temp_column = 1 To totalcolumns
			If temp_column <= temp_end
				ModifyListBoxItem(gadget,temp_slot,temp_column,ndata[temp_column-1])
			Else
				'insert blank cell
				ModifyListBoxItem(gadget,temp_slot,temp_column,"")
			End If
		Next
		ModifyListBoxItem(gadget,temp_slot,0,"")
		SetListBoxColumnWidth(gadget,0,0)
		
		'return row number
		Return totalrows - 1
	End Method
	
	Function create:gstringtable(nrect[],nparent:ggadget=Null)
		Local stringtable:gstringtable = New gstringtable
		Local temp_parent:tgadget = Null
		If nparent <> Null temp_parent = nparent.getclient()

		'create the listbox
		stringtable.gadget = CreateListBox(nrect[0],nrect[1],nrect[2],nrect[3],temp_parent)
		
		'bootstrap it with listbox doofa!
		SetListBoxExtendedStyle(stringtable.gadget,LVS_EX_GRIDLINES)
		
		'update first column
		SetListBoxHeading(stringtable.gadget,0,"")
		SetListBoxColumnWidth(stringtable.gadget,0,0)
		stringtable.totalcolumns = 0
		
		Return stringtable
	End Function
End Type

Type glistbox Extends ggadget
	Field icons:ticonstrip
	
	Function create:glistbox(nrect[],nparent:ggadget=Null,nicons:Object=Null)
		Local listbox:glistbox = New glistbox
		Local temp_parent:tgadget = Null
		If nparent <> Null temp_parent = nparent.getclient()

		listbox.gadget = CreateListBox(nrect[0],nrect[1],nrect[2],nrect[3],temp_parent)
		
		'setup icon strip
		Local iconstrip:ticonstrip = ticonstrip(nicons)
		If iconstrip = Null
			listbox.icons = LoadIconStrip(String(nicons))
		Else
			listbox.icons = ticonstrip(nicons)
		End If
		If listbox.icons SetGadgetIconStrip(listbox.gadget,listbox.icons)
		Return listbox
	End Function
End Type

Type gtoolbar Extends ggadget
	Field clip:tgadget
	Field toolbar:tgadget
	
	Method hastgadget:Int(ngadget:tgadget)
		If toolbar = ngadget Or gadget = ngadget Return True
	End Method
	
	Method enableitem:Int(nbutton:Int)
		EnableGadgetItem(toolbar,nbutton)
	End Method
	
	Method disableitem:Int(nbutton:Int)
		DisableGadgetItem(toolbar,nbutton)
	End Method
	
	Method getgadget:tgadget()
		Return toolbar
	End Method
	
	Function create:gtoolbar(nrect[],nparent:ggadget=Null,nicons:String="",ntips:String[]=Null)
		Local toolbar:gtoolbar = New gtoolbar
		Local temp_parent:tgadget = Null
		
		'setup parent gadget
		If nparent <> Null temp_parent = nparent.getclient()
		
		'create gadgets
		toolbar.gadget = CreatePanel(nrect[0],nrect[1],nrect[2],nrect[3],temp_parent)
		toolbar.clip = CreatePanel(0,-2,ClientWidth(toolbar.gadget),ClientHeight(toolbar.gadget)+2,toolbar.gadget)
		toolbar.toolbar = CreateToolBar(nicons,0,0,ClientWidth(toolbar.clip),ClientHeight(toolbar.clip),toolbar.clip)
		
		'set gadget layouts
		SetGadgetLayout(toolbar.clip,EDGE_ALIGNED,EDGE_ALIGNED,EDGE_ALIGNED,EDGE_ALIGNED)
		SetGadgetLayout(toolbar.toolbar,EDGE_ALIGNED,EDGE_ALIGNED,EDGE_ALIGNED,EDGE_ALIGNED)
		
		'set tips
		If ntips SetToolBarTips(toolbar.toolbar,ntips)
		
		'return toolbar
		Return toolbar
	End Function
End Type

Type gtreeview Extends ggadget
	Field icons:ticonstrip
	
	Method clear:Int()
		
	End Method
	
	Method additem:gitem(ntext:String,nicon:Int=-1,nextra:Object=Null,nid:String="")
		'create new item
		Local temp_item:gtreeviewnode = New gtreeviewnode
		temp_item.id = nid
		temp_item.text = ntext
		temp_item.extra = AddTreeViewNode(ntext,TreeViewRoot(gadget),nicon)
		temp_item.parent = Null
		temp_item.slot:Int = items.length
		
		temp_item.icon = nicon
		
		items = items[..temp_item.slot+1]
		items[temp_item.slot] = temp_item
		Return temp_item
	End Method
	
	Function create:gtreeview(nrect[],nparent:ggadget=Null,nicons:Object=Null)
		Local treeview:gtreeview = New gtreeview
		Local temp_parent:tgadget = Null
		
		'setup parent gadget
		If nparent <> Null temp_parent = nparent.getclient()
		
		'create treeview
		treeview.gadget = CreateTreeView(nrect[0],nrect[1],nrect[2],nrect[3],temp_parent)
		
		'setup icon strip
		Local iconstrip:ticonstrip = ticonstrip(nicons)
		If iconstrip = Null
			treeview.icons = LoadIconStrip(String(nicons))
		Else
			treeview.icons = ticonstrip(nicons)
		End If
		If treeview.icons SetGadgetIconStrip(treeview.gadget,treeview.icons)
		
		Return treeview
	End Function
End Type

Type gtextarea Extends ggadget
	Method setbackgroundcolor:Int(nr:Int,ng:Int,nb:Int)
		SetTextAreaColor(gadget,nr,ng,nb,True)
	End Method
	
	Method settextcolor:Int(nr:Int,ng:Int,nb:Int)
		SetTextAreaColor(gadget,nr,ng,nb,False)
	End Method
	
	Method addline:Int(ntext:String,autoscroll:Int=False)
		AddTextAreaText(gadget,ntext+"~n")
	End Method

	Method addtext:Int(ntext:String)
		AddTextAreaText(gadget,ntext)
	End Method
	
	Method cleartext:Int()
		SetTextAreaText(gadget,"")
	End Method
	
	Function create:gtextarea(nrect[],nparent:ggadget=Null,nreadonly:Int=False)
		Local textarea:gtextarea = New gtextarea
		Local temp_parent:tgadget = Null
		If nparent <> Null temp_parent = nparent.getclient()
		If nreadonly = False
			textarea.gadget = CreateTextArea(nrect[0],nrect[1],nrect[2],nrect[3],temp_parent)
		Else
			textarea.gadget = CreateTextArea(nrect[0],nrect[1],nrect[2],nrect[3],temp_parent,TEXTAREA_READONLY)
		End If
		Return textarea
	End Function
End Type

Type gtextfield Extends ggadget
	Function create:gtextfield(nrect[],nparent:ggadget=Null)
		Local textfield:gtextfield = New gtextfield
		Local temp_parent:tgadget = Null
		
		'setup parent gadget
		If nparent <> Null temp_parent = nparent.getclient()
		
		'create treeview
		textfield.gadget = CreateTextField(nrect[0],nrect[1],nrect[2],nrect[3],temp_parent)
		
		Return textfield
	End Function
End Type

Type gbutton Extends ggadget
	Function create:gbutton(ntitle:String,nrect[],nparent:ggadget=Null)
		Local button:gbutton = New gbutton
		Local temp_parent:tgadget = Null
		
		'setup parent gadget
		If nparent <> Null temp_parent = nparent.getclient()
		
		'create treeview
		button.gadget = CreateButton(ntitle,nrect[0],nrect[1],nrect[2],nrect[3],temp_parent,BUTTON_PUSH)
		
		Return button
	End Function
End Type

Type gprogressbar Extends ggadget
	Method setprogress:Int(nprogress:Float)
		UpdateProgBar(gadget,nprogress)
	End Method

	Function create:gprogressbar(nrect[],nparent:ggadget=Null)
		Local progressbar:gprogressbar = New gprogressbar
		Local temp_parent:tgadget = Null
		
		'setup parent gadget
		If nparent <> Null temp_parent = nparent.getclient()
		
		'create treeview
		progressbar.gadget = CreateProgBar(nrect[0],nrect[1],nrect[2],nrect[3],temp_parent,BUTTON_PUSH)
		
		Return progressbar
	End Function
End Type





Type gitem
	Field id:String
	Field slot:Int
	Field text:String
	Field extra:Object
	Field parent:gitem
	Field items:gitem[]
	Field x:Int
	Field y:Int
	Field z:Int
	Field string1:String
	Field string2:String
	Field string3:String
	Field string4:String
	Field int1:Int
	Field int2:Int
	Field int3:Int
	Field int4:Int
	
	Method expand:Int()
	End Method
	
	Method collapse:Int()
	End Method
	
	Method setint1:Int(nint:Int)
		int1 = nint
	End Method

	Method setint2:Int(nint:Int)
		int2 = nint
	End Method
	
	Method setint3:Int(nint:Int)
		int3 = nint
	End Method
	
	Method setint4:Int(nint:Int)
		int4 = nint
	End Method
	
	Method setstring1:Int(nstring:String)
		string1 = nstring
	End Method
	
	Method setstring2:Int(nstring:String)
		string2 = nstring
	End Method
	
	Method setstring3:Int(nstring:String)
		string3 = nstring
	End Method
	
	Method setstring4:Int(nstring:String)
		string4 = nstring
	End Method
	
	Method settext:Int(ntext:String)
		text = ntext
	End Method
	
	Method gettext:String()
		Return text
	End Method
	
	Method setx:Int(nx:Int)
		x = nx
	End Method
	
	Method sety:Int(ny:Int)
		y = ny
	End Method
	
	Method setz:Int(nz:Int)
		z = nz
	End Method
	
	Method setid:Int(nid:String)
		id = nid
	End Method
	
	Method getx:Int()
		Return x
	End Method
	
	Method gety:Int()
		Return y
	End Method
	
	Method getz:Int()
		Return z
	End Method
	
	Method getint1:Int()
		Return int1
	End Method
	
	Method getint2:Int()
		Return int2
	End Method
	
	Method getint3:Int()
		Return int3
	End Method
	
	Method getint4:Int()
		Return int4
	End Method
	
	Method getstring1:String()
		Return string1
	End Method
	
	Method getstring2:String()
		Return string2
	End Method
	
	Method getstring3:String()
		Return string3
	End Method
	
	Method getstring4:String()
		Return string4
	End Method
	
	Method clear:Int()
		'this will clear all items from this node
		items = items[..0]
	End Method
	
	Method remove()
		'remove the item from the parents item list
		If parent <> Null
			Local temp_i:Int
			'shift data across
			For temp_i = slot+1 Until parent.items.length
				parent.items[temp_i-1] = parent.items[temp_i]
				parent.items[temp_i-1].slot = temp_i-1
			Next
			'resize array
			parent.items = parent.items[..parent.items.length-1]
		End If
	End Method
	
	Method finditem:gitem(ntext:String)
		'check for matching item text
		If ntext = text Or ntext = id Return Self
		'call to update from all items of item
		Local temp_return:gitem
		Local temp_item:gitem
		For temp_item = EachIn items
			temp_return = temp_item.finditem(ntext)
			If temp_return Return temp_return
		Next
	End Method
	
	Method additem:gitem(ntext:String,nicon:Int=-1,nextra:Object=Null,nid:String="")
		'create new item
		Local temp_item:gitem = New gitem
		temp_item.id = nid
		temp_item.text = ntext
		temp_item.extra = nextra
		temp_item.parent = Null
		temp_item.slot = items.length

		items = items[..temp_item.slot+1]
		items[temp_item.slot] = temp_item
		Return temp_item
	End Method
End Type

Type gtreeviewnode Extends gitem
	Field icon:Int

	Method expand:Int()
		ExpandTreeViewNode(tgadget(extra))
	End Method
	
	Method collapse:Int()
		CollapseTreeViewNode(tgadget(extra))
	End Method

	Method clear:Int()
		Local temp_i:Int
		'remove all treeview nodes
		For temp_i = 0 Until items.length
			FreeTreeViewNode(tgadget(items[temp_i].extra))
		Next
		'remove all traces of item nodes so grabage will collect
		items = items[..0]
	End Method
	
	Method remove:Int()
		FreeTreeViewNode(tgadget(extra))
		'remove the item from the parents item list
		If parent <> Null
			Local temp_i:Int
			'shift data across
			For temp_i = slot+1 Until parent.items.length
				parent.items[temp_i-1] = parent.items[temp_i]
				parent.items[temp_i-1].slot = temp_i-1
			Next
			'resize array
			parent.items = parent.items[..parent.items.length-1]
		End If
	End Method
	
	Method additem:gitem(ntext:String,nicon:Int=-1,nextra:Object=Null,nid:String="")
		'create new item
		Local temp_item:gtreeviewnode = New gtreeviewnode
		temp_item.id = nid
		temp_item.text = ntext
		temp_item.extra = AddTreeViewNode(ntext,tgadget(extra),nicon)
		temp_item.parent = Self
		temp_item.slot = items.length
		
		temp_item.icon = nicon

		items = items[..temp_item.slot+1]
		items[temp_item.slot] = temp_item
		Return temp_item
	End Method
	
	Method settext:Int(ntext:String)
		Local temp_gadget:tgadget = tgadget(extra)
		text = ntext
		ModifyTreeViewNode(temp_gadget,ntext,icon)
	End Method
	
	Method gettext:String()
		Return GadgetText(tgadget(extra))
	End Method
End Type


tdriveinfo.bmx
Strict

Import brl.linkedlist

Const DRIVE_UNKNOWN:Int = 0     'The drive Type cannot be determined.
Const DRIVE_NO_ROOT_DIR:Int = 1 'The root path is invalid; For example, there is no volume is mounted at the path.
Const DRIVE_REMOVABLE:Int = 2   'The drive has removable media; For example, a floppy drive, thumb drive, Or flash card reader.
Const DRIVE_FIXED:Int = 3       'The drive has fixed media; For example, a hard drive Or flash drive.
Const DRIVE_REMOTE:Int = 4      'The drive is a remote (network) drive.
Const DRIVE_CDROM:Int = 5       'The drive is a CD-ROM drive.
Const DRIVE_RAMDISK:Int = 6     'The drive is a RAM disk.

Extern "win32"
	Function _GetDiskFreeSpaceEx(lpdirectoryname:Byte Ptr,lpfreebytesavailable:Byte Ptr,lptotalnumberofbytes:Byte Ptr,lptotalnumberoffreebytes:Byte Ptr) = "GetDiskFreeSpaceExA@16"
	Function _GetLogicalDriveStrings(nbufferlength:Int,lpbuffer:Byte Ptr) = "GetLogicalDriveStringsA@8"
	Function _GetDriveType(lprootpathname:Byte Ptr) = "GetDriveTypeA@4"
End Extern

Type tdriveinfo
	Const buffersize:Int = 256
	
	Global buffer:Byte[tdriveinfo.buffersize]
	
	Field drivetype:Int
	Field availablebytes:Long
	Field totalbytes:Long
	Field freebytes:Long
	Field label:String
	
	Function _setcstring:Byte Ptr(nstring:String)
		Local temp_i:Int
		'write string
		For temp_i = 0 Until nstring.length
			tdriveinfo.buffer[temp_i] = nstring[temp_i]
		Next
		'write null
		tdriveinfo.buffer[nstring.length] = 0
		
		Return tdriveinfo.buffer
	End Function
	
	Function _getfromstring:String()
		Local temp_i:Int
		Local temp_string:String
		For temp_i = 0 Until tdriveinfo.buffersize
			If temp_i = tdriveinfo.buffersize-1 Or tdriveinfo.buffer[temp_i] = 0
			Else
				temp_string = temp_string + Chr(tdriveinfo.buffer[temp_i])
			End If
		Next
		Return temp_string
	End Function
	
	Function _bytestospacestring:String(nspace:Long)
		If nspace < 1024 '1 k
			Return tdriveinfo._trimfloat(nspace)+" b"
		ElseIf nspace < 1048576 '1 meg
			Return tdriveinfo._trimfloat(nspace / 1024.0)+" k"
		ElseIf nspace < 1073741824 '1 gig
			Return tdriveinfo._trimfloat(nspace / 1048576.0)+" meg"
		Else
			Return tdriveinfo._trimfloat(nspace / 1073741824.0)+" gig"
		End If
	End Function
	
	Function _trimfloat:String(nfloat:Float,nlength:Int = 2)
		Local temp_pos:Int
		Local temp_string:String = String(nFloat)
		temp_pos = temp_string.find(".")
		If temp_pos > -1 Return temp_string[0..temp_pos+1+nlength]
		Return temp_string
	End Function
	
	Method PercentFree:Float()
		Return (100.0 / totalbytes) * (totalbytes - freebytes)
	End Method
	
	Method PercentUsed:Float()
		Return (100.0 / totalbytes) * availablebytes
	End Method
	
	Method CapacityString:String()
		Return tdriveinfo._bytestospacestring(totalbytes)
	End Method
	
	Method AvailableString:String()
		Return tdriveinfo._bytestospacestring(availablebytes)
	End Method
	
	Method UsedString:String()
		Return tdriveinfo._bytestospacestring(totalbytes - freebytes)
	End Method
	
	Method Capacity:Long()
		Return totalbytes
	End Method
	
	Method Available:Long()
		Return availablebytes
	End Method
	
	Method Used:Long()
		Return totalbytes - freebytes
	End Method
End Type

Function GetAllDrives:TList(nfilter:Int=-1,ngetdiskspace:Int=True)
	Local temp_list:TList = CreateList()
	Local temp_drives:String
	Local temp_label:String
	Local temp_i:Int
	Local temp_type:Int
	
	'get list of drives
	_GetLogicalDriveStrings(tdriveinfo.buffersize,tdriveinfo.buffer)
	temp_drives = tdriveinfo._getfromstring()
	'read in list of drives
	For temp_i = 0 Until temp_drives.length Step 3
		temp_label = temp_drives[temp_i..temp_i+3]
		
		'create tdriveinfo type
		temp_type = _GetDriveType(tdriveinfo._setcstring(temp_label))
		If (nfilter = -1 And (temp_type = DRIVE_CDROM Or temp_type = DRIVE_FIXED Or temp_type = DRIVE_RAMDISK Or temp_type = DRIVE_REMOVABLE Or temp_type = DRIVE_REMOTE)) Or temp_type = nfilter
			'get drive space details
			Local temp_disk:tdriveinfo = New tdriveinfo
			'get space details
			If ngetdiskspace _GetDiskFreeSpaceEx(tdriveinfo._setcstring(temp_label),Varptr(temp_disk.availablebytes),Varptr(temp_disk.totalbytes),Varptr(temp_disk.freebytes))
			'set other details
			temp_disk.label = temp_label.tolower()
			temp_disk.drivetype = temp_type
			
			temp_list.addlast(temp_disk)
		End If
	Next
	
	Return temp_list
End Function

Function GetCDDrives:TList()
	Return GetAllDrives(DRIVE_CDROM)
End Function

Function GetHardDrives:TList()
	Return GetAllDrives(DRIVE_FIXED)
End Function

Function GetRamDrives:TList()
	Return GetAllDrives(DRIVE_RAMDISK)
End Function

Function GetRemoveableDrives:TList()
	Return GetAllDrives(DRIVE_REMOVABLE)
End Function

Function GetNetworkDrives:TList()
	Return GetAllDrives(DRIVE_REMOTE)
End Function


multicolumnlistbox.bmx
Import BRL.Win32MaxGUI

Extern "win32"
   Function SendMessage:Int(hWnd:Int,MSG:Int,wParam:Int,lParam:Int) = "SendMessageA@16"
   Function SetWindowLong:Int(hWnd:Int,nIndex:Int,lNewLong:Int) = "SetWindowLongA@12"
   Function GetWindowLong:Int(hWnd:Int,nIndex:Int) = "GetWindowLongA@8"
End Extern

Const LBS_NOTIFY:Int            = $1
Const LBS_SORT:Int              = $2
Const LBS_NOREDRAW:Int          = $4
Const LBS_MULTIPLESEL:Int       = $8
Const LBS_OWNERDRAWFIXED:Int    = $10
Const LBS_OWNERDRAWVARIABLE:Int = $20
Const LBS_HASSTRINGS:Int        = $40
Const LBS_USETABSTOPS:Int       = $70
Const LBS_NOINTEGRALHEIGHT:Int  = $100
Const LBS_MULTICOLUMN:Int       = $200
Const LBS_WANTKEYBOARDINPUT:Int = $400
Const LBS_EXTENDEDSEL:Int       = $800
Const LBS_DISABLENOSCROLL:Int   = $100
Const LBS_NODATA:Int            = $2000
Const LBS_NOSEL:Int             = $4000
Const LBS_STANDARD:Int          = $a00003

Const LVM_FIRST:Int = $1000
Const LVM_GETITEMA:Int          = (LVM_FIRST+5)
Const LVM_SETITEMA:Int          = (LVM_FIRST+6)
Const LVM_INSERTITEMA:Int       = (LVM_FIRST+7)
Const LVM_DELETEITEM:Int        = (LVM_FIRST+8)
Const LVM_DELETEALLITEMS:Int    = (LVM_FIRST+9)
Const LVM_GETCALLBACKMASK:Int   = (LVM_FIRST+10)
Const LVM_SETCALLBACKMASK:Int   = (LVM_FIRST+11)
Const LVM_GETCOLUMNA:Int        = (LVM_FIRST+25)
Const LVM_SETCOLUMNA:Int        = (LVM_FIRST+26)
Const LVM_INSERTCOLUMNA:Int     = (LVM_FIRST+27)
Const LVM_DELETECOLUMN:Int      = (LVM_FIRST+28)
Const LVM_GETCOLUMNWIDTH:Int    = (LVM_FIRST+29)
Const LVM_SETCOLUMNWIDTH:Int    = (LVM_FIRST+30)
Const LVM_GETTEXTCOLOR:Int      = (LVM_FIRST+35)
Const LVM_SETTEXTCOLOR:Int      = (LVM_FIRST+36)
Const LVM_GETTEXTBKCOLOR:Int    = (LVM_FIRST+37)
Const LVM_SETTEXTBKCOLOR:Int    = (LVM_FIRST+38)
Const LVM_SORTITEMS:Int         = (LVM_FIRST+48)
Const LVM_SETLISTBOXEXSTYLE:Int = (LVM_FIRST+54)
Const LVM_GETITEMW:Int          = (LVM_FIRST+75)
Const LVM_SETITEMW:Int          = (LVM_FIRST+76)
Const LVM_INSERTITEMW:Int       = (LVM_FIRST+77)
Const LVM_GETCOLUMNW:Int        = (LVM_FIRST+95)
Const LVM_SETCOLUMNW:Int        = (LVM_FIRST+96)
Const LVM_INSERTCOLUMNW:Int     = (LVM_FIRST+97)

Const LVS_EX_GRIDLINES:Int        = $1
Const LVS_EX_SUBITEMIMAGES:Int    = $2
Const LVS_EX_CHECKBOXES:Int       = $4
Const LVS_EX_TRACKSELECT:Int      = $8
Const LVS_EX_HEADERDRAGDROP:Int   = $10
Const LVS_EX_FULLROWSELECT:Int    = $20
Const LVS_EX_ONECLICKACTIVATE:Int = $40
Const LVS_EX_TWOCLICKACTIVATE:Int = $80
Const LVS_EX_FLATSB:Int           = $100
Const LVS_EX_REGIONAL:Int         = $200
Const LVS_EX_INFOTIP:Int          = $400
Const LVS_EX_UNDERLINEHOT:Int     = $800
Const LVS_EX_UNDERLINECOLD:Int    = $1000
Const LVS_EX_MULTIWORKAREAS:Int   = $2000
Const LVS_EX_LABELTIP:Int         = $4000
Const LVS_EX_BORDERSELECT:Int     = $8000
Const LVS_EX_DOUBLEBUFFER:Int     = $10000
Const LVS_EX_HIDELABELS:Int       = $20000
Const LVS_EX_SINGLEROW:Int        = $40000
Const LVS_EX_SNAPTOGRID:Int       = $80000
Const LVS_EX_SIMPLESELECT:Int     = $100000

Const LVCF_FMT:Int     = $1
Const LVCF_WIDTH:Int   = $2
Const LVCF_TEXT:Int    = $4
Const LVCF_SUBITEM:Int = $8
Const LVCF_IMAGE:Int   = $10
Const LVCF_ORDER:Int   = $20

Const LVIF_TEXT:Int        = $1
Const LVIF_IMAGE:Int       = $2
Const LVIF_PARAM:Int       = $4
Const LVIF_STATE:Int       = $8
Const LVIF_INDENT:Int      = $10
Const LVIF_GROUPID:Int     = $100
Const LVIF_COLUMNS:Int     = $200
Const LVIF_NORECOMPUTE:Int = $800 
Const LVIF_DI_SETITEM:Int  = $1000

Const LVS_ALIGNTOP:Int        = $0
Const LVS_ICON:Int            = $0
Const LVS_REPORT:Int          = $1
Const LVS_SMALLICON:Int       = $2
Const LVS_LIST:Int            = $3
Const LVS_TYPEMASK:Int        = $3
Const LVS_SINGLESEL:Int       = $4
Const LVS_SHOWSELALWAYS:Int   = $8
Const LVS_SORTASCENDING:Int   = $10
Const LVS_SORTDESCENDING:Int  = $20
Const LVS_SHAREIMAGELISTS:Int = $40
Const LVS_NOLABELWRAP:Int     = $80
Const LVS_AUTOARRANGE:Int     = $100
Const LVS_EDITLABELS:Int      = $200
Const LVS_OWNERDRAWFIXED:Int  = $400
Const LVS_ALIGNLEFT:Int       = $800
Const LVS_ALIGNMASK:Int       = $C00
Const LVS_NOSCROLL:Int        = $2000
Const LVS_NOCOLUMNHEADER:Int  = $4000
Const LVS_NOSORTHEADER:Int    = $8000
Const LVS_TYPESTYLEMASK:Int   = $FC00

Const GWL_STYLE:Int = -$10

Type  LVCOLUMN 
   Field mask:Int
   Field fmt:Int
   Field cx:Int
   Field pszText:Byte Ptr
   Field cchTextMax:Int
   Field iSubItem:Int
EndType

Type LVITEM
   Field mask:Int
   Field iItem:Int
   Field iSubItem:Int
   Field iState:Int
   Field stateMask:Int
   Field pszText:Byte Ptr
   Field cchTextMax:Int
   Field iImage:Int
   Field lParam:Byte Ptr
   Field iIndent:Int
   Field iGroupId:Int
   Field cColumns:Int
   Field puColumns:Int
EndType

Function AddListBoxColumn(ListBox:TGadget,ColumnNr:Int,Width:Int)
   Local ListBoxHwnd:Int = QueryGadget(ListBox,QUERY_HWND)
   Local Column:LVColumn = New LVColumn
   Column.mask = LVCF_WIDTH
   Column.cx = Width
   SendMessageA(ListBoxHwnd,LVM_INSERTCOLUMNA,ColumnNr,Int(Byte Ptr Column))
End Function

Function SetListBoxHeading(ListBox:TGadget,ColumnNr:Int,HeadingText:String,Style:Int=0)
   Local ListBoxHwnd:Int = QueryGadget(ListBox,QUERY_HWND)
   Local Column:LVColumn = New LVColumn
   Column.mask = LVCF_TEXT | LVCF_FMT
   Column.fmt = Style
   Column.pszText = HeadingText.ToCString()
   Local ListBoxStyle:Int = GetWindowLongA(ListBoxHwnd,GWL_STYLE)
   If (ListBoxStyle & LVS_NOCOLUMNHEADER ) Then
      ListBoxStyle = ListBoxStyle ~ LVS_NOCOLUMNHEADER
      SetWindowLongA(ListBoxHwnd,GWL_STYLE,ListBoxStyle)
   EndIf
   SendMessageA(ListBoxHwnd,LVM_SETCOLUMNA,ColumnNr,Int(Byte Ptr Column))
End Function

Function SetListBoxColumnWidth(ListBox:TGadget,ColumnNr:Int,Width:Int)
   Local ListBoxHwnd:Int = QueryGadget(ListBox,QUERY_HWND)
   Local Column:LVColumn = New LVColumn
   Column.mask = LVCF_WIDTH
   Column.cx = Width
   SendMessageA(ListBoxHwnd,LVM_SETCOLUMNA,ColumnNr,Int(Byte Ptr Column))
End Function

Function ModifyListBoxItem(ListBox:TGadget,RowNr:Int,ColumnNr:Int,Text:String)
   Local ListBoxHwnd:Int = QueryGadget(ListBox,QUERY_HWND)
   Local Item:LVItem = New LVItem
   Item.mask = LVIF_TEXT
   Item.iSubItem = ColumnNr
   Item.iItem = RowNr
   Item.pszText = Text.ToCString()
   Item.cchTextMax = Text.Length+1
   SendMessageA(ListBoxHwnd,LVM_SETITEMA,0,Int(Byte Ptr Item))
End Function

Function GetListBoxItemText:String(ListBox:TGadget,RowNr:Int,ColumnNr:Int)
   Local ListBoxHwnd:Int = QueryGadget(ListBox,QUERY_HWND)
   Local ReturnText:String
   Local TextBank:TBank = CreateBank(1024)
   Local Item:LVItem = New LVItem
   Item.mask = LVIF_Text
   Item.iSubItem = ColumnNr
   Item.iItem = RowNr
   Item.pszText = BankBuf(TextBank)
   Item.cchTextMax = 255
   SendMessageA(ListBoxHwnd,LVM_GETITEMA,0,Int(Byte Ptr Item))
   If Item.pszText <> Null Then
      ReturnText = ReturnText.FromCString(Item.pszText)
   EndIf
   Return ReturnText
End Function

Function SetListBoxExtendedStyle(ListBox:TGadget,Style:Int)
   Local ListBoxHwnd:Int = QueryGadget(ListBox,QUERY_HWND)
   SendMessageA(ListBoxHwnd,LVM_SETLISTBOXEXSTYLE,Style,Style)
End Function