467 lines
13 KiB
C++
467 lines
13 KiB
C++
/*
|
|
* This is a part of the BugTrap package.
|
|
* Copyright (c) 2005-2009 IntelleSoft.
|
|
* All rights reserved.
|
|
*
|
|
* Description: BugTrap utilities.
|
|
* Author: Maksim Pyatkovskiy.
|
|
*
|
|
* This source code is only intended as a supplement to the
|
|
* BugTrap package reference and related electronic documentation
|
|
* provided with the product. See these sources for detailed
|
|
* information regarding the BugTrap package.
|
|
*/
|
|
|
|
#include "StdAfx.h"
|
|
#include "resource.h"
|
|
#include "BugTrapUtils.h"
|
|
#include "Globals.h"
|
|
|
|
#ifdef _DEBUG
|
|
#define new DEBUG_NEW
|
|
#endif
|
|
|
|
/**
|
|
* @addtogroup BugTrapUtils Internal BugTrap Utilities
|
|
* @{
|
|
*/
|
|
|
|
// Windows processing.
|
|
|
|
/**
|
|
* @brief Center window according to the desktop or another window.
|
|
* @param hwnd - window handle to center.
|
|
* @param hwndCenter - relative window handle or zero for desktop.
|
|
*/
|
|
void CenterWindow(HWND hwnd, HWND hwndCenter)
|
|
{
|
|
// Determine owner window to center against.
|
|
DWORD dwStyle = GetWindowLong(hwnd, GWL_STYLE);
|
|
if (hwndCenter == NULL)
|
|
{
|
|
if(dwStyle & WS_CHILD)
|
|
hwndCenter = GetParent(hwnd);
|
|
else
|
|
hwndCenter = GetWindow(hwnd, GW_OWNER);
|
|
}
|
|
|
|
// Get coordinates of the window relative to its parent.
|
|
RECT rcDlg;
|
|
GetWindowRect(hwnd, &rcDlg);
|
|
RECT rcArea;
|
|
RECT rcCenter;
|
|
HWND hwndParent;
|
|
if ((dwStyle & WS_CHILD) == 0)
|
|
{
|
|
// Don't center against invisible or minimized windows.
|
|
if (hwndCenter != NULL)
|
|
{
|
|
DWORD dwStyleCenter = GetWindowLong(hwndCenter, GWL_STYLE);
|
|
if (! (dwStyleCenter & WS_VISIBLE) || (dwStyleCenter & WS_MINIMIZE))
|
|
hwndCenter = NULL;
|
|
}
|
|
|
|
// Center within screen coordinates.
|
|
SystemParametersInfo(SPI_GETWORKAREA, NULL, &rcArea, NULL);
|
|
if(hwndCenter == NULL)
|
|
rcCenter = rcArea;
|
|
else
|
|
GetWindowRect(hwndCenter, &rcCenter);
|
|
}
|
|
else
|
|
{
|
|
// Center within parent client coordinates.
|
|
hwndParent = GetParent(hwnd);
|
|
GetClientRect(hwndParent, &rcArea);
|
|
GetClientRect(hwndCenter, &rcCenter);
|
|
MapWindowPoints(hwndCenter, hwndParent, (POINT*)&rcCenter, 2);
|
|
}
|
|
|
|
int nDlgWidth = rcDlg.right - rcDlg.left;
|
|
int nDlgHeight = rcDlg.bottom - rcDlg.top;
|
|
|
|
// Find dialog's upper left based on rcCenter.
|
|
int xLeft = (rcCenter.left + rcCenter.right) / 2 - nDlgWidth / 2;
|
|
int yTop = (rcCenter.top + rcCenter.bottom) / 2 - nDlgHeight / 2;
|
|
|
|
// If the dialog is outside the screen, move it inside.
|
|
if (xLeft < rcArea.left)
|
|
xLeft = rcArea.left;
|
|
else if (xLeft + nDlgWidth > rcArea.right)
|
|
xLeft = rcArea.right - nDlgWidth;
|
|
|
|
if (yTop < rcArea.top)
|
|
yTop = rcArea.top;
|
|
else if (yTop + nDlgHeight > rcArea.bottom)
|
|
yTop = rcArea.bottom - nDlgHeight;
|
|
|
|
// Map screen coordinates to child coordinates.
|
|
SetWindowPos(hwnd, NULL, xLeft, yTop, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
|
|
}
|
|
|
|
/**
|
|
* @brief Display wait message on the screen.
|
|
* @param hwnd - window handle.
|
|
*/
|
|
void DisplayWaitBanner(HWND hwnd)
|
|
{
|
|
_ASSERTE(g_pResManager != NULL);
|
|
TCHAR szMessage[100];
|
|
LoadString(g_hInstance, IDS_PLEASE_WAIT, szMessage, countof(szMessage));
|
|
RECT rect;
|
|
GetClientRect(hwnd, &rect);
|
|
HDC hdc = GetDC(hwnd);
|
|
FillRect(hdc, &rect, g_pResManager->m_hbrWindowBrush);
|
|
HFONT hOldFont = SelectFont(hdc, g_pResManager->m_hDialogFont);
|
|
DrawText(hdc, szMessage, -1, &rect, DT_SINGLELINE | DT_CENTER | DT_VCENTER | DT_NOCLIP | DT_NOPREFIX);
|
|
SelectFont(hdc, hOldFont);
|
|
ReleaseDC(hwnd, hdc);
|
|
}
|
|
|
|
/**
|
|
* @brief Validate scrollbar metrics.
|
|
* @param pSInfo - pointer to scrollbar info.
|
|
*/
|
|
void ValidateScrollInfo(SCROLLINFO* pSInfo)
|
|
{
|
|
_ASSERTE(pSInfo->nMax >= 0 && pSInfo->nPage >= 0);
|
|
if ((UINT)pSInfo->nMax + 1 <= pSInfo->nPage)
|
|
pSInfo->nMax = pSInfo->nPage = 0;
|
|
if (pSInfo->nPos < 0)
|
|
{
|
|
pSInfo->nPos = 0;
|
|
}
|
|
else
|
|
{
|
|
int nMaxScrollPos = pSInfo->nMax - (pSInfo->nPage > 0 ? pSInfo->nPage - 1 : 0);
|
|
if (pSInfo->nPos > nMaxScrollPos)
|
|
pSInfo->nPos = nMaxScrollPos;
|
|
}
|
|
}
|
|
|
|
// Folders processing.
|
|
|
|
/**
|
|
* @brief Delete folder and recursively removes its contents.
|
|
* @param pszFolder - folder name.
|
|
* @return true if operation was completed successfully.
|
|
*/
|
|
BOOL DeleteFolder(PCTSTR pszFolder)
|
|
{
|
|
TCHAR szFindFileTemplate[MAX_PATH];
|
|
PathCombine(szFindFileTemplate, pszFolder, _T("*"));
|
|
|
|
WIN32_FIND_DATA FindData;
|
|
HANDLE hFindFile = FindFirstFile(szFindFileTemplate, &FindData);
|
|
if (hFindFile != INVALID_HANDLE_VALUE)
|
|
{
|
|
BOOL bMore = TRUE;
|
|
while (bMore)
|
|
{
|
|
TCHAR szFileName[MAX_PATH];
|
|
if (FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
|
|
{
|
|
if (_tcscmp(FindData.cFileName, _T(".")) != 0 && _tcscmp(FindData.cFileName, _T("..")) != 0)
|
|
{
|
|
PathCombine(szFileName, pszFolder, FindData.cFileName);
|
|
if (! DeleteFolder(szFileName))
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
PathCombine(szFileName, pszFolder, FindData.cFileName);
|
|
if (! DeleteFile(szFileName))
|
|
break;
|
|
}
|
|
bMore = FindNextFile(hFindFile, &FindData);
|
|
}
|
|
FindClose(hFindFile);
|
|
}
|
|
return RemoveDirectory(pszFolder);
|
|
}
|
|
|
|
/**
|
|
* @brief Creates folder even if parent folder doesn't exist.
|
|
* @param pszFolder - folder name.
|
|
* @return true if operation was completed successfully.
|
|
*/
|
|
BOOL CreateFolder(PCTSTR pszFolder)
|
|
{
|
|
PCTSTR pszOldSegment = PathSkipRoot(pszFolder);
|
|
_ASSERTE(pszOldSegment != NULL);
|
|
if (pszOldSegment == NULL)
|
|
return FALSE;
|
|
TCHAR szFolderPath[MAX_PATH];
|
|
_tcsncpy_s(szFolderPath, countof(szFolderPath), pszFolder, pszOldSegment - pszFolder);
|
|
while (*pszOldSegment)
|
|
{
|
|
PCTSTR pszSegment = PathFindNextComponent(pszOldSegment);
|
|
_tcsncat_s(szFolderPath, countof(szFolderPath), pszOldSegment, pszSegment - pszOldSegment);
|
|
if (! CreateDirectory(szFolderPath, NULL) && GetLastError() != ERROR_ALREADY_EXISTS)
|
|
return FALSE;
|
|
pszOldSegment = pszSegment;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* @brief Creates parent folder for specified file.
|
|
* @param pszFileName - file name.
|
|
* @return true if operation was completed successfully.
|
|
*/
|
|
BOOL CreateParentFolder(PCTSTR pszFileName)
|
|
{
|
|
TCHAR szFolderPath[MAX_PATH];
|
|
_tcscpy_s(szFolderPath, countof(szFolderPath), pszFileName);
|
|
PathRemoveFileSpec(szFolderPath);
|
|
return CreateFolder(szFolderPath);
|
|
}
|
|
|
|
/**
|
|
* @brief Generate temporary folder name and creates it.
|
|
* @param pszTempPath - generated temporary path name.
|
|
* @param nTempPathSize - size of path buffer in characters.
|
|
* @return true if operation was completed successfully.
|
|
*/
|
|
BOOL CreateTempFolder(PTSTR pszTempPath, size_t nTempPathSize)
|
|
{
|
|
TCHAR szSysTempPath[MAX_PATH];
|
|
GetTempPath(countof(szSysTempPath), szSysTempPath);
|
|
PathRemoveBackslash(szSysTempPath);
|
|
DWORD dwTicks = GetTickCount();
|
|
for (;;)
|
|
{
|
|
_stprintf_s(pszTempPath, nTempPathSize, _T("%s\\TEMP%lu"), szSysTempPath, dwTicks);
|
|
if (CreateDirectory(pszTempPath, NULL))
|
|
return TRUE;
|
|
if (GetLastError() != ERROR_ALREADY_EXISTS)
|
|
return FALSE;
|
|
++dwTicks;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Get canonical application name (all non-alphanumeric characters are stripped).
|
|
* @param pszAppName - buffer for resulting application name.
|
|
* @param nBufferSize - size of file name buffer.
|
|
* @param bAllowSpaces - true if spaces are allowed.
|
|
* @return length of the name.
|
|
*/
|
|
size_t GetCanonicalAppName(PTSTR pszAppName, size_t nBufferSize, BOOL bAllowSpaces)
|
|
{
|
|
if (nBufferSize == 0)
|
|
return 0;
|
|
if (*g_szAppName != _T('\0'))
|
|
{
|
|
--nBufferSize;
|
|
size_t nStrLength = _tcslen(g_szAppName);
|
|
if (nStrLength > nBufferSize)
|
|
nStrLength = nBufferSize;
|
|
size_t nSrcPos = 0, nDstPos = 0;
|
|
WORD wCharMask = C1_ALPHA | C1_DIGIT;
|
|
if (bAllowSpaces)
|
|
wCharMask |= C1_SPACE;
|
|
while (nSrcPos < nStrLength)
|
|
{
|
|
WORD arrCharType[2];
|
|
#ifdef _UNICODE
|
|
size_t nCharSize = GetUnicodeCharSize((const BYTE*)(g_szAppName + nSrcPos)) / sizeof(WCHAR);
|
|
GetStringTypeW(CT_CTYPE1, g_szAppName + nSrcPos, (int)nCharSize, arrCharType);
|
|
#else
|
|
size_t nCharSize = IsDBCSLeadByte(g_szAppName[nSrcPos]) ? 2 : 1;
|
|
GetStringTypeA(LOCALE_USER_DEFAULT, CT_CTYPE1, g_szAppName + nSrcPos, (int)nCharSize, arrCharType);
|
|
#endif
|
|
if (*arrCharType & wCharMask)
|
|
{
|
|
pszAppName[nDstPos++] = g_szAppName[nSrcPos++];
|
|
if (nCharSize > 1)
|
|
pszAppName[nDstPos++] = g_szAppName[nSrcPos++];
|
|
}
|
|
else
|
|
{
|
|
/*if (*arrCharType & C1_SPACE)
|
|
pszAppName[nDstPos++] = _T('_');*/
|
|
nSrcPos += (DWORD)nCharSize;
|
|
}
|
|
}
|
|
pszAppName[nDstPos] = _T('\0');
|
|
return nDstPos;
|
|
}
|
|
else
|
|
{
|
|
TCHAR szAppFileName[MAX_PATH];
|
|
if (! GetModuleFileName(NULL, szAppFileName, countof(szAppFileName)))
|
|
return FALSE;
|
|
PTSTR pszFileName = PathFindFileName(szAppFileName);
|
|
PathRemoveExtension(pszFileName);
|
|
_tcscpy_s(pszAppName, nBufferSize, pszFileName);
|
|
return _tcslen(pszAppName);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Create complete log file name.
|
|
* @param pszCompleteLogFileName - output complete file name.
|
|
* @param pszLogFileName - base log file name.
|
|
* @param pszDefFileExtension - default extension.
|
|
* @return true if log file name was created and false otherwise.
|
|
*/
|
|
BOOL GetCompleteLogFileName(PTSTR pszCompleteLogFileName, PCTSTR pszLogFileName, PCTSTR pszDefFileExtension)
|
|
{
|
|
if (pszLogFileName && *pszLogFileName && PathIsRoot(pszLogFileName))
|
|
{
|
|
_tcscpy_s(pszCompleteLogFileName, MAX_PATH, pszLogFileName);
|
|
return TRUE;
|
|
}
|
|
TCHAR szAppDataPath[MAX_PATH];
|
|
if (! SHGetSpecialFolderPath(NULL, szAppDataPath, CSIDL_APPDATA, TRUE))
|
|
return FALSE;
|
|
TCHAR szAppName[MAX_PATH];
|
|
if (! GetCanonicalAppName(szAppName, countof(szAppName), TRUE))
|
|
return FALSE;
|
|
TCHAR szAppFileName[MAX_PATH];
|
|
if (! GetModuleFileName(NULL, szAppFileName, countof(szAppFileName)))
|
|
return FALSE;
|
|
PTSTR pszFileName = PathFindFileName(szAppFileName);
|
|
PathRemoveExtension(pszFileName);
|
|
PTSTR pszAppName = *szAppName ? szAppName : pszFileName;
|
|
PathCombine(pszCompleteLogFileName, szAppDataPath, pszAppName);
|
|
if (pszLogFileName == NULL || *pszLogFileName == _T('\0'))
|
|
{
|
|
if (pszDefFileExtension == NULL || *pszDefFileExtension != _T('.'))
|
|
return FALSE;
|
|
PathAppend(pszCompleteLogFileName, pszFileName);
|
|
PathAddExtension(pszCompleteLogFileName, pszDefFileExtension);
|
|
}
|
|
else
|
|
PathAppend(pszCompleteLogFileName, pszLogFileName);
|
|
return TRUE;
|
|
}
|
|
|
|
// List controls utilities.
|
|
|
|
/**
|
|
* @param hwndList - list view control.
|
|
*/
|
|
|
|
void CListViewOrder::InitList(HWND hwndList)
|
|
{
|
|
m_iColumnNumber = -1;
|
|
m_bAscending = TRUE;
|
|
_ASSERTE(g_pResManager != NULL);
|
|
HWND hwndHeader = ListView_GetHeader(hwndList);
|
|
Header_SetImageList(hwndHeader, g_pResManager->m_hSortArrows);
|
|
}
|
|
|
|
/**
|
|
* @param hwndList - list view control.
|
|
* @param iImage - image index.
|
|
*/
|
|
void CListViewOrder::SetSortImage(HWND hwndList, int iImage)
|
|
{
|
|
_ASSERTE(m_iColumnNumber >= 0);
|
|
HWND hwndHeader = ListView_GetHeader(hwndList);
|
|
HDITEM hdi;
|
|
ZeroMemory(&hdi, sizeof(hdi));
|
|
if (iImage >= 0)
|
|
{
|
|
hdi.mask = HDI_FORMAT | HDI_IMAGE;
|
|
hdi.iImage = iImage;
|
|
hdi.fmt = HDF_LEFT | HDF_STRING | HDF_IMAGE;
|
|
}
|
|
else
|
|
{
|
|
hdi.mask = HDI_FORMAT;
|
|
hdi.fmt = HDF_LEFT | HDF_STRING;
|
|
}
|
|
Header_SetItem(hwndHeader, m_iColumnNumber, &hdi);
|
|
}
|
|
|
|
/**
|
|
* @param hwndList - list view control.
|
|
*/
|
|
void CListViewOrder::ClearSortParams(HWND hwndList)
|
|
{
|
|
SetSortImage(hwndList, -1);
|
|
m_iColumnNumber = -1;
|
|
m_bAscending = TRUE;
|
|
}
|
|
|
|
/**
|
|
* @param hwndList - list view control.
|
|
* @param iColumnNumber - active column number.
|
|
* @param bAscending - ascending/descending sort order.
|
|
*/
|
|
void CListViewOrder::SetSortParams(HWND hwndList, int iColumnNumber, BOOL bAscending)
|
|
{
|
|
if (m_iColumnNumber != iColumnNumber &&
|
|
m_iColumnNumber >= 0)
|
|
{
|
|
SetSortImage(hwndList, -1);
|
|
}
|
|
_ASSERTE(iColumnNumber >= 0);
|
|
m_iColumnNumber = iColumnNumber;
|
|
m_bAscending = bAscending;
|
|
SetSortImage(hwndList, bAscending ? 0 : 1);
|
|
}
|
|
|
|
|
|
/**
|
|
* @param hwndList - list view control.
|
|
* @param iColumnNumber - active column number.
|
|
*/
|
|
void CListViewOrder::ToggleSortParams(HWND hwndList, int iColumnNumber)
|
|
{
|
|
_ASSERTE(iColumnNumber >= 0);
|
|
if (m_iColumnNumber == iColumnNumber)
|
|
{
|
|
m_bAscending = ! m_bAscending;
|
|
SetSortImage(hwndList, m_bAscending ? 0 : 1);
|
|
}
|
|
else
|
|
SetSortParams(hwndList, iColumnNumber, TRUE);
|
|
}
|
|
|
|
/**
|
|
* @brief Compare two list view items.
|
|
* @param lParam1 - 1st item index.
|
|
* @param lParam2 - 2nd item index.
|
|
* @param lParamSort - sort argument.
|
|
* @return comparison result.
|
|
*/
|
|
int CALLBACK ListViewCompareFunc(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
|
|
{
|
|
LISTVIEW_SORT_PARAMS* pLVSortParams = (LISTVIEW_SORT_PARAMS*)lParamSort;
|
|
TCHAR szItem1Text[256];
|
|
ListView_GetItemText(pLVSortParams->hwndList, lParam1, pLVSortParams->iColumnNumber, szItem1Text, countof(szItem1Text));
|
|
TCHAR szItem2Text[256];
|
|
ListView_GetItemText(pLVSortParams->hwndList, lParam2, pLVSortParams->iColumnNumber, szItem2Text, countof(szItem2Text));
|
|
int iResult;
|
|
switch (pLVSortParams->eCompareType)
|
|
{
|
|
case LISTVIEW_SORT_PARAMS::ICT_STRING:
|
|
iResult = _tcscmp(szItem1Text, szItem2Text);
|
|
break;
|
|
case LISTVIEW_SORT_PARAMS::ICT_INTEGER:
|
|
{
|
|
int iItemValue1 = _ttoi(szItem1Text);
|
|
int iItemValue2 = _ttoi(szItem2Text);
|
|
iResult = Comparator(iItemValue1, iItemValue2);
|
|
}
|
|
break;
|
|
case LISTVIEW_SORT_PARAMS::ICT_HEXADECIMAL:
|
|
{
|
|
unsigned uItemValue1 = _tcstoul(szItem1Text, NULL, 16);
|
|
unsigned uItemValue2 = _tcstoul(szItem2Text, NULL, 16);
|
|
iResult = Comparator(uItemValue1, uItemValue2);
|
|
}
|
|
break;
|
|
default:
|
|
iResult = 0;
|
|
break;
|
|
}
|
|
return (pLVSortParams->bAscending ? iResult : -iResult);
|
|
}
|
|
|
|
/** @} */
|