You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
908 lines
23 KiB
908 lines
23 KiB
#include "pch.h"
|
|
|
|
#ifndef CSC_ON_NT
|
|
#define MyStrChr StrChr
|
|
#define MyPathIsUNC(lpT) PathIsUNC(lpT)
|
|
#endif
|
|
|
|
#include "extra.h"
|
|
|
|
// System colors
|
|
COLORREF g_clrHighlightText = 0;
|
|
COLORREF g_clrHighlight = 0;
|
|
COLORREF g_clrWindowText = 0;
|
|
COLORREF g_clrWindow = 0;
|
|
|
|
HBRUSH g_hbrHighlight = 0;
|
|
HBRUSH g_hbrWindow = 0;
|
|
|
|
char const FAR c_szEllipses[] = "...";
|
|
BOOL PUBLIC PathExists(
|
|
LPCSTR pszPath);
|
|
|
|
/*----------------------------------------------------------
|
|
Purpose: Get the system metrics we need
|
|
Returns: --
|
|
Cond: --
|
|
*/
|
|
void PRIVATE GetMetrics(
|
|
WPARAM wParam) // wParam from WM_WININICHANGE
|
|
{
|
|
if ((wParam == 0) || (wParam == SPI_SETNONCLIENTMETRICS))
|
|
{
|
|
g_cxIconSpacing = GetSystemMetrics( SM_CXICONSPACING );
|
|
g_cyIconSpacing = GetSystemMetrics( SM_CYICONSPACING );
|
|
|
|
g_cxBorder = GetSystemMetrics(SM_CXBORDER);
|
|
g_cyBorder = GetSystemMetrics(SM_CYBORDER);
|
|
|
|
g_cxIcon = GetSystemMetrics(SM_CXICON);
|
|
g_cyIcon = GetSystemMetrics(SM_CYICON);
|
|
|
|
g_cxIconMargin = g_cxBorder * 8;
|
|
g_cyIconMargin = g_cyBorder * 2;
|
|
g_cyLabelSpace = g_cyIconMargin + (g_cyBorder * 2);
|
|
g_cxLabelMargin = (g_cxBorder * 2);
|
|
g_cxMargin = g_cxBorder * 5;
|
|
}
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------
|
|
Purpose: Initializes colors
|
|
Returns: --
|
|
Cond: --
|
|
*/
|
|
void PRIVATE InitGlobalColors()
|
|
{
|
|
g_clrWindowText = GetSysColor(COLOR_WINDOWTEXT);
|
|
g_clrWindow = GetSysColor(COLOR_WINDOW);
|
|
g_clrHighlightText = GetSysColor(COLOR_HIGHLIGHTTEXT);
|
|
g_clrHighlight = GetSysColor(COLOR_HIGHLIGHT);
|
|
|
|
g_hbrWindow = GetSysColorBrush(COLOR_WINDOW);
|
|
g_hbrHighlight = GetSysColorBrush(COLOR_HIGHLIGHT);
|
|
}
|
|
|
|
|
|
|
|
/*----------------------------------------------------------
|
|
Purpose: Sets up a bunch of necessary globals
|
|
|
|
Returns: nothing.
|
|
|
|
Cond: --
|
|
*/
|
|
void InitializeAll(WPARAM wParam)
|
|
{
|
|
GetMetrics(wParam); // wParam from WM_WININICHANGE
|
|
InitGlobalColors();
|
|
}
|
|
|
|
/*----------------------------------------------------------
|
|
Purpose: Load the string (if necessary) and format the string
|
|
properly.
|
|
|
|
Returns: A pointer to the allocated string containing the formatted
|
|
message or
|
|
NULL if out of memory
|
|
|
|
Cond: --
|
|
*/
|
|
LPSTR PUBLIC _ConstructMessageString(
|
|
HINSTANCE hinst,
|
|
LPCSTR pszMsg,
|
|
va_list *ArgList)
|
|
{
|
|
char szTemp[MAXBUFLEN];
|
|
LPSTR pszRet;
|
|
LPSTR pszRes;
|
|
|
|
if (HIWORD(pszMsg))
|
|
pszRes = (LPSTR)pszMsg;
|
|
else if (LOWORD(pszMsg) && LoadString(hinst, LOWORD(pszMsg), szTemp, sizeof(szTemp)))
|
|
pszRes = szTemp;
|
|
else
|
|
pszRes = NULL;
|
|
|
|
if (pszRes)
|
|
{
|
|
if (!FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_STRING,
|
|
pszRes, 0, 0, (LPTSTR)&pszRet, 0, ArgList))
|
|
{
|
|
pszRet = NULL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Bad parameter
|
|
pszRet = NULL;
|
|
}
|
|
|
|
return pszRet; // free with LocalFree()
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------
|
|
Purpose: Constructs a formatted string. The returned string
|
|
must be freed using GFree().
|
|
|
|
Returns: TRUE on success
|
|
Cond: --
|
|
*/
|
|
BOOL PUBLIC ConstructMessage(
|
|
LPSTR * ppsz,
|
|
HINSTANCE hinst,
|
|
LPCSTR pszMsg, ...)
|
|
{
|
|
BOOL bRet;
|
|
LPSTR pszRet;
|
|
va_list ArgList;
|
|
|
|
va_start(ArgList, pszMsg);
|
|
|
|
pszRet = _ConstructMessageString(hinst, pszMsg, &ArgList);
|
|
|
|
va_end(ArgList);
|
|
|
|
*ppsz = NULL;
|
|
|
|
if (pszRet)
|
|
{
|
|
bRet = GSetString(ppsz, pszRet);
|
|
LocalFree(pszRet);
|
|
}
|
|
else
|
|
bRet = FALSE;
|
|
|
|
return bRet;
|
|
}
|
|
|
|
#if 0
|
|
/*----------------------------------------------------------
|
|
Purpose: Gets the locality of the path, relative to any
|
|
briefcase. If PL_ROOT or PL_INSIDE is returned,
|
|
pszBuf will contain the path to the root of the
|
|
briefcase.
|
|
|
|
This function may hit the file-system to achieve
|
|
its goal.
|
|
|
|
Worst case: performs 2*n GetFileAttributes, where
|
|
n is the number of components in pszPath.
|
|
|
|
Returns: Path locality (PL_FALSE, PL_ROOT, PL_INSIDE)
|
|
|
|
Cond: --
|
|
*/
|
|
UINT PUBLIC PathGetLocality(
|
|
LPCSTR pszPath,
|
|
LPSTR pszBuf) // Buffer for root path
|
|
{
|
|
UINT uRet;
|
|
|
|
ASSERT(pszPath);
|
|
ASSERT(pszBuf);
|
|
|
|
*pszBuf = NULL_CHAR;
|
|
|
|
// pszPath may be:
|
|
// 1) a path to the briefcase folder itself
|
|
// 2) a path to a file or folder beneath the briefcase
|
|
// 3) a path to something unrelated to a briefcase
|
|
|
|
// We perform our search by first looking in our cache
|
|
// of known briefcase paths (CPATH). If we don't find
|
|
// anything, then we proceed to iterate thru each
|
|
// component of the path, checking for these two things:
|
|
//
|
|
// 1) A directory with the system attribute
|
|
// 2) The existence of a brfcase.dat file in the directory.
|
|
//
|
|
uRet = CPATH_GetLocality(pszPath, pszBuf);
|
|
if (PL_FALSE == uRet)
|
|
{
|
|
int cnt = 0;
|
|
|
|
lstrcpy(pszBuf, pszPath);
|
|
do
|
|
{
|
|
if (PathCheckForBriefcase(pszBuf, (DWORD)-1))
|
|
{
|
|
int atom;
|
|
|
|
uRet = cnt > 0 ? PL_INSIDE : PL_ROOT;
|
|
|
|
// Add this briefcase path to our cache
|
|
//
|
|
atom = Atom_Add(pszBuf);
|
|
if (ATOM_ERR != atom)
|
|
CPATH_Replace(atom);
|
|
|
|
break; // Done
|
|
}
|
|
|
|
cnt++;
|
|
|
|
} while (PathRemoveFileSpec(pszBuf));
|
|
|
|
if (PL_FALSE == uRet)
|
|
*pszBuf = NULL_CHAR;
|
|
}
|
|
|
|
return uRet;
|
|
}
|
|
#endif
|
|
|
|
/*----------------------------------------------------------
|
|
Purpose: Convert FILETIME struct to a readable string
|
|
|
|
Returns: String
|
|
Cond: --
|
|
*/
|
|
void PUBLIC FileTimeToDateTimeString(
|
|
LPFILETIME pft,
|
|
LPSTR pszBuf,
|
|
int cchBuf)
|
|
{
|
|
SYSTEMTIME st;
|
|
FILETIME ftLocal;
|
|
|
|
FileTimeToLocalFileTime(pft, &ftLocal);
|
|
FileTimeToSystemTime(&ftLocal, &st);
|
|
GetDateFormatA(LOCALE_USER_DEFAULT, DATE_SHORTDATE, &st, NULL, pszBuf, cchBuf/2);
|
|
pszBuf += lstrlen(pszBuf);
|
|
*pszBuf++ = ' ';
|
|
GetTimeFormatA(LOCALE_USER_DEFAULT, TIME_NOSECONDS, &st, NULL, pszBuf, cchBuf/2);
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------
|
|
Purpose: Sees whether the entire string will fit in *prc.
|
|
If not, compute the numbder of chars that will fit
|
|
(including ellipses). Returns length of string in
|
|
*pcchDraw.
|
|
|
|
Taken from COMMCTRL.
|
|
|
|
Returns: TRUE if the string needed ellipses
|
|
Cond: --
|
|
*/
|
|
BOOL PRIVATE NeedsEllipses(
|
|
HDC hdc,
|
|
LPCSTR pszText,
|
|
RECT * prc,
|
|
int * pcchDraw,
|
|
int cxEllipses)
|
|
{
|
|
int cchText;
|
|
int cxRect;
|
|
int ichMin, ichMax, ichMid;
|
|
SIZE siz;
|
|
|
|
cxRect = prc->right - prc->left;
|
|
|
|
cchText = lstrlen(pszText);
|
|
|
|
if (cchText == 0)
|
|
{
|
|
*pcchDraw = cchText;
|
|
return FALSE;
|
|
}
|
|
|
|
GetTextExtentPoint(hdc, pszText, cchText, &siz);
|
|
|
|
if (siz.cx <= cxRect)
|
|
{
|
|
*pcchDraw = cchText;
|
|
return FALSE;
|
|
}
|
|
|
|
cxRect -= cxEllipses;
|
|
|
|
// If no room for ellipses, always show first character.
|
|
//
|
|
ichMax = 1;
|
|
if (cxRect > 0)
|
|
{
|
|
// Binary search to find character that will fit
|
|
ichMin = 0;
|
|
ichMax = cchText;
|
|
while (ichMin < ichMax)
|
|
{
|
|
// Be sure to round up, to make sure we make progress in
|
|
// the loop if ichMax == ichMin + 1.
|
|
//
|
|
ichMid = (ichMin + ichMax + 1) / 2;
|
|
|
|
GetTextExtentPoint(hdc, &pszText[ichMin], ichMid - ichMin, &siz);
|
|
|
|
if (siz.cx < cxRect)
|
|
{
|
|
ichMin = ichMid;
|
|
cxRect -= siz.cx;
|
|
}
|
|
else if (siz.cx > cxRect)
|
|
{
|
|
ichMax = ichMid - 1;
|
|
}
|
|
else
|
|
{
|
|
// Exact match up up to ichMid: just exit.
|
|
//
|
|
ichMax = ichMid;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Make sure we always show at least the first character...
|
|
//
|
|
if (ichMax < 1)
|
|
ichMax = 1;
|
|
}
|
|
|
|
*pcchDraw = ichMax;
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
#define CCHELLIPSES 3
|
|
#define DT_LVWRAP (DT_CENTER | DT_WORDBREAK | DT_NOPREFIX | DT_EDITCONTROL)
|
|
|
|
/*----------------------------------------------------------
|
|
Purpose: Draws text the shell's way.
|
|
|
|
Taken from COMMCTRL.
|
|
|
|
Returns: --
|
|
|
|
Cond: This function requires TRANSPARENT background mode
|
|
and a properly selected font.
|
|
*/
|
|
void PUBLIC MyDrawText(
|
|
HDC hdc,
|
|
LPCSTR pszText,
|
|
RECT FAR* prc,
|
|
UINT flags,
|
|
int cyChar,
|
|
int cxEllipses,
|
|
COLORREF clrText,
|
|
COLORREF clrTextBk)
|
|
{
|
|
int cchText;
|
|
COLORREF clrSave;
|
|
COLORREF clrSaveBk;
|
|
UINT uETOFlags = 0;
|
|
RECT rc;
|
|
char ach[MAX_PATH + CCHELLIPSES];
|
|
|
|
// REVIEW: Performance idea:
|
|
// We could cache the currently selected text color
|
|
// so we don't have to set and restore it each time
|
|
// when the color is the same.
|
|
//
|
|
if (!pszText)
|
|
return;
|
|
|
|
rc = *prc;
|
|
|
|
// If needed, add in a little extra margin...
|
|
//
|
|
if (IsFlagSet(flags, MDT_EXTRAMARGIN))
|
|
{
|
|
rc.left += g_cxLabelMargin * 3;
|
|
rc.right -= g_cxLabelMargin * 3;
|
|
}
|
|
else
|
|
{
|
|
rc.left += g_cxLabelMargin;
|
|
rc.right -= g_cxLabelMargin;
|
|
}
|
|
|
|
if (IsFlagSet(flags, MDT_ELLIPSES) &&
|
|
NeedsEllipses(hdc, pszText, &rc, &cchText, cxEllipses))
|
|
{
|
|
hmemcpy(ach, pszText, cchText);
|
|
lstrcpy(ach + cchText, c_szEllipses);
|
|
|
|
pszText = ach;
|
|
|
|
// Left-justify, in case there's no room for all of ellipses
|
|
//
|
|
ClearFlag(flags, (MDT_RIGHT | MDT_CENTER));
|
|
SetFlag(flags, MDT_LEFT);
|
|
|
|
cchText += CCHELLIPSES;
|
|
}
|
|
else
|
|
{
|
|
cchText = lstrlen(pszText);
|
|
}
|
|
|
|
if (IsFlagSet(flags, MDT_TRANSPARENT))
|
|
{
|
|
clrSave = SetTextColor(hdc, 0x000000);
|
|
}
|
|
else
|
|
{
|
|
uETOFlags |= ETO_OPAQUE;
|
|
|
|
if (IsFlagSet(flags, MDT_SELECTED))
|
|
{
|
|
clrSave = SetTextColor(hdc, g_clrHighlightText);
|
|
clrSaveBk = SetBkColor(hdc, g_clrHighlight);
|
|
|
|
if (IsFlagSet(flags, MDT_DRAWTEXT))
|
|
{
|
|
FillRect(hdc, prc, g_hbrHighlight);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (clrText == CLR_DEFAULT && clrTextBk == CLR_DEFAULT)
|
|
{
|
|
clrSave = SetTextColor(hdc, g_clrWindowText);
|
|
clrSaveBk = SetBkColor(hdc, g_clrWindow);
|
|
|
|
if (IsFlagSet(flags, MDT_DRAWTEXT | MDT_DESELECTED))
|
|
{
|
|
FillRect(hdc, prc, g_hbrWindow);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
HBRUSH hbr;
|
|
|
|
if (clrText == CLR_DEFAULT)
|
|
clrText = g_clrWindowText;
|
|
|
|
if (clrTextBk == CLR_DEFAULT)
|
|
clrTextBk = g_clrWindow;
|
|
|
|
clrSave = SetTextColor(hdc, clrText);
|
|
clrSaveBk = SetBkColor(hdc, clrTextBk);
|
|
|
|
if (IsFlagSet(flags, MDT_DRAWTEXT | MDT_DESELECTED))
|
|
{
|
|
hbr = CreateSolidBrush(GetNearestColor(hdc, clrTextBk));
|
|
if (hbr)
|
|
{
|
|
FillRect(hdc, prc, hbr);
|
|
DeleteObject(hbr);
|
|
}
|
|
else
|
|
FillRect(hdc, prc, GetStockObject(WHITE_BRUSH));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// If we want the item to display as if it was depressed, we will
|
|
// offset the text rectangle down and to the left
|
|
if (IsFlagSet(flags, MDT_DEPRESSED))
|
|
OffsetRect(&rc, g_cxBorder, g_cyBorder);
|
|
|
|
if (IsFlagSet(flags, MDT_DRAWTEXT))
|
|
{
|
|
UINT uDTFlags = DT_LVWRAP;
|
|
|
|
if (IsFlagClear(flags, MDT_CLIPPED))
|
|
uDTFlags |= DT_NOCLIP;
|
|
|
|
DrawText(hdc, pszText, cchText, &rc, uDTFlags);
|
|
}
|
|
else
|
|
{
|
|
if (IsFlagClear(flags, MDT_LEFT))
|
|
{
|
|
SIZE siz;
|
|
|
|
GetTextExtentPoint(hdc, pszText, cchText, &siz);
|
|
|
|
if (IsFlagSet(flags, MDT_CENTER))
|
|
rc.left = (rc.left + rc.right - siz.cx) / 2;
|
|
else
|
|
{
|
|
ASSERT(IsFlagSet(flags, MDT_RIGHT));
|
|
rc.left = rc.right - siz.cx;
|
|
}
|
|
}
|
|
|
|
if (IsFlagSet(flags, MDT_VCENTER))
|
|
{
|
|
// Center vertically
|
|
rc.top += (rc.bottom - rc.top - cyChar) / 2;
|
|
}
|
|
|
|
if (IsFlagSet(flags, MDT_CLIPPED))
|
|
uETOFlags |= ETO_CLIPPED;
|
|
|
|
ExtTextOut(hdc, rc.left, rc.top, uETOFlags, prc, pszText, cchText, NULL);
|
|
}
|
|
|
|
if (flags & (MDT_SELECTED | MDT_DESELECTED | MDT_TRANSPARENT))
|
|
{
|
|
SetTextColor(hdc, clrSave);
|
|
if (IsFlagClear(flags, MDT_TRANSPARENT))
|
|
SetBkColor(hdc, clrSaveBk);
|
|
}
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Given a pointer to a point in a path - return a ptr the start of the
|
|
// next path component. Path components are delimted by slashes or the
|
|
// null at the end.
|
|
// There's special handling for UNC names.
|
|
// This returns NULL if you pass in a pointer to a NULL ie if you're about
|
|
// to go off the end of the path.
|
|
LPSTR PUBLIC PathFindNextComponentI(LPCSTR lpszPath)
|
|
{
|
|
LPSTR lpszLastSlash;
|
|
|
|
// Are we at the end of a path.
|
|
if (!*lpszPath)
|
|
{
|
|
// Yep, quit.
|
|
return NULL;
|
|
}
|
|
// Find the next slash.
|
|
// REVIEW UNDONE - can slashes be quoted?
|
|
lpszLastSlash = MyStrChr(lpszPath, '\\');
|
|
// Is there a slash?
|
|
if (!lpszLastSlash)
|
|
{
|
|
// No - Return a ptr to the NULL.
|
|
return (LPSTR) (lpszPath+lstrlen(lpszPath));
|
|
}
|
|
else
|
|
{
|
|
// Is it a UNC style name?
|
|
if ('\\' == *(lpszLastSlash+1))
|
|
{
|
|
// Yep, skip over the second slash.
|
|
return lpszLastSlash+2;
|
|
}
|
|
else
|
|
{
|
|
// Nope. just skip over one slash.
|
|
return lpszLastSlash+1;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*----------------------------------------------------------
|
|
Purpose: Convert a file spec to make it look a bit better
|
|
if it is all upper case chars.
|
|
|
|
Returns: --
|
|
Cond: --
|
|
*/
|
|
BOOL PRIVATE PathMakeComponentPretty(LPSTR lpPath)
|
|
{
|
|
LPSTR lp;
|
|
|
|
// REVIEW: INTL need to deal with lower case chars in (>127) range?
|
|
|
|
// check for all uppercase
|
|
for (lp = lpPath; *lp; lp = AnsiNext(lp)) {
|
|
if ((*lp >= 'a') && (*lp <= 'z'))
|
|
return FALSE; // this is a LFN, dont mess with it
|
|
}
|
|
|
|
AnsiLower(lpPath);
|
|
AnsiUpperBuff(lpPath, 1);
|
|
return TRUE; // did the conversion
|
|
}
|
|
|
|
/*----------------------------------------------------------
|
|
Purpose: Takes the path and makes it presentable.
|
|
|
|
The rules are:
|
|
If the LFN name is simply the short name (all caps),
|
|
then convert to lowercase with first letter capitalized
|
|
|
|
Returns: --
|
|
Cond: --
|
|
*/
|
|
void PUBLIC PathMakePresentable(
|
|
LPSTR pszPath)
|
|
{
|
|
LPSTR pszComp; // pointers to begining and
|
|
LPSTR pszEnd; // end of path component
|
|
LPSTR pch;
|
|
int cComponent = 0;
|
|
BOOL bUNCPath;
|
|
char ch;
|
|
|
|
bUNCPath = MyPathIsUNC(pszPath);
|
|
|
|
pszComp = pszPath;
|
|
while (pszEnd = PathFindNextComponentI(pszComp))
|
|
{
|
|
// pszEnd may be pointing to the right of the backslash
|
|
// beyond the path component, so back up one
|
|
//
|
|
ch = *pszEnd;
|
|
*pszEnd = 0; // temporary null
|
|
|
|
// pszComp points to the path component
|
|
//
|
|
pch = AnsiNext(pszComp);
|
|
if (':' == *pch)
|
|
{
|
|
// Simply capitalize the drive-portion of the path
|
|
//
|
|
AnsiUpper(pszComp);
|
|
}
|
|
else if (bUNCPath && cComponent++ < 3)
|
|
{
|
|
// Network server or share name
|
|
// BUGBUG: handle LFN network names
|
|
//
|
|
AnsiUpper(pszComp);
|
|
PathMakeComponentPretty(pszComp);
|
|
}
|
|
else
|
|
{
|
|
// Normal path component
|
|
//
|
|
PathMakeComponentPretty(pszComp);
|
|
}
|
|
|
|
*pszEnd = ch;
|
|
pszComp = pszEnd;
|
|
}
|
|
}
|
|
|
|
/*----------------------------------------------------------
|
|
Purpose: Get a string from the resource string table. Returned
|
|
ptr is a ptr to static memory. The next call to this
|
|
function will wipe out the prior contents.
|
|
Returns: Ptr to string
|
|
Cond: --
|
|
*/
|
|
LPSTR PUBLIC SzFromIDS(
|
|
UINT ids, // resource ID
|
|
LPSTR pszBuf,
|
|
UINT cchBuf)
|
|
{
|
|
ASSERT(pszBuf);
|
|
|
|
*pszBuf = NULL_CHAR;
|
|
LoadString(vhinstCur, ids, pszBuf, cchBuf);
|
|
return pszBuf;
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------
|
|
Purpose: Sets the rectangle with the bounding extent of the given string.
|
|
Returns: Rectangle
|
|
Cond: --
|
|
*/
|
|
void PUBLIC SetRectFromExtent(
|
|
HDC hdc,
|
|
LPRECT lprect,
|
|
LPCSTR lpcsz)
|
|
{
|
|
SIZE size;
|
|
|
|
GetTextExtentPoint(hdc, lpcsz, lstrlen(lpcsz), &size);
|
|
SetRect(lprect, 0, 0, size.cx, size.cy);
|
|
}
|
|
|
|
/*----------------------------------------------------------
|
|
Purpose: Copies psz into *ppszBuf. Will alloc or realloc *ppszBuf
|
|
accordingly.
|
|
|
|
Returns: TRUE on success
|
|
Cond: --
|
|
*/
|
|
BOOL PUBLIC GSetString(
|
|
LPSTR * ppszBuf,
|
|
LPCSTR psz)
|
|
{
|
|
BOOL bRet = FALSE;
|
|
DWORD cb;
|
|
|
|
ASSERT(ppszBuf);
|
|
ASSERT(psz);
|
|
|
|
cb = CbFromCch(lstrlen(psz)+CCH_NUL);
|
|
|
|
if (*ppszBuf)
|
|
{
|
|
// Need to reallocate?
|
|
if (cb > GGetSize(*ppszBuf))
|
|
{
|
|
// Yes
|
|
LPSTR pszT = GReAlloc(*ppszBuf, cb);
|
|
if (pszT)
|
|
{
|
|
*ppszBuf = pszT;
|
|
bRet = TRUE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// No
|
|
bRet = TRUE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
*ppszBuf = (LPSTR)GAlloc(cb);
|
|
if (*ppszBuf)
|
|
{
|
|
bRet = TRUE;
|
|
}
|
|
}
|
|
|
|
if (bRet)
|
|
{
|
|
ASSERT(*ppszBuf);
|
|
lstrcpy(*ppszBuf, psz);
|
|
}
|
|
return bRet;
|
|
}
|
|
|
|
/*----------------------------------------------------------
|
|
Purpose: Gets the file info given a path. If the path refers
|
|
to a directory, then simply the path field is filled.
|
|
|
|
If himl != NULL, then the function will add the file's
|
|
image to the provided image list and set the image index
|
|
field in the *ppfi.
|
|
|
|
Returns: standard hresult
|
|
Cond: --
|
|
*/
|
|
HRESULT PUBLIC FICreate(
|
|
LPCSTR pszPath,
|
|
FileInfo ** ppfi,
|
|
UINT uFlags)
|
|
{
|
|
HRESULT hres = ResultFromScode(E_OUTOFMEMORY);
|
|
int cchPath;
|
|
SHFILEINFO sfi;
|
|
UINT uInfoFlags = SHGFI_DISPLAYNAME | SHGFI_ATTRIBUTES;
|
|
DWORD dwAttr;
|
|
|
|
ASSERT(pszPath);
|
|
ASSERT(ppfi);
|
|
|
|
// Get shell file info
|
|
if (IsFlagSet(uFlags, FIF_ICON))
|
|
uInfoFlags |= SHGFI_ICON;
|
|
if (IsFlagSet(uFlags, FIF_DONTTOUCH))
|
|
{
|
|
uInfoFlags |= SHGFI_USEFILEATTRIBUTES;
|
|
|
|
// Today, FICreate is not called for folders, so this is ifdef'd out
|
|
#ifdef SUPPORT_FOLDERS
|
|
dwAttr = IsFlagSet(uFlags, FIF_FOLDER) ? FILE_ATTRIBUTE_DIRECTORY : 0;
|
|
#else
|
|
dwAttr = 0;
|
|
#endif
|
|
}
|
|
else
|
|
dwAttr = 0;
|
|
|
|
if (SHGetFileInfo(pszPath, dwAttr, &sfi, sizeof(sfi), uInfoFlags))
|
|
{
|
|
// Allocate enough for the structure, plus buffer for the fully qualified
|
|
// path and buffer for the display name (and extra null terminator).
|
|
cchPath = lstrlen(pszPath);
|
|
|
|
*ppfi = GAlloc(sizeof(FileInfo)+cchPath+1-sizeof((*ppfi)->szPath)+lstrlen(sfi.szDisplayName)+1);
|
|
if (*ppfi)
|
|
{
|
|
FileInfo * pfi = *ppfi;
|
|
|
|
pfi->pszDisplayName = pfi->szPath+cchPath+1;
|
|
lstrcpy(pfi->pszDisplayName, sfi.szDisplayName);
|
|
|
|
if (IsFlagSet(uFlags, FIF_ICON))
|
|
pfi->hicon = sfi.hIcon;
|
|
|
|
pfi->dwAttributes = sfi.dwAttributes;
|
|
|
|
// Does the path refer to a directory?
|
|
if (FIIsFolder(pfi))
|
|
{
|
|
// Yes; just fill in the path field
|
|
lstrcpy(pfi->szPath, pszPath);
|
|
hres = NOERROR;
|
|
}
|
|
else
|
|
{
|
|
// No; assume the file exists?
|
|
if (IsFlagClear(uFlags, FIF_DONTTOUCH))
|
|
{
|
|
// Yes; get the time, date and size of the file
|
|
HANDLE hfile = CreateFile(pszPath, GENERIC_READ,
|
|
FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,
|
|
NULL);
|
|
|
|
if (hfile == INVALID_HANDLE_VALUE)
|
|
{
|
|
GFree(*ppfi);
|
|
hres = ResultFromScode(E_HANDLE);
|
|
}
|
|
else
|
|
{
|
|
hres = NOERROR;
|
|
|
|
lstrcpy(pfi->szPath, pszPath);
|
|
pfi->dwSize = GetFileSize(hfile, NULL);
|
|
GetFileTime(hfile, NULL, NULL, &pfi->ftMod);
|
|
CloseHandle(hfile);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// No; use what we have
|
|
hres = NOERROR;
|
|
lstrcpy(pfi->szPath, pszPath);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if (!PathExists(pszPath))
|
|
{
|
|
// Differentiate between out of memory and file not found
|
|
hres = E_FAIL;
|
|
}
|
|
|
|
return hres;
|
|
}
|
|
|
|
/*----------------------------------------------------------
|
|
Purpose: Set the path entry. This can move the pfi.
|
|
|
|
Returns: FALSE on out of memory
|
|
Cond: --
|
|
*/
|
|
BOOL PUBLIC FISetPath(
|
|
FileInfo ** ppfi,
|
|
LPCSTR pszPathNew,
|
|
UINT uFlags)
|
|
{
|
|
ASSERT(ppfi);
|
|
ASSERT(pszPathNew);
|
|
|
|
FIFree(*ppfi);
|
|
|
|
return SUCCEEDED(FICreate(pszPathNew, ppfi, uFlags));
|
|
}
|
|
|
|
/*----------------------------------------------------------
|
|
Purpose: Free our file info struct
|
|
Returns: --
|
|
Cond: --
|
|
*/
|
|
void PUBLIC FIFree(
|
|
FileInfo * pfi)
|
|
{
|
|
if (pfi)
|
|
{
|
|
if (pfi->hicon)
|
|
DestroyIcon(pfi->hicon);
|
|
|
|
GFree(pfi); // This macro already checks for NULL pfi condition
|
|
}
|
|
}
|
|
|
|
/*----------------------------------------------------------
|
|
Purpose: Returns TRUE if the file/directory exists.
|
|
|
|
Returns: see above
|
|
Cond: --
|
|
*/
|
|
BOOL PUBLIC PathExists(
|
|
LPCSTR pszPath)
|
|
{
|
|
return GetFileAttributes(pszPath) != 0xFFFFFFFF;
|
|
}
|
|
|
|
|