#include "StdAfx.h"
#include "skinnablewindow.h"
#include <commctrl.h>

// Tooltip routines
static HWND hwndTip = NULL;
static void InitToolTip()
{
	if(hwndTip) return;

	INITCOMMONCONTROLSEX iccex; 
 
    /* INITIALIZE COMMON CONTROLS */
    iccex.dwICC = ICC_WIN95_CLASSES;
    iccex.dwSize = sizeof(INITCOMMONCONTROLSEX);
    InitCommonControlsEx(&iccex);

	hwndTip = CreateWindowEx(NULL, TOOLTIPS_CLASS, NULL,
								WS_POPUP | TTS_NOPREFIX | TTS_ALWAYSTIP | TTS_BALLOON,
								CW_USEDEFAULT, CW_USEDEFAULT,
								CW_USEDEFAULT, CW_USEDEFAULT,
								NULL, NULL, env.hInstance,
								NULL);

	SetWindowPos(hwndTip, HWND_TOPMOST,0, 0, 0, 0,
				SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
	SafeSendMessage(hwndTip, TTM_SETMAXTIPWIDTH, 0, 300);
	SafeSendMessage(hwndTip,TTM_ACTIVATE,0,0);
}

static void AddToolTip(HWND h, const TCHAR * text)
{
	TOOLINFO ti;

	InitToolTip();
    /* INITIALIZE MEMBERS OF THE TOOLINFO STRUCTURE */
    ti.cbSize = sizeof(TOOLINFO);
    ti.uFlags = TTF_SUBCLASS|TTF_IDISHWND;
    ti.hwnd = h;
    ti.hinst = env.hInstance;
    ti.uId = (UINT_PTR)h;
    ti.lpszText = (LPWSTR)text;
        // ToolTip control will cover the whole window
	GetClientRect(h,&ti.rect);
    
    /* SEND AN ADDTOOL MESSAGE TO THE TOOLTIP CONTROL WINDOW */
    SafeSendMessage(hwndTip, TTM_ADDTOOL, 0, (LPARAM) (LPTOOLINFO) &ti);	
}

// help balloons
void SkinnableWindow::EnableHelpBalloons()
{
	if(hwndTip)
		SafeSendMessage(hwndTip,TTM_ACTIVATE,1,0);
}
void SkinnableWindow::DisableHelpBalloons()
{
	if(hwndTip)
		SafeSendMessage(hwndTip,TTM_ACTIVATE,0,0);
}

// Custom Event Notification Handlers
std::list<SkinnableWindow *> SkinnableWindow::NotificationRecipients;
int SkinnableWindow::AllNotificationsSuspended = 0;

void SkinnableWindow::PostNotify(int id, Notification& n)
{
	if(AllNotificationsSuspended)
		return;
	std::list<SkinnableWindow *>::iterator i;
	for(i=NotificationRecipients.begin();i!=NotificationRecipients.end();i++){
		SkinnableWindow * wnd = *i;
		if(IsWindow(wnd->hWnd) && wnd->WantsNotify(id)){
			// create a copy for each recipient, they will be responsible for deleting it
			Notification * p = new Notification(n);
			PostMessage(wnd->hWnd,ID_CUSTOMNOTIFY,(WPARAM)id,(LPARAM)p);
		}
	}
}

void SkinnableWindow::SendNotify(int id, Notification& n)
{
	if(AllNotificationsSuspended)
		return;
	std::list<SkinnableWindow *>::iterator i;
	for(i=NotificationRecipients.begin();i!=NotificationRecipients.end();i++){
		SkinnableWindow * wnd = *i;
		if(IsWindow(wnd->hWnd) && wnd->WantsNotify(id)){
			// create a copy for each recipient, they will be responsible for deleting it
			Notification * p = new Notification(n);
			SafeSendMessage(wnd->hWnd,ID_CUSTOMNOTIFY,(WPARAM)id,(LPARAM)p);
		}
	}
}

int SkinnableWindow::WantsNotify(int id)
{
	std::list<int>::iterator i;
	for(i=NotificationIds.begin();i!=NotificationIds.end();i++){
		if(id==*i)
			return 1;
	}
	return 0;
}

void SkinnableWindow::AddCustomNotification(int id)
{
	{ // first find if the notification id exists already
		std::list<int>::iterator i;
		for(i=NotificationIds.begin();i!=NotificationIds.end();i++){
			if(id==*i)
				return;
		}
		// doesn't exist so add it
		NotificationIds.push_back(id);
	}
	{ // now determine if this window is already a recipient
		std::list<SkinnableWindow *>::iterator i;
		for(i=NotificationRecipients.begin();i!=NotificationRecipients.end();i++){
			if(this==*i)
				return;
		}
		// it isn't, so add it
		NotificationRecipients.push_back(this);
	}
}

void SkinnableWindow::RemoveCustomNotification(int id)
{
	NotificationIds.remove(id);
	// if no more notifies than remove this window from the recipient list
	if(NotificationIds.size()==0){ 
		NotificationRecipients.remove(this);
	}
}

void SkinnableWindow::RemoveAllCustomNotifications()
{
	NotificationIds.clear();
	NotificationRecipients.remove(this);
}

void SkinnableWindow::SuspendAllCustomNotifications()
{
	AllNotificationsSuspended = 1;
}

void SkinnableWindow::ResumeAllCustomNotifications()
{
	AllNotificationsSuspended = 0;
}

void SkinnableWindow::OnCustomNotify(int id, Notification& n)
{

}

SkinnableTypes GetType(HWND h){
	SkinnableWindow * wnd = (SkinnableWindow *)GetWindowLongPtr(h,GWLP_USERDATA);
	if(wnd){
		return wnd->type;
	} else
		return UnknownType;
}
//
//  FUNCTION: SkinnableWindowProc(HWND, unsigned, WORD, LONG)
//
//  PURPOSE:  Processes messages for the skinnable window.
//
//  WM_COMMAND	- process the application menu
//  WM_PAINT	- Paint the main window
//  WM_DESTROY	- post a quit message and return
//
//
LRESULT CALLBACK SkinnableWindow::WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	int wmId, wmEvent;
	PAINTSTRUCT ps;
	HDC hdc;

	SkinnableWindow * wnd = (SkinnableWindow*)GetWindowLongPtr(hWnd,GWLP_USERDATA);

	if(!wnd) return DefWindowProc(hWnd, message, wParam, lParam);

	switch (message) 
	{
	case WM_SETCURSOR:
		if(wnd->OnSetCursor())
			break;
		else
			return DefWindowProc(hWnd, message, wParam, lParam);
	case WM_MOUSEMOVE:
		env.mouse.hWnd = hWnd;
		env.mouse.Client.x = (int)(short)LOWORD(lParam);
		env.mouse.Client.y = (int)(short)HIWORD(lParam);
		env.mouse.Screen = env.mouse.Client;
		ClientToScreen(hWnd,&env.mouse.Screen);
		if(wnd->MouseOutside){
			// request notification for a WM_MOUSELEAVE event
			TRACKMOUSEEVENT x;
			x.cbSize = sizeof(TRACKMOUSEEVENT);
			x.dwFlags = TME_LEAVE;
			x.dwHoverTime = 0;
			x.hwndTrack = hWnd;
			TrackMouseEvent(&x);
			wnd->MouseOutside = 0;
			wnd->OnMouseEnter();
		}
		wnd->OnMouseMove();
		break;
	case WM_MOUSELEAVE:
		wnd->MouseOutside = 1;
		wnd->OnMouseLeave();
		env.mouse.hWnd = NULL;
		break;
	case WM_CAPTURECHANGED:
		wnd->OnMouseCaptureChanged();
		break;
	case WM_LBUTTONDOWN:
		env.mouse.LeftButton = 1;
		env.mouse.Client.x = (int)(short)LOWORD(lParam);
		env.mouse.Client.y = (int)(short)HIWORD(lParam);
		env.mouse.Screen = env.mouse.Client;
		ClientToScreen(hWnd,&env.mouse.Screen);
		env.mouse.DragOrigin = env.mouse.Screen; 
		SetCapture(hWnd);
		wnd->OnLButtonDown();
		break;
	case WM_LBUTTONUP:
		env.mouse.LeftButton = 0;
		wnd->OnLButtonUp();
		ReleaseCapture();
		break;
	case WM_RBUTTONDOWN:
		env.mouse.RightButton = 1;
		env.mouse.Client.x = (int)(short)LOWORD(lParam);
		env.mouse.Client.y = (int)(short)HIWORD(lParam);
		env.mouse.Screen = env.mouse.Client;
		ClientToScreen(hWnd,&env.mouse.Screen);
		env.mouse.DragOrigin = env.mouse.Screen; 
		SetCapture(hWnd);
		wnd->OnRButtonDown();
		break;
	case WM_RBUTTONUP:
		env.mouse.RightButton = 0;
		wnd->OnRButtonUp();
		ReleaseCapture();
		break;
	case WM_LBUTTONDBLCLK:
		wnd->OnLButtonDblClk();
		break;
	case WM_ACTIVATE:
		if(LOWORD(wParam)==WA_ACTIVE || LOWORD(wParam)==WA_CLICKACTIVE){
			//SkinnableWindow * wnd2 = (SkinnableWindow*)GetWindowLongPtr((HWND)lParam,GWLP_USERDATA);
			//if(wnd2)
			//	wnd2->OnLoseFocus();
			wnd->OnGainFocus();
		}else if(LOWORD(wParam)==WA_INACTIVE){
			//SkinnableWindow * wnd2 = (SkinnableWindow*)GetWindowLongPtr((HWND)lParam,GWLP_USERDATA);
			//if(wnd2)
			//	wnd2->OnGainFocus();
			wnd->OnLoseFocus();
		}
		break;
	case WM_SIZE:
		wnd->OnSize((int)(short)LOWORD(lParam),(int)(short)HIWORD(lParam));
		break;
	case WM_MOVE:
		wnd->OnMove((int)(short)LOWORD(lParam),(int)(short)HIWORD(lParam));
		break;
	case WM_PAINT:
		hdc = BeginPaint(hWnd, &ps);
		wnd->OnPaint(hWnd,hdc);
		EndPaint(hWnd, &ps);
		break;
    case WM_MEASUREITEM: 
		return wnd->OnMeasureItem((LPMEASUREITEMSTRUCT) lParam);
    case WM_DRAWITEM: 
		return wnd->OnDrawItem((LPDRAWITEMSTRUCT) lParam);
	case WM_CHAR:
		wnd->OnChar(wParam,lParam);
		break;
	case WM_KEYDOWN:
		wnd->OnKeyDown(wParam,lParam);
		break;
	//case WM_KILLFOCUS:
	case ID_LBSELECT:
		wnd->OnListSelect((HWND)wParam,(HWND)lParam);
		break;	
	case ID_LBSELECT_RIGHT:
		wnd->OnListRightSelect((HWND)wParam,(HWND)lParam);
		break;
	case ID_LBSELECT_DBLCLK:
		wnd->OnListDblSelect((HWND)wParam,(HWND)lParam);
		break;
	case WM_TIMER:
		wnd->OnTimer();
		break;
	case ID_POPUPSTART:
		wnd->OnPopupStart(wParam,lParam);
		break;
	case ID_POPUPEND:
		wnd->OnPopupEnd(wParam,lParam);
		break;
	case ID_CONTENTSCHANGED:
		wnd->OnContentsChanged(wParam, lParam);
		break;
	case ID_LIST_DELETE:
		wnd->OnListDeleteItem(wParam,lParam);
		break;
	case WM_MOUSEWHEEL:
		/*
		* Don't handle zoom and datazoom.
		*/
		if (wParam & (MK_SHIFT | MK_CONTROL)) {
			return DefWindowProc(hWnd, message, wParam, lParam);
		}
		wnd->OnMouseWheel((float)(short)HIWORD(wParam)/(float)WHEEL_DELTA);
		break;
	case ID_CARRIAGERETURN:
		wnd->OnCarriageReturn((HWND)wParam);
		break;
	case ID_CUSTOMNOTIFY:
		{
			if(AllNotificationsSuspended)
				break;
			int id = (int)wParam;
			Notification * p = (Notification *)lParam;
			wnd->OnCustomNotify(id,*p);
			delete p;
		}
		break;
	case ID_GENERIC:
		wnd->OnGeneric(wParam,lParam);
		break;
	case WM_NOTIFY:
		wnd->OnNotify((int) wParam, (LPNMHDR) lParam);
		break;
	case WM_COMMAND:
		wmId    = LOWORD(wParam); 
		wmEvent = HIWORD(wParam); 
		switch (wmEvent) {
			case ID_BUTTONMOVE:
				wnd->OnButtonMove((HWND)lParam);
				return 0;
			case ID_SCROLLEVENT:
				wnd->OnScroll((HWND)lParam);
				return 0;
		case 0: // Parse the menu notification:
			return DefWindowProc(hWnd, message, wParam, lParam);
		case 1: // Parse the accelerator notification
			break;
		case ID_LEFTBUTTON: // parse the button notifications
			wnd->OnButton((HWND)lParam);
			return 0;
		case ID_RIGHTBUTTON: // parse the button notifications
			wnd->OnRightButton((HWND)lParam);
			return 0;
		case ID_DOUBLEBUTTON:
			wnd->OnDblButton((HWND)lParam);
			return 0;
		case ID_CARRIAGERETURN: // parse the carriage return (in a textbox)
			wnd->OnCarriageReturn((HWND)lParam);
			return 0;
		case ID_MINIMIZEBUTTON:
			ShowWindow(wnd->hWnd,SW_MINIMIZE);
			return 0;
		default:
			return DefWindowProc(hWnd, message, wParam, lParam);
		}
		break;
	case WM_CLOSE:
		wnd->OnClose();
		break;
	case WM_DESTROY:
		wnd->OnDestroy();
		break;
	default:
		return DefWindowProc(hWnd, message, wParam, lParam);
	}

	// already handled the message so return
	return 0;
}

SkinnableWindow::SkinnableWindow(void)
{
	type = WindowType;
	MouseOutside = 1;
	title = _T("BridgeClient");
	pressable = 1;
	moveable = 1;
	hidden = 0;
	hWnd = NULL;
	hTabTo = NULL;
	hTabFrom = NULL;
	ClipChildren = 1;
	ShowInTaskBar = 1;
	hContextMenu = NULL;
}

SkinnableWindow::~SkinnableWindow(void)
{
	OnDestroy();
}

void SkinnableWindow::OnDestroy()
{
	if(IsWindow(hWnd)){
		SetWindowLongPtr(hWnd,GWLP_USERDATA,NULL);
		DestroyWindow(hWnd);
	}
	RemoveAllCustomNotifications();
}

void SkinnableWindow::SetTabTo(HWND h)
{
	hTabTo = h;
}

void SkinnableWindow::SetTabFrom(HWND h)
{
	hTabFrom = h;
}

HWND SkinnableWindow::GetTabToTarget()
{
	return hWnd;
}

HWND SkinnableWindow::GetTabFromTarget()
{
	return hWnd;
}

void SkinnableWindow::SetState(AppearanceState s)
{
	Attributes * last = current;
	state = s;
	switch(s){
		case AppearanceState::selected:
			current = &selected;
			break;
		case AppearanceState::mouseover:
			current = &mouseover;
			break;
		case AppearanceState::normal:
			current = &normal;
			break;
		case AppearanceState::pressed:
			if(pressable)
				current = &pressed;
			break;
		default:
			current= &normal;
	}
	
	if(last!=current && hWnd){
		InvalidateRect(hWnd,NULL,TRUE);
		UpdateWindow(hWnd);
	}
}

void SkinnableWindow::OnPaint(HWND hWnd, HDC hdc)
{
	Graphics graphics(hdc);
	Draw(graphics);
}

int SkinnableWindow::Create(HWND parent)
{
	hWndParent = parent;
	int style,exstyle=0;
	if(ClipChildren)
		style= WS_POPUP|WS_CLIPCHILDREN;
	else
		style = WS_POPUP;
	if(!ShowInTaskBar)
		exstyle = WS_EX_TOOLWINDOW;
	if(parent==NULL){
		style = style|WS_SYSMENU;
		hWnd = CreateWindowEx(exstyle,_T("SkinnableWindow"), title.data(), style,
			location.X, location.Y, size.Width, size.Height, hWndParent, NULL, env.hInstance, NULL);
	}else
		hWnd = CreateWindowEx(exstyle,_T("SkinnableWindow"), title.data(), WS_CHILD|WS_CLIPSIBLINGS,
			location.X, location.Y, size.Width, size.Height, hWndParent, NULL, env.hInstance, NULL);

	// set to the background opacity
	SetOpacity(current->opacity);

	if (!hWnd)
	{
		PrintLastError("SkinnableWindow.Create");
		return 0;
	}

	// save the ptr to this class in the window structure
	SetWindowLongPtr(hWnd,GWLP_USERDATA,(LONG_PTR)this);

	// add a tooltip based on the description text
	if(description.length()>0)
		AddToolTip(hWnd,description.data());

	if(hidden)
		ShowWindow(hWnd,SW_HIDE);
	else
		ShowWindow(hWnd, SW_SHOWNOACTIVATE);
	UpdateWindow(hWnd);
	return 1;
}

// 0 =  0% (transparent) 100 = 100%(opaque)
void SkinnableWindow::SetOpacity(float x)
{
	if(hWndParent==NULL && hWnd){
		if(x==100){
			SetWindowLong(hWnd,GWL_EXSTYLE,0);
		} else {
			SetWindowLong(hWnd,GWL_EXSTYLE,WS_EX_LAYERED);
			SetLayeredWindowAttributes(hWnd, 0, (255 * x) / 100, LWA_ALPHA);
		}
	}
}

void SkinnableWindow::OnLoseFocus()
{
	if(!Selected())
		SetState(AppearanceState::normal);
}

void SkinnableWindow::OnGainFocus()
{

}

void SkinnableWindow::OnKeyDown(WPARAM wParam, LPARAM lParam)
{
	print(L"WM_KEYDOWN: %x, %x\n",wParam,lParam);
	switch (wParam) 
	{ 
	case VK_LEFT: 

		// Process the LEFT ARROW key. 

		break; 

	case VK_RIGHT: 

		// Process the RIGHT ARROW key. 

		break; 

	case VK_UP: 

		// Process the UP ARROW key. 

		break; 

	case VK_DOWN: 

		// Process the DOWN ARROW key. 

		break; 

	case VK_HOME: 

		// Process the HOME key. 

		break; 

	case VK_END: 

		// Process the END key. 

		break; 

	case VK_INSERT: 

		// Process the INS key. 

		break; 

	case VK_DELETE: 

		// Process the DEL key. 

		break; 

	case VK_F2: 
		// Process the F2 key. 
		if(env.console.hidden){
			env.console.Show();
		} else {
			env.console.Hide();
		}
		break; 


		// Process other non-character keystrokes. 

	default: 
		break; 
	}
}

int SkinnableWindow::OnChar(WPARAM wParam, LPARAM lParam)
{
	print(L"WM_CHAR: %x, %x\n",wParam,lParam);
	switch (wParam) 
	{ 
	case 17: // ctrl+q
		print(L"Quiting...");
		PostQuitMessage(0);
		DoEvents();
		break;
	case 0x08: 

		// Process a backspace. 

		break; 

	case 0x0A: 

		// Process a linefeed. 

		break; 

	case 0x1B: 

		// Process an escape. 

		break; 

	case 0x09: 

		// Process a tab. 
		if(GetKeyState(VK_SHIFT)){
			if(hTabFrom)
				SetFocus(hTabFrom);
		} else {
			if(hTabTo)
				SetFocus(hTabTo);
		}


		break; 

	case 0x0D: 

		// Process a carriage return. 
		print(L"Process a carriage return. \n");
		OnCarriageReturn((HWND)lParam);
		break; 

	default: 

		// Process displayable characters. 

		break; 
	} 
	return 0;
}

int SkinnableWindow::OnSetCursor()
{
	// return 1 if the cursor is changed (or else the system will change it back)
	// otherwise return 0
	return 0;
}

void SkinnableWindow::OnMove(int x, int y)
{
	location.X = x;
	location.Y = y;
}

void SkinnableWindow::OnSize(int cx, int cy)
{
	size.Width = cx;
	size.Height = cy;
}

void SkinnableWindow::OnMouseEnter()
{

}

void SkinnableWindow::OnMouseMove()
{
	// Moving the window
	if(hWndParent==NULL && env.mouse.LeftButton){
		if(moveable)
			SetLocation(env.mouse.InitialPos.x+(env.mouse.Screen.x-env.mouse.DragOrigin.x),env.mouse.InitialPos.y+(env.mouse.Screen.y-env.mouse.DragOrigin.y));
	} else  {
		if(Selected()){
			
		} else {
			SetState(AppearanceState::mouseover);
		}
	}

	
}

void SkinnableWindow::OnButtonMove(HWND h)
{

}

void SkinnableWindow::OnMouseLeave()
{
	if(!Selected())
		SetState(AppearanceState::normal);
}

void SkinnableWindow::OnLButtonDown()
{
	SetFocus(hWnd);
	env.mouse.InitialPos.x = location.X;
	env.mouse.InitialPos.y = location.Y;
	SetState(AppearanceState::pressed);
}

void SkinnableWindow::OnLButtonUp()
{
	if(Selected())
		SetState(AppearanceState::selected);
	else
		SetState(AppearanceState::mouseover);
}

void SkinnableWindow::OnRButtonDown()
{
	SetFocus(hWnd);
	env.mouse.InitialPos.x = location.X;
	env.mouse.InitialPos.y = location.Y;
	SetState(AppearanceState::pressed);
}

void SkinnableWindow::OnRButtonUp()
{
	if(Selected())
		SetState(AppearanceState::selected);
	else
		SetState(AppearanceState::mouseover);
}

void SkinnableWindow::OnLButtonDblClk()
{

}

void SkinnableWindow::OnButton(HWND h)
{
}

void SkinnableWindow::OnRightButton(HWND h)
{
}

void SkinnableWindow::OnDblButton(HWND h)
{
}

void SkinnableWindow::OnCarriageReturn(HWND h)
{
}

int SkinnableWindow::OnMeasureItem(MEASUREITEMSTRUCT * x)
{
	return 0;
}

int SkinnableWindow::OnDrawItem(DRAWITEMSTRUCT * x)
{
	return 0;
}

void SkinnableWindow::SetSize(int width, int height)
{
	size = Size(width,height);
	if(hWnd)
		SetWindowPos(hWnd,NULL,0,0,width,height,SWP_NOZORDER|SWP_NOMOVE|SWP_NOACTIVATE);
}

void SkinnableWindow::SetLocation(int x, int y)
{
	if(x==location.X && y==location.Y) return;

	location = Point(x,y);

	if(hWnd){
		SetWindowPos(hWnd,NULL,x,y,0,0,SWP_NOZORDER|SWP_NOSIZE|SWP_NOACTIVATE);
		//MoveWindow(hWnd,x,y,size.Width,size.Height,FALSE);
	}

}

void SkinnableWindow::Redraw(void)
{
	if(hWnd){
		InvalidateRgn(hWnd,NULL,FALSE);
		UpdateWindow(hWnd);
	}
}

void SkinnableWindow::OnListSelect(HWND h, HWND hItem)
{
	// an item inside the list was selected with a left click
	// used mainly by listboxes
}

void SkinnableWindow::OnListRightSelect(HWND h, HWND hItem)
{
	// an item inside the list was selected with a right click
	// used mainly by listboxes
}

void SkinnableWindow::OnListDblSelect(HWND h, HWND hItem)
{
	// an item inside the list was double-clicked
	// used mainly by listboxes
}

void SkinnableWindow::Draw(Graphics& graphics)
{
	try {
		SetOpacity(current->opacity);

		// Create a Rect object.
		Rect fillRect(Point(0,0),size);
		
		if(current->bgcolor.GetAlpha()>0) {
			// Create a SolidBrush object.
			SolidBrush bgbrush(current->bgcolor);

			// Fill the rectangle.
			graphics.FillRectangle(&bgbrush, fillRect);
		} 

		if(current->image){
			DrawTransparentImage(&graphics,
				current->image,
				current->imagex,
				current->imagey,
				current->image->GetWidth(),
				current->image->GetHeight());
		}

		if(current->borderwidth>0){
			// Create a Pen object.
			Pen borderpen(current->bordercolor, current->borderwidth);

			// Draw the rectangle that bounds the image.
			fillRect.Width--;
			fillRect.Height--;
			graphics.DrawRectangle(&borderpen, fillRect);
		}

		if(current->drawtext && text.size()>0){
			// draw the text
			FontFamily   fontFamily(fontname.data());
			Font         font(&fontFamily, fontsize, FontStyleRegular, UnitPoint);
			SolidBrush brush(current->textcolor);
			PointF		origin(location.X+textlocation.X,
								location.Y+textlocation.Y);
			StringFormat stringFormat;
			// left-justify each line of text.
			stringFormat.SetAlignment(StringAlignmentNear);
			stringFormat.SetLineAlignment(StringAlignmentNear);
			graphics.DrawString(text.data(), -1, &font, origin, &stringFormat, &brush);
		}

		std::list<Appearance *>::iterator i;
		for(i=visuals.begin();i!=visuals.end();i++){
			(*i)->Draw(graphics);
		}

	} catch(...) {
		print(L"Error drawing SkinnableWindow.\n");
		return;
	}
}

void SkinnableWindow::Show()
{
	if(hWnd){
		ShowWindow(hWnd,SW_SHOWNOACTIVATE);
	}
	hidden = 0;
	MouseOutside = 1;
}

void SkinnableWindow::Hide()
{
	if(hWnd)
		ShowWindow(hWnd,SW_HIDE);
	hidden = 1;
	MouseOutside = 1;
}

void SkinnableWindow::OnClose()
{

}

void SkinnableWindow::OnScroll(HWND h)
{

}

void SkinnableWindow::OnTimer()
{

}

void SkinnableWindow::OnPopupStart(WPARAM wParam, LPARAM lParam)
{

}

void SkinnableWindow::OnPopupEnd(WPARAM wParam, LPARAM lParam)
{

}

void SkinnableWindow::OnMouseWheel(float n)
{

}

void SkinnableWindow::OnContentsChanged(WPARAM wParam, LPARAM lParam)
{

}

void SkinnableWindow::OnListDeleteItem(WPARAM wParam, LPARAM lParam)
{

}

void SkinnableWindow::OnNotify(int idCtrl, NMHDR * pnmh)
{

}

bool SkinnableWindow::Selected()
{
	return (GetFocus()==hWnd);
}

void SkinnableWindow::OnGeneric(WPARAM wParam, LPARAM lParam)
{

}

void SkinnableWindow::Enable()
{
	if(hWnd)
		EnableWindow(hWnd,TRUE);
}

void SkinnableWindow::Disable()
{
	if(hWnd)
		EnableWindow(hWnd,FALSE);
}

void SkinnableWindow::OnMouseCaptureChanged()
{

}



LRESULT SkinnableWindow::DisplayContextMenu() 
{ 
	if(hContextMenu){
		POINT pt;
		if(!GetCursorPos(&pt)){
			print(L"Unable to read cursor position.\n");
		}
	 
		SetForegroundWindow(hWnd);
		LRESULT x = TrackPopupMenu(hContextMenu, 
				TPM_RETURNCMD | TPM_BOTTOMALIGN | TPM_RIGHTALIGN | TPM_RIGHTBUTTON | TPM_LEFTBUTTON, 
				pt.x, pt.y, 0, hWnd, NULL); 
		PostMessage(hWnd,WM_NULL,0,0);
		return x;
	} else {
		return -1;
	}
} 

int SkinnableWindow::LoadContextMenu(UINT id)
{
	// setup context menu
	if ((hContextMenu = LoadMenu(env.hInstance, MAKEINTRESOURCE(id))) == NULL) {
		print(L"SkinnableWindow:: unable to load shortcut menu\n");
        return 0; 
	}
	hContextMenu = GetSubMenu(hContextMenu,0);
	return 1;
}