1022 lines
25 KiB
Ucode
1022 lines
25 KiB
Ucode
// ====================================================================
|
|
// Class: GUITreeList
|
|
//
|
|
// The GUITree is a part of a tree control.
|
|
//
|
|
// Written by Bruce Bickar
|
|
// (Cloned from GUIList)
|
|
// (c) 2002, 2003, Epic Games, Inc. All Rights Reserved
|
|
// ====================================================================
|
|
|
|
class GUITreeList extends GUIVertList Native;
|
|
|
|
#exec OBJ LOAD FILE=// if _RO_
|
|
// else
|
|
//#exec OBJ LOAD FILE=InterfaceContent.utx
|
|
// end if _RO_InterfaceContent.utx
|
|
|
|
cpptext
|
|
{
|
|
INT FindParentIndex( INT ChildIndex ) const;
|
|
void UpdateVisibleCount();
|
|
void Draw(UCanvas* Canvas);
|
|
|
|
protected:
|
|
virtual void DrawItem(UCanvas* Canvas, INT Item, FLOAT X, FLOAT Y, FLOAT W, FLOAT HT, UBOOL bSelected, UBOOL bPending);
|
|
public:
|
|
}
|
|
|
|
var() eTextAlign TextAlign;
|
|
var() editconstarray editconst noexport array<GUITreeNode> Elements;
|
|
var() editconstarray editconst noexport array<GUITreeNode> SelectedElements;
|
|
var() editconst const noexport int VisibleCount; // How many elements are currently visible
|
|
var() const editconst float PrefixWidth, SelectedPrefixWidth;
|
|
var() bool bAllowParentSelection, bAllowDuplicateCaption;
|
|
var() bool bGroupItems; // really bad hack to get onslaught link setups working
|
|
|
|
native final function UpdateVisibleCount();
|
|
native final function SortList();
|
|
|
|
// Used by SortList.
|
|
delegate int CompareItem(GUITreeNode ElemA, GUITreeNode ElemB)
|
|
{
|
|
return StrCmp(ElemA.Caption, ElemB.Caption);
|
|
}
|
|
|
|
function int AddItem(string Caption, string Value, optional string ParentCaption, optional bool bEnabled, optional string ExtraInfo)
|
|
{
|
|
local int i;
|
|
local int idx;
|
|
|
|
if ( !bAllowEmptyItems && Caption == "" && ParentCaption == "" )
|
|
return -1;
|
|
|
|
if ( !bAllowDuplicateCaption && FindIndex(Caption) != -1 )
|
|
return -1;
|
|
|
|
if ( ParentCaption == "" )
|
|
idx = HardInsert( Elements.Length, Caption, Value, "", 0, True, ExtraInfo );
|
|
else
|
|
{
|
|
// find parent
|
|
if ( bGroupItems )
|
|
{
|
|
i = FindIndex(ParentCaption,true);
|
|
if ( i == -1 )
|
|
i = FindIndex(ParentCaption);
|
|
|
|
// Implicitly create the entry for the parent, if it wasn't found
|
|
if ( i == -1 )
|
|
i = HardInsert( Elements.Length, ParentCaption, "", "", 0, True );
|
|
}
|
|
else if ( ParentCaption != "" )
|
|
i = HardInsert(Elements.Length, ParentCaption, Value, "", 0, True, ExtraInfo);
|
|
|
|
idx = HardInsert( i+1, Caption, Value, ParentCaption, Elements[i].Level + 1, bEnabled, ExtraInfo );
|
|
}
|
|
|
|
if (Elements.Length == 1 && bInitializeList)
|
|
SetIndex(0);
|
|
else if ( bNotify )
|
|
CheckLinkedObjects(Self);
|
|
|
|
UpdateVisibleCount();
|
|
if (MyScrollBar != None)
|
|
MyScrollBar.AlignThumb();
|
|
|
|
return idx;
|
|
}
|
|
|
|
function int AddElement( GUITreeNode Node )
|
|
{
|
|
return AddItem(Node.Caption, Node.Value, Node.ParentCaption, Node.bEnabled, Node.ExtraInfo);
|
|
}
|
|
|
|
function Replace(int i, string NewItem, string NewValue, optional string ParentCaption, optional bool bNoSort, optional string ExtraInfo)
|
|
{
|
|
if ( !IsValidIndex(i) )
|
|
AddItem(NewItem,NewValue,ParentCaption);
|
|
else
|
|
{
|
|
if ( !bAllowEmptyItems && NewItem == "" && NewValue == "" && ParentCaption == "" )
|
|
return;
|
|
|
|
Elements[i].Caption = NewItem;
|
|
Elements[i].Value = NewValue;
|
|
Elements[i].ExtraInfo = ExtraInfo;
|
|
if ( bSorted && !bNoSort )
|
|
Sort();
|
|
|
|
SetIndex(Index);
|
|
}
|
|
}
|
|
|
|
function int InsertItem( int idx, string Caption, string Value, string ParentCaption, int Level, bool bEnabled, optional string ExtraInfo )
|
|
{
|
|
local int ParentIndex, ChildIndex;
|
|
|
|
if ( !IsValidIndex(idx) )
|
|
return AddItem(Caption, Value, ParentCaption, bEnabled, ExtraInfo);
|
|
|
|
if ( bGroupItems )
|
|
{
|
|
ParentIndex = FindParentIndex(idx);
|
|
if ( IsValidIndex(ParentIndex) && Elements[ParentIndex].Caption == ParentCaption )
|
|
{
|
|
ChildIndex = FindAvailableChildIndex(ParentIndex);
|
|
if ( idx > ParentIndex && idx <= ChildIndex )
|
|
return HardInsert(idx, Caption, Value, ParentCaption, Elements[ParentIndex].Level + 1, bEnabled, ExtraInfo);
|
|
else return HardInsert(ChildIndex, Caption, Value, ParentCaption, Elements[ParentIndex].Level + 1, bEnabled, ExtraInfo);
|
|
}
|
|
else if ( ParentCaption == "" )
|
|
{
|
|
idx = FindNextAvailableRootIndex(idx);
|
|
return HardInsert(idx,Caption,Value,ParentCaption,Level,True,ExtraInfo);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ParentIndex = FindNextAvailableRootIndex(idx);
|
|
if ( ParentCaption != "" )
|
|
idx = HardInsert(idx,ParentCaption,Value,"",Level++,True,ExtraInfo);
|
|
|
|
return HardInsert(idx+1, Caption, Value, ParentCaption, Level, True, ExtraInfo);
|
|
}
|
|
|
|
return AddItem(Caption, Value, ParentCaption, bEnabled);
|
|
}
|
|
|
|
protected function int HardInsert( int idx, string Caption, string Value, string ParentCaption, int Level, bool bEnabled, optional string ExtraInfo )
|
|
{
|
|
Elements.Insert(idx,1);
|
|
|
|
Elements[idx].Caption = Caption;
|
|
Elements[idx].Value = Value;
|
|
Elements[idx].ParentCaption = ParentCaption;
|
|
Elements[idx].ExtraInfo = ExtraInfo;
|
|
Elements[idx].Level = Level;
|
|
Elements[idx].bEnabled = bEnabled;
|
|
|
|
ItemCount = Elements.Length;
|
|
|
|
return idx;
|
|
}
|
|
|
|
function LoadFrom(GUITreeList Source, optional bool bClearFirst)
|
|
{
|
|
local int i, Level;
|
|
local byte bEnabled;
|
|
local string Caption, Value, ParentCaption, ExtraInfo;
|
|
|
|
if (bClearfirst)
|
|
Clear();
|
|
|
|
for ( i = 0; i < Source.ItemCount; i++ )
|
|
{
|
|
if ( Source.ValidSelectionAt(i) )
|
|
{
|
|
Source.GetAtIndex(i,Caption,Value,ParentCaption,Level,bEnabled, ExtraInfo);
|
|
AddItem(Caption, Value, ParentCaption, bool(bEnabled), ExtraInfo);
|
|
}
|
|
}
|
|
}
|
|
|
|
function int RemoveSilent( string Caption )
|
|
{
|
|
local int i;
|
|
|
|
bNotify = False;
|
|
i = RemoveItem(Caption);
|
|
bNotify = True;
|
|
|
|
return i;
|
|
}
|
|
|
|
function int RemoveItem(string Caption)
|
|
{
|
|
local int i;
|
|
|
|
i = FindIndex(Caption, True);
|
|
if ( i == -1 )
|
|
i = FindIndex(Caption);
|
|
|
|
return RemoveItemAt(i);
|
|
}
|
|
|
|
function int RemoveItemAt( int idx, optional bool bNoSort, optional bool bSkipCleanup )
|
|
{
|
|
local int Level, ParentIndex;
|
|
|
|
if ( IsValidIndex(idx) )
|
|
{
|
|
Level = Elements[idx].Level + 1;
|
|
ParentIndex = FindParentIndex(idx);
|
|
|
|
// Remove all children of this element
|
|
Elements.Remove(idx,1);
|
|
ItemCount--;
|
|
|
|
while ( idx < ItemCount && Elements[idx].Level == Level )
|
|
{
|
|
if ( RemoveItemAt(idx,True,True) == -2 )
|
|
break;
|
|
}
|
|
|
|
// If parent selection isn't allowed, we should remove the entry for the parent if we're the last child
|
|
// bSkipCleanup should probably only be used when recursing within this function
|
|
if ( !bAllowParentSelection && !bSkipCleanup && IsValidIndex(ParentIndex) && !HasChildren(ParentIndex) )
|
|
RemoveItemAt(ParentIndex,True);
|
|
|
|
UpdateVisibleCount();
|
|
if ( bSorted && !bNoSort )
|
|
Sort();
|
|
|
|
// In case we now have an invalid index
|
|
SetIndex(Index);
|
|
if ( MyScrollBar != None )
|
|
MyScrollBar.AlignThumb();
|
|
|
|
return Index;
|
|
}
|
|
|
|
return -2;
|
|
}
|
|
|
|
function int RemoveElement( GUITreeNode Node, optional int Count, optional bool bNoSort )
|
|
{
|
|
local int i;
|
|
|
|
// TODO Hook up count
|
|
Count = Max(Count, 1);
|
|
for ( i = 0; i < Elements.Length; i++ )
|
|
if ( Elements[i] == Node )
|
|
return RemoveItemAt(i, bNoSort);
|
|
|
|
return -1;
|
|
}
|
|
|
|
function Clear()
|
|
{
|
|
if (Elements.Length == 0)
|
|
return;
|
|
|
|
Elements.Remove(0,Elements.Length);
|
|
ItemCount = 0;
|
|
|
|
UpdateVisibleCount();
|
|
Super.Clear();
|
|
}
|
|
|
|
event bool Swap(int IndexA, int IndexB)
|
|
{
|
|
local int ParentIndexA, ParentIndexB;
|
|
|
|
ParentIndexA = FindParentIndex(IndexA);
|
|
ParentIndexB = FindParentIndex(IndexB);
|
|
|
|
// TODO handle cases of attempting to swap elements at different levels
|
|
if ( bGroupItems )
|
|
{
|
|
if ( IsValidIndex(ParentIndexA) )
|
|
{
|
|
if ( IsValidIndex(ParentIndexB) )
|
|
{
|
|
if ( ParentIndexA != ParentIndexB )
|
|
return false;
|
|
}
|
|
|
|
else return false;
|
|
}
|
|
else if ( IsValidIndex(ParentIndexB) )
|
|
return false;
|
|
}
|
|
|
|
// TODO Support swapping elements that contain children
|
|
if ( IsValidIndex(IndexA) && IsValidIndex(IndexB) )
|
|
{
|
|
if ( !bGroupItems )
|
|
{
|
|
if ( IndexA > IndexB )
|
|
{
|
|
if ( Elements[IndexA].Level > 0 )
|
|
IndexA = ParentIndexA;
|
|
|
|
if ( Elements[IndexB].Level > 0 )
|
|
IndexB = ParentIndexB;
|
|
|
|
if ( IndexA == IndexB )
|
|
{
|
|
IndexA = FindNextAvailableRootIndex(IndexB-1);
|
|
if ( !IsValidIndex(IndexA) )
|
|
return False;
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
if ( Elements[IndexA].Level > 0 )
|
|
IndexA = ParentIndexA;
|
|
|
|
if ( Elements[IndexB].Level > 0 )
|
|
IndexB = ParentIndexB;
|
|
|
|
if ( IndexA == IndexB )
|
|
IndexB = FindAvailableChildIndex(IndexA);
|
|
|
|
if ( !IsValidIndex(IndexB) )
|
|
return False;
|
|
}
|
|
}
|
|
|
|
HardSwap(IndexA,IndexB);
|
|
return True;
|
|
}
|
|
|
|
return False;
|
|
}
|
|
|
|
protected function HardSwap( int IndexA, int IndexB )
|
|
{
|
|
local array<int> chIdxA, chIdxB;
|
|
local int i;
|
|
local array<GUITreeNode> NodesA, NodesB;
|
|
|
|
NodesA[NodesA.Length] = Elements[IndexA];
|
|
NodesB[NodesB.Length] = Elements[IndexB];
|
|
|
|
if ( HasChildren(IndexA) )
|
|
{
|
|
chIdxA = GetChildIndexList(IndexA);
|
|
for ( i = 0; i < chIdxA.Length; i++ )
|
|
NodesA[NodesA.Length] = Elements[chIdxA[i]];
|
|
}
|
|
|
|
if ( HasChildren(IndexB) )
|
|
{
|
|
chIdxB = GetChildIndexList(IndexB);
|
|
for ( i = 0; i < chIdxB.Length; i++ )
|
|
NodesB[NodesB.Length] = Elements[chIdxB[i]];
|
|
}
|
|
|
|
if ( IndexA > IndexB )
|
|
{
|
|
Elements.Remove( IndexA, NodesA.Length );
|
|
Elements.Insert( IndexA, NodesB.Length );
|
|
for ( i = 0; i < NodesB.Length; i++ )
|
|
Elements[IndexA + i] = NodesB[i];
|
|
|
|
Elements.Remove( IndexB, NodesB.Length );
|
|
Elements.Insert( IndexB, NodesA.Length );
|
|
for ( i = 0; i < NodesB.Length; i++ )
|
|
Elements[IndexB + i] = NodesA[i];
|
|
|
|
}
|
|
else
|
|
{
|
|
Elements.Remove( IndexB, NodesB.Length );
|
|
Elements.Insert( IndexB, NodesA.Length );
|
|
for ( i = 0; i < NodesA.Length; i++ )
|
|
Elements[IndexB + i] = NodesA[i];
|
|
|
|
Elements.Remove( IndexA, NodesA.Length );
|
|
Elements.Insert( IndexA, NodesB.Length );
|
|
for ( i = 0; i < NodesB.Length; i++ )
|
|
Elements[IndexA + i] = NodesB[i];
|
|
}
|
|
|
|
if ( bNotify )
|
|
CheckLinkedObjects(Self);
|
|
|
|
if ( bSorted )
|
|
Sort();
|
|
}
|
|
|
|
|
|
// =====================================================================================================================
|
|
// =====================================================================================================================
|
|
// Query Functions
|
|
// =====================================================================================================================
|
|
// =====================================================================================================================
|
|
|
|
function string GetCaption()
|
|
{
|
|
return GetCaptionAtIndex(Index);
|
|
}
|
|
|
|
function string GetParentCaption()
|
|
{
|
|
return GetParentCaptionAtIndex(Index);
|
|
}
|
|
|
|
function string GetValue()
|
|
{
|
|
return GetValueAtIndex(Index);
|
|
}
|
|
|
|
function int GetLevel()
|
|
{
|
|
return GetLevelAtIndex(Index);
|
|
}
|
|
|
|
function string GetExtra()
|
|
{
|
|
return GetExtraAtIndex(Index);
|
|
}
|
|
|
|
function string GetCaptionAtIndex(int i)
|
|
{
|
|
if (!IsValidIndex(i))
|
|
return "";
|
|
|
|
return Elements[i].Caption;
|
|
}
|
|
|
|
function string GetParentCaptionAtIndex(int idx)
|
|
{
|
|
if ( !IsValidIndex(idx) )
|
|
return "";
|
|
|
|
// TODO this is not a good way to do this...gah
|
|
if ( Elements[idx].ParentCaption == "" )
|
|
return Elements[idx].Caption;
|
|
|
|
return Elements[idx].ParentCaption;
|
|
}
|
|
|
|
function string GetValueAtIndex(int i)
|
|
{
|
|
if (!IsValidIndex(i))
|
|
return "";
|
|
|
|
return Elements[i].Value;
|
|
}
|
|
|
|
function int GetLevelAtIndex(int i)
|
|
{
|
|
if (!IsValidIndex(i))
|
|
return -1;
|
|
|
|
return Elements[i].Level;
|
|
}
|
|
|
|
function string GetExtraAtIndex( int idx )
|
|
{
|
|
if ( !IsValidIndex(idx) )
|
|
return "";
|
|
|
|
return Elements[idx].ExtraInfo;
|
|
}
|
|
|
|
function bool GetElementAtIndex( int i, out GUITreeNode Node )
|
|
{
|
|
if ( !IsValidIndex(i) )
|
|
return False;
|
|
|
|
Node = Elements[i];
|
|
return True;
|
|
}
|
|
|
|
function bool GetAtIndex(int i, out string Caption, out string Value, out string ParentCaption, out int Level, out byte bEnabled, out string ExtraInfo)
|
|
{
|
|
if (!IsValidIndex(i))
|
|
return false;
|
|
|
|
Caption = Elements[i].Caption;
|
|
Value = Elements[i].Value;
|
|
Level = Elements[i].Level;
|
|
ParentCaption = Elements[i].ParentCaption;
|
|
bEnabled = byte(Elements[i].bEnabled);
|
|
ExtraInfo = Elements[i].ExtraInfo;
|
|
return True;
|
|
}
|
|
|
|
|
|
function array<int> GetIndexList()
|
|
{
|
|
local array<int> Indexes;
|
|
local int i;
|
|
|
|
for ( i = 0; i < ItemCount; i++ )
|
|
if ( ValidSelectionAt(i) )
|
|
Indexes[Indexes.Length] = i;
|
|
|
|
return Indexes;
|
|
}
|
|
|
|
function array<int> GetChildIndexList( int idx, optional bool bNoRecurse )
|
|
{
|
|
local array<int> Indexes;
|
|
local int Level;
|
|
|
|
if ( IsValidIndex(idx) )
|
|
{
|
|
Level = Elements[idx].Level + 1;
|
|
while ( ++idx < ItemCount )
|
|
{
|
|
if ( Elements[idx].Level < Level )
|
|
break;
|
|
|
|
if ( bNoRecurse && Elements[idx].Level > Level )
|
|
break;
|
|
|
|
Indexes[Indexes.Length] = idx;
|
|
}
|
|
}
|
|
|
|
return Indexes;
|
|
}
|
|
|
|
event bool ValidSelection()
|
|
{
|
|
return ValidSelectionAt(Index);
|
|
}
|
|
|
|
event bool ValidSelectionAt( int idx )
|
|
{
|
|
if ( !IsValidIndex(idx) )
|
|
return False;
|
|
|
|
return bAllowParentSelection || !HasChildren(idx);
|
|
}
|
|
|
|
event bool HasChildren( int ParentIndex )
|
|
{
|
|
if ( !IsValidIndex(ParentIndex) )
|
|
return False;
|
|
|
|
if ( ParentIndex < ItemCount - 1 )
|
|
return Elements[ParentIndex+1].Level > Elements[ParentIndex].Level;
|
|
|
|
return False;
|
|
}
|
|
|
|
event bool IsExpanded( int ParentIndex )
|
|
{
|
|
if ( !HasChildren(ParentIndex) )
|
|
return True;
|
|
|
|
return Elements[ParentIndex+1].bEnabled;
|
|
}
|
|
|
|
// =====================================================================================================================
|
|
// =====================================================================================================================
|
|
// Assignment functions
|
|
// =====================================================================================================================
|
|
// =====================================================================================================================
|
|
|
|
function SetCaptionAtIndex(int i, string NewCaption)
|
|
{
|
|
if (!IsValidIndex(i))
|
|
return;
|
|
|
|
Elements[i].Caption = NewCaption;
|
|
}
|
|
|
|
function SetValueAtIndex(int i, string NewValue)
|
|
{
|
|
if (!IsValidIndex(i))
|
|
return;
|
|
|
|
Elements[i].Value = NewValue;
|
|
}
|
|
|
|
function SetLevelAtIndex(int i, int NewLevel)
|
|
{
|
|
if (!IsValidIndex(i))
|
|
return;
|
|
|
|
Elements[i].Level = NewLevel;
|
|
}
|
|
|
|
function bool Expand(int idx, optional bool bRecursive)
|
|
{
|
|
local int i;
|
|
local array<int> Indexes;
|
|
|
|
if (!IsValidIndex(idx))
|
|
return false;
|
|
|
|
// Make sure all other elements on this level, as well as all parent items, are also visible
|
|
Expand(FindParentIndex(idx));
|
|
|
|
// Then enable all children, perhaps with recursion
|
|
Indexes = GetChildIndexList(idx, !bRecursive);
|
|
for ( i = 0; i < Indexes.Length; i++ )
|
|
Elements[Indexes[i]].bEnabled = True;
|
|
|
|
return True;
|
|
}
|
|
|
|
function bool Collapse(int idx)
|
|
{
|
|
local int i;
|
|
local array<int> Indexes;
|
|
|
|
if (!IsValidIndex(idx))
|
|
return false;
|
|
|
|
Indexes = GetChildIndexList(idx);
|
|
for ( i = 0; i < Indexes.Length; i++ )
|
|
Elements[Indexes[i]].bEnabled = False;
|
|
|
|
return True;
|
|
}
|
|
|
|
function ToggleExpand(int idx, optional bool bRecursive)
|
|
{
|
|
if (!IsValidIndex(idx))
|
|
return;
|
|
|
|
if ( IsExpanded(idx) )
|
|
Collapse(idx);
|
|
else Expand(idx, bRecursive);
|
|
}
|
|
|
|
function bool IsToggleClick(int idx)
|
|
{
|
|
local float PrefixOffset, CaptionOffset;
|
|
|
|
if ( !IsValidIndex(idx) )
|
|
return false;
|
|
|
|
if ( !HasChildren(idx) )
|
|
return false;
|
|
|
|
// Calculate the position of the plus/minus
|
|
PrefixOffset = ClientBounds[0] + (SelectedPrefixWidth * GetLevelAtIndex(idx));
|
|
CaptionOffset = PrefixOffset + SelectedPrefixWidth;
|
|
|
|
// if we were clicking on the plus/minus, toggle the item
|
|
if ( Controller.MouseX >= PrefixOffset && Controller.MouseX <= CaptionOffset )
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
function bool InternalOnClick( GUIComponent Sender )
|
|
{
|
|
local bool bResult;
|
|
|
|
bResult = Super.InternalOnClick(Sender);
|
|
if ( bResult && IsToggleClick(Index) )
|
|
ToggleExpand(Index);
|
|
|
|
return bResult;
|
|
}
|
|
|
|
function bool InternalDblClick( GUIComponent Sender )
|
|
{
|
|
ToggleExpand(Index);
|
|
return True;
|
|
}
|
|
|
|
// =====================================================================================================================
|
|
// =====================================================================================================================
|
|
// Search functions
|
|
// =====================================================================================================================
|
|
// =====================================================================================================================
|
|
function int FindIndex(string Caption, optional bool bExact)
|
|
{
|
|
local int i;
|
|
|
|
for ( i = 0; i < Elements.Length; i++)
|
|
if ( (bExact && Elements[i].Caption == Caption) || (!bExact && Elements[i].Caption ~= Caption) )
|
|
return i;
|
|
|
|
return -1;
|
|
}
|
|
|
|
function int FindFullIndex( string Caption, string Value, string ParentCaption )
|
|
{
|
|
local int i;
|
|
|
|
for ( i = 0; i < Elements.Length; i++ )
|
|
if ( Elements[i].Caption == Caption && Elements[i].Value == Value && Elements[i].ParentCaption == ParentCaption )
|
|
return i;
|
|
|
|
return -1;
|
|
}
|
|
|
|
function int FindParentIndex( int ChildIndex )
|
|
{
|
|
local int Level;
|
|
|
|
if ( !IsValidIndex(ChildIndex) )
|
|
return -1;
|
|
|
|
if ( Elements[ChildIndex].Level == 0 )
|
|
return -1;
|
|
|
|
Level = Elements[ChildIndex].Level - 1;
|
|
while ( --ChildIndex >= 0 && Elements[ChildIndex].Level > Level );
|
|
return ChildIndex;
|
|
}
|
|
|
|
function int FindNextAvailableRootIndex( int Target )
|
|
{
|
|
if ( !IsValidIndex(Target) )
|
|
return Elements.Length;
|
|
|
|
if ( Elements[Target].Level == 0 )
|
|
return Target;
|
|
|
|
while ( --Target > 0 && Elements[Target].Level > 0 );
|
|
return Target;
|
|
}
|
|
|
|
// This function calculates the appropriate index to use for adding a child node
|
|
function int FindAvailableChildIndex( int ParentIndex )
|
|
{
|
|
local int ParentLevel;
|
|
|
|
if ( IsValidIndex(ParentIndex) )
|
|
{
|
|
ParentLevel = Elements[ParentIndex].Level + 1;
|
|
while ( ++ParentIndex < ItemCount && Elements[ParentIndex].Level >= ParentLevel );
|
|
return ParentIndex;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
function int FindIndexByValue(string Value, optional bool bExact)
|
|
{
|
|
local int i;
|
|
|
|
for ( i = 0; i < Elements.Length; i++)
|
|
if ( (bExact && Elements[i].Value == Value) || (!bExact && Elements[i].Value ~= Value) )
|
|
return i;
|
|
|
|
return -1;
|
|
}
|
|
|
|
function int FindElement( string Caption, string Value, int Level, optional bool bCaseSensitive )
|
|
{
|
|
local int i;
|
|
|
|
if ( bCaseSensitive )
|
|
for ( i = 0; i < Elements.Length; i++ )
|
|
if ( Elements[i].Caption == Caption && Elements[i].Value == Value && Elements[i].Level == Level )
|
|
return i;
|
|
|
|
else for ( i = 0; i < Elements.Length; i++ )
|
|
if ( Elements[i].Caption ~= Caption && Elements[i].Value ~= Value && Elements[i].Level == Level )
|
|
return i;
|
|
|
|
return -1;
|
|
}
|
|
|
|
// Find the node index that is distance away from node specified by idx, considering only visible nodes
|
|
function int FindVisibleIndex( int Idx, int Distance )
|
|
{
|
|
local int Count, i, increment;
|
|
|
|
if ( Distance == 0 )
|
|
return Idx;
|
|
else if ( Distance < 0 )
|
|
increment = -1;
|
|
else increment = 1;
|
|
|
|
for ( i = Idx; IsValidIndex(i); i += increment )
|
|
{
|
|
if ( Elements[i].bEnabled )
|
|
Count++;
|
|
|
|
if ( Count >= Abs(Distance) )
|
|
break;
|
|
}
|
|
|
|
return i;
|
|
}
|
|
|
|
// =====================================================================================================================
|
|
// =====================================================================================================================
|
|
// GUIListBase Interface
|
|
// =====================================================================================================================
|
|
// =====================================================================================================================
|
|
event int CalculateIndex( optional bool bRequireValidIndex )
|
|
{
|
|
local int Row, NewIndex;
|
|
|
|
// Figure out which Item we're clicking on
|
|
Row = Ceil( (Controller.MouseY - ClientBounds[1]) / ItemHeight );
|
|
|
|
NewIndex = FindVisibleIndex(Top, Row);
|
|
return Min( NewIndex, ItemCount - 1 );
|
|
}
|
|
|
|
function Sort()
|
|
{
|
|
SortList();
|
|
}
|
|
|
|
function int SetIndex(int NewIndex)
|
|
{
|
|
if ( IsValidIndex(NewIndex) && !Elements[NewIndex].bEnabled )
|
|
{
|
|
Expand(NewIndex);
|
|
UpdateVisibleCount();
|
|
}
|
|
|
|
return Super.SetIndex(NewIndex);
|
|
}
|
|
|
|
function SetTopItem(int Item)
|
|
{
|
|
Top = Item;
|
|
|
|
if ( Top < 0 )
|
|
Top = 0;
|
|
else if ( FindVisibleIndex(Top,ItemsPerPage) >= ItemCount )
|
|
Top = FindVisibleIndex(ItemCount - 1, -ItemsPerPage);
|
|
|
|
if ( ItemCount <= 0 )
|
|
Top = 0;
|
|
else Top = Clamp(Top, 0, ItemCount - 1);
|
|
|
|
if ( bNotify )
|
|
CheckLinkedObjects(Self);
|
|
|
|
OnAdjustTop(Self);
|
|
|
|
if ( MyScrollBar != None )
|
|
MyScrollBar.AlignThumb();
|
|
}
|
|
|
|
function bool Up()
|
|
{
|
|
local int NewIndex;
|
|
|
|
if ( ItemCount < 2 || Index == 0 )
|
|
return True;
|
|
|
|
NewIndex = Index;
|
|
while ( --NewIndex >= 0 && !Elements[NewIndex].bEnabled );
|
|
|
|
SetIndex( Max(0, NewIndex) );
|
|
if ( MyScrollBar != None )
|
|
MyScrollBar.AlignThumb();
|
|
|
|
return true;
|
|
}
|
|
|
|
function bool Down()
|
|
{
|
|
local int NewIndex;
|
|
|
|
if ( ItemCount < 2 || Index == ItemCount - 1 )
|
|
return true;
|
|
|
|
// Scan down until an enabled element is found
|
|
NewIndex = Index;
|
|
while ( ++NewIndex < ItemCount && !Elements[NewIndex].bEnabled );
|
|
|
|
SetIndex(Min(NewIndex, ItemCount - 1));
|
|
if ( MyScrollBar != None )
|
|
MyScrollBar.AlignThumb();
|
|
|
|
return true;
|
|
}
|
|
|
|
function End()
|
|
{
|
|
local int NewIndex;
|
|
|
|
if ( ItemCount < 2 )
|
|
return;
|
|
|
|
// Scan up from bottom until enabled element found
|
|
NewIndex = FindVisibleIndex(ItemCount - 1, 1);
|
|
if ( IsValidIndex(NewIndex) )
|
|
{
|
|
SetIndex(NewIndex);
|
|
if ( MyScrollBar != None )
|
|
MyScrollBar.AlignThumb();
|
|
}
|
|
}
|
|
|
|
function PgUp()
|
|
{
|
|
if ( ItemCount < 2 )
|
|
return;
|
|
|
|
UpdateVisibleCount();
|
|
Super.PgUp();
|
|
}
|
|
|
|
function PgDn()
|
|
{
|
|
if ( ItemCount < 2 )
|
|
return;
|
|
|
|
UpdateVisibleCount();
|
|
Super.PgDn();
|
|
}
|
|
|
|
// =====================================================================================================================
|
|
// =====================================================================================================================
|
|
// Drag-n-drop interface
|
|
// =====================================================================================================================
|
|
// =====================================================================================================================
|
|
|
|
// Called on the drop source when when an Item has been dropped. bAccepted tells it whether
|
|
// the operation was successful or not.
|
|
function InternalOnEndDrag(GUIComponent Accepting, bool bAccepted)
|
|
{
|
|
local int i;
|
|
|
|
// log(Name@"InternalOnEndDrag Accepting:"$Accepting@"bAccepted:"$bAccepted);
|
|
if (bAccepted && Accepting != None)
|
|
{
|
|
GetPendingElements();
|
|
if ( Accepting != Self )
|
|
{
|
|
for ( i = 0; i < SelectedElements.Length; i++ )
|
|
RemoveElement(SelectedElements[i]);
|
|
}
|
|
|
|
bRepeatClick = False;
|
|
}
|
|
|
|
// Simulate repeat click if the operation was a failure to prevent InternalOnMouseRelease from clearing
|
|
// the SelectedItems array
|
|
// This way we don't lose the items we clicked on
|
|
if (Accepting == None)
|
|
bRepeatClick = True;
|
|
|
|
SetOutlineAlpha(255);
|
|
if ( bNotify )
|
|
CheckLinkedObjects(Self);
|
|
}
|
|
|
|
// Called on the drop target when the mouse is released - Sender is always DropTarget
|
|
function bool InternalOnDragDrop(GUIComponent Sender)
|
|
{
|
|
local array<GUITreeNode> NewItem;
|
|
local GUITreeList SourceTree;
|
|
local int i;
|
|
// log(Name@"InternalOnDragDrop Sender:"$ Sender);
|
|
|
|
if (Controller.DropTarget == Self)
|
|
{
|
|
if (Controller.DropSource != None && GUITreeList(Controller.DropSource) != None)
|
|
{
|
|
SourceTree = GUITreeList(Controller.DropSource);
|
|
NewItem = SourceTree.GetPendingElements();
|
|
|
|
// Special case for drag-n-drop between the same list.
|
|
if ( Controller.DropSource == Self )
|
|
{
|
|
for ( i = NewItem.Length - 1; i >= 0; i-- )
|
|
RemoveElement(NewItem[i],,True);
|
|
}
|
|
|
|
if ( !IsValidIndex(DropIndex) )
|
|
DropIndex = ItemCount;
|
|
|
|
for ( i = NewItem.Length-1; i >= 0; i-- )
|
|
InsertItem(DropIndex, NewItem[i].Caption, NewItem[i].Value, NewItem[i].ParentCaption, NewItem[i].Level, NewItem[i].bEnabled, NewItem[i].ExtraInfo);
|
|
|
|
SetIndex(DropIndex);
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
function ClearPendingElements()
|
|
{
|
|
Super.ClearPendingElements();
|
|
if ( SelectedItems.Length == 0 )
|
|
SelectedElements.Remove(0, SelectedElements.Length);
|
|
}
|
|
|
|
function array<GUITreeNode> GetPendingElements(optional bool bGuarantee)
|
|
{
|
|
local int i;
|
|
|
|
if ( (DropState == DRP_Source && Controller.DropSource == Self) || bGuarantee )
|
|
{
|
|
if ( SelectedElements.Length == 0 )
|
|
{
|
|
for (i = 0; i < SelectedItems.Length; i++)
|
|
if (IsValidIndex(SelectedItems[i]) && ValidSelectionAt(SelectedItems[i]))
|
|
SelectedElements[SelectedElements.Length] = Elements[SelectedItems[i]];
|
|
|
|
if ( SelectedElements.Length == 0 && IsValid() && ValidSelection() )
|
|
{
|
|
SelectedElements.Length = SelectedElements.Length + 1;
|
|
GetElementAtIndex(Index,SelectedElements[0]);
|
|
}
|
|
}
|
|
|
|
return SelectedElements;
|
|
}
|
|
}
|
|
|
|
defaultproperties
|
|
{
|
|
bAllowDuplicateCaption=True
|
|
bGroupItems=True
|
|
}
|