/*
**	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/comboboxctrl.cpp         $*
 *                                                                                             *
 *                       Author:: Patrick Smith                                                *
 *                                                                                             *
 *                     $Modtime:: 1/10/02 12:30p                                              $*
 *                                                                                             *
 *                    $Revision:: 25                                                          $*
 *                                                                                             *
 *---------------------------------------------------------------------------------------------*
 * Functions:                                                                                  *
 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */


#include "comboboxctrl.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"


////////////////////////////////////////////////////////////////
//
//	ComboBoxCtrlClass
//
////////////////////////////////////////////////////////////////
ComboBoxCtrlClass::ComboBoxCtrlClass (void)	:
	WasButtonPressedOnMe (false),
	IsDropDownDisplayed (false),
	TextRect (0, 0, 0, 0),
	ButtonRect (0, 0, 0, 0),
	FullRect (0, 0, 0, 0),
	DropDownSize (0, 0),
	IsInitialized (false),
	LastDropDownDisplayChange (0),
	CurrSel(-1)
{
	//
	//	Set the font for the text renderers
	//
	StyleMgrClass::Assign_Font (&TextRenderer, StyleMgrClass::FONT_LISTS);
	StyleMgrClass::Configure_Renderer (&ControlRenderer);

	//
	//	Configure the hilight renderer 
	//
	StyleMgrClass::Configure_Renderer (&HilightRenderer);

	//
	//	Let the drop down know who its parent is
	//
	DropDownCtrl.Set_Combo_Box	(this);
	DropDownCtrl.Set_Is_Embedded (true);
	EditControl.Set_Is_Embedded (true);
	return ;
}


////////////////////////////////////////////////////////////////
//
//	~ComboBoxCtrlClass
//
////////////////////////////////////////////////////////////////
ComboBoxCtrlClass::~ComboBoxCtrlClass (void)
{
	return ;
}


void ComboBoxCtrlClass::Set_Style(DWORD style)
{
	DialogControlClass::Set_Style(style);

	if ((style & CBS_OEMCONVERT) != 0) {
		EditControl.Set_Style(ES_OEMCONVERT);
	} else {
		EditControl.Set_Style(0);
	}
}


////////////////////////////////////////////////////////////////
//
//	Create_Text_Renderers
//
////////////////////////////////////////////////////////////////
void
ComboBoxCtrlClass::Create_Text_Renderers (void)
{
	if ((Style & 0x0F) == CBS_DROPDOWN) {
		return;
	}

	HilightRenderer.Reset ();
	HilightRenderer.Set_Coordinate_Range (Render2DClass::Get_Screen_Resolution ());
	StyleMgrClass::Configure_Hilighter (&HilightRenderer);

	//
	//	Lookup the string to display
	//
	WideStringClass display_string(255, true);
	DropDownCtrl.Get_String (DropDownCtrl.Get_Curr_Sel(), display_string);

	//
	//	Setup this renderer
	//
	Render2DSentenceClass &renderer = TextRenderer;
	renderer.Reset ();

	//
	//	Draw the text
	//
	StyleMgrClass::Render_Text (display_string, &renderer, TextRect, true, true,
							StyleMgrClass::LEFT_JUSTIFY, IsEnabled);

	//
	//	Do the hilight
	//
	if (HasFocus && IsEnabled) {
		StyleMgrClass::Render_Hilight (&HilightRenderer, TextRect);
	}

	return ;
}


////////////////////////////////////////////////////////////////
//
//	Create_Control_Renderers
//
////////////////////////////////////////////////////////////////
void
ComboBoxCtrlClass::Create_Control_Renderers (void)
{
	Render2DClass &renderer = ControlRenderer;

	//
	//	Configure this renderer
	//
	renderer.Reset ();
	renderer.Enable_Texturing (false);
	renderer.Set_Coordinate_Range (Render2DClass::Get_Screen_Resolution());

	//
	//	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 control outline
	//
	RectClass rect = ClientRect;
	renderer.Add_Outline (rect, 1.0F, color);

	//
	//	Now draw the background
	//
	rect.Right	-= 1;
	rect.Bottom -= 1;
	renderer.Add_Quad (rect, bkcolor);

	//
	//	Draw the button
	//
	rect = ButtonRect;
	renderer.Add_Line (Vector2 (rect.Left, rect.Bottom),	Vector2 (rect.Left, rect.Top-1),		1, color);
	float width		= int(ButtonRect.Width () / 4);
	float height	= int(ButtonRect.Height () / 4);
	float center_x = int(ButtonRect.Left + (ButtonRect.Width () / 2));
	float center_y = int(ButtonRect.Top + (ButtonRect.Height () / 2));
	renderer.Add_Line (Vector2 (center_x - width, center_y - height),	Vector2 (center_x + width, center_y - height), 1, color);
	renderer.Add_Line (Vector2 (center_x + width, center_y - height),	Vector2 (center_x, center_y + height), 1, color);
	renderer.Add_Line (Vector2 (center_x, center_y + height),	Vector2 (center_x - width, center_y - height), 1, color);
	return ;
}


////////////////////////////////////////////////////////////////
//
//	On_Set_Cursor
//
////////////////////////////////////////////////////////////////
void
ComboBoxCtrlClass::On_Set_Cursor (const Vector2 &mouse_pos)
{
	//
	//	Change the mouse cursor if necessary
	//
	if (ClientRect.Contains (mouse_pos)) {
		MouseMgrClass::Set_Cursor (MouseMgrClass::CURSOR_ACTION);
	}

	return ;
}


////////////////////////////////////////////////////////////////
//
//	Set_Window_Pos
//
////////////////////////////////////////////////////////////////
void
ComboBoxCtrlClass::Set_Window_Pos (const Vector2 &pos)
{
	float width		= FullRect.Width ();
	float height	= FullRect.Height ();

	//
	//	Recalculate the window's bounding rectangle
	//
	FullRect.Left	= (int)pos.X;
	FullRect.Top		= (int)pos.Y;
	FullRect.Right	= (int)(FullRect.Left + width);
	FullRect.Bottom	= (int)(FullRect.Top + height);

	//
	//	Let the control recalculate anything it needs
	//
	DialogControlClass::Set_Window_Pos (pos);
	return ;
}


////////////////////////////////////////////////////////////////
//
//	Update_Client_Rect
//
////////////////////////////////////////////////////////////////
void
ComboBoxCtrlClass::Update_Client_Rect (void)
{
	if (IsInitialized == false) {
		FullRect = Rect;
		IsInitialized = true;
	}

	//
	//	Determine what one character spacing would be
	//
	Vector2 char_size = TextRenderer.Get_Text_Extents (L"W");

	float border_width	= char_size.X + 2;
	float border_height	= 2;

	float button_height	= int(char_size.Y * 2.0F);
	float button_width	= button_height;

	//
	//	Shrink the client area
	//
	ClientRect			= Rect;
	ClientRect.Bottom	= ClientRect.Top + button_height;

	//
	//	Remember how much space we have for the drop down control
	//
	DropDownSize.X = int(ClientRect.Width ());
	DropDownSize.Y = int(FullRect.Bottom - ClientRect.Bottom);
	
	//
	//	Calculate the bounding rectangle for the drop-down control
	//
	RectClass drop_down_rect;
	drop_down_rect.Left		= ClientRect.Left;
	drop_down_rect.Top		= ClientRect.Bottom;
	drop_down_rect.Right		= drop_down_rect.Left + DropDownSize.X;
	drop_down_rect.Bottom	= drop_down_rect.Top + DropDownSize.Y;

	//
	//	Should the drop-down, go down or up?
	//
	RectClass screen_rect = Render2DClass::Get_Screen_Resolution ();
	if (drop_down_rect.Bottom > screen_rect.Bottom) {
		drop_down_rect.Bottom	= ClientRect.Top;
		drop_down_rect.Top		= drop_down_rect.Bottom - DropDownSize.Y;
	}

	//
	//	Configure the drop-down control
	//
	DropDownCtrl.Set_Full_Rect (drop_down_rect);
	DropDownCtrl.Set_Window_Rect (drop_down_rect);

	//
	//	Make the window rect and the client rect the same
	//
	Rect = ClientRect;
	
	//
	//	Calculate where the button is located
	//
	ButtonRect			= ClientRect;
	ButtonRect.Left	= ClientRect.Right - button_width;
	ButtonRect.Bottom	= ClientRect.Top + button_height;

	//
	//	Calculate the area the text can render into
	//
	TextRect				= ClientRect;
	TextRect.Top		+= border_height;
	TextRect.Left		+= border_width;
	TextRect.Right		= ButtonRect.Left - border_width;
	TextRect.Bottom	= ButtonRect.Bottom - border_height;

	RectClass editRect = ClientRect;
	editRect.Right = ButtonRect.Left;
	editRect.Bottom	= ButtonRect.Bottom;
	EditControl.Set_Window_Rect(editRect);

	// If this is a dropdown then make the controls rect only cover the button.
	// The edit control will handle the rest of the control orginal area.
	if ((Style & 0x0F) == CBS_DROPDOWN) {
		ClientRect = ButtonRect;
		Rect = ButtonRect;
	}

	Set_Dirty ();
	return ;
}


////////////////////////////////////////////////////////////////
//
//	Render
//
////////////////////////////////////////////////////////////////
void
ComboBoxCtrlClass::Render (void)
{
	//
	//	Recreate the renderers (if necessary)
	//
	if (IsDirty) {		
		Create_Control_Renderers ();
		Create_Text_Renderers ();
	}

	//
	//	Render the background and text for the current state
	//
	ControlRenderer.Render ();
	TextRenderer.Render ();
	HilightRenderer.Render ();

	//
	//	Check to see if the mouse button is still down
	//
	WasButtonPressedOnMe	&= DialogMgrClass::Is_Button_Down (VK_LBUTTON);

	DialogControlClass::Render ();
	return ;
}


////////////////////////////////////////////////////////////////
//
//	On_LButton_Down
//
////////////////////////////////////////////////////////////////
void
ComboBoxCtrlClass::On_LButton_Down (const Vector2 &mouse_pos)
{
	if (HasFocus) {
		WasButtonPressedOnMe = true;
	}

//	Display_Drop_Down (true);
	Display_Drop_Down(!IsDropDownDisplayed);
	return ;
}


////////////////////////////////////////////////////////////////
//
//	On_LButton_Up
//
////////////////////////////////////////////////////////////////
void
ComboBoxCtrlClass::On_LButton_Up (const Vector2 &mouse_pos)
{
	//
	//	Reset our flags
	//
	WasButtonPressedOnMe	= false;
	return ;
}


////////////////////////////////////////////////////////////////
//
//	On_Mouse_Move
//
////////////////////////////////////////////////////////////////
void
ComboBoxCtrlClass::On_Mouse_Move (const Vector2 &mouse_pos)
{
	return ;
}


////////////////////////////////////////////////////////////////
//
//	On_Set_Focus
//
////////////////////////////////////////////////////////////////
void
ComboBoxCtrlClass::On_Set_Focus (void)
{
	Set_Dirty ();

	//
	//	Set the focus to the edit control if we are in that mode
	//
	/*if ((Style & 0x0F) == CBS_DROPDOWN) {
		DialogMgrClass::Set_Focus(&EditControl);
	}*/

	DialogControlClass::On_Set_Focus ();
	return ;
}


////////////////////////////////////////////////////////////////
//
//	On_Kill_Focus
//
////////////////////////////////////////////////////////////////
void
ComboBoxCtrlClass::On_Kill_Focus (DialogControlClass *focus)
{
	Set_Dirty ();

	DialogControlClass::On_Kill_Focus (focus);
	return ;
}


////////////////////////////////////////////////////////////////
//
//	On_Key_Down
//
////////////////////////////////////////////////////////////////
bool
ComboBoxCtrlClass::On_Key_Down (uint32 key_id, uint32 key_data)
{
	bool handled = false;
	bool is_dirty = true;

	switch (key_id)
	{
		case VK_HOME:
			Set_Sel (0, true);
			break;

		case VK_END:
			Set_Sel (DropDownCtrl.Get_Count () - 1, true);
			break;

		case VK_UP:
		case VK_LEFT:
			Set_Sel (max<int>(0, (DropDownCtrl.Get_Curr_Sel () - 1)), true);
			break;

		case VK_DOWN:
		case VK_RIGHT:
			Set_Sel (DropDownCtrl.Get_Curr_Sel () + 1, true);
			break;

		default:
			is_dirty = false;
			handled = false;
			break;
	}

	if (is_dirty) {
		Set_Dirty ();
	}

	return handled;
}


////////////////////////////////////////////////////////////////
//
//	On_Create
//
////////////////////////////////////////////////////////////////
void
ComboBoxCtrlClass::On_Create (void)
{
	if ((Style & 0x0F) == CBS_DROPDOWN) {
		Parent->Add_Control(&EditControl);
		Set_Wants_Focus(false);
		EditControl.Set_Advise_Sink(this);
	}

	return ;
}


////////////////////////////////////////////////////////////////
//
//	On_Destroy
//
////////////////////////////////////////////////////////////////
void
ComboBoxCtrlClass::On_Destroy (void)
{
	Parent->Remove_Control(&DropDownCtrl);
	Parent->Remove_Control(&EditControl);
	return ;
}


////////////////////////////////////////////////////////////////
//
//	On_Drop_Down_End
//
////////////////////////////////////////////////////////////////
void
ComboBoxCtrlClass::On_Drop_Down_End (int curr_sel)
{
	Set_Sel(curr_sel, true);

	//	Hide the drop down
	Display_Drop_Down (false);
	
	//
	//	If we aren't embedding an edit control, then we
	//	need to focus after the drop down exits
	//
	if ((Style & 0x0F) != CBS_DROPDOWN) {
		Set_Focus ();
	} else {
		EditControl.Set_Focus ();
	}

	return ;
}


////////////////////////////////////////////////////////////////
//
//	Display_Drop_Down
//
////////////////////////////////////////////////////////////////
void
ComboBoxCtrlClass::Display_Drop_Down (bool onoff)
{
	int curr_time = DialogMgrClass::Get_Time ();

	if (onoff) {

		//
		//	Show the drop down control
		//
		if (IsDropDownDisplayed == false && (curr_time - LastDropDownDisplayChange) > 0.1F) {
			Parent->Add_Control (&DropDownCtrl);
			DropDownCtrl.Set_Focus ();
			IsDropDownDisplayed = true;
			LastDropDownDisplayChange = curr_time;

			if ((Style & 0x0F) == CBS_DROPDOWN) {
				WideStringClass text(255, true);
				text = EditControl.Get_Text();

				int index = DropDownCtrl.Find_Closest_String(text);
				Set_Sel(index, true);
			}
		}

	} else {
		
		//
		//	Hide the drop down
		//
		if (IsDropDownDisplayed) {
			Parent->Remove_Control (&DropDownCtrl);
			IsDropDownDisplayed = false;
			LastDropDownDisplayChange = curr_time;
		}
	}

	return ;
}


////////////////////////////////////////////////////////////////
//
//	On_Mouse_Wheel
//
////////////////////////////////////////////////////////////////
void
ComboBoxCtrlClass::On_Mouse_Wheel (int direction)
{
	if (direction < 0) {		
		Set_Sel (max<int>(0, (DropDownCtrl.Get_Curr_Sel () - 1)), true);
	} else {
		Set_Sel (DropDownCtrl.Get_Curr_Sel () + 1, true);
	}

	Set_Dirty ();
	return ;
}


////////////////////////////////////////////////////////////////
//
//	Select_String
//
////////////////////////////////////////////////////////////////
int
ComboBoxCtrlClass::Select_String (const WCHAR* string)
{
	int index = Find_String (string);
	if (index >= 0) {
		Set_Curr_Sel (index);
	}

	return index;
}


void ComboBoxCtrlClass::Delete_String(int index)
{
	if (index >= 0 && index < DropDownCtrl.Get_Count()) {
		DropDownCtrl.Delete_String(index);

		CurrSel = -2;
		int newSel = DropDownCtrl.Get_Curr_Sel();
		Set_Sel(newSel, false);
	}
}


////////////////////////////////////////////////////////////////
//
//	Set_Curr_Sel
//
////////////////////////////////////////////////////////////////
void
ComboBoxCtrlClass::Set_Curr_Sel (int index)
{
	Set_Sel(index, false);
}


void
ComboBoxCtrlClass::Set_Sel (int index, bool notify)
{
	if (CurrSel != index) {
		DropDownCtrl.Set_Curr_Sel(index);
		int newSel = DropDownCtrl.Get_Curr_Sel();

		if (CurrSel != newSel) {
			Set_Dirty();
	
			// If this is a dropdown then automatically fill in the edit part with
			// the new selection string.
			if ((Style & 0x0F) == CBS_DROPDOWN) {
				if (newSel >= 0) {
					WideStringClass editString(255, true);
					DropDownCtrl.Get_String(newSel, editString);
					EditControl.Set_Text(editString);
				} else {
					EditControl.Set_Text(L"");
				}
			}

			int oldSel = CurrSel;
			CurrSel = newSel;

			if (notify) {
				ADVISE_NOTIFY(On_ComboBoxCtrl_Sel_Change(this, Get_ID(), oldSel, newSel));
			}
		}
	}
}


////////////////////////////////////////////////////////////////
//
//	Get_Text
//
////////////////////////////////////////////////////////////////
const WCHAR*
ComboBoxCtrlClass::Get_Text(void) const
{
	if ((Style & 0x0F) == CBS_DROPDOWN) {
		return EditControl.Get_Text();
	}

	return DropDownCtrl.Get_String(Get_Curr_Sel());
}


////////////////////////////////////////////////////////////////
//
//	Set_Text
//
////////////////////////////////////////////////////////////////
void
ComboBoxCtrlClass::Set_Text (const WCHAR *title)
{
	if ((Style & 0x0F) == CBS_DROPDOWN) {
		EditControl.Set_Text (title);
	}

	DialogControlClass::Set_Text (title);
	return ;
}


////////////////////////////////////////////////////////////////
//
//	On_EditCtrl_Key_Down
//
////////////////////////////////////////////////////////////////
void
ComboBoxCtrlClass::On_EditCtrl_Change(EditCtrlClass* edit, int)
{
	if (edit == &EditControl) {
		ADVISE_NOTIFY(On_ComboBoxCtrl_Edit_Change(this, Get_ID()));
	}
}


////////////////////////////////////////////////////////////////
//
//	On_EditCtrl_Key_Down
//
////////////////////////////////////////////////////////////////
bool
ComboBoxCtrlClass::On_EditCtrl_Key_Down (EditCtrlClass *edit, uint32 key_id, uint32 key_data)
{
	if (edit != &EditControl) {
		return false;
	}

	bool handled = true;

	switch (key_id)
	{
		case VK_UP:
			Set_Sel (max<int>(0, (DropDownCtrl.Get_Curr_Sel () - 1)), true);
			break;

		case VK_DOWN:
			Set_Sel (DropDownCtrl.Get_Curr_Sel () + 1, true);
			break;

		default:
			handled = false;
			break;
	}

	return handled;
}

