/*
  Copyright (C) 2001 Kimmo Pekkola

  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, write to the Free Software
  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
*/
/*
  $Header: /home/cvsroot/Rainmeter/Library/MeterWindow.cpp,v 1.1.1.1 2005/07/10 18:51:06 rainy Exp $

  $Log: MeterWindow.cpp,v $
  Revision 1.1.1.1  2005/07/10 18:51:06  rainy
  no message

  Revision 1.26  2004/08/13 15:47:45  rainy
  Fixed z-position changing.
  Mouse move events work for non-movable windows too.

  Revision 1.25  2004/07/11 17:19:29  rainy
  Changed messageboxes to logs.
  Mouse leave checks transparent pixels.
  Added !RainmeterToggleConfig.

  Revision 1.24  2004/06/05 10:55:54  rainy
  Too much changes to be listed in here...

  Revision 1.23  2004/03/13 16:19:17  rainy
  Added support for multiple configs

  Revision 1.22  2003/12/05 15:50:10  Rainy
  Multi-instance changes.

  Revision 1.21  2003/02/10 18:11:33  rainy
  Too much changes to mention :-)

  Revision 1.20  2002/12/23 14:25:21  rainy
  Separated skin and other settings.
  Stats are now written in ini file.

  Revision 1.19  2002/07/01 15:27:36  rainy
  AlwaysOnTop gets now three values.
  Added Toggle().
  Added RainmeterCurrentConfig and RainmeterCurrentConfigIni.
  The ini file's timestamp is checked before it is overwritten.
  AboutBox is now in separate source file.
  Added SnapEdges.

  Revision 1.18  2002/05/05 10:48:56  rainy
  Fixed few bugs.

  Revision 1.17  2002/05/04 08:16:35  rainy
  There can be any number of ini files in the config folders.
  WM_COPYDATA can be used to deliver the bangs.
  Added support for per meter actions.

  Revision 1.16  2002/04/27 10:27:42  rainy
  Added hide/show to meters and enable/disable to measures

  Revision 1.15  2002/04/01 15:36:10  rainy
  Added PLAY, PLAYSTOP and PLAYLOOP build-in commands.

  Revision 1.14  2002/03/31 09:58:53  rainy
  Added some comments

  Revision 1.13  2002/03/29 16:32:58  rainy
  Fixed a typo

  Revision 1.12  2002/01/16 16:06:29  rainy
  The file doesn't need to be named Rainmeter.ini anymore.
  If the old config doesn't exist, we'll yse the first one instead.

  Revision 1.11  2001/12/23 10:14:09  rainy
  Added support for different configs.
  The position of the window is now remembered.

  Revision 1.10  2001/10/28 10:15:21  rainy
  Added left and right mouse up actions.
  IsNT() can now identify different OS more precisely.

  Revision 1.9  2001/10/14 07:27:58  rainy
  Changed the errorhandling.
  Stats now include the date when they were started to collect.

  Revision 1.8  2001/09/26 16:25:44  rainy
  Added support for statistics.
  Meters and Measures are now stored here.

  Revision 1.7  2001/09/01 12:57:13  rainy
  Added support for Bar and Bitmap meters.

  Revision 1.6  2001/08/25 18:08:34  rainy
  Added mousebutton actions.
  The About dialog has now the build date.

  Revision 1.5  2001/08/25 17:15:52  rainy
  Added context menu, which can show the about dialog, refresh the configuration or quit the program.
  The ini-file can be defined in the step.rc also.

  Revision 1.4  2001/08/19 09:43:29  rainy
  Mouse over hid the window even if it was not wanted.

  Revision 1.3  2001/08/19 09:12:12  rainy
  Added support for GetRevID.
  Added StartHidden.

  Revision 1.2  2001/08/12 15:36:55  Rainy
  The ini-file's exsistance is checked.
  Added String meter.
  Improved mouse over hiding.

  Revision 1.1.1.1  2001/08/11 10:58:19  Rainy
  Added to CVS.

*/

#pragma warning(disable: 4786)
#pragma warning(disable: 4996)

#include <windows.h>
#include <gdiplus.h>
#include "MeterWindow.h"
#include "Rainmeter.h"
#include "Error.h"
#include <math.h>
#include <time.h>
#include "Meter.h"
#include "Measure.h"
#include "AboutDialog.h"
#include "resource.h"
#include "Litestep.h"
#include <Mmsystem.h>
#include <assert.h>
#include <tchar.h>
#include "MeasureCalc.h"
#include "MeasureNet.h"
#include "MeterButton.h"

using namespace Gdiplus;

#define ULW_ALPHA               0x00000002
#define WS_EX_LAYERED           0x00080000

#define METERTIMER 1
#define MOUSETIMER 2
#define FADETIMER 3

#define SNAPDISTANCE 10

int CMeterWindow::m_InstanceCount = 0;

extern CRainmeter* Rainmeter;

/* 
** CMeterWindow
**
** Constructor
**
*/
CMeterWindow::CMeterWindow(std::wstring& config, std::wstring& iniFile)
{
	m_Background = NULL;
	m_Window = NULL;
	m_ChildWindow = false;

	m_DoubleBuffer = NULL;

	m_WindowX = 0;
	m_WindowY = 0;
	m_WindowW = 0;
	m_WindowH = 0;
	m_WindowZPosition = ZPOSITION_NORMAL;
	m_WindowDraggable = true;
	m_WindowUpdate = 1000;
	m_WindowHide = HIDEMODE_NONE;
	m_WindowStartHidden = false;
	m_SnapEdges = true;
	m_Rainmeter = NULL;
	m_ResetRegion = false;
	m_Refreshing = false;
	m_NativeTransparency = true;
	m_MeasuresToVariables = false;
	m_AllowNegativeCoordinates = true;
	m_SavePosition = false;			// Must be false
	m_AlphaValue = 255;
	m_FadeDuration = 250;
	m_ClickThrough = false;
	m_DynamicWindowSize = false;
	m_KeepOnScreen = true;

	m_BackgroundSize.cx = 0;
	m_BackgroundSize.cy = 0;

	m_FadeStartTime = 0;
	m_FadeStartValue = 0;
	m_FadeEndValue = 0;
	m_TransparencyValue = 0;

	m_MouseOver = false;

	m_BackgroundMode = BGMODE_IMAGE;
	m_SolidBevel = BEVELTYPE_NONE;

	m_SkinName = config;
	m_SkinIniFile = iniFile;

	m_User32Library = LoadLibrary(L"user32.dll");

	m_UpdateCounter = 0;

	m_InstanceCount++;
}

/* 
** ~CMeterWindow
**
** Destructor
**
*/
CMeterWindow::~CMeterWindow()
{
	WriteConfig();

	// Destroy the meters
	std::list<CMeter*>::iterator j = m_Meters.begin();
	for( ; j != m_Meters.end(); j++)
	{
		delete (*j);
	}

	// Destroy the measures
	std::list<CMeasure*>::iterator i = m_Measures.begin();
	for( ; i != m_Measures.end(); i++)
	{
		delete (*i);
	}

	if(m_Background) delete m_Background;
	if(m_DoubleBuffer) delete (m_DoubleBuffer);

	if(m_Window) DestroyWindow(m_Window);

	FreeLibrary(m_User32Library);

	m_InstanceCount--;

	if (m_InstanceCount == 0)
	{
		BOOL Result;
		int counter = 0;
		do {
			// Wait for the window to die
			Result = UnregisterClass(L"RainmeterMeterWindow", m_Rainmeter->GetInstance());
			Sleep(100);
			counter += 1;
		} while(!Result && counter < 10);
	}
}

/* 
** Initialize
**
** Initializes the window, creates the class and the window.
**
*/
int CMeterWindow::Initialize(CRainmeter& Rainmeter)
{
	WNDCLASSEX wc;

	m_Rainmeter = &Rainmeter;

	// Register the windowclass
	memset(&wc, 0, sizeof(WNDCLASSEX));
	wc.style = CS_NOCLOSE;
	wc.cbSize = sizeof(WNDCLASSEX);
	wc.hCursor = LoadCursor(NULL, IDC_ARROW);
	wc.lpfnWndProc = WndProc;
	wc.hInstance = m_Rainmeter->GetInstance();
	wc.lpszClassName = L"RainmeterMeterWindow";
	
	if(!RegisterClassEx(&wc))
	{
		DWORD err = GetLastError();

		if (err != 0 && ERROR_CLASS_ALREADY_EXISTS != err)
		{
			throw CError(CError::ERROR_REGISTER_WINDOWCLASS, __LINE__, __FILE__);
		}
	}

	m_Window = CreateWindowEx(WS_EX_TOOLWINDOW,
							L"RainmeterMeterWindow", 
							NULL, 
							WS_POPUP,
							CW_USEDEFAULT,
							CW_USEDEFAULT,
							CW_USEDEFAULT,
							CW_USEDEFAULT,
							NULL,
							NULL,
							m_Rainmeter->GetInstance(),
							this);

	if(m_Window == NULL) 
	{ 
		throw CError(CError::ERROR_CREATE_WINDOW, __LINE__, __FILE__);
	}

#ifndef _WIN64
	SetWindowLong(m_Window, GWL_USERDATA, magicDWord);
#endif

	setlocale(LC_NUMERIC, "C");

	// Gotta have some kind of buffer during initialization
	m_DoubleBuffer = new Bitmap(1, 1, PixelFormat32bppARGB);

	Refresh(true);
	if (!m_WindowStartHidden) 
	{
		if (m_WindowHide == HIDEMODE_FADEOUT) 
		{
			FadeWindow(0, 255);
		}
		else
		{
			FadeWindow(0, m_AlphaValue);
		}
	}

	LSLog(LOG_DEBUG, L"Rainmeter", L"Initialization successful.");

	return 0;
}

/*
** Refresh
**
** This deletes everything and rebuilds the config again.
**
*/
void CMeterWindow::Refresh(bool init)
{
	assert(m_Rainmeter != NULL);

	m_Rainmeter->SetCurrentParser(&m_Parser);

	std::wstring dbg = L"Refreshing (Name: \"" + m_SkinName;
	dbg += L"\" Ini: \"" + m_SkinIniFile; 
	dbg += L"\")"; 
	LSLog(LOG_DEBUG, L"Rainmeter", dbg.c_str());
	
	m_Refreshing = true;

	if(!init)
	{
		// First destroy everything
		WriteConfig();

		KillTimer(m_Window, METERTIMER);	// Kill the timer
		KillTimer(m_Window, MOUSETIMER);	// Kill the timer
		KillTimer(m_Window, FADETIMER);	// Kill the timer

		std::list<CMeasure*>::iterator i = m_Measures.begin();
		for( ; i != m_Measures.end(); i++)
		{
			delete (*i);
		}
		m_Measures.clear();

		std::list<CMeter*>::iterator j = m_Meters.begin();
		for( ; j != m_Meters.end(); j++)
		{
			delete (*j);
		}
		m_Meters.clear();

		if(m_Background) delete m_Background;
		m_Background = NULL;

		m_BackgroundSize.cx = m_BackgroundSize.cy = 0;

		m_BackgroundName.erase();
	}

	ReadConfig();	// Read the general settings
	ReadSkin();

	InitializeMeters();

	// Remove transparent flag
	LONG style = GetWindowLong(m_Window, GWL_EXSTYLE);
	if ((style & WS_EX_TRANSPARENT) != 0)
	{
		SetWindowLong(m_Window, GWL_EXSTYLE, style & ~WS_EX_TRANSPARENT);
	}

	if (m_KeepOnScreen) 
	{
		MapCoordsToScreen(m_WindowX, m_WindowY, m_WindowW, m_WindowH);
	}

	SetWindowPos(m_Window, NULL, m_WindowX, m_WindowY, m_WindowW, m_WindowH, SWP_NOZORDER | SWP_NOACTIVATE);

	// Set the window region
	CreateRegion(true);	// Clear the region
	Update(false);
	CreateRegion(false);

	// Start the timers
	if(0 == SetTimer(m_Window, METERTIMER, m_WindowUpdate, NULL))
	{
		throw CError(L"Unable to create a timer!", __LINE__, __FILE__);
	}

	if(0 == SetTimer(m_Window, MOUSETIMER, 500, NULL))	// Mouse position is checked twice per sec
	{
		throw CError(L"Unable to create a timer!", __LINE__, __FILE__);
	}

	UpdateTransparency(m_AlphaValue, true);

	ChangeZPos(m_WindowZPosition);

	m_Rainmeter->SetCurrentParser(NULL);

	m_Refreshing = false;

	if (!m_OnRefreshAction.empty())
	{
		m_Rainmeter->ExecuteCommand(m_OnRefreshAction.c_str(), this);
	}
}

void CMeterWindow::MapCoordsToScreen(int& x, int& y, int w, int h)
{
	// Check that the window is inside the screen area
	HMONITOR hMonitor;
	MONITORINFO mi;

	POINT pt = {x, y};
	for (int i = 0; i < 5; i++) 
	{
		switch(i) 
		{
		case 0:
			pt.x = x + w / 2;
			pt.y = y + h / 2;
			break;

		case 1:
			pt.x = x;
			pt.y = y;
			break;

		case 2:
			pt.x = x + w;
			pt.y = y + h;
			break;

		case 3:
			pt.x = x;
			pt.y = y + h;
			break;

		case 4:
			pt.x = x + w;
			pt.y = y;
			break;
		}

		hMonitor = MonitorFromPoint(pt, MONITOR_DEFAULTTONULL);

		if(hMonitor != NULL)
		{
			mi.cbSize = sizeof(mi);
			GetMonitorInfo(hMonitor, &mi);

			x = max(x, mi.rcMonitor.left);
			x = min(x, mi.rcMonitor.right - m_WindowW);
			y = max(y, mi.rcMonitor.top);
			y = min(y, mi.rcMonitor.bottom - m_WindowH);
			return;
		}
	}

	// No monitor found for the window -> Use the default work area
	RECT workArea;
	SystemParametersInfo(SPI_GETWORKAREA, 0, &workArea, 0);	// Store the old value
	x = max(x, workArea.left);
	x = min(x, workArea.right - m_WindowW);
	y = max(y, workArea.top);
	y = min(y, workArea.bottom - m_WindowH);
}

/*
** MoveWindow
**
** Moves the window to a new place
**
*/
void CMeterWindow::MoveWindow(int x, int y)
{
	if (!m_AllowNegativeCoordinates)
	{
		RECT r;
		GetClientRect(GetDesktopWindow(), &r); 
		if(x < 0) x += r.right;
		if(y < 0) y += r.bottom;
	}

	SetWindowPos(m_Window, NULL, x, y, 0, 0, SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE);

	if (m_SavePosition)
	{
		WriteConfig();
	}
}

/*
** ChangeZPos
**
** Sets the window's z-position
**
*/
void CMeterWindow::ChangeZPos(ZPOSITION zPos)
{
	if(!m_ChildWindow)
	{
		HWND parent = GetAncestor(m_Window, GA_PARENT);
		HWND winPos = HWND_NOTOPMOST;
		m_WindowZPosition = zPos;

		switch (zPos)
		{
		case ZPOSITION_ONTOPMOST:
		case ZPOSITION_ONTOP:
			winPos = HWND_TOPMOST;
 			break;

		case ZPOSITION_ONBOTTOM:
			 winPos = HWND_BOTTOM;
			 break;

		case ZPOSITION_ONDESKTOP:
			// Set the window's parent to progman, so it stays always on desktop
			HWND ProgmanHwnd = FindWindow(L"Progman", L"Program Manager");
			if (ProgmanHwnd && (parent != ProgmanHwnd))
			{
				SetParent(m_Window, ProgmanHwnd);
			}
			else
			{
				return;		// The window is already on desktop
			}
			break;
		}

		if (zPos != ZPOSITION_ONDESKTOP && (parent != GetDesktopWindow()))
		{
			SetParent(m_Window, NULL);
		}

		bool refresh = m_Refreshing;
		m_Refreshing = true;	// Fake refreshing so that the z-position can be changed
		SetWindowPos(m_Window, winPos, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
		m_Refreshing = refresh;
	}
}

/*
** RunBang
**
** Runs the bang command
**
*/
void CMeterWindow::RunBang(BANGCOMMAND bang, const WCHAR* arg)
{
	const WCHAR* pos = NULL;
	const WCHAR* pos2 = NULL;

	if (!m_Window) return;

	switch(bang) 
	{
	case BANG_REFRESH:
		Refresh(false);
		break;

	case BANG_REDRAW:
		Redraw();
		break;

	case BANG_TOGGLEMETER:
		ToggleMeter(arg);
		break;

	case BANG_SHOWMETER:
		ShowMeter(arg);
		break;

	case BANG_HIDEMETER:
		HideMeter(arg);
		break;

	case BANG_TOGGLEMEASURE:
		ToggleMeasure(arg);
		break;

	case BANG_ENABLEMEASURE:
		EnableMeasure(arg);
		break;

	case BANG_DISABLEMEASURE:
		DisableMeasure(arg);
		break;

	case BANG_SHOW:
		ShowWindow(m_Window, SW_SHOWNOACTIVATE);
		break;

	case BANG_HIDE:
		ShowWindow(m_Window, SW_HIDE);
		break;

	case BANG_TOGGLE:
		if (IsWindowVisible(m_Window))
		{
			ShowWindow(m_Window, SW_HIDE);
		}
		else
		{
			ShowWindow(m_Window, SW_SHOWNOACTIVATE);
		}
		break;

	case BANG_MOVE:
		pos = wcschr(arg, ' ');
		if (pos != NULL)
		{
			MoveWindow(_wtoi(arg), _wtoi(pos));
		}
		else
		{
			DebugLog(L"Cannot parse parameters for !RainmeterMove");
		}
		break;

	case BANG_ZPOS:
		ChangeZPos((ZPOSITION)_wtoi(arg));
		break;

	case BANG_LSHOOK:
		{
			pos = wcsrchr(arg, ' ');
			if (pos != NULL)
			{
#ifdef _WIN64
				HWND hWnd = (HWND)_wtoi64(pos);
#else
				HWND hWnd = (HWND)_wtoi(pos);
#endif
				if (hWnd) 
				{
					// Disable native transparency
					m_NativeTransparency = false;
					UpdateTransparency(m_AlphaValue, true);
					SetWindowLong(m_Window, GWL_STYLE, (GetWindowLong(m_Window, GWL_STYLE) &~ WS_POPUP) | WS_CHILD);
					SetParent(m_Window, hWnd);
					m_ChildWindow = true;
				}
			}
			else
			{
				DebugLog(L"Cannot parse parameters for !RainmeterLsBoxHook (%s)", arg);
			}
		}
		break;

	case BANG_ABOUT:
		OpenAboutDialog(m_Window, m_Rainmeter->GetInstance());
		break;

	case BANG_MOVEMETER:
		pos = wcschr(arg, ' ');
		if (pos != NULL)
		{
			pos2 = wcschr(pos + 1, ' ');
			if (pos2 != NULL)
			{
				MoveMeter(_wtoi(arg), _wtoi(pos), pos2 + 1);
			}
			else
			{
				DebugLog(L"Cannot parse the for !RainmeterMoveMeter");
			}
		}
		else
		{
			DebugLog(L"Cannot parse the coordinates for !RainmeterMoveMeter");
		}
		break;

	case BANG_PLUGIN:
		{
			std::wstring args = arg;
			std::wstring measure = arg;
			size_t pos = std::wstring::npos;
			do 
			{
				pos = args.find('\"');
				if (pos != std::wstring::npos)
				{
					args.erase(pos, 1);
				}
				
			} while(pos != std::wstring::npos);

			pos = args.find(' ');
			if (pos != std::wstring::npos)
			{
				measure = args.substr(0, pos);
				args.erase(0, pos + 1);
			}

			if (!measure.empty())
			{
				std::list<CMeasure*>::iterator iter = m_Measures.begin();
				for( ; iter != m_Measures.end(); iter++)
				{
					if (wcsicmp((*iter)->GetName(), measure.c_str()) == 0)
					{
						(*iter)->ExecuteBang(args.c_str());
						return;
					}
				}

				DebugLog(L"Unable to find [%s] for !RainmeterPluginBang", measure.c_str());
			}
			else
			{
				DebugLog(L"Cannot parse the !RainmeterPluginBang");
			}
		}
		break;
	}
}

/*
** ShowMeter
**
** Shows the given meter
**
*/
void CMeterWindow::ShowMeter(const WCHAR* name)
{
	if (name == NULL || wcslen(name) == 0) return;

	std::list<CMeter*>::iterator j = m_Meters.begin();
	for( ; j != m_Meters.end(); j++)
	{
		if (wcscmp((*j)->GetName(), name) == 0)
		{
			(*j)->Show();
			m_ResetRegion = true;	// Need to recalculate the window region
			return;
		}
	}

	DebugLog(L"Unable to show the meter %s (there is no such thing in %s)", name, m_SkinName.c_str());
}

/*
** HideMeter
**
** Hides the given meter
**
*/
void CMeterWindow::HideMeter(const WCHAR* name)
{
	if (name == NULL || wcslen(name) == 0) return;

	std::list<CMeter*>::iterator j = m_Meters.begin();
	for( ; j != m_Meters.end(); j++)
	{
		if (wcscmp((*j)->GetName(), name) == 0)
		{
			(*j)->Hide();
			m_ResetRegion = true;	// Need to recalculate the windowregion
			return;
		}
	}

	DebugLog(L"Unable to hide the meter %s (there is no such thing in %s)", name, m_SkinName.c_str());
}

/*
** ToggleMeter
**
** Toggles the given meter
**
*/
void CMeterWindow::ToggleMeter(const WCHAR* name)
{
	if (name == NULL || wcslen(name) == 0) return;

	std::list<CMeter*>::iterator j = m_Meters.begin();
	for( ; j != m_Meters.end(); j++)
	{
		if (wcscmp((*j)->GetName(), name) == 0)
		{
			if ((*j)->IsHidden())
			{
				(*j)->Show();
			}
			else
			{
				(*j)->Hide();
			}
			m_ResetRegion = true;	// Need to recalculate the window region
			return;
		}
	}

	DebugLog(L"Unable to toggle the meter %s (there is no such thing in %s)", name, m_SkinName.c_str());
}

/*
** MoveMeter
**
** Moves the given meter
**
*/
void CMeterWindow::MoveMeter(int x, int y, const WCHAR* name)
{
	if (name == NULL || wcslen(name) == 0) return;

	std::list<CMeter*>::iterator j = m_Meters.begin();
	for( ; j != m_Meters.end(); j++)
	{
		if (wcscmp((*j)->GetName(), name) == 0)
		{
			(*j)->SetX(x);
				(*j)->SetY(y);
			m_ResetRegion = true;
			return;
		}
	}

	DebugLog(L"Unable to move the meter %s (there is no such thing in %s)", name, m_SkinName.c_str());
}
/*
** EnableMeasure
**
** Enables the given measure
**
*/
void CMeterWindow::EnableMeasure(const WCHAR* name)
{
	if (name == NULL || wcslen(name) == 0) return;

	std::list<CMeasure*>::iterator i = m_Measures.begin();
	for( ; i != m_Measures.end(); i++)
	{
		if (wcscmp((*i)->GetName(), name) == 0)
		{
			(*i)->Enable();
			return;
		}
	}

	DebugLog(L"Unable to enable the measure %s (there is no such thing in %s)", name, m_SkinName.c_str());
}


/*
** DisableMeasure
**
** Disables the given measure
**
*/
void CMeterWindow::DisableMeasure(const WCHAR* name)
{
	if (name == NULL || wcslen(name) == 0) return;

	std::list<CMeasure*>::iterator i = m_Measures.begin();
	for( ; i != m_Measures.end(); i++)
	{
		if (wcscmp((*i)->GetName(), name) == 0)
		{
			(*i)->Disable();
			return;
		}
	}

	DebugLog(L"Unable to disable the measure %s (there is no such thing in %s)", name, m_SkinName.c_str());
}


/*
** ToggleMeasure
**
** Toggless the given measure
**
*/
void CMeterWindow::ToggleMeasure(const WCHAR* name)
{
	if (name == NULL || wcslen(name) == 0) return;

	std::list<CMeasure*>::iterator i = m_Measures.begin();
	for( ; i != m_Measures.end(); i++)
	{
		if (wcscmp((*i)->GetName(), name) == 0)
		{
			if ((*i)->IsDisabled())
			{
				(*i)->Enable();
			}
			else
			{
				(*i)->Disable();
			}
			return;
		}
	}

	DebugLog(L"Unable to toggle the measure %s (there is no such thing in %s)", name, m_SkinName.c_str());
}

/*
** ReadConfig
**
** Reads the current config
**
*/
void CMeterWindow::ReadConfig()
{
	std::wstring iniFile = m_Rainmeter->GetIniFile();
	const WCHAR* section = L"Rainmeter";

	m_SavePosition = true;		// Default value

	CConfigParser parser;
	parser.Initialize(iniFile.c_str());

	for (int i = 0; i < 2; i++)
	{
		m_WindowX = parser.ReadInt(section, L"WindowX", m_WindowX);
		m_WindowY = parser.ReadInt(section, L"WindowY", m_WindowY);

		if (!m_Rainmeter->GetDummyLitestep())
		{
			// Check if step.rc has overrides these values
			m_WindowX = GetRCInt("RainmeterWindowX", m_WindowX);
			m_WindowY = GetRCInt("RainmeterWindowY", m_WindowY);
		}

		if(m_WindowX != 0 || m_WindowY != 0)
		{
			// TODO: check that pt is somewhere on screen
		}

		int zPos = parser.ReadInt(section, L"AlwaysOnTop", m_WindowZPosition);
		if (zPos == -1)
		{
			m_WindowZPosition = ZPOSITION_ONBOTTOM;
		}
		else if (zPos == -2)
		{
			m_WindowZPosition = ZPOSITION_ONDESKTOP;
		}
		else if (zPos == 1)
		{
			m_WindowZPosition = ZPOSITION_ONTOP;
		}
		else if (zPos == 2)
		{
			m_WindowZPosition = ZPOSITION_ONTOPMOST;
		}
		else
		{
			m_WindowZPosition = ZPOSITION_NORMAL;
		}

		m_WindowDraggable = 0!=parser.ReadInt(section, L"Draggable", m_WindowDraggable);
		m_WindowHide = (HIDEMODE)parser.ReadInt(section, L"HideOnMouseOver", m_WindowHide);
		m_WindowStartHidden = 0!=parser.ReadInt(section, L"StartHidden", m_WindowStartHidden);
		m_SavePosition = 0!=parser.ReadInt(section, L"SavePosition", m_SavePosition);
		m_SnapEdges = 0!=parser.ReadInt(section, L"SnapEdges", m_SnapEdges);
		m_MeasuresToVariables = 0!=parser.ReadInt(section, L"MeasuresToVariables", m_MeasuresToVariables);
		m_AllowNegativeCoordinates = 0!=parser.ReadInt(section, L"AllowNegativeCoordinates", m_AllowNegativeCoordinates);
		m_NativeTransparency = 0!=parser.ReadInt(section, L"NativeTransparency", m_NativeTransparency);
		m_ClickThrough = 0!=parser.ReadInt(section, L"ClickThrough", m_ClickThrough);
		m_KeepOnScreen = 0!=parser.ReadInt(section, L"KeepOnScreen", m_KeepOnScreen);

		m_AlphaValue = parser.ReadInt(section, L"AlphaValue", m_AlphaValue);
		m_AlphaValue = min(255, m_AlphaValue);
		m_AlphaValue = max(0, m_AlphaValue);

		m_FadeDuration = parser.ReadInt(section, L"FadeDuration", m_FadeDuration);

		// Disable native transparency if not 2K/XP
		if(CRainmeter::IsNT() == PLATFORM_9X || CRainmeter::IsNT() == PLATFORM_NT4)
		{
			m_NativeTransparency = 0;
		}

		// On the second loop override settings from the skin's section
		section = m_SkinName.c_str();
	}
}

/*
** WriteConfig
**
** Writes the new settings to the config
**
*/
void CMeterWindow::WriteConfig()
{
	WCHAR buffer[256];
	std::wstring iniFile = m_Rainmeter->GetIniFile();
	const WCHAR* section = m_SkinName.c_str();

	if(!iniFile.empty())
	{
		// If position needs to be save, do so.
		if(m_SavePosition)
		{
			int x = m_WindowX, y = m_WindowY;
			if (!m_AllowNegativeCoordinates)
			{
				x = max(0, x);
				y = max(0, y);
			}

			wsprintf(buffer, L"%i", x);
			WritePrivateProfileString(section, L"WindowX", buffer, iniFile.c_str());
			wsprintf(buffer, L"%i", y);
			WritePrivateProfileString(section, L"WindowY", buffer, iniFile.c_str());
		}

		wsprintf(buffer, L"%i", m_AlphaValue);
		WritePrivateProfileString(section, L"AlphaValue", buffer, iniFile.c_str());

		wsprintf(buffer, L"%i", m_FadeDuration);
		WritePrivateProfileString(section, L"FadeDuration", buffer, iniFile.c_str());
		
		wsprintf(buffer, L"%i", m_ClickThrough);
		WritePrivateProfileString(section, L"ClickThrough", buffer, iniFile.c_str());
		wsprintf(buffer, L"%i", m_WindowDraggable);
		WritePrivateProfileString(section, L"Draggable", buffer, iniFile.c_str());
		wsprintf(buffer, L"%i", m_WindowHide);
		WritePrivateProfileString(section, L"HideOnMouseOver", buffer, iniFile.c_str());
		wsprintf(buffer, L"%i", m_SavePosition);
		WritePrivateProfileString(section, L"SavePosition", buffer, iniFile.c_str());
		wsprintf(buffer, L"%i", m_SnapEdges);
		WritePrivateProfileString(section, L"SnapEdges", buffer, iniFile.c_str());
		wsprintf(buffer, L"%i", m_KeepOnScreen);
		WritePrivateProfileString(section, L"KeepOnScreen", buffer, iniFile.c_str());

		wsprintf(buffer, L"%i", m_WindowZPosition);
		WritePrivateProfileString(section, L"AlwaysOnTop", buffer, iniFile.c_str());
	}
}


/*
** ReadSkin
**
** Reads the skin config, creates the meters and measures and does the bindings.
**
*/
void CMeterWindow::ReadSkin()
{
	std::wstring iniFile = m_Rainmeter->GetSkinPath();
	iniFile += m_SkinName;
	iniFile += L"\\";
	iniFile += m_SkinIniFile;

	m_Parser.Initialize(iniFile.c_str());

	// Global settings

	// Check the version 
	int appVersion = m_Parser.ReadInt(L"Rainmeter", L"AppVersion", 0);
	if (appVersion > RAINMETER_VERSION)
	{
		WCHAR buffer[256];
		std::wstring text;
		wsprintf(buffer, L"%i.%i", appVersion / 1000, appVersion % 1000);
		text = L"This skin needs Rainmeter version 0.";
		text += buffer;
		text += L" or newer.\nIt might not function as well as it should.\nYou should get an updated version or Rainmeter\nfrom http://www.rainmeter.net";
		MessageBox(m_Window, text.c_str(), L"Rainmeter", MB_OK);
	}

	m_Author = m_Parser.ReadString(L"Rainmeter", L"Author", L"");
	m_BackgroundName = m_Parser.ReadString(L"Rainmeter", L"Background", L"");
	m_BackgroundName = Rainmeter->FixPath(m_BackgroundName, PATH_FOLDER_CURRENT_SKIN, m_SkinName);

	std::wstring margins = m_Parser.ReadString(L"Rainmeter", L"BackgroundMargins", L"0, 0, 0, 0");
	int left = 0, top = 0, right = 0, bottom = 0;
	swscanf(margins.c_str(), L"%i, %i, %i, %i", &left, &top, &right, &bottom);
	m_BackgroundMargins.X = left;
	m_BackgroundMargins.Width = right - left;
	m_BackgroundMargins.Y = top;
	m_BackgroundMargins.Height = bottom - top;

	margins = m_Parser.ReadString(L"Rainmeter", L"DragMargins", L"0, 0, 0, 0");
	left = 0, top = 0, right = 0, bottom = 0;
	swscanf(margins.c_str(), L"%i, %i, %i, %i", &left, &top, &right, &bottom);
	m_DragMargins.X = left;
	m_DragMargins.Width = right - left;
	m_DragMargins.Y = top;
	m_DragMargins.Height = bottom - top;

	m_BackgroundMode = (BGMODE)m_Parser.ReadInt(L"Rainmeter", L"BackgroundMode", 0);
	m_SolidBevel = (BEVELTYPE)m_Parser.ReadInt(L"Rainmeter", L"BevelType", m_SolidBevel);

	m_SolidColor = m_Parser.ReadColor(L"Rainmeter", L"SolidColor", Color::Gray);
	m_SolidColor2 = m_Parser.ReadColor(L"Rainmeter", L"SolidColor2", m_SolidColor);
	m_SolidAngle = (Gdiplus::REAL)m_Parser.ReadFloat(L"Rainmeter", L"GradientAngle", 0.0);

	m_DynamicWindowSize = 0!=m_Parser.ReadInt(L"Rainmeter", L"DynamicWindowSize", m_DynamicWindowSize);

	if ((m_BackgroundMode == BGMODE_IMAGE || m_BackgroundMode == BGMODE_SCALED_IMAGE) && m_BackgroundName.empty())
	{
		m_BackgroundMode = BGMODE_COPY;
	}

	m_RightMouseDownAction = m_Parser.ReadString(L"Rainmeter", L"RightMouseDownAction", L"");
	m_LeftMouseDownAction = m_Parser.ReadString(L"Rainmeter", L"LeftMouseDownAction", L"");
	m_RightMouseUpAction = m_Parser.ReadString(L"Rainmeter", L"RightMouseUpAction", L"");
	m_LeftMouseUpAction = m_Parser.ReadString(L"Rainmeter", L"LeftMouseUpAction", L"");
	m_MouseOverAction = m_Parser.ReadString(L"Rainmeter", L"MouseOverAction", L"");
	m_MouseLeaveAction = m_Parser.ReadString(L"Rainmeter", L"MouseLeaveAction", L"");
	m_OnRefreshAction = m_Parser.ReadString(L"Rainmeter", L"OnRefreshAction", L"");

	m_WindowUpdate = m_Parser.ReadInt(L"Rainmeter", L"Update", m_WindowUpdate);

	// Create the meters and measures

	// Get all the sections (i.e. different meters)
	WCHAR* items = new WCHAR[MAX_LINE_LENGTH];
	int size = MAX_LINE_LENGTH;

	// Get all the sections
	while(true)
	{
		int res = GetPrivateProfileString( NULL, NULL, NULL, items, size, iniFile.c_str());
		if (res == 0) return;		// File not found
		if (res != size - 2) break;		// Fits in the buffer

		delete [] items;
		size *= 2;
		items = new WCHAR[size];
	};

	WCHAR* pos = items;
	while(wcslen(pos) > 0)
	{
		if(wcsicmp(L"Rainmeter", pos) != 0 && wcsicmp(L"Variables", pos) != 0)
		{
			std::wstring meterName, measureName;

			// Check if the item is a meter or a measure (or perhaps something else)
			measureName = m_Parser.ReadString(pos, L"Measure", L"");
			meterName = m_Parser.ReadString(pos, L"Meter", L"");
			if (measureName.length() > 0)
			{
				try
				{
					// It's a measure
					CMeasure* measure = CMeasure::Create(measureName.c_str(), this);
					measure->SetName(pos);
					measure->ReadConfig(m_Parser, pos);
					m_Measures.push_back(measure);
				}
				catch (CError& error)
				{
					MessageBox(m_Window, error.GetString().c_str(), APPNAME, MB_OK | MB_TOPMOST | MB_ICONEXCLAMATION);
				}
			}
			else if (meterName.length() > 0)
			{
				try
				{
					// It's a meter
					CMeter* meter = CMeter::Create(meterName.c_str(), this);
					meter->SetName(pos);
					meter->ReadConfig(pos);
					m_Meters.push_back(meter);
				}
				catch (CError& error)
				{
					MessageBox(m_Window, error.GetString().c_str(), APPNAME, MB_OK | MB_TOPMOST | MB_ICONEXCLAMATION);
				}
			} 
			else
			{
				// It's something else
                throw CError(std::wstring(L"Section [") + pos + L"] is not a meter or a measure!", __LINE__, __FILE__);
            }
		}
		pos = pos + wcslen(pos) + 1;
	}

	delete [] items;

	if (m_Meters.empty())
	{
		MessageBox(m_Window, L"Your configuration file doesn't contain any meters!\nYour skin's ini-file might be out of date.", APPNAME, MB_OK | MB_TOPMOST | MB_ICONEXCLAMATION);
	}
	else
	{
		// Bind the meters to the measures
		std::list<CMeter*>::iterator j = m_Meters.begin();
		for( ; j != m_Meters.end(); j++)
		{
			try
			{
				(*j)->BindMeasure(m_Measures);
			}
			catch (CError& error)
			{
				MessageBox(m_Window, error.GetString().c_str(), APPNAME, MB_OK | MB_TOPMOST | MB_ICONEXCLAMATION);
			}
		}
	}
}

/*
** InitializeMeters
**
** Initializes all the meters and the background
**
*/
void CMeterWindow::InitializeMeters()
{
	// Initalize all meters
	std::list<CMeter*>::iterator j = m_Meters.begin();
	for( ; j != m_Meters.end(); j++)
	{
		try
		{
			(*j)->Initialize();
		}
		catch (CError& error)
		{
			MessageBox(m_Window, error.GetString().c_str(), APPNAME, MB_OK | MB_TOPMOST | MB_ICONEXCLAMATION);
		}
	}

	// Handle negative coordinates
	if (!m_AllowNegativeCoordinates)
	{
		RECT r;
		GetClientRect(GetDesktopWindow(), &r); 
		if(m_WindowX < 0) m_WindowX += r.right;
		if(m_WindowY < 0) m_WindowY += r.bottom;
	}

	Update(true);

	ResizeWindow(true);
}

/*
** ResizeWindow
** 
** Changes the size of the window and re-adjusts the background
*/
bool CMeterWindow::ResizeWindow(bool reset)
{
	int w = m_BackgroundMargins.GetLeft();
	int h = m_BackgroundMargins.GetTop();

	// Get the largest meter point
	std::list<CMeter*>::iterator j = m_Meters.begin();
	for( ; j != m_Meters.end(); j++)
	{
		w = max(w, (*j)->GetX() + (*j)->GetW());
		h = max(h, (*j)->GetY() + (*j)->GetH());
	}

	w += m_BackgroundMargins.GetRight();
	h += m_BackgroundMargins.GetBottom();

	w = max(w, m_BackgroundSize.cx);
	h = max(h, m_BackgroundSize.cy);

	if (!reset && m_WindowW == w && m_WindowH == h)
	{
		return false;		// The window is already correct size
	}

	// Reset size (this is calculated below)
	m_WindowW = 0;
	m_WindowH = 0;

	if (m_Background)
	{
		delete m_Background;
		m_Background = NULL;
	}

	if ((m_BackgroundMode == BGMODE_IMAGE || m_BackgroundMode == BGMODE_SCALED_IMAGE) && !m_BackgroundName.empty())
	{
		// Load the background
		m_Background = new Bitmap(m_BackgroundName.c_str());
		Status status = m_Background->GetLastStatus();
		if(Ok != status)
		{
			std::wstring err = L"Unable to load background: " + m_BackgroundName;
			MessageBox(m_Window, err.c_str(), APPNAME, MB_OK | MB_TOPMOST | MB_ICONEXCLAMATION);
			delete m_Background;
			m_Background = NULL;
			m_BackgroundSize.cx = 0;
			m_BackgroundSize.cy = 0;
		}

		// Calculate the window dimensions
		if(m_Background)
		{
			m_BackgroundSize.cx = m_Background->GetWidth();
			m_BackgroundSize.cy = m_Background->GetHeight();

			w = max(w, m_BackgroundSize.cx);
			h = max(h, m_BackgroundSize.cy);

			if (m_BackgroundMode == BGMODE_SCALED_IMAGE)
			{
				// Scale the background to fill the whole window
				Bitmap* scaledBackground = new Bitmap(w, h, PixelFormat32bppARGB);

				Graphics graphics(scaledBackground);

				if (m_BackgroundMargins.GetTop() > 0) 
				{
					if (m_BackgroundMargins.GetLeft() > 0) 
					{
						// Top-Left
						Rect r(0, 0, m_BackgroundMargins.GetLeft(), m_BackgroundMargins.GetTop());
						graphics.DrawImage(m_Background, r, 0, 0, m_BackgroundMargins.GetLeft(), m_BackgroundMargins.GetTop(), UnitPixel);
					}

					// Top
					Rect r(m_BackgroundMargins.GetLeft(), 0, w - m_BackgroundMargins.GetLeft() - m_BackgroundMargins.GetRight(), m_BackgroundMargins.GetTop());
					graphics.DrawImage(m_Background, r, m_BackgroundMargins.GetLeft(), 0, m_Background->GetWidth() - m_BackgroundMargins.GetLeft() - m_BackgroundMargins.GetRight(), m_BackgroundMargins.GetTop(), UnitPixel);

					if (m_BackgroundMargins.GetRight() > 0) 
					{
						// Top-Right
						Rect r(w - m_BackgroundMargins.GetRight(), 0, m_BackgroundMargins.GetRight(), m_BackgroundMargins.GetTop());
						graphics.DrawImage(m_Background, r, m_Background->GetWidth() - m_BackgroundMargins.GetRight(), 0, m_BackgroundMargins.GetRight(), m_BackgroundMargins.GetTop(), UnitPixel);
					}
				}

				if (m_BackgroundMargins.GetLeft() > 0) 
				{
					// Left
					Rect r(0, m_BackgroundMargins.GetTop(), m_BackgroundMargins.GetLeft(), h - m_BackgroundMargins.GetTop() - m_BackgroundMargins.GetBottom());
					graphics.DrawImage(m_Background, r, 0, m_BackgroundMargins.GetTop(), m_BackgroundMargins.GetLeft(), m_Background->GetHeight() - m_BackgroundMargins.GetTop() - m_BackgroundMargins.GetBottom(), UnitPixel);
				}

				// Center
				Rect r(m_BackgroundMargins.GetLeft(), m_BackgroundMargins.GetTop(), w - m_BackgroundMargins.GetLeft() - m_BackgroundMargins.GetRight(), h - m_BackgroundMargins.GetTop() - m_BackgroundMargins.GetBottom());
				graphics.DrawImage(m_Background, r, m_BackgroundMargins.GetLeft(), m_BackgroundMargins.GetTop(), m_Background->GetWidth() - m_BackgroundMargins.GetLeft() - m_BackgroundMargins.GetRight(), m_Background->GetHeight() - m_BackgroundMargins.GetTop() - m_BackgroundMargins.GetBottom(), UnitPixel);

				if (m_BackgroundMargins.GetRight() > 0) 
				{
					// Right
					Rect r(w - m_BackgroundMargins.GetRight(), m_BackgroundMargins.GetTop(), m_BackgroundMargins.GetRight(), h - m_BackgroundMargins.GetTop() - m_BackgroundMargins.GetBottom());
					graphics.DrawImage(m_Background, r, m_Background->GetWidth() - m_BackgroundMargins.GetRight(), m_BackgroundMargins.GetTop(), m_BackgroundMargins.GetRight(), m_Background->GetHeight() - m_BackgroundMargins.GetTop() - m_BackgroundMargins.GetBottom(), UnitPixel);
				}
				
				if (m_BackgroundMargins.GetBottom() > 0) 
				{
					if (m_BackgroundMargins.GetLeft() > 0) 
					{
						// Bottom-Left
						Rect r(0, h - m_BackgroundMargins.GetBottom(), m_BackgroundMargins.GetLeft(), m_BackgroundMargins.GetBottom());
						graphics.DrawImage(m_Background, r, 0, m_Background->GetHeight() - m_BackgroundMargins.GetBottom(), m_BackgroundMargins.GetLeft(), m_BackgroundMargins.GetBottom(), UnitPixel);
					}
					// Bottom
					Rect r(m_BackgroundMargins.GetLeft(), h - m_BackgroundMargins.GetBottom(), w - m_BackgroundMargins.GetLeft() - m_BackgroundMargins.GetRight(), m_BackgroundMargins.GetBottom());
					graphics.DrawImage(m_Background, r, m_BackgroundMargins.GetLeft(), m_Background->GetHeight() - m_BackgroundMargins.GetBottom(), m_Background->GetWidth() - m_BackgroundMargins.GetLeft() - m_BackgroundMargins.GetRight(), m_BackgroundMargins.GetBottom(), UnitPixel);

					if (m_BackgroundMargins.GetRight() > 0) 
					{
						// Bottom-Right
						Rect r(w - m_BackgroundMargins.GetRight(), h - m_BackgroundMargins.GetBottom(), m_BackgroundMargins.GetRight(), m_BackgroundMargins.GetBottom());
						graphics.DrawImage(m_Background, r, m_Background->GetWidth() - m_BackgroundMargins.GetRight(), m_Background->GetHeight() - m_BackgroundMargins.GetBottom(), m_BackgroundMargins.GetRight(), m_BackgroundMargins.GetBottom(), UnitPixel);
					}
				}

				delete m_Background;
				m_Background = scaledBackground;
			}

			// Get the size form the background bitmap
			m_WindowW = m_Background->GetWidth();
			m_WindowH = m_Background->GetHeight();

			if (!m_NativeTransparency)
			{
				// Graph the desktop and place the background on top of it
				Bitmap* desktop = GrabDesktop(m_WindowX, m_WindowY, m_WindowW, m_WindowH);
				Graphics graphics(desktop);
				Rect r(0, 0, m_WindowW, m_WindowH);
				graphics.DrawImage(m_Background, r, 0, 0, m_WindowW, m_WindowH, UnitPixel);
				delete m_Background;
				m_Background = desktop;
			}
		} 
	} 
	else
	{
		m_WindowW = w;
		m_WindowH = h;
	}

	// If Background is not set, take a copy from the desktop
	if(m_Background == NULL) 
	{
		if (m_BackgroundMode == BGMODE_COPY)
		{
			if (!m_NativeTransparency)
			{
				m_Background = GrabDesktop(m_WindowX, m_WindowY, m_WindowW, m_WindowH);
			}
		}
		else
		{
			// Create a solid color bitmap for the background
			m_Background = new Bitmap(m_WindowW, m_WindowH, PixelFormat32bppARGB);
			Graphics graphics(m_Background);

			if (m_SolidColor.GetValue() == m_SolidColor2.GetValue())
			{
				SolidBrush solid(m_SolidColor);
				graphics.FillRectangle(&solid, 0, 0, m_WindowW, m_WindowH);
			}
			else
			{
				Rect r(0, 0, m_WindowW, m_WindowH);
				LinearGradientBrush gradient(r, m_SolidColor, m_SolidColor2, m_SolidAngle, TRUE);
				graphics.FillRectangle(&gradient, r);
			}

			if (m_SolidBevel != BEVELTYPE_NONE)
			{
				Pen light(Color(255, 255, 255, 255));
				Pen dark(Color(255, 0, 0, 0));

				if (m_SolidBevel == BEVELTYPE_DOWN)
				{
					light.SetColor(Color(255, 0, 0, 0));
					dark.SetColor(Color(255, 255, 255, 255));
				}
				Rect rect(0, 0, m_WindowW, m_WindowH);	
				CMeter::DrawBevel(graphics, rect, light, dark);
			}
		}
	}

	return true;
}

/*
** GrabDesktop
** 
** Grabs a part of the desktop
*/
Bitmap* CMeterWindow::GrabDesktop(int x, int y, int w, int h)
{
	HDC desktopDC = GetDC(GetDesktopWindow());
	HDC dc = CreateCompatibleDC(desktopDC);
	HBITMAP desktopBM = CreateCompatibleBitmap(desktopDC, w, h);
	HBITMAP oldBM = (HBITMAP)SelectObject(dc, desktopBM);
	BitBlt(dc, 0, 0, w, h, desktopDC, x, y, SRCCOPY);
	SelectObject(dc, oldBM);
	DeleteDC(dc);
	ReleaseDC(GetDesktopWindow(), desktopDC);
	Bitmap* background = new Bitmap(desktopBM, NULL);
	DeleteObject(desktopBM);
	return background;
}

/*
** CreateRegion
**
** Creates/Clears a window region
**
*/
void CMeterWindow::CreateRegion(bool clear)
{
	if (clear)
	{
		SetWindowRgn(m_Window, NULL, TRUE);
	}
	else
	{
		// Set window region if needed
		if(!m_BackgroundName.empty()) 
		{
			HBITMAP background = NULL;
			m_DoubleBuffer->GetHBITMAP(Color(255,0,255), &background);
			if (background)
			{
				HRGN region = BitmapToRegion(background, RGB(255,0,255), 0x101010, 0, 0);
				SetWindowRgn(m_Window, region, TRUE);
				DeleteObject(background);
			}
		}
	}
}

/*
** Redraw
**
** Redraws the meters and paints the window
**
*/
void CMeterWindow::Redraw() 
{
	if (m_DoubleBuffer) delete m_DoubleBuffer;
	m_DoubleBuffer = new Bitmap(m_WindowW, m_WindowH, PixelFormat32bppARGB);

	if (m_ResetRegion)
	{
		ResizeWindow(false);
		CreateRegion(true);
	}

	if (m_Background)
	{
		// Copy the background over the doublebuffer
		Graphics graphics(GetDoubleBuffer());
		Rect r(0, 0, m_WindowW, m_WindowH);
		graphics.DrawImage(m_Background, r, 0, 0, m_Background->GetWidth(), m_Background->GetHeight(), UnitPixel);
	}

	// Draw the meters
	std::list<CMeter*>::iterator j = m_Meters.begin();
	for( ; j != m_Meters.end(); j++)
	{
		try
		{
			(*j)->Draw();
		}
		catch (CError& error)
		{
			MessageBox(m_Window, error.GetString().c_str(), APPNAME, MB_OK | MB_TOPMOST | MB_ICONEXCLAMATION);
		}
	}
		
	if (m_ResetRegion) CreateRegion(false);
	m_ResetRegion = false;

	UpdateTransparency(m_TransparencyValue, false);

	if (!m_NativeTransparency)
	{
		InvalidateRect(m_Window, NULL, FALSE);
	}
}


/*
** Update
**
** Updates all the measures and redraws the meters
**
*/
void CMeterWindow::Update(bool nodraw)
{
	m_UpdateCounter++;

	// Pre-updates
	CMeasureNet::UpdateIFTable();
	CMeasureCalc::UpdateVariableMap(*this);

	// Update all measures
	std::list<CMeasure*>::iterator i = m_Measures.begin();
	for( ; i != m_Measures.end(); i++)
	{
		try
		{
			(*i)->Update();
		}
		catch (CError& error)
		{
			MessageBox(m_Window, error.GetString().c_str(), APPNAME, MB_OK | MB_TOPMOST | MB_ICONEXCLAMATION);
		}
	}

	// Update the meters
	std::list<CMeter*>::iterator j = m_Meters.begin();
	for( ; j != m_Meters.end(); j++)
	{
		try
		{
			(*j)->Update();
		}
		catch (CError& error)
		{
			MessageBox(m_Window, error.GetString().c_str(), APPNAME, MB_OK | MB_TOPMOST | MB_ICONEXCLAMATION);
		}
	}

	// Statistics
	CMeasureNet::UpdateStats();

	if (!nodraw)
	{
		if (m_DynamicWindowSize)
		{
			// Resize the window
			m_ResetRegion = true;
		}

		Redraw();
	}

//	if (m_MeasuresToVariables)	// BUG: LSSetVariable doens't seem to work for some reason.
//	{
//		std::list<CMeasure*>::iterator i = m_Measures.begin();
//		for( ; i != m_Measures.end(); i++)
//		{
//			const char* sz = (*i)->GetStringValue(true, 1, 1, false);
//			if (sz && wcslen(sz) > 0)
//			{
//				WCHAR* wideSz = CMeter::ConvertToWide(sz);
//				WCHAR* wideName = CMeter::ConvertToWide((*i)->GetName());
//				LSSetVariable(wideName, wideSz);
//				delete [] wideSz;
//				delete [] wideName;
//			}
//		}
//	}
}

/*
** UpdateTransparency
**
** Updates the native Windows transparency
*/
void CMeterWindow::UpdateTransparency(int alpha, bool reset)
{
	if (m_NativeTransparency)
	{
		if (reset)
		{
			// Add the window flag
			LONG style = GetWindowLong(m_Window, GWL_EXSTYLE);
			SetWindowLong(m_Window, GWL_EXSTYLE, style | WS_EX_LAYERED);
		}

		typedef BOOL (WINAPI * FPUPDATELAYEREDWINDOW)(HWND hWnd, HDC hdcDst, POINT *pptDst, SIZE *psize, HDC hdcSrc, POINT *pptSrc, COLORREF crKey, BLENDFUNCTION *pblend, DWORD dwFlags);
		FPUPDATELAYEREDWINDOW UpdateLayeredWindow = (FPUPDATELAYEREDWINDOW)GetProcAddress(m_User32Library, "UpdateLayeredWindow");

		BLENDFUNCTION blendPixelFunction= {AC_SRC_OVER, 0, alpha, AC_SRC_ALPHA};
		POINT ptWindowScreenPosition = {m_WindowX, m_WindowY};
		POINT ptSrc = {0, 0};
		SIZE szWindow = {m_WindowW, m_WindowH};

		HDC dcScreen = GetDC(GetDesktopWindow());
		HDC dcMemory = CreateCompatibleDC(dcScreen);

		HBITMAP dbBitmap;
		m_DoubleBuffer->GetHBITMAP(Color(0, 0, 0, 0), &dbBitmap);
		HBITMAP oldBitmap = (HBITMAP)SelectObject(dcMemory, dbBitmap);
		UpdateLayeredWindow(m_Window, dcScreen, &ptWindowScreenPosition, &szWindow, dcMemory, &ptSrc, 0, &blendPixelFunction, ULW_ALPHA);
		ReleaseDC(GetDesktopWindow(), dcScreen);
		SelectObject(dcMemory, oldBitmap);
		DeleteDC(dcMemory);
		DeleteObject(dbBitmap);

		m_TransparencyValue = alpha;
	}
	else
	{
		if (reset)
		{
			// Remove the window flag
			LONG style = GetWindowLong(m_Window, GWL_EXSTYLE);
			SetWindowLong(m_Window, GWL_EXSTYLE, style & ~WS_EX_LAYERED);
		}
	}
}

/*
** OnPaint
**
** Repaints the window. This does not cause update of the measures.
**
*/
LRESULT CMeterWindow::OnPaint(WPARAM wParam, LPARAM lParam) 
{
	PAINTSTRUCT ps;
	HDC winDC = BeginPaint(m_Window, &ps);

	Graphics graphics(winDC);
	graphics.DrawImage(m_DoubleBuffer, 0, 0);

	EndPaint(m_Window, &ps);
	return 0;
}

/*
** OnTimer
**
** Handles the timers. The METERTIMER updates all the measures 
** MOUSETIMER is used to hide/show the window.
**
*/
LRESULT CMeterWindow::OnTimer(WPARAM wParam, LPARAM lParam) 
{
	if(wParam == METERTIMER) 
	{
		Update(false);
		UpdateAboutStatistics();

		if (m_KeepOnScreen) 
		{
			int x = m_WindowX;
			int y = m_WindowY;
			MapCoordsToScreen(x, y, m_WindowW, m_WindowH);
			if (x != m_WindowX || y != m_WindowY)
			{
				MoveWindow(x, y);
			}
		}
	}
	else if(wParam == MOUSETIMER)
	{
		ShowWindowIfAppropriate();

		POINT pos;
		GetCursorPos(&pos);
		MapWindowPoints(NULL, m_Window, &pos, 1);
		
		if (!m_MouseLeaveAction.empty())
		{
			// Check mouse leave actions
			DoAction(pos.x, pos.y, MOUSE_LEAVE, false);
		}

		if (m_WindowZPosition == ZPOSITION_ONTOPMOST)
		{
			ChangeZPos(ZPOSITION_ONTOPMOST);
		}

		// Handle buttons
		bool redraw = false;
		std::list<CMeter*>::iterator j = m_Meters.begin();
		for( ; j != m_Meters.end(); j++)
		{
			// Hidden meters are ignored
			if ((*j)->IsHidden()) continue;

			CMeterButton* button = dynamic_cast<CMeterButton*>(*j);
			if (button)
			{
				redraw |= button->MouseMove(pos);
			}
		}
		if (redraw)
		{
			Redraw();
		}
	}
	else if(wParam == FADETIMER)
	{
		DWORD ticks = GetTickCount();
		if (m_FadeStartTime == 0)
		{
			m_FadeStartTime = ticks;
		}

		if (ticks - m_FadeStartTime > (DWORD)m_FadeDuration)
		{
			KillTimer(m_Window, FADETIMER);
			m_FadeStartTime = 0;
			if (m_FadeEndValue == 0)
			{
				ShowWindow(m_Window, SW_HIDE);
			}
			else
			{
				UpdateTransparency(m_FadeEndValue, false);
			}
		}
		else
		{
			double value = (ticks - m_FadeStartTime);
			value /= m_FadeDuration;
 			value *= m_FadeEndValue - m_FadeStartValue;
			value += m_FadeStartValue;
			value = min(value, 255);
			value = max(value, 0);

			UpdateTransparency((int)value, false);
		}
	}

//	// TEST
//	if (!m_ChildWindow)
//	{
//		RECT rect;
//		GetWindowRect(m_Window, &rect);
//		if (rect.left != m_WindowX && rect.top != m_WindowY)
//		{
//			DebugLog(L"Window position has been changed. Moving it back to the place it belongs.");
//			SetWindowPos(m_Window, NULL, m_WindowX, m_WindowY, 0, 0, SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE);
//		}
//	}

	// ~TEST

	return 0;
}

void CMeterWindow::FadeWindow(int from, int to)
{
	if (!m_NativeTransparency || m_FadeDuration == 0)
	{
		if (to == 0)
		{
			ShowWindow(m_Window, SW_HIDE);
		}
		else
		{
			if (m_FadeDuration == 0)
			{
				UpdateTransparency(to, false);
			}
			if (from == 0) 
			{
				ShowWindow(m_Window, SW_SHOWNOACTIVATE);
			}
		}
	}
	else
	{
		m_FadeStartValue = from;
		m_FadeEndValue = to;
		UpdateTransparency(from, false);
		if (from == 0) 
		{
			ShowWindow(m_Window, SW_SHOWNOACTIVATE);
		}

		SetTimer(m_Window, FADETIMER, 10, NULL);
	}
}

/*
** ShowWindowIfAppropriate
**
** Show the window if it is temporarily hidden.
**
*/
void CMeterWindow::ShowWindowIfAppropriate()
{
	bool inside = false;
	bool keyDown = GetKeyState(VK_CONTROL) & 0x8000 || GetKeyState(VK_SHIFT) & 0x8000 || GetKeyState(VK_MENU) & 0x8000;
	POINT pos;
	RECT rect;

	GetWindowRect(m_Window, &rect);
	GetCursorPos(&pos);

	if(rect.left <= pos.x && rect.right >= pos.x &&
	   rect.top <= pos.y && rect.bottom >= pos.y) 
	{
		// Check transparent pixels
		if (m_DoubleBuffer)
		{
			Color color;
			m_DoubleBuffer->GetPixel(pos.x - rect.left, pos.y - rect.top, &color);
			if (color.GetA() != 0)
			{
				inside = true;
			}
		}
		else
		{
			inside = true;
		}
	}

	if (m_ClickThrough)
	{
		if (!inside || keyDown)
		{
			// If Alt, shift or control is down, remove the transparent flag
			LONG style = GetWindowLong(m_Window, GWL_EXSTYLE);
			if ((style & WS_EX_TRANSPARENT) != 0)
			{
				SetWindowLong(m_Window, GWL_EXSTYLE, style & ~WS_EX_TRANSPARENT);
			}
		}
	}

	if(m_WindowHide)
	{
		if (!inside && !keyDown)
		{
			switch(m_WindowHide) 
			{
			case HIDEMODE_HIDE:
				if (m_TransparencyValue == 0 || !IsWindowVisible(m_Window))
				{
					ShowWindow(m_Window, SW_SHOWNOACTIVATE);
					FadeWindow(0, m_AlphaValue);
				}
				break;

			case HIDEMODE_FADEIN:
				if (m_TransparencyValue == 255)
				{
					FadeWindow(255, m_AlphaValue);
				}
				break;

			case HIDEMODE_FADEOUT:
				if (m_TransparencyValue == m_AlphaValue)
				{
					FadeWindow(m_AlphaValue, 255);
				}
				break;
			}
		}
	}
}


/*
** OnMouseMove
**
** When we get WM_MOUSEMOVE messages, hide the window as the mouse is over it.
**
*/
LRESULT CMeterWindow::OnMouseMove(WPARAM wParam, LPARAM lParam) 
{
	bool keyDown = GetKeyState(VK_CONTROL) & 0x8000 || GetKeyState(VK_SHIFT) & 0x8000 || GetKeyState(VK_MENU) & 0x8000;

	if (!keyDown)
	{
		if (m_ClickThrough)
		{
			LONG style = GetWindowLong(m_Window, GWL_EXSTYLE);
			if ((style & WS_EX_TRANSPARENT) == 0)
			{
				SetWindowLong(m_Window, GWL_EXSTYLE, style | WS_EX_TRANSPARENT);
			}
		}

		// If Alt, shift or control is down, do not hide the window
		switch(m_WindowHide) 
		{
		case HIDEMODE_HIDE:
			if (m_TransparencyValue == m_AlphaValue)
			{
				FadeWindow(m_AlphaValue, 0);
			}
			break;

		case HIDEMODE_FADEIN:
			if (m_TransparencyValue == m_AlphaValue)
			{
				FadeWindow(m_AlphaValue, 255);
			}
			break;

		case HIDEMODE_FADEOUT:
			if (m_TransparencyValue == 255)
			{
				FadeWindow(255, m_AlphaValue);
			}
			break;
		}
	}

	POINT pos;
	pos.x = (SHORT)LOWORD(lParam);
	pos.y = (SHORT)HIWORD(lParam);

	if (m_Message == WM_NCMOUSEMOVE)
	{
		// Map to local window
		MapWindowPoints(GetDesktopWindow(), m_Window, &pos, 1);
	}
	
	DoAction(pos.x, pos.y, MOUSE_OVER, false);

	// Handle buttons
	bool redraw = false;
	std::list<CMeter*>::iterator j = m_Meters.begin();
	for( ; j != m_Meters.end(); j++)
	{
		// Hidden meters are ignored
		if ((*j)->IsHidden()) continue;

		CMeterButton* button = dynamic_cast<CMeterButton*>(*j);
		if (button)
		{
			redraw |= button->MouseMove(pos);
		}
	}
	if (redraw)
	{
		Redraw();
	}

	return 0;
}

/*
** OnCreate
**
** During window creation we do nothing.
**
*/
LRESULT CMeterWindow::OnCreate(WPARAM wParam, LPARAM lParam) 
{
	return 0;
}

/*
** OnCommand
**
** Handle the menu commands.
**
*/
LRESULT CMeterWindow::OnCommand(WPARAM wParam, LPARAM lParam) 
{
	try 
	{
		if(wParam == ID_CONTEXT_SKINMENU_EDITSKIN)
		{
			std::wstring command = m_Rainmeter->GetConfigEditor();
			command += L" \"";
			command += m_Rainmeter->GetSkinPath() + L"\\" + m_SkinName + L"\\" + m_SkinIniFile + L"\"";
			m_Rainmeter->ExecuteCommand(command.c_str(), this);
		}
		else if(wParam == ID_CONTEXT_SKINMENU_REFRESH)
		{
			Refresh(false);
		} 
		else if(wParam == ID_CONTEXT_SKINMENU_VERYTOPMOST)
		{
			ChangeZPos(ZPOSITION_ONTOPMOST);
			WriteConfig();
		}
		else if(wParam == ID_CONTEXT_SKINMENU_TOPMOST)
		{
			ChangeZPos(ZPOSITION_ONTOP);
			WriteConfig();
		}
		else if(wParam == ID_CONTEXT_SKINMENU_BOTTOM)
		{
			ChangeZPos(ZPOSITION_ONBOTTOM);
			WriteConfig();
		}
		else if(wParam == ID_CONTEXT_SKINMENU_NORMAL)
		{
			ChangeZPos(ZPOSITION_NORMAL);
			WriteConfig();
		}
		else if(wParam == ID_CONTEXT_SKINMENU_ONDESKTOP)
		{
			ChangeZPos(ZPOSITION_ONDESKTOP);
			WriteConfig();
		}
		else if(wParam == ID_CONTEXT_SKINMENU_KEEPONSCREEN)
		{
			m_KeepOnScreen = !m_KeepOnScreen;
			WriteConfig();
		}
		else if(wParam == ID_CONTEXT_SKINMENU_CLICKTHROUGH)
		{
			m_ClickThrough = !m_ClickThrough;
			WriteConfig();
			Refresh(false);
		}
		else if(wParam == ID_CONTEXT_SKINMENU_DRAGGABLE)
		{
			m_WindowDraggable = !m_WindowDraggable;
			WriteConfig();
		}
		else if(wParam == ID_CONTEXT_SKINMENU_HIDEONMOUSE)
		{
			if (m_WindowHide == HIDEMODE_NONE)
			{
				m_WindowHide = HIDEMODE_HIDE;
			}
			else
			{
				m_WindowHide = HIDEMODE_NONE;
			}
			WriteConfig();
			Refresh(false);
		}		
		else if(wParam == ID_CONTEXT_SKINMENU_TRANSPARENCY_FADEIN)
		{
			if (m_WindowHide == HIDEMODE_NONE)
			{
				m_WindowHide = HIDEMODE_FADEIN;
			}
			else
			{
				m_WindowHide = HIDEMODE_NONE;
			}
			WriteConfig();
			Refresh(false);
		}
		else if(wParam == ID_CONTEXT_SKINMENU_TRANSPARENCY_FADEOUT)
		{
			if (m_WindowHide == HIDEMODE_NONE)
			{
				m_WindowHide = HIDEMODE_FADEOUT;
			}
			else
			{
				m_WindowHide = HIDEMODE_NONE;
			}
			WriteConfig();
			Refresh(false);
		}
		else if(wParam == ID_CONTEXT_SKINMENU_REMEMBERPOSITION)
		{
			m_SavePosition = !m_SavePosition;
			WriteConfig();
		}
		else if(wParam == ID_CONTEXT_SKINMENU_SNAPTOEDGES)
		{
			m_SnapEdges = !m_SnapEdges;
			WriteConfig();
		}
		else if(wParam >= ID_CONTEXT_SKINMENU_TRANSPARENCY_0 && wParam <= ID_CONTEXT_SKINMENU_TRANSPARENCY_90)
		{
			m_AlphaValue = (int)(255.0 - 230.0 * (double)(wParam - ID_CONTEXT_SKINMENU_TRANSPARENCY_0) / (double)(ID_CONTEXT_SKINMENU_TRANSPARENCY_90 - ID_CONTEXT_SKINMENU_TRANSPARENCY_0));
			WriteConfig();
			Refresh(false);
		}
		else if(wParam == ID_CONTEXT_CLOSESKIN)
		{
			const std::vector<CRainmeter::CONFIG>& configs = m_Rainmeter->GetAllConfigs();

			for (int i = 0; i < configs.size(); i++)
			{
				if (configs[i].config == m_SkinName)
				{
					m_Rainmeter->DeactivateConfig(this, i);
					break;
				}
			}
		}
		else
		{
			// Forward to tray window, which handles all the other commands
			SendMessage(m_Rainmeter->GetTrayWindow()->GetWindow(), WM_COMMAND, wParam, lParam);
		}
	} 
    catch(CError& error) 
    {
		MessageBox(m_Window, error.GetString().c_str(), APPNAME, MB_OK | MB_TOPMOST | MB_ICONEXCLAMATION);
	}

	return 0;
}

/*
** OnNcHitTest
**
** This is overwritten so that the window can be dragged
**
*/
LRESULT CMeterWindow::OnNcHitTest(WPARAM wParam, LPARAM lParam) 
{
	if (m_WindowDraggable)
	{
		POINT pos;
		pos.x = (SHORT)LOWORD(lParam);
		pos.y = (SHORT)HIWORD(lParam);
		MapWindowPoints(GetDesktopWindow(), m_Window, &pos, 1);

		int x1 = m_DragMargins.GetLeft();
		int x2 = m_WindowW - m_DragMargins.GetRight();
		int y1 = m_DragMargins.GetTop();
		int y2 = m_WindowH - m_DragMargins.GetBottom();

		if (x1 < 0) x1 += m_WindowW;
		if (y1 < 0) y1 += m_WindowH;
		if (x2 > m_WindowW) x2 -= m_WindowW;
		if (y2 > m_WindowH) y2 -= m_WindowH;

		if (pos.x >= x1 && pos.x < x2) 
		{
			if (pos.y >= y1 && pos.y < y2) 
			{
				return HTCAPTION;
			}
		}
	}
	return HTCLIENT;
}

/*
** OnWindowPosChanging
**
** Called when windows position is about to change
**
*/
LRESULT CMeterWindow::OnWindowPosChanging(WPARAM wParam, LPARAM lParam) 
{
	LPWINDOWPOS wp=(LPWINDOWPOS)lParam;

	if(m_WindowZPosition == ZPOSITION_ONBOTTOM && !m_Refreshing)
	{
		// do not change the z-order. This keeps the window on bottom.
		wp->flags|=SWP_NOZORDER;
	}

	if ((wp->flags & SWP_NOMOVE) == 0) 
	{
		if (m_SnapEdges && !(GetKeyState(VK_CONTROL) & 0x8000 || GetKeyState(VK_SHIFT) & 0x8000))
		{
			RECT workArea;
			GetClientRect(GetDesktopWindow(), &workArea);

			// only process movement (ignore anything without winpos values)
			if(wp->cx != 0 && wp->cy != 0)
			{
				HMONITOR hMonitor;
				MONITORINFO mi;

				hMonitor = MonitorFromWindow(m_Window, MONITOR_DEFAULTTONULL);

				if(hMonitor != NULL)
				{
					mi.cbSize = sizeof(mi);
					GetMonitorInfo(hMonitor, &mi);
					workArea = mi.rcWork;
				}

				// Snap to other windows
				std::map<std::wstring, CMeterWindow*>& windows = Rainmeter->GetAllMeterWindows();
				std::map<std::wstring, CMeterWindow*>::iterator iter = windows.begin();
				for( ; iter != windows.end(); iter++)
				{
					if ((*iter).second != this)
					{
						SnapToWindow((*iter).second, wp);
					}
				}

				int w = workArea.right - m_WindowW;
				int h = workArea.bottom - m_WindowH;

				if ((wp->x < SNAPDISTANCE + workArea.left) && (wp->x > workArea.left - SNAPDISTANCE)) wp->x = workArea.left;
				if ((wp->y < SNAPDISTANCE + workArea.top) && (wp->y > workArea.top - SNAPDISTANCE)) wp->y = workArea.top;
				if ((wp->x < SNAPDISTANCE + w) && (wp->x > -SNAPDISTANCE + w)) wp->x = w;
				if ((wp->y < SNAPDISTANCE + h) && (wp->y > -SNAPDISTANCE + h)) wp->y = h;
			}
		}

		if (m_KeepOnScreen) 
		{
			MapCoordsToScreen(wp->x, wp->y, m_WindowW, m_WindowH);
		}
	}

	return DefWindowProc(m_Window, m_Message, wParam, lParam);
}

void CMeterWindow::SnapToWindow(CMeterWindow* window, LPWINDOWPOS wp)
{
	int x = window->m_WindowX;
	int y = window->m_WindowY;
	int w = window->m_WindowW;
	int h = window->m_WindowH;

	if (wp->y < y + h && wp->y + m_WindowH > y)
	{
		if((wp->x < SNAPDISTANCE + x) && (wp->x > x - SNAPDISTANCE)) wp->x = x;
		if((wp->x < SNAPDISTANCE + x + w) && (wp->x > x + w - SNAPDISTANCE)) wp->x = x + w;

		if((wp->x + m_WindowW < SNAPDISTANCE + x) && (wp->x + m_WindowW > x - SNAPDISTANCE)) wp->x = x - m_WindowW;
		if((wp->x + m_WindowW < SNAPDISTANCE + x + w) && (wp->x + m_WindowW > x + w - SNAPDISTANCE)) wp->x = x + w - m_WindowW;
	}

	if (wp->x < x + w && wp->x + m_WindowW > x)
	{
		if((wp->y < SNAPDISTANCE + y) && (wp->y > y - SNAPDISTANCE)) wp->y = y;
		if((wp->y < SNAPDISTANCE + y + h) && (wp->y > y + h - SNAPDISTANCE)) wp->y = y + h;

		if((wp->y + m_WindowH < SNAPDISTANCE + y) && (wp->y + m_WindowH > y - SNAPDISTANCE)) wp->y = y - m_WindowH;
		if((wp->y + m_WindowH < SNAPDISTANCE + y + h) && (wp->y + m_WindowH > y + h - SNAPDISTANCE)) wp->y = y + h - m_WindowH;
	}
}

/*
** OnDestroy
**
** During destruction of the window do nothing.
**
*/
LRESULT CMeterWindow::OnDestroy(WPARAM wParam, LPARAM lParam) 
{
	return 0;
}


/*
** OnLeftButtonDown
**
** Runs the action when left mouse button is down
**
*/
LRESULT CMeterWindow::OnLeftButtonDown(WPARAM wParam, LPARAM lParam) 
{
	POINT pos;
	pos.x = (SHORT)LOWORD(lParam); 
	pos.y = (SHORT)HIWORD(lParam); 

	if (m_Message == WM_NCLBUTTONDOWN)
	{
		// Transform the point to client rect
		RECT rect;
		GetWindowRect(m_Window, &rect);
		pos.x = pos.x - rect.left;
		pos.y = pos.y - rect.top;
	}

	// Handle buttons
	bool redraw = false;
	std::list<CMeter*>::iterator j = m_Meters.begin();
	for( ; j != m_Meters.end(); j++)
	{
		// Hidden meters are ignored
		if ((*j)->IsHidden()) continue;

		CMeterButton* button = dynamic_cast<CMeterButton*>(*j);
		if (button)
		{
			redraw |= button->MouseDown(pos);
		}
	}
	if (redraw)
	{
		Redraw();
	}
	else if(!DoAction(pos.x, pos.y, MOUSE_LMB_DOWN, false))
	{
		// Run the DefWindowProc so the dragging works
		return DefWindowProc(m_Window, m_Message, wParam, lParam);
	}

	return 0;
}

/*
** OnLeftButtonUp
**
** Runs the action when left mouse button is up
**
*/
LRESULT CMeterWindow::OnLeftButtonUp(WPARAM wParam, LPARAM lParam) 
{
	POINT pos;
	pos.x = (SHORT)LOWORD(lParam); 
	pos.y = (SHORT)HIWORD(lParam); 

	if (m_Message == WM_NCLBUTTONUP)
	{
		// Transform the point to client rect
		RECT rect;
		GetWindowRect(m_Window, &rect);
		pos.x = pos.x - rect.left;
		pos.y = pos.y - rect.top;
	}

	// Handle buttons
	bool redraw = false;
	std::list<CMeter*>::iterator j = m_Meters.begin();
	for( ; j != m_Meters.end(); j++)
	{
		// Hidden meters are ignored
		if ((*j)->IsHidden()) continue;

		CMeterButton* button = dynamic_cast<CMeterButton*>(*j);
		if (button)
		{
			redraw |= button->MouseUp(pos, this);
		}
	}
	if (redraw)
	{
		Redraw();
	}

	DoAction(pos.x, pos.y, MOUSE_LMB_UP, false);

	return 0;
}

/*
** OnRightButtonDown
**
** Runs the action when right mouse button is down
**
*/
LRESULT CMeterWindow::OnRightButtonDown(WPARAM wParam, LPARAM lParam) 
{
	POINT pos;
	pos.x = (SHORT)LOWORD(lParam); 
	pos.y = (SHORT)HIWORD(lParam); 

	if (m_Message == WM_NCRBUTTONDOWN)
	{
		// Transform the point to client rect
		RECT rect;
		GetWindowRect(m_Window, &rect);
		pos.x = pos.x - rect.left;
		pos.y = pos.y - rect.top;
	}

	DoAction(pos.x, pos.y, MOUSE_RMB_DOWN, false);

	return 0;
}

/*
** OnRightButtonUp
**
** Runs the action when right mouse button is up
**
*/
LRESULT CMeterWindow::OnRightButtonUp(WPARAM wParam, LPARAM lParam) 
{
	if (!DoAction((SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam), MOUSE_RMB_UP, false))
	{
		// Run the DefWindowProc so the context menu works
		return DefWindowProc(m_Window, WM_RBUTTONUP, wParam, lParam);
	}

	return 0;
}

/*
** OnContextMenu
**
** Handles the context menu. The menu is recreated every time it is shown.
**
*/
LRESULT CMeterWindow::OnContextMenu(WPARAM wParam, LPARAM lParam) 
{
	int xPos = (SHORT)LOWORD(lParam); 
	int yPos = (SHORT)HIWORD(lParam); 

	// Transform the point to client rect
	int x = (INT)(SHORT)LOWORD(lParam); 
	int y = (INT)(SHORT)HIWORD(lParam); 
	RECT rect;
	GetWindowRect(m_Window, &rect);
	x = x - rect.left;
	y = y - rect.top;

	// If RMB up or RMB down cause actions, do not show the menu!
	if (DoAction(x, y, MOUSE_RMB_UP, false) || DoAction(x, y, MOUSE_RMB_DOWN, true))
	{
		return 0;
	}

	POINT pos;
	pos.x = xPos;
	pos.y = yPos;
	m_Rainmeter->ShowContextMenu(pos, this);

	return 0;
}

/*
** DoAction
**
** Executes the action if such are defined. Returns true, if action was executed.
** If the test is true, the action is not executed.
**
*/
bool CMeterWindow::DoAction(int x, int y, MOUSE mouse, bool test) 
{
	// Check if the hitpoint was over some meter
	std::list<CMeter*>::iterator j = m_Meters.begin();
	for( ; j != m_Meters.end(); j++)
	{
		// Hidden meters are ignored
		if ((*j)->IsHidden()) continue;

		if ((*j)->HitTest(x, y))
		{
			switch (mouse)
			{
			case MOUSE_LMB_DOWN:
				if (!((*j)->GetLeftMouseDownAction().empty()))
				{
					if (!test) m_Rainmeter->ExecuteCommand((*j)->GetLeftMouseDownAction().c_str(), this);
					return true;
				}
				break;

			case MOUSE_LMB_UP:
				if (!((*j)->GetLeftMouseUpAction().empty()))
				{
					if (!test) m_Rainmeter->ExecuteCommand((*j)->GetLeftMouseUpAction().c_str(), this);
					return true;
				}
				break;

			case MOUSE_RMB_DOWN:
				if (!((*j)->GetRightMouseDownAction().empty()))
				{
					if (!test) m_Rainmeter->ExecuteCommand((*j)->GetRightMouseDownAction().c_str(), this);
					return true;
				}
				break;

			case MOUSE_RMB_UP:
				if (!((*j)->GetRightMouseUpAction().empty()))
				{
					if (!test) m_Rainmeter->ExecuteCommand((*j)->GetRightMouseUpAction().c_str(), this);
					return true;
				}
				break;

			case MOUSE_OVER:
				if (!(*j)->IsMouseOver())
				{
					(*j)->SetMouseOver(true);

					if (!((*j)->GetMouseOverAction().empty()))
					{
						m_MouseOver = true;		// If the mouse is over a meter it's also over the main window
						if (!test) m_Rainmeter->ExecuteCommand((*j)->GetMouseOverAction().c_str(), this);
						return true;
					}
				}
				break;
			}
		}
		else
		{
			if (mouse == MOUSE_LEAVE || mouse == MOUSE_OVER)
			{
				if ((*j)->IsMouseOver())
				{
					(*j)->SetMouseOver(false);

					if (!((*j)->GetMouseLeaveAction().empty()))
					{
						if (!test) m_Rainmeter->ExecuteCommand((*j)->GetMouseLeaveAction().c_str(), this);
						return true;
					}
				}
			}
		}
	}

	bool inside = false;
	if (x >= 0 && y >= 0 && x < m_WindowW && y < m_WindowH)
	{
		// Check transparent pixels
		if (m_DoubleBuffer)
		{
			Color color;
			m_DoubleBuffer->GetPixel(x, y, &color);
			if (color.GetA() != 0)
			{
				inside = true;
			}
		}
		else
		{
			inside = true;
		}
	}

	if (inside)
	{
		// If no meters caused actions, do the default actions
		switch (mouse)
		{
		case MOUSE_LMB_DOWN:
			if (!m_LeftMouseDownAction.empty())
			{
				if (!test) m_Rainmeter->ExecuteCommand(m_LeftMouseDownAction.c_str(), this);
				return true;
			}
			break;

		case MOUSE_LMB_UP:
			if (!m_LeftMouseUpAction.empty())
			{
				if (!test) m_Rainmeter->ExecuteCommand(m_LeftMouseUpAction.c_str(), this);
				return true;
			}
			break;

		case MOUSE_RMB_DOWN:
			if (!m_RightMouseDownAction.empty())
			{
				if (!test) m_Rainmeter->ExecuteCommand(m_RightMouseDownAction.c_str(), this);
				return true;
			}
			break;

		case MOUSE_RMB_UP:
			if (!m_RightMouseUpAction.empty())
			{
				if (!test) m_Rainmeter->ExecuteCommand(m_RightMouseUpAction.c_str(), this);
				return true;
			}
			break;

		case MOUSE_OVER:
			if (!m_MouseOver)
			{
				m_MouseOver = true;
				if (!m_MouseOverAction.empty())
				{
					if (!test) m_Rainmeter->ExecuteCommand(m_MouseOverAction.c_str(), this);
					return true;
				}
			}
			break;
		}
	}
	else
	{
		// Mouse leave happens when the mouse is outside the window
		if (mouse == MOUSE_LEAVE)
		{
			if (m_MouseOver)
			{
				m_MouseOver = false;
				if (!m_MouseLeaveAction.empty())
				{
					if (!test) m_Rainmeter->ExecuteCommand(m_MouseLeaveAction.c_str(), this);
					return true;
				}
			}
		}
	}

	return false;
}


/*
** OnMove
**
** Stores the new place of the window so that it can be written in the config file.
**
*/
LRESULT CMeterWindow::OnMove(WPARAM wParam, LPARAM lParam) 
{
	// Store the new window position
	m_WindowX = (SHORT)LOWORD(lParam);
	m_WindowY = (SHORT)HIWORD(lParam);

	if (m_SavePosition)
	{
		WriteConfig();
	}

	return 0;
}

/* 
** WndProc
**
** The window procedure for the Meter
**
*/
LRESULT CALLBACK CMeterWindow::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	CMeterWindow* Window = NULL;

	if(uMsg == WM_CREATE) 
	{
		// Fetch this window-object from the CreateStruct
		Window=(CMeterWindow*)((LPCREATESTRUCT)lParam)->lpCreateParams;

		SetProp(hWnd, L"RAINMETER", Window);
	}
	else if(uMsg == WM_DESTROY) 
	{
		RemoveProp(hWnd, L"RAINMETER");
	}
	else
	{
		Window = (CMeterWindow*)GetProp(hWnd, L"RAINMETER");
	}

	if (Window) Window->m_Message = uMsg;

	BEGIN_MESSAGEPROC
	MESSAGE(OnPaint, WM_PAINT)
	MESSAGE(OnMove, WM_MOVE)
	MESSAGE(OnCreate, WM_CREATE)
	MESSAGE(OnDestroy, WM_DESTROY)
	MESSAGE(OnTimer, WM_TIMER)
	MESSAGE(OnCommand, WM_COMMAND)
	MESSAGE(OnNcHitTest, WM_NCHITTEST)
	MESSAGE(OnMouseMove, WM_MOUSEMOVE)
	MESSAGE(OnMouseMove, WM_NCMOUSEMOVE)
	MESSAGE(OnContextMenu, WM_CONTEXTMENU)
	MESSAGE(OnRightButtonDown, WM_NCRBUTTONDOWN)
	MESSAGE(OnRightButtonDown, WM_RBUTTONDOWN)
	MESSAGE(OnRightButtonUp, WM_RBUTTONUP)
	MESSAGE(OnContextMenu, WM_NCRBUTTONUP)
	MESSAGE(OnLeftButtonDown, WM_NCLBUTTONDOWN)
	MESSAGE(OnLeftButtonDown, WM_LBUTTONDOWN)
	MESSAGE(OnLeftButtonUp, WM_LBUTTONUP)
	MESSAGE(OnLeftButtonUp, WM_NCLBUTTONUP)
	MESSAGE(OnWindowPosChanging, WM_WINDOWPOSCHANGING)
	MESSAGE(OnCopyData, WM_COPYDATA)
	MESSAGE(OnDelayedExecute, WM_DELAYED_EXECUTE)
	END_MESSAGEPROC
}

/*
** OnDelayedExecute
**
** Handles delayed executes
**
*/
LRESULT CMeterWindow::OnDelayedExecute(WPARAM wParam, LPARAM lParam)
{
	if (lParam)
	{
		LPCTSTR szExecute = (LPCTSTR)lParam;
		COPYDATASTRUCT copyData;

		copyData.dwData = 1;
		copyData.cbData = (DWORD)((lstrlen(szExecute))* sizeof(WCHAR));
		copyData.lpData = (void*)szExecute;

		OnCopyData(NULL, (LPARAM)&copyData);
	}
	return 0;
}


/*
** OnCopyData
**
** Handles bangs from the exe
**
*/
LRESULT CMeterWindow::OnCopyData(WPARAM wParam, LPARAM lParam)
{
	COPYDATASTRUCT* pCopyDataStruct = (COPYDATASTRUCT*) lParam;

	if (pCopyDataStruct && (pCopyDataStruct->dwData == 1) && (pCopyDataStruct->cbData > 0))
	{
		// Check that we're still alive
		bool found = false;
		std::map<std::wstring, CMeterWindow*>& meters = Rainmeter->GetAllMeterWindows();
		std::map<std::wstring, CMeterWindow*>::iterator iter = meters.begin();

		for ( ; iter != meters.end(); iter++)
		{
			if ((*iter).second == this)
			{
				found = true;
				break;
			}
		}
		if (!found) 
		{
			DebugLog(L"Unable to send the !bang to a deactivated config.");
			return 0;	// This meterwindow has been deactivated
		}

		std::wstring str = (const WCHAR*)pCopyDataStruct->lpData;
		std::wstring bang;
		std::wstring arg;

		// Find the first space
		std::wstring::size_type pos = str.find(' ');
		if (pos != std::wstring::npos)
		{
			bang = str.substr(0, pos);
			str.erase(0, pos + 1);
			arg = str;
		}
		else
		{
			bang = str;
		}

		// Add the current config name to the args. If it's not defined already
		// the bang only affects this config, if there already is a config defined
		// another one doesn't matter.
		arg += L" \"";
		arg += m_SkinName.c_str();
		arg += L"\"";

		return Rainmeter->ExecuteBang(bang, arg, this);
	}
	else
	{
		return FALSE;
	}
}

