557 lines
12 KiB
C
557 lines
12 KiB
C
/************************************************************/
|
|
/* Windows Write, Copyright 1985-1992 Microsoft Corporation */
|
|
/************************************************************/
|
|
|
|
/* util.c -- more frequently used utility routines */
|
|
#define NOCOLOR
|
|
#define NOCOMM
|
|
#define NOCLIPBOARD
|
|
#define NOCTLMGR
|
|
#define NOGDICAPMASKS
|
|
#define NOMENUS
|
|
#define NOSOUND
|
|
#define NOVIRTUALKEYCODES
|
|
#define NOWINMESSAGES
|
|
#define NOWINSTYLES
|
|
#include <windows.h>
|
|
|
|
#if WINVER < 0x300
|
|
#define SM_CURSORLEVEL 25
|
|
#endif
|
|
|
|
#include "mw.h"
|
|
#include "doslib.h"
|
|
#include "str.h"
|
|
#include "machdefs.h"
|
|
#include "cmddefs.h"
|
|
#include "propdefs.h"
|
|
#include "fkpdefs.h"
|
|
#include "docdefs.h"
|
|
#include "debug.h"
|
|
#include "editdefs.h"
|
|
#include "wwdefs.h"
|
|
#define NOKCCODES
|
|
#include "ch.h"
|
|
|
|
extern struct DOD (**hpdocdod)[];
|
|
extern HANDLE hMmwModInstance;
|
|
|
|
CHAR *PchFillPchId(PCH, int);
|
|
|
|
typeCP CpMax(cp1, cp2)
|
|
typeCP cp1, cp2;
|
|
{{ /* return larger of two cps */
|
|
return((cp1 > cp2) ? cp1 : cp2);
|
|
}} /* end of C p M a x */
|
|
|
|
|
|
typeCP CpMin(cp1, cp2)
|
|
typeCP cp1, cp2;
|
|
{{ /* return smaller of two cps */
|
|
return((cp1 < cp2) ? cp1 : cp2);
|
|
}} /* end of C p M i n */
|
|
|
|
|
|
unsigned umin( w1, w2 )
|
|
register unsigned w1, w2;
|
|
{ return (w1 < w2) ? w1 : w2; }
|
|
|
|
|
|
unsigned umax( w1, w2 )
|
|
register unsigned w1, w2;
|
|
{ return (w1 > w2) ? w1 : w2; }
|
|
|
|
|
|
int imin( i1, i2 )
|
|
register int i1, i2;
|
|
{ return (i1 < i2) ? i1 : i2; }
|
|
|
|
|
|
int imax( i1, i2 )
|
|
register int i1, i2;
|
|
{ return (i1 > i2) ? i1 : i2; }
|
|
|
|
|
|
#define BZNATIVE
|
|
#ifndef BZNATIVE
|
|
/* C C H D I F F E R */
|
|
/* commented out, moved to native code in lib.asm bz 6/20/85 */
|
|
int CchDiffer(rgch1, rgch2, cch)
|
|
register CHAR *rgch1, *rgch2;
|
|
int cch;
|
|
{{ /* Return cch of shortest prefix leaving a common remainder */
|
|
int ich;
|
|
|
|
for (ich = cch - 1; ich >= 0; ich--)
|
|
if (rgch1[ich] != rgch2[ich])
|
|
break;
|
|
return ich + 1;
|
|
}}
|
|
#endif
|
|
|
|
|
|
int CchSz(sz)
|
|
register CHAR sz[];
|
|
{ /* Returns length of string in bytes, including trailing 0 */
|
|
register int cch = 1;
|
|
while (*sz++ != 0)
|
|
cch++;
|
|
return cch;
|
|
} /* end of C c h S z */
|
|
|
|
|
|
|
|
typeCP CpMacText(doc)
|
|
register int doc;
|
|
{
|
|
#ifdef FOOTNOTES
|
|
struct FNTB **hfntb;
|
|
if ((hfntb = HfntbGet(doc)) != 0)
|
|
return((**hfntb).rgfnd[0].cpFtn - ccpEol);
|
|
else
|
|
#endif /* FOOTNOTES */
|
|
return((**hpdocdod)[doc].cpMac);
|
|
|
|
} /* end of C p M a c T e x t */
|
|
|
|
|
|
struct FNTB **HfntbGet(doc)
|
|
register int doc;
|
|
{ /* Return hfntb if doc has one, 0 if none or style sheet */
|
|
#ifdef STYLES
|
|
register struct DOD *pdod;
|
|
|
|
if ((pdod = &(**hpdocdod)[doc])->dty == dtySsht)
|
|
return 0;
|
|
else
|
|
#endif
|
|
return (**hpdocdod)[doc].hfntb;
|
|
} /* end of H F n t b G e t */
|
|
|
|
|
|
/* N O U N D O */
|
|
NoUndo()
|
|
{
|
|
extern struct UAB vuab;
|
|
vuab.uac = uacNil;
|
|
SetUndoMenuStr(IDSTRUndoBase);
|
|
} /* end of N o U n d o */
|
|
|
|
|
|
|
|
|
|
SetUndoMenuStr(idstr)
|
|
int idstr;
|
|
{
|
|
extern int idstrCurrentUndo;
|
|
|
|
idstrCurrentUndo = idstr;
|
|
}
|
|
|
|
|
|
/* Returns number chars copied EXCLUDING zero terminator */
|
|
|
|
int CchCopySz(pchFrom, pchTo)
|
|
register PCH pchFrom;
|
|
register PCH pchTo;
|
|
{
|
|
int cch = 0;
|
|
while ((*pchTo = *pchFrom++) != 0)
|
|
{
|
|
pchTo++;
|
|
cch++;
|
|
}
|
|
return cch;
|
|
} /* end of C c h C o p y S z */
|
|
|
|
|
|
|
|
|
|
/*---------------------------------------------------------------------------
|
|
-- Routine: WCompSz(psz1,psz2)
|
|
-- Description and Usage:
|
|
Alphabetically compares the two null-terminated strings lpsz1 and lpsz2.
|
|
Upper case alpha characters are mapped to lower case.
|
|
Comparison of non-alpha characters is by ascii code.
|
|
Returns 0 if they are equal, a negative number if lpsz1 precedes lpsz2, and
|
|
a non-zero positive number if lpsz2 precedes lpsz1.
|
|
-- Arguments:
|
|
psz1, psz2 - pointers to two null-terminated strings to compare
|
|
-- Returns:
|
|
a short - 0 if strings are equal, negative number if lpsz1 precedes lpsz2,
|
|
and non-zero positive number if psz2 precedes psz1.
|
|
-- Side-effects: none
|
|
-- Bugs:
|
|
-- History:
|
|
3/14/83 - created (tsr)
|
|
6/12/86 - Kanji Version (yxy)
|
|
----------------------------------------------------------------------------*/
|
|
short
|
|
|
|
WCompSz(psz1,psz2)
|
|
register PCH psz1;
|
|
register PCH psz2;
|
|
{
|
|
int ch1;
|
|
int ch2;
|
|
|
|
for(ch1=ChLower(*psz1++),ch2=ChLower(*psz2++);
|
|
ch1==ch2;
|
|
ch1=ChLower(*psz1++),ch2=ChLower(*psz2++))
|
|
{
|
|
if(ch1 == '\0')
|
|
return(0);
|
|
}
|
|
return(ch1-ch2);
|
|
} /* end of W C o m p S z */
|
|
|
|
|
|
/*---------------------------------------------------------------------------
|
|
-- Routine: ChLower(ch)
|
|
-- Description and Usage:
|
|
Converts its argument to lower case iff its argument is upper case.
|
|
Returns the de-capitalized character or the initial char if it wasn't caps.
|
|
-- Arguments:
|
|
ch - character to be de-capitalized
|
|
-- Returns:
|
|
a character - initial character, de-capitalized if needed.
|
|
-- Side-effects:
|
|
-- Bugs:
|
|
-- History:
|
|
3/14/83 - created (tsr)
|
|
----------------------------------------------------------------------------*/
|
|
int
|
|
ChLower(ch)
|
|
register CHAR ch;
|
|
{ /* use Windows' ANSI char set, the difference of upper/lower case
|
|
is also 20 (HEX) for foreign chars */
|
|
#ifdef JAPAN
|
|
// check for half-size katakana.
|
|
extern struct WWD rgwwd[];
|
|
extern BOOL IsKanaInDBCS(int);
|
|
static TEXTMETRIC tm;
|
|
if(IsWindow(wwdCurrentDoc.wwptr)
|
|
&& GetTextMetrics(wwdCurrentDoc.hDC,(LPTEXTMETRIC)&tm)){
|
|
if(tm.tmCharSet == SHIFTJIS_CHARSET && IsKanaInDBCS((int)ch))
|
|
return (int)(0x00ff&ch);
|
|
}
|
|
#endif
|
|
|
|
#ifdef KOREA
|
|
if(isupper(ch) && !((ch > 0xa1) && (ch < 0xfe)))
|
|
#else
|
|
if(isupper(ch))
|
|
#endif
|
|
|
|
return(ch + ('a' - 'A')); /* foreign is taken care of */
|
|
else
|
|
return ch;
|
|
} /* end of C h L o w e r */
|
|
|
|
|
|
|
|
static int cLongOpCount = 0; /* to ensure we don't do too much hide cursor */
|
|
|
|
StartLongOp()
|
|
{
|
|
extern int vfInLongOperation;
|
|
extern int vfCursorVisible;
|
|
extern int vfMouseExist;
|
|
extern HCURSOR vhcHourGlass;
|
|
|
|
int cursorlevel;
|
|
|
|
if (cLongOpCount++ == 0)
|
|
{
|
|
vfInLongOperation = TRUE;
|
|
vfCursorVisible = TRUE;
|
|
|
|
if (!vfMouseExist)
|
|
{ /* in a mouseless system, set the cursor to middle of window */
|
|
extern HWND vhWndMsgBoxParent;
|
|
extern HWND hParentWw;
|
|
extern int vfInitializing;
|
|
RECT rect;
|
|
POINT pt;
|
|
HWND hWnd = vhWndMsgBoxParent;
|
|
|
|
if (vhWndMsgBoxParent == NULL)
|
|
hWnd = hParentWw; /* next choice */
|
|
if (!vfInitializing && hWnd != NULL && IsWindow(hWnd))
|
|
{ /* we have a good window to put in */
|
|
GetClientRect(hWnd, (LPRECT)&rect);
|
|
pt.x = (rect.right - rect.left) / 2;
|
|
pt.y = (rect.bottom - rect.top) / 2;
|
|
ClientToScreen(hWnd, (LPPOINT)&pt);
|
|
}
|
|
else
|
|
{ /* put in the middle of screen */
|
|
HDC hDCScreen = GetDC(NULL);
|
|
if (hDCScreen == NULL)
|
|
goto Out; /* the worst, setcursor only */
|
|
pt.x = GetDeviceCaps(hDCScreen, HORZRES) / 2;
|
|
pt.y = GetDeviceCaps(hDCScreen, VERTRES) / 2;
|
|
ReleaseDC(NULL, hDCScreen);
|
|
}
|
|
SetCursorPos(pt.x, pt.y);
|
|
}
|
|
Out:
|
|
SetCursor(vhcHourGlass);
|
|
#if WINVER < 0x300
|
|
ShowCursor(TRUE);
|
|
|
|
/* precaution - make sure the cusor is visible */
|
|
cursorlevel = GetSystemMetrics(SM_CURSORLEVEL);
|
|
#else
|
|
/* use a supported method to get cursor level! ..pault 2/6/90 */
|
|
cursorlevel = ShowCursor(TRUE);
|
|
#endif
|
|
while (cursorlevel++ < 0)
|
|
ShowCursor(TRUE);
|
|
}
|
|
}
|
|
|
|
|
|
EndLongOp(hc)
|
|
HCURSOR hc; /* cursor to be changed to */
|
|
{
|
|
extern int vfInLongOperation;
|
|
extern int vfCursorVisible;
|
|
extern int vfMouseExist;
|
|
extern int vfDeactByOtherApp;
|
|
|
|
int cursorlevel;
|
|
|
|
if (cLongOpCount > 0)
|
|
{
|
|
if (vfDeactByOtherApp && (cLongOpCount == 1)) // OLE presents this case
|
|
{
|
|
vfInLongOperation = FALSE;
|
|
vfCursorVisible = FALSE;
|
|
SetCursor(vfMouseExist ? hc : NULL);
|
|
}
|
|
else if (--cLongOpCount == 0)
|
|
{
|
|
vfInLongOperation = FALSE;
|
|
vfCursorVisible = FALSE;
|
|
#if WINVER < 0x300
|
|
ShowCursor(FALSE);
|
|
SetCursor(vfMouseExist ? hc : NULL);
|
|
|
|
/* make sure the cursor is still visible in a mouse system
|
|
and invisible in a mouseless system */
|
|
cursorlevel = GetSystemMetrics(SM_CURSORLEVEL);
|
|
#else
|
|
/* use a supported method to get cursor level! ..pault 2/6/90 */
|
|
cursorlevel = ShowCursor(FALSE);
|
|
SetCursor(vfMouseExist ? hc : NULL);
|
|
#endif
|
|
if (vfMouseExist)
|
|
{
|
|
while (cursorlevel++ < 0)
|
|
ShowCursor(TRUE);
|
|
}
|
|
else /* no mouse */
|
|
{
|
|
while (cursorlevel-- >= 0)
|
|
ShowCursor(FALSE);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/* String utility functions - moved here from string.c */
|
|
|
|
/* I N D E X */
|
|
/* ** Returns pointer to first occurrence of character ch found in null-
|
|
terminated string pch, or 0 if ch does not appear. If ch==0, we
|
|
return a pointer to the null terminator. */
|
|
/* In Kanji version, a kanji character is excluded from the search. */
|
|
|
|
CHAR *index(pch, ch)
|
|
REG1 CHAR *pch;
|
|
REG2 CHAR ch; // fixed bug, previously int (2.11.91) D. Kent
|
|
{
|
|
while (low(*pch)!=ch)
|
|
{
|
|
#ifdef DBCS /* KenjiK '90-11-20 */
|
|
if (*pch=='\0')
|
|
#else
|
|
if (*pch++=='\0')
|
|
#endif
|
|
return(NULL);
|
|
#ifdef DBCS /* KenjiK '90-11-20 */
|
|
pch = AnsiNext(pch);
|
|
#endif
|
|
}
|
|
return(pch);
|
|
}
|
|
|
|
/* We may want to make these 'type' functions into macros */
|
|
/* These are designed for ANSI character set (used by windows) only */
|
|
|
|
/* I S A L P H A */
|
|
/* ** TRUE if ch is a letter, FALSE otherwise */
|
|
|
|
isalpha(ch)
|
|
REG1 CHAR ch;
|
|
{/* Note: even though DF and FF are lowercase, they have no
|
|
corresponding uppercase, so they are excluded from the
|
|
lowercase class, and simply mapped to themselves */
|
|
return(islower(ch) || isupper(ch) || ch == 0x00FF || ch == 0x00DF);
|
|
}
|
|
|
|
|
|
/* ** TRUE if ch is a lowercase letter, FALSE otherwise */
|
|
|
|
/* I S L O W E R */
|
|
islower(ch)
|
|
REG1 CHAR ch;
|
|
{/* Note: even though DF and FF are lowercase, they have no
|
|
corresponding uppercase, so they are excluded from the
|
|
lowercase class, and simply mapped to themselves */
|
|
return((ch >= 'a' && ch <= 'z') ||
|
|
/* foreign */
|
|
(ch >= 0x00E0 && ch <= 0x00F6) ||
|
|
(ch >= 0x00F8 && ch <= 0x00FE) );
|
|
}
|
|
|
|
/* ** TRUE if ch is an uppercase letter, FALSE otherwise */
|
|
|
|
isupper(ch)
|
|
REG1 CHAR ch;
|
|
{
|
|
return((ch >= 'A' && ch <= 'Z') ||
|
|
/* foreign */
|
|
(ch >= 0x00C0 && ch <= 0x00D6) ||
|
|
(ch >= 0x00D8 && ch <= 0x00DE));
|
|
}
|
|
|
|
/* ** TRUE if ch is a digit, FALSE otherwise */
|
|
|
|
isdigit(ch)
|
|
REG1 CHAR ch;
|
|
{
|
|
return(ch>='0' && ch<='9');
|
|
}
|
|
|
|
|
|
#ifdef ENABLE
|
|
/* ** TRUE if ch is a character or a digit, FALSE otherwise */
|
|
|
|
isalnum(ch)
|
|
REG1 CHAR ch;
|
|
{
|
|
return(isalpha(ch) || isdigit(ch));
|
|
}
|
|
#endif
|
|
|
|
|
|
|
|
int ChUpper(ch)
|
|
REG1 CHAR ch;
|
|
{
|
|
#ifdef DBCS
|
|
return AnsiUpper( ch );
|
|
#else
|
|
#ifdef BOGUS
|
|
return (ch >= 'a' && ch <= 'z') ? ch + ('A' - 'a') : ch;
|
|
#endif
|
|
/* use Windows' ANSI char set, the difference of upper/lower case
|
|
is also 20 (HEX) for foreign chars */
|
|
|
|
if (islower(ch))
|
|
return(ch + ('A' - 'a')); /* foreign is taken care of */
|
|
else return(ch);
|
|
#endif
|
|
}
|
|
|
|
|
|
|
|
|
|
/* similar to blcomp except compares by bytes and is not case sensitive */
|
|
BOOL FRgchSame(rgch1, rgch2, cch)
|
|
CHAR rgch1[], rgch2[];
|
|
int cch;
|
|
{
|
|
short ich;
|
|
|
|
for(ich = 0; ich < cch; ich++)
|
|
{
|
|
if(ChLower(rgch1[ich]) != ChLower(rgch2[ich]))
|
|
return(FALSE);
|
|
}
|
|
return(TRUE);
|
|
}
|
|
|
|
/* PchStartBaseNameSz() ---- returns a character pointer to the
|
|
beginning of a base file name. If
|
|
the name only consists of a base
|
|
name, sz is returned.
|
|
|
|
Note: If sz ends with a back-slash or a colon, pch returned
|
|
is pointing to the null terminator of the given string. */
|
|
|
|
CHAR *PchStartBaseNameSz(sz)
|
|
CHAR *sz;
|
|
{
|
|
CHAR *pchBS, *pchC, *pchLast;
|
|
CHAR *PchLastSzCh();
|
|
|
|
pchBS = PchLastSzCh(sz, '\\');
|
|
pchC = PchLastSzCh(sz, ':');
|
|
pchLast = (pchBS > pchC) ? pchBS : pchC;
|
|
if (pchLast == NULL) {
|
|
pchLast = sz;
|
|
}
|
|
else {
|
|
pchLast++;
|
|
}
|
|
return (pchLast);
|
|
}
|
|
|
|
/* PchLastSzCh() ----- returns a pointer to the last occurrence of a given
|
|
character in a given string. If it does not occur
|
|
in the string, it returns NULL. If the given
|
|
character is '\0', it returns sz itself.
|
|
Note: All kanji characters are excluded from the search. */
|
|
|
|
CHAR *PchLastSzCh(sz, ch)
|
|
CHAR *sz;
|
|
CHAR ch;
|
|
{
|
|
if (ch == '\0') {
|
|
return (sz);
|
|
}
|
|
else {
|
|
CHAR *pchCur, *pchLast;
|
|
|
|
#ifdef DBCS
|
|
for (pchLast = pchCur = sz; *pchCur != '\0'; pchCur = AnsiNext(pchCur))
|
|
#else
|
|
for (pchLast = pchCur = sz; *pchCur != '\0'; pchCur++)
|
|
#endif
|
|
{
|
|
{
|
|
if (low(*pchCur) == ch) {
|
|
pchLast = pchCur;
|
|
}
|
|
}
|
|
}
|
|
return ((pchLast == sz) ? NULL : pchLast);
|
|
}
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
_Assert(pch, line, f)
|
|
PCH pch;
|
|
int line;
|
|
BOOL f;
|
|
{
|
|
if (!f)
|
|
Do_Assert(pch, line, f);
|
|
}
|
|
#endif
|
|
|