This repository has been archived on 2025-02-27. You can view files and clone it, but cannot push or open issues or pull requests.
CnC_Renegade/Code/wwui/treectrl.cpp

1669 lines
37 KiB
C++

/*
** Command & Conquer Renegade(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/***********************************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***********************************************************************************************
* *
* Project Name : wwui *
* *
* $Archive:: /Commando/Code/wwui/treectrl.cpp $*
* *
* Author:: Patrick Smith *
* *
* $Modtime:: 11/08/01 5:56p $*
* *
* $Revision:: 2 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#include "treectrl.h"
#include "assetmgr.h"
#include "refcount.h"
#include "font3d.h"
#include "mousemgr.h"
#include "ww3d.h"
#include "dialogmgr.h"
#include "dialogbase.h"
#include "stylemgr.h"
////////////////////////////////////////////////////////////////
// Local constants
////////////////////////////////////////////////////////////////
static const char *PLUS_IMAGE = "if_treeplus.tga";
static const RectClass PLUS_RECT = RectClass (0, 0, 16, 16);
static const RectClass MINUS_RECT = RectClass (16, 0, 32, 16);
////////////////////////////////////////////////////////////////
// Static member initialization
////////////////////////////////////////////////////////////////
TREECTRL_SORT_CALLBACK TreeCtrlClass::CurrentSortCallback = NULL;
TreeCtrlClass * TreeCtrlClass::CurrentSorter = NULL;
uint32 TreeCtrlClass::CurrentSortUserData = 0;
const char * TreeCtrlClass::ICON_FOLDER = "if_treefld.tga";
const char * TreeCtrlClass::ICON_FOLDER_OPEN = "if_treefld_open.tga";
////////////////////////////////////////////////////////////////
//
// TreeCtrlClass
//
////////////////////////////////////////////////////////////////
TreeCtrlClass::TreeCtrlClass (void) :
SelectedItem (NULL),
ScrollPos (0),
RowHeight (0),
RowsPerPage (0),
IsScrollBarDisplayed (false)
{
//
// Set the font for the text renderers
//
StyleMgrClass::Assign_Font (&TextRenderer, StyleMgrClass::FONT_LISTS);
StyleMgrClass::Configure_Renderer (&ControlRenderer);
StyleMgrClass::Configure_Renderer (&PlusRenderer);
StyleMgrClass::Configure_Renderer (&IconRenderer);
HilightRenderer.Set_Coordinate_Range (Render2DClass::Get_Screen_Resolution());
StyleMgrClass::Configure_Hilighter (&HilightRenderer);
//
// Load the "plus" texture
//
TextureClass *texture = WW3DAssetManager::Get_Instance ()->Get_Texture (PLUS_IMAGE, TextureClass::MIP_LEVELS_1);
if (texture != NULL) {
PlusRenderer.Set_Texture (texture);
REF_PTR_RELEASE (texture);
}
TextRenderer.Set_Texture_Size_Hint (256);
//
// We don't want the scroll bar getting focus
//
ScrollBarCtrl.Set_Wants_Focus (false);
ScrollBarCtrl.Set_Advise_Sink (this);
ScrollBarCtrl.Set_Is_Embedded (true);
return ;
}
////////////////////////////////////////////////////////////////
//
// ~TreeCtrlClass
//
////////////////////////////////////////////////////////////////
TreeCtrlClass::~TreeCtrlClass (void)
{
ScrollBarCtrl.Set_Advise_Sink (NULL);
if (Parent != NULL) {
Parent->Remove_Control (&ScrollBarCtrl);
}
return ;
}
////////////////////////////////////////////////////////////////
//
// Create_Text_Renderers
//
////////////////////////////////////////////////////////////////
void
TreeCtrlClass::Create_Text_Renderers (void)
{
//
// Configure the renderers
//
HilightRenderer.Reset ();
TextRenderer.Reset ();
IconRenderer.Reset ();
PlusRenderer.Reset ();
IconMgr.Reset_Renderers ();
float x_pos = ClientRect.Left;
float y_pos = ClientRect.Top;
int row_index = 0;
int level = 0;
//
// Render each row...
//
for (int index = 0; index < ItemList.Count (); index ++) {
TreeItemClass *item = ItemList[index];
if (Render_Item (item, x_pos, y_pos, row_index, level) == false) {
break;
}
}
return ;
}
////////////////////////////////////////////////////////////////
//
// Render_Item
//
////////////////////////////////////////////////////////////////
bool
TreeCtrlClass::Render_Item (TreeItemClass *item, float x_pos, float &y_pos, int &row_index, int level)
{
bool retval = true;
if (item == NULL) {
return false;
}
//
// Determine which color to draw the outline in
//
int color = StyleMgrClass::Get_Line_Color ();
if (IsEnabled == false) {
color = StyleMgrClass::Get_Disabled_Line_Color ();
}
const float LINE_START = 16.0F * StyleMgrClass::Get_X_Scale ();
const float LINE_SPACING = 16.0F * StyleMgrClass::Get_X_Scale ();
const float TEXT_SPACING = 4.0F * StyleMgrClass::Get_X_Scale ();
//
// Build the display rectangles
//
RectClass rect;
rect.Left = ClientRect.Left + LINE_START + int(level * LINE_SPACING);
rect.Top = y_pos;
rect.Right = ClientRect.Right;
rect.Bottom = y_pos + RowHeight;
RectClass plus_rect;
plus_rect.Left = int(rect.Left - LINE_SPACING);
plus_rect.Top = int(rect.Center ().Y - (LINE_SPACING * 0.5F));
plus_rect.Right = int((plus_rect.Left + LINE_SPACING));
plus_rect.Bottom = int(rect.Center ().Y + (LINE_SPACING * 0.5F));
RectClass icon_rect;
icon_rect.Left = rect.Left;
icon_rect.Top = rect.Top;
icon_rect.Right = icon_rect.Left + LINE_SPACING;
icon_rect.Bottom = rect.Bottom;
RectClass text_rect;
text_rect.Left = icon_rect.Right + TEXT_SPACING;
text_rect.Top = rect.Top;
text_rect.Right = rect.Right;
text_rect.Bottom = rect.Bottom;
//
// Only draw the entry if its inside the view
//
if (row_index >= ScrollPos && ((y_pos + RowHeight) <= ClientRect.Bottom)) {
//
// Draw the tick to the left of the entry
//
if (level > 0) {
float half_rect = rect.Height () / 2;
Vector2 line_start (icon_rect.Left - (LINE_SPACING * 0.5F), rect.Top + half_rect);
Vector2 line_end (icon_rect.Left, rect.Top + half_rect);
ControlRenderer.Add_Line (line_start, line_end, 1.0F, color);
}
//
// Draw the plus (if necessary)
//
if ((item->Get_Child_Count () > 0 || item->Needs_Children ())) {
RectClass plus_uv_rect = PLUS_RECT;
if (item->Is_Expanded ()) {
plus_uv_rect = MINUS_RECT;
}
plus_uv_rect.Inverse_Scale (Vector2 (32.0F, 32.0F));
PlusRenderer.Add_Quad (plus_rect, plus_uv_rect);
}
//
// Render the icon
//
const char *icon_name = item->Get_Icon ();
if (SelectedItem == item) {
icon_name = item->Get_Selected_Icon ();
}
if (icon_name != NULL && icon_name[0] != 0) {
IconMgr.Render_Icon (icon_rect, icon_name);
}
//
// Draw the text
//
StyleMgrClass::Render_Text (item->Get_Name (), &TextRenderer, text_rect, true, true,
StyleMgrClass::LEFT_JUSTIFY, IsEnabled);
//
// Adjust the width of the rectangle so its the approximate size
// of the text
//
float width = TextRenderer.Get_Text_Extents (item->Get_Name ()).X;
text_rect.Right = text_rect.Left + width + TEXT_SPACING;
//
// Is this the selected item?
//
if (SelectedItem == item) {
if (HasFocus) {
RectClass hilight_rect = text_rect;
hilight_rect.Right -= 1.0F;
hilight_rect.Bottom -= 1.0F;
StyleMgrClass::Render_Hilight (&HilightRenderer, hilight_rect);
}
ControlRenderer.Add_Outline (text_rect, 1.0F, color);
}
//
// Move down one row
//
y_pos += RowHeight;
}
row_index ++;
retval = ((y_pos + RowHeight) <= ClientRect.Bottom);
//
// Should we render the children?
//
if (item->Is_Expanded ()) {
float start_y_pos = y_pos;
int start_row_index = max (row_index, ScrollPos);
int end_row_index = start_row_index;
//
// Render each child item
//
int child_count = item->Get_Child_Count ();
for (int index = 0; index < child_count; index ++) {
end_row_index = row_index + 1;
retval = Render_Item (item->Get_Child (index), x_pos, y_pos, row_index, level + 1);
}
//
// Render the line connecting this item to all its children
//
if ( end_row_index != start_row_index &&
end_row_index >= ScrollPos &&
start_row_index <= (ScrollPos + RowsPerPage))
{
float end_y_pos = start_y_pos + (end_row_index - start_row_index) * RowHeight;
//
// Clip the line start and end to the window
//
end_y_pos = min (end_y_pos, ClientRect.Top + (RowsPerPage * RowHeight));
start_y_pos = max (start_y_pos, ClientRect.Top);
//
// Render the line
//
if (start_y_pos < end_y_pos) {
float icon_x_pos = icon_rect.Center ().X;
Vector2 line_start (icon_x_pos, start_y_pos);
Vector2 line_end (icon_x_pos, end_y_pos - (RowHeight * 0.5F));
ControlRenderer.Add_Line (line_start, line_end, 1.0F, color);
}
}
}
return retval;
}
////////////////////////////////////////////////////////////////
//
// Set_Scroll_Pos
//
////////////////////////////////////////////////////////////////
void
TreeCtrlClass::Set_Scroll_Pos (int new_pos)
{
//
// Ensure this scroll position doesn't extend out of bounds
//
int visible_rows = Count_Visible_Rows ();
new_pos = min (visible_rows - RowsPerPage, new_pos);
new_pos = max (new_pos, 0);
//
// Did anything change?
//
if (ScrollPos != new_pos) {
ScrollPos = new_pos;
ScrollBarCtrl.Set_Pos (ScrollPos, false);
Set_Dirty ();
}
return ;
}
////////////////////////////////////////////////////////////////
//
// Count_Visible_Rows
//
////////////////////////////////////////////////////////////////
int
TreeCtrlClass::Count_Visible_Rows (void)
{
int total = 0;
//
// Count each row
//
for (int index = 0; index < ItemList.Count (); index ++) {
TreeItemClass *item = ItemList[index];
//
// Count this item and it's children
//
total += Count_Visible_Rows (item);
}
return total;
}
////////////////////////////////////////////////////////////////
//
// Count_Visible_Rows
//
////////////////////////////////////////////////////////////////
int
TreeCtrlClass::Count_Visible_Rows (TreeItemClass *item)
{
if (item == NULL) {
return 0;
}
int total = 1;
//
// Should we count the children?
//
if (item->Is_Expanded ()) {
//
// Count each child
//
int child_count = item->Get_Child_Count ();
for (int index = 0; index < child_count; index ++) {
total += Count_Visible_Rows (item->Get_Child (index));
}
}
return total;
}
////////////////////////////////////////////////////////////////
//
// Create_Control_Renderers
//
////////////////////////////////////////////////////////////////
void
TreeCtrlClass::Create_Control_Renderers (void)
{
Render2DClass &renderer = ControlRenderer;
//
// Configure this renderer
//
renderer.Reset ();
renderer.Enable_Texturing (false);
//
// Determine which color to draw the outline in
//
int color = StyleMgrClass::Get_Line_Color ();
int bkcolor = StyleMgrClass::Get_Bk_Color ();
if (IsEnabled == false) {
color = StyleMgrClass::Get_Disabled_Line_Color ();
bkcolor = StyleMgrClass::Get_Disabled_Bk_Color ();
}
//
// Draw the outline
//
renderer.Add_Rect (Rect, 1.0F, color, bkcolor);
return ;
}
////////////////////////////////////////////////////////////////
//
// On_Set_Cursor
//
////////////////////////////////////////////////////////////////
void
TreeCtrlClass::On_Set_Cursor (const Vector2 &mouse_pos)
{
//
// Change the mouse cursor
//
MouseMgrClass::Set_Cursor (MouseMgrClass::CURSOR_ACTION);
return ;
}
////////////////////////////////////////////////////////////////
//
// Update_Client_Rect
//
////////////////////////////////////////////////////////////////
void
TreeCtrlClass::Update_Client_Rect (void)
{
const float BORDER_SIZE_X = 4.0F * StyleMgrClass::Get_X_Scale ();
const float BORDER_SIZE_Y = 4.0F * StyleMgrClass::Get_Y_Scale ();
//
// Set the client area
//
ClientRect = Rect;
ClientRect.Inflate (Vector2 (-BORDER_SIZE_X, -BORDER_SIZE_Y));
//
// Calculate how tall each row should be...
//
const float ROW_SPACING = 4.0F;
float border_height = (ROW_SPACING * StyleMgrClass::Get_Y_Scale ());
RowHeight = (TextRenderer.Get_Text_Extents (L"W").Y + border_height);
//
// Determine how many rows we can fit on a page
//
RowsPerPage = ClientRect.Height () / RowHeight;
ClientRect.Top = int(Rect.Center ().Y - (RowsPerPage * RowHeight * 0.5F));
ClientRect.Bottom = int(Rect.Center ().Y + (RowsPerPage * RowHeight * 0.5F));
//
// Choose an arbitrary width, the scroll bar
// will snap to the only width it supports
//
float width = 10;
//
// Calculate the scroll bar's rectangle
//
RectClass scroll_rect;
scroll_rect.Left = Rect.Right - width;
scroll_rect.Top = Rect.Top;
scroll_rect.Right = Rect.Right;
scroll_rect.Bottom = Rect.Bottom;
//
// Size the scroll bar
//
ScrollBarCtrl.Set_Window_Rect (scroll_rect);
Update_Scroll_Bar_Visibility ();
Set_Dirty ();
return ;
}
////////////////////////////////////////////////////////////////
//
// On_VScroll
//
////////////////////////////////////////////////////////////////
void
TreeCtrlClass::On_VScroll (ScrollBarCtrlClass *, int , int new_position)
{
Set_Scroll_Pos (new_position);
return ;
}
////////////////////////////////////////////////////////////////
//
// Render
//
////////////////////////////////////////////////////////////////
void
TreeCtrlClass::Render (void)
{
//
// Recreate the renderers (if necessary)
//
if (IsDirty) {
Create_Control_Renderers ();
Create_Text_Renderers ();
}
//
// Render the background and text for the current state
//
IconMgr.Render_Icons ();
TextRenderer.Render ();
ControlRenderer.Render ();
PlusRenderer.Render ();
HilightRenderer.Render ();
DialogControlClass::Render ();
return ;
}
////////////////////////////////////////////////////////////////
//
// Sort_Children_Alphabetically
//
////////////////////////////////////////////////////////////////
void
TreeCtrlClass::Sort_Children_Alphabetically (TreeItemClass *parent)
{
Sort_Children (parent, Alphabetic_Sort_Callback, 0);
return ;
}
////////////////////////////////////////////////////////////////
//
// Sort_Children
//
////////////////////////////////////////////////////////////////
void
TreeCtrlClass::Sort_Children (TreeItemClass *parent, TREECTRL_SORT_CALLBACK sort_callback, uint32 user_data)
{
CurrentSortCallback = sort_callback;
CurrentSorter = this;
CurrentSortUserData = user_data;
if (parent == NULL) {
//
// Sort the root-level entries
//
if (ItemList.Count () != 0) {
::qsort (&ItemList[0], ItemList.Count (), sizeof (ItemList[0]), Sort_Callback);
}
} else {
TREE_ITEM_LIST &child_list = parent->Get_Child_List ();
//
// Sort the immediate children of this item
//
if (child_list.Count () != 0) {
::qsort (&child_list[0], child_list.Count (), sizeof (child_list[0]), Sort_Callback);
}
}
return ;
}
////////////////////////////////////////////////////////////////
//
// Sort_Callback
//
////////////////////////////////////////////////////////////////
int __cdecl
TreeCtrlClass::Sort_Callback (const void *elem1, const void *elem2)
{
WWASSERT (elem1 != NULL);
WWASSERT (elem2 != NULL);
TreeItemClass *item1 = *((TreeItemClass **)elem1);
TreeItemClass *item2 = *((TreeItemClass **)elem2);
//
// Pass the sorting off to the user... (NOT THREAD SAFE)
//
return CurrentSortCallback (CurrentSorter, item1, item2, CurrentSortUserData);
}
////////////////////////////////////////////////////////////////
//
// Alphabetic_Sort_Callback
//
////////////////////////////////////////////////////////////////
int CALLBACK
TreeCtrlClass::Alphabetic_Sort_Callback
(
TreeCtrlClass * tree_ctrl,
TreeItemClass * item1,
TreeItemClass * item2,
uint32 user_param
)
{
//
// Sort by name
//
const WCHAR *name1 = item1->Get_Name ();
const WCHAR *name2 = item2->Get_Name ();
int result = ::CompareStringW (LOCALE_USER_DEFAULT, NORM_IGNORECASE, name1, -1, name2, -1);
int retval = 0;
if (result == CSTR_LESS_THAN) {
retval = -1;
} else if (result == CSTR_GREATER_THAN) {
retval = 1;
}
return retval;
}
////////////////////////////////////////////////////////////////
//
// Find_Top_Item
//
////////////////////////////////////////////////////////////////
TreeItemClass *
TreeCtrlClass::Find_Top_Item (void)
{
TreeItemClass *retval = NULL;
//
// Count visible items until we've hit the current scroll position
//
retval = ItemList.Count () > 0 ? ItemList[0] : NULL;
for (int index = 0; retval != NULL && index < ScrollPos; index ++) {
retval = Find_Next_Visible (retval);
}
return retval;
}
////////////////////////////////////////////////////////////////
//
// Find_Last_Visible_Item
//
////////////////////////////////////////////////////////////////
TreeItemClass *
TreeCtrlClass::Find_Last_Visible_Item (void)
{
//
// Determine where to start...
//
TreeItemClass *curr_item = NULL;
if (ItemList.Count () > 0) {
curr_item = ItemList[ItemList.Count () - 1];
}
//
// Keep searching until we stop finding new visible items...
//
while (curr_item != NULL) {
TreeItemClass *next_item = Find_Next_Visible (curr_item);
if (next_item == curr_item) {
break;
}
curr_item = next_item;
}
return curr_item;
}
////////////////////////////////////////////////////////////////
//
// Hit_Test
//
////////////////////////////////////////////////////////////////
TreeItemClass *
TreeCtrlClass::Hit_Test (const Vector2 &mouse_pos, HITTYPE &type)
{
TreeItemClass *retval = NULL;
//
// Attempt to locate the first visible item
//
TreeItemClass *hit_item = Find_Top_Item ();
if (hit_item != NULL) {
//
// Determine on which row of the screen the user clicked
//
int row_index = (mouse_pos.Y - ClientRect.Top) / RowHeight;
//
// Now move down that number of items...
//
for (int index = 0; hit_item != NULL && index < row_index; index ++) {
hit_item = Find_Next_Visible (hit_item);
}
const float LINE_START = 16.0F * StyleMgrClass::Get_X_Scale ();
const float LINE_SPACING = 16.0F * StyleMgrClass::Get_X_Scale ();
const float TEXT_SPACING = 4.0F * StyleMgrClass::Get_X_Scale ();
//
// Now determine on what part of the item the user clicked...
//
if (hit_item != NULL) {
int level = hit_item->Get_Indent_Level ();
float icon_left = ClientRect.Left + LINE_START + int(level * LINE_SPACING);
float plus_left = icon_left - LINE_SPACING;
float text_left = icon_left + LINE_SPACING + TEXT_SPACING;
//
// Return the hit type and hit item to the caller
//
if (mouse_pos.X >= plus_left && mouse_pos.X < icon_left) {
type = HIT_PLUS;
retval = hit_item;
} else if (mouse_pos.X >= icon_left && mouse_pos.X < text_left) {
type = HIT_ICON;
retval = hit_item;
} else if (mouse_pos.X >= text_left) {
type = HIT_TEXT;
retval = hit_item;
}
}
}
return retval;
}
////////////////////////////////////////////////////////////////
//
// On_LButton_Down
//
////////////////////////////////////////////////////////////////
void
TreeCtrlClass::On_LButton_Down (const Vector2 &mouse_pos)
{
//
// Find which item hit clicked on
//
HITTYPE type;
TreeItemClass *tree_item = Hit_Test (mouse_pos, type);
if (tree_item != NULL) {
SelectedItem = tree_item;
//
// Toggle the expanded state if the user clicked on the
// plus
//
if (type == HIT_PLUS) {
tree_item->Expand (!tree_item->Is_Expanded ());
}
Set_Dirty ();
}
return ;
}
////////////////////////////////////////////////////////////////
//
// On_LButton_DblClk
//
////////////////////////////////////////////////////////////////
void
TreeCtrlClass::On_LButton_DblClk (const Vector2 &mouse_pos)
{
//
// Find which item hit clicked on
//
HITTYPE type;
TreeItemClass *tree_item = Hit_Test (mouse_pos, type);
if (tree_item != NULL) {
SelectedItem = tree_item;
//
// Toggle the expanded state if the user clicked on the
// plus
//
tree_item->Expand (!tree_item->Is_Expanded ());
Set_Dirty ();
}
return ;
}
////////////////////////////////////////////////////////////////
//
// On_LButton_Up
//
////////////////////////////////////////////////////////////////
void
TreeCtrlClass::On_LButton_Up (const Vector2 &mouse_pos)
{
return ;
}
////////////////////////////////////////////////////////////////
//
// On_Set_Focus
//
////////////////////////////////////////////////////////////////
void
TreeCtrlClass::On_Set_Focus (void)
{
Set_Dirty ();
DialogControlClass::On_Set_Focus ();
return ;
}
////////////////////////////////////////////////////////////////
//
// On_Kill_Focus
//
////////////////////////////////////////////////////////////////
void
TreeCtrlClass::On_Kill_Focus (DialogControlClass *focus)
{
Set_Dirty ();
DialogControlClass::On_Kill_Focus (focus);
return ;
}
////////////////////////////////////////////////////////////////
//
// On_Mouse_Wheel
//
////////////////////////////////////////////////////////////////
void
TreeCtrlClass::On_Mouse_Wheel (int direction)
{
if (direction < 0) {
Set_Scroll_Pos (ScrollPos - 1);
} else {
Set_Scroll_Pos (ScrollPos + 1);
}
Set_Dirty ();
return ;
}
////////////////////////////////////////////////////////////////
//
// On_Key_Down
//
////////////////////////////////////////////////////////////////
bool
TreeCtrlClass::On_Key_Down (uint32 key_id, uint32 key_data)
{
bool handled = false;
bool is_dirty = true;
switch (key_id)
{
case VK_HOME:
if (ItemList.Count () > 0) {
SelectedItem = ItemList[0];
Set_Scroll_Pos (0);
}
break;
case VK_END:
if (ItemList.Count () > 0) {
SelectedItem = Find_Last_Visible_Item ();
Ensure_Visible (SelectedItem);
}
break;
case VK_UP:
SelectedItem = Find_Prev_Visible (SelectedItem);
Ensure_Visible (SelectedItem);
Set_Dirty ();
break;
case VK_DOWN:
SelectedItem = Find_Next_Visible (SelectedItem);
Ensure_Visible (SelectedItem);
Set_Dirty ();
break;
case VK_RIGHT:
if (SelectedItem != NULL) {
SelectedItem->Expand (true);
}
break;
case VK_LEFT:
if (SelectedItem != NULL) {
SelectedItem->Expand (false);
}
break;
default:
is_dirty = false;
break;
}
if (is_dirty) {
Set_Dirty ();
}
return handled;
}
////////////////////////////////////////////////////////////////
//
// On_Create
//
////////////////////////////////////////////////////////////////
void
TreeCtrlClass::On_Create (void)
{
return ;
}
////////////////////////////////////////////////////////////////
//
// Ensure_Visible
//
////////////////////////////////////////////////////////////////
void
TreeCtrlClass::Ensure_Visible (TreeItemClass *item_to_find)
{
if (item_to_find == NULL) {
Set_Scroll_Pos (0);
Set_Dirty ();
return ;
}
//
// Start at the first visible item and keep going until we've found the
// item we're looking for
//
TreeItemClass *item = ItemList.Count () > 0 ? ItemList[0] : NULL;
for (int row_index = 0; item != NULL; row_index ++) {
if (item == item_to_find) {
//
// Determine if this row is outside the visible window
//
if (row_index < ScrollPos) {
Set_Scroll_Pos (row_index);
Set_Dirty ();
} else if (row_index >= ScrollPos + RowsPerPage) {
Set_Scroll_Pos (row_index - (RowsPerPage-1));
Set_Dirty ();
}
break;
}
//
// Advance to the next visible item
//
item = Find_Next_Visible (item);
}
return ;
}
////////////////////////////////////////////////////////////////
//
// Insert_Item
//
////////////////////////////////////////////////////////////////
TreeItemClass *
TreeCtrlClass::Insert_Item
(
const WCHAR * name,
const char * icon_name,
const char * selected_icon_name,
TreeItemClass * parent
)
{
//
// Allocate a new tree item
//
TreeItemClass *retval = new TreeItemClass (this);
retval->Set_Name (name);
retval->Set_Icon (icon_name);
retval->Set_Selected_Icon (selected_icon_name);
//
// Now insert this item into the tree
//
if (parent != NULL) {
parent->Add_Child (retval);
retval->Set_Parent (parent);
} else {
ItemList.Add (retval);
//
// Select the first item by default
//
if (ItemList.Count () == 1) {
SelectedItem = retval;
}
}
//
// Check to see if we need a scroll bar or not
//
Update_Scroll_Bar_Visibility ();
Set_Dirty ();
return retval;
}
////////////////////////////////////////////////////////////////
//
// Delete_Item
//
////////////////////////////////////////////////////////////////
void
TreeCtrlClass::Delete_Item (TreeItemClass *item)
{
if (item == NULL) {
return ;
}
//
// Remove the selected item if necessary
//
if (SelectedItem == item) {
SelectedItem = NULL;
}
//
// Notify any advise sinks
//
ADVISE_NOTIFY (On_TreeCtrl_Delete_Item (this, Get_ID (), item));
//
// Recursively delete all this item's children
//
int child_count = item->Get_Child_Count ();
for (int index = 0; index < child_count; index ++) {
TreeItemClass *child = item->Get_Child (index);
Delete_Item (child);
}
//
// Now unlink this child from its parent (or the root list)
//
TreeItemClass *parent = item->Get_Parent ();
if (parent != NULL) {
parent->Remove_Child (item);
} else {
//
// Remove this item from the root list
//
index = ItemList.ID (item);
if (index != -1) {
ItemList.Delete (index);
}
}
//
// Free this item
//
delete item;
item = NULL;
//
// Ensure the scroll bar is correctly displayed
//
Update_Scroll_Bar_Visibility ();
Set_Dirty ();
return ;
}
////////////////////////////////////////////////////////////////
//
// Delete_All_Items
//
////////////////////////////////////////////////////////////////
void
TreeCtrlClass::Delete_All_Items (void)
{
//
// Note: We count down because the Delete_Item () call will
// delete the entry from the list...
//
int index = ItemList.Count ();
while (index --) {
Delete_Item (ItemList[index]);
}
//
// Ensure the scroll bar is correctly displayed
//
ScrollPos = 0;
SelectedItem = NULL;
Update_Scroll_Bar_Visibility ();
return ;
}
////////////////////////////////////////////////////////////////
//
// Select_Item
//
////////////////////////////////////////////////////////////////
void
TreeCtrlClass::Select_Item (TreeItemClass *item)
{
//
// Notify any advise sinks
//
ADVISE_NOTIFY (On_TreeCtrl_Sel_Change (this, Get_ID (), SelectedItem, item));
SelectedItem = item;
Set_Dirty ();
return ;
}
////////////////////////////////////////////////////////////////
//
// Get_Selected_Item
//
////////////////////////////////////////////////////////////////
TreeItemClass *
TreeCtrlClass::Get_Selected_Item (void)
{
return SelectedItem;
}
////////////////////////////////////////////////////////////////
//
// Get_Next_Sibling
//
////////////////////////////////////////////////////////////////
TreeItemClass *
TreeCtrlClass::Get_Next_Sibling (TreeItemClass *item)
{
TreeItemClass *retval = NULL;
if (item == NULL) {
return NULL;
}
//
// If this item has a parent, then ask it for the sibling...
//
TreeItemClass *parent = item->Get_Parent ();
if (parent != NULL) {
retval = parent->Get_Next_Child (item);
} else {
//
// Return the next entry in the root item list...
//
int index = ItemList.ID (item);
if (index != -1 && (index + 1) < ItemList.Count ()) {
retval = ItemList[index + 1];
}
}
return retval;
}
////////////////////////////////////////////////////////////////
//
// Get_Prev_Sibling
//
////////////////////////////////////////////////////////////////
TreeItemClass *
TreeCtrlClass::Get_Prev_Sibling (TreeItemClass *item)
{
TreeItemClass *retval = NULL;
if (item == NULL) {
return NULL;
}
//
// If this item has a parent, then ask it for the sibling...
//
TreeItemClass *parent = item->Get_Parent ();
if (parent != NULL) {
retval = parent->Get_Prev_Child (item);
} else {
//
// Return the previous entry in the root item list...
//
int index = ItemList.ID (item);
if (index > 0) {
retval = ItemList[index - 1];
}
}
return retval;
}
////////////////////////////////////////////////////////////////
//
// Find_Prev_Visible
//
////////////////////////////////////////////////////////////////
TreeItemClass *
TreeCtrlClass::Find_Prev_Visible (TreeItemClass *item)
{
TreeItemClass *retval = NULL;
if (item == NULL) {
if (ItemList.Count () > 0) {
return ItemList[0];
}
return NULL;
}
//
// Simply find the previous sibling of this item.
// If you can't find the sibling, then return its parent
//
retval = Get_Prev_Sibling (item);
if (retval == NULL) {
retval = item->Get_Parent ();
} else {
while (retval->Is_Expanded ()) {
//
// Find the last child for this given item
//
TreeItemClass *curr_item = retval->Get_Last_Child ();
if (curr_item != NULL) {
retval = curr_item;
} else {
break;
}
}
}
//
// If we didn't find a previous visible entry, then
// simply return the item we were given
//
if (retval == NULL) {
retval = item;
}
return retval;
}
////////////////////////////////////////////////////////////////
//
// Find_Next_Visible
//
////////////////////////////////////////////////////////////////
TreeItemClass *
TreeCtrlClass::Find_Next_Visible (TreeItemClass *item)
{
TreeItemClass *retval = NULL;
if (item == NULL) {
if (ItemList.Count () > 0) {
return ItemList[0];
}
return NULL;
}
//
// First try to get this item's first child...
//
if (item->Is_Expanded () && item->Get_Child_Count () > 0) {
retval = item->Get_Child (0);
}
if (retval == NULL) {
//
// Try to find this item's next sibling...
//
retval = Get_Next_Sibling (item);
if (retval == NULL) {
//
// Loop outwards through each parent until
// we can find the next sibling of any parent...
//
TreeItemClass *curr_item = item;
while (curr_item != NULL && retval == NULL) {
curr_item = curr_item->Get_Parent ();
if (curr_item != NULL) {
retval = Get_Next_Sibling (curr_item);
}
}
}
}
//
// If we didn't find a previous visible entry, then
// simply return the item we were given
//
if (retval == NULL) {
retval = item;
}
return retval;
}
////////////////////////////////////////////////////////////////
//
// Update_Scroll_Bar_Visibility
//
////////////////////////////////////////////////////////////////
void
TreeCtrlClass::Update_Scroll_Bar_Visibility (void)
{
int visible_rows = Count_Visible_Rows ();
//
// Determine if we have more entries then we can
// display on one page
//
bool needs_scrollbar = (visible_rows > RowsPerPage);
float new_right = 0;
if (needs_scrollbar != IsScrollBarDisplayed) {
//
// Do we need to show a scroll bar?
//
if (needs_scrollbar) {
new_right = ScrollBarCtrl.Get_Window_Rect ().Left;
IsScrollBarDisplayed = true;
} else if (Parent != NULL) {
new_right = ScrollBarCtrl.Get_Window_Rect ().Right;
IsScrollBarDisplayed = false;
}
const float BORDER_SIZE_X = 4.0F * StyleMgrClass::Get_X_Scale ();
//
// Reset our window size (as necessary)
//
Rect.Right = new_right;
ClientRect.Left = Rect.Left + BORDER_SIZE_X;
ClientRect.Right = Rect.Right - BORDER_SIZE_X;
//
// Add or remove the scrollbar (as necessary)
//
if (IsScrollBarDisplayed) {
Parent->Add_Control (&ScrollBarCtrl);
} else {
Parent->Remove_Control (&ScrollBarCtrl);
}
}
//
// Configure the scroll-bar's ranges
//
ScrollBarCtrl.Set_Page_Size (RowsPerPage);
ScrollBarCtrl.Set_Range (0, visible_rows - RowsPerPage);
ScrollBarCtrl.Set_Pos (ScrollPos);
ScrollPos = ScrollBarCtrl.Get_Pos ();
return ;
}
////////////////////////////////////////////////////////////////
//
// On_Expanded
//
////////////////////////////////////////////////////////////////
void
TreeCtrlClass::On_Expanded (TreeItemClass *item)
{
//
// Notify any advise sinks
//
ADVISE_NOTIFY (On_TreeCtrl_Expand_Item (this, Get_ID (), item));
//
// Check to see if this item needs its children filled in. If so,
// then notify the advise sinks
//
if (item != NULL && item->Is_Expanded () && item->Needs_Children ()) {
ADVISE_NOTIFY (On_TreeCtrl_Needs_Children (this, Get_ID (), item));
}
return ;
}
//********************************************************************************//
//
// Start of TreeItemClass
//
//********************************************************************************//
////////////////////////////////////////////////////////////////
//
// Remove_Child
//
////////////////////////////////////////////////////////////////
void
TreeItemClass::Remove_Child (TreeItemClass *child)
{
int index = ChildList.ID (child);
if (index != -1) {
ChildList.Delete (index);
}
return ;
}
////////////////////////////////////////////////////////////////
//
// Expand
//
////////////////////////////////////////////////////////////////
void
TreeItemClass::Expand (bool onoff)
{
IsExpanded = onoff;
TreeCtrl->On_Expanded (this);
TreeCtrl->Select_Item (this);
TreeCtrl->Update_Scroll_Bar_Visibility ();
TreeCtrl->Set_Dirty ();
return ;
}
////////////////////////////////////////////////////////////////
//
// Set_Name
//
////////////////////////////////////////////////////////////////
void
TreeItemClass::Set_Name (const WCHAR *name)
{
Name = name;
TreeCtrl->Set_Dirty ();
return ;
}
////////////////////////////////////////////////////////////////
//
// Set_Icon
//
////////////////////////////////////////////////////////////////
void
TreeItemClass::Set_Icon (const char *texture_name)
{
IconName = texture_name;
//
// Add this icon to our manager
//
if (texture_name != NULL) {
TreeCtrl->IconMgr.Add_Icon (texture_name);
}
TreeCtrl->Set_Dirty ();
return ;
}
////////////////////////////////////////////////////////////////
//
// Set_Selected_Icon
//
////////////////////////////////////////////////////////////////
void
TreeItemClass::Set_Selected_Icon (const char *texture_name)
{
SelectedIconName = texture_name;
//
// Add this icon to our manager
//
if (texture_name != NULL) {
TreeCtrl->IconMgr.Add_Icon (texture_name);
}
TreeCtrl->Set_Dirty ();
return ;
}
////////////////////////////////////////////////////////////////
//
// Get_Prev_Child
//
////////////////////////////////////////////////////////////////
TreeItemClass *
TreeItemClass::Get_Prev_Child (TreeItemClass *child)
{
TreeItemClass *retval = NULL;
if (child == NULL) {
return NULL;
}
//
// Return the previous entry in our list...
//
int index = ChildList.ID (child);
if (index > 0) {
retval = ChildList[index - 1];
}
return retval;
}
////////////////////////////////////////////////////////////////
//
// Get_Prev_Child
//
////////////////////////////////////////////////////////////////
TreeItemClass *
TreeItemClass::Get_Next_Child (TreeItemClass *child)
{
TreeItemClass *retval = NULL;
if (child == NULL) {
return NULL;
}
//
// Return the next entry in our list...
//
int index = ChildList.ID (child);
if (index != -1 && (index + 1) < ChildList.Count ()) {
retval = ChildList[index + 1];
}
return retval;
}
////////////////////////////////////////////////////////////////
//
// Get_Indent_Level
//
////////////////////////////////////////////////////////////////
int
TreeItemClass::Get_Indent_Level (void)
{
int retval = 0;
//
// Count the number of parents this item has
//
TreeItemClass *curr_item = this;
while ((curr_item = curr_item->Get_Parent ()) != NULL) {
retval ++;
}
return retval;
}
////////////////////////////////////////////////////////////////
//
// Get_Last_Child
//
////////////////////////////////////////////////////////////////
TreeItemClass *
TreeItemClass::Get_Last_Child (void)
{
TreeItemClass *retval = NULL;
if (ChildList.Count () > 0) {
retval = ChildList[ChildList.Count () - 1];
}
return retval;
}
////////////////////////////////////////////////////////////////
//
// Set_Needs_Children
//
////////////////////////////////////////////////////////////////
void
TreeItemClass::Set_Needs_Children (bool onoff)
{
NeedsChildren = onoff;
TreeCtrl->Set_Dirty ();
return ;
}