/*
 ============================================================================
 xoblite -> an alternative shell based on Blackbox for Windows
 Copyright © 2002-2005 Karl-Henrik Henriksson [qwilk]
 Copyright © 2001-2004 The Blackbox for Windows Development Team
 http://xoblite.net/ - #bb4win on irc.freenode.net
 ============================================================================

  Blackbox for Windows is free software, released under the
  GNU General Public License (GPL version 2 or later), with an extension
  that allows linking of proprietary modules under a controlled interface.
  What this means is that plugins etc. are allowed to be released
  under any license the author wishes. Please note, however, that the
  original Blackbox gradient math code used in Blackbox for Windows
  is available under the BSD license.

  http://www.fsf.org/licenses/gpl.html
  http://www.fsf.org/licenses/gpl-faq.html#LinkingOverControlledInterface
  http://www.xfree86.org/3.3.6/COPYRIGHT2.html#5

  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.

  For additional license information, please read the included license.html

 ============================================================================
*/

#pragma warning(disable: 4786) // STL naming warnings 

#include "BBApi.h" 
#include "BImage.h" 
#include "PluginManager.h" 
#include "TrayManager.h" 
#include "Systembar.h" 
#include "Workspaces.h" 
#include "Menu\Menu.h" 
#include "Menu\MenuMaker.h" 
#include "Menu\MenuItem.h" 
#include "Menu\CommandItem.h" 
#include "Menu\FolderItem.h" 
#include "Menu\TitleItem.h" 
#include "Settings.h" 
#include <stdlib.h> 
#include <stdio.h> 
#include <time.h> 
#include <shellapi.h> 
#include <winver.h> 

//===========================================================================

// Transparency defines - just in case... <g>
#ifndef WS_EX_LAYERED 
#define WS_EX_LAYERED			0x00080000 
#endif 
#ifndef LWA_COLORKEY 
#define LWA_COLORKEY			0x00000001 
#endif 
#ifndef LWA_ALPHA 
#define LWA_ALPHA				0x00000002 
#endif 

//===========================================================================

extern BImage *pBImage;
extern Menu *pMenu;
extern MenuMaker *pMenuMaker;
extern PluginManager *pPluginManager;
extern TrayManager *pTrayManager;
extern Systembar *pSystembar;
extern Workspaces *pWorkspaces;
extern Settings *pSettings;

static char tempBuf[MAX_LINE_LENGTH];
static char lineFromFile[MAX_LINE_LENGTH + 1];

typedef vector<HWND> StickyWindowsVector;
StickyWindowsVector stickyWindows;

//LPSTR ReadValue(LPCSTR fp, LPCSTR keyword, LPLONG ptr = NULL);
COLORREF ParseLiteralColor(LPCSTR color);

HWND hBlackbox = NULL;

bool hideNamedMenu = false;

//===========================================================================
// Function: GetBBVersion
// Purpose: Returns the current version
// In: None
// Out: LPCSTR = Formatted Version String
//===========================================================================

LPCSTR GetBBVersion()
{
	char buffer[MAX_LINE_LENGTH];
	ULONG *size = 0;
	DWORD number = GetFileVersionInfoSize("Blackbox.exe", size);
	GetFileVersionInfo("Blackbox.exe", NULL, number, (LPVOID)buffer);

	void *value;
	UINT bytes;
	 // Make sure the correct version encoding is used (currently "000004b0" = language neutral)
	VerQueryValue(buffer, TEXT("\\StringFileInfo\\000004b0\\ProductVersion"), &value, &bytes);
	strcpy(tempBuf, (LPCSTR)value);

	return tempBuf;
}

//===========================================================================
// Function: GetBBWnd
// Purpose: Returns the handle to the main Blackbox window
// In: None
// Out: HWND = Handle to the Blackbox window
//===========================================================================

HWND GetBBWnd()
{
	if (!hBlackbox) hBlackbox = FindWindow("xoblite", NULL);
	return hBlackbox;
}

//===========================================================================
// Function: GetOSInfo
// Purpose: Retrieves info about the current OS
// In: None
// Out: LPCSTR = Returns a readable string containing the OS name
//===========================================================================

LPCSTR GetOSInfo(void)
{
	char buffer[MAX_LINE_LENGTH];
	OSVERSIONINFO osv;
	ZeroMemory(&osv, sizeof(osv));
	osv.dwOSVersionInfoSize = sizeof(osv);
	GetVersionEx(&osv);
	
	if (osv.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) // Windows 9x/ME
	{
		if (osv.dwMinorVersion >= 90) strcpy(buffer, "Windows ME");
		else if (osv.dwMinorVersion >= 10) strcpy(buffer, "Windows 98");
		else strcpy(buffer, "Windows 95");
	}
	else // Windows NT/2000/XP
	{
		if ((osv.dwMajorVersion == 5 ) && (osv.dwMinorVersion >= 1)) strcpy(buffer, "Windows XP");
		else if (osv.dwMajorVersion == 5) strcpy(buffer, "Windows 2000");
		else sprintf(buffer, "Windows NT %d.%d", osv.dwMajorVersion, osv.dwMinorVersion);
	}

	strcpy(tempBuf, buffer);

	return tempBuf;
}

//===========================================================================
// Function: GetBlackboxPath
// Purpose: Copies the path of the Blackbox executable to the specified buffer
// In: LPSTR pszPath = Location of the buffer for the path
// In: int nMaxLen = Maximum length for the buffer
// Out: bool = Returns status of completion
//===========================================================================

bool WINAPI GetBlackboxPath(LPSTR pszPath, int nMaxLen)
{
	static char szPath[MAX_PATH] = "";
	
	if (!szPath[0])
	{
		int nLen;
		GetModuleFileName(NULL, szPath, MAX_PATH);
		nLen = strlen(szPath) - 1;

		while (nLen > 0 && szPath[nLen] != '\\') nLen--;

		szPath[nLen+1] = 0;
	}
	
	strncpy(pszPath, szPath, nMaxLen);

	return true;
}

//===========================================================================
// Function: FileClose
// Purpose: Close selected file
// In: FILE *fp = The file you wish to close
// Out: bool = Returns the status of completion
//===========================================================================

bool FileClose(FILE *fp)
{
	if (fp)
	{
		if (fclose(fp)) return false;
		else return true;
	}
	else return false;
}

//===========================================================================
// Function: Tokenize
// Purpose: Retrieve the first string seperated by the delim and return a pointer to the rest of the string
// In: LPCSTR string = String to be parsed
// In: LPSTR buf = String where the tokenized string will be placed
// In: LPSTR delims = The delimeter that signifies the separation of the tokens in the string
// Out: LPSTR = Returns a pointer to the entire remaining string
//===========================================================================

LPSTR Tokenize(LPCSTR string, LPSTR buf, LPSTR delims)
{
	static char remaining[MAX_LINE_LENGTH];
	strcpy(remaining, string);

	char character, quoted = 0, *b = buf, *r = remaining;

	while ((character = *r) != 0)
	{
		r++;

		if (!quoted)
		{
			if (strchr(delims, character)) break; // User-specified delimiter
			else if (character == '\"' || character == '\'') quoted = character;
			else if (((unsigned char)character <= 32) && (b == buf)) continue;
		}
		else if (character == quoted) quoted = 0;

		*b++ = character;
	}

	while ((b > buf) && ((unsigned char)b[-1] <= 32)) b--;

	*b = '\0';

	while (((unsigned char)*r <= 32) && (*r)) r++;

	strcpy(remaining, r);

	return remaining;
}

//===========================================================================
// Function: BBTokenize
// Purpose: Assigns a specified number of string variables and outputs the remaining to a variable
// In: LPCSTR szString = The string to be parsed
// In: LPSTR lpszBuffers[] = A pointer to the location for the tokenized strings
// In: DWORD dwNumBuffers = The amount of tokens to be parsed
// In: LPSTR szExtraParameters = A pointer to the location for the remaining string
// Out: int = Number of tokens parsed
//===========================================================================

int BBTokenize (LPCSTR szString, char *lpszBuffers[], DWORD dwNumBuffers, LPSTR szExtraParameters)
{
	int inputIndex = 0, outputIndex, numberOfTokens = 0;
	DWORD dwBufferCount = 0;
	char quoteChar = 0, currentChar, *outputBuffer;

	//====================

	while (dwBufferCount < dwNumBuffers)
	{
		outputBuffer = lpszBuffers[dwBufferCount];
		outputIndex = 0;

		//====================

		while ((currentChar = szString[inputIndex]) != 0)
		{
			inputIndex++;

			switch (currentChar)
			{
				//====================

				case ' ':
				case '\t':
				{
					if (quoteChar) goto storeThisChar; // We're inside a quoted string, store spaces/tabs just like other characters...
					if (outputIndex) goto finishedToken; // ...otherwise a space/tab specifies the end of a token
					continue;
				}

				//====================

				case '"':
				case '\'':
				{
					if (quoteChar == 0) // Quoted string begins here...
					{
						// Remember which quote character was used...
						quoteChar = currentChar;
						continue;
					}
					else if (quoteChar == currentChar) // Quoted string ends here...
					{
						// Reset quote character and finish parsing of the current token...
						quoteChar = 0;
						goto finishedToken;
					}
					else goto storeThisChar;
				}

				//====================

				storeThisChar:
				default:
				{
					// Write current character to output buffer...
					outputBuffer[outputIndex] = currentChar;
					outputIndex++;
					continue;
				}

				//====================
			}
		}

		//====================

		// Finish parsing of the current token...
		if (outputIndex) finishedToken: numberOfTokens++;
		outputBuffer[outputIndex]=0;
		dwBufferCount++;
	}

	//====================

	if (szExtraParameters) strcpy(szExtraParameters, szString + inputIndex);

	return numberOfTokens;
}

//===========================================================================
// Function: StrRemoveEncap
// Purpose: Removes the first and last characters of a string
// In: LPSTR string = The string to be altered
// Out: LPSTR = A pointer to the altered string
// Warning: This will remove the first and last characters from ANY string, so don't use it unless you know that it has the chars there
// Warning: My suggestion would be to use strchr() to search the string for the appropriate encaps before you remove them, just to make sure
//===========================================================================

LPSTR StrRemoveEncap(LPSTR string)
{
	int last = strlen(string);
	last--;
	memmove(string, string+1, last-1);
	*(string+last-1) = '\0';

	return string;
}

//===========================================================================
// Function: ReadValue
// Purpose: Opens given file and searches the file for the supplied keyword and returns a pointer to the value
// In: LPCSTR fp = String containing the name of the file to be opened
// In: LPCSTR keyword = String containing the keyword to be looked for
// In: LPLONG ptr = Pointer to the position to start from (defaults to NULL)
// Out: LPSTR = Pointer to the value of the keyword
//===========================================================================

LPSTR ReadValue(LPCSTR fp, LPCSTR keyword, LPLONG ptr)
{
	FILE *f = FileOpen(fp);
	if (!f) return NULL;

	//====================

	// Move to starting position if specified...
	if (ptr && *ptr != 0) fseek(f, *ptr, SEEK_SET);
	else fseek(f, 0, SEEK_SET);

	//====================

	LONG fpos = 0;
	char* currentLine;

	while (FileRead(f, lineFromFile))
	{
		currentLine = lineFromFile;
		currentLine += strspn(currentLine, " \t\r\n");

		// Is the current line a comment line?
		if (*currentLine == '#' || *currentLine == '!')
		{
			fpos = ftell(f);
			continue;
		}
		// If not, look for the keyword a the beginning of the string...
		else if (!strnicmp(currentLine, keyword, strlen(keyword)))
		{
			// We're only interested in the string after the keyword...
			currentLine += strlen(keyword);
			// Remove any space characters, tabs or returns...
			currentLine += strspn(currentLine, " \t\r\n");
			// Remove any trailing space characters...
			int nLen = strlen(currentLine) - 1;
			while (nLen >0 && currentLine[nLen] == ' ') nLen--;
			currentLine[nLen + 1] = 0;

			if (ptr) *ptr = fpos;
			FileClose(f);

			return (char*)currentLine;
		}

		fpos = ftell(f);
	}

	//====================

	FileClose(f);

	return NULL;
}

//===========================================================================
// Function: ReadBool
// Purpose: Read's a string in the given file and outputs the result as a boolean
// In: LPCSTR fp = File to be searched
// In: LPCSTR szString = Keyword to search for
// In: bool szDefault = Default value to return if not found
// Out: bool = Returns the value of the found bool
//===========================================================================

bool ReadBool(LPCSTR fp, LPCSTR szString, bool szDefault)
{
	// NULL/empty filepointers are not supported for ReadBool,
	// but since it is the only Read* function that does not
	// support them I have added a safeguard here to prevent
	// a borked plugin from crashing the core... <g>
	if(!fp || !stricmp(fp, "")) return szDefault;

	LPSTR buffer = ReadValue(fp, szString);
	if (buffer)
	{
		if (!stricmp(buffer, "true")) return true;
		else if (!stricmp(buffer, "false")) return false;
		else return szDefault;
	}

	return szDefault;
}

//===========================================================================
// Function: ReadInt
// Purpose: Searches given file for int value
// In: LPCSTR fp = File to search
// In: LPCSTR szString = Keyword to look for
// In: int szDefault = Default value to to return if not found
// Out: int = Value found
//===========================================================================

int ReadInt(LPCSTR fp, LPCSTR szString, int szDefault)
{
	return pSettings->GetInt(fp, szString, szDefault);
}

//===========================================================================
// Function: ReadString
// Purpose: Searches given file for string
// In: LPCSTR fp = File to search
// In: LPCSTR szString = String to return value of
// In: LPSTR szDefault = Pointer to value to return if not found
// Out: LPSTR = Pointer to found string
//===========================================================================

LPSTR ReadString(LPCSTR fp, LPCSTR szString, LPSTR szDefault)
{
	return pSettings->GetString(fp, szString, szDefault);
}

//===========================================================================
// Function: ReadColor
// Purpose: Read's a value in the given file and outputs the return as a COLORREF
// In: LPCSTR fp = File to search
// In: LPCSTR szString = String to return value of
// In: LPCSTR szDefault = String to parse and return if not found
// Out: COLORREF = Value returned from file (parsed)
//===========================================================================

COLORREF ReadColor(LPCSTR fp, LPCSTR szString, LPCSTR szDefault)
{
	return pSettings->GetColor(fp, szString, szDefault, NULL, false);
}

//===========================================================================
// Function: GetSettingPtr
// Purpose: Return a pointer to a setting in the core buffer
// In: int index = Setting number = SN_XXX constants in BBApi.h
// Out: Pointer to the requested setting
//===========================================================================

void* GetSettingPtr(int index)
{
	switch (index)
	{
		case SN_TOOLBAR				: return (void*) pSettings->Toolbar;
		case SN_TOOLBARBUTTON		: return (void*) pSettings->ToolbarButton;
		case SN_TOOLBARBUTTONP		: return (void*) pSettings->ToolbarButtonPressed;
		case SN_TOOLBARLABEL		: return (void*) pSettings->ToolbarLabel;
		case SN_TOOLBARWINDOWLABEL	: return (void*) pSettings->ToolbarWindowLabel;
		case SN_TOOLBARCLOCK		: return (void*) pSettings->ToolbarClock;
		case SN_MENUTITLE			: return (void*) pSettings->MenuTitle;
		case SN_MENUFRAME			: return (void*) pSettings->MenuFrame;
		case SN_MENUHILITE			: return (void*) pSettings->MenuHilite;

		case SN_BORDERWIDTH			: return (void*) &pSettings->borderWidth;
		case SN_BORDERCOLOR			: return (void*) &pSettings->borderColor;
		case SN_BEVELWIDTH			: return (void*) &pSettings->bevelWidth;

		case SN_METRICSUNIX			: return (void*) &pSettings->alternativeBullets;
		case SN_BULLETUNIX			: return (void*) &pSettings->alternativeBullets;
		case SN_ROOTCOMMAND			: return (void*) &pSettings->rootCommand;

		case SN_MENUBULLET			: return (void*) &pSettings->menuBullet;
		case SN_MENUBULLETPOS		: return (void*) &pSettings->menuBulletPosition;
		case SN_FRAMEWIDTH			: return (void*) &pSettings->frameWidth;
		case SN_HANDLEWIDTH			: return (void*) &pSettings->handleWidth;

		case SN_MENUALPHA			: return (void*) &pSettings->menuTransparencyAlpha;
		case SN_TOOLBARALPHA		: return (void*) &pSettings->toolbarTransparencyAlpha;
/*
		//=====================================
		// For compatibility with bbLeanSkin...
		// ###### WORK IN PROGRESS / TBD ######
		//=====================================

		case SN_WINFOCUS_TITLE		: return (void*) pSettings->Toolbar;
		case SN_WINFOCUS_LABEL		: return (void*) pSettings->Toolbar;
		case SN_WINFOCUS_HANDLE		: return (void*) pSettings->Toolbar;
		case SN_WINFOCUS_GRIP		: return (void*) pSettings->Toolbar;
		case SN_WINFOCUS_BUTTON		: return (void*) pSettings->Toolbar;
		case SN_WINFOCUS_BUTTONP	: return (void*) pSettings->Toolbar;

		case SN_WINUNFOCUS_TITLE	: return (void*) pSettings->Toolbar;
		case SN_WINUNFOCUS_LABEL	: return (void*) pSettings->Toolbar;
		case SN_WINUNFOCUS_HANDLE	: return (void*) pSettings->Toolbar;
		case SN_WINUNFOCUS_GRIP		: return (void*) pSettings->Toolbar;
		case SN_WINUNFOCUS_BUTTON	: return (void*) pSettings->Toolbar;

		case SN_WINFOCUS_FRAME_COLOR	: return (void*) &pSettings->borderColor;
		case SN_WINUNFOCUS_FRAME_COLOR	: return (void*) &pSettings->borderColor;
*/
		case SN_NEWMETRICS			: return (void*) false;
	}

	return NULL;
}

//===========================================================================
// Function: Log
// Purpose: Appends given line to xoblite.log file
// In: LPCSTR des = Value to identify variable
// In: LPCSTR line = Outputted line
// Out: void = NULL
//===========================================================================

void Log(LPCSTR des, LPCSTR line)
{
	char date[9], time[9];
	char tempBuf[MAX_LINE_LENGTH];
	GetBlackboxPath(tempBuf, sizeof(tempBuf));
	strcat(tempBuf, "xoblite.log");

	FILE *f = fopen(tempBuf, "a+");
	if (f)
	{
		_strdate(date);
		_strtime(time);
		fprintf(f, "%s %s - %s: %s\n", date, time, des, line);
		fclose(f);
	}
	else MBoxErrorFile(tempBuf);
}

//===========================================================================
// Function: FileRead
// Purpose: Reads a line from the specified file and returns boolean on status
// In: FILE *stream = File to parse
// In: LPSTR string = Pointer to returned line from the file
// Out: bool = Status (true if read, false if not)
//===========================================================================

bool FileRead(FILE *stream, LPSTR string)
{
	int ch,										// Character read from file
		cnbr;									// Index into returned string

	cnbr = 0;									// Start at the beginning...
	memset(string, ' ', MAX_LINE_LENGTH);		// ...and prepare the entire line

	for (;;)
	{
		ch = fgetc(stream);						// Get next character from the file...

		if (ch == '\t')							// Jump if the character is a tab...
		{
			cnbr = ((cnbr >> 3) << 3) + 8;
		}
		// If the character is end of line, end of file, or MS-DOS Ctrl-Z...
		/*else*/ if ((ch == '\n') ||  (ch == EOF) ||  (ch == 26))
		{
			string[cnbr] = '\0';				// ...terminate the string...
			return(ch == '\n' || cnbr);			// ...and return true/false
		}
		else if (cnbr < MAX_LINE_LENGTH)
		{
			// ...otherwise add the character to the output string
			string[cnbr++] = (char) ch;
		}

		// Return in any case if line is too long, the line will be cut into pieces...
		if (cnbr >= MAX_LINE_LENGTH)
		{
			string[MAX_LINE_LENGTH] = '\0';
			return true;
		}
	}
}

//===========================================================================
// Function: ReadNextCommand
// Purpose: Reads the next line of the file
// In: FILE* = File to be parsed
// In: LPSTR = buffer to assign next line to
// DWORD = maximum size of line to be outputted
// Out: bool = whether there was a next line to parse
//===========================================================================

bool ReadNextCommand(FILE *f, LPSTR szBuffer, DWORD dwLength)
{
	if (!f) return false;

	char szTempBuffer[MAX_LINE_LENGTH];

	while (FileRead(f, szTempBuffer))
	{
		while (isspace(*szTempBuffer) && *szTempBuffer) // cleaning out spaces before comments
			memmove(szTempBuffer, szTempBuffer+1, strlen(szTempBuffer));

		if (szTempBuffer[0] && szTempBuffer[0] != '#' && szTempBuffer != 0 && szTempBuffer[0] != '!')
		{
			strncpy(szBuffer, szTempBuffer, dwLength);
			return true;
		}

		if (!strcmp(szTempBuffer, "[end]"))
			strncpy(szBuffer, szTempBuffer, dwLength);
	}

	return false;
}

//===========================================================================
// Function: bbrcPath
// Purpose: Returns the handle to the blackboxrc file that is being used
// In: LPCSTR = file to set as default bbrc file (defaults to NULL if not specified)
// Out: LPCSTR = full path of file
//===========================================================================

LPCSTR bbrcPath(LPCSTR other)
{
	if (other != NULL) strcpy(pSettings->bbrcFile, other);

	else if (strlen(pSettings->bbrcFile) == 0) // We only need to do this once!
	{
		// Check for blackbox.rc in the Blackbox and $UserAppData$\Blackbox directories...
		strcpy(pSettings->bbrcFile, ConfigFileExists("blackbox.rc", NULL));
		if (!stricmp(pSettings->bbrcFile, ""))
		{
			// If not found, check for blackboxrc in the same directories...
			strcpy(pSettings->bbrcFile, ConfigFileExists("blackboxrc", NULL));
			if (!stricmp(pSettings->bbrcFile, ""))
			{
				// If still not found, create the default
				// blackbox.rc in the Blackbox directory...
				pSettings->WriteDefaultRCSettings();
				// If still not found, set the default path ->
				// blackbox.rc in the Blackbox directory...
				GetBlackboxPath(pSettings->bbrcFile, sizeof(pSettings->bbrcFile));
				strcat(pSettings->bbrcFile, "blackbox.rc");
			}
		}
	}

	return pSettings->bbrcFile;
}

//===========================================================================
// Function: menuPath
// Purpose: Returns the handle to the menu file that is being used
// In: LPCSTR = file to set as default menu file (defaults to NULL if not specified)
// Out: LPCSTR = full path of file
//===========================================================================

LPCSTR menuPath(LPCSTR other)
{
	if (other != NULL)
	{
		strcpy(pSettings->menuFile, other);
		if (strchr(pSettings->menuFile, '\"')) StrRemoveEncap(pSettings->menuFile);

		if (strchr(pSettings->menuFile, '$')) ReplaceShellFolders(pSettings->menuFile);

		// If the full path to the menu file is specified and it is in a subfolder
		// to the Blackbox folder, extract the relative path to the menu file
		// -> full path with the Blackbox folder replaced by an environment variable.
		// Example: "C:\Blackbox\menu.rc" becomes "$Blackbox$\menu.rc"
		// This way the Blackbox folder can be moved, and the menu path will still work! :D

		if (IsInString(pSettings->menuFile, pSettings->SF_blackboxPath))
		{
			char relativePath[MAX_LINE_LENGTH];
			strcpy(relativePath, "$Blackbox$");
			strcat(relativePath, &pSettings->menuFile[(strlen(pSettings->SF_blackboxPath))]);
			WriteString(bbrcPath(), "session.menuFile:", relativePath);
		}
		else if (IsInString(pSettings->menuFile, pSettings->SF_userAppData))
		{
			char relativePath[MAX_LINE_LENGTH];
			strcpy(relativePath, "$UserAppData$");
			strcat(relativePath, &pSettings->menuFile[(strlen(pSettings->SF_userAppData))]);
			WriteString(bbrcPath(), "session.menuFile:", relativePath);
		}
		else WriteString(bbrcPath(), "session.menuFile:", pSettings->menuFile);
	}

	return pSettings->menuFile;
}

//===========================================================================
// Function: plugrcPath
// Purpose: Returns the handle to the plugins rc file that is being used
// In: LPCSTR = file to set as default plugins rc file (defaults to NULL if not specified)
// Out: LPCSTR = full path of file
//===========================================================================

LPCSTR plugrcPath(LPCSTR other)
{
	if (other != NULL)
	{
		strcpy(pSettings->plugrcFile, other);
		if (strchr(pSettings->plugrcFile, '\"')) StrRemoveEncap(pSettings->plugrcFile);
	}

	else if (strlen(pSettings->plugrcFile) == 0) // We only need to do this once!
	{
		// Check for plugins.rc in the Blackbox and $UserAppData$\Blackbox directories...
		strcpy(pSettings->plugrcFile, ConfigFileExists("plugins.rc", NULL));
		if (!stricmp(pSettings->plugrcFile, ""))
		{
			// If not found, check for pluginsrc in the same directories...
			strcpy(pSettings->plugrcFile, ConfigFileExists("pluginsrc", NULL));
			if (!stricmp(pSettings->plugrcFile, ""))
			{
				// If still not found, set the default path ->
				// plugins.rc in the Blackbox directory...
				GetBlackboxPath(pSettings->plugrcFile, sizeof(pSettings->plugrcFile));
				strcat(pSettings->plugrcFile, "plugins.rc");
			}
		}
	}

	return pSettings->plugrcFile;
}

//===========================================================================
// Function: extensionsrcPath
// Purpose: Returns the handle to the extensions rc file that is being used
// In: LPCSTR = file to set as default extensions rc file (defaults to NULL if not specified)
// Out: LPCSTR = full path of file
//===========================================================================

LPCSTR extensionsrcPath(LPCSTR other)
{
	if (other != NULL)
	{
		strcpy(pSettings->extrcFile, other);
		if (strchr(pSettings->extrcFile, '\"')) StrRemoveEncap(pSettings->extrcFile);
	}

	else if (strlen(pSettings->extrcFile) == 0) // We only need to do this once!
	{
		// Check for extensions.rc in the Blackbox and $UserAppData$\Blackbox directories...
		strcpy(pSettings->extrcFile, ConfigFileExists("extensions.rc", NULL));
		if (!stricmp(pSettings->extrcFile, ""))
		{
			// If not found, check for extensionsrc in the same directories...
			strcpy(pSettings->extrcFile, ConfigFileExists("extensionsrc", NULL));
			if (!stricmp(pSettings->extrcFile, ""))
			{
				// If still not found, set the default path ->
				// extensions.rc in the Blackbox directory...
				GetBlackboxPath(pSettings->extrcFile, sizeof(pSettings->extrcFile));
				strcat(pSettings->extrcFile, "extensions.rc");
			}
		}
	}

	return pSettings->extrcFile;
}

//===========================================================================
// Function: stylePath
// Purpose: Returns the handle to the style file that is being used
// In: LPCSTR = file to set as default style file (defaults to NULL if not specified)
// Out: LPCSTR = full path of file
//===========================================================================

LPCSTR stylePath(LPCSTR other)
{
	if (other != NULL)
	{
		strcpy(pSettings->styleFile, other);
		if (strchr(pSettings->styleFile, '\"')) StrRemoveEncap(pSettings->styleFile);

		if (strchr(pSettings->styleFile, '$')) ReplaceShellFolders(pSettings->styleFile);

		// If the full path to the style is specified and it is in a subfolder
		// to the Blackbox folder, extract the relative path to the style
		// -> full path with the Blackbox folder replaced by an environment variable.
		// Example: "C:\Blackbox\styles\charcoal" becomes "$Blackbox$\styles\charcoal"
		// This way the Blackbox folder can be moved, and the style path will still work! :D

		if (!pSettings->usingDefaultTheme && IsInString(pSettings->styleFile, pSettings->SF_currentThemePath))
		{
			char relativePath[MAX_LINE_LENGTH];
			strcpy(relativePath, "$CurrentTheme$");
			strcat(relativePath, &pSettings->styleFile[(strlen(pSettings->SF_currentThemePath))]);
			WriteString(bbrcPath(), "session.styleFile:", relativePath);
		}
		else if (IsInString(pSettings->styleFile, pSettings->SF_blackboxPath))
		{
			char relativePath[MAX_LINE_LENGTH];
			strcpy(relativePath, "$Blackbox$");
			strcat(relativePath, &pSettings->styleFile[(strlen(pSettings->SF_blackboxPath))]);
			WriteString(bbrcPath(), "session.styleFile:", relativePath);
		}
		else WriteString(bbrcPath(), "session.styleFile:", pSettings->styleFile);
	}

	return pSettings->styleFile;
}

//===========================================================================
// Function: ConfigFileExists
// Purpose: Look for a config file in the Blackbox, UserAppData and
//          (optionally) plugin directories
// In: LPCSTR = filename to look for, LPCSTR = plugin directory or NULL
// Out: LPCSTR = full path to file, or "" if not found
//===========================================================================

LPCSTR ConfigFileExists(LPCSTR filename, LPCSTR pluginDir)
{
	// First we look for the file in the $UserAppData$\Blackbox directory,
	// C:\Documents and Settings\username\Application Data\Blackbox
	strcpy(tempBuf, pSettings->SF_userAppData);
	strcat(tempBuf, "\\Blackbox\\");
	strcat(tempBuf, filename);
	if (!FileExists(tempBuf))
	{
		// If not found, and if pluginDir is specified,
		// we look for the file in this directory...
		if (pluginDir != NULL)
		{
			strcpy(tempBuf, pluginDir);
			strcat(tempBuf, filename);
			if (FileExists(tempBuf)) return tempBuf;
		}

		// Finally, we look for the file in the Blackbox directory...
		GetBlackboxPath(tempBuf, sizeof(tempBuf));
		strcat(tempBuf, filename);
		if (!FileExists(tempBuf)) strcpy(tempBuf, "");
	}

	return tempBuf;
}

//===========================================================================
// Function: FileExists
// Purpose: Checks if a file exists
// In: LPCSTR = file to look for
// Out: bool = whether found or not
//===========================================================================

bool FileExists(LPCSTR szFileName)
{
	HANDLE handle;
	WIN32_FIND_DATA findData;

	handle = FindFirstFile(szFileName, &findData);
	if (handle != INVALID_HANDLE_VALUE)
	{
		FindClose(handle);
		if (!(findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
			return true;
	}

	return false;
}

//===========================================================================
// Function: FolderExists
// Purpose: Checks if a folder exists
// In: LPCSTR = Folder to look for
// Out: bool = Whether found or not
//===========================================================================

bool FolderExists(LPCSTR szFolderPath)
{
	HANDLE handle;
	WIN32_FIND_DATA findData;

	handle = FindFirstFile(szFolderPath, &findData);
	if (handle != INVALID_HANDLE_VALUE)
	{
		FindClose(handle);
		if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
			return true;
	}

	return false;
}

//===========================================================================
// Function: GetBlackboxEditor
// Purpose: Returns the path to the preferred editor as defined in ext.rc
// In: LPSTR = string to copy path to
// Out: void
//===========================================================================

void GetBlackboxEditor(LPSTR editor)
{
	strcpy(editor, pSettings->preferredEditor);
}

//===========================================================================
// Function: FileOpen
// Purpose: Opens file for parsing
// In: LPCSTR = file to open
// Out: FILE* = the file itself
//===========================================================================

FILE *FileOpen(LPCSTR szPath)
{
	FILE *file = NULL;
	if (FileExists(szPath)) file = fopen(szPath, "r");

	return (file);
}

//===========================================================================
// Function: ParseLiteralColor
// Purpose: Parses a given literal colour and returns the hex value
// In: LPCSTR = color to parse (eg. "black", "white")
// Out: COLORREF (DWORD) of rgb value
// (old)Out: LPCSTR = literal hex value
//===========================================================================

COLORREF ParseLiteralColor(LPCSTR colour)
{
	int i = strchr(colour, ' ') - colour;
	while (i > 0)
	{
		memmove ( (VOID*) (colour + i), (VOID*) (colour + i + 1), strlen (colour) - i );
		i = strchr(colour, ' ') - colour;
	}

	//====================

	if (!strnicmp(colour, "gray", 4) || !strnicmp(colour, "grey", 4))
	{
		i = strlen(colour);
		if (i > 5)
		{
			float tmp = (float)(2.55 * atoi(colour+4));
			i = (int)(tmp + .5);
			if (i < (int)tmp) i = (int)tmp;
			return RGB(i,i,i);
		}
		else return RGB(80,80,80);
	}

	//====================

	if (colour[0] == 'a') //====================
	{
		if (!stricmp(colour, "aliceblue")) return RGB(240,248,255);
		else if (!stricmp(colour, "antiquewhite")) return RGB(250,235,215);
		else if (!stricmp(colour, "aquamarine")) return RGB(127,255,212);
		else if (!stricmp(colour, "azure")) return RGB(240,255,255);
/*
		else if (!stricmp(colour, "antiquewhite1")) return RGB(255,239,219);
		else if (!stricmp(colour, "antiquewhite2")) return RGB(238,223,204);
		else if (!stricmp(colour, "antiquewhite3")) return RGB(205,192,176);
		else if (!stricmp(colour, "antiquewhite4")) return RGB(139,131,120);
		else if (!stricmp(colour, "aquamarine1")) return RGB(127,255,212);
		else if (!stricmp(colour, "aquamarine2")) return RGB(118,238,198);
		else if (!stricmp(colour, "aquamarine3")) return RGB(102,205,170);
		else if (!stricmp(colour, "aquamarine4")) return RGB(69,139,116);
		else if (!stricmp(colour, "azure1")) return RGB(240,255,255);
		else if (!stricmp(colour, "azure2")) return RGB(224,238,238);
		else if (!stricmp(colour, "azure3")) return RGB(193,205,205);
		else if (!stricmp(colour, "azure4")) return RGB(131,139,139);
*/
	}
	else if (colour[0] == 'b') //====================
	{
		if (!stricmp(colour, "beige")) return RGB(245,245,220);
		else if (!stricmp(colour, "bisque")) return RGB(255,228,196);
		else if (!stricmp(colour, "black")) return RGB(0,0,0);
		else if (!stricmp(colour, "blanchedalmond")) return RGB(255,235,205);
		else if (!stricmp(colour, "blue")) return RGB(0,0,255);
		else if (!stricmp(colour, "blueviolet")) return RGB(138,43,226);
		else if (!stricmp(colour, "brown")) return RGB(165,42,42);
		else if (!stricmp(colour, "burlywood")) return RGB(222,184,135);
/*
		else if (!stricmp(colour, "bisque1")) return RGB(255,228,196);
		else if (!stricmp(colour, "bisque2")) return RGB(238,213,183);
		else if (!stricmp(colour, "bisque3")) return RGB(205,183,158);
		else if (!stricmp(colour, "bisque4")) return RGB(139,125,107);
		else if (!stricmp(colour, "blue1")) return RGB(0,0,255);
		else if (!stricmp(colour, "blue2")) return RGB(0,0,238);
		else if (!stricmp(colour, "blue3")) return RGB(0,0,205);
		else if (!stricmp(colour, "blue4")) return RGB(0,0,139);
		else if (!stricmp(colour, "brown1")) return RGB(255,64,64);
		else if (!stricmp(colour, "brown2")) return RGB(238,59,59);
		else if (!stricmp(colour, "brown3")) return RGB(205,51,51);
		else if (!stricmp(colour, "brown4")) return RGB(139,35,35);
		else if (!stricmp(colour, "burlywood1")) return RGB(255,211,155);
		else if (!stricmp(colour, "burlywood2")) return RGB(238,197,145);
		else if (!stricmp(colour, "burlywood3")) return RGB(205,170,125);
		else if (!stricmp(colour, "burlywood4")) return RGB(139,115,85);
*/
	}
	else if (colour[0] == 'c') //====================
	{
		if (!stricmp(colour, "cadetblue")) return RGB(95,158,160);
		else if (!stricmp(colour, "chartreuse")) return RGB(127,255,0);
		else if (!stricmp(colour, "chocolate")) return RGB(210,105,30);
		else if (!stricmp(colour, "coral")) return RGB(255,127,80);
		else if (!stricmp(colour, "cornflowerblue")) return RGB(100,149,237);
		else if (!stricmp(colour, "cornsilk")) return RGB(255,248,220);
		else if (!stricmp(colour, "cyan")) return RGB(0,255,255);
/*
		else if (!stricmp(colour, "cadetblue1")) return RGB(152,245,255);
		else if (!stricmp(colour, "cadetblue2")) return RGB(142,229,238);
		else if (!stricmp(colour, "cadetblue3")) return RGB(122,197,205);
		else if (!stricmp(colour, "cadetblue4")) return RGB(83,134,139);
		else if (!stricmp(colour, "chartreuse1")) return RGB(127,255,0);
		else if (!stricmp(colour, "chartreuse2")) return RGB(118,238,0);
		else if (!stricmp(colour, "chartreuse3")) return RGB(102,205,0);
		else if (!stricmp(colour, "chartreuse4")) return RGB(69,139,0);
		else if (!stricmp(colour, "chocolate1")) return RGB(255,127,36);
		else if (!stricmp(colour, "chocolate2")) return RGB(238,118,33);
		else if (!stricmp(colour, "chocolate3")) return RGB(205,102,29);
		else if (!stricmp(colour, "chocolate4")) return RGB(139,69,19);
		else if (!stricmp(colour, "coral1")) return RGB(255,114,86);
		else if (!stricmp(colour, "coral2")) return RGB(238,106,80);
		else if (!stricmp(colour, "coral3")) return RGB(205,91,69);
		else if (!stricmp(colour, "coral4")) return RGB(139,62,47);
		else if (!stricmp(colour, "cornsilk1")) return RGB(255,248,220);
		else if (!stricmp(colour, "cornsilk2")) return RGB(238,232,205);
		else if (!stricmp(colour, "cornsilk3")) return RGB(205,200,177);
		else if (!stricmp(colour, "cornsilk4")) return RGB(139,136,120);
		else if (!stricmp(colour, "cyan1")) return RGB(0,255,255);
		else if (!stricmp(colour, "cyan2")) return RGB(0,238,238);
		else if (!stricmp(colour, "cyan3")) return RGB(0,205,205);
		else if (!stricmp(colour, "cyan4")) return RGB(0,139,139);
*/
	}
	else if (colour[0] == 'd') //====================
	{
		if (!stricmp(colour, "darkblue")) return RGB(0,0,139);
		else if (!stricmp(colour, "darkcyan")) return RGB(0,139,139);
		else if (!stricmp(colour, "darkgoldenrod")) return RGB(184,134,11);
		else if (!stricmp(colour, "darkgray")) return RGB(169,169,169);
		else if (!stricmp(colour, "darkgreen")) return RGB(0,100,0);
		else if (!stricmp(colour, "darkgrey")) return RGB(169,169,169);
		else if (!stricmp(colour, "darkkhaki")) return RGB(189,183,107);
		else if (!stricmp(colour, "darkmagenta")) return RGB(139,0,139);
		else if (!stricmp(colour, "darkolivegreen")) return RGB(85,107,47);
		else if (!stricmp(colour, "darkorange")) return RGB(255,140,0);
		else if (!stricmp(colour, "darkorchid")) return RGB(153,50,204);
		else if (!stricmp(colour, "darkred")) return RGB(139,0,0);
		else if (!stricmp(colour, "darksalmon")) return RGB(233,150,122);
		else if (!stricmp(colour, "darkseagreen")) return RGB(143,188,143);
		else if (!stricmp(colour, "darkslateblue")) return RGB(72,61,139);
		else if (!stricmp(colour, "darkslategray")) return RGB(47,79,79);
		else if (!stricmp(colour, "darkslategrey")) return RGB(47,79,79);
		else if (!stricmp(colour, "darkturquoise")) return RGB(0,206,209);
		else if (!stricmp(colour, "darkviolet")) return RGB(148,0,211);
		else if (!stricmp(colour, "deeppink")) return RGB(255,20,147);
		else if (!stricmp(colour, "deepskyblue")) return RGB(0,191,255);
		else if (!stricmp(colour, "dimgray")) return RGB(105,105,105);
		else if (!stricmp(colour, "dimgrey")) return RGB(105,105,105);
		else if (!stricmp(colour, "dodgerblue")) return RGB(30,144,255);
/*
		else if (!stricmp(colour, "darkgoldenrod1")) return RGB(255,185,15);
		else if (!stricmp(colour, "darkgoldenrod2")) return RGB(238,173,14);
		else if (!stricmp(colour, "darkgoldenrod3")) return RGB(205,149,12);
		else if (!stricmp(colour, "darkgoldenrod4")) return RGB(139,101,8);
		else if (!stricmp(colour, "darkolivegreen1")) return RGB(202,255,112);
		else if (!stricmp(colour, "darkolivegreen2")) return RGB(188,238,104);
		else if (!stricmp(colour, "darkolivegreen3")) return RGB(162,205,90);
		else if (!stricmp(colour, "darkolivegreen4")) return RGB(110,139,61);
		else if (!stricmp(colour, "darkorange1")) return RGB(255,127,0);
		else if (!stricmp(colour, "darkorange2")) return RGB(238,118,0);
		else if (!stricmp(colour, "darkorange3")) return RGB(205,102,0);
		else if (!stricmp(colour, "darkorange4")) return RGB(139,69,0);
		else if (!stricmp(colour, "darkorchid1")) return RGB(191,62,255);
		else if (!stricmp(colour, "darkorchid2")) return RGB(178,58,238);
		else if (!stricmp(colour, "darkorchid3")) return RGB(154,50,205);
		else if (!stricmp(colour, "darkorchid4")) return RGB(104,34,139);
		else if (!stricmp(colour, "darkseagreen1")) return RGB(193,255,193);
		else if (!stricmp(colour, "darkseagreen2")) return RGB(180,238,180);
		else if (!stricmp(colour, "darkseagreen3")) return RGB(155,205,155);
		else if (!stricmp(colour, "darkseagreen4")) return RGB(105,139,105);
		else if (!stricmp(colour, "deeppink1")) return RGB(255,20,147);
		else if (!stricmp(colour, "deeppink2")) return RGB(238,18,137);
		else if (!stricmp(colour, "deeppink3")) return RGB(205,16,118);
		else if (!stricmp(colour, "deeppink4")) return RGB(139,10,80);
		else if (!stricmp(colour, "deepskyblue1")) return RGB(0,191,255);
		else if (!stricmp(colour, "deepskyblue2")) return RGB(0,178,238);
		else if (!stricmp(colour, "deepskyblue3")) return RGB(0,154,205);
		else if (!stricmp(colour, "deepskyblue4")) return RGB(0,104,139);
		else if (!stricmp(colour, "darkslategray1")) return RGB(151,255,255);
		else if (!stricmp(colour, "darkslategray2")) return RGB(141,238,238);
		else if (!stricmp(colour, "darkslategray3")) return RGB(121,205,205);
		else if (!stricmp(colour, "darkslategray4")) return RGB(82,139,139);
		else if (!stricmp(colour, "dodgerblue1")) return RGB(30,144,255);
		else if (!stricmp(colour, "dodgerblue2")) return RGB(28,134,238);
		else if (!stricmp(colour, "dodgerblue3")) return RGB(24,116,205);
		else if (!stricmp(colour, "dodgerblue4")) return RGB(16,78,139);
*/
	}
	else if (colour[0] == 'f') //====================
	{
		if (!stricmp(colour, "firebrick")) return RGB(178,34,34);
		else if (!stricmp(colour, "floralwhite")) return RGB(255,250,240);
		else if (!stricmp(colour, "forestgreen")) return RGB(34,139,34);
/*
		else if (!stricmp(colour, "firebrick1")) return RGB(255,48,48);
		else if (!stricmp(colour, "firebrick2")) return RGB(238,44,44);
		else if (!stricmp(colour, "firebrick3")) return RGB(205,38,38);
		else if (!stricmp(colour, "firebrick4")) return RGB(139,26,26);
*/
	}
	else if (colour[0] == 'g') //====================
	{
		if (!stricmp(colour, "gainsboro")) return RGB(220,220,220);
		else if (!stricmp(colour, "ghostwhite")) return RGB(248,248,255);
		else if (!stricmp(colour, "gold")) return RGB(255,215,0);
		else if (!stricmp(colour, "goldenrod")) return RGB(218,165,32);
		else if (!stricmp(colour, "gray")) return RGB(190,190,190);
		else if (!stricmp(colour, "green")) return RGB(0,255,0);
		else if (!stricmp(colour, "greenyellow")) return RGB(173,255,47);
		else if (!stricmp(colour, "grey")) return RGB(190,190,190);
/*
		else if (!stricmp(colour, "gold1")) return RGB(255,215,0);
		else if (!stricmp(colour, "gold2")) return RGB(238,201,0);
		else if (!stricmp(colour, "gold3")) return RGB(205,173,0);
		else if (!stricmp(colour, "gold4")) return RGB(139,117,0);
		else if (!stricmp(colour, "goldenrod1")) return RGB(255,193,37);
		else if (!stricmp(colour, "goldenrod2")) return RGB(238,180,34);
		else if (!stricmp(colour, "goldenrod3")) return RGB(205,155,29);
		else if (!stricmp(colour, "goldenrod4")) return RGB(139,105,20);
		else if (!stricmp(colour, "green1")) return RGB(0,255,0);
		else if (!stricmp(colour, "green2")) return RGB(0,238,0);
		else if (!stricmp(colour, "green3")) return RGB(0,205,0);
		else if (!stricmp(colour, "green4")) return RGB(0,139,0);
*/
	}
	else if (colour[0] == 'h') //====================
	{
		if (!stricmp(colour, "honeydew")) return RGB(240,255,240);
		else if (!stricmp(colour, "hotpink")) return RGB(255,105,180);
/*
		else if (!stricmp(colour, "honeydew1")) return RGB(240,255,240);
		else if (!stricmp(colour, "honeydew2")) return RGB(224,238,224);
		else if (!stricmp(colour, "honeydew3")) return RGB(193,205,193);
		else if (!stricmp(colour, "honeydew4")) return RGB(131,139,131);
		else if (!stricmp(colour, "hotpink1")) return RGB(255,110,180);
		else if (!stricmp(colour, "hotpink2")) return RGB(238,106,167);
		else if (!stricmp(colour, "hotpink3")) return RGB(205,96,144);
		else if (!stricmp(colour, "hotpink4")) return RGB(139,58,98);
*/
	}
	else if (colour[0] == 'i') //====================
	{
		if (!stricmp(colour, "indianred")) return RGB(205,92,92);
		else if (!stricmp(colour, "ivory")) return RGB(255,255,240);
/*
		else if (!stricmp(colour, "indianred1")) return RGB(255,106,106);
		else if (!stricmp(colour, "indianred2")) return RGB(238,99,99);
		else if (!stricmp(colour, "indianred3")) return RGB(205,85,85);
		else if (!stricmp(colour, "indianred4")) return RGB(139,58,58);
		else if (!stricmp(colour, "ivory1")) return RGB(255,255,240);
		else if (!stricmp(colour, "ivory2")) return RGB(238,238,224);
		else if (!stricmp(colour, "ivory3")) return RGB(205,205,193);
		else if (!stricmp(colour, "ivory4")) return RGB(139,139,131);
*/
	}
	else if (colour[0] == 'k') //====================
	{
		if (!stricmp(colour, "khaki")) return RGB(240,230,140);
/*
		else if (!stricmp(colour, "khaki1")) return RGB(255,246,143);
		else if (!stricmp(colour, "khaki2")) return RGB(238,230,133);
		else if (!stricmp(colour, "khaki3")) return RGB(205,198,115);
		else if (!stricmp(colour, "khaki4")) return RGB(139,134,78);
*/
	}
	else if (colour[0] == 'l') //====================
	{
		if (!stricmp(colour, "lavender")) return RGB(230,230,250);
		else if (!stricmp(colour, "lavenderblush")) return RGB(255,240,245);
		else if (!stricmp(colour, "lawngreen")) return RGB(124,252,0);
		else if (!stricmp(colour, "lemonchiffon")) return RGB(255,250,205);
		else if (!stricmp(colour, "lightblue")) return RGB(173,216,230);
		else if (!stricmp(colour, "lightcoral")) return RGB(240,128,128);
		else if (!stricmp(colour, "lightcyan")) return RGB(224,255,255);
		else if (!stricmp(colour, "lightgoldenrod")) return RGB(238,221,130);
		else if (!stricmp(colour, "lightgoldenrodyellow")) return RGB(250,250,210);
		else if (!stricmp(colour, "lightgray")) return RGB(211,211,211);
		else if (!stricmp(colour, "lightgreen")) return RGB(144,238,144);
		else if (!stricmp(colour, "lightgrey")) return RGB(211,211,211);
		else if (!stricmp(colour, "lightpink")) return RGB(255,182,193);
		else if (!stricmp(colour, "lightsalmon")) return RGB(255,160,122);
		else if (!stricmp(colour, "lightseagreen")) return RGB(32,178,170);
		else if (!stricmp(colour, "lightskyblue")) return RGB(135,206,250);
		else if (!stricmp(colour, "lightslateblue")) return RGB(132,112,255);
		else if (!stricmp(colour, "lightslategray")) return RGB(119,136,153);
		else if (!stricmp(colour, "lightslategrey")) return RGB(119,136,153);
		else if (!stricmp(colour, "lightsteelblue")) return RGB(176,196,222);
		else if (!stricmp(colour, "lightyellow")) return RGB(255,255,224);
		else if (!stricmp(colour, "limegreen")) return RGB(50,205,50);
		else if (!stricmp(colour, "linen")) return RGB(250,240,230);
/*
		else if (!stricmp(colour, "lavenderblush1")) return RGB(255,240,245);
		else if (!stricmp(colour, "lavenderblush2")) return RGB(238,224,229);
		else if (!stricmp(colour, "lavenderblush3")) return RGB(205,193,197);
		else if (!stricmp(colour, "lavenderblush4")) return RGB(139,131,134);
		else if (!stricmp(colour, "lemonchiffon1")) return RGB(255,250,205);
		else if (!stricmp(colour, "lemonchiffon2")) return RGB(238,233,191);
		else if (!stricmp(colour, "lemonchiffon3")) return RGB(205,201,165);
		else if (!stricmp(colour, "lemonchiffon4")) return RGB(139,137,112);
		else if (!stricmp(colour, "lightblue1")) return RGB(191,239,255);
		else if (!stricmp(colour, "lightblue2")) return RGB(178,223,238);
		else if (!stricmp(colour, "lightblue3")) return RGB(154,192,205);
		else if (!stricmp(colour, "lightblue4")) return RGB(104,131,139);
		else if (!stricmp(colour, "lightcyan1")) return RGB(224,255,255);
		else if (!stricmp(colour, "lightcyan2")) return RGB(209,238,238);
		else if (!stricmp(colour, "lightcyan3")) return RGB(180,205,205);
		else if (!stricmp(colour, "lightcyan4")) return RGB(122,139,139);
		else if (!stricmp(colour, "lightgoldenrod1")) return RGB(255,236,139);
		else if (!stricmp(colour, "lightgoldenrod2")) return RGB(238,220,130);
		else if (!stricmp(colour, "lightgoldenrod3")) return RGB(205,190,112);
		else if (!stricmp(colour, "lightgoldenrod4")) return RGB(139,129,76);
		else if (!stricmp(colour, "lightpink1")) return RGB(255,174,185);
		else if (!stricmp(colour, "lightpink2")) return RGB(238,162,173);
		else if (!stricmp(colour, "lightpink3")) return RGB(205,140,149);
		else if (!stricmp(colour, "lightpink4")) return RGB(139,95,101);
		else if (!stricmp(colour, "lightsalmon1")) return RGB(255,160,122);
		else if (!stricmp(colour, "lightsalmon2")) return RGB(238,149,114);
		else if (!stricmp(colour, "lightsalmon3")) return RGB(205,129,98);
		else if (!stricmp(colour, "lightsalmon4")) return RGB(139,87,66);
		else if (!stricmp(colour, "lightskyblue1")) return RGB(176,226,255);
		else if (!stricmp(colour, "lightskyblue2")) return RGB(164,211,238);
		else if (!stricmp(colour, "lightskyblue3")) return RGB(141,182,205);
		else if (!stricmp(colour, "lightskyblue4")) return RGB(96,123,139);
		else if (!stricmp(colour, "lightsteelblue1")) return RGB(202,225,255);
		else if (!stricmp(colour, "lightsteelblue2")) return RGB(188,210,238);
		else if (!stricmp(colour, "lightsteelblue3")) return RGB(162,181,205);
		else if (!stricmp(colour, "lightsteelblue4")) return RGB(110,123,139);
		else if (!stricmp(colour, "lightyellow1")) return RGB(255,255,224);
		else if (!stricmp(colour, "lightyellow2")) return RGB(238,238,209);
		else if (!stricmp(colour, "lightyellow3")) return RGB(205,205,180);
		else if (!stricmp(colour, "lightyellow4")) return RGB(139,139,122);
*/
	}
	else if (colour[0] == 'm') //====================
	{
		if (!stricmp(colour, "magenta")) return RGB(255,0,255);
		else if (!stricmp(colour, "maroon")) return RGB(176,48,96);
		else if (!stricmp(colour, "mediumaquamarine")) return RGB(102,205,170);
		else if (!stricmp(colour, "mediumblue")) return RGB(0,0,205);
		else if (!stricmp(colour, "mediumorchid")) return RGB(186,85,211);
		else if (!stricmp(colour, "mediumpurple")) return RGB(147,112,219);
		else if (!stricmp(colour, "mediumseagreen")) return RGB(60,179,113);
		else if (!stricmp(colour, "mediumslateblue")) return RGB(123,104,238);
		else if (!stricmp(colour, "mediumspringgreen")) return RGB(0,250,154);
		else if (!stricmp(colour, "mediumturquoise")) return RGB(72,209,204);
		else if (!stricmp(colour, "mediumvioletred")) return RGB(199,21,133);
		else if (!stricmp(colour, "midnightblue")) return RGB(25,25,112);
		else if (!stricmp(colour, "mintcream")) return RGB(245,255,250);
		else if (!stricmp(colour, "mistyrose")) return RGB(255,228,225);
		else if (!stricmp(colour, "moccasin")) return RGB(255,228,181);
/*
		else if (!stricmp(colour, "magenta1")) return RGB(255,0,255);
		else if (!stricmp(colour, "magenta2")) return RGB(238,0,238);
		else if (!stricmp(colour, "magenta3")) return RGB(205,0,205);
		else if (!stricmp(colour, "magenta4")) return RGB(139,0,139);
		else if (!stricmp(colour, "maroon1")) return RGB(255,52,179);
		else if (!stricmp(colour, "maroon2")) return RGB(238,48,167);
		else if (!stricmp(colour, "maroon3")) return RGB(205,41,144);
		else if (!stricmp(colour, "maroon4")) return RGB(139,28,98);
		else if (!stricmp(colour, "mediumorchid1")) return RGB(224,102,255);
		else if (!stricmp(colour, "mediumorchid2")) return RGB(209,95,238);
		else if (!stricmp(colour, "mediumorchid3")) return RGB(180,82,205);
		else if (!stricmp(colour, "mediumorchid4")) return RGB(122,55,139);
		else if (!stricmp(colour, "mediumpurple1")) return RGB(171,130,255);
		else if (!stricmp(colour, "mediumpurple2")) return RGB(159,121,238);
		else if (!stricmp(colour, "mediumpurple3")) return RGB(137,104,205);
		else if (!stricmp(colour, "mediumpurple4")) return RGB(93,71,139);
		else if (!stricmp(colour, "mistyrose1")) return RGB(255,228,225);
		else if (!stricmp(colour, "mistyrose2")) return RGB(238,213,210);
		else if (!stricmp(colour, "mistyrose3")) return RGB(205,183,181);
		else if (!stricmp(colour, "mistyrose4")) return RGB(139,125,123);
*/
	}
	else if (colour[0] == 'n') //====================
	{
		if (!stricmp(colour, "navajowhite")) return RGB(255,222,173);
		else if (!stricmp(colour, "navy")) return RGB(0,0,128);
		else if (!stricmp(colour, "navyblue")) return RGB(0,0,128);
/*
		else if (!stricmp(colour, "navajowhite1")) return RGB(255,222,173);
		else if (!stricmp(colour, "navajowhite2")) return RGB(238,207,161);
		else if (!stricmp(colour, "navajowhite3")) return RGB(205,179,139);
		else if (!stricmp(colour, "navajowhite4")) return RGB(139,121,94);
*/
	}
	else if (colour[0] == 'o') //====================
	{
		if (!stricmp(colour, "oldlace")) return RGB(253,245,230);
		else if (!stricmp(colour, "olivedrab")) return RGB(107,142,35);
		else if (!stricmp(colour, "orange")) return RGB(255,165,0);
		else if (!stricmp(colour, "orangered")) return RGB(255,69,0);
		else if (!stricmp(colour, "orchid")) return RGB(218,112,214);
/*
		else if (!stricmp(colour, "olivedrab1")) return RGB(192,255,62);
		else if (!stricmp(colour, "olivedrab2")) return RGB(179,238,58);
		else if (!stricmp(colour, "olivedrab3")) return RGB(154,205,50);
		else if (!stricmp(colour, "olivedrab4")) return RGB(105,139,34);
		else if (!stricmp(colour, "orange1")) return RGB(255,165,0);
		else if (!stricmp(colour, "orange2")) return RGB(238,154,0);
		else if (!stricmp(colour, "orange3")) return RGB(205,133,0);
		else if (!stricmp(colour, "orange4")) return RGB(139,90,0);
		else if (!stricmp(colour, "orangered1")) return RGB(255,69,0);
		else if (!stricmp(colour, "orangered2")) return RGB(238,64,0);
		else if (!stricmp(colour, "orangered3")) return RGB(205,55,0);
		else if (!stricmp(colour, "orangered4")) return RGB(139,37,0);
		else if (!stricmp(colour, "orchid1")) return RGB(255,131,250);
		else if (!stricmp(colour, "orchid2")) return RGB(238,122,233);
		else if (!stricmp(colour, "orchid3")) return RGB(205,105,201);
		else if (!stricmp(colour, "orchid4")) return RGB(139,71,137);
*/
	}
	else if (colour[0] == 'p') //====================
	{
		if (!stricmp(colour, "palegoldenrod")) return RGB(238,232,170);
		else if (!stricmp(colour, "palegreen")) return RGB(152,251,152);
		else if (!stricmp(colour, "paleturquoise")) return RGB(175,238,238);
		else if (!stricmp(colour, "palevioletred")) return RGB(219,112,147);
		else if (!stricmp(colour, "papayawhip")) return RGB(255,239,213);
		else if (!stricmp(colour, "peachpuff")) return RGB(255,218,185);
		else if (!stricmp(colour, "peru")) return RGB(205,133,63);
		else if (!stricmp(colour, "pink")) return RGB(255,192,203);
		else if (!stricmp(colour, "plum")) return RGB(221,160,221);
		else if (!stricmp(colour, "powderblue")) return RGB(176,224,230);
		else if (!stricmp(colour, "purple")) return RGB(160,32,240);
/*
		else if (!stricmp(colour, "palegreen1")) return RGB(154,255,154);
		else if (!stricmp(colour, "palegreen2")) return RGB(144,238,144);
		else if (!stricmp(colour, "palegreen3")) return RGB(124,205,124);
		else if (!stricmp(colour, "palegreen4")) return RGB(84,139,84);
		else if (!stricmp(colour, "palevioletred1")) return RGB(255,130,171);
		else if (!stricmp(colour, "palevioletred2")) return RGB(238,121,159);
		else if (!stricmp(colour, "palevioletred3")) return RGB(205,104,137);
		else if (!stricmp(colour, "palevioletred4")) return RGB(139,71,93);
		else if (!stricmp(colour, "paleturquoise1")) return RGB(187,255,255);
		else if (!stricmp(colour, "paleturquoise2")) return RGB(174,238,238);
		else if (!stricmp(colour, "paleturquoise3")) return RGB(150,205,205);
		else if (!stricmp(colour, "paleturquoise4")) return RGB(102,139,139);
		else if (!stricmp(colour, "peachpuff1")) return RGB(255,218,185);
		else if (!stricmp(colour, "peachpuff2")) return RGB(238,203,173);
		else if (!stricmp(colour, "peachpuff3")) return RGB(205,175,149);
		else if (!stricmp(colour, "peachpuff4")) return RGB(139,119,101);
		else if (!stricmp(colour, "pink1")) return RGB(255,181,197);
		else if (!stricmp(colour, "pink2")) return RGB(238,169,184);
		else if (!stricmp(colour, "pink3")) return RGB(205,145,158);
		else if (!stricmp(colour, "pink4")) return RGB(139,99,108);
		else if (!stricmp(colour, "plum1")) return RGB(255,187,255);
		else if (!stricmp(colour, "plum2")) return RGB(238,174,238);
		else if (!stricmp(colour, "plum3")) return RGB(205,150,205);
		else if (!stricmp(colour, "plum4")) return RGB(139,102,139);
		else if (!stricmp(colour, "purple1")) return RGB(155,48,255);
		else if (!stricmp(colour, "purple2")) return RGB(145,44,238);
		else if (!stricmp(colour, "purple3")) return RGB(125,38,205);
		else if (!stricmp(colour, "purple4")) return RGB(85,26,139);
*/
	}
	else if (colour[0] == 'r') //====================
	{
		if (!stricmp(colour, "red")) return RGB(255,0,0);
		else if (!stricmp(colour, "rosybrown")) return RGB(188,143,143);
		else if (!stricmp(colour, "royalblue")) return RGB(65,105,225);
/*
		else if (!stricmp(colour, "red1")) return RGB(255,0,0);
		else if (!stricmp(colour, "red2")) return RGB(238,0,0);
		else if (!stricmp(colour, "red3")) return RGB(205,0,0);
		else if (!stricmp(colour, "red4")) return RGB(139,0,0);
		else if (!stricmp(colour, "rosybrown1")) return RGB(255,193,193);
		else if (!stricmp(colour, "rosybrown2")) return RGB(238,180,180);
		else if (!stricmp(colour, "rosybrown3")) return RGB(205,155,155);
		else if (!stricmp(colour, "rosybrown4")) return RGB(139,105,105);
		else if (!stricmp(colour, "royalblue1")) return RGB(72,118,255);
		else if (!stricmp(colour, "royalblue2")) return RGB(67,110,238);
		else if (!stricmp(colour, "royalblue3")) return RGB(58,95,205);
		else if (!stricmp(colour, "royalblue4")) return RGB(39,64,139);
*/
	}
	else if (colour[0] == 's') //====================
	{
		if (!stricmp(colour, "saddlebrown")) return RGB(139,69,19);
		else if (!stricmp(colour, "salmon")) return RGB(250,128,114);
		else if (!stricmp(colour, "sandybrown")) return RGB(244,164,96);
		else if (!stricmp(colour, "seagreen")) return RGB(46,139,87);
		else if (!stricmp(colour, "seashell")) return RGB(255,245,238);
		else if (!stricmp(colour, "sienna")) return RGB(160,82,45);
		else if (!stricmp(colour, "skyblue")) return RGB(135,206,235);
		else if (!stricmp(colour, "slateblue")) return RGB(106,90,205);
		else if (!stricmp(colour, "slategray")) return RGB(112,128,144);
		else if (!stricmp(colour, "slategrey")) return RGB(112,128,144);
		else if (!stricmp(colour, "springgreen")) return RGB(0,255,127);
		else if (!stricmp(colour, "steelblue")) return RGB(70,130,180);
/*
		else if (!stricmp(colour, "salmon1")) return RGB(255,140,105);
		else if (!stricmp(colour, "salmon2")) return RGB(238,130,98);
		else if (!stricmp(colour, "salmon3")) return RGB(205,112,84);
		else if (!stricmp(colour, "salmon4")) return RGB(139,76,57);
		else if (!stricmp(colour, "seagreen1")) return RGB(84,255,159);
		else if (!stricmp(colour, "seagreen2")) return RGB(78,238,148);
		else if (!stricmp(colour, "seagreen3")) return RGB(67,205,128);
		else if (!stricmp(colour, "seagreen4")) return RGB(46,139,87);
		else if (!stricmp(colour, "seashell1")) return RGB(255,245,238);
		else if (!stricmp(colour, "seashell2")) return RGB(238,229,222);
		else if (!stricmp(colour, "seashell3")) return RGB(205,197,191);
		else if (!stricmp(colour, "seashell4")) return RGB(139,134,130);
		else if (!stricmp(colour, "sienna1")) return RGB(255,130,71);
		else if (!stricmp(colour, "sienna2")) return RGB(238,121,66);
		else if (!stricmp(colour, "sienna3")) return RGB(205,104,57);
		else if (!stricmp(colour, "sienna4")) return RGB(139,71,38);
		else if (!stricmp(colour, "skyblue1")) return RGB(135,206,255);
		else if (!stricmp(colour, "skyblue2")) return RGB(126,192,238);
		else if (!stricmp(colour, "skyblue3")) return RGB(108,166,205);
		else if (!stricmp(colour, "skyblue4")) return RGB(74,112,139);
		else if (!stricmp(colour, "slateblue1")) return RGB(131,111,255);
		else if (!stricmp(colour, "slateblue2")) return RGB(122,103,238);
		else if (!stricmp(colour, "slateblue3")) return RGB(105,89,205);
		else if (!stricmp(colour, "slateblue4")) return RGB(71,60,139);
		else if (!stricmp(colour, "slategray1")) return RGB(198,226,255);
		else if (!stricmp(colour, "slategray2")) return RGB(185,211,238);
		else if (!stricmp(colour, "slategray3")) return RGB(159,182,205);
		else if (!stricmp(colour, "slategray4")) return RGB(108,123,139);
		else if (!stricmp(colour, "snow1")) return RGB(255,250,250);
		else if (!stricmp(colour, "snow2")) return RGB(238,233,233);
		else if (!stricmp(colour, "snow3")) return RGB(205,201,201);
		else if (!stricmp(colour, "snow4")) return RGB(139,137,137);
		else if (!stricmp(colour, "springgreen1")) return RGB(0,255,127);
		else if (!stricmp(colour, "springgreen2")) return RGB(0,238,118);
		else if (!stricmp(colour, "springgreen3")) return RGB(0,205,102);
		else if (!stricmp(colour, "springgreen4")) return RGB(0,139,69);
		else if (!stricmp(colour, "steelblue1")) return RGB(99,184,255);
		else if (!stricmp(colour, "steelblue2")) return RGB(92,172,238);
		else if (!stricmp(colour, "steelblue3")) return RGB(79,148,205);
		else if (!stricmp(colour, "steelblue4")) return RGB(54,100,139);
*/
	}
	else if (colour[0] == 't') //====================
	{
		if (!stricmp(colour, "tan")) return RGB(210,180,140);
		else if (!stricmp(colour, "thistle")) return RGB(216,191,216);
		else if (!stricmp(colour, "tomato")) return RGB(255,99,71);
		else if (!stricmp(colour, "turquoise")) return RGB(64,224,208);
/*
		else if (!stricmp(colour, "tan1")) return RGB(255,165,79);
		else if (!stricmp(colour, "tan2")) return RGB(238,154,73);
		else if (!stricmp(colour, "tan3")) return RGB(205,133,63);
		else if (!stricmp(colour, "tan4")) return RGB(139,90,43);
		else if (!stricmp(colour, "thistle1")) return RGB(255,225,255);
		else if (!stricmp(colour, "thistle2")) return RGB(238,210,238);
		else if (!stricmp(colour, "thistle3")) return RGB(205,181,205);
		else if (!stricmp(colour, "thistle4")) return RGB(139,123,139);
		else if (!stricmp(colour, "tomato1")) return RGB(255,99,71);
		else if (!stricmp(colour, "tomato2")) return RGB(238,92,66);
		else if (!stricmp(colour, "tomato3")) return RGB(205,79,57);
		else if (!stricmp(colour, "tomato4")) return RGB(139,54,38);
		else if (!stricmp(colour, "turquoise1")) return RGB(0,245,255);
		else if (!stricmp(colour, "turquoise2")) return RGB(0,229,238);
		else if (!stricmp(colour, "turquoise3")) return RGB(0,197,205);
		else if (!stricmp(colour, "turquoise4")) return RGB(0,134,139);
*/
	}
	else if (colour[0] == 'v') //====================
	{
		if (!stricmp(colour, "violet")) return RGB(238,130,238);
		else if (!stricmp(colour, "violetred")) return RGB(208,32,144);
/*
		else if (!stricmp(colour, "violetred1")) return RGB(255,62,150);
		else if (!stricmp(colour, "violetred2")) return RGB(238,58,140);
		else if (!stricmp(colour, "violetred3")) return RGB(205,50,120);
		else if (!stricmp(colour, "violetred4")) return RGB(139,34,82);
*/
	}
	else if (colour[0] == 'w') //====================
	{
		if (!stricmp(colour, "wheat")) return RGB(245,222,179);
		else if (!stricmp(colour, "white")) return RGB(255,255,255);
		else if (!stricmp(colour, "whitesmoke")) return RGB(245,245,245);
/*
		else if (!stricmp(colour, "wheat1")) return RGB(255,231,186);
		else if (!stricmp(colour, "wheat2")) return RGB(238,216,174);
		else if (!stricmp(colour, "wheat3")) return RGB(205,186,150);
		else if (!stricmp(colour, "wheat4")) return RGB(139,126,102);
*/
	}
	else if (colour[0] == 'y') //====================
	{
		if (!stricmp(colour, "yellow")) return RGB(255,255,0);
		else if (!stricmp(colour, "yellowgreen")) return RGB(154,205,50);
/*
		else if (!stricmp(colour, "yellow1")) return RGB(255,255,0);
		else if (!stricmp(colour, "yellow2")) return RGB(238,238,0);
		else if (!stricmp(colour, "yellow3")) return RGB(205,205,0);
		else if (!stricmp(colour, "yellow4")) return RGB(139,139,0);
*/
	}

	//====================

	return 0x000000;
}

//===========================================================================
// Function: MBoxErrorFile
// Purpose: Gives a message box proclaming missing file
// In: LPCSTR = missing file
// Out: int = return value of messagebox
//===========================================================================

int MBoxErrorFile(LPCSTR szFile)
{
	sprintf(tempBuf, "Error: Unable to open file \"%s\". Please check location and try again.", szFile);
	Log("MBoxErrorFile Called", szFile);

	return MessageBox(GetBBWnd(), tempBuf, "xoblite", MB_OK | MB_ICONERROR | MB_SETFOREGROUND | MB_TOPMOST);
}

//===========================================================================
// Function: MBoxErrorValue
// Purpose: Gives a message box proclaming a given value
// In: LPCSTR = value
// Out: int = return value of messagebox
//===========================================================================

int MBoxErrorValue(LPCSTR szValue)
{
	sprintf(tempBuf, "Error: %s", szValue);
	Log("MBoxErrorValue Called", szValue);

	return MessageBox(GetBBWnd(), tempBuf, "xoblite", MB_OK | MB_ICONERROR | MB_SETFOREGROUND | MB_TOPMOST);
}

//===========================================================================
// Function: BBExecute
// Purpose: A safe execute routine
// In: HWND = owner
// In: LPCSTR = operation (eg. "open")
// In: LPCSTR = command to run
// In: LPCSTR = arguments
// In: LPCSTR = directory to run from
// In: int = show status
// In: bool = suppress error messages
// Out: HINSTANCE = instance of file running
//===========================================================================

HINSTANCE BBExecute(HWND Owner, LPCSTR szOperation, LPCSTR szCommand, LPCSTR szArgs, LPCSTR szDirectory, int nShowCmd, bool noErrorMsgs)
{
	DWORD type;
	SHELLEXECUTEINFO si;

	if (!strlen(szCommand)) return HINSTANCE(32);

    type = GetFileAttributes(szCommand);
    if (type & FILE_ATTRIBUTE_DIRECTORY && type != 0xFFFFFFFF)
		return ShellExecute(Owner, szOperation, szCommand, szArgs, NULL, nShowCmd ? nShowCmd : SW_SHOWNORMAL);
    else
    {
		//ZeroMemory(&si, sizeof(si));
		memset(&si, 0, sizeof(si));

		si.cbSize = sizeof(SHELLEXECUTEINFO);
		si.hwnd = Owner;
		si.lpVerb = szOperation;
		si.lpFile = szCommand;
		si.lpParameters = szArgs;
		si.lpDirectory = szDirectory;
		//si.nShow = nShowCmd;
		si.nShow = nShowCmd ? nShowCmd : SW_SHOWNORMAL;
		si.fMask = SEE_MASK_DOENVSUBST | SEE_MASK_FLAG_NO_UI;

		ShellExecuteEx(&si);

		if (((int)si.hInstApp <= 32) && !noErrorMsgs)
		{
			char msg[MAX_LINE_LENGTH];
			sprintf(msg, "%s %s (%s)", szCommand, szArgs, szDirectory);
			Log("BBExecute failed", msg);

			strcpy(msg, "Could not execute...\n\nCommand:\n");

			if (szCommand != NULL) strcat(msg, szCommand);
				strcat(msg, "   \n\nArguments:\n");
			if (szArgs != NULL) strcat(msg, szArgs);
				strcat(msg, "   \n\nWorking directory:\n");
			if (szDirectory != NULL) strcat(msg, szDirectory);
				strcat(msg, "   ");

			MessageBox(GetBBWnd(), msg, "xoblite", MB_OK | MB_ICONERROR | MB_TOPMOST);
		}
		return si.hInstApp;
    }
}

//===========================================================================
// Function: IsAppWindow
// Purpose: checks given hwnd to see if it's an app
// In: HWND = hwnd to check
// Out: bool = if app or not
//===========================================================================

bool IsAppWindow(HWND hWnd)
{
	if(!(GetWindowLong(hWnd, GWL_STYLE) & WS_SYSMENU)) return false;

	if (IsWindowVisible(hWnd))
	{
		if (!(GetWindowLong(hWnd, GWL_EXSTYLE) & WS_EX_TOOLWINDOW) )
		{
			if (!GetParent(hWnd))
			{
				HWND hwndOwner = GetWindow(hWnd, GW_OWNER);
				if (!hwndOwner ||
					((GetWindowLong(hwndOwner, GWL_STYLE) & (WS_VISIBLE | WS_CLIPCHILDREN)) != (WS_VISIBLE | WS_CLIPCHILDREN)) || 
					GetWindowLong(hwndOwner, GWL_EXSTYLE) & WS_EX_TOOLWINDOW )
				{
					return true;
				}
			}
		}
	}

	return false;
}

//===========================================================================
// Function: SnapWindowToEdge
// Purpose:Snaps a given windowpos at a specified distance
// In: WINDOWPOS* = WINDOWPOS recieved from WM_WINDOWPOSCHANGING
// In: int = distance to snap to
// In: bool = use screensize of workspace
// Out: void = none
//===========================================================================

void SnapWindowToEdge(WINDOWPOS* pwPos, int nDist, bool bUseScreenSize)
{
	RECT workArea;
	int nTemp;

	if(bUseScreenSize)
	{
		ZeroMemory(&workArea, sizeof(workArea));
		workArea.right = GetSystemMetrics(SM_CXSCREEN);
		workArea.bottom = GetSystemMetrics(SM_CYSCREEN);
	}
	else SystemParametersInfo(SPI_GETWORKAREA, 0, &workArea, 0);
	
	// Left edge
	nTemp = pwPos->x - workArea.left;
	if (nTemp < nDist && nTemp > -nDist)
		pwPos->x = workArea.left;

	// Top edge
	nTemp = pwPos->y - workArea.top;
	if (nTemp < nDist && nTemp > -nDist)
		pwPos->y = workArea.top;

	// Right edge
	nTemp = workArea.right - (pwPos->x + pwPos->cx);
	if (nTemp < nDist && nTemp > -nDist) 
		pwPos->x = workArea.right - pwPos->cx;

	// Bottom edge
	nTemp =  workArea.bottom - (pwPos->y + pwPos->cy);
	if (nTemp < nDist && nTemp > -nDist)
		pwPos->y = workArea.bottom - pwPos->cy;
}

//===========================================================================
// Function: MakeGradient
// Purpose: creates a gradient and fills it with the specified options
// In: HDC = handle of the device context to fill
// In: RECT = rect you wish to fill
// In: int = type of gradient (eg. B_HORIZONTAL)
// In: COLORREF = first colour to use
// In: COLORREF = second colour to use
// In: bool = whether to interlace (darken every other line)
// In: int = style of bevel (eg. BEVEL_FLAT)
// In: int = position of bevel (eg. BEVEL1)
// In: int = width of border around bitmap
//===========================================================================

RECT gradientRect;

void MakeGradient(HDC hDC, RECT rect, int type, COLORREF colour1, COLORREF colour2, bool bInterlaced, int bevelStyle, int bevelPosition, int bevelWidth, COLORREF borderColour, int borderWidth)
{
	if (borderWidth > 0)
	{
		gradientRect.left = rect.left + borderWidth;
		gradientRect.top = rect.top + borderWidth;
		gradientRect.right = rect.right - borderWidth;
		gradientRect.bottom = rect.bottom - borderWidth;

		pBImage->CreateBorder(hDC, rect, borderColour, borderWidth);
		pBImage->CreateGradientByRect(hDC, gradientRect, type, colour1, colour2, bInterlaced, bevelStyle, bevelPosition, bevelWidth);
	}
	else pBImage->CreateGradientByRect(hDC, rect, type, colour1, colour2, bInterlaced, bevelStyle, bevelPosition, bevelWidth);
}

//===========================================================================

void MakeStyleGradient(HDC hdc, RECT *rect, StyleItem *item, bool withBorder)
{
	MakeGradient(hdc, *rect, item->type, item->Color, item->ColorTo, item->interlaced, item->bevelstyle, item->bevelposition, 0, pSettings->borderColor, withBorder ? pSettings->borderWidth : 0);
}

//===========================================================================

void CreateBorder(HDC hdc, RECT *rect, int borderColour, int borderWidth)
{
	pBImage->CreateBorder(hdc, *rect, borderColour, borderWidth);
}

//===========================================================================

static int CALLBACK EnumFontFamProc(
	ENUMLOGFONT *lpelf,     // Pointer to logical font data 
	NEWTEXTMETRIC *lpntm,   // Pointer to physical font data 
	int FontType,           // Type of font 
	LPARAM lParam           // Address of application defined data  
   )
{
	(*(int*)lParam)++;
	return 0;
}

//====================

HFONT CreateStyleFont(StyleItem *item)
{
	char *face = item->Font;
	int height = item->FontHeight;
	int weight = item->FontWeight;

	HDC hdc = CreateCompatibleDC(NULL);
	int data = 0;
	EnumFontFamilies(hdc, face, (FONTENUMPROC)EnumFontFamProc, (LPARAM)&data);
	DeleteDC(hdc);
	if (!data) face = "verdana";

	return CreateFont(height, 0, 0, 0, weight, false, false, false,	DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH|FF_DONTCARE, face);
}

//===========================================================================
// Function: IsInString
// Purpose: Checks a given string to an occurance of the second string
// In: LPCSTR = string to search
// In: LPCSTR = string to search for
// Out: bool = found or not
//===========================================================================

bool IsInString(LPCSTR inputString, LPCSTR searchString)
{
	char input[MAX_LINE_LENGTH], search[MAX_LINE_LENGTH];

	strcpy(input, inputString);
	strcpy(search, searchString);
	strlwr(input);
	strlwr(search);

	if (strstr(input, search)) return true;
	else return false;
}

//===========================================================================
// Function: ParseItem
// Purpose: parses a given string and assigns settings to a StyleItem class
// In: LPCSTR = item to parse out
// In: StyleItem* = class to assign values to
// Out: void = None
//===========================================================================

void ParseItem(LPCSTR szItem, StyleItem *item)
{
	// Default settings
	item->type = B_SOLID;
	item->bevelstyle = BEVEL_RAISED;
	item->bevelposition = BEVEL1;
	item->parentRelative = false;
	item->interlaced = false;

	if (IsInString(szItem, "solid")) item->type = B_SOLID;
	else if (IsInString(szItem, "horizontal")) item->type = B_HORIZONTAL;
	else if (IsInString(szItem, "vertical")) item->type = B_VERTICAL;
	else if (IsInString(szItem, "crossdiagonal")) item->type = B_CROSSDIAGONAL; // Must be checked before diagonal!!!
	else if (IsInString(szItem, "diagonal")) item->type = B_DIAGONAL;
	else if (IsInString(szItem, "pipecross")) item->type = B_PIPECROSS;
	else if (IsInString(szItem, "elliptic")) item->type = B_ELLIPTIC;
	else if (IsInString(szItem, "rectangle")) item->type = B_RECTANGLE;
	else if (IsInString(szItem, "pyramid")) item->type = B_PYRAMID;

	if (IsInString(szItem, "flat")) item->bevelstyle=BEVEL_FLAT;
	else if (IsInString(szItem, "raised")) item->bevelstyle=BEVEL_RAISED; 
	else if (IsInString(szItem, "sunken")) item->bevelstyle=BEVEL_SUNKEN;

	if (IsInString(szItem, "bevel2")) item->bevelposition=BEVEL2;
	else if (IsInString(szItem, "bevel1")) item->bevelposition=BEVEL1;

	if (IsInString(szItem, "interlaced")) item->interlaced = true;
	if (IsInString(szItem, "parentrelative")) item->parentRelative = true;
}

//===========================================================================
// Function: ReplaceEnvVars
// Purpose: parses a given string and replaces all %VAR% with the environment
//          variable value if such a value exists
// In: LPSTR = string to replace env vars in
// Out: void = None
//===========================================================================

void ReplaceEnvVars(LPSTR string)
{
    LPSTR pos0 = string, pos1;

	while ((pos0 = strchr(pos0,'%')) && (pos1 = strchr(++pos0,'%')))
	{
		if (pos0 == pos1)  // ...this means that we have "%%"
			continue;

		char var_name[MAX_LINE_LENGTH]="", var_value[MAX_LINE_LENGTH]="";
		memcpy(var_name,pos0,pos1-pos0);

		// Grab the variable name(s) from the string...
		int nSize = GetEnvironmentVariable(var_name, var_value, MAX_LINE_LENGTH);
		if (GetLastError() == ERROR_ENVVAR_NOT_FOUND)
		{
			SetLastError(ERROR_SUCCESS);
			continue;
		}

		if ((nSize + strlen(string) + 1 - (pos1-pos0+2)) > MAX_LINE_LENGTH)
		{
			//TODO signal some sort of error
			continue;
		}

		// Perform the substitution...
		memmove(pos0-1+nSize,pos1+1,strlen(pos1)+1); // Don't forget the \0 !!!
		memcpy(pos0-1,var_value,nSize);
	}
}

//===========================================================================
// Function: ReplaceShellFolders
// Purpose: 
// In: LPSTR = string to replace shell folders in
// Out: void = None
//===========================================================================

void ReplaceShellFolders(LPSTR string)
{
    LPSTR pos0 = string, pos1;
	char folderpath[MAX_PATH];

	while ((pos0 = strchr(pos0,'$')) && (pos1 = strchr(pos0+1,'$')))
	{
		if(pos0 == pos1) continue; // ...this means that we have "$$"

		// Note that there can be more than one shell folder
		// variable in a string, check current position only!
		if (!_strnicmp(pos0, "$ProgramFiles$", 14)) strcpy(folderpath, pSettings->SF_programFiles);
		else if (!_strnicmp(pos0, "$Blackbox$", 10)) strcpy(folderpath, pSettings->SF_blackboxPath);
		else if (!_strnicmp(pos0, "$CurrentTheme$", 14)) strcpy(folderpath, pSettings->SF_currentThemePath);
		else if (!_strnicmp(pos0, "$StartMenu$", 11)) strcpy(folderpath, pSettings->SF_startMenu);
		else if (!_strnicmp(pos0, "$CommonStartMenu$", 17)) strcpy(folderpath, pSettings->SF_commonStartMenu);
		else if (!_strnicmp(pos0, "$Desktop$", 9)) strcpy(folderpath, pSettings->SF_desktop);
		else if (!_strnicmp(pos0, "$UserAppData$", 13)) strcpy(folderpath, pSettings->SF_userAppData);
		else if (!_strnicmp(pos0, "$Clipboard$", 11)) RetrieveStringFromClipboard(folderpath);

		memmove(pos0+strlen(folderpath), pos1+1, strlen(pos1)+1); // Don't forget the \0 !!!
		memcpy(pos0, folderpath, strlen(folderpath));
	}	
}

//===========================================================================
// Function: ParseFontString
// Purpose: Parses a Name/Height/WeightShadow format font string
// In: LPSTR string, StyleItem *item
// Out: void = None
//===========================================================================

void ParseFontString(LPSTR string, StyleItem *item)
{
	char height[MAX_LINE_LENGTH];
	strcpy(tempBuf, string);

	strcpy(tempBuf, Tokenize(tempBuf, item->Font, "/"));
	CheckFontSubstitution(item->Font);

	if (strlen(tempBuf))
	{
		strcpy(tempBuf, Tokenize(tempBuf, height, "/"));
		item->FontHeight = atoi(height);

		if (IsInString(tempBuf, "Bold")) item->FontWeight = FW_BOLD;
		else item->FontWeight = FW_NORMAL;

		if (IsInString(tempBuf, "Shadow")) item->FontShadow = true;
		else item->FontShadow = false;
	}
	else
	{
		item->FontHeight = 12;
		item->FontWeight = FW_BOLD;
		item->FontShadow = false;
	}
}

//===========================================================================
// Function: CheckFontSubstitution
// Purpose: Replaces *nix-only fonts with (hopefully) Windows equivalents
// In: LPSTR = string to replace font names in
// Out: void = None
//===========================================================================

void CheckFontSubstitution(LPSTR font)
{
	// We can save a few IsInString's by checking for "-" in the font name...
	if (strchr(font, '-'))
	{
		if (IsInString(font, "lucidasans-")) strcpy(font, "verdana");
		else if (IsInString(font, "-lucidatypewriter-")) strcpy(font, "verdana");
		else if (IsInString(font, "-lucida-")) strcpy(font, "verdana");
		else if (IsInString(font, "-snap-")) strcpy(font, "snap");
		else if (IsInString(font, "-verdana-")) strcpy(font, "verdana");
		else if (IsInString(font, "-helvetica-")) strcpy(font, "verdana");
		else if (IsInString(font, "-arial-")) strcpy(font, "arial");
	}
	else
	{
		if (IsInString(font, "lucidatypewriter")) strcpy(font, "verdana");
		else if (IsInString(font, "helvetica")) strcpy(font, "verdana");
		else if (IsInString(font, "fixed")) strcpy(font, "terminal");
	}

	// No idea what to substitute with:
	// shine -beatbox- bright outcast -clean- seeds
}

//===========================================================================

Menu *MakeMenu(LPCSTR HeaderText)
{
	Menu *newMenu = new Menu(pMenuMaker->hInst);
	pMenuMaker->AddHeaderItem(newMenu, (LPSTR)HeaderText);
	return newMenu;
}

//====================

Menu *MakeNamedMenu(LPCSTR HeaderText, LPCSTR MenuId, bool popup)
{
	if (popup)
	{
		hideNamedMenu = false;
		return MakeMenu(HeaderText);
	}
	else
	{
		hideNamedMenu = true;
		return NULL;
	}
}

//====================

void DelMenu(Menu *PluginMenu)
{
	if (pMenuMaker->m_pPluginMenu)
	{
		delete pMenuMaker->m_pPluginMenu;
		pMenuMaker->m_pPluginMenu = NULL;
	}
}

//====================

MenuItem *MakeSubmenu(Menu *ParentMenu, Menu *ChildMenu, LPCSTR Title)
{
	if (!ParentMenu) return NULL;

	MenuItem *newMenuItem = NULL;

	if (ChildMenu)
	{
		ChildMenu->Validate();
		pMenuMaker->AddBottomItem(ChildMenu);

		if (ParentMenu)
		{
			newMenuItem = new FolderItem(ChildMenu, (LPSTR)Title);
			newMenuItem->SetHeight(pMenuMaker->m_nSubmenuHeight);
			newMenuItem->SetPainter(pMenuMaker->m_pEntry);
			newMenuItem->SetActivePainter(pMenuMaker->m_pSelEntry);
			ParentMenu->AddMenuItem(newMenuItem);
		}
	}

	return newMenuItem;
}

//====================

MenuItem *MakeMenuItem(Menu *PluginMenu, LPCSTR Title, LPCSTR Cmd, bool ShowIndicator)
{
	if (!PluginMenu) return NULL;
//	if (strchr(Cmd, '[') && strchr(Cmd, ']')) pMenuMaker->CreateMenuItem(PluginMenu, (LPSTR)Cmd, (LPSTR)Cmd, (LPSTR)Title, ShowIndicator);
//	else pMenuMaker->CreateMenuItem(PluginMenu, "[exec]", (LPSTR)Cmd, (LPSTR)Title, ShowIndicator);
	return pMenuMaker->CreateMenuItem(PluginMenu, "[exec]", (LPSTR)Cmd, (LPSTR)Title, ShowIndicator);
}

//====================

MenuItem *MakeMenuNOP(Menu *PluginMenu, LPCSTR Title)
{
	if (!PluginMenu) return NULL;
	return pMenuMaker->CreateMenuItem(PluginMenu, "[nop]", NULL, (LPSTR)Title, false);
}

//====================

MenuItem *MakeMenuItemString(Menu *PluginMenu, LPCSTR Title, LPCSTR Cmd, LPCSTR init_string)
{
	if (!PluginMenu) return NULL;

	char TitleColonInitString [MAX_LINE_LENGTH], CmdInitString[MAX_LINE_LENGTH];

	// Make sure there's a colon after the title for consistency... :)
	if (Title[strlen(Title)-1] == ':') sprintf(TitleColonInitString, "%s <%s>", Title, init_string);
	else sprintf(TitleColonInitString, "%s: <%s>", Title, init_string);
	// Does the command string includes spaces?
	if (strstr(Cmd, " ")) sprintf(CmdInitString, "\"%s\" %s", Cmd, init_string);
	else sprintf(CmdInitString, "%s %s", Cmd, init_string);

	return pMenuMaker->CreateMenuItem(PluginMenu, "<edit-string>", (LPSTR)CmdInitString, (LPSTR)TitleColonInitString, false);
}

//====================

MenuItem *MakeMenuItemInt(Menu *PluginMenu, LPCSTR Title, LPCSTR Cmd, int val, int minval, int maxval)
{
	if (!PluginMenu) return NULL;

	char TitleWithColon [MAX_LINE_LENGTH], CmdValues[MAX_LINE_LENGTH];

	// Make sure there's a colon after the title for consistency... :)
	if (Title[strlen(Title)-1] == ':') sprintf(TitleWithColon, "%s", Title);
	else sprintf(TitleWithColon, "%s:", Title);
	// Does the command string includes spaces?
	if (strstr(Cmd, " ")) sprintf(CmdValues, "\"%s\" %d %d %d", Cmd, val, minval, maxval);
	else sprintf(CmdValues, "%s %d %d %d", Cmd, val, minval, maxval);

	bool enabled = false;
//	if ((val>=minval) && (val<=maxval)) enabled = true;

	return pMenuMaker->CreateMenuItem(PluginMenu, "<edit-int>", (LPSTR)CmdValues, (LPSTR)TitleWithColon, enabled);
}

//====================

void ShowMenu(Menu *PluginMenu)
{
	if (hideNamedMenu)
	{
		pMenuMaker->Hide();
		if (pMenuMaker->m_pPluginMenu) DelMenu(pMenuMaker->m_pPluginMenu);
		hideNamedMenu = false;
		return;
	}

	if (!PluginMenu) return;

	pMenuMaker->Hide();
	if (pMenuMaker->m_pPluginMenu) DelMenu(pMenuMaker->m_pPluginMenu);

	PluginMenu->Validate();
	pMenuMaker->AddBottomItem(PluginMenu);
	PluginMenu->Show();
	PluginMenu->SetPinned(true);

	pMenuMaker->m_pPluginMenu = PluginMenu;

	SendMessage(GetBBWnd(), BB_MENU, (WPARAM)(-1), 0); // ...doesn't do anything, just to signal e.g. BBSoundFX
}

//===========================================================================
// Function: MakeSticky
//===========================================================================

void MakeSticky(HWND window)
{
	stickyWindows.push_back(window);
}

//===========================================================================
// Function: RemoveSticky
//===========================================================================

void RemoveSticky(HWND window)
{
	for (int i=0; i < (int)stickyWindows.size(); i++)
	{
		if (stickyWindows[i] == window)
			stickyWindows.erase(stickyWindows.begin() + i);
	}
}

//===========================================================================
// Function: CheckSticky
//===========================================================================

bool CheckSticky(HWND window)
{
	for (int i=0; i < (int)stickyWindows.size(); i++)
	{
		if (stickyWindows[i] == window)
			return true;
	}

	return false;
}

//===========================================================================
// Function: ClearSticky
//===========================================================================

void ClearSticky()
{
	stickyWindows.clear();
}

//===========================================================================
// Function: SetTransparency
//===========================================================================

typedef bool (__stdcall *slwaFunc)(HWND, COLORREF, BYTE, DWORD);

bool SetTransparency(HWND hwnd, BYTE alpha)
{
	if (alpha < 255)
	{
		slwaFunc SLWA = (slwaFunc)GetProcAddress(GetModuleHandle("USER32"), "SetLayeredWindowAttributes");

		if (!SLWA) return false;
		else
		{
			LONG windowStyle = GetWindowLong(hwnd, GWL_EXSTYLE);
			windowStyle = windowStyle | WS_EX_LAYERED;
			SetWindowLong(hwnd, GWL_EXSTYLE, windowStyle);
			return SLWA(hwnd, NULL, alpha, LWA_ALPHA);
		}
	}
	else
	{
		LONG windowStyle = GetWindowLong(hwnd, GWL_EXSTYLE);
		windowStyle = windowStyle & ~WS_EX_LAYERED;
		SetWindowLong(hwnd, GWL_EXSTYLE, windowStyle);
		return false;
	}

	return false;
}

//===========================================================================
// Function: WriteValue
//===========================================================================

void WriteValue(LPCSTR fp, LPCSTR keyword, LPCSTR value)
{
	if (pSettings->writeProtection) return;

	vector <string> fileContents;
	char fileLine[MAX_LINE_LENGTH];
	int pointer;
	bool written = false;

	FILE *file = fopen(fp, "r");
	if (file)
	{
		fseek(file, 0, SEEK_SET);
		while (fgets(fileLine, MAX_LINE_LENGTH, file))
		{
			if (!IsInString(fileLine, "\n"))
				strcat(fileLine, "\n");

			if (IsInString(fileLine, keyword))
			{
				sprintf(fileLine, "%s %s\n", keyword, value);
				written = true;
			}

			fileContents.push_back(fileLine);
		}
		fclose(file);
	}

	if (!written)
	{
		sprintf(fileLine, "%s %s\n", keyword, value);
		fileContents.push_back(fileLine);
	}

	file = fopen(fp, "w");
	if (file)
	{
		fseek(file, 0, SEEK_SET);
		for (pointer = 0; pointer < (int)fileContents.size(); pointer++)
			fputs(fileContents[pointer].c_str(), file);
		fclose(file);
	}
}

//===========================================================================
// Function: WriteBool
//===========================================================================

void WriteBool(LPCSTR fp, LPCSTR keyword, bool value)
{
	if (pSettings->writeProtection) return;

	char tmp[50];
	if (value) strcpy(tmp, "true");
	else strcpy(tmp, "false");

	WriteValue(fp, keyword, tmp);
}

//===========================================================================
// Function: WriteInt
//===========================================================================

void WriteInt(LPCSTR fp, LPCSTR keyword, int value)
{
	if (pSettings->writeProtection) return;

	char tmp[50];
	sprintf(tmp, "%d", value);

	WriteValue(fp, keyword, tmp);
}

//===========================================================================
// Function: WriteString
//===========================================================================

void WriteString(LPCSTR fp, LPCSTR keyword, LPSTR value)
{
	if (pSettings->writeProtection) return;

	WriteValue(fp, keyword, value);
}

//===========================================================================
// Function: WriteColor
//===========================================================================

void WriteColor(LPCSTR fp, LPCSTR keyword, COLORREF value)
{
	if (pSettings->writeProtection) return;

	char tmp[MAX_LINE_LENGTH];
	sprintf(tmp, "#%x%x%x", GetRValue(value), GetGValue(value), GetBValue(value));

	WriteValue(fp, keyword, tmp);
}

//===========================================================================

int GetTraySize()
{
	if (!pSettings->accessLock && !pSettings->systrayDisabled)
	{
		return pTrayManager->trayIconList.size();
	}
	else return 0;
}

//====================

trayIconItem* GetTrayIcon(int pointer)
{
	if (!pSettings->accessLock && !pSettings->systrayDisabled)
	{
		return pTrayManager->trayIconList[pointer];
	}
	else return NULL;
}

//===========================================================================

void GetDesktopInfo(DesktopInfo *deskInfo)
{
    strncpy(deskInfo->name, pWorkspaces->deskNames[pWorkspaces->currentScreen].c_str(), 32);
	deskInfo->name[31] = 0;
    deskInfo->isCurrent = true;
    deskInfo->number = pWorkspaces->currentScreen;
	deskInfo->ScreensX = pSettings->workspaces;
	// deskInfo->deskNames is currently not supported
}

//===========================================================================

int GetTaskListSize()
{
	return pSystembar->WinEnumeratorCount;
}

//====================

void SetTaskWorkspace(HWND hwnd, int workspace)
{
	pWorkspaces->MoveWindowToSpecified(hwnd, workspace);
}

//====================

int GetTaskWorkspace(HWND hwnd)
{
	return pWorkspaces->getDesktop(hwnd);
}

//====================

HWND GetTask(int pointer)
{
	if ((pointer >= 0) && (pointer <= pSystembar->WinEnumeratorCount)) return pSystembar->WindowList[pointer].Window;
	else return NULL;
}

//====================

int GetActiveTask()
{
	// Find the currently active window...
	// Returns the index of currently active task (or -1 if none or the shell itself)
	for (int i=0; i < pSystembar->WinEnumeratorCount; i++)
	{
		if (pSystembar->WindowList[i].Active) return i;
	}

	return -1;
}

//===========================================================================

bool GetUnderExplorer()
{
	return pSettings->underExplorer;
}

//===========================================================================

bool DownloadFile(LPCSTR url, LPCSTR path)
{
	// DownloadFile will accept an input string containing multiple URLs
	// (using space as the delimiter), in which case it will merge all
	// the files together into a single output file! :D

	HANDLE file = NULL;
	HINTERNET iopen = NULL, iopenurl = NULL;
	bool status = true; // Will be set to false if any of the specified downloads fail

	char remaining[MAX_LINE_LENGTH], token[MAX_LINE_LENGTH], extra[MAX_LINE_LENGTH];
	LPSTR tokens[1];
	tokens[0] = token;

	strcpy(remaining, url);

	//====================

	while (strlen(remaining))
	{
		// Get the next token from the list of URLs...
		token[0] = extra[0] = '\0';
		BBTokenize (remaining, tokens, 1, extra);
		strcpy(remaining, extra);

		// ...then try fetching the file from the web...
		iopen = InternetOpen("xoblite", INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0);//INTERNET_FLAG_ASYNC);
		if (iopen)
		{
			iopenurl = InternetOpenUrl(iopen, token, NULL, 0, INTERNET_FLAG_RELOAD, 0);
			if (iopenurl)
			{
				// unsigned int size = 0;
				// InternetQueryDataAvailable(iopenurl, (LPDWORD)&size, 0, 0);

				char buffer[1024];
				DWORD bytesRead = 0, retLength = 0;

				// Create the file if we haven't done so already...
				if (!file) file = CreateFile(path, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);

				if (file)
				{
					// Download data in chunks of 1024 bytes...
					while (InternetReadFile(iopenurl, (LPVOID)buffer, 1024, &bytesRead))
					{
						if (bytesRead == 0) break;

						// Stop downloading if the file seems to be 404...
//						if (strstr(buffer, "<title>404 Not Found</title>")) break;

						WriteFile(file, buffer, bytesRead, &retLength, NULL);
					}

					InternetCloseHandle(iopenurl);
					InternetCloseHandle(iopen);
				}
				else
				{
					status = false;
					InternetCloseHandle(iopenurl);
					InternetCloseHandle(iopen);
				}
			}
			else
			{
				status = false;
				InternetCloseHandle(iopen);
			}
		}
	}

	//====================

	// Close the file...
	if (file) CloseHandle(file);

	return status;
}

//===========================================================================

bool CopyStringToClipboard(LPSTR string)
{
	HGLOBAL hGlobal;
	hGlobal = GlobalAlloc(GHND | GMEM_SHARE, strlen(string)+1) ;
	if (hGlobal == NULL) return false;

	char* pGlobal;
	pGlobal = (LPSTR)GlobalLock(hGlobal) ;
	if (pGlobal == NULL) return false;

	strcpy(pGlobal, string);
	GlobalUnlock (hGlobal);

	if (OpenClipboard(GetBBWnd()))
	{
		if (EmptyClipboard())
		{
			if (SetClipboardData(CF_TEXT, hGlobal) != NULL)
			{
			    CloseClipboard();
				return true;
			}
		}

		CloseClipboard();
	}

	return false;
}

//===========================================================================

bool RetrieveStringFromClipboard(LPSTR target)
{
	HANDLE handle;

	if (IsClipboardFormatAvailable(CF_TEXT))
	{
		if (OpenClipboard(GetBBWnd()))
		{
			handle = GetClipboardData(CF_TEXT);
			if (handle != NULL)
			{
				strcpy(target, (char*)handle);
			    CloseClipboard();
				return true;
			}

			CloseClipboard();
		}
	}
	
	strcpy(target, ""); // Failed or nothing was found on the clipboard...

	return false;
}

//===========================================================================

void DrawTextWithShadow(HDC hdc, LPSTR text, RECT r, unsigned int format, COLORREF textColor, COLORREF shadowColor, bool shadow)
{
	if (shadow)
	{
		RECT s;
		s.left = r.left + 1;
		s.top = r.top + 1;
		s.bottom = r.bottom + 1;
		s.right = r.right + 1;

		SetTextColor(hdc, shadowColor);
		DrawText(hdc, text, strlen(text), &s, format);
	}

	SetTextColor(hdc, textColor);
	DrawText(hdc, text, strlen(text), &r, format);
}

//===========================================================================





syntax highlighting by

w e b c p p
web c plus plus