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/listctrl.cpp

2512 lines
54 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 : Combat *
* *
* $Archive:: /Commando/Code/wwui/listctrl.cpp $*
* *
* Author:: Patrick Smith *
* *
* $Modtime:: 3/01/02 4:37p $*
* *
* $Revision:: 41 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#include "listctrl.h"
#include "assetmgr.h"
#include "refcount.h"
#include "mousemgr.h"
#include "ww3d.h"
#include "dialogmgr.h"
#include "dialogbase.h"
#include "stylemgr.h"
#include <commctrl.h>
////////////////////////////////////////////////////////////////
// Local constants
////////////////////////////////////////////////////////////////
const float PULSE_RATE = 2.0F;
const int ROW_SPACING = 4;
////////////////////////////////////////////////////////////////
//
// ListCtrlClass
//
////////////////////////////////////////////////////////////////
ListCtrlClass::ListCtrlClass (void) :
CurrState (NORMAL),
ScrollPos (0),
HeaderRect (0, 0, 0, 0),
TextRect (0, 0, 0, 0),
CurrSel (-1),
RowBorderHeight (0),
PulsePercent (1.0F),
PulseDirection (1.0F),
IsScrollBarDisplayed (false),
LastPageTopEntryIndex (0),
IsSelectionAllowed(true),
IsNoSelectionAllowed(false),
IsMultipleSelection (false),
SortColumn (0),
SortType (SORT_NONE),
MinRowHeight (0)
{
//
// Configure each renderer
//
StyleMgrClass::Configure_Renderer (&ControlRenderer);
StyleMgrClass::Configure_Renderer (&UnderlineRenderer);
StyleMgrClass::Configure_Renderer (&HilightRenderer);
StyleMgrClass::Configure_Hilighter (&HilightRenderer);
//
// Set the font for each text renderer
//
StyleMgrClass::Assign_Font (&HeaderRenderer, StyleMgrClass::FONT_HEADER);
StyleMgrClass::Assign_Font (&TextRenderer, StyleMgrClass::FONT_LISTS);
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 ;
}
////////////////////////////////////////////////////////////////
//
// ~ListCtrlClass
//
////////////////////////////////////////////////////////////////
ListCtrlClass::~ListCtrlClass (void)
{
Delete_All_Entries ();
if (Parent != NULL) {
Parent->Remove_Control (&ScrollBarCtrl);
}
return ;
}
void ListCtrlClass::Set_Tabstop(float stop)
{
TextRenderer.Set_Tabstop(stop);
}
////////////////////////////////////////////////////////////////
//
// Create_Control_Renderer
//
////////////////////////////////////////////////////////////////
void
ListCtrlClass::Create_Control_Renderer (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 (CurrState == DISABLED) {
color = StyleMgrClass::Get_Disabled_Line_Color ();
bkcolor = StyleMgrClass::Get_Disabled_Bk_Color ();
}
//
// Draw the outline
//
renderer.Add_Rect (Rect, 1.0F, color, bkcolor);
//
// Draw the selection bar if necessary
//
if (CurrSel != -1) {
//
// Calculate what percentage our pulse effect is
//
PulsePercent += PulseDirection * PULSE_RATE * (DialogMgrClass::Get_Frame_Time () / 1000.0F);
if (PulsePercent < 0 || PulsePercent > 1.0F) {
PulseDirection = -PulseDirection;
PulsePercent = WWMath::Clamp (PulsePercent, 0, 1);
}
//
// If this control has the focus then apply our pulsating effect to
// the color of selection bar.
//
if (HasFocus) {
float red = ((color & 0x00FF0000) >> 16) / 256.0F;
float green = ((color & 0x0000FF00) >> 8) / 256.0F;
float blue = ((color & 0x000000FF)) / 256.0F;
float percent = (PulsePercent * 0.5F) + 0.5F;
color = VRGB_TO_INT32 (Vector3 (red * percent, green * percent, blue * percent));
}
//
// Get the hilight rect
//
RectClass sel_rect;
Get_Entry_Rect (CurrSel, sel_rect);
if (sel_rect.Top >= TextRect.Top && sel_rect.Bottom <= TextRect.Bottom) {
//
// Render the outline of the selection (if its on the screen)
//
renderer.Add_Outline (sel_rect, 1.0F, color);
}
}
return ;
}
////////////////////////////////////////////////////////////////
//
// Create_Text_Renderers
//
////////////////////////////////////////////////////////////////
void
ListCtrlClass::Create_Text_Renderers (void)
{
//
// Configure the text renderers
//
HeaderRenderer.Reset ();
TextRenderer.Reset ();
HilightRenderer.Reset ();
UnderlineRenderer.Reset ();
IconMgr.Reset_Renderers ();
//
// Prepare the column header (if necessary)
//
if ((Style & LVS_NOCOLUMNHEADER) == 0) {
//
// Render each column header
//
int x_pos = HeaderRect.Left;
for (int index = 0; index < ColList.Count (); index ++) {
//
// Build a bounding rectangle for this column header
//
RectClass rect = HeaderRect;
rect.Left = x_pos;
rect.Right = x_pos + (ColList[index]->Get_Width () * HeaderRect.Width ());
//
// Let the last column extend to the edge
//
if (index == ColList.Count () - 1) {
rect.Right = HeaderRect.Right;
}
//
// Get the color
//
int color = VRGB_TO_INT32 (ColList[index]->Get_Color ());
//
// Underline the header
//
Vector2 text_extent = HeaderRenderer.Get_Text_Extents (ColList[index]->Get_Name ());
float y_pos = (rect.Top + (rect.Height () / 2) + (text_extent.Y / 2)) + 2.0F;
UnderlineRenderer.Add_Line (Vector2 (rect.Left, y_pos),
Vector2 (rect.Left + text_extent.X, y_pos), 1.0F, color);
//
// Draw the sort designator
//
if (SortType != SORT_NONE && SortColumn == index) {
const float TRI_WIDTH = 8;
const float TRI_SPACE = 2;
float tri_size = TRI_WIDTH * StyleMgrClass::Get_Y_Scale ();
float tri_half_size = tri_size * 0.5F;
float tri_x_pos = rect.Left + text_extent.X + tri_size + (TRI_SPACE * StyleMgrClass::Get_Y_Scale ());
float tri_y_pos = HeaderRect.Center ().Y;
if (SortType == SORT_ASCENDING) {
UnderlineRenderer.Add_Line (Vector2 (tri_x_pos - tri_half_size, tri_y_pos + tri_half_size),
Vector2 (tri_x_pos + tri_half_size, tri_y_pos + tri_half_size), 1.0F, color);
UnderlineRenderer.Add_Line (Vector2 (tri_x_pos + tri_half_size, tri_y_pos + tri_half_size),
Vector2 (tri_x_pos, tri_y_pos - tri_half_size), 1.0F, color);
UnderlineRenderer.Add_Line (Vector2 (tri_x_pos, tri_y_pos - tri_half_size),
Vector2 (tri_x_pos - tri_half_size, tri_y_pos + tri_half_size), 1.0F, color);
} else {
UnderlineRenderer.Add_Line (Vector2 (tri_x_pos - tri_half_size, tri_y_pos - tri_half_size),
Vector2 (tri_x_pos + tri_half_size, tri_y_pos - tri_half_size), 1.0F, color);
UnderlineRenderer.Add_Line (Vector2 (tri_x_pos + tri_half_size, tri_y_pos - tri_half_size),
Vector2 (tri_x_pos, tri_y_pos + tri_half_size), 1.0F, color);
UnderlineRenderer.Add_Line (Vector2 (tri_x_pos, tri_y_pos + tri_half_size),
Vector2 (tri_x_pos - tri_half_size, tri_y_pos - tri_half_size), 1.0F, color);
}
}
//
// Render the text
//
StyleMgrClass::Render_Text (ColList[index]->Get_Name (), &HeaderRenderer,
color, RGB_TO_INT32 (0, 0, 0), rect, true, true);
//
// Move on to the next column
//
x_pos = rect.Right;
}
}
//
// Render the data by rows
//
float y_pos = TextRect.Top;
int row_count = Get_Entry_Count ();
for (int row_index = ScrollPos; row_index < row_count; row_index ++) {
//
// Get the height of this row
//
float row_height = RowInfoList[row_index]->Get_Height ();
//
// Don't render past the bottom of the control
//
if ((y_pos + row_height) >= TextRect.Bottom) {
break;
}
//
// Render each column in this row
//
float x_pos = TextRect.Left;
for (int index = 0; index < ColList.Count (); index ++) {
//
// Determine how wide this column is
//
float col_width = (ColList[index]->Get_Width () * HeaderRect.Width ());
if (index == ColList.Count () - 1) {
col_width = TextRect.Right - x_pos;
}
//
// Build a bounding rectangle for this entry
//
RectClass rect;
rect.Left = int(x_pos);
rect.Right = int(x_pos + col_width);
rect.Top = int(y_pos);
rect.Bottom = int(y_pos + row_height);
//
// Render the entry
//
Render_Entry (rect, index, row_index);
//
// Move on to the next column
//
x_pos = rect.Right;
}
//
// Render a hilight on this row if necessary
//
if (Is_Entry_Selected (row_index)) {
RectClass row_rect = TextRect;
row_rect.Top = (int)max (TextRect.Top, (float)y_pos);
row_rect.Bottom = (int)min (TextRect.Bottom, (float)(y_pos + row_height));
StyleMgrClass::Render_Hilight (&HilightRenderer, row_rect);
}
//
// Move down to the next row
//
y_pos += row_height;
}
return ;
}
////////////////////////////////////////////////////////////////
//
// Render_Entry
//
////////////////////////////////////////////////////////////////
void
ListCtrlClass::Render_Entry (const RectClass &clip_rect, int col_index, int row_index)
{
RectClass rect = clip_rect;
//
// Render the icons
//
int icon_count = ColList[col_index]->Get_Icon_Count (row_index);
for (int index = 0; index < icon_count; index ++) {
//
// Render this icon
//
const char *icon_name = ColList[col_index]->Get_Icon (row_index, index);
IconMgr.Render_Icon (rect, icon_name);
//
// Move the rect by the width of the icon
//
rect.Left += IconMgr.Get_Icon_Width ();
}
//
// Get the text
//
const WCHAR *text = ColList[col_index]->Get_Entry_Text (row_index);
int text_color = VRGB_TO_INT32 (ColList[col_index]->Get_Entry_Color (row_index));
//
// Render the text
//
StyleMgrClass::Render_Wrapped_Text (text, &TextRenderer, text_color,
RGB_TO_INT32 (0, 0, 0), rect, true, true);
return ;
}
////////////////////////////////////////////////////////////////
//
// On_Set_Cursor
//
////////////////////////////////////////////////////////////////
void
ListCtrlClass::On_Set_Cursor (const Vector2 &mouse_pos)
{
if (IsSelectionAllowed) {
//
// Change the mouse cursor
//
MouseMgrClass::Set_Cursor (MouseMgrClass::CURSOR_ACTION);
}
return ;
}
////////////////////////////////////////////////////////////////
//
// Set_Sort_Designator
//
////////////////////////////////////////////////////////////////
void
ListCtrlClass::Set_Sort_Designator (int col_index, SORT_TYPE type)
{
SortColumn = col_index;
SortType = type;
Set_Dirty ();
return ;
}
////////////////////////////////////////////////////////////////
//
// Sort_Alphabetically
//
////////////////////////////////////////////////////////////////
void
ListCtrlClass::Sort_Alphabetically (int col_index, SORT_TYPE type)
{
//
// Sort the entries
//
Sort (Default_Sort_Callback, MAKELONG ((WORD)col_index, (WORD)type));
//
// Update the sort marker
//
Set_Sort_Designator (col_index, type);
return ;
}
////////////////////////////////////////////////////////////////
//
// Default_Sort_Callback
//
////////////////////////////////////////////////////////////////
int CALLBACK
ListCtrlClass::Default_Sort_Callback (ListCtrlClass *list_ctrl, int item_index1, int item_index2, uint32 user_param)
{
//
// Get the sorting params
//
int sort_col_index = LOWORD (user_param);
SORT_TYPE sort_type = (SORT_TYPE)HIWORD (user_param);
//
// Sort by name
//
const WCHAR *name1 = list_ctrl->Get_Entry_Text (item_index1, sort_col_index);
const WCHAR *name2 = list_ctrl->Get_Entry_Text (item_index2, sort_col_index);
int retval = ::CompareStringW (LOCALE_USER_DEFAULT, NORM_IGNORECASE, name1, -1, name2, -1);
if (retval == 0) {
retval = wcsicmp(name1, name2);
} else {
retval -= 2;
}
//
// Invert the return value if we are sorting descendingly
//
if (sort_type == SORT_DESCENDING) {
retval = -retval;
}
return retval;
}
////////////////////////////////////////////////////////////////
//
// Update_Client_Rect
//
////////////////////////////////////////////////////////////////
void
ListCtrlClass::Update_Client_Rect (void)
{
Vector2 header_size = HeaderRenderer.Get_Text_Extents (L"W");
//
// Set the client area
//
ClientRect = Rect;
ClientRect.Inflate (Vector2 (-header_size.X, -1));
HeaderRect = ClientRect;
TextRect = ClientRect;
//
// Calculate what the header rectangle should be
//
if ((Style & LVS_NOCOLUMNHEADER) == 0) {
float char_height = header_size.Y;
HeaderRect.Bottom = HeaderRect.Top + (char_height * 3);
} else {
HeaderRect.Bottom = HeaderRect.Top;
}
//
// Move the text rectangle down
//
TextRect.Top = HeaderRect.Bottom;
//float scale_y = Render2DClass::Get_Screen_Resolution().Height () / 600;
//
// 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);
Set_Dirty ();
return ;
}
////////////////////////////////////////////////////////////////
//
// Find_Top_Of_Page
//
////////////////////////////////////////////////////////////////
int
ListCtrlClass::Find_Top_Of_Page (int bottom_index)
{
int retval = 0;
//int count = RowInfoList.Count ();
float y_pos = TextRect.Bottom;
//
// Scan the entries until we've found the first one
// to extend off the top of the page
//
for (int index = bottom_index; index >= 0; index --) {
y_pos -= RowInfoList[index]->Get_Height ();
//
// If we've gone off the page, then back off one entry
//
if (y_pos < TextRect.Top) {
retval = (index + 1);
break;
}
}
return retval;
}
////////////////////////////////////////////////////////////////
//
// Find_End_Of_Page
//
////////////////////////////////////////////////////////////////
int
ListCtrlClass::Find_End_Of_Page (void)
{
int count = RowInfoList.Count ();
int retval = count;
float y_pos = TextRect.Top;
//
// Scan the entries until we've found the first one
// to extend off the page
//
for (int index = ScrollPos; index < count; index ++) {
y_pos += RowInfoList[index]->Get_Height ();
//
// If we've gone off the page, then back off one entry
//
if (y_pos >= TextRect.Bottom) {
retval = (index - 1);
break;
}
}
return retval;
}
////////////////////////////////////////////////////////////////
//
// Find_Last_Page_Top_Entry
//
////////////////////////////////////////////////////////////////
int
ListCtrlClass::Find_Last_Page_Top_Entry (void)
{
int retval = 0;
float y_pos = TextRect.Bottom;
int count = RowInfoList.Count ();
//
// Scan backwards from the bottom entry until we've
// found one that extends off the top of the page.
//
for (int index = count - 1; index >= 0; index --) {
y_pos -= RowInfoList[index]->Get_Height ();
if (y_pos <= TextRect.Top) {
retval = index + 1;
break;
}
}
return retval;
}
////////////////////////////////////////////////////////////////
//
// Update_Scroll_Bar_Visibility
//
////////////////////////////////////////////////////////////////
void
ListCtrlClass::Update_Scroll_Bar_Visibility (void)
{
LastPageTopEntryIndex = Find_Last_Page_Top_Entry ();
//
// Configure the scroll-bar's ranges
//
ScrollBarCtrl.Set_Page_Size (0);
ScrollBarCtrl.Set_Range (0, LastPageTopEntryIndex);
//
// Determine if we have more entries then we can
// display on one page
//
bool needs_scrollbar = false;
float y_pos = TextRect.Top;
for (int index = 0; index < RowInfoList.Count (); index ++) {
y_pos += RowInfoList[index]->Get_Height ();
if (y_pos >= TextRect.Bottom) {
needs_scrollbar = true;
break;
}
}
float new_right = 0;
//
// Do we need to show a scroll bar?
//
bool was_scrollbar_displayed = IsScrollBarDisplayed;
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;
}
//
// Reset our window size (as necessary)
//
Rect.Right = new_right;
ClientRect.Right = Rect.Right - (ClientRect.Left - Rect.Left);
TextRect.Right = ClientRect.Right;
HeaderRect.Right = ClientRect.Right;
//
// Update the row heights of each entry if the scroll bar
// visibility changed
//
if (was_scrollbar_displayed != IsScrollBarDisplayed) {
for (index = 0; index < RowInfoList.Count (); index ++) {
Update_Row_Height (index);
}
//
// Update the last page top entry
//
LastPageTopEntryIndex = Find_Last_Page_Top_Entry ();
}
//
// Configure the scroll-bar's ranges
//
ScrollBarCtrl.Set_Page_Size (0);
ScrollBarCtrl.Set_Range (0, LastPageTopEntryIndex);
//
// Configure the scroll bar's position
//
if (ScrollBarCtrl.Get_Pos () > LastPageTopEntryIndex) {
int new_pos = max (LastPageTopEntryIndex, 0);
ScrollBarCtrl.Set_Pos (new_pos);
}
return ;
}
////////////////////////////////////////////////////////////////
//
// Render
//
////////////////////////////////////////////////////////////////
void
ListCtrlClass::Render (void)
{
//
// Recreate the renderers (if necessary)
//
if (IsDirty) {
Update_Scroll_Bar_Visibility ();
Create_Text_Renderers ();
}
if (IsScrollBarDisplayed) {
Parent->Add_Control (&ScrollBarCtrl);
} else {
Parent->Remove_Control (&ScrollBarCtrl);
}
if (HasFocus || IsDirty) {
Create_Control_Renderer ();
}
//
// Render the background and text
//
IconMgr.Render_Icons ();
TextRenderer.Render ();
HilightRenderer.Render ();
UnderlineRenderer.Render ();
HeaderRenderer.Render ();
ControlRenderer.Render ();
DialogControlClass::Render ();
return ;
}
////////////////////////////////////////////////////////////////
//
// Get_First_Selected
//
////////////////////////////////////////////////////////////////
int
ListCtrlClass::Get_First_Selected (void) const
{
int retval = -1;
//
// Loop over all the entries in the list control
//
int entry_count = Get_Entry_Count ();
for (int index = 0; index < entry_count; index ++) {
//
// Is this entry selected?
//
if (RowInfoList[index]->Is_Selected ()) {
retval = index;
break;
}
}
return retval;
}
////////////////////////////////////////////////////////////////
//
// Get_Next_Selected
//
////////////////////////////////////////////////////////////////
int
ListCtrlClass::Get_Next_Selected (int index) const
{
int retval = -1;
//
// Loop over all the entries in the list control
//
int entry_count = Get_Entry_Count ();
for (index ++; index < entry_count; index ++) {
//
// Is this entry selected?
//
if (RowInfoList[index]->Is_Selected ()) {
retval = index;
break;
}
}
return retval;
}
////////////////////////////////////////////////////////////////
//
// Select_All
//
////////////////////////////////////////////////////////////////
void
ListCtrlClass::Select_All (bool select)
{
//
// Loop over all the entries in the list control
//
int entry_count = Get_Entry_Count ();
for (int index = 0; index < entry_count; index ++) {
RowInfoList[index]->Select (select);
}
Set_Dirty();
return ;
}
////////////////////////////////////////////////////////////////
//
// Toggle_Entry_Selection
//
////////////////////////////////////////////////////////////////
bool
ListCtrlClass::Toggle_Entry_Selection (int index)
{
if (index < 0 || index >= Get_Entry_Count ()) {
return false;
}
//
// Toggle the selection of this entry
//
bool selected = RowInfoList[index]->Is_Selected ();
RowInfoList[index]->Select (!selected);
Set_Dirty ();
return false;
}
////////////////////////////////////////////////////////////////
//
// On_LButton_DblClk
//
////////////////////////////////////////////////////////////////
void
ListCtrlClass::On_LButton_DblClk (const Vector2 &mouse_pos)
{
int sel_entry = Entry_From_Pos (mouse_pos);
if (sel_entry == CurrSel) {
//
// Notify any advise sinks
//
ADVISE_NOTIFY (On_ListCtrl_DblClk (this, Get_ID (), sel_entry));
}
return ;
}
////////////////////////////////////////////////////////////////
//
// On_LButton_Down
//
////////////////////////////////////////////////////////////////
void
ListCtrlClass::On_LButton_Down (const Vector2 &mouse_pos)
{
if (mouse_pos.Y <= HeaderRect.Bottom) {
//
// Find out which column was clicked
//
int col_index = Col_From_Pos (mouse_pos);
if (-1 != col_index) {
//
// Notify any advise sinks
//
ADVISE_NOTIFY (On_ListCtrl_Column_Click (this, Get_ID (), col_index));
}
} else {
//
// Change the hilighted entry to reflect the mouse click
//
int new_sel = Entry_From_Pos (mouse_pos);
if (new_sel != -1) {
Set_Sel (new_sel, true);
} else if (IsNoSelectionAllowed) {
Set_Sel(-1, true);
}
}
return ;
}
////////////////////////////////////////////////////////////////
//
// On_LButton_Up
//
////////////////////////////////////////////////////////////////
void
ListCtrlClass::On_LButton_Up (const Vector2 &mouse_pos)
{
return ;
}
////////////////////////////////////////////////////////////////
//
// On_Mouse_Move
//
////////////////////////////////////////////////////////////////
void
ListCtrlClass::On_Mouse_Move (const Vector2 &mouse_pos)
{
int sel_entry = Entry_From_Pos(mouse_pos);
ADVISE_NOTIFY(On_ListCtrl_Mouse_Over(this, Get_ID(), sel_entry));
return ;
}
////////////////////////////////////////////////////////////////
//
// On_Set_Focus
//
////////////////////////////////////////////////////////////////
void
ListCtrlClass::On_Set_Focus (void)
{
Set_Dirty ();
DialogControlClass::On_Set_Focus ();
return ;
}
////////////////////////////////////////////////////////////////
//
// On_Kill_Focus
//
////////////////////////////////////////////////////////////////
void
ListCtrlClass::On_Kill_Focus (DialogControlClass *focus)
{
Set_Dirty ();
DialogControlClass::On_Kill_Focus (focus);
return ;
}
////////////////////////////////////////////////////////////////
//
// On_Key_Down
//
////////////////////////////////////////////////////////////////
bool
ListCtrlClass::On_Key_Down (uint32 key_id, uint32 key_data)
{
bool handled = true;
switch (key_id)
{
case VK_UP:
case VK_LEFT:
Set_Sel (CurrSel - 1, true);
break;
case VK_DOWN:
case VK_RIGHT:
Set_Sel (CurrSel + 1, true);
break;
case VK_PRIOR:
if (CurrSel == ScrollPos) {
Scroll_Page (-1);
} else {
Set_Sel (ScrollPos, true);
}
break;
case VK_NEXT:
{
int end_of_page = Find_End_Of_Page ();
//
// If we are at the end of the page, then
// scroll one page, otherwise snap to the
// end of the page
//
if (CurrSel == end_of_page) {
Scroll_Page (1);
} else {
Set_Sel (end_of_page, true);
}
break;
}
case VK_HOME:
Set_Sel (0, true);
break;
case VK_END:
Set_Sel (Get_Entry_Count () - 1, true);
break;
case VK_SPACE:
case VK_RETURN:
ADVISE_NOTIFY(On_ListCtrl_DblClk(this, Get_ID(), Get_Curr_Sel()));
break;
default:
handled = false;
break;
}
return handled;
}
////////////////////////////////////////////////////////////////
//
// Sort
//
////////////////////////////////////////////////////////////////
void
ListCtrlClass::Sort (LISTCTRL_SORT_CALLBACK sort_callback, uint32 user_param)
{
//
// Quick sort the data
//
int entry_count = Get_Entry_Count ();
if (entry_count > 1) {
Quick_Sort (0, entry_count - 1, sort_callback, user_param);
}
//
// Force a repaint
//
Set_Dirty ();
return ;
}
////////////////////////////////////////////////////////////////
//
// Quick_Sort
//
////////////////////////////////////////////////////////////////
void
ListCtrlClass::Quick_Sort
(
int start_index,
int end_index,
LISTCTRL_SORT_CALLBACK sort_callback,
uint32 user_param
)
{
//
// Setup a handy macro for swapping all columns of a particular row
//
#define QSORT_ROW_SWAP(index1, index2) \
{ \
if (CurrSel == index1) { CurrSel = index2; } \
else if (CurrSel == index2) { CurrSel = index1; } \
ListRowClass *temp = RowInfoList[index1]; \
RowInfoList[index1] = RowInfoList[index2]; \
RowInfoList[index2] = temp; \
for (int col_index = 0; col_index < ColList.Count (); col_index ++) { \
ColList[col_index]->Swap_Entries (index1, index2); \
} \
}
//
// Determine our ranges
//
int pivot_index = start_index;
int min_index = min (start_index + 1, end_index);
int max_index = end_index;
//
// If the range is large enough, try to pick
// a *good* pivot point.
//
if ((end_index - start_index) > 10) {
int middle_index = start_index + ((end_index - start_index) / 2);
//
// Put the smaller of the middle and end indices into the middle slot
//
if ((*sort_callback) (this, end_index, middle_index, user_param) < 0) {
QSORT_ROW_SWAP (end_index, middle_index);
}
//
// Put the larger of the middle and start indices into the start slot
//
if ((*sort_callback) (this, middle_index, start_index, user_param) > 0) {
QSORT_ROW_SWAP (middle_index, start_index);
}
//
// Put the smaller of the start and end indices into the start slot
//
if ((*sort_callback) (this, end_index, start_index, user_param) < 0) {
QSORT_ROW_SWAP (end_index, start_index);
}
}
//
// Put all the other entries in our range on either the lower or upper
// side of the pivot entry based on their relative sort value.
//
bool keep_going = true;
do
{
//
// Find the first entry that is "greater-than" the pivot
//
for (; min_index <= max_index; min_index ++) {
//
// Is this entry "greater-than" the pivot?
//
if ((*sort_callback) (this, min_index, start_index, user_param) > 0) {
break;
}
//
// The last index that is "less-than" the pivot entry will
// be the pivot's new index.
//
pivot_index = min_index;
}
//
// Find the last entry that is "less-than" the pivot
//
for (; max_index >= min_index; max_index --) {
//
// Is this entry "less-than" the pivot?
//
if ((*sort_callback) (this, max_index, start_index, user_param) < 0) {
break;
}
}
//
// Have we processed all the entries in our range?
//
if (min_index >= max_index) {
keep_going = false;
} else {
//
// Swap the entries
//
QSORT_ROW_SWAP (min_index, max_index);
//
// For efficiency's sake, skip over the entries we just swapped.
//
min_index ++;
max_index --;
pivot_index ++;
}
} while (keep_going);
//
// Insert the pivot point into its "sorted" position in the array (if necessary)
//
if (pivot_index > start_index) {
QSORT_ROW_SWAP (start_index, pivot_index);
}
//
// Calculate the region below the pivot
//
int lower_range_min = start_index;
int lower_range_max = pivot_index - 1;
//
// Recurse (if necessary) into the region below the pivot
//
if (lower_range_max < end_index && lower_range_min < lower_range_max) {
Quick_Sort (lower_range_min, lower_range_max, sort_callback, user_param);
}
//
// Calculate the region above the pivot
//
int upper_range_min = pivot_index + 1;
int upper_range_max = end_index;
//
// Recurse (if necessary) into the region above the pivot
//
if (upper_range_min > start_index && upper_range_min < upper_range_max) {
Quick_Sort (upper_range_min, upper_range_max, sort_callback, user_param);
}
return ;
}
////////////////////////////////////////////////////////////////
//
// On_Create
//
////////////////////////////////////////////////////////////////
void
ListCtrlClass::On_Create (void)
{
return ;
}
////////////////////////////////////////////////////////////////
//
// On_Destroy
//
////////////////////////////////////////////////////////////////
void
ListCtrlClass::On_Destroy (void)
{
Delete_All_Entries ();
Delete_All_Columns ();
return ;
}
////////////////////////////////////////////////////////////////
//
// Auto_Size_Columns_Include_Contents
//
////////////////////////////////////////////////////////////////
void
ListCtrlClass::Auto_Size_Columns_Include_Contents (float col_spacing)
{
float total_width = HeaderRect.Width ();
//
// Loop over all the columns
//
for (int index = 0; index < ColList.Count (); index ++) {
float max_width = HeaderRenderer.Get_Text_Extents (ColList[index]->Get_Name ()).X;
//
// Now, take into consideration the largest entry we've got
//
int count = ColList[index]->Get_Entry_Count ();
for (int row = 0; row < count; row ++) {
float width = TextRenderer.Get_Text_Extents (ColList[index]->Get_Entry_Text (row)).X;
max_width = max (width, max_width);
//
// Add the width of all the icons to this row's total
//
int icon_count = ColList[index]->Get_Icon_Count (row);
for (int icon_index = 0; icon_index < icon_count; icon_index ++) {
max_width += IconMgr.Get_Icon_Width ();
}
}
//
// Auto-size this column
//
float percent = (max_width + col_spacing) / total_width;
ColList[index]->Set_Width (percent);
}
return ;
}
////////////////////////////////////////////////////////////////
//
// Auto_Size_Columns
//
////////////////////////////////////////////////////////////////
void
ListCtrlClass::Auto_Size_Columns (float col_spacing)
{
float max_width = HeaderRect.Width ();
for (int index = 0; index < ColList.Count (); index ++) {
//
// Auto-size this column
//
float width = HeaderRenderer.Get_Text_Extents (ColList[index]->Get_Name ()).X;
float percent = (width + col_spacing) / max_width;
ColList[index]->Set_Width (percent);
}
return ;
}
////////////////////////////////////////////////////////////////
//
// Add_Icon
//
////////////////////////////////////////////////////////////////
void
ListCtrlClass::Add_Icon (int index, int col_index, const char *texture_name)
{
if (col_index < 0 || col_index >= ColList.Count ()) {
return ;
}
//
// Add an icon to this entry
//
IconMgr.Add_Icon (texture_name);
ColList[col_index]->Add_Icon (index, texture_name);
return ;
}
////////////////////////////////////////////////////////////////
//
// Reset_Icons
//
////////////////////////////////////////////////////////////////
void
ListCtrlClass::Reset_Icons (int index, int col_index)
{
if (col_index < 0 || col_index >= ColList.Count ()) {
return ;
}
//
// Reset all the icons in this entry
//
ColList[col_index]->Reset_Icons (index);
return ;
}
////////////////////////////////////////////////////////////////
//
// Add_Column
//
////////////////////////////////////////////////////////////////
void
ListCtrlClass::Add_Column (const WCHAR *column_name, float width, const Vector3 &color)
{
//
// Create a new column and add it to the list
//
ListColumnClass *column = new ListColumnClass;
column->Set_Name (column_name);
column->Set_Width (width);
column->Set_Color (color);
ColList.Add (column);
Set_Dirty ();
return ;
}
////////////////////////////////////////////////////////////////
//
// Set_Column_Color
//
////////////////////////////////////////////////////////////////
void
ListCtrlClass::Set_Column_Color (int col_index, const Vector3 &color)
{
if (col_index < 0 || col_index >= ColList.Count ()) {
return ;
}
ColList[col_index]->Set_Color (color);
Set_Dirty ();
return ;
}
////////////////////////////////////////////////////////////////
//
// Remove_Column
//
////////////////////////////////////////////////////////////////
bool
ListCtrlClass::Remove_Column (int index)
{
if (index < 0 || index >= ColList.Count ()) {
return false;
}
//
// Free the column
//
ListColumnClass *column = ColList[index];
delete column;
//
// Remove the column from the list
//
ColList.Delete (index);
Set_Dirty ();
return true;
}
////////////////////////////////////////////////////////////////
//
// Delete_All_Columns
//
////////////////////////////////////////////////////////////////
void
ListCtrlClass::Delete_All_Columns (void)
{
for (int index = 0; index < ColList.Count (); index ++) {
ListColumnClass *column = ColList[index];
delete column;
}
ColList.Delete_All ();
Set_Dirty ();
return ;
}
////////////////////////////////////////////////////////////////
//
// Get_Column_Count
//
////////////////////////////////////////////////////////////////
int
ListCtrlClass::Get_Column_Count (void) const
{
return ColList.Count ();
}
////////////////////////////////////////////////////////////////
//
// Get_Entry_Count
//
////////////////////////////////////////////////////////////////
int
ListCtrlClass::Get_Entry_Count (void) const
{
return RowInfoList.Count ();
}
////////////////////////////////////////////////////////////////
//
// Delete_Entry
//
////////////////////////////////////////////////////////////////
bool
ListCtrlClass::Delete_Entry (int index)
{
bool retval = true;
//
// Nofity the advise sinks that we are deleting this entry
//
ADVISE_NOTIFY (On_ListCtrl_Delete_Entry (this, Get_ID (), index));
//
// Remove this row from our data structures
//
if (index >= 0 && index < RowInfoList.Count ()) {
delete RowInfoList[index];
RowInfoList.Delete (index);
}
//
// Remove this entry from each column
//
for (int col_index = 0; col_index < ColList.Count (); col_index ++) {
retval &= ColList[col_index]->Delete_Entry (index);
}
// When an entry that is before the current selection is deleted then
// we need to adjust the current selection to reflect that.
if (index < CurrSel) {
--CurrSel;
}
//
// Update the current selection indicator (if necessary)
//
int entry_count = Get_Entry_Count ();
if (CurrSel >= entry_count) {
CurrSel = entry_count - 1;
}
//
// Hilight the new current selection (if necessary)
//
if (CurrSel >= 0 && CurrSel <= RowInfoList.Count ()) {
RowInfoList[CurrSel]->Select ((IsMultipleSelection == false));
}
Set_Dirty ();
return retval;
}
////////////////////////////////////////////////////////////////
//
// Find_Entry
//
////////////////////////////////////////////////////////////////
int ListCtrlClass::Find_Entry(int col_index, const WCHAR* text)
{
int count = ColList.Count();
if (col_index >= 0 && col_index < count) {
ListColumnClass* list = ColList[col_index];
count = list->Get_Entry_Count();
for (int index = 0; index < count; index++) {
const WCHAR* entryText = list->Get_Entry_Text(index);
if (wcscmp(entryText, text) == 0) {
return index;
}
}
}
return -1;
}
////////////////////////////////////////////////////////////////
//
// Insert_Entry
//
////////////////////////////////////////////////////////////////
int
ListCtrlClass::Insert_Entry (int index, const WCHAR *text)
{
if (ColList.Count () <= 0) {
return -1;
}
//
// Insert a blank row in our data structure
//
if (index < RowInfoList.Count ()) {
RowInfoList.Insert (index + 1, new ListRowClass);
} else {
RowInfoList.Add (new ListRowClass);
}
//
// Insert a new entry in the first column with the given text
//
index = ColList[0]->Insert_Entry (index, text);
//
// Use the default text color for each new entry
//
int color = StyleMgrClass::Get_Text_Color ();
float red = ((color & 0x00FF0000) >> 16) / 256.0F;
float green = ((color & 0x0000FF00) >> 8) / 256.0F;
float blue = ((color & 0x000000FF)) / 256.0F;
Vector3 new_color (red, green, blue);
ColList[0]->Set_Entry_Color (index, new_color);
//
// Add blank entries to all the other columns
//
for (int col_index = 1; col_index < ColList.Count (); col_index ++) {
ListColumnClass *column = ColList[col_index];
column->Insert_Entry (index, L"");
column->Set_Entry_Color (index, new_color);
}
//
// Update the cached row height for this entry...
//
Update_Row_Height (index);
//
// Update the last page top entry
//
LastPageTopEntryIndex = Find_Last_Page_Top_Entry ();
Set_Dirty ();
return index;
}
////////////////////////////////////////////////////////////////
//
// Update_Row_Height
//
////////////////////////////////////////////////////////////////
void
ListCtrlClass::Update_Row_Height (int row_index)
{
int border_height = (ROW_SPACING * StyleMgrClass::Get_Y_Scale ());
float height = (TextRenderer.Get_Text_Extents (L"W").Y + border_height);
//
// Render each column in this row
//
float x_pos = TextRect.Left;
for (int index = 0; index < ColList.Count (); index ++) {
//
// Determine how wide this column is
//
float col_width = (ColList[index]->Get_Width () * HeaderRect.Width ());
if (index == ColList.Count () - 1) {
col_width = TextRect.Right - x_pos;
}
//
// Set the wrapping width
//
TextRenderer.Set_Wrapping_Width (col_width);
//
// Calculate the height of this text
//
const WCHAR *text = ColList[index]->Get_Entry_Text (row_index);
Vector2 extents = TextRenderer.Get_Formatted_Text_Extents (text);
height = max (height, extents.Y + border_height);
//
// Increment the x-position
//
x_pos += col_width;
}
//
// Make sure the row is AT LEST MinRowHeight units high
//
float min_height = MinRowHeight * StyleMgrClass::Get_Y_Scale ();
height = max (min_height, height);
//
// Store the row height
//
RowInfoList[row_index]->Set_Height (height);
TextRenderer.Set_Wrapping_Width (0);
return ;
}
////////////////////////////////////////////////////////////////
//
// Set_Icon_Size
//
////////////////////////////////////////////////////////////////
void
ListCtrlClass::Set_Icon_Size (float width, float height)
{
//
// Pass these values on the icon manager
//
IconMgr.Set_Icon_Width (width);
IconMgr.Set_Icon_Height (height);
Set_Dirty ();
return ;
}
////////////////////////////////////////////////////////////////
//
// Set_Min_Row_Height
//
////////////////////////////////////////////////////////////////
void
ListCtrlClass::Set_Min_Row_Height (int height)
{
MinRowHeight = height;
//
// Update each row using this new height information
//
for (int index = 0; index < RowInfoList.Count (); index ++) {
Update_Row_Height (index);
}
Set_Dirty ();
return ;
}
////////////////////////////////////////////////////////////////
//
// Select_Entry
//
////////////////////////////////////////////////////////////////
bool
ListCtrlClass::Select_Entry (int index, bool onoff)
{
if (index < 0 || index >= Get_Entry_Count ()) {
return false;
}
//
// Set the selection state of the row
//
RowInfoList[index]->Select (onoff);
Set_Dirty ();
return true;
}
////////////////////////////////////////////////////////////////
//
// Is_Entry_Selected
//
////////////////////////////////////////////////////////////////
bool
ListCtrlClass::Is_Entry_Selected (int index)
{
bool retval = false;
if (index >= 0 && index < Get_Entry_Count ()) {
//
// Get the selection state of the row
//
retval = RowInfoList[index]->Is_Selected ();
Set_Dirty ();
}
return retval;
}
////////////////////////////////////////////////////////////////
//
// Set_Entry_Text
//
////////////////////////////////////////////////////////////////
bool
ListCtrlClass::Set_Entry_Text (int index, int col_index, const WCHAR *text)
{
if (col_index < 0 || col_index >= ColList.Count ()) {
return false;
}
//
// Change the text entry in this cell
//
ColList[col_index]->Set_Entry_Text (index, text);
//
// Update the cached row height for this entry...
//
Update_Row_Height (index);
//
// Update the last page top entry
//
LastPageTopEntryIndex = Find_Last_Page_Top_Entry ();
Set_Dirty ();
return true;
}
////////////////////////////////////////////////////////////////
//
// Set_Entry_Int
//
////////////////////////////////////////////////////////////////
bool
ListCtrlClass::Set_Entry_Int (int index, int col_index, int value)
{
if (col_index < 0 || col_index >= ColList.Count ()) {
return false;
}
//
// Convert the integer to a string
//
WideStringClass number_str;
number_str.Format (L"%d", value);
//
// Change the text entry in this cell
//
ColList[col_index]->Set_Entry_Text (index, number_str);
Set_Dirty ();
return true;
}
////////////////////////////////////////////////////////////////
//
// Set_Entry_Color
//
////////////////////////////////////////////////////////////////
bool
ListCtrlClass::Set_Entry_Color (int index, int col_index, const Vector3 &color)
{
if (col_index < 0 || col_index >= ColList.Count ()) {
return false;
}
//
// Change the color for the entry in this cell
//
ColList[col_index]->Set_Entry_Color (index, color);
Set_Dirty ();
return true;
}
////////////////////////////////////////////////////////////////
//
// Set_Entry_Data
//
////////////////////////////////////////////////////////////////
bool
ListCtrlClass::Set_Entry_Data (int index, int col_index, uint32 user_data)
{
//
// Store the user data in the first column
//
ColList[col_index]->Set_Entry_Data (index, user_data);
return true;
}
////////////////////////////////////////////////////////////////
//
// Get_Entry_Data
//
////////////////////////////////////////////////////////////////
uint32
ListCtrlClass::Get_Entry_Data (int index, int col_index)
{
uint32 user_data = 0;
//
// Lookup the user data
//
if (index >= 0 && index < Get_Entry_Count () && col_index >= 0 && col_index < ColList.Count ()) {
user_data = ColList[col_index]->Get_Entry_Data (index);
}
return user_data;
}
////////////////////////////////////////////////////////////////
//
// Get_Entry_Text
//
////////////////////////////////////////////////////////////////
const WCHAR *
ListCtrlClass::Get_Entry_Text (int index, int col_index)
{
//
// Return the string to the caller
//
return ColList[col_index]->Get_Entry_Text (index);
}
////////////////////////////////////////////////////////////////
//
// Delete_All_Entries
//
////////////////////////////////////////////////////////////////
void
ListCtrlClass::Delete_All_Entries (void)
{
//
// Notify the advise sinks (if necessary) that each entry
// is being deleted
//
int entry_count = Get_Entry_Count ();
for (int item_index = 0; item_index < entry_count; item_index ++) {
ADVISE_NOTIFY (On_ListCtrl_Delete_Entry (this, Get_ID (), item_index));
}
//
// Delete each of our row information sturctures
//
for (int index = 0; index < RowInfoList.Count (); index ++) {
delete RowInfoList[index];
}
RowInfoList.Delete_All ();
//
// Now delete all the entries from each column
//
for (index = 0; index < ColList.Count (); index ++) {
ColList[index]->Delete_All_Entries ();
}
Set_Dirty ();
//
// Reset the scroll and current selection positions
//
ScrollPos = 0;
CurrSel = -1;
Set_Dirty ();
return ;
}
////////////////////////////////////////////////////////////////
//
// Scroll_To_End
//
////////////////////////////////////////////////////////////////
void
ListCtrlClass::Scroll_To_End (void)
{
//
// Update the last page top entry
//
LastPageTopEntryIndex = Find_Last_Page_Top_Entry ();
//
// Force scroll to the end
//
ScrollPos = LastPageTopEntryIndex;
ScrollPos = max (ScrollPos, 0);
//
// Update the scrollbar
//
ScrollBarCtrl.Set_Pos (ScrollPos, false);
Set_Dirty ();
return ;
}
////////////////////////////////////////////////////////////////
//
// Set_Curr_Sel
//
////////////////////////////////////////////////////////////////
void
ListCtrlClass::Set_Curr_Sel (int new_sel)
{
if (new_sel == -1) {
Select_All(false);
CurrSel = -1;
} else {
Set_Sel(new_sel, false);
}
return ;
}
////////////////////////////////////////////////////////////////
//
// Set_Sel
//
////////////////////////////////////////////////////////////////
void
ListCtrlClass::Set_Sel (int new_sel, bool notify)
{
if (IsSelectionAllowed) {
//
// Unselect the old entry (if necessary)
//
if (IsMultipleSelection == false) {
Select_Entry (CurrSel, false);
}
int old_sel = CurrSel;
if ((new_sel == -1) && IsNoSelectionAllowed) {
CurrSel = -1;
} else {
//
// Bound the selection index
//
int count = Get_Entry_Count ();
CurrSel = max (new_sel, 0);
CurrSel = min (CurrSel, count - 1);
//
// Select the new entry
//
if (IsMultipleSelection) {
Toggle_Entry_Selection (CurrSel);
} else {
Select_Entry (CurrSel, true);
}
}
//
// Notify anyone who cares that we have changed the selection
//
if (notify && (old_sel != CurrSel)) {
ADVISE_NOTIFY(On_ListCtrl_Sel_Change(this, Get_ID(), old_sel, CurrSel));
}
//
// Force a repaint
//
Set_Dirty ();
Update_Scroll_Pos ();
}
return ;
}
////////////////////////////////////////////////////////////////
//
// Update_Scroll_Pos
//
////////////////////////////////////////////////////////////////
void
ListCtrlClass::Update_Scroll_Pos (void)
{
if (CurrSel < 0) {
return ;
}
//
// Do we need to scroll up?
//
if (CurrSel < ScrollPos) {
ScrollPos = CurrSel;
Set_Dirty ();
} else {
//
// Do we need to scroll down?
//
RectClass rect;
Get_Entry_Rect (CurrSel, rect);
if (rect.Bottom >= TextRect.Bottom) {
//
// Calculate where we should scroll to
//
ScrollPos = Find_Top_Of_Page (CurrSel);
Set_Dirty ();
}
}
//
// Update the scrollbar
//
ScrollBarCtrl.Set_Pos (ScrollPos, false);
return ;
}
////////////////////////////////////////////////////////////////
//
// Get_Entry_Rect
//
////////////////////////////////////////////////////////////////
void
ListCtrlClass::Get_Entry_Rect (int index, RectClass &rect)
{
if (index < 0 || index >= RowInfoList.Count ()) {
return ;
}
//
// Lookup the height of this row
//
float row_height = RowInfoList[index]->Get_Height ();
float y_pos = -1000.0F;
if (ScrollPos <= index) {
//
// Calculate the starting y-position of this entry
//
y_pos = TextRect.Top;
for (int curr_index = ScrollPos; curr_index < index; curr_index ++) {
y_pos += RowInfoList[curr_index]->Get_Height ();
}
}
rect.Left = int(TextRect.Left);
rect.Right = int(TextRect.Right);
rect.Top = int(y_pos);
rect.Bottom = int(rect.Top + row_height);
return ;
}
////////////////////////////////////////////////////////////////
//
// Col_From_Pos
//
////////////////////////////////////////////////////////////////
int
ListCtrlClass::Col_From_Pos (const Vector2 &mouse_pos)
{
int retval = -1;
//
// Test each column
//
int x_pos = HeaderRect.Left;
int col_count = ColList.Count ();
for (int col_index = 0; col_index < col_count; col_index ++) {
//
// Determine how wide this column is
//
int col_width = (ColList[col_index]->Get_Width () * HeaderRect.Width ());
//
// Let the last column extend to the edge
//
if (col_index == col_count - 1) {
col_width = HeaderRect.Right - x_pos;
}
//
// Is the coordinate inside this col?
//
if (mouse_pos.X >= x_pos && mouse_pos.X <= (x_pos + col_width)) {
retval = col_index;
break;
}
//
// Move on to the next column
//
x_pos += col_width;
}
return retval;
}
////////////////////////////////////////////////////////////////
//
// Entry_From_Pos
//
////////////////////////////////////////////////////////////////
int
ListCtrlClass::Entry_From_Pos (const Vector2 &mouse_pos)
{
int retval = -1;
if (mouse_pos.Y < TextRect.Top || mouse_pos.Y > TextRect.Bottom) {
return -1;
}
//
// Test each row
//
int y_pos = TextRect.Top;
int row_count = Get_Entry_Count ();
for (int row_index = ScrollPos; row_index < row_count; row_index ++) {
float row_height = RowInfoList[row_index]->Get_Height ();
//
// Is the coordinate inside this row?
//
if (mouse_pos.Y >= y_pos && mouse_pos.Y <= (y_pos + row_height)) {
retval = row_index;
break;
}
//
// Move down to the next row
//
y_pos += row_height;
//
// Stop searching if we've moved off the page
//
if (mouse_pos.Y >= TextRect.Bottom) {
break;
}
}
return retval;
}
////////////////////////////////////////////////////////////////
//
// Scroll_Page
//
////////////////////////////////////////////////////////////////
void
ListCtrlClass::Scroll_Page (int direction)
{
int count = RowInfoList.Count ();
float height = TextRect.Height ();
bool found = false;
//
// Scan either direction from the current scroll
// position until we've moved a whole page
//
for ( int index = ScrollPos;
index >= 0 && index < count;
index += direction)
{
//
// Decrement the remaining distance
//
height -= RowInfoList[index]->Get_Height ();
//
// If we've gone of the page, then back off one entry
//
if (height < 0) {
ScrollPos = (index - direction);
ScrollPos = min (ScrollPos, LastPageTopEntryIndex);
ScrollBarCtrl.Set_Pos (ScrollPos, false);
Set_Dirty ();
found = true;
break;
}
}
//
// Check the boundary conditions
//
if (index < 0 && found == false) {
ScrollPos = 0;
ScrollBarCtrl.Set_Pos (ScrollPos, false);
Set_Dirty ();
}
return ;
}
////////////////////////////////////////////////////////////////
//
// On_VScroll_Page
//
////////////////////////////////////////////////////////////////
void
ListCtrlClass::On_VScroll_Page (ScrollBarCtrlClass *scrollbar, int ctrl_id, int direction)
{
Scroll_Page (direction);
return ;
}
////////////////////////////////////////////////////////////////
//
// On_VScroll
//
////////////////////////////////////////////////////////////////
void
ListCtrlClass::On_VScroll (ScrollBarCtrlClass *, int , int new_position)
{
if (ScrollPos != new_position) {
ScrollPos = new_position;
Set_Dirty ();
}
return ;
}
////////////////////////////////////////////////////////////////
//
// On_Mouse_Wheel
//
////////////////////////////////////////////////////////////////
void
ListCtrlClass::On_Mouse_Wheel (int direction)
{
if (direction < 0) {
if (ScrollPos > 0) {
ScrollPos --;
ScrollBarCtrl.Set_Pos (ScrollPos, false);
}
} else {
if (ScrollPos < LastPageTopEntryIndex) {
ScrollPos ++;
ScrollBarCtrl.Set_Pos (ScrollPos, false);
}
}
Set_Dirty ();
return ;
}
//********************************************************************************//
//
// Start of ListColumnClass
//
//********************************************************************************//
////////////////////////////////////////////////////////////////
//
// Free_Data
//
////////////////////////////////////////////////////////////////
void
ListColumnClass::Free_Data (void)
{
Delete_All_Entries ();
return ;
}
////////////////////////////////////////////////////////////////
//
// Reset_Contents
//
////////////////////////////////////////////////////////////////
void
ListColumnClass::Reset_Contents (void)
{
//
// Remove all the entries
//
Free_Data ();
return ;
}
////////////////////////////////////////////////////////////////
//
// Insert_Entry
//
////////////////////////////////////////////////////////////////
int
ListColumnClass::Insert_Entry (int index, const WCHAR *entry_name)
{
ListEntryClass *entry = new ListEntryClass (entry_name);
//
// Should we insert this entry in the list or add it to the end?
//
if (index < EntryList.Count ()) {
EntryList.Insert (index + 1, entry);
} else {
EntryList.Add (entry);
index = (EntryList.Count () - 1);
}
return index;
}
////////////////////////////////////////////////////////////////
//
// Delete_Entry
//
////////////////////////////////////////////////////////////////
bool
ListColumnClass::Delete_Entry (int index)
{
bool retval = false;
//
// Delete the entry if we can find it in our list
//
if (index >= 0 && index < EntryList.Count ()) {
delete EntryList[index];
EntryList.Delete (index);
}
return retval;
}
////////////////////////////////////////////////////////////////
//
// Delete_All_Entries
//
////////////////////////////////////////////////////////////////
void
ListColumnClass::Delete_All_Entries (void)
{
//
// Free each of the entries in the list
//
for (int index = 0; index < EntryList.Count (); index ++) {
delete EntryList[index];
}
EntryList.Delete_All ();
return ;
}
////////////////////////////////////////////////////////////////
//
// Move_Entry
//
////////////////////////////////////////////////////////////////
void
ListColumnClass::Move_Entry (int old_index, int new_index)
{
if ( old_index < 0 || (old_index >= EntryList.Count ()) &&
new_index < 0 || (new_index >= EntryList.Count ()))
{
return ;
}
//
// Move the entry
//
ListEntryClass *old_entry = EntryList[old_index];
EntryList.Insert (new_index, old_entry);
EntryList.Delete (old_index);
return ;
}
////////////////////////////////////////////////////////////////
//
// Swap_Entries
//
////////////////////////////////////////////////////////////////
void
ListColumnClass::Swap_Entries (int index1, int index2)
{
if ( index1 < 0 || (index1 >= EntryList.Count ()) &&
index2 < 0 || (index2 >= EntryList.Count ()))
{
return ;
}
//
// Move the entry
//
ListEntryClass *temp_entry = EntryList[index1];
EntryList[index1] = EntryList[index2];
EntryList[index2] = temp_entry;
return ;
}