/* ** 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 . */ /*********************************************************************************************** *** 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 ; }