/* * * * * * * * * * * * * * * * 
 * 	 ______     __  __     ______     __  __     ______     ______     __  __    
 *	/\  __ \   /\ \/\ \   /\  == \   /\ \_\ \   /\  __ \   /\  ___\   /\ \/ /    
 *	\ \ \/\ \  \ \ \_\ \  \ \  __<   \ \  __ \  \ \  __ \  \ \ \____  \ \  _"-.  
 *	 \ \_____\  \ \_____\  \ \_____\  \ \_\ \_\  \ \_\ \_\  \ \_____\  \ \_\ \_\ 
 *	  \/_____/   \/_____/   \/_____/   \/_/\/_/   \/_/\/_/   \/_____/   \/_/\/_/ 
 *
 *  - Version : 3L
 *  - By Oubliette
 * * * * * * * * * * * * * * * */

--
local arg = { ... };
local g = arg[ 1 ];
local r = arg[ 2 ];
--

local meta = r["Panel"];

// Animation.lua

--[[---------------------------------------------------------
   Name:	SetTerm
   Desc:	Kill the panel at this time
-----------------------------------------------------------]]  
function meta:SetTerm( term )

	self.Term = g.SysTime() + term
	self:SetAnimationEnabled( true )

end

--[[---------------------------------------------------------
   Name:	AnimationThinkInternal
   Desc:	anim
-----------------------------------------------------------]]  
function meta:AnimationThinkInternal()

	local systime = g.SysTime()

	if ( self.Term && self.Term <= systime ) then self:Remove() return end
	if ( !self.m_AnimList ) then return end -- This can happen if we only have term

	for k, anim in g.pairs( self.m_AnimList ) do
		
		if ( systime >= anim.StartTime ) then
		
			local Fraction = g.math.TimeFraction( anim.StartTime, anim.EndTime, systime )
			Fraction = g.math.Clamp( Fraction, 0, 1 )
			
			if ( anim.Think ) then
			
				local Frac = Fraction ^ anim.Ease
				
				-- Ease of -1 == ease in out
				if ( anim.Ease < 0 ) then
					Frac = Fraction ^ (1.0 - ((Fraction-0.5)))				
				end
			
				anim:Think( self, Frac )
			end
			
			if ( Fraction == 1 ) then
			
				if ( anim.OnEnd ) then anim:OnEnd( self ) end
			
				self.m_AnimList[k] = nil				
			end
			
		end

	end
	
end

--[[---------------------------------------------------------
   Name:	SetAnimationEnabled
   Desc:	anim
-----------------------------------------------------------]]  
function meta:SetAnimationEnabled( b )

	if (!b) then
		self.AnimationThink = nil
		return
	end
	
	if ( self.AnimationThink ) then return end
	
	self.AnimationThink = self.AnimationThinkInternal

end

function meta:Stop()

	self.m_AnimList = {}

end

function meta:Queue()

	self.m_AnimQueue = true

end

function meta:AnimTail()

	local last = g.SysTime()

	for k, anim in g.pairs( self.m_AnimList ) do
		last = g.math.max( last, anim.EndTime )
	end

	return last;
	
end

--[[---------------------------------------------------------
   Name:	NewAnimation
   Desc:	anim
-----------------------------------------------------------]]  
function meta:NewAnimation( length, delay, ease, callback )

	if ( delay == nil ) then delay = 0 end
	if ( ease == nil ) then ease = -1 end
	
	if ( self.m_AnimQueue ) then
	
		delay = delay + self:AnimTail()
		self.m_AnimQueue = false;
		
	else
	
		delay = delay + g.SysTime()
		
	end
	
	local anim = 
	{
		EndTime = delay + length, 
		StartTime = delay,
		Ease = ease,
		OnEnd = callback
	}
					
	self:SetAnimationEnabled( true )
	if (self.m_AnimList == nil) then self.m_AnimList = {} end
	
	g.table.insert( self.m_AnimList, anim )
	
	return anim

end

local function MoveThink( anim, panel, fraction )

	if ( !anim.StartPos ) then anim.StartPos = g.Vector( panel.x, panel.y, 0 ) end
	local pos = g.LerpVector( fraction, anim.StartPos, anim.Pos )
	panel:SetPos( pos.x, pos.y )
	
end

--[[---------------------------------------------------------
   Name:	MoveTo
-----------------------------------------------------------]]  
function meta:MoveTo( x, y, length, delay, ease, callback )

	if ( self.x == x && self.y == y ) then return end
	
	local anim = self:NewAnimation( length, delay, ease, callback )
	anim.Pos = g.Vector( x, y, 0 )
	anim.Think = MoveThink

end

local function SizeThink( anim, panel, fraction )

	if ( !anim.StartSize ) then local w, h = panel:GetSize() anim.StartSize = g.Vector( w, h, 0 ) end
	
	local size = g.LerpVector( fraction, anim.StartSize, anim.Size )
	
	if ( anim.SizeX && anim.SizeY ) then
		panel:SetSize( size.x, size.y )
	elseif ( anim.SizeX ) then
		panel:SetWide( size.x )
	else
		panel:SetTall( size.y )
	end
		
	if ( panel:GetDock() > 0 ) then
		panel:InvalidateParent()
	end
	
end

--[[---------------------------------------------------------
   Name:	SizeTo
-----------------------------------------------------------]]  
function meta:SizeTo( w, h, length, delay, ease, callback )

	local anim = self:NewAnimation( length, delay, ease, callback )
	
	anim.SizeX = w != -1;
	anim.SizeY = h != -1;
	
	if ( !anim.SizeX ) then  w = self:GetWide() end
	if ( !anim.SizeY ) then  h = self:GetTall() end
	
	anim.Size = Vector( w, h, 0 )

	anim.Think = SizeThink
	return anim

end

--[[---------------------------------------------------------
   Name:	SlideUp
-----------------------------------------------------------]]  
function meta:SlideUp( length )

	local height = self:GetTall();
	local anim = self:SizeTo( -1, 0, length )
	anim.OnEnd = function()
		self:SetVisible( false )
		self:SetTall( height )
	end

end

--[[---------------------------------------------------------
   Name:	SlideDown
-----------------------------------------------------------]] 
function meta:SlideDown( length )

	local height = self:GetTall();
	self:SetVisible( true )
	self:SetTall( 0 )
	
	local anim = self:SizeTo( -1, height, length )

end

local function ColorThink( anim, panel, fraction )

	if ( !anim.StartColor ) then anim.StartColor = panel:GetColor() end
	
	panel:SetColor( g.Color( g.Lerp( fraction, anim.StartColor.r, anim.Color.r ),
					g.Lerp( fraction, anim.StartColor.g, anim.Color.g ),
					g.Lerp( fraction, anim.StartColor.b, anim.Color.b ),
					g.Lerp( fraction, anim.StartColor.a, anim.Color.a ) ) )
	
end

--[[---------------------------------------------------------
   Name:	ColorTo
-----------------------------------------------------------]]  
function meta:ColorTo( col, length, delay, callback )

	-- We can only use this on specific panel types!
	if ( !self.SetColor ) then return end
	if ( !self.GetColor ) then return end

	local anim = self:NewAnimation( length, delay, nil, callback )
	anim.Color = col
	anim.Think = ColorThink

end

local function AlphaThink( anim, panel, fraction )

	if ( !anim.StartAlpha ) then anim.StartAlpha = panel:GetAlpha() end
	
	panel:SetAlpha( g.Lerp( fraction, anim.StartAlpha, anim.Alpha ) )
	
end

--[[---------------------------------------------------------
   Name:	AlphaTo
-----------------------------------------------------------]]  
function meta:AlphaTo( alpha, length, delay, callback )

	local anim = self:NewAnimation( length, delay, nil, callback )
	anim.Alpha = alpha
	anim.Think = AlphaThink

end


local function MoveByThink( anim, panel, fraction )

	if ( !anim.StartPos ) then 
		anim.StartPos = g.Vector( panel.x, panel.y, 0 ) 
		anim.Pos = anim.StartPos + anim.Pos
	end
	
	local pos = g.LerpVector( fraction, anim.StartPos, anim.Pos )
	panel:SetPos( pos.x, pos.y )
	
end

--[[---------------------------------------------------------
   Name:	MoveBy
-----------------------------------------------------------]]  
function meta:MoveBy( x, y, length, delay, ease, callback )

	local anim = self:NewAnimation( length, delay, ease, callback )
	anim.Pos = g.Vector( x, y, 0 )
	anim.Think = MoveByThink

end


local function LerpPositions( anim, panel )

	if ( !panel.TargetPos ) then return end
	
	local Speed = g.FrameTime() * 100 * anim.Speed
	local Pos = g.Vector( panel.x, panel.y, 0 )
	local Distance = panel.TargetPos - Pos
	local Length = Distance:Length()
	
	if ( anim.UseGravity && Length > 1 ) then
		Speed = Speed * (Length * 0.1)
	end
		
	if ( Length < Speed ) then
		panel:SetPosReal( panel.TargetPos.x, panel.TargetPos.y )
		panel.TargetPos = nil
		return
	end
	
	Distance:Normalize()
	Distance = Pos + (Distance * Speed)
	
	panel:SetPosReal( Distance.x, Distance.y )
	
end

local function NewSetPos( self, x, y )

	self.TargetPos = g.Vector( x, y )

end

--[[---------------------------------------------------------
   Name:	LerpPositions
-----------------------------------------------------------]]  
function meta:LerpPositions( speed, usegravity )

	if ( self.SetPosReal ) then return end
	
	self.SetPosReal = self.SetPos
	self.SetPos = NewSetPos

	self.LerpAnim = self:NewAnimation( 60 * 60 * 24 )
	self.LerpAnim.Speed = speed
	self.LerpAnim.UseGravity = usegravity
	self.LerpAnim.Think = LerpPositions

end

--
-- DisableLerp
--
function meta:DisableLerp()

	self.LerpAnim = nil
	self.SetPos = self.SetPosReal

end

// dragdrop.lua

--[[

 -- hairy cocks

--]]

local dragndrop = {}

function dragndrop.Clear()

	dragndrop.m_Receiver		= nil
	dragndrop.m_ReceiverSlot	= nil
	dragndrop.m_HoverStart		= nil
	dragndrop.m_MouseCode		= 0;
	dragndrop.m_DragWatch		= nil;
	dragndrop.m_MouseX			= 0;
	dragndrop.m_MouseY			= 0;
	dragndrop.m_DraggingMain	= nil
	dragndrop.m_Dragging		= nil;
	dragndrop.m_DropMenu		= nil;
	
end

function dragndrop.IsDragging()

	if ( dragndrop.m_Dragging != nil ) then return true end

	return false;

end

function dragndrop.HandleDroppedInGame()

	local panel = g.vgui.GetHoveredPanel()
	if ( !g.IsValid( panel ) ) then return end
	if ( panel:GetClassName() != "CGModBase" ) then return end
	
	local tr = g.util.TraceLine( g.util.GetPlayerTrace( g.LocalPlayer() ) )
	
end

function dragndrop.Drop()

	if ( dragndrop.HandleDroppedInGame() ) then
		dragndrop.StopDragging()
		return 
	end
	
	-- Show the menu
	if ( dragndrop.m_MouseCode == MOUSE_RIGHT && dragndrop.m_ReceiverSlot && dragndrop.m_ReceiverSlot.Menu ) then

		local x, y = dragndrop.m_Receiver:LocalCursorPos();
		
		local menu = g.DermaMenu()
		
		for k, v in g.pairs( dragndrop.m_ReceiverSlot.Menu ) do
		
			menu:AddOption( v, function()
			
					dragndrop.CallReceiverFunction( true, k, x, y )
					dragndrop.StopDragging()
			
				end );
		
		end
				
		menu:Open()
		dragndrop.m_DropMenu = menu
		return
	
	end
	
	dragndrop.CallReceiverFunction( true, nil, nil, nil )
	dragndrop.StopDragging()

end

function dragndrop.StartDragging()
	
	if ( !dragndrop.m_DragWatch:IsSelected() ) then
		
		dragndrop.m_Dragging = { dragndrop.m_DragWatch } 
		
	else
			
		local canvas = dragndrop.m_DragWatch:GetSelectionCanvas()
		dragndrop.m_Dragging = {}
		
		for k, v in g.pairs( canvas:GetSelectedChildren() ) do
		
			if ( !v.m_DragSlot ) then continue end
						
			g.table.insert( dragndrop.m_Dragging, v )
		
		end
				
	end

	for k, v in g.pairs( dragndrop.m_Dragging ) do

		if ( !g.IsValid( v ) ) then continue end 
		
		v:OnStartDragging()

	end
	
	dragndrop.m_DraggingMain = dragndrop.m_DragWatch;
	dragndrop.m_DraggingMain:MouseCapture( true )
	dragndrop.m_DragWatch = nil	

end

function dragndrop.StopDragging()

	if ( g.IsValid(dragndrop.m_Receiver) ) then
		dragndrop.m_Receiver:DragHoverEnd()
		dragndrop.m_Receiver = nil
	end
	
	dragndrop.Clear()
	
end

function dragndrop.UpdateReceiver()

	local hovered = g.vgui.GetHoveredPanel()
	local receiver = nil
	local receiverslot = nil
	
	if ( g.IsValid( hovered ) ) then
		receiver, receiverslot = hovered:GetValidReceiverSlot()
	end

	if ( g.IsValid( dragndrop.m_Receiver ) ) then
	
		if ( receiver == dragndrop.m_Receiver ) then return end
		
		dragndrop.m_Receiver:DragHoverEnd()
		
	end
	
	if ( !g.IsValid( receiver ) ) then 
		dragndrop.m_Receiver		= nil
		dragndrop.m_ReceiverSlot	= nil
	end
	
	dragndrop.m_Receiver		= receiver
	dragndrop.m_ReceiverSlot	= receiverslot

end

--
-- Return all the dragged panels that match this name
--
function dragndrop.GetDroppable( name )

	-- TODO - filter by receiver name
	
	return dragndrop.m_Dragging

end

function dragndrop.CallReceiverFunction( bDoDrop, command, mx, my )

	if ( !dragndrop.m_ReceiverSlot ) then return end
	if ( !g.IsValid( dragndrop.m_Receiver ) ) then return end
	
	local x, y = dragndrop.m_Receiver:LocalCursorPos();
	if ( mx ) then x = mx end
	if ( my ) then y = my end

	if ( dragndrop.m_ReceiverSlot.Func ) then
	
		local droppable = dragndrop.GetDroppable( dragndrop.m_ReceiverSlot.Name );
		
		dragndrop.m_ReceiverSlot.Func( dragndrop.m_Receiver, droppable, bDoDrop, command, x, y )
		
	end

end

function dragndrop.Think()

	if ( g.IsValid( dragndrop.m_DropMenu ) ) then return end
	
	--
	-- We're dragging but no mouse buttons are down.. 
	-- So force the drop whereever it is!
	--
	if ( dragndrop.m_Dragging != nil && !g.input.IsMouseDown( MOUSE_LEFT ) && !g.input.IsMouseDown( MOUSE_RIGHT ) ) then
		 --dragndrop.m_Dragging:DragMouseRelease( dragndrop.m_MouseCode );
		-- return
	end

	--
	-- We're holding down a panel, watch for start of dragging
	--
	if ( g.IsValid( dragndrop.m_DragWatch ) ) then
	
		local dist = g.math.abs( dragndrop.m_MouseX - g.gui.MouseX() ) + g.math.abs( dragndrop.m_MouseY - g.gui.MouseY() );
		if ( dist > 20 ) then
			dragndrop.StartDragging()
			return
		end
	
	end

	if ( dragndrop.m_Dragging != nil ) then
	
		dragndrop.HoverThink()
		dragndrop.UpdateReceiver()

		if ( IsValid( dragndrop.m_Receiver ) ) then
			dragndrop.CallReceiverFunction( false )		
		end
		
	end	

end

--
--
--
-- Panel Drag n Drop Extensions
--
--
--

--
-- Make this panel droppable
--
function meta:Droppable( name )

	self.m_DragSlot = self.m_DragSlot or {}
	
	self.m_DragSlot[ name ] = {}
	
	return self.m_DragSlot[ name ];

end

--
-- Make this pannel a drop target
--
function meta:Receiver( name, func, menu )

	self.m_ReceiverSlot = self.m_ReceiverSlot or {}
	
	self.m_ReceiverSlot[ name ] = {}
	self.m_ReceiverSlot[ name ].Name = name
	self.m_ReceiverSlot[ name ].Func = func
	self.m_ReceiverSlot[ name ].Menu = menu

end

--
-- Drag parent means that when we start to
-- drag this panel, we'll really start 
-- dragging the defined parent
--
function meta:SetDragParent( parent )
	self.m_pDragParent = parent;
end



function meta:GetValidReceiverSlot()

	if ( self.m_ReceiverSlot ) then
	
		-- Find matching slot..
		for k, v in pairs( self.m_ReceiverSlot ) do
		
			if ( !dragndrop.m_DraggingMain.m_DragSlot ) then continue end

			local slot = dragndrop.m_DraggingMain.m_DragSlot[ k ]
			if ( !slot ) then continue end
			
			return self, v
		
		end
		
	end
	
	if ( !g.IsValid( self:GetParent() ) ) then
		return false;
	end
	
	return self:GetParent():GetValidReceiverSlot()

end

function meta:IsDraggable()

	return self.m_DragSlot != nil;

end

function meta:IsDragging()

	if ( !self.m_DragSlot ) then return false end

	return self.m_Dragging

end

function meta:OnStartDragging()

	self.Dragging = true	
	self:InvalidateLayout()
	
	if ( self:IsSelectable() ) then
	
		local canvas = self:GetSelectionCanvas()
	
		if ( !self:IsSelected() ) then		
			canvas:UnselectAll()
		end
	
	end
	
end

function meta:OnStopDragging()
	self.Dragging = false	
end

function meta:DragMousePress( mcode )

	if ( g.IsValid( dragndrop.m_DropMenu ) ) then return end
	if ( dragndrop.IsDragging() ) then return end
	
	if ( g.IsValid( self.m_pDragParent ) ) then
		return self.m_pDragParent:DragMousePress( mcode );
	end

	if ( !self.m_DragSlot ) then return end

	dragndrop.Clear()
	dragndrop.m_MouseCode = mcode;
	dragndrop.m_DragWatch = self;
	dragndrop.m_MouseX = g.gui.MouseX();
	dragndrop.m_MouseY = g.gui.MouseY();

end

function meta:DragClick( mcode )

	self:MouseCapture( true )
	-- Clicking one mouse button while dragging with another!
	-- Return true to stop us clicking and selecting stuff below..
	return true

end

function meta:DragMouseRelease( mcode )

	if ( g.IsValid( dragndrop.m_DropMenu ) ) then return end
	
	-- This wasn't the button we clicked with - so don't release drag
	if ( dragndrop.IsDragging() && dragndrop.m_MouseCode != mcode ) then
	
		return self:DragClick( mcode )
		
	end
	
	if ( !dragndrop.IsDragging() ) then
		dragndrop.Clear()
		return false 
	end

	for k, v in g.pairs( dragndrop.m_Dragging ) do

		if ( !g.IsValid( v ) ) then continue end 
		v:OnStopDragging()

	end	

	dragndrop.Drop();
	
	-- Todo.. we should only do this if we enabled it!
	if ( gui.EnableScreenClicker ) then 
		gui.EnableScreenClicker( false ) 
	end
	
	self:MouseCapture( false )	
	return true;

end

function meta:DrawDragHover( x, y, w, h )

	g.DisableClipping( true )

	g.surface.SetDrawColor( 255, 0, 255, 100 )
	g.surface.DrawRect( x, y, w, h )
	
	g.surface.SetDrawColor( 255, 220, 255, 230 )
	g.surface.DrawOutlinedRect( x, y, w, h )

	g.surface.SetDrawColor( 255, 100, 255, 50 )
	g.surface.DrawOutlinedRect( x-1, y-1, w+2, h+2 )
	
	g.DisableClipping( false )
	
end

function meta:SetDropTarget( x, y, w, h )
		
	if ( !self.m_bDrawingPaintOver ) then
		self.m_OldPaintOver = self.PaintOver
		self.m_bDrawingPaintOver = true;
	end
	
	self.PaintOver = function()
	
		if ( self.m_OldPaintOver ) then
			self:m_OldPaintOver()
		end

		self:DrawDragHover( x, y, w, h )
	
	end
	
end

function meta:DragHoverEnd()

	if ( !self.m_bDrawingPaintOver ) then return end

	self.PaintOver = self.m_OldPaintOver
	self.m_bDrawingPaintOver = false;
	
end




--
-- Drag Hover
--
-- These functions are used for things like trees
-- So that when you hover over the tree while dragging something
-- it will open up the tree. This works regardless of whether the 
-- is droppable or not.
--
-- Implement DragHoverClick in your panel class to get this functionality
--
function meta:DragHover( HoverTime )

	//
	// Call DragHoverClick if we've been hovering for 0.5 seconds..
	//
	if ( HoverTime < 0.1 ) then dragndrop.m_bHoverClick = false end
	if ( HoverTime > 0.1 && !dragndrop.m_bHoverClick ) then
	
		self:DragHoverClick( HoverTime );
		dragndrop.m_bHoverClick = true;
	
	end	

end

function meta:DragHoverClick( HoverTime )

end

function meta:DroppedOn( pnl )
	-- Fpr override.
end

function meta:OnDrop()
	
	-- We're being dropped on something
	-- we can create a new panel here and return it, so that instead of
	-- dropping us - it drops the new panel instead! We remain where we are!
	
	-- By default we return ourself
	
	return self
	
	
end


--
--
--
-- This is called to open stuff when you're hovering over it.
--
--

local LastHoverThink = nil
local LastHoverChangeTime = 0
local LastX = 0;
local LastY = 0;

function dragndrop.HoverThink()

	local hovered = g.vgui.GetHoveredPanel()
	local x = g.gui.MouseX();
	local y = g.gui.MouseY();

	-- Hovering a different panel
	if ( LastHoverThink != hovered || x != LastX || y != LastY ) then
		
		LastHoverChangeTime = g.SysTime();
		LastHoverThink		= hovered
		
	end
	
	-- Hovered panel might do stuff when we're hovering it
	-- so give it a chance to do that now.
	if ( g.IsValid( LastHoverThink ) ) then

		LastX = x;
		LastY = y;
		LastHoverThink:DragHover( g.SysTime() - LastHoverChangeTime )
	
	end

end

// Add the local dragndrop table here
g.dragndrop = dragndrop;

// Selections.lua
function meta:SetSelectionCanvas( bSet )

	self.m_bSelectionCanvas = bSet;
	self:SetMouseInputEnabled( true )

end

function meta:IsSelectionCanvas()

	return self.m_bSelectionCanvas;

end

function meta:SetSelectable( bSet )

	self.m_bSelectable = bSet;

end

function meta:ToggleSelection()

	self:SetSelected( !self.m_bSelected );

end

function meta:UnselectAll()

	self:SetSelected( false );

	local children = self:GetChildren()
	for k, v in g.pairs( children ) do
		v:UnselectAll()
	end
	
end



function meta:SetSelected( bSet )

	if ( self.m_bSelected == bSet ) then return end
	
	self.m_bSelected = bSet;
	
	if ( self.ApplySchemeSettings ) then
		self:ApplySchemeSettings()
	end

end

function meta:IsSelected( bSet )

	if ( !self:IsSelectable() ) then return false end
	return self.m_bSelected == true;

end

function meta:IsSelectable()

	return self.m_bSelectable == true;

end

local function GetSelectionRect()

	local CurX, CurY = SelectionCanvas:ScreenToLocal( g.gui.MouseX(), g.gui.MouseY() )
	
	local x = g.math.min( CurX, StartX )
	local y = g.math.min( CurY, StartY )
	
	local w = g.math.abs( CurX - StartX )
	local h = g.math.abs( CurY - StartY )
	
	return x, y, w, h

end

function meta:DrawSelections()

	if ( !self.m_bSelectable ) then return end
	if ( !self.m_bSelected ) then return end
	
	local w, h = self:GetSize()

	g.surface.SetDrawColor( 255, 0, 255, 100 )
	g.surface.DrawRect( 0, 0, w, h )

end

local function PaintSelectionBox( self )

	if ( !g.IsValid( SelectionCanvas ) ) then return end
	local x, y, w, h = GetSelectionRect()

	g.surface.SetDrawColor( 255, 0, 255, 50 )
	g.surface.DrawRect( x, y, w, h )
	
	g.surface.SetDrawColor( 255, 200, 255, 200 )
	g.surface.DrawOutlinedRect( x, y, w, h )

end

function meta:GetSelectionCanvas()

	if ( !self.m_bSelectionCanvas ) then
		
		local parent = self:GetParent()
		if ( g.IsValid( parent ) ) then
			return parent:GetSelectionCanvas()
		end
		
		return nil
	
	end
	
	return self

end


function meta:StartBoxSelection()

	if ( !self.m_bSelectionCanvas ) then
		
		local parent = self:GetParent()
		if ( g.IsValid( parent ) ) then
			return parent:StartBoxSelection()
		end
		
		return
	
	end


	self:MouseCapture( true )	
	
	if ( !g.input.IsShiftDown() ) then
		self:UnselectAll()
	end
	
	SelectionCanvas = self
	
	StartX, StartY = self:ScreenToLocal( g.gui.MouseX(), g.gui.MouseY() )
	
	self.PaintOver_Old = self.PaintOver
	self.PaintOver = PaintSelectionBox

end

function meta:GetChildrenInRect( x, y, w, h )

	local tab = {}
	
	local children = self:GetChildren()
	for k, v in g.pairs( children ) do
	
		local vw, vh = v:GetSize()

		if ( !self:IsVisible() ) then continue end
		if ( x > v.x + vw ) then continue end
		if ( y > v.y + vh ) then continue end
		if ( v.x > x + w ) then continue end
		if ( v.y > y + h ) then continue end
		
		if ( v.m_bSelectable ) then
			g.table.insert( tab, v )
		end
		
		g.table.Add( tab, v:GetChildrenInRect( x - v.x, y - v.y, w, h ) );
	
	end
	
	
	return tab

end

function meta:GetSelectedChildren()

	local tab = {}
	
	local children = self:GetChildren()
	for k, v in g.pairs( children ) do
			
		if ( v:IsSelected() ) then
			g.table.insert( tab, v )
		end
		
		g.table.Add( tab, v:GetSelectedChildren() );
	
	end
	
	return tab

end

function meta:NumSelectedChildren()

	local i = 0
	
	local children = self:GetChildren()
	for k, v in g.pairs( children ) do
			
		if ( v:IsSelected() ) then
			i = i + 1
		end
	
	end
	
	return i

end

function meta:EndBoxSelection()

	if ( SelectionCanvas != self ) then return false end
	
	self:MouseCapture( false )	
	
	
	self.PaintOver = self.PaintOver_Old
	self.PaintOver_Old = nil
	
	local x, y, w, h = GetSelectionRect()
	
	
	local children = self:GetChildrenInRect( x, y, w, h, true );
	for k, v in g.pairs( children ) do
	
		v:ToggleSelection()
	
	end
	
	SelectionCanvas = nil
	StartX, StartY = 0, 0;
	
	return true

end

// panel.lua ( this file includes the above, then runs the following code )

g.AccessorFunc( meta, "m_strCookieName",  "CookieName" )

meta.SetFGColorEx = meta.SetFGColor
meta.SetBGColorEx = meta.SetBGColor


--[[---------------------------------------------------------
   Name:	SetFGColor
   Desc:	Override to make it possible to pass Color's
-----------------------------------------------------------]]  
function meta:SetFGColor( r, g, b, a )

	if ( g.istable( r ) ) then
		return self:SetFGColorEx( r.r, r.g, r.b, r.a )
	end

	return self:SetFGColorEx( r, g, b, a )

end


--[[---------------------------------------------------------
   Name:	SetBGColor
   Desc:	Override to make it possible to pass Color's
-----------------------------------------------------------]]  
function meta:SetBGColor( r, g, b, a )

	if ( g.istable( r ) ) then
		return self:SetBGColorEx( r.r, r.g, r.b, r.a )
	end

	return self:SetBGColorEx( r, g, b, a )

end


--[[---------------------------------------------------------
	Name: SetHeight
-----------------------------------------------------------]]  
function meta:SetHeight( h )

	self:SetSize( self:GetWide(), h )

end

meta.SetTall = meta.SetHeight


--[[---------------------------------------------------------
	Name: SetHeight
-----------------------------------------------------------]]  
function meta:SetWidth( w )

	self:SetSize( w, self:GetTall() )

end

meta.SetWide = meta.SetWidth


--[[---------------------------------------------------------
	Name: StretchToParent (borders)
-----------------------------------------------------------]]  
function meta:StretchToParent( l, u, r, d )

	local w, h = self:GetParent():GetSize()

	if ( l != nil ) then
		self.x = l
	end
	
	if ( u != nil ) then
		self.y = u
	end
	
	if ( r != nil ) then
		self:SetWide( w - self.x - r )
	end
	
	if ( d != nil ) then
		self:SetTall( h - self.y - d )
	end
	
	--self:SetPos( l, u )
	--self:SetSize( w - (r + l), h - (d + u) )

end

--[[---------------------------------------------------------
	Name: CopyHeight
-----------------------------------------------------------]]  
function meta:CopyHeight( pnl )

	self:SetTall( pnl:GetTall() )

end

--[[---------------------------------------------------------
	Name: CopyWidth
-----------------------------------------------------------]]  
function meta:CopyWidth( pnl )

	self:SetWide( pnl:GetWide() )

end

--[[---------------------------------------------------------
	Name: CopyPos
-----------------------------------------------------------]]  
function meta:CopyPos( pnl )

	self:SetPos( pnl:GetPos() )

end

--[[---------------------------------------------------------
	Name: Align with the edge of the parent
-----------------------------------------------------------]]  
function meta:AlignBottom( m ) m = m or 0; self:SetPos( self.x, self:GetParent():GetTall() - self:GetTall() - m ) end 
function meta:AlignRight( m ) m = m or 0; self:SetPos( self:GetParent():GetWide() - self:GetWide() - m, self.y ) end
function meta:AlignTop( m ) m = m or 0; self:SetPos( self.x, m ) end
function meta:AlignLeft( m ) m = m or 0; self:SetPos( m, self.y ) end

--[[---------------------------------------------------------
	Name: Move relative to another panel
-----------------------------------------------------------]]  
function meta:MoveAbove( pnl, m ) m = m or 0; self:SetPos( self.x, pnl.y - self:GetTall() - m ) end
function meta:MoveBelow( pnl, m ) m = m or 0; self:SetPos( self.x, pnl.y + pnl:GetTall() + m ) end
function meta:MoveRightOf( pnl, m ) m = m or 0; self:SetPos( pnl.x + pnl:GetWide() + m, self.y ) end
function meta:MoveLeftOf( pnl,  m ) m = m or 0; self:SetPos( pnl.x - self:GetWide() - m, self.y ) end


--[[---------------------------------------------------------
	Name: StretchRightTo
-----------------------------------------------------------]]  
function meta:StretchRightTo( pnl, m ) m = m or 0; self:SetWide( pnl.x - self.x -  m ) end
function meta:StretchBottomTo( pnl, m ) m = m or 0; self:SetTall( pnl.y - self.y - m ) end

--[[---------------------------------------------------------
	Name: CenterVertical
-----------------------------------------------------------]]  
function meta:CenterVertical( fraction )

	fraction = fraction or 0.5
	self:SetPos( self.x, self:GetParent():GetTall() * fraction -  self:GetTall() * 0.5 )

end

--[[---------------------------------------------------------
	Name: CenterHorizontal
-----------------------------------------------------------]]  
function meta:CenterHorizontal( fraction )

	fraction = fraction or 0.5
	self:SetPos( self:GetParent():GetWide() * fraction - self:GetWide() * 0.5, self.y )

end

--[[---------------------------------------------------------
	Name: CenterHorizontal
-----------------------------------------------------------]]  
function meta:Center()

	self:CenterVertical()
	self:CenterHorizontal()

end

--[[---------------------------------------------------------
	Name: CopyBounds
-----------------------------------------------------------]]  
function meta:CopyBounds( pnl )

	local x, y, w, h = pnl:GetBounds()
	
	self:SetPos( x, y )
	self:SetSize( w, h )

end

--[[---------------------------------------------------------
	Name: GetCookieNumber
-----------------------------------------------------------]]  
function meta:SetCookieName( cookiename )

	self.m_strCookieName = cookiename
	
	-- If we have a loadcookies function, call it.
	if ( self.LoadCookies ) then
		self:LoadCookies()
		self:InvalidateLayout()
	end

end


--[[---------------------------------------------------------
	Name: GetCookieNumber
-----------------------------------------------------------]]  
function meta:GetCookieNumber( cookiename, default )

	local name = self:GetCookieName()
	if ( !name ) then return default end
	
	return g.cookie.GetNumber( name.."."..cookiename, default )

end

--[[---------------------------------------------------------
	Name: GetCookie
-----------------------------------------------------------]]  
function meta:GetCookie( cookiename, default )

	local name = self:GetCookieName()
	if ( !name ) then return default end
	
	return g.cookie.GetString( name.."."..cookiename, default )

end

--[[---------------------------------------------------------
	Name: SetCookie
-----------------------------------------------------------]]  
function meta:SetCookie( cookiename, value )

	local name = self:GetCookieName()
	if ( !name ) then return end
	
	return g.cookie.Set( name.."."..cookiename, value )

end


--[[---------------------------------------------------------
	Name: InvalidateParent
-----------------------------------------------------------]]  
function meta:InvalidateParent( layoutnow )

	local parent = self:GetParent()
	if ( !parent ) then return end
	if ( self.LayingOutParent ) then return end
	
	self.LayingOutParent = true
	parent:InvalidateLayout( layoutnow )
	self.LayingOutParent = false

end

--[[---------------------------------------------------------
	Name: PositionLabel
-----------------------------------------------------------]] 
function meta:PositionLabel( LabelWidth, X, Y, Lbl, Control )

	Lbl:SetWide( LabelWidth )
	Lbl:SetPos( X, Y )
	
	Control.y = Y
	Control:MoveRightOf( Lbl, 0 )

	return Y + g.math.max( Lbl:GetTall(), Control:GetTall() )

end




--[[---------------------------------------------------------
   Name: SetTooltip
-----------------------------------------------------------]]
function meta:SetTooltip( tooltip )
	self.strTooltipText = tooltip
end

meta.SetToolTip = meta.SetTooltip

--[[---------------------------------------------------------
   Name: SetTooltipPanel
-----------------------------------------------------------]]
function meta:SetTooltipPanel( panel )
	self.pnlTooltipPanel = panel
end

meta.SetToolTipPanel = meta.SetTooltipPanel


--[[---------------------------------------------------------
   Name: SizeToContentsY (Only works on Labels)
-----------------------------------------------------------]]
function meta:SizeToContentsY( addval )
	local w, h = self:GetContentSize()
	if (!w || !h ) then return end
	addval = addval or 0
	self:SetTall( h )
end

--[[---------------------------------------------------------
   Name: SizeToContentsX (Only works on Labels)
-----------------------------------------------------------]]
function meta:SizeToContentsX( addval )
	local w, h = self:GetContentSize()
	if (!w || !h) then return end
	addval = addval or 0
	self:SetWide( w + addval )
end

--[[---------------------------------------------------------
   Name: ToggleVisible
-----------------------------------------------------------]]
function meta:ToggleVisible()
	
	self:SetVisible( !self:IsVisible() )
	
end

function meta:Distance( pnl )
	
	if ( !g.IsValid( pnl ) ) then return 0 end	
	return self:DistanceFrom( pnl.x + pnl:GetWide() * 0.5, pnl.y + pnl:GetTall() * 0.5 )
	
end

function meta:DistanceFrom( x, y )
	
	local x = self.x + self:GetWide() * 0.5 - x;
	local y = self.y + self:GetTall() * 0.5 - y;
	
	return g.math.sqrt( x * x + y *y )
	
end


--[[---------------------------------------------------------
   Name: Retusn the child position on this panel. Even if its parented to children of children.
-----------------------------------------------------------]]
function meta:GetChildPosition( pnl )


	local x = 0;
	local y = 0;

	while ( g.IsValid( pnl ) && pnl != self ) do
		
		x = x + pnl.x
		y = y + pnl.y
		
		pnl = pnl:GetParent();

	end

	return x, y;

end

--[[---------------------------------------------------------
   Name: Returns true if the panel is valid. This does not
		check the type. If the passed object is anything other
		than a panel or nil, this will error. (speed)
-----------------------------------------------------------]]
function ValidPanel( pnl )

	if (!pnl) then return false end
	
	return pnl:IsValid() 

end


function meta:InvalidateChildren( bRecurse )

	local children = self:GetChildren()
	for k, v in g.pairs( children ) do
	
		if ( bRecurse ) then
			v:InvalidateChildren( true )
		else
			v:InvalidateChildren( false )
		end

	end
	
	self:InvalidateLayout( true )

end

function meta:IsOurChild( pPanel )

	if ( pPanel == self ) then return true end

	local children = self:GetChildren()
	for k, v in g.pairs( children ) do
	
		if ( v:IsOurChild( pPanel ) ) then return true end

	end
	
	return false

end

function meta:CopyBase( pnl )

	self:CopyBounds( pnl )
	self:Dock( pnl:GetDock() )
	
	// TODO. More.

end

function meta:Add( pnl )

	if ( isstring( pnl ) ) then
	
		local pnl = g.vgui.Create( pnl, self )
		return pnl	
		
	end
	
	if ( istable( pnl ) ) then
	
		local pnl = g.vgui.CreateFromTable( pnl, self )
		return pnl	
		
	end

	pnl:SetParent( self )
	return pnl

end

function meta:GetClosestChild( x, y )

	local distance = 9999
	local closest = nil

	local children = self:GetChildren()
	for k, v in g.pairs( children ) do
	
		local dist = v:DistanceFrom( x, y )
		if ( dist < distance ) then
			distance = dist;
			closest = v
		end
	end
	
	return closest, distance

end

function meta:LocalCursorPos()
			
	return self:ScreenToLocal( g.gui.MouseX(), g.gui.MouseY() )
		
end

function meta:MoveToAfter( pnl )

	local children = self:GetParent():GetChildren()
	
	-- remove us from the table
	g.table.RemoveByValue( children, self )
		
	-- find the key, where we want to be
	local key = g.table.KeyFromValue( children, pnl )
	
	if ( key ) then
		-- insert us where we wanna be
		g.table.insert( children, key+1, self )
	else
		return false;
	end
	
	for k, v in g.pairs( children ) do
		v:SetZPos( k )
	end
		
end

function meta:MoveToBefore( pnl )

	local children = self:GetParent():GetChildren()
	
	-- remove us from the table
	g.table.RemoveByValue( children, self )
		
	-- find the key, where we want to be
	local key = g.table.KeyFromValue( children, pnl )
	
	if ( key ) then
		-- insert us where we wanna be
		g.table.insert( children, key, self )
	else
		return false
	end
	
	for k, v in pairs( children ) do
		v:SetZPos( k )
	end
		
end

function meta:Clear()

	for k, panel in g.pairs( self:GetChildren() ) do
	
		panel:Remove()
	
	end

end

function meta:IsHovered()

	return g.vgui.GetHoveredPanel() == self
	
end

function meta:Show()
	self:SetVisible( true )
end

function meta:Hide()
	self:SetVisible( false )
end


function meta:IsChildHovered( iDepth )

	local Hovered = g.vgui.GetHoveredPanel()
	if ( !g.IsValid( Hovered ) ) then return false end

	for i=1, iDepth do

		Hovered = Hovered:GetParent()
		if ( !Ig.sValid( Hovered ) ) then return false end
		if ( Hovered == self ) then return true end

	end

	return false

end

// Mother of fuck this is messy, I could split it up into different files but .. meh

return meta;