/*
Copyright (C) 2011 ezQuake team

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 2
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/>.
*/
/**
	HUD Editor module

	Initial concept code jogihoogi, rewritten by Cokeman, Feb 2007
*/

#include "quakedef.h"
#include "qsound.h"
#include "utils.h"
#include "menu.h"
#include "keys.h"
#include "Ctrl.h"
#include "ez_controls.h"
#include "ez_button.h"
#include "ez_label.h"
#include "ez_scrollbar.h"
#include "ez_scrollpane.h"
#include "ez_slider.h"
#include "ez_listview.h"
#include "ez_window.h"
#include "hud.h"
#include "hud_editor.h"

ez_tree_t help_control_tree;

extern mpic_t		*scr_cursor_icon;	// cl_screen.c
extern cvar_t		hud_planmode;		// hud_common.c

mpic_t				*hud_editor_move_icon	= NULL;
mpic_t				*hud_editor_resize_icon	= NULL;
mpic_t				*hud_editor_align_icon	= NULL;
mpic_t				*hud_editor_place_icon	= NULL;

cvar_t				hud_editor_allowresize	= {"hud_editor_allowresize", "1"};	// Show resize handles / allow resizing.
cvar_t				hud_editor_allowmove	= {"hud_editor_allowmove", "1"};	// Allow moving...
cvar_t				hud_editor_allowplace	= {"hud_editor_allowplace", "1"};	// Allow placing HUDs.
cvar_t				hud_editor_allowalign	= {"hud_editor_allowalign", "1"};	// Allow aligning HUDs.

extern hud_t		*hud_huds;										// The list of HUDs..
hud_t				*selected_hud			= NULL;					// The currently selected HUD.

qbool				hud_editor				= false;				// If we're in HUD editor mode or not.
hud_editor_mode_t	hud_editor_mode			= hud_editmode_off;		// The current mode the HUD editor is in.
hud_editor_mode_t	hud_editor_prevmode		= hud_editmode_off;		// The previous mode the HUD editor was in.

qbool				hud_editor_showhelp		= false;				// Show the help plaque or not?
qbool				hud_editor_showoutlines	= true;					// Show guidelines around the HUD elements.

float				hud_mouse_x;									// The screen coordinates of the mouse cursor.
float				hud_mouse_y;

// Macros for what mouse buttons are clicked.
#define MOUSEDOWN			( keydown[K_MOUSE1] ||  keydown[K_MOUSE2]|| keydown[K_MOUSE3])
#define MOUSEDOWN_1_2		( keydown[K_MOUSE1] &&  keydown[K_MOUSE2])
#define MOUSEDOWN_1_3		( keydown[K_MOUSE1] &&  keydown[K_MOUSE3])
#define MOUSEDOWN_1_2_3		( keydown[K_MOUSE1] &&  keydown[K_MOUSE2] &&  keydown[K_MOUSE3])
#define MOUSEDOWN_1_ONLY	( keydown[K_MOUSE1] && !keydown[K_MOUSE2] && !keydown[K_MOUSE3])
#define MOUSEDOWN_2_ONLY	(!keydown[K_MOUSE1] &&  keydown[K_MOUSE2] && !keydown[K_MOUSE3])
#define MOUSEDOWN_3_ONLY	(!keydown[K_MOUSE1] && !keydown[K_MOUSE2] &&  keydown[K_MOUSE3])
#define MOUSEDOWN_1_2_ONLY	( keydown[K_MOUSE1] &&  keydown[K_MOUSE2] && !keydown[K_MOUSE3])
#define MOUSEDOWN_1_3_ONLY	( keydown[K_MOUSE1] && !keydown[K_MOUSE2] &&  keydown[K_MOUSE3])
#define MOUSEDOWN_2_3_ONLY	(!keydown[K_MOUSE1] &&  keydown[K_MOUSE2] &&  keydown[K_MOUSE3])
#define MOUSEDOWN_NONE		(!keydown[K_MOUSE1] && !keydown[K_MOUSE2] && !keydown[K_MOUSE3])

#define HUD_CENTER_X(h)		((h)->lx + (h)->lw / 2)	// Gets the coordinates for the center point of the specified hud.
#define HUD_CENTER_Y(h)		((h)->ly + (h)->lh / 2)

//
// HUD align polygons.
//
#define HUD_ALIGN_POLYCOUNT_CORNER	5
#define HUD_ALIGN_POLYCOUNT_EDGE	4
#define HUD_ALIGN_POLYCOUNT_CENTER	8
#define HUD_ALIGN_POLYCOUNT_CONSOLE	4

vec3_t *hud_align_current_poly = NULL;	// The current alignment polygon when in alignment mode.
int hud_align_current_polycount = 0;	// Number of vertices in teh polygon.

vec3_t hud_align_topright_poly[HUD_ALIGN_POLYCOUNT_CORNER];
vec3_t hud_align_top_poly[HUD_ALIGN_POLYCOUNT_EDGE];
vec3_t hud_align_topleft_poly[HUD_ALIGN_POLYCOUNT_CORNER];
vec3_t hud_align_left_poly[HUD_ALIGN_POLYCOUNT_EDGE];
vec3_t hud_align_bottomleft_poly[HUD_ALIGN_POLYCOUNT_CORNER];
vec3_t hud_align_bottom_poly[HUD_ALIGN_POLYCOUNT_EDGE];
vec3_t hud_align_bottomright_poly[HUD_ALIGN_POLYCOUNT_CORNER];
vec3_t hud_align_right_poly[HUD_ALIGN_POLYCOUNT_EDGE];
vec3_t hud_align_center_poly[HUD_ALIGN_POLYCOUNT_CENTER];

vec3_t hud_align_consoleleft_poly[HUD_ALIGN_POLYCOUNT_CONSOLE];
vec3_t hud_align_console_poly[HUD_ALIGN_POLYCOUNT_CONSOLE];
vec3_t hud_align_consoleright_poly[HUD_ALIGN_POLYCOUNT_CONSOLE];

typedef enum hud_alignmode_s
{
	hud_align_center,
	hud_align_top,
	hud_align_topleft,
	hud_align_left,
	hud_align_bottomleft,
	hud_align_bottom,
	hud_align_bottomright,
	hud_align_right,
	hud_align_topright,
	hud_align_consoleleft,
	hud_align_consoleright,
	hud_align_console
	// Not including before/after for now.
} hud_alignmode_t;

hud_alignmode_t	hud_alignmode = hud_align_center;

// Possible positions for a grep handle.
typedef enum hud_greppos_s
{
	pos_visible,	// On screen.
	pos_top,
	pos_left,
	pos_bottom,
	pos_right
} hud_greppos_t;

// A Grep handle for when a HUD goes offscreen.
typedef struct hud_grephandle_s
{
	hud_t					*hud;			// HUD associated with this grep handle.
	int						x;				// The position in screen coordinates for the grephandle.
	int						y;
	int						width;
	int						height;
	qbool					highlighted;	// Should this grephandle be drawn highlighted?
	hud_greppos_t			pos;			// The offscreen position of the HUD associated with this handle.
	struct hud_grephandle_s	*next;
	struct hud_grephandle_s	*previous;
} hud_grephandle_t;

hud_grephandle_t	*hud_greps = NULL;					// The list of "grep handles" that are shown if a HUD element is moved offscreen.

hud_grephandle_t	*hud_hoverlist = NULL;				// The list of HUDs the mouse is hovering.
int					hud_hoverlist_count = 0;			// The number of hovered HUDs...
qbool				hud_hoverlist_pos_is_set = false;	// Has the coordinates been set for the hoverlist yet?
float				hud_hoverlist_x;					// The x position of the hover list.
float				hud_hoverlist_y;					// The y position of the hover list.
hud_grephandle_t	hud_containers[MAX_HUD_ELEMENTS];	// List of HUDs which have been hovered.

//
// Adds a new HUD to the list of HUDs that are being hovered.
//
static void HUD_Editor_AddHoverHud(hud_grephandle_t *hud_container)
{
	// Nothing to add.
	if(!hud_container)
	{
		return;
	}

	hud_container->next = hud_hoverlist;
	if(hud_hoverlist)
	{
		hud_hoverlist->previous = hud_container;
	}

	hud_hoverlist = hud_container;

	hud_hoverlist_count++;
}

//
// Associates ("Creates") a HUD with a HUD container.
//
static hud_grephandle_t *HUD_Editor_CreateHoverHud(hud_t *hud)
{
	static int i = 0;
	int j = 0;

	if(i >= MAX_HUD_ELEMENTS - 1)
	{
		return NULL;
	}

	// Check if the HUD already exists and use that one if that's the case...
	for(j = 0; j < MAX_HUD_ELEMENTS && hud_containers[j].hud; j++)
	{
		if(hud_containers[j].hud == hud)
		{
			hud_containers[j].next = NULL;
			hud_containers[j].previous = NULL;
			return &hud_containers[j];
		}
	}

	// The HUD wasn't found so add it to the end of the list.
	hud_containers[i].hud = hud;
	hud_containers[i].next = NULL;
	hud_containers[i].previous = NULL;
	i++;

	return &hud_containers[i - 1];
}

//
// Find the HUD that is being selected in the hover list.
//
static hud_t *HUD_Editor_FindHoverListSelection(hud_grephandle_t *list)
{
	hud_t *match = NULL;
	hud_grephandle_t *hud_iter = list;

	while(hud_iter)
	{
		if(hud_iter->hud
			&& hud_mouse_x > hud_iter->x
			&& hud_mouse_x < (hud_iter->x + hud_iter->width)
			&& hud_mouse_y > hud_iter->y
			&& hud_mouse_y < (hud_iter->y + hud_iter->height))
		{
			hud_iter->highlighted = true;
			match = hud_iter->hud;
		}
		else
		{
			hud_iter->highlighted = false;
		}

		hud_iter = hud_iter->next;
	}

	return match;
}

//
// Draw the hover list...
//
static qbool HUD_Editor_DrawHoverList(int x, int y, hud_grephandle_t *list)
{
	#define PADDING 5
	#define LINEGAP	1
	int width = 0;
	int height = 0;
	int i = 0;
	clrinfo_t color, highlight;
	hud_grephandle_t *hud_iter = list;

	// No point in showing a list if there's only one hud under the cursor
	// or if the user hasn't right-clicked.
	if (hud_hoverlist_count <= 1 || hud_editor_mode != hud_editmode_hoverlist)
	{
		return false;
	}

	// Get the width of to draw with.
	while (hud_iter)
	{
		width = max(width, (PADDING + 2 + strlen(hud_iter->hud->name)) * 8);
		hud_iter = hud_iter->next;
	}

	// Draw the background fill.
	height = ((hud_hoverlist_count - 1) * (8 + LINEGAP)) + (2 * PADDING);
	Draw_AlphaFillRGB(x, y - height, width, height, RGBA_TO_COLOR(0, 0, 0, 255));

	hud_iter = list;

	// Highlight color (yellow)...
	highlight.c = RGBA_TO_COLOR(0, 255, 0, 255);
	highlight.i = 0;

	// Orange.
	color.c = RGBA_TO_COLOR(255, 150, 0, 255);
	color.i = 0;

	// Draw the list items.
	while(hud_iter)
	{
		// Calculate the size and position of the HUD in the list.
		hud_iter->x = x;
		hud_iter->y = y - height + (i * 8) + LINEGAP;
		hud_iter->width = width;
		hud_iter->height = 8;

		// Draw the z-order + name of the HUD.
		Draw_ColoredString3(hud_iter->x, hud_iter->y,
			va("%2d - %s", hud_iter->hud->order->integer, hud_iter->hud->name),
			(hud_iter->highlighted ? &highlight : &color), 1, 0);

		hud_iter = hud_iter->next;
		i++;
	}

	return true;
}

//
// Sets a new HUD Editor mode (and saves the previous one).
//
static void HUD_Editor_SetMode(hud_editor_mode_t newmode)
{
	hud_editor_prevmode = hud_editor_mode;
	hud_editor_mode = newmode;
}

//
// Draws a tool tip with a background next to the cursor.
//
static void HUD_Editor_DrawTooltip(int x, int y, char *string, color_t color) //float r, float g, float b, float a)
{
	int len = strlen(string) * 8;

	y -= 9;

	// Make sure we're drawing within the screen.
	if (x + len > vid.width)
	{
		x -= len;
	}

	if (y + 9 > vid.height)
	{
		y -= 9;
	}

	Draw_AlphaFillRGB(x, y, len, 8, color);
	Draw_String(x, y, string);
}

//
// Gets an alignment string for a specified alignmode enum.
//
static char *HUD_Editor_GetAlignmentString(hud_alignmode_t align)
{
	switch(hud_alignmode)
	{
		case hud_align_center :		return "center center";
		case hud_align_top :		return "center top";
		case hud_align_topleft :	return "left top";
		case hud_align_left :		return "left center";
		case hud_align_bottomleft :	return "left bottom";
		case hud_align_bottom :		return "center bottom";
		case hud_align_bottomright :return "right bottom";
		case hud_align_right :		return "right center";
		case hud_align_topright :	return "right top";
		case hud_align_consoleleft :return "left console";
		case hud_align_console :	return "center console";
		case hud_align_consoleright:return "right console";
		default :					return "";
	}
}

//
// Returns an alignment enum based on a specified alignment string.
//
static hud_alignmode_t HUD_Editor_GetAlignmentFromString(char *alignstr)
{
	if(!strcmp(alignstr, "center center"))
	{
		return hud_align_center;
	}
	else if(!strcmp(alignstr, "center top"))
	{
		return hud_align_top;
	}
	else if(!strcmp(alignstr, "left top"))
	{
		return hud_align_topleft;
	}
	else if(!strcmp(alignstr, "left center"))
	{
		return hud_align_left;
	}
	else if(!strcmp(alignstr, "left bottom"))
	{
		return hud_align_bottomleft;
	}
	else if(!strcmp(alignstr, "center bottom"))
	{
		return hud_align_bottom;
	}
	else if(!strcmp(alignstr, "right bottom"))
	{
		return hud_align_bottomright;
	}
	else if(!strcmp(alignstr, "right center"))
	{
		return hud_align_right;
	}
	else if(!strcmp(alignstr, "right top"))
	{
		return hud_align_topright;
	}
	else if(!strcmp(alignstr, "left console"))
	{
		return hud_align_consoleleft;
	}
	else if(!strcmp(alignstr, "center console"))
	{
		return hud_align_console;
	}
	else if(!strcmp(alignstr, "right console"))
	{
		return hud_align_consoleright;
	}

	return hud_align_center;
}

//
// Returns the alignment we are trying to align the selected HUD to at it's placement
// when in alignment mode.
//
static hud_alignmode_t HUD_Editor_GetAlignment(int x, int y, hud_t *hud_element)
{
	// For less clutter.
	float	mid_x		= 0.0;
	float	mid_y		= 0.0;
	int		offset_x	= 0;
	int		offset_y	= 0;

	if(hud_element)
	{
		// Aligns for HUD elements.
		offset_x = hud_element->lx;
		offset_y = hud_element->ly;
		mid_x = hud_element->lw / 2.0;
		mid_y = hud_element->lh / 2.0;
	}
	else
	{
		// Aligns for Screen.
		offset_x = 0;
		offset_y = (int)(vid.height * 0.05);
		mid_x = vid.width / 2.0;
		mid_y = (vid.height + offset_y) / 2.0;
	}

	if(!hud_element)
	{
		// Console left.
		{
			hud_align_consoleleft_poly[0][0] = 0;
			hud_align_consoleleft_poly[0][1] = 0;
			hud_align_consoleleft_poly[0][2] = 0;

			hud_align_consoleleft_poly[1][0] = 0;
			hud_align_consoleleft_poly[1][1] = offset_y;
			hud_align_consoleleft_poly[1][2] = 0;

			hud_align_consoleleft_poly[2][0] = mid_x / 2.0;
			hud_align_consoleleft_poly[2][1] = offset_y;
			hud_align_consoleleft_poly[2][2] = 0;

			hud_align_consoleleft_poly[3][0] = mid_x / 2.0;
			hud_align_consoleleft_poly[3][1] = 0;
			hud_align_consoleleft_poly[3][2] = 0;

			if (IsPointInPolygon(HUD_ALIGN_POLYCOUNT_CONSOLE, hud_align_consoleleft_poly, x, y))
			{
				hud_align_current_poly = hud_align_consoleleft_poly;
				hud_align_current_polycount = HUD_ALIGN_POLYCOUNT_CONSOLE;
				return hud_align_consoleleft;
			}
		}

		// Console.
		{
			hud_align_console_poly[0][0] = mid_x / 2.0;
			hud_align_console_poly[0][1] = 0;
			hud_align_console_poly[0][2] = 0;

			hud_align_console_poly[1][0] = mid_x / 2.0;
			hud_align_console_poly[1][1] = offset_y;
			hud_align_console_poly[1][2] = 0;

			hud_align_console_poly[2][0] = mid_x + (mid_x / 2.0);
			hud_align_console_poly[2][1] = offset_y;
			hud_align_console_poly[2][2] = 0;

			hud_align_console_poly[3][0] = mid_x + (mid_x / 2.0);
			hud_align_console_poly[3][1] = 0;
			hud_align_console_poly[3][2] = 0;

			if (IsPointInPolygon(HUD_ALIGN_POLYCOUNT_CONSOLE, hud_align_console_poly, x, y))
			{
				hud_align_current_poly = hud_align_console_poly;
				hud_align_current_polycount = HUD_ALIGN_POLYCOUNT_CONSOLE;
				return hud_align_console;
			}
		}

		// Console right.
		{
			hud_align_consoleright_poly[0][0] = mid_x + (mid_x / 2.0);
			hud_align_consoleright_poly[0][1] = 0;
			hud_align_consoleright_poly[0][2] = 0;

			hud_align_consoleright_poly[1][0] = mid_x + (mid_x / 2.0);
			hud_align_consoleright_poly[1][1] = offset_y;
			hud_align_consoleright_poly[1][2] = 0;

			hud_align_consoleright_poly[2][0] = 2 * mid_x;
			hud_align_consoleright_poly[2][1] = offset_y;
			hud_align_consoleright_poly[2][2] = 0;

			hud_align_consoleright_poly[3][0] = 2 * mid_x;
			hud_align_consoleright_poly[3][1] = 0;
			hud_align_consoleright_poly[3][2] = 0;

			if (IsPointInPolygon(HUD_ALIGN_POLYCOUNT_CONSOLE, hud_align_consoleright_poly, x, y))
			{
				hud_align_current_poly = hud_align_consoleright_poly;
				hud_align_current_polycount = HUD_ALIGN_POLYCOUNT_CONSOLE;
				return hud_align_consoleright;
			}
		}
	}

	// Top right.
	{
		hud_align_topright_poly[0][0] = mid_x + (mid_x / 2.0);
		hud_align_topright_poly[0][1] = 0;
		hud_align_topright_poly[0][2] = 0;

		hud_align_topright_poly[1][0] = mid_x + (mid_x / 4.0);
		hud_align_topright_poly[1][1] = mid_y - (mid_y / 2.0);
		hud_align_topright_poly[1][2] = 0;

		hud_align_topright_poly[2][0] = mid_x + (mid_x / 2.0);
		hud_align_topright_poly[2][1] = mid_y - (mid_y / 4.0);
		hud_align_topright_poly[2][2] = 0;

		hud_align_topright_poly[3][0] = 2 * mid_x;
		hud_align_topright_poly[3][1] = mid_y - (mid_y / 2.0);
		hud_align_topright_poly[3][2] = 0;

		hud_align_topright_poly[4][0] = 2 * mid_x;
		hud_align_topright_poly[4][1] = 0;
		hud_align_topright_poly[4][2] = 0;

		if (IsPointInPolygon(HUD_ALIGN_POLYCOUNT_CORNER, hud_align_topright_poly, x - offset_x, y - offset_y))
		{
			hud_align_current_poly = hud_align_topright_poly;
			hud_align_current_polycount = HUD_ALIGN_POLYCOUNT_CORNER;
			return hud_align_topright;
		}
	}

	// Top.
	{
		hud_align_top_poly[0][0] = mid_x / 2.0;
		hud_align_top_poly[0][1] = 0;
		hud_align_top_poly[0][2] = 0;

		hud_align_top_poly[1][0] = mid_x - (mid_x / 4.0);
		hud_align_top_poly[1][1] = mid_y - (mid_y / 2.0);
		hud_align_top_poly[1][2] = 0;

		hud_align_top_poly[2][0] = mid_x + (mid_x / 4.0);
		hud_align_top_poly[2][1] = mid_y - (mid_y / 2.0);
		hud_align_top_poly[2][2] = 0;

		hud_align_top_poly[3][0] = mid_x + (mid_x / 2.0);
		hud_align_top_poly[3][1] = 0;
		hud_align_top_poly[3][2] = 0;

		if (IsPointInPolygon(HUD_ALIGN_POLYCOUNT_EDGE, hud_align_top_poly, x - offset_x, y - offset_y))
		{
			hud_align_current_poly = hud_align_top_poly;
			hud_align_current_polycount = HUD_ALIGN_POLYCOUNT_EDGE;
			return hud_align_top;
		}
	}

	// Top Left.
	{
		hud_align_topleft_poly[0][0] = 0;
		hud_align_topleft_poly[0][1] = 0;
		hud_align_topleft_poly[0][2] = 0;

		hud_align_topleft_poly[1][0] = 0;
		hud_align_topleft_poly[1][1] = mid_y - (mid_y / 2.0);
		hud_align_topleft_poly[1][2] = 0;

		hud_align_topleft_poly[2][0] = mid_x - (mid_x / 2.0);
		hud_align_topleft_poly[2][1] = mid_y - (mid_y / 4.0);
		hud_align_topleft_poly[2][2] = 0;

		hud_align_topleft_poly[3][0] = mid_x - (mid_x / 4.0);
		hud_align_topleft_poly[3][1] = mid_y - (mid_y / 2.0);
		hud_align_topleft_poly[3][2] = 0;

		hud_align_topleft_poly[4][0] = mid_x - (mid_x / 2.0);
		hud_align_topleft_poly[4][1] = 0;
		hud_align_topleft_poly[4][2] = 0;

		if (IsPointInPolygon(HUD_ALIGN_POLYCOUNT_CORNER, hud_align_topleft_poly, x - offset_x, y - offset_y))
		{
			hud_align_current_poly = hud_align_topleft_poly;
			hud_align_current_polycount = HUD_ALIGN_POLYCOUNT_CORNER;
			return hud_align_topleft;
		}
	}

	// Left.
	{
		hud_align_left_poly[0][0] = 0;
		hud_align_left_poly[0][1] = mid_y / 2.0;
		hud_align_left_poly[0][2] = 0;

		hud_align_left_poly[1][0] = 0;
		hud_align_left_poly[1][1] = mid_y + (mid_y / 2.0);
		hud_align_left_poly[1][2] = 0;

		hud_align_left_poly[2][0] = mid_x / 2.0;
		hud_align_left_poly[2][1] = mid_y + (mid_y / 4.0);
		hud_align_left_poly[2][2] = 0;

		hud_align_left_poly[3][0] = mid_x / 2.0;
		hud_align_left_poly[3][1] = mid_y - (mid_y / 4.0);
		hud_align_left_poly[3][2] = 0;

		if (IsPointInPolygon(HUD_ALIGN_POLYCOUNT_EDGE, hud_align_left_poly, x - offset_x, y - offset_y))
		{
			hud_align_current_poly = hud_align_left_poly;
			hud_align_current_polycount = HUD_ALIGN_POLYCOUNT_EDGE;
			return hud_align_left;
		}
	}

	// Bottom Left.
	{
		hud_align_bottomleft_poly[0][0] = 0;
		hud_align_bottomleft_poly[0][1] = mid_y + (mid_y / 2.0);
		hud_align_bottomleft_poly[0][2] = 0;

		hud_align_bottomleft_poly[1][0] = 0;
		hud_align_bottomleft_poly[1][1] = 2 * mid_y;
		hud_align_bottomleft_poly[1][2] = 0;

		hud_align_bottomleft_poly[2][0] = mid_x / 2.0;
		hud_align_bottomleft_poly[2][1] = 2 * mid_y;
		hud_align_bottomleft_poly[2][2] = 0;

		hud_align_bottomleft_poly[3][0] = mid_x - (mid_x / 4.0);
		hud_align_bottomleft_poly[3][1] = mid_y + (mid_y / 2.0);
		hud_align_bottomleft_poly[3][2] = 0;

		hud_align_bottomleft_poly[4][0] = mid_x / 2.0;
		hud_align_bottomleft_poly[4][1] = mid_y + (mid_y / 4.0);
		hud_align_bottomleft_poly[4][2] = 0;

		if (IsPointInPolygon(HUD_ALIGN_POLYCOUNT_CORNER, hud_align_bottomleft_poly, x - offset_x, y - offset_y))
		{
			hud_align_current_poly = hud_align_bottomleft_poly;
			hud_align_current_polycount = HUD_ALIGN_POLYCOUNT_CORNER;
			return hud_align_bottomleft;
		}
	}

	// Bottom.
	{
		hud_align_bottom_poly[0][0] = mid_x - (mid_x / 2.0);
		hud_align_bottom_poly[0][1] = 2 * mid_y;
		hud_align_bottom_poly[0][2] = 0;

		hud_align_bottom_poly[1][0] = mid_x + (mid_x / 2.0);
		hud_align_bottom_poly[1][1] = 2 * mid_y;
		hud_align_bottom_poly[1][2] = 0;

		hud_align_bottom_poly[2][0] = mid_x + (mid_x / 4.0);
		hud_align_bottom_poly[2][1] = mid_y + (mid_y / 2.0);
		hud_align_bottom_poly[2][2] = 0;

		hud_align_bottom_poly[3][0] = mid_x - (mid_x / 4.0);
		hud_align_bottom_poly[3][1] = mid_y + (mid_y / 2.0);
		hud_align_bottom_poly[3][2] = 0;

		if (IsPointInPolygon(HUD_ALIGN_POLYCOUNT_EDGE, hud_align_bottom_poly, x - offset_x, y - offset_y))
		{
			hud_align_current_poly = hud_align_bottom_poly;
			hud_align_current_polycount = HUD_ALIGN_POLYCOUNT_EDGE;
			return hud_align_bottom;
		}
	}

	// Bottom Right.
	{
		hud_align_bottomright_poly[0][0] = mid_x + (mid_x / 2.0);
		hud_align_bottomright_poly[0][1] = 2 * mid_y;
		hud_align_bottomright_poly[0][2] = 0;

		hud_align_bottomright_poly[1][0] = 2 * mid_x;
		hud_align_bottomright_poly[1][1] = 2 * mid_y;
		hud_align_bottomright_poly[1][2] = 0;

		hud_align_bottomright_poly[2][0] = 2 * mid_x;
		hud_align_bottomright_poly[2][1] = mid_y + (mid_y / 2.0);
		hud_align_bottomright_poly[2][2] = 0;

		hud_align_bottomright_poly[3][0] = mid_x + (mid_x / 2.0);
		hud_align_bottomright_poly[3][1] = mid_y + (mid_y / 4.0);
		hud_align_bottomright_poly[3][2] = 0;

		hud_align_bottomright_poly[4][0] = mid_x + (mid_x / 4.0);
		hud_align_bottomright_poly[4][1] = mid_y + (mid_y / 2.0);
		hud_align_bottomright_poly[4][2] = 0;

		if (IsPointInPolygon(HUD_ALIGN_POLYCOUNT_CORNER, hud_align_bottomright_poly, x - offset_x, y - offset_y))
		{
			hud_align_current_poly = hud_align_bottomright_poly;
			hud_align_current_polycount = HUD_ALIGN_POLYCOUNT_CORNER;
			return hud_align_bottomright;
		}
	}

	// Right.
	{
		hud_align_right_poly[0][0] = 2 * mid_x;
		hud_align_right_poly[0][1] = mid_y + (mid_y / 2.0);
		hud_align_right_poly[0][2] = 0;

		hud_align_right_poly[1][0] = 2 * mid_x;
		hud_align_right_poly[1][1] = mid_y / 2.0;
		hud_align_right_poly[1][2] = 0;

		hud_align_right_poly[2][0] = mid_x + (mid_x / 2.0);
		hud_align_right_poly[2][1] = mid_y - (mid_y / 4.0);
		hud_align_right_poly[2][2] = 0;

		hud_align_right_poly[3][0] = mid_x + (mid_x / 2.0);
		hud_align_right_poly[3][1] = mid_y + (mid_y / 4.0);
		hud_align_right_poly[3][2] = 0;

		if (IsPointInPolygon(HUD_ALIGN_POLYCOUNT_EDGE, hud_align_right_poly, x - offset_x, y - offset_y))
		{
			hud_align_current_poly = hud_align_right_poly;
			hud_align_current_polycount = HUD_ALIGN_POLYCOUNT_EDGE;
			return hud_align_right;
		}
	}

	// Center.
	{
		hud_align_center_poly[0][0] = mid_x - (mid_x / 4.0);
		hud_align_center_poly[0][1] = mid_y / 2.0;
		hud_align_center_poly[0][2] = 0;

		hud_align_center_poly[1][0] = mid_x / 2.0;
		hud_align_center_poly[1][1] = mid_y - (mid_y / 4.0);
		hud_align_center_poly[1][2] = 0;

		hud_align_center_poly[2][0] = mid_x / 2.0;
		hud_align_center_poly[2][1] = mid_y + (mid_y / 4.0);
		hud_align_center_poly[2][2] = 0;

		hud_align_center_poly[3][0] = mid_x - (mid_x / 4.0);
		hud_align_center_poly[3][1] = mid_y + (mid_y / 2.0);
		hud_align_center_poly[3][2] = 0;

		hud_align_center_poly[4][0] = mid_x + (mid_x / 4.0);
		hud_align_center_poly[4][1] = mid_y + (mid_y / 2.0);
		hud_align_center_poly[4][2] = 0;

		hud_align_center_poly[5][0] = mid_x + (mid_x / 2.0);
		hud_align_center_poly[5][1] = mid_y + (mid_y / 4.0);
		hud_align_center_poly[5][2] = 0;

		hud_align_center_poly[6][0] = mid_x + (mid_x / 2.0);
		hud_align_center_poly[6][1] = mid_y - (mid_y / 4.0);
		hud_align_center_poly[6][2] = 0;

		hud_align_center_poly[7][0] = mid_x + (mid_x / 4.0);
		hud_align_center_poly[7][1] = mid_y / 2.0;
		hud_align_center_poly[7][2] = 0;

		if (IsPointInPolygon(HUD_ALIGN_POLYCOUNT_CENTER, hud_align_center_poly, x - offset_x, y - offset_y))
		{
			hud_align_current_poly = hud_align_center_poly;
			hud_align_current_polycount = HUD_ALIGN_POLYCOUNT_CENTER;
			return hud_align_center;
		}
	}

	// Default to center.
	hud_align_current_poly = hud_align_center_poly;
	hud_align_current_polycount = HUD_ALIGN_POLYCOUNT_CENTER;
	return hud_align_center;
}

//
// Finds the next child of the specified HUD element.
//
static hud_t *HUD_Editor_FindNextChild(hud_t *hud_element)
{
	static hud_t	*hud_it = NULL;
	static hud_t	*parent = NULL;
	hud_t			*hud_result = NULL;

	// Reset everything if we're given a NULL argument.
	if(!hud_element)
	{
		hud_it = NULL;
		parent = NULL;
		return NULL;
	}

	// There's a new parent so start searching from the beginning.
	if(!parent || strcmp(parent->name, hud_element->name))
	{
		parent = hud_element;
		hud_it = hud_huds;
	}

	while(hud_it)
	{
		// Check if this HUD is placed at the parent, if so we've found a child.
		if(hud_it->place_hud && !strcmp(hud_it->place_hud->name, parent->name))
		{
			hud_result = hud_it;
			hud_it = hud_it->next;
			return hud_result;
		}

		hud_it = hud_it->next;
	}

	parent = NULL;

	// No children found.
	return NULL;
}

//
// Moves a HUD element.
//
static void HUD_Editor_Move(float dx, float dy, hud_t *hud_element)
{
	if(!hud_element)
	{
		return;
	}

	Cvar_SetValue(hud_element->pos_x, hud_element->pos_x->value + dx);
	Cvar_SetValue(hud_element->pos_y, hud_element->pos_y->value + dy);
}

//
// Draw the current alignment.
//
static void HUD_Editor_DrawAlignment(hud_t *hud_parent)
{
	extern void Draw_Polygon(int x, int y, vec3_t *vertices, int num_vertices, qbool fill, color_t color);
	color_t c = RGBA_TO_COLOR(255, 255, 0, 50);

	if(hud_parent)
	{
		//Draw_Polygon(hud_parent->lx, hud_parent->ly, hud_align_current_poly, hud_align_current_polycount, true, RGBA_TO_COLOR(255, 255, 0, 50));
		Draw_Polygon(hud_parent->lx, hud_parent->ly, hud_align_current_poly, hud_align_current_polycount, true, c);
	}
	else
	{
		//Draw_Polygon(0, 0, hud_align_current_poly, hud_align_current_polycount, true, RGBA_TO_COLOR(255, 255, 0, 50));
		Draw_Polygon(0, 0, hud_align_current_poly, hud_align_current_polycount, true, c);
	}
}

typedef struct hud_resize_handle_s
{
	int x;
	int y;
	int width;
	int height;
} hud_resize_handle_t;

#define HUD_RESIZEHANDLE_THICKNESS		5
#define HUD_RESIZEHANDLE_SIZEFACTOR		0.3
#define HUD_RESIZEHANDLE_COUNT			8
#define HUD_RESIZE_MAGICSCALE			200 // HACK : This seems to work nice though...

#define HUD_RESIZEHANDLE_NONE			-1
#define HUD_RESIZEHANDLE_TOPLEFT		0
#define HUD_RESIZEHANDLE_TOP			1
#define HUD_RESIZEHANDLE_TOPRIGHT		2
#define HUD_RESIZEHANDLE_RIGHT			3
#define HUD_RESIZEHANDLE_BOTTOMRIGHT	4
#define HUD_RESIZEHANDLE_BOTTOM			5
#define HUD_RESIZEHANDLE_BOTTOMLEFT		6
#define HUD_RESIZEHANDLE_LEFT			7

//
// Resizes the height/width of a HUD element by a delta size and alignment.
//
static void HUD_Editor_ResizeDelta(cvar_t *size, float delta_size, hud_alignmode_t alignment)
{
	if(!size)
	{
		return;
	}

	switch(alignment)
	{
		case hud_align_right :
		case hud_align_bottom :
			Cvar_SetValue(size, size->value + delta_size);
			break;
		case hud_align_left :
		case hud_align_top :
			Cvar_SetValue(size, size->value - delta_size);
			break;
		// unhandled
		case hud_align_center:
		case hud_align_topleft:
		case hud_align_bottomleft:
		case hud_align_bottomright:
		case hud_align_topright:
		case hud_align_consoleleft:
		case hud_align_consoleright:
		case hud_align_console:
			break;
	}
}

//
// Scales a HUD element.
//
static void HUD_EditorScaleDelta(cvar_t *scale, float delta_scale, hud_alignmode_t alignment)
{
	if(!scale)
	{
		return;
	}

	switch(alignment)
	{
		case hud_align_topleft :
		case hud_align_bottomleft :
			Cvar_SetValue(scale, scale->value - delta_scale);
			break;
		case hud_align_topright :
		case hud_align_bottomright :
			Cvar_SetValue(scale, scale->value + delta_scale);
			break;
		// unhandled
		case hud_align_center:
		case hud_align_top:
		case hud_align_left:
		case hud_align_bottom:
		case hud_align_right:
		case hud_align_consoleleft:
		case hud_align_consoleright:
		case hud_align_console:
			break;
	}

	if(scale->value < 0)
	{
		Cvar_SetValue(scale, 0);
	}
}

//
// Checks if the HUD element we're hovering has any dimension variables
// and handles resizing for it if it does by drawing resize handles
// that the user can click.
//
static qbool HUD_Editor_Resizing(hud_t *hud_hover)
{
	extern float mouse_x, mouse_y;		// in_win.c, delta mouse.
	int i					= 0;
	qbool found_handle		= false;	// Did we find any handle under the cursor?
	static cvar_t *width	= NULL;
	static cvar_t *height	= NULL;
	static cvar_t *scale	= NULL;
	hud_resize_handle_t *resize_handles[HUD_RESIZEHANDLE_COUNT];
	static int last_resize_handle = HUD_RESIZEHANDLE_NONE;

	// Is this mode turned on?
	if(!hud_editor_allowresize.value)
	{
		return false;
	}

	// Select a HUD if it hasn't already been done.
	if(hud_hover && hud_editor_mode == hud_editmode_move_resize
		&& !selected_hud && hud_hover)
	{
		selected_hud = hud_hover;
		return true;
	}

	memset(resize_handles, 0, sizeof(resize_handles));

	// Try getting any available dimension variables
	// that are available for this HUD element, these
	// aren't mandatory for all HUD elements, so only
	// some will have them.
	if(hud_hover)
	{
		width	= HUD_FindVar(hud_hover, "width");
		height	= HUD_FindVar(hud_hover, "height");
		scale	= HUD_FindVar(hud_hover, "scale");

		if(width)
		{
			// Right & left.
			static hud_resize_handle_t right;
			static hud_resize_handle_t left;

			right.width		= HUD_RESIZEHANDLE_THICKNESS;
			right.height	= hud_hover->lh * HUD_RESIZEHANDLE_SIZEFACTOR;
			right.x			= hud_hover->lw - right.width;
			right.y			= (hud_hover->lh - right.height) / 2;
			resize_handles[HUD_RESIZEHANDLE_RIGHT] = &right;

			left.width		= HUD_RESIZEHANDLE_THICKNESS;
			left.height		= hud_hover->lh * HUD_RESIZEHANDLE_SIZEFACTOR;
			left.x			= 0;
			left.y			= (hud_hover->lh - left.height) / 2;
			resize_handles[HUD_RESIZEHANDLE_LEFT] = &left;
		}

		if(height)
		{
			// Top & bottom
			static hud_resize_handle_t top;
			static hud_resize_handle_t bottom;

			top.width		= hud_hover->lw * HUD_RESIZEHANDLE_SIZEFACTOR;
			top.height		= HUD_RESIZEHANDLE_THICKNESS;
			top.x			= (hud_hover->lw - top.width) / 2;
			top.y			= 0;
			resize_handles[HUD_RESIZEHANDLE_TOP] = &top;

			bottom.width		= hud_hover->lw * HUD_RESIZEHANDLE_SIZEFACTOR;
			bottom.height		= HUD_RESIZEHANDLE_THICKNESS;
			bottom.x			= (hud_hover->lw - bottom.width) / 2;
			bottom.y			= hud_hover->lh - bottom.height;
			resize_handles[HUD_RESIZEHANDLE_BOTTOM] = &bottom;
		}

		if(scale)
		{
			// Top left, top right, bottom left & bottom right.
			static hud_resize_handle_t topleft;
			static hud_resize_handle_t bottomleft;
			static hud_resize_handle_t topright;
			static hud_resize_handle_t bottomright;

			topleft.width		= HUD_RESIZEHANDLE_THICKNESS;
			topleft.height		= HUD_RESIZEHANDLE_THICKNESS;
			topleft.x			= 0;
			topleft.y			= 0;
			resize_handles[HUD_RESIZEHANDLE_TOPLEFT] = &topleft;

			bottomleft.width	= HUD_RESIZEHANDLE_THICKNESS;
			bottomleft.height	= HUD_RESIZEHANDLE_THICKNESS;
			bottomleft.x		= 0;
			bottomleft.y		= hud_hover->lh - bottomleft.height;
			resize_handles[HUD_RESIZEHANDLE_BOTTOMLEFT] = &bottomleft;

			topright.width		= HUD_RESIZEHANDLE_THICKNESS;
			topright.height		= HUD_RESIZEHANDLE_THICKNESS;
			topright.x			= hud_hover->lw - topright.width;
			topright.y			= 0;
			resize_handles[HUD_RESIZEHANDLE_TOPRIGHT] = &topright;

			bottomright.width	= HUD_RESIZEHANDLE_THICKNESS;
			bottomright.height	= HUD_RESIZEHANDLE_THICKNESS;
			bottomright.x		= hud_hover->lw - bottomright.width;
			bottomright.y		= hud_hover->lh - bottomright.height;
			resize_handles[HUD_RESIZEHANDLE_BOTTOMRIGHT] = &bottomright;
		}
	}

	for(i = 0; i < HUD_RESIZEHANDLE_COUNT; i++)
	{
		// Non existant for this HUD element.
		if(!resize_handles[i])
		{
			continue;
		}

		if(hud_editor_mode == hud_editmode_move_resize)
		{
			// We're in resize mode, so check if we're clicking any of the
			// resize handles.

			if((selected_hud && (last_resize_handle == i)) ||
				  (hud_mouse_x >= (selected_hud->lx + resize_handles[i]->x)
				&& (hud_mouse_x <= (selected_hud->lx + resize_handles[i]->x + resize_handles[i]->width))
				&& (hud_mouse_y >= (selected_hud->ly + resize_handles[i]->y))
				&& (hud_mouse_y <= (selected_hud->ly + resize_handles[i]->y + resize_handles[i]->height))))
			{
				// Keep track of which resize handle we're grabbing
				// so that it doesn't matter if the mouse "slips" outside
				// it's bounding box as long as the mouse is still pressed.
				if(last_resize_handle < 0 || last_resize_handle != i)
				{
					last_resize_handle = i;
				}

				found_handle = true;

				// Draw the resize handle highlighted.
				Draw_AlphaFillRGB(
					selected_hud->lx + resize_handles[last_resize_handle]->x,
					selected_hud->ly + resize_handles[last_resize_handle]->y,
					resize_handles[last_resize_handle]->width,
					resize_handles[last_resize_handle]->height,
					RGBA_TO_COLOR(255, 255, 0, 125));

				// Check which resize handle that has been selected
				// and resize the HUD element accordingly.
				switch(i)
				{
					case HUD_RESIZEHANDLE_RIGHT :
						HUD_Editor_ResizeDelta(width, mouse_x, hud_align_right);
						break;
					case HUD_RESIZEHANDLE_LEFT :
						HUD_Editor_ResizeDelta(width, mouse_x, hud_align_left);
						break;
					case HUD_RESIZEHANDLE_TOP :
						HUD_Editor_ResizeDelta(height, mouse_y, hud_align_top);
						break;
					case HUD_RESIZEHANDLE_BOTTOM :
						HUD_Editor_ResizeDelta(height, mouse_y, hud_align_bottom);
						break;
					case HUD_RESIZEHANDLE_TOPRIGHT :
						// HACK : Dividing by 200 seems to work fine, but this could probably be done a lot nicer.
						HUD_EditorScaleDelta(scale, (mouse_x - mouse_y) / HUD_RESIZE_MAGICSCALE, hud_align_topright);
						break;
					case HUD_RESIZEHANDLE_BOTTOMRIGHT :
						HUD_EditorScaleDelta(scale, (mouse_x + mouse_y) / HUD_RESIZE_MAGICSCALE, hud_align_bottomright);
						break;
					case HUD_RESIZEHANDLE_TOPLEFT :
						HUD_EditorScaleDelta(scale, (mouse_x + mouse_y) / HUD_RESIZE_MAGICSCALE, hud_align_topleft);
						break;
					case HUD_RESIZEHANDLE_BOTTOMLEFT :
						HUD_EditorScaleDelta(scale, (mouse_x - mouse_y) / HUD_RESIZE_MAGICSCALE, hud_align_bottomleft);
						break;
				}

				// Recalculate all HUD elements.
				HUD_Recalculate();
				return true;
			}
		}
		else if(hud_editor_prevmode == hud_editmode_move_resize)
		{
			// We left resize mode.
			selected_hud = NULL;
			last_resize_handle = HUD_RESIZEHANDLE_NONE;
		}
		else if(hud_hover)
		{
			// If we're hovering a HUD, always draw all it's resize handles.
			if((hud_mouse_x >= (hud_hover->lx + resize_handles[i]->x)
				&& hud_mouse_x <= (hud_hover->lx + resize_handles[i]->x + resize_handles[i]->width)
				&& hud_mouse_y >= (hud_hover->ly + resize_handles[i]->y)
				&& hud_mouse_y <= (hud_hover->ly + resize_handles[i]->y + resize_handles[i]->height)))
			{
				// Highlight the resize handle under the cursor.
				Draw_AlphaFillRGB(hud_hover->lx + resize_handles[i]->x, hud_hover->ly + resize_handles[i]->y,
					resize_handles[i]->width, resize_handles[i]->height,
					RGBA_TO_COLOR(255, 255, 0, 125));

				// Set the resize cursor icon since we're hovering a resize handle.
				scr_cursor_icon = hud_editor_resize_icon;
				found_handle = true;
			}
			else
			{
				// Normal resize handle.
				Draw_AlphaFillRGB(hud_hover->lx + resize_handles[i]->x, hud_hover->ly + resize_handles[i]->y,
				resize_handles[i]->width, resize_handles[i]->height,
				RGBA_TO_COLOR(255, 255, 0, 50));
			}
		}
	}

	// We didn't hover any resize handle so show no icon.
	if(!found_handle)
	{
		scr_cursor_icon = NULL;
	}

	// We didn't perform any action, so let others try.
	return false;
}

static qbool hud_editor_locked_axis_is_x = true;	// Are we locking X- or Y-axis movement?
static qbool hud_editor_lockaxis_found= false;		// Have we found what axist to lock on to?
static qbool hud_editor_finding_lockaxis = false;	// Are we in the process of finding the lock axis?

//
// Check if we're supposed to be moving anything.
//
static qbool HUD_Editor_Moving(hud_t *hud_hover)
{
	// Mouse delta (in_win.c)
	extern float mouse_x, mouse_y;

	// Is this mode allowed?
	if(!hud_editor_allowmove.value)
	{
		return false;
	}

	// Set the move cursor icon if we're hovering a HUD element
	// unless it's not already set (resize may have set it if
	// a resize handle is being hovered).
	if(hud_hover && !scr_cursor_icon)
	{
		scr_cursor_icon = hud_editor_move_icon;
	}
	else if(!hud_hover)
	{
		// Not hovering anything so show no cursor.
		scr_cursor_icon = NULL;
	}

	// Left mousebutton down = lets move it !
	if (hud_editor_mode == hud_editmode_move_resize || hud_editor_mode == hud_editmode_move_lockedaxis)
	{
		// If we just entered movement mode, nothing is selected
		// so select the hud we're hovering to start with.
		if(!selected_hud && hud_hover)
		{
			selected_hud = hud_hover;
			return true;
		}

		if(selected_hud)
		{
			// Move using the mouse delta instead of the absolute
			// mouse cursor coordinates on the screen.

			// Lock the movement to the axis that the user starts
			// dragging the HUD element along if we're in locked-axis mode.
			if(hud_editor_mode == hud_editmode_move_lockedaxis)
			{
				// We haven't found the axis.
				if(!hud_editor_lockaxis_found)
				{
					static float last_mouse_x = 0.0;
					static float last_mouse_y = 0.0;

					if(!hud_editor_finding_lockaxis)
					{
						// Start trying to find the lock axis.
						hud_editor_finding_lockaxis = true;
						last_mouse_x = 0;
						last_mouse_y = 0;
					}
					else
					{
						// Get the delta of the mouse movement from when the
						// user clicked the mouse button and started dragging.
						float mouse_delta_x = fabs(last_mouse_x - mouse_x);
						float mouse_delta_y = fabs(last_mouse_y - mouse_y);

						// Increment the mouse movement since last frame.
						last_mouse_x += mouse_x;
						last_mouse_y += mouse_y;

						// If the mouse has moved more than 5 pixels in any
						// direction we decide to lock onto that axis.
						if(mouse_delta_x > 5 || mouse_delta_y > 5)
						{
							hud_editor_locked_axis_is_x = (mouse_delta_x > mouse_delta_y);
							hud_editor_lockaxis_found = true;
						}
					}
				}

				// Move while locked to the axis.
				if(hud_editor_locked_axis_is_x)
				{
					// Move only along X-axis.
					HUD_Editor_Move(mouse_x, 0, hud_hover);
				}
				else
				{
					// Move only along Y-axis.
					HUD_Editor_Move(0, mouse_y, hud_hover);
				}
			}
			else
			{
				// Ordinary move.
				HUD_Editor_Move(mouse_x, mouse_y, hud_hover);
			}

			HUD_Recalculate();
			return true;
		}
	}
	else if((hud_editor_prevmode == hud_editmode_move_resize && hud_editor_mode != hud_editmode_move_lockedaxis)
		 || (hud_editor_prevmode == hud_editmode_move_lockedaxis && hud_editor_mode != hud_editmode_move_resize))
	{
		// Only deselect if we're going from a move mode to a non-move mode (Such as align/place).

		// We've left move mode, so deselect.
		selected_hud = NULL;
		hud_editor_lockaxis_found = false;
		hud_editor_finding_lockaxis = false;
	}

	// We haven't handled the input.
	return false;
}

//
// Handles input from mouse if in alignment mode.
//
static qbool HUD_Editor_Aligning(hud_t *hud_hover)
{
	// Is this mode allowed?
	if(!hud_editor_allowalign.value)
	{
		return false;
	}

	if(hud_editor_mode == hud_editmode_align)
	{
		// If we just entered alignment mode, nothing is selected
		// so select the hud we're hovering to start with.
		if(!selected_hud && hud_hover)
		{
			selected_hud = hud_hover;
			return true;
		}

		// Set the align icon for the cursor.
		scr_cursor_icon = hud_editor_align_icon;

		// We have something selected so show some visual
		// feedback for when aligning to the user.
		if(selected_hud)
		{
			if(selected_hud->place_hud)
			{
				// Placed at another HUD, so align onto that.
				hud_alignmode = HUD_Editor_GetAlignment(hud_mouse_x, hud_mouse_y, selected_hud->place_hud);
				HUD_Editor_DrawAlignment(selected_hud->place_hud);
			}
			else
			{
				// Placed on the screen.
				hud_alignmode = HUD_Editor_GetAlignment(hud_mouse_x, hud_mouse_y, NULL);
				HUD_Editor_DrawAlignment(NULL);
			}
		}
	}
	else if(hud_editor_prevmode == hud_editmode_align && keydown[K_ALT])
	{
		// The user just released the mousebutton but is still holding
		// down ALT. If the user releases ALT before the mouse button
		// the operation will be cancelled. So commit the users actions.

		// We must have something to align.
		if(selected_hud)
		{
			// Reset position.
			Cvar_Set(selected_hud->pos_x, "0");
			Cvar_Set(selected_hud->pos_y, "0");

			// Align to the area the mouse is placed over (previously set in hud_alignmode).
			Cbuf_AddText(va("align %s %s\n", selected_hud->name, HUD_Editor_GetAlignmentString(hud_alignmode)));
			HUD_Recalculate();

			// Free selection.
			selected_hud = NULL;
			return true;
		}
	}

	return false;
}

//
// Handles feedback/commiting of actions when in placement mode.
//
static qbool HUD_Editor_Placing(hud_t *hud_hover)
{
	// Is this mode allowed?
	if(!hud_editor_allowplace.value)
	{
		return false;
	}

	// Show the place icon at the cursor if ctrl is pressed
	// while hovering a HUD element.
	if(hud_hover && keydown[K_CTRL])
	{
		// Set the place cursor icon.
		scr_cursor_icon = hud_editor_place_icon;
	}
	else if(!hud_hover)
	{
		scr_cursor_icon = NULL;
	}

	if(hud_editor_mode == hud_editmode_place)
	{
		// If we just entered placement mode, nothing is selected
		// so select the hud we're hovering to start with.
		if(!selected_hud && hud_hover)
		{
			selected_hud = hud_hover;
			return true;
		}

		// Set the place cursor icon.
		scr_cursor_icon = hud_editor_place_icon;

		if(selected_hud)
		{
			// Find the center of the selected HUD so we know where
			// to draw the line from.

			if(hud_hover)
			{
				// We're trying to place the HUD on itself or on the HUD it's already placed at.
				if(hud_hover == selected_hud || (selected_hud->place_hud && selected_hud->place_hud == hud_hover))
				{
					// Red "not allowed".
					Draw_AlphaRectangleRGB(hud_hover->lx, hud_hover->ly, hud_hover->lw, hud_hover->lh, 1, true, RGBA_TO_COLOR(255, 0, 0, 25));
					Draw_AlphaRectangleRGB(hud_hover->lx, hud_hover->ly, hud_hover->lw, hud_hover->lh, 1, false, RGBA_TO_COLOR(255, 0, 0, 255));
				}
				else
				{
					// Green "allowed" placement.
					Draw_AlphaRectangleRGB(hud_hover->lx, hud_hover->ly, hud_hover->lw, hud_hover->lh, 1, true, RGBA_TO_COLOR(0, 255, 0, 25));
				}

				return true;
			}

			// Placement at the screen (We don't care about stuff like IFREE, HBAR, SBAR and so on).
			if(!strcmp(selected_hud->place->string, "screen"))
			{
				// Not allowed, already placed there.
				Draw_AlphaRectangleRGB(0, 0, vid.width, vid.height, 1, true, RGBA_TO_COLOR(255, 0, 0, 25));
			}
			else
			{
				// Allowed.
				Draw_AlphaRectangleRGB(0, 0, vid.width, vid.height, 1, true, RGBA_TO_COLOR(0, 255, 0, 25));
			}
		}
	}
	else if(hud_editor_prevmode == hud_editmode_place && keydown[K_CTRL])
	{
		// We've just exited placement mode, but control is still pressed,
		// that means we should place the selected_hud.
		// (If you release ctrl before you release Mouse 1, you cancel the place operation).

		if(selected_hud)
		{
			// If we're hovering a HUD place it there.
			if(hud_hover)
			{
				// We're trying to place the HUD on itself or on the HUD it's already placed at so do nothing.
				if(hud_hover == selected_hud || (selected_hud->place_hud && selected_hud->place_hud == hud_hover))
				{
					return true;
				}

				// Place at other HUD.
				Cvar_Set(selected_hud->align_x, "center");
				Cvar_Set(selected_hud->align_y, "center");
				Cvar_Set(selected_hud->pos_x, "0");
				Cvar_Set(selected_hud->pos_y, "0");
				Cvar_Set(selected_hud->place, hud_hover->name);

				// Make sure the child has a higher order.
				if((int)selected_hud->order->value <= (int)hud_hover->order->value)
				{
					Cvar_SetValue(selected_hud->order, hud_hover->order->value + 1);
				}

				HUD_Recalculate();

				// Free selection.
				selected_hud = NULL;
				return true;
			}
			else
			{
				// Mouse button was released on on the "screen".

				if(!strcmp(selected_hud->place->string, "screen"))
				{
					return false;
				}

				// Place at other HUD.
				Cvar_Set(selected_hud->align_x, "center");
				Cvar_Set(selected_hud->align_y, "center");
				Cvar_Set(selected_hud->pos_x, "0");
				Cvar_Set(selected_hud->pos_y, "0");
				Cvar_Set(selected_hud->place, "screen");
				HUD_Recalculate();

				// Free selection.
				selected_hud = NULL;
				return true;
			}
		}
	}

	// We weren't placing something so check other states also.
	return false;
}

// =============================================================================
//							HUD Editor Grep Handles.
// =============================================================================
// These show up when a HUD element is moved completly offscreen (by accident
// most likely), so that the user can still grab it and move it back onto the
// screen again.
// =============================================================================

//
// Finds a HUD grephandle (if the hud element has gone offscreen).
//
static hud_grephandle_t *HUD_Editor_FindGrep(hud_t *hud_element)
{
	hud_grephandle_t *greps_it = NULL;
	greps_it = hud_greps;

	while(greps_it)
	{
		if(!strcmp(greps_it->hud->name, hud_element->name))
		{
			return greps_it;
		}

		greps_it = greps_it->next;
	}

	return NULL;
}

//
// Returns an "offscreen" arrow in the correct direction based on where
// offscreen the HUD element is located.
//
static char *HUD_Editor_GetGrepArrow(hud_grephandle_t *grep)
{
	switch(grep->pos)
	{
		case pos_top :		return "/\\";
		case pos_bottom :	return "\\/";
		case pos_left :		return "<<<";
		case pos_right :	return ">>>";
		default :			return "";
	}
}

//
// Draws the grephandles.
//
static void HUD_Editor_DrawGreps()
{
	clrinfo_t color, highlight;
	hud_grephandle_t *greps_it = NULL;
	greps_it = hud_greps;

	// Highlight color (yellow).
	highlight.c = RGBA_TO_COLOR(0, 255, 0, 255);
	highlight.i = 0;

	// Orange.
	color.c = RGBA_TO_COLOR(255, 150, 0, 255);
	color.i = 0;

	while(greps_it)
	{
		Draw_ColoredString3(greps_it->x, greps_it->y,
			va("%s %s", HUD_Editor_GetGrepArrow(greps_it), greps_it->hud->name),
			(greps_it->highlighted ? &highlight : &color), 1, 0);

		greps_it = greps_it->next;
	}
}

//
// Get's the position offscreen for a HUD element
// left/right/top/bottom or visible if it's not offscreen.
//
static hud_greppos_t HUD_Editor_GetHudGrepPosition(hud_t *hud)
{
	if(hud->lx + hud->lw <= 0)
	{
		return pos_left;
	}
	else if(hud->lx >= (signed)vid.width)
	{
		return pos_right;
	}
	else if(hud->ly + hud->lh <= 0)
	{
		return pos_top;
	}
	else if(hud->ly >= (signed)vid.height)
	{
		return pos_bottom;
	}

	return pos_visible;
}

//
// Positions a grephandle based on it's position and where the HUD element
// it's associated with is located.
//
static void HUD_Editor_PositionGrep(hud_t *hud_element, hud_grephandle_t *grep)
{
	// Get the position of the grephandle.
	grep->pos = HUD_Editor_GetHudGrepPosition(hud_element);

	// Position the grephandle on screen.
	switch(grep->pos)
	{
		case pos_top :
			grep->x	= hud_element->lx;
			grep->y	= 5;
			break;
		case pos_bottom :
			grep->x	= hud_element->lx;
			grep->y	= vid.height - grep->height - 5;
			break;
		case pos_left :
			grep->x = 5;
			grep->y = hud_element->ly;
			break;
		case pos_right :
			grep->x = vid.width - grep->width - 5;
			grep->y = hud_element->ly;
			break;
		default :
			grep->x = hud_element->lx;
			grep->y = hud_element->ly;
			break;
	}
}

//
// Creates a new grephandle and associates it with a HUD element that is offscreen.
//
static hud_grephandle_t *HUD_Editor_CreateGrep(hud_t *hud_element)
{
	hud_grephandle_t *grep = NULL;

	grep			= Q_malloc(sizeof(hud_grephandle_t));
	memset(grep, 0, sizeof(*grep));

	grep->width		= 8 * (4 + strlen(hud_element->name));
	grep->height	= 8;
	grep->hud		= hud_element;
	grep->previous	= NULL;
	grep->next		= hud_greps;
	if(hud_greps)
	{
		hud_greps->previous = grep;
	}

	hud_greps = grep;

	HUD_Editor_PositionGrep(hud_element, grep);

	return grep;
}

//
// Destroys a grephandle (called if it's no longer offscreen).
//
static void HUD_Editor_DestroyGrep(hud_grephandle_t *grep)
{
	// Already destroyed.
	if(!grep)
	{
		return;
	}

	// Relink any neighbours in the list.
	if(grep->next && grep->previous)
	{
		grep->previous->next = grep->next;
		grep->next->previous = grep->previous;
	}
	else if(grep->next)
	{
		grep->next->previous = NULL;
		hud_greps = grep->next;
	}
	else if(grep->previous)
	{
		grep->previous->next = NULL;
	}
	else
	{
		hud_greps = NULL;
	}

	memset(grep, 0, sizeof(*grep));

	Q_free(grep);
}

//
// Finds a HUD element associated with the grephandle under the mouse cursor.
//
static hud_t *HUD_Editor_FindHudByGrep()
{
	hud_grephandle_t *greps_it = NULL;
	greps_it = hud_greps;

	while(greps_it)
	{
		if (greps_it->hud
			&& hud_mouse_x >= greps_it->x
			&& hud_mouse_x <= (greps_it->x + greps_it->width)
			&& hud_mouse_y >= greps_it->y
			&& hud_mouse_y <= (greps_it->y + greps_it->height))
		{
			greps_it->highlighted = true;
			return greps_it->hud;
		}

		greps_it->highlighted = false;

		greps_it = greps_it->next;
	}

	return NULL;
}

//
// Draws the outline of the visible HUD elements.
//
static void HUD_Editor_DrawOutlines(void)
{
	hud_t *temp_hud	= hud_huds;

	if(!temp_hud || !hud_editor_showoutlines)
	{
		return;
	}

	while(temp_hud->next)
	{
		// Check if the item is visible.
		if (!temp_hud->show->value || (temp_hud->place_hud && !temp_hud->place_hud->show->value))
		{
			temp_hud = temp_hud->next;
			continue;
		}

		// Draw an outline for all hud elements (faint).
		Draw_AlphaRectangleRGB(temp_hud->lx, temp_hud->ly, temp_hud->lw, temp_hud->lh, 1, false, RGBA_TO_COLOR(0, 255, 0, 25));

		temp_hud = temp_hud->next;
	}
}

//
// Finds if there's any HUD under the cursor and draws outlines for all HUD elements.
//
static qbool HUD_Editor_FindHudUnderCursor(hud_t **hud)
{
	hud_grephandle_t	*grep		= NULL;
	hud_greppos_t		pos			= pos_visible;
	hud_t				*temp_hud	= hud_huds;
	qbool				found		= false;

	if(!temp_hud)
	{
		return false;
	}

	// Check if we already had something selected since last time and was moving.
	if(selected_hud && (hud_editor_mode == hud_editmode_move_resize || hud_editor_mode == hud_editmode_move_lockedaxis))
	{
		found = true;
		(*hud) = selected_hud;
	}

	// If the hover list is being showed, only look for HUDs in that
	// not the HUD's that are below the cursor.
	if(hud_editor_mode == hud_editmode_hoverlist && hud_hoverlist_count > 0)
	{
		(*hud) = HUD_Editor_FindHoverListSelection(hud_hoverlist);
		return (hud != NULL);
	}

	// Reset the hoverlist since we might be hovering something new.
	hud_hoverlist_count = 0;
	hud_hoverlist = NULL;

	while(temp_hud)
	{
		// Not visible.
		if (!temp_hud->show->value || (temp_hud->place_hud && !temp_hud->place_hud->show->value))
		{
			temp_hud = temp_hud->next;
			continue;
		}

		// We found one.
		if (hud_mouse_x >= temp_hud->lx
			&& hud_mouse_x <= (temp_hud->lx + temp_hud->lw)
			&& hud_mouse_y >= temp_hud->ly
			&& hud_mouse_y <= (temp_hud->ly + temp_hud->lh))
		{
			// If we're moving/resizing something only continue checking for
			// more HUD elements we have the mouse over if we haven't already
			// found one. If we don't do this when you drag a HUD element
			// over another HUD element that has a greater Z-order the selection
			// will jump to that HUD element, and you'll start moving that instead.
			//
			// Vice versa if we would skip any HUD item after we've found one
			// when not already moving an item, it would mean that we could only
			// select HUD elements that are topmost in the Z-order, so an item
			// placed within another item would not be selectable.
			if(((hud_editor_mode == hud_editmode_move_resize || hud_editor_mode == hud_editmode_move_lockedaxis) && !found)
				|| (hud_editor_mode != hud_editmode_move_resize && hud_editor_mode != hud_editmode_move_lockedaxis))
			{
				found = true;
				(*hud) = temp_hud;

				// Add this HUD to the list of HUDs under the cursor.
				HUD_Editor_AddHoverHud(HUD_Editor_CreateHoverHud(temp_hud));
			}
		}

		// Check if the hud element is offscreen and
		// if there's any grep handle for this hud element
		{
			pos = HUD_Editor_GetHudGrepPosition(temp_hud);
			grep = HUD_Editor_FindGrep(temp_hud);

			if(pos != pos_visible)
			{
				// We didn't find any grep handle so create one.
				if(!grep)
				{
					grep = HUD_Editor_CreateGrep(temp_hud);
				}

				// Position the grep if we got one.
				if(grep)
				{
					HUD_Editor_PositionGrep(temp_hud, grep);
				}
			}
			else
			{
				// The HUD element is visbile, so no need for a grep handle for it.
				HUD_Editor_DestroyGrep(grep);
			}
		}

		temp_hud = temp_hud->next;
	}

	// We didn't find any HUD's under the cursor, but
	// what about "grep handles" (for offscreen HUDs).
	if(!found)
	{
		temp_hud = HUD_Editor_FindHudByGrep();

		if(temp_hud)
		{
			found = true;
			(*hud) = temp_hud;
		}
	}

	// Nothing found, make sure result is NULL.
	if (!found)
	{
		(*hud) = NULL;
	}

	return found;
}

//
// Returns the point a HUD is aligned to on it's parent in screen coordinates.
//
static void HUD_Editor_GetAlignmentPoint(hud_t *hud, int *x, int *y)
{
	extern float scr_con_current; // Console height. console.c
	int parent_x = 0;
	int parent_y = 0;
	int parent_w = 0;
	int parent_h = 0;
	hud_alignmode_t alignmode = HUD_Editor_GetAlignmentFromString(va("%s %s", hud->align_x->string, hud->align_y->string));

	if(hud->place_hud)
	{
		// Placed at another HUD.
		parent_x = hud->place_hud->lx;
		parent_y = hud->place_hud->ly;
		parent_w = hud->place_hud->lw;
		parent_h = hud->place_hud->lh;

		(*x) = HUD_CENTER_X(hud->place_hud);
		(*y) = HUD_CENTER_Y(hud->place_hud);
	}
	else
	{
		// Placed at "screen".
		parent_x = 0;
		parent_y = 0;
		parent_w = vid.width;
		parent_h = vid.height;

		(*x) = vid.width / 2;
		(*y) = vid.height / 2;
	}

	switch(alignmode)
	{
		default:
		case hud_align_center:
			// Already set.
			break;
		case hud_align_right:
			(*x) = parent_x + parent_w;
			(*y) = parent_y + (parent_h / 2);
			break;
		case hud_align_topright:
			(*x) = parent_x + parent_w;
			(*y) = parent_y;
			break;
		case hud_align_top:
			(*x) = parent_x + (parent_w / 2);
			(*y) = parent_y;
			break;
		case hud_align_topleft:
			(*x) = parent_x;
			(*y) = parent_y;
			break;
		case hud_align_left:
			(*x) = parent_x;
			(*y) = parent_y + (parent_h / 2);
			break;
		case hud_align_bottomleft:
			(*x) = parent_x;
			(*y) = parent_y + parent_h;
			break;
		case hud_align_bottom:
			(*x) = parent_x + (parent_w / 2);
			(*y) = parent_y + parent_h;
			break;
		case hud_align_bottomright:
			(*x) = parent_x + parent_w;
			(*y) = parent_y + parent_h;
			break;
		case hud_align_consoleleft:
			(*x) = parent_x;
			(*y) = scr_con_current;
			break;
		case hud_align_console:
			(*x) = parent_x + (parent_w / 2);
			(*y) = scr_con_current;
			break;
		case hud_align_consoleright:
			(*x) = parent_x + parent_w;
			(*y) = scr_con_current;
			break;
	}
}

//
// Draws a green line to each corner of a HUD element from a specified point.
//
/*
static void HUD_Editor_DrawLinesToEachCorner(hud_t *hud, int x, int y)
{
	Draw_AlphaLineRGB(hud->lx, hud->ly,						x, y, 1, RGBA_TO_COLOR(0, 255, 0, 25));
	Draw_AlphaLineRGB(hud->lx, hud->ly + hud->lh,			x, y, 1, RGBA_TO_COLOR(0, 255, 0, 25));
	Draw_AlphaLineRGB(hud->lx + hud->lw, hud->ly,			x, y, 1, RGBA_TO_COLOR(0, 255, 0, 25));
	Draw_AlphaLineRGB(hud->lx + hud->lw, hud->ly + hud->lh, x, y, 1, RGBA_TO_COLOR(0, 255, 0, 25));
}
*/

//
// Draws connections to/from a HUD element.
//
static void HUD_Editor_DrawConnections(hud_t *hud_hover)
{
	hud_t *child = NULL;
	int align_x = 0.0;
	int align_y = 0.0;

	if (!hud_hover || !hud_editor_showoutlines)
	{
		return;
	}

	// Get the alignment point at the parent in screen coordinates.
	HUD_Editor_GetAlignmentPoint(hud_hover, &align_x, &align_y);

	// Draw a line to the parent of the HUD we're hovering.
	Draw_AlphaLineRGB(HUD_CENTER_X(hud_hover), HUD_CENTER_Y(hud_hover), align_x, align_y, 1, RGBA_TO_COLOR(0, 255, 0, 25));

	// Draw a line to all children of the HUD we're hovering.
	while((child = HUD_Editor_FindNextChild(hud_hover)))
	{
		// Don't bother with hidden children.
		if(!child->show->value)
		{
			continue;
		}

		// Draw a line from the alignment point on the parent to the child.
		HUD_Editor_GetAlignmentPoint(child, &align_x, &align_y);
		Draw_AlphaLineRGB(align_x, align_y, HUD_CENTER_X(child), HUD_CENTER_Y(child), 1, RGBA_TO_COLOR(0, 255, 0, 25));
	}
}

//
// Evaluates the current mouse/keyboard state and sets the appropriate mode.
//
static void HUD_Editor_EvaluateState(hud_t *hud_hover)
{
	// Mouse 1			= Move + Resize
	// Mouse 2			= Toggle menu
	// Ctrl  + Mouse 1	= Place
	// Alt	 + Mouse 1	= Align
	// Shift + Mouse 1	= Lock moving to one axis (If you start dragging along x-axis, it will stick to that)

	if (MOUSEDOWN)
	{
		// Turn of help on mouse click.
		hud_editor_showhelp = false;
	}

	if (hud_editor_mode == hud_editmode_hoverlist)
	{
		if (!MOUSEDOWN)
		{
			// Stay in hoverlist mode until the user clicks something.
			return;
		}
	}

	if (hud_hover && MOUSEDOWN_1_ONLY && keydown[K_SHIFT])
	{
		// Move (Locked to an axis).
		HUD_Editor_SetMode(hud_editmode_move_lockedaxis);
	}
	else if ((hud_hover || hud_editor_prevmode == hud_editmode_place) && MOUSEDOWN_1_ONLY && keydown[K_CTRL])
	{
		// Place.
		HUD_Editor_SetMode(hud_editmode_place);
	}
	else if ((hud_hover || hud_editor_prevmode == hud_editmode_align) && MOUSEDOWN_1_ONLY && keydown[K_ALT])
	{
		// Align.
		HUD_Editor_SetMode(hud_editmode_align);
	}
	else if ((hud_hover || selected_hud) && MOUSEDOWN_1_ONLY)
	{
		// Move + resize.
		HUD_Editor_SetMode(hud_editmode_move_resize);
	}
	else if (hud_hoverlist_count > 1 && MOUSEDOWN_2_ONLY)
	{
		// Hover list for when hovering more than one HUD.
		HUD_Editor_SetMode(hud_editmode_hoverlist);
	}
	else if (hud_hover && MOUSEDOWN_2_ONLY)
	{
		// HUD element menu for the HUD element we have the mouse over.
		HUD_Editor_SetMode(hud_editmode_hudmenu);
	}
	else if (MOUSEDOWN_2_ONLY)
	{
		// Main menu for adding HUDs if we right click non-occupied space.
		HUD_Editor_SetMode(hud_editmode_menu);
	}
	else
	{
		// Nothing special happening.
		HUD_Editor_SetMode(hud_editmode_normal);
	}
}

//
// Draws the tooltips for a HUD element based on the state we're in.
//
static void HUD_Editor_DrawTooltips(hud_t *hud_hover)
{
	char *message = NULL;
	byte color[4] = {0, 0, 0, 0};

	if(!hud_hover)
	{
		return;
	}

	if (selected_hud)
	{
		switch(hud_editor_mode)
		{
			case hud_editmode_move_lockedaxis :
			case hud_editmode_move_resize :
			{
				message = va("(%d, %d) moving %s", (int)selected_hud->pos_x->value, (int)selected_hud->pos_y->value, selected_hud->name);
				color[0] = 255;
				color[3] = 125;
				break;
			}
			case hud_editmode_align :
			{
				char *align = NULL;

				align = HUD_Editor_GetAlignmentString(hud_alignmode);

				message = va("align %s to %s", selected_hud->name, align);
				color[1] = 255;
				color[2] = 255;
				color[3] = 125;

				break;
			}
			case hud_editmode_place :
			{
				message = va("placing %s", selected_hud->name);
				color[0] = 255;
				color[3] = 125;
				break;
			}
			case hud_editmode_normal :
			{
				message = hud_hover->name;
				color[2] = 255;
				color[3] = 125;
				break;
			}
			// unhandled
			case hud_editmode_off:
			case hud_editmode_resize:
			case hud_editmode_hudmenu:
			case hud_editmode_menu:
			case hud_editmode_hoverlist:
				break;
		}
	}

	if(!message)
	{
		message = hud_hover->name;
		color[2] = 255;
		color[3] = 125;
	}

	HUD_Editor_DrawTooltip(hud_mouse_x, hud_mouse_y, message, RGBA_TO_COLOR(color[0], color[1], color[2], color[3]));
}

//
// Draws a help window.
//
static void HUD_Editor_DrawHelp()
{
	#define HUD_EDITOR_HELP_BORDER	32
	#define HUD_EDITOR_HELP_WIDTH	min(vid.conwidth - (2 * HUD_EDITOR_HELP_BORDER), 500)
	#define HUD_EDITOR_HELP_HEIGHT	(vid.conheight - (2 * HUD_EDITOR_HELP_BORDER))
	#define HUD_EDITOR_HELP_X		((vid.conwidth - HUD_EDITOR_HELP_WIDTH) / 2)
	#define HUD_EDITOR_HELP_Y		HUD_EDITOR_HELP_BORDER
	#define HUD_EDITOR_HELP_TITLE	"&cfd0HUD EDITOR HELP"

	Draw_TextBox(HUD_EDITOR_HELP_X, HUD_EDITOR_HELP_Y, HUD_EDITOR_HELP_WIDTH / 8, HUD_EDITOR_HELP_HEIGHT / 8);

	Draw_ColoredString(
		HUD_EDITOR_HELP_X + ((HUD_EDITOR_HELP_WIDTH - strlen(HUD_EDITOR_HELP_TITLE) * 8) / 2),
		HUD_EDITOR_HELP_Y + 10,
		HUD_EDITOR_HELP_TITLE, 1);

	UI_PrintTextBlock(
		HUD_EDITOR_HELP_X + 10,
		HUD_EDITOR_HELP_Y + 30,
		HUD_EDITOR_HELP_WIDTH,
		HUD_EDITOR_HELP_HEIGHT - 30,
		"The HUD Editor helps you to customize your Heads Up Display. "
		"When you move the cursor over a HUD element it will be "
		"highlighted and it's name will be shown. When hovering a HUD "
		"you can perform the following actions:\n"
		"\n"
		"&cfd0MOVE&r relative to the HUD elements parent/alignment by "
		"holding down &c0dfMOUSE 1&r and &c0dfdragging&r.\n"
		"(Lock movement to a specific axis by holding down &c0dfSHIFT&r).\n"
		"\n"
		"&cfd0RESIZE&r the HUD element by clicking on one of the "
		"&c0dfresize handles&r that appears when hovering and item and "
		"&c0dfdragging&r. (Not all HUD elements are resizeable/scaleable).\n"
		"\n"
		"&cfd0PLACE&r the HUD element at another HUD element or a location "
		"such as the screen/console by holding down &c0dfCTRL&r when "
		"dragging. The target that the element will be placed in "
		"will turn green if you can place it there, red otherwise.\n"
		"\n"
		"&cfd0ALIGN&r the HUD element in different ways to it's parent by "
		"holding down &c0dfALT&r when dragging. Doing this will show "
		"yellow highlights at the position you're about to align to.\n"
		"\n"
		"  &cfd0Keyboard shortcuts:\n"
		"  &c0dfP&r      Toggle HUD planmode on/off (default on).\n"
		"  &c0dfH&r      Toggle this help.\n"
		"  &c0dfF1&r     Toggle if moving should be allowed.\n"
		"  &c0dfF2&r     Toggle resizing.\n"
		"  &c0dfF3&r     Toggle aligning.\n"
		"  &c0dfF4&r     Toggle placing.\n"
		"  &c0dfSPACE&r  Toggle outlines/guidelines.\n",
		0);
}

int Test_OnGotFocus(ez_control_t *self, void *payload, void *ext_event_info)
{
	EZ_control_SetBackgroundColor(self, self->background_color[0], self->background_color[1], self->background_color[2], 200);
	return 0;
}

int Test_OnLostFocus(ez_control_t *self, void *payload, void *ext_event_info)
{
	EZ_control_SetBackgroundColor(self, self->background_color[0], self->background_color[1], self->background_color[2], 100);
	return 0;
}

ez_control_t *root = NULL;
ez_control_t *child1 = NULL;
ez_control_t *child2 = NULL;
ez_button_t *button = NULL;
ez_label_t *label = NULL;
ez_label_t *label2 = NULL;
ez_slider_t *slider = NULL;
ez_scrollbar_t *scrollbar = NULL;
ez_scrollpane_t *scrollpane = NULL;
ez_listview_t *listview = NULL;
ez_window_t *window = NULL;

int Test_OnButtonDraw(ez_control_t *self, void *payload, void *ext_event_info)
{
	int x, y;
	EZ_control_GetDrawingPosition(self, &x, &y);

	return 0;
}

int Test_OnSliderPositionChanged(ez_control_t *self, void *payload, void *ext_event_info)
{
	ez_slider_t *slider = (ez_slider_t *)self;

	int slider_pos = EZ_slider_GetPosition(slider);

	EZ_label_SetText(label2, va("%i", slider_pos));

	return 0;
}

int Test_OnControlDraw(ez_control_t *self, void *payload, void *ext_event_info)
{
	int x, y; //, i;
	EZ_control_GetDrawingPosition(self, &x, &y);

	/*for (i = 0; i < 30; i++)
	{
		Draw_String(x, y + i*8, va("%d%d%d%d", i, i, i, i));
	}*/

	/*
	{
		char str[] = "Hello this is a sentence that's supposed to fit within a box of stuff, I hope this works...";
		char line[1024];
		int last_index = 0;
		int i = 0;

		while (Util_GetNextWordwrapString(str, line, last_index, &last_index, sizeof(str) / sizeof(char), self->width, 8))
		{
			Draw_String(x, y + i*8, line);
			i++;
		}
	}
	*/

	/*{
		clrinfo_t color;
		color.i = 0;
		color.c = RGBA_TO_COLOR(0, 255, 0, 255);
		Draw_BigString(x, y, "Hej", &color, 1, 1, 1, 0);
	}*/

	return 0;
}

static hud_t *hud_hover = NULL;

//
// Main HUD Editor function.
//
static void HUD_Editor(void)
{
	qbool found = false;

	// If we just entered hoverlist mode we want to keep the mouse coordinates
	// so we know where to draw the list until the user has picked a HUD.
	if (!hud_hoverlist_pos_is_set
		&& hud_editor_mode == hud_editmode_hoverlist
		&& hud_editor_prevmode != hud_editmode_hoverlist)
	{
		hud_hoverlist_x = cursor_x;
		hud_hoverlist_y = cursor_y;
		hud_hoverlist_pos_is_set = true;
	}
	else if (hud_editor_mode != hud_editmode_hoverlist)
	{
		hud_hoverlist_pos_is_set = false;
	}

	// Find the HUD we're moving or have the cursor over.
	found = HUD_Editor_FindHudUnderCursor(&hud_hover);

	// Draw faint outlines for all visible hud elements.
	HUD_Editor_DrawOutlines();

	// Draw the "grep handles" for offscreen HUDs.
	HUD_Editor_DrawGreps();

	// Draw a rectangle around the currently active HUD element.
	if(found && hud_hover)
	{
		Draw_AlphaRectangleRGB(hud_hover->lx, hud_hover->ly, hud_hover->lw, hud_hover->lh, 1, false, RGBA_TO_COLOR(0, 255, 0, 255));
	}

	// If we are realigning draw a green outline for the selected hud element.
	if (selected_hud)
	{
		Draw_AlphaRectangleRGB(selected_hud->lx, selected_hud->ly, selected_hud->lw, selected_hud->lh, 2, false, RGBA_TO_COLOR(0, 255, 0, 255));
	}

	// Check the mouse/keyboard states and if we're hovering above a hud or not.
	HUD_Editor_EvaluateState(hud_hover);

	// Draw the child/parent connections the hud we're hovering has.
	HUD_Editor_DrawConnections(hud_hover);

	// Draw a red line from selected hud to cursor.
	if (selected_hud)
	{
		Draw_AlphaLineRGB(hud_mouse_x, hud_mouse_y, HUD_CENTER_X(selected_hud), HUD_CENTER_Y(selected_hud), 1, RGBA_TO_COLOR(255, 0, 0, 255));
	}

	// Check if we're performing any action.
	// (Only perform one at any given time).
	(void)
	(HUD_Editor_DrawHoverList(hud_hoverlist_x, hud_hoverlist_y, hud_hoverlist)
		 || HUD_Editor_Resizing(hud_hover)
		 || HUD_Editor_Moving(hud_hover)
		 || HUD_Editor_Placing(hud_hover)
		 || HUD_Editor_Aligning(hud_hover));

	// Draw tooltips for the HUD.
	HUD_Editor_DrawTooltips(hud_hover);

	// Show the help window?
	if(hud_editor_showhelp)
	{
		HUD_Editor_DrawHelp();
	}

	EZ_tree_EventLoop(&help_control_tree);
}

//
// Toggles the HUD Editor on or off.
//
void HUD_Editor_Toggle_f(void)
{
	extern cvar_t scr_newHud;
	// static keydest_t key_dest_prev = key_game;
	static int old_hud_planmode = 0;

	if (cls.state != ca_active)
	{
		// We can't turn on the hud editor when disconnected.
		if(!hud_editor)
		{
			Com_Printf("You need to be in game to use the HUD editor.\n");
		}

		// If the hud editor managed to still be on while disconnected.
		hud_editor = false;
	}
	else if (!scr_newHud.value)
	{
		Com_Printf("You have to have scr_newHud turned on to use the HUD editor.\n");
		hud_editor = false;
	}
	else
	{
		// Toggle.
		hud_editor = !hud_editor;
		S_LocalSound("misc/basekey.wav");
	}

	if (hud_editor)
	{
		// Start HUD Editor.

		key_dest = key_hudeditor;
		HUD_Editor_SetMode(hud_editmode_normal);

		// Set planmode by default.
		old_hud_planmode = hud_planmode.value;
		Cvar_SetValue(&hud_planmode, 1.0);

		// Start showing the help plaque so the user learns the controls.
		hud_editor_showhelp = true;
	}
	else
	{
		// Exit the HUD Editor.

		key_dest = key_game;
		key_dest_beforecon = key_game;
		HUD_Editor_SetMode(hud_editmode_off);
		scr_cursor_icon = NULL;

		// Reset to the old value for HUD planmode.
		Cvar_SetValue(&hud_planmode, old_hud_planmode);
	}
}

//
// Handles mouse events sent to the HUD editor.
//
qbool HUD_Editor_MouseEvent(mouse_state_t *ms)
{
	// Updating cursor location.
	if(hud_editor_mode == hud_editmode_move_lockedaxis)
	{
		// Don't update the HUD Editor cursor if the axis is locked
		// so that we avoid explicit checks for that.
		// The cursor will still move around the screen properly.
		if(hud_editor_locked_axis_is_x)
		{
			hud_mouse_x = ms->x;

			// Draw a line that indicates that the movement is locked to the X-axis.
			if (selected_hud)
			{
				Draw_AlphaLineRGB(0, HUD_CENTER_Y(selected_hud), vid.width, HUD_CENTER_Y(selected_hud), 1, RGBA_TO_COLOR(255, 0, 0, 75));
			}
		}
		else
		{
			hud_mouse_y = ms->y;

			if (selected_hud)
			{
				Draw_AlphaLineRGB(HUD_CENTER_X(selected_hud), 0, HUD_CENTER_X(selected_hud), vid.height, 1, RGBA_TO_COLOR(255, 0, 0, 75));
			}
		}
	}
	else
	{
		// Normal operation, always update cursor.
		hud_mouse_x = ms->x;
		hud_mouse_y = ms->y;
	}

	return EZ_tree_MouseEvent(&help_control_tree, ms);
}

//
// Handles key events sent to the HUD editor.
//
void HUD_Editor_Key(int key, int unichar, qbool down)
{
	static int planmode = 1;
	int togglekeys[2];

	EZ_tree_KeyEvent(&help_control_tree, key, unichar, down);

	M_FindKeysForCommand("toggleconsole", togglekeys);
	if ((key == togglekeys[0]) || (key == togglekeys[1]))
	{
		Con_ToggleConsole_f();
		return;
	}

	if (down)
	{
		switch (key)
		{
			case K_ESCAPE:
				HUD_Editor_Toggle_f();
				break;
			case 'p' :
				// Toggle HUD plan mode.
				planmode = !planmode;
				Cvar_SetValue(&hud_planmode, planmode);
				break;
			case 'h' :
				// Toggle the help window.
				hud_editor_showhelp = !hud_editor_showhelp;
				break;
			case K_SPACE :
				// Toggle hud element outlines.
				hud_editor_showoutlines = !hud_editor_showoutlines;
				break;
			case K_F1 :
				// Toggle moving.
				Cvar_SetValue(&hud_editor_allowmove, !hud_editor_allowmove.value);
				break;
			case K_F2 :
				// Toggle resizing.
				Cvar_SetValue(&hud_editor_allowresize, !hud_editor_allowresize.value);
				break;
			case K_F3 :
				// Toggle aligning.
				Cvar_SetValue(&hud_editor_allowalign, !hud_editor_allowalign.value);
				break;
			case K_F4 :
				// Toggle placing.
				Cvar_SetValue(&hud_editor_allowplace, !hud_editor_allowplace.value);
				break;
			case K_UPARROW :
				// TODO : Add "nudging" in hud editor.
				break;
			case K_DOWNARROW :
				// TODO : Add "nudging" in hud editor.
				break;
			case K_LEFTARROW :
				// TODO : Add "nudging" in hud editor.
				break;
			case K_RIGHTARROW :
				// TODO : Add "nudging" in hud editor.
				break;
		}
	}
}

//
// Inits HUD Editor.
//
void HUD_Editor_Init(void)
{
	extern mpic_t *SCR_LoadCursorImage(char *cursorimage);

#if 0
	clrinfo_t color;

	color.c = RGBA_TO_COLOR(255, 255, 255, 255);
	color.i = 0;

	// Root
	{
		root = EZ_control_Create(&help_control_tree, NULL, "Test window", "Test", 50, 50, 400, 400, control_focusable | control_movable | control_resize_h | control_resize_v);
		EZ_control_SetBackgroundColor(root, 0, 0, 0, 100);
		EZ_control_SetSize(root, 400, 400);
	}

	// Child 1
	{
		child1 = EZ_control_Create(&help_control_tree, root, "Child 1", "Test", 10, 10, 50, 50, control_focusable | control_movable | control_contained | control_scrollable | control_resizeable);

		EZ_control_AddOnGotFocus(child1, Test_OnGotFocus, NULL);
		EZ_control_AddOnLostFocus(child1, Test_OnLostFocus, NULL);
		EZ_control_AddOnDraw(child1, Test_OnControlDraw, NULL);

		EZ_control_SetMinVirtualSize(child1, child1->width * 3, child1->height * 3);
		//EZ_control_SetVirtualSize(child1, child1->width * 4, child1->height * 2);

		EZ_control_SetBackgroundColor(child1, 150, 150, 0, 100);
	}

	// Child 2
	{
		child2 = EZ_control_Create(&help_control_tree, root, "Child 2", "Test", 30, 50, 50, 20, control_focusable | control_contained);

		EZ_control_AddOnGotFocus(child2, Test_OnGotFocus, NULL);
		EZ_control_AddOnLostFocus(child2, Test_OnLostFocus, NULL);

		EZ_control_SetBackgroundColor(child2, 150, 150, 200, 100);
	}

	// Button.
	{
		button = EZ_button_Create(&help_control_tree, child1, "button", "A crazy button!", 15, -15, 80, 60, control_contained | control_resizeable);
		EZ_control_AddOnDraw((ez_control_t *)button, Test_OnButtonDraw, NULL);

		EZ_button_SetFocusedColor(button, 255, 0, 0, 255);
		EZ_button_SetNormalColor(button, 255, 255, 0, 100);
		EZ_button_SetPressedColor(button, 255, 255, 0, 255);
		EZ_button_SetHoverColor(button, 255, 0, 0, 150);

		EZ_button_SetToggleable(button, true);

		EZ_button_SetText(button, "Button");
		EZ_button_SetTextAlignment(button, middle_center);

		EZ_control_SetAnchor((ez_control_t *)button, (anchor_left | anchor_right | anchor_bottom));
	}

	// Label.
	{
		label = EZ_label_Create(&help_control_tree, root,
			"label", "A crazy label!", 200, 200, 250, 80,
			control_focusable | control_contained | control_resizeable | control_scrollable /*| control_movable */ | control_resize_h | control_resize_v,
			label_wraptext | label_autosize,
			"Hello\nthis is a test are you fine because I am bla bla bla this is a very long string and it's plenty of fun haha!");

		EZ_label_SetTextScale(label, 2.0);
		//EZ_label_SetTextFlags(label, LABEL_READONLY);

		EZ_control_SetBackgroundColor((ez_control_t *)label, 150, 150, 0, 50);
		//EZ_control_SetAnchor((ez_control_t *)label, anchor_top | anchor_right | anchor_bottom);
	}

	// Label 2.
	{
		label2 = EZ_label_Create(&help_control_tree, root,
			"label2", "A crazy label!", 100, 50, 32, 16,
			control_focusable | control_contained | control_resizeable,
			0, "");
	}

	// Slider.
	{
		slider = EZ_slider_Create(&help_control_tree, root,
			"slider", "Slider omg", 50, 100, 150, 8, control_focusable | control_contained | control_resizeable);

		EZ_control_SetAnchor((ez_control_t *)slider, anchor_left | anchor_right);

		EZ_slider_SetMax(slider, 100);
		EZ_slider_SetMin(slider, 50);
		EZ_slider_SetPosition(slider, 5);
		EZ_slider_SetScale(slider, 1.0);

		EZ_slider_AddOnSliderPositionChanged(slider, Test_OnSliderPositionChanged, NULL);
	}

	/*
	// Scrollbar.
	{
		ez_control_t *label_ctrl = (ez_control_t *)label;

		scrollbar = EZ_scrollbar_Create(&help_control_tree, root, "Scrollbar", "",
			30, 150, 10, 150, control_anchor_viewport);

		EZ_scrollbar_SetTargetParent(scrollbar, false);
		//EZ_control_SetContained((ez_control_t *)scrollbar, false);
		EZ_control_SetAnchor((ez_control_t *)scrollbar, anchor_right | anchor_top | anchor_bottom);
		EZ_control_SetMovable((ez_control_t *)scrollbar, false);
	}
	*/

	// Listview
	{
		listview = EZ_listview_Create(&help_control_tree, root, "Listview", "", 50, 50, 200, 200,
			control_resize_h | control_resize_v | control_resizeable);

		EZ_listview_SetHeaderText(listview, 0, "Hej");
		EZ_listview_SetHeaderText(listview, 1, "Hej 2");

		EZ_listview_SetColumnWidth(listview, 0, 80);
		EZ_listview_SetColumnWidth(listview, 1, 50);
	}

	// Scrollpane
	{
		scrollpane = EZ_scrollpane_Create(&help_control_tree, root, "Scrollpane", "", -10, -20, 150, 150,
			control_resize_h | control_resize_v | control_resizeable);

		EZ_control_SetBackgroundColor((ez_control_t *)scrollpane, 255, 0, 0, 100);

		//EZ_scrollpane_SetTarget(scrollpane, child1);
		EZ_scrollpane_SetTarget(scrollpane, (ez_control_t *)listview);
	}

	// Window.
	{
		window = EZ_window_Create(&help_control_tree, root, "Window", NULL, 20, 20, 150, 150,
			control_movable | control_focusable | control_resize_h | control_resize_v | control_contained);

		EZ_control_SetBackgroundColor((ez_control_t *)window, 0, 100, 0, 100);

		EZ_window_SetWindowAreaMinVirtualSize(window, 200, 200);

		//EZ_window_AddChild(window, (ez_control_t *)scrollpane);
	}

	/*
	// Test.
	{
		ez_control_t *c = EZ_control_Create(&help_control_tree, root, "C test 1", "Test", 10, 10, 150, 150,
			control_resize_h | control_focusable | control_movable | control_contained | control_scrollable | control_resizeable);

		ez_control_t *c2 = EZ_control_Create(&help_control_tree, c, "C test 1", "Test", 0, 10, 30, 10,
			control_focusable | control_movable | control_contained | control_scrollable | control_resizeable);

		EZ_control_SetAnchor(c2, anchor_top | anchor_right);
		EZ_control_SetBackgroundColor(c2, 150, 0, 20, 100);

		EZ_control_SetBackgroundColor(c, 50, 40, 50, 100);
	}
	*/

	EZ_tree_Refresh(&help_control_tree);

#endif

	// Register commands.
	Cmd_AddCommand("hud_editor", HUD_Editor_Toggle_f);

	// Register variables.
	Cvar_SetCurrentGroup(CVAR_GROUP_HUD);
	Cvar_Register(&hud_editor_allowresize);
	Cvar_Register(&hud_editor_allowmove);
	Cvar_Register(&hud_editor_allowplace);
	Cvar_Register(&hud_editor_allowalign);
    Cvar_ResetCurrentGroup();

	// Load HUD editor cursor icons.
	hud_editor_move_icon = SCR_LoadCursorImage("gfx/hud_move_icon");
	hud_editor_resize_icon = SCR_LoadCursorImage("gfx/hud_resize_icon");
	hud_editor_align_icon = SCR_LoadCursorImage("gfx/hud_align_icon");
	hud_editor_place_icon = SCR_LoadCursorImage("gfx/hud_place_icon");

	hud_editor = false;
	HUD_Editor_SetMode(hud_editmode_off);
}

//
// Draws the HUD Editor if it's on.
//
void HUD_Editor_Draw(void)
{
	if (!hud_editor)
		return;

	HUD_Editor();
}

//
// Should this HUD element be fully drawn or not when in align mode
// when using the HUD editor?
//
qbool HUD_Editor_ConfirmDraw(hud_t *hud)
{
	if(hud_editor_mode == hud_editmode_align || hud_editor_mode == hud_editmode_place)
	{
		// If this is the selected hud, or the parent of the selected hud then draw it.
		if((selected_hud && !strcmp(selected_hud->name, hud->name))
			|| (selected_hud && hud->place_hud && !strcmp(selected_hud->name, hud->place_hud->name)))
		{
			return true;
		}

		return false;
	}

	return true;
}

