home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
World of Shareware - Software Farm 2
/
wosw_2.zip
/
wosw_2
/
GENERAL
/
CONTRAST.ZIP
/
CONTRAST.C
next >
Wrap
C/C++ Source or Header
|
1991-01-14
|
120KB
|
3,390 lines
/******************************************************************/
/* contrast - conversion for os/2 of fulcon */
/* adapted by B.N.H.Scott */
/* from FULCON by Dave Pullin This Version: 9/1/91 */
/******************************************************************/
/* This program contrasts two files by finding shared lines of data,
and displaying the files with identical sections in the same
colours.
*/
/* The two files, either defined on the command line or in a dialog box, are
read in, the number of lines counted, and arrays set up. A hash code for each
line is calculated from its contents, and lines that occur exactly once in
each file found (these are referred to as 'pins'). Lines that are the same in
the two files, which are above or below pins, are also counted as pins. All
other lines are treated as unique.
A composite file is constructed, which is an attempt to show all
the lines from both files in the most likely order. Matching blocks of lines
are kept together; if two blocks of lines have swapped positions in the
second file, the smaller one is shown twice, above and below the larger one.
Unique lines are fitted in between the matched blocks in the order they come
in the real files.
The two real files may be displayed, or the composite. The text colour
indicates which lines have been matched, and which are unique. The
background colour shows either which file the lines belong to (if they are
unique or have been moved to a different position in the second file), or
that they are matched, and also not moved.
A 'bar chart' of the two files is also displayed.
*/
/* 'Hungarian notation' prefixes used: ht for hash table index
lt for line table index
*/
/* include headers for Dos, Win & Gpi calls & for string manipulation and
character type checking
*/
#define INCL_DOSMISC
#define INCL_DOSMEMMGR
#define INCL_GPILCIDS
#define INCL_GPIPRIMITIVES
#define INCL_WINBUTTONS
#define INCL_WINDIALOGS
#define INCL_WINENTRYFIELDS
#define INCL_WINERRORS
#define INCL_WINFRAMEMGR
#define INCL_WINHELP
#define INCL_WINHOOKS
#define INCL_WININPUT
#define INCL_WINLISTBOXES
#define INCL_WINMENUS
#define INCL_WINPOINTERS
#define INCL_WINSCROLLBARS
#define INCL_WINSHELLDATA
#define INCL_WINSTATICS
#define INCL_WINSYS
#define INCL_WINWINDOWMGR
#define _MT
#include <string.h>
#include <stdio.h>
#include <ctype.h>
#include <process.h>
#include <os2.h>
/* include header defining constants for commands, etc. */
#include "contrast.h"
/* macros for accessing huge file in memory, given a linear pointer */
#define MEM(a) MAKEP ((SELECTOROF(a) << usHugeShift) + selBase, OFFSETOF(a))
#define CMEM(a) * (PCH) MEM(a)
/* max & min macros */
#define MAX(a,b) ((a) > (b)) ? (a) : (b)
#define MIN(a,b) ((a) < (b)) ? (a) : (b)
typedef struct _FONT {
FATTRS fattrs;
struct _FONT *pfontNext;
USHORT usPointSize;
} FONT;
typedef FONT *PFONT;
/* special structure to stop trap Ds with some file systems;
allow extra 20 bytes for file name overflow */
typedef struct _MYFILEFINDBUF {
FILEFINDBUF real;
char dummy[20];
} MYFILEFINDBUF;
#define UM_FAIL WM_USER
#define UM_TABLES_MADE WM_USER+1
#define COL_UNIQUE 4
#define COL_A_BGRND 5
#define COL_B_BGRND 6
#define COL_MATCHED_BGRND 7
/* ids for control windows */
#define ID_VSCROLL 1
#define ID_HSCROLL 2
#define ID_TITLE 3
/* local identifier for font */
#define LCID_SELECTED 1L
#define STACKSIZE 4096 /* for second thread */
/* function declarations */
void cdecl main (int argc, char *argv[]);
void BarChart (HPS hps, LONG cxClient, LONG cyClient, LONG cxBar);
LONG FillBlock (HPS hps, LONG cxClient, LONG cyClient, LONG cxBar,
SHORT ltTop, SHORT ltBottom);
LONG PrintableLine (SHORT ltIndex, PLONG plColour, PLONG plBackCol);
SHORT RelLine (SHORT ltBaseLine, SHORT sInc);
MRESULT EXPENTRY ClientWndProc (HWND hwnd, USHORT msg, MPARAM mp1, MPARAM mp2);
void ShowError (PSZ string);
void ShowPMError (PSZ string);
PFONT InitialiseFonts (HWND hwnd);
MRESULT EXPENTRY AboutDlgProc (HWND hwnd, USHORT msg, MPARAM mp1, MPARAM mp2);
MRESULT EXPENTRY ActualColoursProc (HWND hwnd, USHORT msg, MPARAM mp1, MPARAM mp2);
void InitialiseHelp (void);
MRESULT EXPENTRY OpenDlgProc (HWND hwnd, USHORT msg, MPARAM mp1, MPARAM mp2);
void FillOpenListBoxes (PSZ szPath, HWND hwndFileList, HWND hwndDirList);
MRESULT EXPENTRY SetColoursProc (HWND hwnd, USHORT msg, MPARAM mp1, MPARAM mp2);
void CopyColours (PLONG lFromColour, PLONG lToColour);
USHORT MakeFullPath (PSZ szFileName);
BOOL GetDefaultDir (PSZ szDefDir);
MRESULT EXPENTRY FontsDlgProc (HWND hwnd, USHORT msg, MPARAM mp1, MPARAM mp2);
MRESULT EXPENTRY MyEntryFieldProc (HWND hwnd, USHORT msg, MPARAM mp1, MPARAM mp2);
PVOID AllocMemPM (USHORT usSize);
void cdecl far MakeTables (void far *dummy);
SHORT CreateTables (void);
void ReleaseMemory (void);
ULONG OpenFile (PSZ szFileName, PHFILE pFileHandle);
SHORT ReadFile (HFILE FileHandle, ULONG ulStartMem, ULONG ulEndMem);
SHORT MakeHashValues (ULONG ulChar, ULONG ulEnd, SHORT ltIndex);
void ChainIdenticalLines (void);
void FindUniquePairs (void);
void ExpandUniquePairs (void);
void SetColours (void);
void Composite (SHORT ltTopA, SHORT ltBottomA, SHORT ltTopB,
SHORT ltBottomB, SHORT ltPrecLine, SHORT ltFolLine);
void Add (PSHORT pltIndex, PSHORT pltPrecLine);
PVOID AllocMem (USHORT usSize);
/* All global variables are initialised. This is due to a quirk in the MS C6.0
compiler (and maybe many others); if they aren't initialised, ADDRESSES of
the variables are put in the data segment, so that a lot of indirection goes
on during the program. If they are, the values are put in the data segment.
Can't work out why, myself, but there you go. This gives over 10%
improvement in speed.
*/
/* line table arrays */
ULONG *ulLine = NULL; /* pointer to text of line in memory */
SHORT *ltLinePair = NULL; /* line table indexes of identical line */
SHORT *ltLineNext = NULL, *ltLinePrev = NULL; /* next and previous lines of composite file */
USHORT *usLineCol = NULL, *usBackCol = NULL; /* colour of line and background */
ULONG *ulLineHash = NULL; /* hash code of line */
SHORT *HashTable = NULL; /* index of line table entries */
/****************************DIAGRAM******************************************\
The line tables consist of several arrays, each with one entry for every line
in each file.
For each line in each file, a hash value is calculated. This is used as an
index for the hash table, which is an array of subscripts for the line tables.
If there is no subscript at that point in the hash table, the subscript of the
current line is entered.
HashTable ltLinePair
┌───────┐ ┌────────┐
│ │ ┌──>╞════════╡
│ │ │ │ │
│ │ │ │ │
│ │ │ │ │
╞═══════╡──┘ │ │
│ │ │ │
│ │ │ │
│ │ │ │
│ │ │ │
│ │ │ │
│ │ │ │
│ │ │ │
│ │ │ │
└───────┘ │ │
└────────┘
If there is a subscript present, the hash value of that line (stored in
ulLineHash) is compared with that of the current line. If they don't match, the
computer looks at the next entry in the hash table.
However, if the hash values match, the computer has found an identical line,
so it changes the entry in the hash table to point to the current line, and
puts the index of the identical line in the ltLinePair entry of the current
line. Thus a chain of identical lines will be built up.
HashTable ltLinePair
┌───────┐ ┌────────┐
│ │ ╞════════╡<────┐
│ │ │ │ │
│ │ │ │ │
│ │ │ │ │
╞═══════╡──┐ ╞════════╡<──┐─┘
│ │ │ │ │ │
│ │ │ │ │ │
│ │ │ │ │ │
│ │ │ ╞════════╡<┐─┘
│ │ │ │ │ │
│ │ │ │ │ │
│ │ │ │ │ │
│ │ │ │ │ │
└───────┘ └──>╞════════╡─┘
└────────┘
After this has been done for all the lines in the two files, the computer looks
at each entry in the hash table. If an entry points to a pair of lines, one
from each file, the ltLinePair entry for each line is given the other's index.
In any other case (no matching line, two matching lines in the same file, or
more than two matching lines) the chain is broken up totally.
HashTable ltLinePair
┌───────┐ ┌────────┐
│ │ ┌─╞════════╡<┐
╞═══════╡──┐ │ │ │ │
│ │ │ │ │ │ │
│ │ │ │ │ │ │
│ │ │ │ │ │ │
│ │ └>└>╞════════╡─┘
│ │ │ │
│ │ │ │
│ │ ┌─╞════════╡<┐
│ │ │ │ │ │
│ │ │ │ │ │
│ │ │ │ │ │
╞═══════╡──┐ │ │ │ │
└───────┘ └>└>╞════════╡─┘
└────────┘
Lines either side of these 'pins' which match each other (but have not
already been made pins, because there were more than 2 of them) are then made
into pins.
\*************************END OF DIAGRAM**************************************/
HAB hab = NULL; /* handle to anchor block */
PSZ szFile[3] = {NULL, NULL, NULL}; /* names of files, and composite */
SHORT sNumLines[3] = {0, 0, 0}; /* number of lines in each file, and composite */
SHORT sTotalLines = 0; /* total number of lines in the two real files */
SHORT sMaxLength = 0; /* maximum line length in either file */
PFONT pfontHead = NULL; /* head of linked list of fonts */
PFONT pfontSelected = NULL; /* current font */
SEL selBase = 0; /* variables for accessing huge memory for the files */
USHORT usHugeShift = 0;
char pWriteLine[512] = ""; /* output buffer */
/* user options - loadable and savable - initialise to defaults */
struct {
LONG lColour[8];
BOOL fIgnoreLeadingBlanks;
BOOL fIgnoreTrailingBlanks;
BOOL fIgnoreAllBlanks;
BOOL fInterleaveUniqueLines;
USHORT usPointSize;
char szFacename[60];
USHORT fsFontSelection;
} uoUserOptions =
{
/* keep up to date with CONTRAST.RC & lDefColour below */
{ CLR_BLUE, /* matched text colour */
CLR_DARKPINK, /* " " " */
CLR_DARKGREEN, /* " " " */
CLR_DARKCYAN, /* " " " */
CLR_BLACK, /* unmatched " " */
CLR_YELLOW, /* background to file A */
CLR_RED, /* " " " B */
CLR_WHITE /* " " identical text */
},
FALSE,
FALSE,
TRUE,
FALSE,
0,
"",
0
};
/* array of default colours - keep up to date with CONTRAST.RC & user options */
LONG lDefColour[8] = { CLR_BLUE,
CLR_DARKPINK,
CLR_DARKGREEN,
CLR_DARKCYAN,
CLR_BLACK,
CLR_YELLOW,
CLR_RED,
CLR_WHITE
};
#define FILE_A 0
#define FILE_B 1
#define COMPOSITE 2
SHORT file = COMPOSITE; /* file currently displayed */
USHORT usMaxPathLength = 0; /* maximum path length allowed by filing system */
PFNWP pfnOldEntryFieldProc = NULL; /* subcalssing of entryfields for */
BOOL fEntryFieldASubclassed = FALSE; /* Open dialog */
BOOL fEntryFieldBSubclassed = FALSE;
HWND hwndHelpInstance = NULL; /* handle for help instance */
HWND hwndFrame = NULL, hwndClient = NULL; /* handles for main window */
/* flags for thread communication */
BOOL fStartTables = TRUE; /* set by the PM thread if the table thread is to start a new
set of tables; if not, the table thread will terminate */
BOOL fLoadNewFiles = TRUE; /* set by the PM thread if the user has asked for
different files to be used */
ULONG ulClearTables = 0;
HSEM hsemClearTables = &ulClearTables; /* semaphore; if 0, the table thread will delete
any old tables, and (depending on
fStartTables) start new ones. At the
beginning of this action, the table thread sets
the semaphore. It periodically checks
the semaphore to see if it has been
cleared by the PM thread (if it has, it
starts again), and when it has
finished the tables, it waits on the
semaphore until it is cleared. It then
starts again. */
void cdecl main (int argc, char *argv[])
/**********************************************************************/
/* expected arguments: if argc >= 3, */
/* argv[1] = name of file A */
/* argv[2] = name of file B */
/* if argc < 3, */
/* one or both names are missing */
/* */
/* function : Gets filenames from command line or dialog box. Creates */
/* application window, starts contrasting process, passes messages to */
/* window, and terminates application. */
/**********************************************************************/
{
HMQ hmq;
PSZ szClientClass = "Contrast";
ULONG flFrameFlags = FCF_STANDARD, ulOptionsSize;
QMSG qmsg;
BOOL fCreateWindow;
USHORT usResult;
PBYTE Stack;
if (hab = WinInitialize (0))
{
if (hmq = WinCreateMsgQueue (hab, 0))
{
InitialiseHelp ();
/* get max path length for file system */
DosQSysInfo (0, (PBYTE) &usMaxPathLength, 2);
/* get user options */
ulOptionsSize = sizeof(uoUserOptions);
PrfQueryProfileData (HINI_USERPROFILE, "Contrast", "Options", &uoUserOptions, &ulOptionsSize);
/* allocate memory for full file paths */
if ((szFile[0] = AllocMemPM (usMaxPathLength)) != NULL &&
(szFile[1] = AllocMemPM (usMaxPathLength)) != NULL)
{
/* usResult's bits mean as follows:
bit 0: need Open dialog
bit 1: couldn't get memory, so exit
*/
if (argc >= 2)
{
strcpy (szFile[0], argv[1]);
usResult = MakeFullPath (szFile[0]);
}
else
{
if (GetDefaultDir (szFile[0]) == TRUE)
usResult = 1;
else
usResult = 2;
}
if (usResult != 2)
{
if (argc >= 3)
{
/* if a directory was specified as the 1st parameter, we'll need the Open dialog */
if (szFile[0][strlen (szFile[0]) - 1] == '\\')
{
usResult = 1;
}
strcpy (szFile[1], argv[2]);
usResult |= MakeFullPath (szFile[1]);
}
else
{
strcpy (szFile[1], szFile[0]);
usResult = 1;
}
}
switch (usResult)
{
case 0:
fCreateWindow = TRUE;
break;
case 1:
/* call the Open dialog - returns FALSE if Cancel button pressed */
fCreateWindow = WinDlgBox (HWND_DESKTOP, HWND_DESKTOP, OpenDlgProc,
(HMODULE)NULL, DID_OPEN, NULL);
break;
default:
fCreateWindow = FALSE;
break;
}
if (fCreateWindow == TRUE)
{
if (WinRegisterClass (hab,
szClientClass,
ClientWndProc,
CS_SIZEREDRAW,
0))
{
if (hwndFrame = WinCreateStdWindow (HWND_DESKTOP,
WS_VISIBLE,
&flFrameFlags,
szClientClass,
NULL,
0L,
(HMODULE)NULL,
ID_CONTRAST,
&hwndClient))
{
if (WinAssociateHelpInstance (hwndHelpInstance, hwndFrame))
{
if (Stack = AllocMemPM (STACKSIZE))
{
_beginthread (MakeTables,
Stack,
STACKSIZE,
NULL);
while (WinGetMsg (hab, &qmsg, NULL, 0, 0))
WinDispatchMsg (hab, &qmsg);
/* tell other thread it can release resources */
fStartTables = FALSE;
DosSemClear (hsemClearTables);
}
}
WinDestroyWindow (hwndFrame);
}
}
}
}
WinDestroyHelpInstance (hwndHelpInstance);
WinDestroyMsgQueue (hmq);
}
}
WinTerminate (hab);
return;
} /* end of main */
void BarChart (HPS hps, LONG cxClient, LONG cyClient, LONG cxBar)
/****************************************************************************/
/* expected parameters: hps = handle for display (input) */
/* cxClient & cyClient = size of client window (input) */
/* cxBar = width of bar (input) */
/* */
/* function : Draws bar chart. */
/****************************************************************************/
{
SHORT ltTopABlock, ltTopBBlock, ltBottomBBlock, ltIndex, ltPair, ltTopBlock;
POINTL point;
ltTopABlock = 0;
ltTopBBlock = ltBottomBBlock = 0;
for (ltIndex = 1; ltIndex <= sNumLines[0] + 1; ltIndex++)
{
ltPair = ltLinePair[ltIndex];
if (ltPair == ltBottomBBlock + 1)
ltBottomBBlock = ltPair; /* continuation of block */
else
{
if (ltBottomBBlock != 0) /* blocks to be coloured */
{
/* draw blocks and connect with line */
point.x = cxClient - 4 * cxBar;
point.y = FillBlock (hps, cxClient, cyClient, cxBar,
ltTopABlock, ltIndex - 1);
GpiMove (hps, &point);
point.x = cxClient - 2* cxBar;
point.y = FillBlock (hps, cxClient, cyClient, cxBar,
ltTopBBlock, ltBottomBBlock);
GpiSetColor (hps, uoUserOptions.lColour[usLineCol[ltTopBBlock]]);
GpiLine (hps, &point);
/* reset bottom of B block, i.e. no block current */
ltBottomBBlock = 0;
}
if (ltPair != 0) /* new block */
{
ltTopABlock = ltIndex;
ltTopBBlock = ltBottomBBlock = ltPair;
}
}
}
/* fill blocks for unique lines. This must be done after the matched blocks,
so that single line changes are always shown. */
ltTopBlock = 0;
for (ltIndex = 1; ltIndex <= sTotalLines; ltIndex++)
{
if (ltIndex == sNumLines[0] + 1 && ltTopBlock != 0)
{
/* we're at the end of the 1st file, and a block is pending, so fill it */
FillBlock (hps, cxClient, cyClient, cxBar,
ltTopBlock, ltIndex - 1);
ltTopBlock = 0;
}
if ((ltPair = ltLinePair[ltIndex]) != 0)
{
if (ltTopBlock != 0)
{
/* we've just finished a block of unique lines, so fill it */
FillBlock (hps, cxClient, cyClient, cxBar,
ltTopBlock, ltIndex - 1);
ltTopBlock = 0;
}
}
else if (ltTopBlock == 0)
ltTopBlock = ltIndex;
}
if (ltTopBlock != 0)
/* we're at the end of the 2nd file, and a block is pending, so fill it */
FillBlock (hps, cxClient, cyClient, cxBar, ltTopBlock, ltIndex - 1);
return;
} /* end of BarChart () */
LONG FillBlock (HPS hps, LONG cxClient, LONG cyClient, LONG cxBar,
SHORT ltTop, SHORT ltBottom)
/***************************************************************************/
/* expected parameters: hps = handle for display (input) */
/* cxClient, cyClient = size of client window (input) */
/* cxBar = width of bar (input) */
/* ltTop, ltBottom = limiting lines of block (input) */
/* return value : y coordinate of centre of block */
/* */
/* function : Draws a block in the appropriate column */
/***************************************************************************/
{
SHORT sScaler;
RECTL rectl;
LONG lBlockColour;
sScaler = MAX (sNumLines[0], sNumLines[1]);
if (ltTop <= sNumLines[0])
{
rectl.xLeft = cxClient - 5 * cxBar;
rectl.xRight = cxClient - 4 * cxBar;
rectl.yTop = cyClient - (ltTop - 1) * cyClient / sScaler;
rectl.yBottom = cyClient - ltBottom * cyClient / sScaler - 1;
}
else
{
rectl.xLeft = cxClient - 2 * cxBar;
rectl.xRight = cxClient - cxBar;
rectl.yTop = cyClient - (ltTop - 1 - sNumLines[0]) * cyClient / sScaler;
rectl.yBottom = cyClient - (ltBottom - sNumLines[0]) * cyClient / sScaler - 1;
}
if (usLineCol[ltTop] != COL_UNIQUE)
lBlockColour = uoUserOptions.lColour[usLineCol[ltTop]];
else
lBlockColour = uoUserOptions.lColour[usBackCol[ltTop]];
WinFillRect (hps, &rectl, lBlockColour);
return (rectl.yTop + rectl.yBottom ) / 2;
} /* end of FillBlock () */
LONG PrintableLine (SHORT ltIndex, PLONG plColour, PLONG plBackCol)
/***********************************************************************/
/* expected parameters: ltIndex = entry in line table (input) */
/* plColour = colour of line (output) */
/* plBackCol = background colour (output) */
/* return value: number of characters in line */
/* */
/* function : Converts a line of text in the file to a printable line, */
/* taking care of control characters, and tab characters. */
/***********************************************************************/
{
ULONG ulChar;
char Char;
LONG lCount;
int i;
ulChar = ulLine[ltIndex];
*plColour = uoUserOptions.lColour[usLineCol[ltIndex]];
*plBackCol = uoUserOptions.lColour[usBackCol[ltIndex]];
lCount = 0;
/* while ((Char = CMEM (ulChar)) != 10 && Char != 13 && lCount < 512) */
while ((Char = CMEM (ulChar)) != '\r' && lCount < 512)
{
if (isprint (Char))
pWriteLine[lCount] = Char;
else if (Char == '\t')
{
for (i = 0; i < 4; i++)
pWriteLine[lCount + i] = ' ';
lCount += 3;
}
else
pWriteLine[lCount] = '?';
ulChar++;
lCount++;
}
return lCount;
} /* end of PrintableLine () */
SHORT RelLine (SHORT ltBaseLine, SHORT sInc)
/*****************************************************************************/
/* expected parameters: sBaseLine = reference line in composite file */
/* sInc = number of lines forward */
/* return value: index of line in line table */
/* */
/* function : Finds a line in a real, or composite, file relative to a base. */
/*****************************************************************************/
{
if (file != COMPOSITE)
return (ltBaseLine + sInc);
else if (sInc > 0)
while (sInc-- > 0)
ltBaseLine = ltLineNext[ltBaseLine];
else
while (sInc++ < 0)
ltBaseLine = ltLinePrev[ltBaseLine];
return ltBaseLine;
} /* end of RelLine () */
MRESULT EXPENTRY ClientWndProc (HWND hwnd, USHORT msg, MPARAM mp1, MPARAM mp2)
/**********************************************************************/
/* All-purpose window procedure. For functions, see individual cases. */
/**********************************************************************/
{
HPS hps;
static LONG cxClient, cyClient, cxBar, cyBar, cyTitle;
RECTL rclInvalid, rclLine, rclScroll, rclBar;
static LONG cxCaps, cyChar, cyAsc;
static SHORT sLinesPerPage, sColsPerPage, fTablesValid = 0;
static SHORT ltTopLine[3], sColPos[3], sFileLine[3];
static HWND hwndVScroll, hwndHScroll, hwndTitle, hwndMenu;
LONG lCount, lPrintCol, lBackColour;
POINTL point;
SHORT ltIndex, sScaler, sIndex, ltFirstA, ltLastA, ltFirstB, ltLastB;
SHORT sLineInc, sColInc;
SHORT sStartPrint, sStopPrint, sPrintLine;
FONTMETRICS fm;
PSZ szTitle;
static HPOINTER hptrWait, hptrArrow;
switch (msg)
{
case WM_CREATE:
/******************************************************/
/* function : Initialises variables, fonts, title and */
/* scroll bar windows, and user options. */
/******************************************************/
hwndMenu = WinWindowFromID (WinQueryWindow (hwnd, QW_PARENT, FALSE),
FID_MENU);
hptrWait = WinQuerySysPointer (HWND_DESKTOP, SPTR_WAIT, FALSE);
hptrArrow = WinQuerySysPointer (HWND_DESKTOP, SPTR_ARROW, FALSE);
szFile[COMPOSITE] = "Composite";
/* get width & height of system scroll bars */
cxBar = WinQuerySysValue (HWND_DESKTOP, SV_CXVSCROLL);
cyBar = WinQuerySysValue (HWND_DESKTOP, SV_CYHSCROLL);
cyTitle = WinQuerySysValue (HWND_DESKTOP, SV_CYTITLEBAR);
/* create scroll bars */
hwndVScroll = WinCreateWindow (hwnd,
WC_SCROLLBAR,
NULL,
WS_VISIBLE | SBS_VERT,
(SHORT) (cxClient - 7 * cxBar),
(SHORT) cyBar,
(SHORT) cxBar,
(SHORT) (cyClient - cyBar - cyTitle),
hwnd,
HWND_BOTTOM,
ID_VSCROLL,
NULL,
NULL);
hwndHScroll = WinCreateWindow (hwnd,
WC_SCROLLBAR,
NULL,
WS_VISIBLE | SBS_HORZ,
0,
0,
(SHORT) (cxClient - 7 * cxBar),
(SHORT) cyBar,
hwnd,
HWND_BOTTOM,
ID_HSCROLL,
NULL,
NULL);
hwndTitle = WinCreateWindow (hwnd,
WC_STATIC,
szFile[2],
WS_VISIBLE | SS_TEXT | DT_CENTER,
0,
(SHORT) (cyClient - cyTitle),
(SHORT) (cxClient - 6 * cxBar),
(SHORT) cyTitle,
hwnd,
HWND_BOTTOM,
ID_TITLE,
NULL,
NULL);
/* find fixed pitch font attributes */
pfontHead = InitialiseFonts (hwnd);
/* set colour of title window */
WinSetPresParam (hwndTitle, PP_BACKGROUNDCOLORINDEX, 4, &(uoUserOptions.lColour)[COL_A_BGRND+file]);
/* set checked status of menu items as appropriate */
if (uoUserOptions.fIgnoreLeadingBlanks)
WinSendMsg (hwndMenu,
MM_SETITEMATTR,
MPFROM2SHORT (MID_IGNORE_LEADING_BLANKS, TRUE),
MPFROM2SHORT (MIA_CHECKED, MIA_CHECKED));
if (uoUserOptions.fIgnoreTrailingBlanks)
WinSendMsg (hwndMenu,
MM_SETITEMATTR,
MPFROM2SHORT (MID_IGNORE_TRAILING_BLANKS, TRUE),
MPFROM2SHORT (MIA_CHECKED, MIA_CHECKED));
if (uoUserOptions.fIgnoreAllBlanks)
WinSendMsg (hwndMenu,
MM_SETITEMATTR,
MPFROM2SHORT (MID_IGNORE_ALL_BLANKS, TRUE),
MPFROM2SHORT (MIA_CHECKED, MIA_CHECKED));
if (uoUserOptions.fInterleaveUniqueLines)
WinSendMsg (hwndMenu,
MM_SETITEMATTR,
MPFROM2SHORT (MID_INTERLEAVE_UNIQUE_LINES, TRUE),
MPFROM2SHORT (MIA_CHECKED, MIA_CHECKED));
return 0;
case WM_SIZE:
/**********************************************************************/
/* function : Gets new size of window, and repositions child windows. */
/**********************************************************************/
cxClient = (LONG) SHORT1FROMMP (mp2);
cyClient = (LONG) SHORT2FROMMP (mp2);
WinSetWindowPos (hwndVScroll,
HWND_BOTTOM,
(SHORT) (cxClient - 7 * cxBar),
(SHORT) cyBar,
(SHORT) cxBar,
(SHORT) (cyClient - cyBar - cyTitle),
SWP_SIZE | SWP_MOVE | SWP_SHOW);
WinSetWindowPos (hwndHScroll,
HWND_BOTTOM,
0,
0,
(SHORT) (cxClient - 7 * cxBar),
(SHORT) cyBar,
SWP_SIZE | SWP_MOVE | SWP_SHOW);
WinSetWindowPos (hwndTitle,
HWND_BOTTOM,
0,
(SHORT) (cyClient - cyTitle),
(SHORT) (cxClient - 6 * cxBar),
(SHORT) cyTitle,
SWP_SIZE | SWP_MOVE | SWP_SHOW);
hps = WinGetPS (hwnd);
if (pfontSelected != NULL)
{
if (GpiCreateLogFont (hps, NULL, LCID_SELECTED, &(pfontSelected->fattrs))
!= FONT_MATCH)
ShowPMError ("GpiCreateLogFont");
if (GpiSetCharSet (hps, LCID_SELECTED) == FALSE)
ShowPMError ("GpiSetCharSet");
}
GpiQueryFontMetrics (hps, (LONG) sizeof (fm), &fm);
cxCaps = fm.lAveCharWidth;
cyChar = fm.lMaxBaselineExt + fm.lExternalLeading;
cyAsc = fm.lLowerCaseAscent;
/*
if (pfontSelected != NULL)
{
GpiSetCharSet (hps, LCID_DEFAULT);
GpiDeleteSetId (hps, LCID_SELECTED);
}
*/
WinReleasePS (hps);
sLinesPerPage = (SHORT) ((cyClient - cyTitle - cyBar) / cyChar);
sColsPerPage = (SHORT) ((cxClient - 7 * cxBar) / cxCaps);
return 0;
case WM_PAINT:
/**********************************************************************/
/* function : Paints any area of the client window when it is needed. */
/**********************************************************************/
/* get coordinates of invalidated area, so the minimum of painting is done */
hps = WinBeginPaint (hwnd, NULL, &rclInvalid);
GpiErase (hps);
/* don't show lines until tables created */
if (fTablesValid == FALSE)
{
WinSetPointer (HWND_DESKTOP, hptrWait);
WinEndPaint (hps);
return 0;
}
WinSetPointer (HWND_DESKTOP, hptrArrow);
if (rclInvalid.xRight > cxClient - 7 * cxBar)
BarChart (hps, cxClient, cyClient, cxBar);
/* set font. Don't bother with error messages - these will have been
given during WM_CREATE, and would just slow the whole process down.
*/
if (pfontSelected != NULL)
{
GpiCreateLogFont (hps, NULL, LCID_SELECTED, &(pfontSelected->fattrs));
GpiSetCharSet (hps, LCID_SELECTED);
}
/* set x coordinates of background line, and printing */
rclLine.xLeft = rclInvalid.xLeft;
rclLine.xRight = MIN (rclInvalid.xRight, cxClient - 7 * cxBar);
rclInvalid.yTop = MIN (rclInvalid.yTop, cyClient - cyTitle);
rclInvalid.yBottom = MAX (rclInvalid.yBottom, cyBar);
point.x = (1 - sColPos[file]) * cxCaps;
/* find which lines are to be displayed */
sStartPrint = (SHORT) ((cyClient - cyTitle - rclInvalid.yTop) / cyChar);
sStopPrint = (SHORT) (MIN ((cyClient - cyTitle - rclInvalid.yBottom)
/ cyChar, sNumLines[file] - sFileLine[file]));
ltIndex = ltTopLine[file];
rclLine.yBottom = cyClient - cyTitle - sStartPrint * cyChar;
/* initialise variables for top of bar chart marks */
ltFirstA = ltFirstB = 0;
/* get each line, and print it at appropriate y coordinates */
for (sPrintLine = 0; sPrintLine <= sLinesPerPage; sPrintLine++)
{
/* adjust variables for top and bottom of bar chart marks */
if (ltIndex > 0 && ltIndex <= sNumLines[0])
{
if (file == COMPOSITE && usBackCol[ltIndex] == COL_MATCHED_BGRND)
{
/* composite lines only come from A, so we must include the checking of B here */
if (ltFirstB == 0)
ltFirstB = ltLinePair[ltIndex];
ltLastB = ltLinePair[ltIndex];
}
if (ltFirstA == 0)
ltFirstA = ltIndex;
ltLastA = ltIndex;
}
if (ltIndex >sNumLines[0])
{
if (ltFirstB == 0)
ltFirstB = ltIndex;
ltLastB = ltIndex;
}
if (sPrintLine >= sStartPrint && sPrintLine <= sStopPrint)
{
lCount = PrintableLine (ltIndex, &lPrintCol, &lBackColour);
GpiSetColor (hps, lPrintCol);
GpiSetBackColor (hps, lBackColour);
rclLine.yTop = rclLine.yBottom;
rclLine.yBottom = MAX (rclInvalid.yBottom,
rclLine.yBottom - cyChar);
point.y = rclLine.yTop - cyAsc;
GpiCharStringPosAt (hps, &point, &rclLine, CHS_OPAQUE | CHS_CLIP,
lCount, pWriteLine, NULL);
}
ltIndex = RelLine (ltIndex, 1);
}
/*
if (pfontSelected != NULL)
{
GpiSetCharSet (hps, LCID_DEFAULT);
GpiDeleteSetId (hps, LCID_SELECTED);
}
*/
WinEndPaint (hps);
/* adjust scroll bars */
WinSendMsg (hwndVScroll,
SBM_SETSCROLLBAR,
MPFROMSHORT (sFileLine[file]),
MPFROM2SHORT (1, sNumLines[file] + 1 - sLinesPerPage));
WinSendMsg (hwndVScroll,
SBM_SETTHUMBSIZE,
MPFROM2SHORT (sLinesPerPage, sNumLines[file]),
NULL);
WinSendMsg (hwndHScroll,
SBM_SETSCROLLBAR,
MPFROMSHORT (sColPos[file]),
MPFROM2SHORT (1, sMaxLength + 1 - sColsPerPage));
WinSendMsg (hwndHScroll,
SBM_SETTHUMBSIZE,
MPFROM2SHORT (sColsPerPage, sMaxLength),
NULL);
/* draw marks by bar charts to indicate position in file */
hps = WinGetPS (hwnd);
/* erase old marks */
rclBar.xLeft = cxClient - 11 * cxBar / 2;
rclBar.xRight = cxClient - 21 * cxBar / 4;
rclBar.yTop = cyClient;
rclBar.yBottom = 0L;
WinFillRect (hps, &rclBar, CLR_BACKGROUND);
rclBar.xLeft = cxClient - 3 * cxBar / 4;
rclBar.xRight = cxClient - cxBar / 2;
WinFillRect (hps, &rclBar, CLR_BACKGROUND);
sScaler = MAX (sNumLines[0], sNumLines[1]);
if (ltFirstA != 0)
{
rclBar.xLeft = cxClient - 11 * cxBar / 2;
rclBar.xRight = cxClient - 21 * cxBar / 4;
rclBar.yTop = cyClient - (ltFirstA - 1) * cyClient / sScaler;
rclBar.yBottom = cyClient - ltLastA * cyClient / sScaler - 1;
WinFillRect (hps, &rclBar, CLR_NEUTRAL);
}
if (ltFirstB !=0)
{
rclBar.xLeft = cxClient - 3 * cxBar / 4;
rclBar.xRight = cxClient - cxBar / 2;
rclBar.yTop = cyClient - (ltFirstB - 1 - sNumLines[0]) * cyClient / sScaler;
rclBar.yBottom = cyClient - (ltLastB - sNumLines[0]) * cyClient / sScaler - 1;
WinFillRect (hps, &rclBar, CLR_NEUTRAL);
}
WinReleasePS (hps);
return 0;
case WM_COMMAND:
/************************************************/
/* function : Processes commands from the menu. */
/************************************************/
switch (COMMANDMSG(&msg)->cmd)
{
case MID_EXIT:
WinSendMsg (hwnd, WM_CLOSE, 0L,0L);
return 0;
case MID_FILEA:
case MID_FILEB:
case MID_COMPOSITE:
/* switch file displayed */
WinSendMsg (hwndMenu,
MM_SETITEMATTR,
MPFROM2SHORT (file + MID_FILEA, TRUE),
MPFROM2SHORT (MIA_CHECKED, 0));
file = COMMANDMSG(&msg)->cmd - MID_FILEA;
WinSendMsg (hwndMenu,
MM_SETITEMATTR,
MPFROM2SHORT (file + MID_FILEA, TRUE),
MPFROM2SHORT (MIA_CHECKED, MIA_CHECKED));
WinSetWindowText (hwndTitle, szFile[file]);
WinSetPresParam (hwndTitle, PP_BACKGROUNDCOLORINDEX, 4, &(uoUserOptions.lColour)[COL_A_BGRND+file]);
rclInvalid.xLeft = 0L;
rclInvalid.xRight = (LONG) (cxClient - 7 * cxBar);
rclInvalid.yTop = cyClient - cyTitle;
rclInvalid.yBottom = (LONG) cyBar;
WinInvalidateRect (hwnd, &rclInvalid, TRUE);
return 0;
case MID_ABOUT:
WinDlgBox (HWND_DESKTOP, hwndFrame, AboutDlgProc, (HMODULE)NULL, DID_ABOUT, NULL);
return 0;
case MID_OPEN:
if (WinDlgBox (HWND_DESKTOP, hwndFrame, OpenDlgProc,
(HMODULE)NULL, DID_OPEN, NULL) == TRUE)
{
fTablesValid = 0;
fLoadNewFiles = TRUE;
DosSemClear (hsemClearTables);
WinSetPointer (HWND_DESKTOP, hptrWait);
}
return 0;
case MID_IGNORE_LEADING_BLANKS:
uoUserOptions.fIgnoreLeadingBlanks ^= TRUE;
WinSendMsg (hwndMenu,
MM_SETITEMATTR,
MPFROM2SHORT (MID_IGNORE_LEADING_BLANKS, TRUE),
MPFROM2SHORT (MIA_CHECKED,
uoUserOptions.fIgnoreLeadingBlanks ? MIA_CHECKED : 0));
fTablesValid = 0;
fLoadNewFiles = FALSE;
DosSemClear (hsemClearTables);
WinSetPointer (HWND_DESKTOP, hptrWait);
return 0;
case MID_IGNORE_TRAILING_BLANKS:
uoUserOptions.fIgnoreTrailingBlanks ^= TRUE;
WinSendMsg (hwndMenu,
MM_SETITEMATTR,
MPFROM2SHORT (MID_IGNORE_TRAILING_BLANKS, TRUE),
MPFROM2SHORT (MIA_CHECKED,
uoUserOptions.fIgnoreTrailingBlanks ? MIA_CHECKED : 0));
fTablesValid = 0;
fLoadNewFiles = FALSE;
DosSemClear (hsemClearTables);
WinSetPointer (HWND_DESKTOP, hptrWait);
return 0;
case MID_IGNORE_ALL_BLANKS:
uoUserOptions.fIgnoreAllBlanks ^= TRUE;
WinSendMsg (hwndMenu,
MM_SETITEMATTR,
MPFROM2SHORT (MID_IGNORE_ALL_BLANKS, TRUE),
MPFROM2SHORT (MIA_CHECKED,
uoUserOptions.fIgnoreAllBlanks ? MIA_CHECKED : 0));
fTablesValid = 0;
fLoadNewFiles = FALSE;
DosSemClear (hsemClearTables);
WinSetPointer (HWND_DESKTOP, hptrWait);
return 0;
case MID_INTERLEAVE_UNIQUE_LINES:
uoUserOptions.fInterleaveUniqueLines ^= TRUE;
WinSendMsg (hwndMenu,
MM_SETITEMATTR,
MPFROM2SHORT (MID_INTERLEAVE_UNIQUE_LINES, TRUE),
MPFROM2SHORT (MIA_CHECKED,
uoUserOptions.fInterleaveUniqueLines ? MIA_CHECKED : 0));
fTablesValid = 0;
fLoadNewFiles = FALSE;
DosSemClear (hsemClearTables);
WinSetPointer (HWND_DESKTOP, hptrWait);
return 0;
case MID_SET_COLOURS:
if (WinDlgBox (HWND_DESKTOP, hwndFrame, SetColoursProc,
(HMODULE)NULL, DID_COLOURS, NULL) == TRUE)
{
WinInvalidateRect (hwnd, NULL, TRUE);
}
return 0;
case MID_SET_FONT:
if (WinDlgBox (HWND_DESKTOP, hwndFrame, FontsDlgProc,
(HMODULE)NULL, DID_SET_FONT, NULL) == TRUE)
{
WinSendMsg (hwnd,
WM_SIZE,
MPFROM2SHORT ((SHORT) cxClient, (SHORT) cyClient),
MPFROM2SHORT ((SHORT) cxClient, (SHORT) cyClient));
/* only invalidate text area */
rclInvalid.xLeft = 0L;
rclInvalid.xRight = cxClient - 7 * cxBar;
rclInvalid.yTop = cyClient - cyTitle;
rclInvalid.yBottom = cyBar;
WinInvalidateRect (hwnd, &rclInvalid, TRUE);
}
return 0;
case MID_HELP_FOR_HELP:
WinSendMsg (hwndHelpInstance, HM_DISPLAY_HELP, 0L, 0L);
return 0;
default:
break;
}
break;
case HM_QUERY_KEYS_HELP:
return (MRESULT) HID_KEYS;
case HM_INFORM:
WinDlgBox (HWND_DESKTOP, HWND_DESKTOP, ActualColoursProc, (HMODULE)NULL, DID_SHOW_COLOURS, NULL);
return 0;
case WM_VSCROLL:
/****************************************************************/
/* function : Adjusts place in file using scroll bar messages. */
/****************************************************************/
if (fTablesValid == 0)
return 0;
switch (SHORT2FROMMP (mp2))
{
case SB_LINEUP:
sLineInc = -1;
break;
case SB_LINEDOWN:
sLineInc = 1;
break;
case SB_PAGEUP:
sLineInc = 1 - sLinesPerPage;
break;
case SB_PAGEDOWN:
sLineInc = sLinesPerPage - 1;
break;
case SB_SLIDERTRACK:
sLineInc = SHORT1FROMMP (mp2) - sFileLine[file];
break;
default:
sLineInc = 0;
break;
}
sLineInc = MAX (1 - sFileLine[file],
MIN (sLineInc,
sNumLines[file] + 1 - sLinesPerPage - sFileLine[file]));
if (sLineInc != 0)
{
sFileLine[file] += sLineInc;
ltTopLine[file] = RelLine (ltTopLine[file], sLineInc);
rclScroll.xLeft = 0L;
rclScroll.xRight = cxClient - 7 * cxBar;
rclScroll.yTop = cyClient - cyTitle;
rclScroll.yBottom = cyBar;
WinScrollWindow (hwnd, 0, (SHORT) (sLineInc * cyChar),
&rclScroll, &rclScroll, NULL, NULL, SW_INVALIDATERGN);
/* force update of window, since WM_PAINT messages have a low priority,
and can get pushed to the back of the queue.
*/
WinUpdateWindow (hwnd);
}
return 0;
case WM_HSCROLL:
/************************************************/
/* function : Moves view of file left or right. */
/************************************************/
if (fTablesValid == 0)
return 0;
switch (SHORT2FROMMP (mp2))
{
case SB_LINELEFT:
sColInc = -1;
break;
case SB_LINERIGHT:
sColInc = 1;
break;
case SB_PAGELEFT:
sColInc = -sColsPerPage / 2;
break;
case SB_PAGERIGHT:
sColInc = sColsPerPage / 2;
break;
case SB_SLIDERTRACK:
sColInc = SHORT1FROMMP (mp2) - sColPos[file];
break;
default:
sColInc = 0;
break;
}
sColInc = MAX (1 - sColPos[file],
MIN (sColInc,
(sMaxLength) + 1 - sColsPerPage - sColPos[file]));
if (sColInc != 0)
{
sColPos[file] += sColInc;
rclScroll.xLeft = 0L;
rclScroll.xRight = cxClient - 7 * cxBar;
rclScroll.yTop = cyClient - cyTitle;
rclScroll.yBottom = cyBar;
WinScrollWindow (hwnd, (SHORT) (-sColInc * cxCaps), 0,
&rclScroll, &rclScroll, NULL, NULL, SW_INVALIDATERGN);
WinUpdateWindow (hwnd);
}
return 0;
case WM_CHAR:
/*********************************************************************/
/* function : Duplicates scroll bar action with cursor control keys. */
/*********************************************************************/
/* PM has same messages for keys as for scroll bars,
so they just need to be passed on.
*/
if ((SHORT1FROMMP (mp1) & KC_KEYUP) == 0)
switch (CHARMSG (&msg) -> vkey)
{
case VK_UP:
case VK_DOWN:
case VK_PAGEUP:
case VK_PAGEDOWN:
return WinSendMsg (hwndVScroll, msg, mp1, mp2);
case VK_LEFT:
case VK_RIGHT:
return WinSendMsg (hwndHScroll, msg, mp1, mp2);
default:
break;
}
break;
case WM_SAVEAPPLICATION:
case WM_CLOSE:
if (pfontSelected != NULL)
{
strcpy (uoUserOptions.szFacename, pfontSelected->fattrs.szFacename);
uoUserOptions.usPointSize = pfontSelected->usPointSize;
uoUserOptions.fsFontSelection = pfontSelected->fattrs.fsSelection;
}
PrfWriteProfileData (HINI_USERPROFILE, "Contrast", "Options", &uoUserOptions, sizeof(uoUserOptions));
break;
case WM_MOUSEMOVE:
if (fTablesValid == FALSE)
{
WinSetPointer (HWND_DESKTOP, hptrWait);
return FALSE;
}
break;
case UM_FAIL:
/*****************************************************************************/
/* function : Calls Open dialog box if the lookup tables cannot be made. */
/* Restarts program, or ends it, acccording to the user's wishes. */
/*****************************************************************************/
ShowError (pWriteLine);
if (WinDlgBox (HWND_DESKTOP, hwndFrame, OpenDlgProc,
(HMODULE)NULL, DID_OPEN, NULL) != TRUE)
{
fStartTables = FALSE;
WinSendMsg (hwnd, WM_CLOSE, 0L, 0L);
}
fLoadNewFiles = TRUE;
DosSemClear (hsemClearTables);
return 0;
case UM_TABLES_MADE:
/************************************************************/
/* function : Sets views of files when tables are complete. */
/************************************************************/
for (sIndex = 0; sIndex < 3; sIndex++)
{
sFileLine[sIndex] = 1;
sColPos[sIndex] = 1;
}
ltTopLine[0] = 1;
ltTopLine[1] = sNumLines[0] + 1;
ltTopLine[2] = ltLineNext[0];
fTablesValid = 1;
szTitle = AllocMemPM (strlen (szFile[0]) + strlen (szFile[1]) + 11);
sprintf (szTitle, "Contrast %s %s", szFile[0], szFile[1]);
WinSetWindowText (WinQueryWindow (hwnd, QW_PARENT, FALSE), szTitle);
DosFreeSeg (SELECTOROF(szTitle));
WinInvalidateRect (hwnd, NULL, TRUE);
return 0;
default:
break;
}
return WinDefWindowProc (hwnd, msg, mp1, mp2);
} /* end of ClientWndProc() */
void ShowError(PSZ string)
{
WinMessageBox (HWND_DESKTOP,
hwndFrame,
string,
"Problem",
ID_PROBLEM,
MB_OK | MB_ICONEXCLAMATION);
return;
} /* end of ShowError () */
void ShowPMError (PSZ string)
{
char buffer[256];
sprintf (buffer, "%s error number 0x%x", string, WinGetLastError (hab));
ShowError (buffer);
return;
} /* end of ShowPMError () */
PFONT InitialiseFonts (HWND hwnd)
/***********************************************************/
/* expected parameter: hwnd = client window handle (input) */
/* return value: NULL = no fixed width font found */
/* other = pointer to head of font list */
/* */
/* function : Gets attributes of fixed width fonts. */
/***********************************************************/
{
HPS hps;
HDC hdc;
FONTMETRICS *pfm;
LONG lHorzRes, lVertRes, lRequestFonts, lNumberFonts, lIndex;
USHORT usSmallestSize = 64000; /* anything very big */
PFONT pfontNew, pfontCurrent = NULL;
hps = WinGetPS (hwnd);
/* get device font resolution, so we can match the font to the display */
hdc = GpiQueryDevice (hps);
DevQueryCaps (hdc, CAPS_HORIZONTAL_FONT_RES, 1L, &lHorzRes);
DevQueryCaps (hdc, CAPS_VERTICAL_FONT_RES, 1L, &lVertRes);
/* find out number of fonts available */
lRequestFonts = 0;
lNumberFonts = GpiQueryFonts (hps, QF_PUBLIC, NULL,
&lRequestFonts, 0L, NULL);
/* get metrics of fonts */
if ((pfm = AllocMemPM ((SHORT) lNumberFonts * sizeof (FONTMETRICS))) == NULL)
{
WinReleasePS (hps);
return NULL;
}
GpiQueryFonts (hps, QF_PUBLIC, NULL, &lNumberFonts,
(LONG) sizeof (FONTMETRICS), pfm);
WinReleasePS (hps);
/* search for fonts appropriate to display device */
for (lIndex = 0; lIndex < lNumberFonts; lIndex++)
if (pfm[lIndex].sXDeviceRes == (SHORT) lHorzRes &&
pfm[lIndex].sYDeviceRes == (SHORT) lVertRes &&
(pfm[lIndex].fsType & FM_TYPE_FIXED))
{
if ((pfontNew = AllocMemPM (sizeof (FONT))) == NULL)
break;
/* link to list */
pfontNew->pfontNext = pfontCurrent;
pfontCurrent = pfontNew;
/* copy attributes */
pfontNew->fattrs.lMatch = pfm[lIndex].lMatch;
strcpy (pfontNew->fattrs.szFacename, pfm[lIndex].szFacename);
pfontNew->fattrs.idRegistry = pfm[lIndex].idRegistry;
pfontNew->fattrs.usCodePage = pfm[lIndex].usCodePage;
pfontNew->fattrs.lMaxBaselineExt = pfm[lIndex].lMaxBaselineExt;
pfontNew->fattrs.lAveCharWidth = pfm[lIndex].lAveCharWidth;
pfontNew->fattrs.usRecordLength = sizeof (FATTRS);
pfontNew->fattrs.fsSelection = 0;
pfontNew->fattrs.fsType = 0;
pfontNew->usPointSize = pfm[lIndex].sNominalPointSize / 10;
/* speed up printing - tell PM font will not interfere with graphics */
pfontNew->fattrs.fsFontUse = FATTR_FONTUSE_NOMIX;
/* if it's the user's preferred font, use it and stop looking for others */
if (!strcmp(pfontNew->fattrs.szFacename, uoUserOptions.szFacename) &&
pfontNew->usPointSize == uoUserOptions.usPointSize)
{
pfontSelected = pfontNew;
pfontNew->fattrs.fsSelection = uoUserOptions.fsFontSelection;
usSmallestSize = 0;
}
/* if it's the smallest found yet, use it */
if (pfontNew->usPointSize < usSmallestSize)
{
pfontSelected = pfontNew;
usSmallestSize = pfontNew->usPointSize;
}
}
DosFreeSeg (SELECTOROF (pfm));
return pfontCurrent;
} /* end of InitFont () */
MRESULT EXPENTRY AboutDlgProc (HWND hwnd, USHORT msg, MPARAM mp1, MPARAM mp2)
/***************************************************************************/
/* Simple dialog procedure. Prints some information about the program, and */
/* exits when the 'OK' button is pressed. */
/***************************************************************************/
{
switch (msg)
{
case WM_COMMAND:
switch (COMMANDMSG (&msg)->cmd)
{
case DID_OK:
WinDismissDlg (hwnd, TRUE);
return 0;
default:
break;
}
default:
break;
}
return WinDefDlgProc (hwnd, msg, mp1, mp2);
}
MRESULT EXPENTRY ActualColoursProc (HWND hwnd, USHORT msg, MPARAM mp1, MPARAM mp2)
/**********************************************************************/
/* Displays the actual and default colours used, with an 'OK' button. */
/**********************************************************************/
{
USHORT usColourIndex;
LONG lUserColour;
char szString[30];
switch (msg)
{
case WM_INITDLG:
for (usColourIndex = 0; usColourIndex < 8; usColourIndex++)
{
lUserColour = uoUserOptions.lColour[usColourIndex];
if (lUserColour == CLR_WHITE)
{
lUserColour = 0L;
}
else if (lUserColour == CLR_BLACK)
{
lUserColour = 7L;
}
WinLoadString (hab, (HMODULE) NULL, SID_POSSIBLE_BASE + (USHORT)lUserColour, 30, szString);
WinSetDlgItemText (hwnd, DID_ACTUAL_COLOURS + usColourIndex, szString);
}
return 0;
case WM_COMMAND:
switch (COMMANDMSG (&msg)->cmd)
{
case DID_OK:
WinDismissDlg (hwnd, TRUE);
return 0;
default:
break;
}
default:
break;
}
return WinDefDlgProc (hwnd, msg, mp1, mp2);
}
void InitialiseHelp ()
/****************************************/
/* function : Sets up help information. */
/****************************************/
{
HELPINIT helpinit;
helpinit.cb = sizeof helpinit;
helpinit.pszTutorialName = NULL;
helpinit.phtHelpTable = (PHELPTABLE) (0xFFFF0000 | ID_CONTRAST);
helpinit.hmodHelpTableModule = (HMODULE)NULL;
helpinit.hmodAccelActionBarModule = (HMODULE)NULL;
helpinit.idAccelTable = 0;
helpinit.idActionBar = 0;
helpinit.pszHelpWindowTitle = "Contrast Help Window";
helpinit.usShowPanelId = CMIC_HIDE_PANEL_ID;
helpinit.pszHelpLibraryName = "contrast.hlp";
hwndHelpInstance = WinCreateHelpInstance (hab, &helpinit);
}
MRESULT EXPENTRY OpenDlgProc (HWND hwnd, USHORT msg, MPARAM mp1, MPARAM mp2)
/*************************************************************************/
/* Dialog procedure to get two file names from the user. As close to the */
/* spirit of CUA as a double file dialog will get. */
/*************************************************************************/
{
PSZ szName, szPath, szEnd, szPath2;
HWND hwndFile, hwndDir;
USHORT id;
switch (msg)
{
case WM_INITDLG:
WinAssociateHelpInstance (hwndHelpInstance, hwnd);
if ((szPath = AllocMemPM (usMaxPathLength)) == NULL)
{
WinSendMsg (hwnd, WM_CLOSE, NULL, NULL);
return 0;
}
/* split full path of A into subdirectory & name */
szName = strrchr (szFile[0], '\\') + 1;
WinSetDlgItemText (hwnd, DID_FILEA, szName);
strcpy (szPath, "");
strncat (szPath, szFile[0], szName - szFile[0]);
WinSetDlgItemText (hwnd, DID_CUR_DIR_A, szPath);
/* fill listboxes for this subdirectory */
hwndFile = WinWindowFromID (hwnd, DID_FILES_A);
hwndDir = WinWindowFromID (hwnd, DID_DIRS_A);
FillOpenListBoxes (szPath, hwndFile, hwndDir);
/* do the same for B */
szName = strrchr (szFile[1], '\\') + 1;
WinSetDlgItemText (hwnd, DID_FILEB, szName);
strcpy (szPath, "");
strncat (szPath, szFile[1], szName - szFile[1]);
WinSetDlgItemText (hwnd, DID_CUR_DIR_B, szPath);
hwndFile = WinWindowFromID (hwnd, DID_FILES_B);
hwndDir = WinWindowFromID (hwnd, DID_DIRS_B);
FillOpenListBoxes (szPath, hwndFile, hwndDir);
DosFreeSeg (SELECTOROF(szPath));
return 0;
case WM_COMMAND:
switch (COMMANDMSG (&msg)->cmd)
{
case DID_OK:
/* the user wants these files to be contrasted - get the names */
if (((szPath = AllocMemPM (usMaxPathLength)) == NULL) ||
((szPath2 = AllocMemPM (usMaxPathLength)) == NULL) ||
((szName = AllocMemPM (usMaxPathLength)) == NULL))
{
WinDismissDlg (hwnd, FALSE);
return 0;
}
WinQueryDlgItemText (hwnd, DID_FILEA, usMaxPathLength, szName);
if (szName[1] != ':')
{
WinQueryDlgItemText (hwnd, DID_CUR_DIR_A, usMaxPathLength, szPath);
strcat (szPath, szName);
}
else
{
strcpy (szPath, szName);
}
WinQueryDlgItemText (hwnd, DID_FILEB, usMaxPathLength, szName);
if (szName[0] == '\0')
{
WinQueryDlgItemText (hwnd, DID_FILEA, usMaxPathLength, szName);
}
if (szName[1] != ':')
{
WinQueryDlgItemText (hwnd, DID_CUR_DIR_B, usMaxPathLength, szPath2);
strcat (szPath2, szName);
}
else
{
strcpy (szPath2, szName);
}
if (stricmp (szPath, szPath2) != 0)
{
strcpy (szFile[0], szPath);
strcpy (szFile[1], szPath2);
WinDismissDlg (hwnd, TRUE);
}
DosFreeSeg (SELECTOROF(szName));
DosFreeSeg (SELECTOROF(szPath));
DosFreeSeg (SELECTOROF(szPath2));
return 0;
case DID_CANCEL:
/* leave dialog with no permanent changes */
WinDismissDlg (hwnd, FALSE);
return 0;
case DID_HELP:
WinSendMsg (hwndHelpInstance, HM_EXT_HELP, 0L, 0L);
return 0;
default:
break;
}
case WM_CONTROL:
if (((szPath = AllocMemPM (usMaxPathLength)) == NULL) ||
((szName = AllocMemPM (usMaxPathLength)) == NULL))
{
WinDismissDlg (hwnd, FALSE);
return 0;
}
switch (SHORT2FROMMP(mp1))
{
case LN_SELECT: /* same value as EN_SETFOCUS */
switch (SHORT1FROMMP(mp1))
{
case DID_FILES_A:
case DID_FILES_B:
/* set the appropriate file entry field to the current selection */
WinSendMsg ((HWND)mp2,
LM_QUERYITEMTEXT,
MPFROM2SHORT(SHORT1FROMMP(WinSendMsg ((HWND)mp2,
LM_QUERYSELECTION,
MPFROMSHORT(LIT_FIRST),
NULL)),
usMaxPathLength),
(MPARAM)szName);
id = SHORT1FROMMP(mp1) - DID_FILES_A + DID_FILEA;
WinSetDlgItemText (hwnd, id, szName);
break;
case DID_FILEA: /* really EN_SETFOCUS */
/* if this is the first time here, subclass the field to produce a DID_OK
message when enter is pressed, and limit the test length. We do this here
because it seems to be the 1st point at which the handle to the field is
guaranteed OK. */
if (!fEntryFieldASubclassed)
{
WinSendMsg (HWNDFROMMP(mp2), EM_SETTEXTLIMIT,
MPFROMSHORT (usMaxPathLength), NULL);
pfnOldEntryFieldProc = WinSubclassWindow (HWNDFROMMP(mp2),
MyEntryFieldProc);
fEntryFieldASubclassed = TRUE;
}
break;
case DID_FILEB:
/* subclass this field, and limit it. */
if (!fEntryFieldBSubclassed)
{
WinSendMsg (HWNDFROMMP(mp2), EM_SETTEXTLIMIT,
MPFROMSHORT (usMaxPathLength), NULL);
WinSubclassWindow (HWNDFROMMP(mp2), MyEntryFieldProc);
fEntryFieldBSubclassed = TRUE;
}
break;
default:
break;
}
break;
case LN_ENTER:
switch (SHORT1FROMMP(mp1))
{
case DID_DIRS_A:
case DID_DIRS_B:
/* get the selection */
WinSendMsg ((HWND)mp2,
LM_QUERYITEMTEXT,
MPFROM2SHORT(SHORT1FROMMP(WinSendMsg ((HWND)mp2,
LM_QUERYSELECTION,
MPFROMSHORT(LIT_FIRST),
NULL)),
usMaxPathLength),
(MPARAM)szPath);
/* if it's a drive, just get the drive letter from it */
if (szPath[2] == ':')
{
strcpy (szName, "C:\\");
szName[0] = szPath[1];
}
else
{
id = SHORT1FROMMP(mp1) - DID_DIRS_A + DID_CUR_DIR_A;
WinQueryDlgItemText (hwnd, id, usMaxPathLength, szName);
if (strcmp (szPath, "..") == 0)
{
/* remove last subdirectory; start at the end of the path, skip the null, the
last '\', and one character, and change the next '\' to a null */
szEnd = szName + strlen(szName) - 3;
while (*szEnd != '\\')
{
szEnd--;
}
*szEnd = '\0';
}
else
{
/* add subdirectory to current directory */
strcat (szName, szPath);
}
strcat (szName, "\\");
}
/* put the directory in the text field */
id = SHORT1FROMMP(mp1) - DID_DIRS_A + DID_CUR_DIR_A;
WinSetDlgItemText (hwnd, id, szName);
/* refresh the listboxes */
hwndFile = WinWindowFromID (hwnd, SHORT1FROMMP(mp1) - DID_DIRS_A + DID_FILES_A);
hwndDir = WinWindowFromID (hwnd, SHORT1FROMMP(mp1));
WinSendMsg (hwndFile, LM_DELETEALL, NULL, NULL);
WinSendMsg (hwndDir, LM_DELETEALL, NULL, NULL);
FillOpenListBoxes (szName, hwndFile, hwndDir);
/* use a null string as the starting filename for A; use filename A for B */
if (SHORT1FROMMP(mp1) == DID_DIRS_A)
{
strcpy(szName, "");
}
else
{
WinQueryDlgItemText (hwnd, DID_FILEA, usMaxPathLength, szName);
}
id = SHORT1FROMMP(mp1) - DID_DIRS_A + DID_FILEA;
WinSetDlgItemText (hwnd, id, szName);
break;
case DID_FILES_A:
case DID_FILES_B:
/* get the full file paths, and get out of here */
if (((szPath2 = AllocMemPM (usMaxPathLength)) == NULL))
{
WinDismissDlg (hwnd, FALSE);
return 0;
}
WinQueryDlgItemText (hwnd, DID_FILEA, usMaxPathLength, szName);
if (szName[1] != ':')
{
WinQueryDlgItemText (hwnd, DID_CUR_DIR_A, usMaxPathLength, szPath);
strcat (szPath, szName);
}
else
{
strcpy (szPath, szName);
}
WinQueryDlgItemText (hwnd, DID_FILEB, usMaxPathLength, szName);
if (szName[0] == '\0')
{
WinQueryDlgItemText (hwnd, DID_FILEA, usMaxPathLength, szName);
}
if (szName[1] != ':')
{
WinQueryDlgItemText (hwnd, DID_CUR_DIR_B, usMaxPathLength, szPath2);
strcat (szPath2, szName);
}
else
{
strcpy (szPath2, szName);
}
DosFreeSeg (SELECTOROF(szName));
if (stricmp (szPath, szPath2) != 0)
{
strcpy (szFile[0], szPath);
strcpy (szFile[1], szPath2);
WinDismissDlg (hwnd, TRUE);
}
DosFreeSeg (SELECTOROF(szPath2));
break;
default:
break;
}
break;
default:
break;
}
DosFreeSeg (SELECTOROF(szPath));
DosFreeSeg (SELECTOROF(szName));
return 0;
case HM_INFORM:
WinDlgBox (HWND_DESKTOP, HWND_DESKTOP, ActualColoursProc, (HMODULE)NULL, DID_SHOW_COLOURS, NULL);
return 0;
default:
break;
}
return WinDefDlgProc (hwnd, msg, mp1, mp2);
} /* end of OpenDlgProc () */
void FillOpenListBoxes (PSZ szPath, HWND hwndFileList, HWND hwndDirList)
/**************************************************************************/
/* expected parameters: szPath = full path of current directory (input) */
/* hwndFileList = handle of File listbox (input) */
/* hwndDirList = handle of Direectory listbox (input)*/
/**************************************************************************/
{
HDIR hdir = 0xffff;
MYFILEFINDBUF FileFindBuf;
USHORT cFileNum = 1, usDriveNumber;
ULONG ulLogicalDriveMap;
char szDrive[5];
/* DosFindFirst needs a wildcard to search for files */
strcat (szPath, "*");
if (!DosFindFirst (szPath, &hdir, 0x10, &FileFindBuf, sizeof(FileFindBuf), &cFileNum, 0L))
{
if (FileFindBuf.real.attrFile & 0x10)
{
/* subdirectory; put in directory listbox */
if (strcmp (FileFindBuf.real.achName, "."))
{
/* don't add "." to list */
WinSendMsg (hwndDirList, LM_INSERTITEM,
MPFROMSHORT (LIT_SORTASCENDING), FileFindBuf.real.achName);
}
}
else
{
WinSendMsg (hwndFileList, LM_INSERTITEM,
MPFROMSHORT (LIT_SORTASCENDING), FileFindBuf.real.achName);
}
while (!DosFindNext (hdir, &FileFindBuf, sizeof(FileFindBuf), &cFileNum))
{
if (FileFindBuf.real.attrFile & 0x10)
{
if (strcmp (FileFindBuf.real.achName, "."))
{
WinSendMsg (hwndDirList, LM_INSERTITEM,
MPFROMSHORT (LIT_SORTASCENDING), FileFindBuf.real.achName);
}
}
else
{
WinSendMsg (hwndFileList, LM_INSERTITEM,
MPFROMSHORT (LIT_SORTASCENDING), FileFindBuf.real.achName);
}
}
}
DosFindClose (hdir);
/* add the drives to the directory listbox */
DosQCurDisk (&usDriveNumber, &ulLogicalDriveMap);
strcpy (szDrive, "[A:]");
/* check for each of the 26 possible drives */
for (usDriveNumber = 'A'; usDriveNumber < 'Z'; usDriveNumber++)
{
if ((ulLogicalDriveMap & 1L) == 1)
{
/* drive exists; is it the one currently named in the path? */
if (usDriveNumber != szPath[0])
{
szDrive[1] = usDriveNumber;
WinSendMsg (hwndDirList, LM_INSERTITEM,
MPFROMSHORT (LIT_SORTASCENDING), szDrive);
}
}
ulLogicalDriveMap = ulLogicalDriveMap >> 1;
}
return;
} /* end of FillOpenListBoxes () */
MRESULT EXPENTRY SetColoursProc (HWND hwnd, USHORT msg, MPARAM mp1, MPARAM mp2)
/*********************************************************/
/* Dialog procedure to allow the user to specify colours */
/*********************************************************/
{
LONG lUserColour;
static LONG lNewColour[8];
USHORT usSampleIndex, usColourSel, usStringIndex;
char szString[30], szFontSizeName[60];
HWND hwndSample;
switch (msg)
{
case WM_INITDLG:
WinAssociateHelpInstance (hwndHelpInstance, hwnd);
/* put current colours in candidate colours */
CopyColours (uoUserOptions.lColour, lNewColour);
/* put use-of-colour names in listbox */
for (usStringIndex = 0; usStringIndex < 8; usStringIndex++)
{
WinLoadString (hab, (HMODULE) NULL, SID_USED_BASE + usStringIndex, 30, szString);
WinSendDlgItemMsg (hwnd, DID_COLOURS_USED, LM_INSERTITEM,
MPFROMSHORT (LIT_END), szString);
}
WinSendDlgItemMsg (hwnd, DID_COLOURS_USED, LM_SELECTITEM,
(MPARAM)0, (MPARAM)TRUE);
/* The list of available colours is, due to the strange design of PM, itself strange.
For most colours, their position in the listbox gives their index. However, CLR_WHITE
and CLR_BLACK have negative indices, so they have been put at positions 0 and 7
respectively. If I used CLR_BACKGROUND and CLR_NEUTRAL the coding would be simpler,
but changing the default colours in the Control Panel would affect them, and I don't
want that (nor do I want to try to explain it to a user). It thus depends on the
colour constants staying the same.
*/
/* put possible-colour names in listbox */
for (usStringIndex = 0; usStringIndex < 16; usStringIndex++)
{
WinLoadString (hab, (HMODULE) NULL, SID_POSSIBLE_BASE + usStringIndex, 30, szString);
WinSendDlgItemMsg (hwnd, DID_POSSIBLE_COLOURS, LM_INSERTITEM,
MPFROMSHORT (LIT_END), szString);
}
/* set the selection of the possible-colours listbox */
lUserColour = lNewColour[0];
if (lUserColour == CLR_WHITE)
{
lUserColour = 0L;
}
else if (lUserColour == CLR_BLACK)
{
lUserColour = 7L;
}
WinSendDlgItemMsg (hwnd, DID_POSSIBLE_COLOURS, LM_SELECTITEM,
MPFROMLONG(lUserColour), (MPARAM)TRUE);
/* set foreground & background of sample texts, & the font */
sprintf (szFontSizeName, "%u.%s", pfontSelected->usPointSize,
pfontSelected->fattrs.szFacename);
for (usSampleIndex = 0; usSampleIndex < 14; usSampleIndex++)
{
hwndSample = WinWindowFromID (hwnd, DID_F1_BA + usSampleIndex);
WinSetPresParam (hwndSample,
PP_FOREGROUNDCOLORINDEX,
4,
&lNewColour[usSampleIndex / 3]);
WinSetPresParam (hwndSample,
PP_BACKGROUNDCOLORINDEX,
4,
&lNewColour[usSampleIndex % 3 + 5]);
WinSetPresParam (hwndSample,
PP_FONTNAMESIZE,
sizeof (szFontSizeName),
szFontSizeName);
}
return 0;
case WM_COMMAND:
switch (COMMANDMSG (&msg)->cmd)
{
case DID_OK:
/* use the candidate colours */
CopyColours (lNewColour, uoUserOptions.lColour);
WinDismissDlg (hwnd, TRUE);
return 0;
case DID_CANCEL:
/* exit with no changes */
WinDismissDlg (hwnd, FALSE);
return 0;
case DID_HELP:
WinSendMsg (hwndHelpInstance, HM_EXT_HELP, 0L, 0L);
return 0;
case DID_DEFAULT:
/* put the defaults to the candidate colours */
CopyColours (lDefColour, lNewColour);
/* reset the selection of the possible-colours listbox */
lUserColour = lNewColour[SHORT1FROMMR(WinSendDlgItemMsg (hwnd,
DID_COLOURS_USED,
LM_QUERYSELECTION,
NULL,
NULL))];
if (lUserColour == CLR_WHITE)
{
lUserColour = 0L;
}
else if (lUserColour == CLR_BLACK)
{
lUserColour = 7L;
}
WinSendDlgItemMsg (hwnd, DID_POSSIBLE_COLOURS, LM_SELECTITEM,
MPFROMLONG(lUserColour), (MPARAM)TRUE);
/* reset the colours of the sample texts */
for (usSampleIndex = 0; usSampleIndex < 14; usSampleIndex++)
{
hwndSample = WinWindowFromID (hwnd, DID_F1_BA + usSampleIndex);
WinSetPresParam (hwndSample,
PP_FOREGROUNDCOLORINDEX,
4,
&lNewColour[usSampleIndex / 3]);
WinSetPresParam (hwndSample,
PP_BACKGROUNDCOLORINDEX,
4,
&lNewColour[usSampleIndex % 3 + 5]);
}
return 0;
default:
break;
}
case WM_CONTROL:
switch (SHORT2FROMMP(mp1))
{
case LN_ENTER:
/* exit using the newly-set colours */
CopyColours (lNewColour, uoUserOptions.lColour);
WinDismissDlg (hwnd, TRUE);
break;
case LN_SELECT:
if (SHORT1FROMMP(mp1) == DID_COLOURS_USED)
{
/* reset the selection of the possible-colours listbox */
lUserColour = lNewColour[SHORT1FROMMR(WinSendMsg (mp2,
LM_QUERYSELECTION,
NULL,
NULL))];
if (lUserColour == CLR_WHITE)
{
lUserColour = 0L;
}
else if (lUserColour == CLR_BLACK)
{
lUserColour = 7L;
}
WinSendDlgItemMsg (hwnd, DID_POSSIBLE_COLOURS, LM_SELECTITEM,
MPFROMLONG(lUserColour), (MPARAM)TRUE);
}
else if (SHORT1FROMMP(mp1) == DID_POSSIBLE_COLOURS)
{
/* set the selected used-colour to the selected possible-colour */
lUserColour = (LONG)WinSendMsg (mp2, LM_QUERYSELECTION, NULL, NULL);
if (lUserColour == 0L)
{
lUserColour = CLR_WHITE;
}
else if (lUserColour == 7L)
{
lUserColour = CLR_BLACK;
}
usColourSel = SHORT1FROMMR(WinSendDlgItemMsg (hwnd,
DID_COLOURS_USED,
LM_QUERYSELECTION,
NULL,
NULL));
lNewColour[usColourSel] = lUserColour;
/* reset the colours of the affected samples */
if (usColourSel > 4)
{
for (usSampleIndex = usColourSel - 5;
usSampleIndex < 14;
usSampleIndex += 3)
{
WinSetPresParam (WinWindowFromID (hwnd, DID_F1_BA + usSampleIndex),
PP_BACKGROUNDCOLORINDEX,
4,
&lNewColour[usColourSel]);
}
}
else
{
for (usSampleIndex = usColourSel * 3;
usSampleIndex < (usColourSel + 1) * 3 - usColourSel / 4;
usSampleIndex++)
{
WinSetPresParam (WinWindowFromID (hwnd, DID_F1_BA + usSampleIndex),
PP_FOREGROUNDCOLORINDEX,
4,
&lNewColour[usColourSel]);
}
}
}
break;
default:
break;
}
return NULL;
case HM_INFORM:
WinDlgBox (HWND_DESKTOP, HWND_DESKTOP, ActualColoursProc, (HMODULE)NULL, DID_SHOW_COLOURS, NULL);
return 0;
default:
break;
}
return WinDefDlgProc (hwnd, msg, mp1, mp2);
}
void CopyColours (PLONG lFromColour, PLONG lToColour)
/**********************************************/
/* copies one set of colour values to another */
/**********************************************/
{
USHORT usColIndex;
for (usColIndex = 0; usColIndex < 8; usColIndex++)
{
lToColour[usColIndex] = lFromColour[usColIndex];
}
}
USHORT MakeFullPath (PSZ szFileName)
/*****************************************************************************/
/* expected parameter : szFileName = file name to be expanded (input/output) */
/* return value: 0 = success */
/* 1 = no such file or directory */
/* 2 = failure (no memory) */
/* */
/* function : expands file name to a full directory path, including drive */
/*****************************************************************************/
{
PSZ szParse, szTemp, szCurPath;
USHORT usDisk, usAttr;
ULONG ulDriveMap;
if ((szCurPath = AllocMemPM (usMaxPathLength)) == NULL ||
(szTemp = AllocMemPM (usMaxPathLength)) == NULL)
return 2;
/* convert to upper case - looks better, since OS/2 returns paths in upper
case, and makes drive calculation easier */
szParse = strupr(szFileName);
/* if a drive was supplied, use it; if not, use the current one */
if (szParse[1] == ':')
{
usDisk = *szParse - 'A' + 1;
szParse += 2;
}
else
{
DosQCurDisk (&usDisk, &ulDriveMap);
}
/* if the path isn't specified from the root directory, use the current one */
if (*szParse != '\\')
{
DosQCurDir (usDisk, szTemp, &usMaxPathLength);
sprintf (szCurPath, "\\%s",szTemp);
/* don't add backslash if either current directory or rest of parameter are null */
if (*szTemp != '\0' && *szParse != '\0')
strcat (szCurPath, "\\");
}
else
{
strcpy (szCurPath, "");
}
strcpy (szTemp, szParse);
sprintf (szFileName, "%c:%s%s", usDisk + 'A' - 1, szCurPath, szTemp);
DosFreeSeg (SELECTOROF (szCurPath));
/* remove any "\dir\.." found in the string */
while ((szParse = strstr (szFileName, "..")) != NULL)
{
/* save rest of string after ".." */
strcpy (szTemp, szParse + 2);
/* go back to before preceding '\' & 1 character */
szParse -= 3;
/* look for preceding '\' and change to null character to truncate string */
while (*szParse != '\\')
szParse--;
*szParse = '\0';
/* add rest of string */
strcat (szFileName, szTemp);
}
DosFreeSeg (SELECTOROF (szTemp));
/* if the user has specified a sub-directory, return at once */
if (szFileName[strlen (szFileName) - 1] == '\\')
{
return 0;
}
/* If it is just a path, append a backslash. */
/* get file attributes */
if (DosQFileMode (szFileName, &usAttr, 0L))
/* we can't find it, so default to the current directory, & say we need
the dialog */
{
sprintf (pWriteLine,"Couldn't find %s",szFileName);
ShowError (pWriteLine);
if (GetDefaultDir (szFileName))
return 1;
else
return 2;
}
else
{
/* check to see if subdirectory */
if (usAttr & 0x10)
{
strcat (szFileName, "\\");
}
}
return 0;
} /* end of MakeFullPath () */
BOOL GetDefaultDir (PSZ szDefDir)
/*************************************************************/
/* expected parameter: szDefDir = current directory (output) */
/* return value: TRUE = success */
/* FALSE = failure (no memory) */
/*************************************************************/
{
USHORT usDisk;
ULONG ulDriveMap;
PSZ szDir;
if ((szDir = AllocMemPM (usMaxPathLength)) == NULL)
{
return FALSE;
}
DosQCurDisk (&usDisk, &ulDriveMap);
DosQCurDir (usDisk, szDir, &usMaxPathLength);
sprintf (szDefDir, "%c:\\%s", usDisk - 1 + 'A', szDir);
if (szDir[0] != '\0')
{
strcat (szDefDir, "\\");
}
DosFreeSeg (SELECTOROF(szDir));
return TRUE;
} /* end of GetDefaultDir () */
MRESULT EXPENTRY FontsDlgProc (HWND hwnd, USHORT msg, MPARAM mp1, MPARAM mp2)
/***************************************************************************/
/* allows different fonts to be selected */
/***************************************************************************/
{
static PFONT pfontNew;
char szFontName[60];
int iFontSel, iFontIndex;
switch (msg)
{
case WM_INITDLG:
WinAssociateHelpInstance (hwndHelpInstance, hwnd);
iFontSel = 0;
iFontIndex = 0;
/* walk list of fonts (created in InitialiseFonts ()) to get font names and
sizes, and select the current font in the listbox */
for (pfontNew = pfontHead; pfontNew != NULL; pfontNew = pfontNew->pfontNext)
{
sprintf (szFontName, "%s %u", pfontNew->fattrs.szFacename,
pfontNew->usPointSize);
WinSendDlgItemMsg (hwnd, DID_FONTS_AVAILABLE, LM_INSERTITEM,
MPFROMSHORT (LIT_END), szFontName);
if (pfontNew == pfontSelected)
{
iFontSel = iFontIndex;
}
iFontIndex++;
}
WinSendDlgItemMsg (hwnd,
DID_FONTS_AVAILABLE,
LM_SELECTITEM,
MPFROMSHORT(iFontSel),
(MPARAM)TRUE);
/* from here on in the dialog, pfontNew is the candidate font */
pfontNew = pfontSelected;
return 0;
case WM_COMMAND:
switch (COMMANDMSG (&msg)->cmd)
{
case DID_OK:
/* set current font to new one */
pfontSelected = pfontNew;
WinDismissDlg (hwnd, TRUE);
return 0;
case DID_CANCEL:
/* return without changing the font */
WinDismissDlg (hwnd, FALSE);
return 0;
case DID_HELP:
WinSendMsg (hwndHelpInstance, HM_EXT_HELP, 0L, 0L);
return 0;
default:
break;
}
case WM_CONTROL:
switch (SHORT2FROMMP(mp1))
{
/* PM assigns the same value (1) to LN_SELECT and BN_CLICKED, both of which
we want to respond to, so we use one case statement for both, and check
the ID of the control (which we really ought to do anyway) */
case LN_SELECT:
if (SHORT1FROMMP(mp1) == DID_FONTS_AVAILABLE)
{
/* this really is a LN_SELECT message from the listbox, so find which item
was selected */
iFontSel = SHORT1FROMMR (WinSendMsg (mp2,
LM_QUERYSELECTION,
NULL,
NULL));
/* walk through the linked list of fonts to the correct one */
pfontNew = pfontHead;
for (iFontIndex = 0; iFontIndex < iFontSel; iFontIndex++)
{
pfontNew = pfontNew->pfontNext;
}
/* set the state of the bold & italic checkboxes to those last requested for
this font */
WinSendDlgItemMsg (hwnd,
DID_BOLD,
BM_SETCHECK,
MPFROMSHORT ((pfontNew->fattrs.fsSelection & FATTR_SEL_BOLD) == FATTR_SEL_BOLD),
NULL);
WinSendDlgItemMsg (hwnd,
DID_ITALIC,
BM_SETCHECK,
MPFROMSHORT ((pfontNew->fattrs.fsSelection & FATTR_SEL_ITALIC) == FATTR_SEL_ITALIC),
NULL);
}
else
{
/* this is really a BN_CLICKED message; toggle the state of the font
characteristics, according to which checkbox it was */
if (SHORT1FROMMP(mp1) == DID_BOLD)
pfontNew->fattrs.fsSelection ^= FATTR_SEL_BOLD;
else
pfontNew->fattrs.fsSelection ^= FATTR_SEL_ITALIC;
/* toggle the state of the checkbox */
WinSendDlgItemMsg (hwnd,
SHORT1FROMMP(mp1),
BM_SETCHECK,
MPFROMSHORT(SHORT1FROMMR(WinSendDlgItemMsg (hwnd,
SHORT1FROMMP(mp1),
BM_QUERYCHECK,
NULL,
NULL)) ^ 1),
NULL);
}
break;
default:
break;
}
return NULL;
case HM_INFORM:
WinDlgBox (HWND_DESKTOP, HWND_DESKTOP, ActualColoursProc, (HMODULE)NULL, DID_SHOW_COLOURS, NULL);
return 0;
default:
break;
}
return WinDefDlgProc (hwnd, msg, mp1, mp2);
} /* end of FontsDlgProc () */
MRESULT EXPENTRY MyEntryFieldProc (HWND hwnd, USHORT msg, MPARAM mp1, MPARAM mp2)
/**************************************************************************/
/* subclasses the entry fields for the Open dialog, so a return causes a */
/* DID_OK message. This is because we can't use a DEFPUSHBUTTON, since it */
/* ends the dialog when return is pressed in the directory list. */
/**************************************************************************/
{
if (msg == WM_CHAR &&
(SHORT1FROMMP(mp1) & (KC_VIRTUALKEY | KC_KEYUP)) == KC_VIRTUALKEY &&
(SHORT2FROMMP(mp2) == VK_ENTER) || (SHORT2FROMMP(mp2) == VK_NEWLINE))
{
WinPostMsg (WinQueryWindow (hwnd, QW_PARENT, 0),
WM_COMMAND,
MPFROMSHORT (DID_OK),
MPFROM2SHORT (CMDSRC_PUSHBUTTON, FALSE));
}
return pfnOldEntryFieldProc (hwnd, msg, mp1, mp2);
}
PVOID AllocMemPM (USHORT usSize)
/* allocates memory for PM thread */
{
SEL Selector;
char buffer[24];
if (DosAllocSeg (usSize, &Selector, 0))
{
strcpy (buffer, "can't allocate memory");
ShowError (buffer);
return NULL;
}
return MAKEP (Selector, 0);
} /* end of AllocMemPM () */
/************************************************************/
/* The following functions are in the second (table) thread */
/************************************************************/
void cdecl far MakeTables (void far *dummy)
{
while (fStartTables == TRUE)
{
DosSemSet (hsemClearTables);
switch (CreateTables ())
{
case 0:
WinPostMsg (hwndClient, UM_FAIL, NULL, NULL);
break;
case 1:
WinPostMsg (hwndClient, UM_TABLES_MADE, NULL, NULL);
break;
default:
break;
}
DosSemWait (hsemClearTables, -1L);
ReleaseMemory ();
}
_endthread ();
}
SHORT CreateTables (void)
/***********************************************************************/
/* return value: 0 = failure */
/* 1 = success */
/* 2 = interrupted */
/* */
/* function: Reads in files to memory and creates hash and line tables */
/***********************************************************************/
{
HFILE hFileA, hFileB; /* files opened by OpenFile, closed by ReadFile */
static ULONG ulFileLengthA, ulFileLengthB, ulStartFileB, ulTotalLength;
SHORT sArraySize;
PSZ szDefFileName;
if (fLoadNewFiles == TRUE)
{
/* Check if second file is just a path, i.e. the user wants to use the
same filename as the first file. If so, extract & append the filename. */
if (szFile[1][strlen (szFile[1]) - 1] == '\\')
{
szDefFileName = strrchr (szFile[0], '\\') + 1;
strcat (szFile[1], szDefFileName);
}
if ((ulFileLengthA = OpenFile (szFile[0], &hFileA)) == 0L)
return 0;
/* work out space needed, including gap so second file is aligned on a
32768 byte boundary
*/
ulTotalLength = ulStartFileB = (ulFileLengthA / 32768 + 1) * 32768;
if ((ulFileLengthB = OpenFile (szFile[1], &hFileB)) == 0L)
return 0;
ulTotalLength += ulFileLengthB;
if (DosAllocHuge ((USHORT) (ulTotalLength / 65536L),
(USHORT) (ulTotalLength % 65536L),
&selBase,
0,
0))
{
strcpy (pWriteLine, "can't allocate memory for files");
return 0;
}
/* get selector shift for access to huge memory */
DosGetHugeShift (&usHugeShift);
if (ReadFile (hFileA, 0L, ulFileLengthA) == 0)
{
strcat (pWriteLine, szFile[0]);
return 0;
}
if (ReadFile (hFileB, ulStartFileB, ulTotalLength) == 0)
{
strcat (pWriteLine, szFile[1]);
return 0;
}
}
/* allocate full 64k for start-of-line and hash-value arrays. We will shrink
them to the correct size later. This means the files are only read once. In
future, if we haven't enough memory for this, we should count the line and
try to reallocate, so small comparisons might fit in. If we do, we should
let the user know we're trying to use less memory */
if ( (ulLine = AllocMem (0)) == NULL
|| (ulLineHash = AllocMem (0)) == NULL)
return 0;
sMaxLength = 0;
if (DosSemWait (hsemClearTables, 0L) == 0)
return 2;
sNumLines[0] = MakeHashValues (0L, ulFileLengthA, 1);
if (DosSemWait (hsemClearTables, 0L) == 0)
return 2;
sTotalLines = MakeHashValues (ulStartFileB, ulTotalLength, sNumLines[0] + 1);
if (DosSemWait (hsemClearTables, 0L) == 0)
return 2;
sNumLines[1] = sTotalLines - sNumLines[0];
/* limit maximum length to 512, since PM won't print longer strings */
sMaxLength = MIN (sMaxLength, 512);
if (sTotalLines >= 16384)
{
strcpy (pWriteLine, "too many lines - must be less than 16K in total");
return 0;
}
sArraySize = sTotalLines + 1;
/************************************************************/
/* reallocate memory for line hash and start of line tables */
/************************************************************/
DosReallocSeg (sArraySize * sizeof (ULONG), SELECTOROF (ulLine));
DosReallocSeg (sArraySize * sizeof (ULONG), SELECTOROF (ulLineHash));
/* Allocate memory for other tables */
if ( (ltLinePair = AllocMem (sArraySize * sizeof (SHORT))) == NULL
|| (ltLinePrev = AllocMem (sArraySize * sizeof (SHORT))) == NULL
|| (ltLineNext = AllocMem (sArraySize * sizeof (SHORT))) == NULL
|| (usLineCol = AllocMem (sArraySize * sizeof (USHORT))) == NULL
|| (usBackCol = AllocMem (sArraySize * sizeof (USHORT))) == NULL
|| (HashTable = AllocMem (sTotalLines * sizeof (SHORT))) == NULL)
return 0;
ChainIdenticalLines ();
if (DosSemWait (hsemClearTables, 0L) == 0)
return 2;
FindUniquePairs ();
DosFreeSeg (SELECTOROF (HashTable));
if (DosSemWait (hsemClearTables, 0L) == 0)
return 2;
ExpandUniquePairs ();
DosFreeSeg (SELECTOROF (ulLineHash));
if (DosSemWait (hsemClearTables, 0L) == 0)
return 2;
SetColours ();
if (DosSemWait (hsemClearTables, 0L) == 0)
return 2;
/* make composite file */
sNumLines[2] = 0;
Composite (1, sNumLines[0], sNumLines[0] + 1, sTotalLines, 0, 0);
return 1;
} /* end of CreateTables () */
void ReleaseMemory (void)
{
if (fLoadNewFiles == TRUE)
DosFreeSeg (selBase);
DosFreeSeg (SELECTOROF (ulLine));
DosFreeSeg (SELECTOROF (ltLinePrev));
DosFreeSeg (SELECTOROF (ltLineNext));
DosFreeSeg (SELECTOROF (ltLinePair));
DosFreeSeg (SELECTOROF (usLineCol));
DosFreeSeg (SELECTOROF (usBackCol));
} /* end of ReleaseMemory () */
ULONG OpenFile (PSZ szFileName, PHFILE pFileHandle)
/***********************************************************************/
/* expected parameters : szFileName = name of file (input) */
/* pFileHandle = pointer to file handle (output) */
/* return value: 0 = failure */
/* other = length of file in bytes */
/***********************************************************************/
{
USHORT usAction;
FILESTATUS FileInfo;
if (DosOpen (szFileName,
pFileHandle,
&usAction, /* file exists/created/replaced */
0L, /* file size (ignored) */
1, /* attributes (read only) */
1, /* open if exists, fail if not */
32, /* deny write, ask for read-only */
0L)) /* reserved */
{
strcpy (pWriteLine, "failed to open ");
strcat (pWriteLine, szFileName);
return 0L;
}
/* get file length from file information */
if (DosQFileInfo (*pFileHandle,
1, /* level of info */
(PCH) &FileInfo, /* buffer for info */
sizeof FileInfo )) /* size of buffer */
{
strcpy (pWriteLine, "failed to get information for ");
strcat (pWriteLine, szFileName);
return 0L;
}
return FileInfo.cbFile;
} /* end of OpenFile () */
SHORT ReadFile (HFILE FileHandle, ULONG ulStartMem, ULONG ulEndMem)
/***********************************************************************/
/* expected parameters : FileHandle = handle of file (input) */
/* ulStartMem = start of memory for file (input) */
/* ulEndMem = end of memory for file (input) */
/* return value : 0 = failure */
/* 1 = success */
/* */
/* function : reads in a file and closes it */
/***********************************************************************/
{
USHORT usBytesRequested = 32768, usBytesRead;
/********************************************************************/
/* read in file in blocks half a segment long, so no overlap occurs */
/********************************************************************/
do
{
if (ulEndMem < ulStartMem + 32768)
{
usBytesRequested = ulEndMem - ulStartMem;
}
if (DosRead (FileHandle, (PVOID)MEM (ulStartMem), usBytesRequested, &usBytesRead))
{
strcpy (pWriteLine, "error in reading ");
DosClose (FileHandle);
return 0;
}
ulStartMem += usBytesRead;
} while (usBytesRead != 0);
DosClose (FileHandle);
return 1;
} /* end of ReadFile () */
SHORT MakeHashValues (ULONG ulChar, ULONG ulEnd, SHORT ltIndex)
/******************************************************************/
/* expected parameters : ulChar = start of file in memory (input) */
/* ulEnd = end of file in memory (input) */
/* ltIndex = index of first line (input) */
/* return value : index of last line in file */
/* */
/* function : sets up tables for starts of lines and hash values, */
/* and finds the length of the longest line */
/******************************************************************/
{
ULONG ulHashVal, ulHashValNoBlanks, ulRandom[512];
BOOL fLastWasGraphic;
USHORT usRandomIndex;
char Char;
/* The hashing is done by setting up a table of pseudo-random numbers (the
table is 512 long because PM won't print more characters than that, so
we'll only pay attention to the first 512 chracters). The ASCII value of
each character is multiplied by the next random number in the sequence,
and added to the hash value for the line.
Mathematical diversion:
The random values and the hash values are 32 bits long; hopefully (but I'm
not going to write a thesis to prove it) this will produce a distribution
of hash values very close to perfect, in which case the chances of collision
for N lines are given by: 1 - (2^32 - 1)! / ((2^32 - N)! * (2^32)^(N - 1) .
(I think; my maths is getting weak, but it tallies with the case given in a
text book of the chances of a shared birthday in a room of 23 people being
0.5). This works out at 0.03 for the maximum N of 16384.
The values for the pseudo-random sequence satisfy rules given in Kurth (Art
of Computer Programming) for R(N) = (R(N-1) * A + C) mod M, i.e.
1) C and M are relatively prime
2) B = A - 1 is a multiple of every prime of M
3) if M is a multiple of 4, B must be as well.
Since M = 2^32, 2) is satisfied by satisfying 3), and 1) is satisfied by any
odd number. This should give a sequence which only repeats every M values.
To avoid 0 turning up in the ones used, I have started the sequence at C,
i.e. the value following 0. Using B = 4 speeds up computation, and using a
large C ensures that overflow will take place, which should help make the
distribution of hash values even. */
ulRandom[0] = 0xABCDEF01;
for (usRandomIndex = 1; usRandomIndex < 512; usRandomIndex++)
{
ulRandom[usRandomIndex] = ulRandom[usRandomIndex - 1] * 5 + 0xABCDEF01;
}
while (ulChar < ulEnd && ltIndex <= 16384)
{
ulHashVal = 0;
usRandomIndex = 0;
ulLine[ltIndex] = ulChar;
/* White space is regarded as a space, null character or tab. Skip over any
leading white space if required */
if (uoUserOptions.fIgnoreLeadingBlanks == TRUE)
while (ulChar < ulEnd &&
((Char = CMEM (ulChar)) == ' ' ||
Char == '\0' ||
Char == '\t'))
ulChar++;
fLastWasGraphic = TRUE;
do
{
Char = CMEM (ulChar);
/* Skip all white space if required. Ignore characters if we have run out of
random multipliers. */
if ((!uoUserOptions.fIgnoreAllBlanks ||
(Char != ' ' &&
Char != '\0' &&
Char != '\t')) &&
usRandomIndex < 512)
{
/* If ignoring trailing blanks, remember the last hash value from a non-blank
character. */
if (uoUserOptions.fIgnoreTrailingBlanks == TRUE)
{
if (Char <= 32)
{
if (fLastWasGraphic == TRUE)
{
fLastWasGraphic = FALSE;
ulHashValNoBlanks = ulHashVal;
}
}
else
fLastWasGraphic = TRUE;
}
ulHashVal += ulRandom[usRandomIndex++] * Char;
}
ulChar++;
} while (Char != '\n' && ulChar < ulEnd);
if (uoUserOptions.fIgnoreTrailingBlanks && !uoUserOptions.fIgnoreAllBlanks)
ulHashVal = ulHashValNoBlanks;
if (Char == '\n')
{
sMaxLength = MAX (sMaxLength, (SHORT)(ulChar - ulLine[ltIndex]));
ulLineHash[ltIndex] = ulHashVal;
ltIndex++;
}
}
return ltIndex - 1;
} /* end of MakeHashValues () */
void ChainIdenticalLines (void)
/**************************************************************************/
/* function : Finds lines with the same hash value, and forms them into a */
/* chain using the ltLinePair array as pointers. Also sets the foreground */
/* and background colours to the unmatched values. */
/**************************************************************************/
{
SHORT htIndex, ltIndex, ltPair;
USHORT usColBackgrnd;
ULONG ulHashVal;
/* clear hash table */
for (htIndex = 0; htIndex < sTotalLines; htIndex++)
HashTable[htIndex] = 0;
usColBackgrnd = COL_A_BGRND;
/* set up hash and line tables */
for (ltIndex = 1; ltIndex <= sTotalLines; ltIndex++)
{
usBackCol[ltIndex] = usColBackgrnd;
usLineCol[ltIndex] = COL_UNIQUE;
ulHashVal = ulLineHash[ltIndex];
htIndex = (SHORT) (ulHashVal % sTotalLines);
/* look for free place in hash table */
while ((ltPair = HashTable[htIndex]) != 0
&& ulLineHash[ltPair] != ulHashVal)
htIndex = (htIndex +1) % sTotalLines;
/* chain this line to any others with same hash value */
ltLinePair[ltIndex] = ltPair;
HashTable[htIndex] = ltIndex;
/* change background colour if starting on second file */
if (ltIndex == sNumLines[0])
usColBackgrnd = COL_B_BGRND;
}
return;
} /* end of ChainIdenticalLines () */
void FindUniquePairs (void)
/***************************************************************************/
/* function : pairs lines that occur exactly once in each file, and breaks */
/* any other chains */
/***************************************************************************/
{
SHORT htIndex, ltIndex, ltPair;
for (htIndex = 0; htIndex < sTotalLines; htIndex++)
{
if ((ltIndex = HashTable[htIndex]) == 0)
/* no such line */
continue;
if ((ltPair = ltLinePair[ltIndex]) != 0
&& ltLinePair[ltPair] == 0
&& ltIndex > sNumLines[0]
&& ltPair <= sNumLines[0])
/* one line in each file, so pair up */
ltLinePair[ltPair] = ltIndex;
else
/* identical lines in the same file, so break chain */
while (ltIndex != 0)
{
ltPair = ltLinePair[ltIndex];
ltLinePair[ltIndex] = 0;
ltIndex = ltPair;
}
}
return;
} /* end of FindUniquePairs () */
void ExpandUniquePairs (void)
/*********************************************************************/
/* function : Pairs up identical lines before or after unique pairs. */
/* These will be lines that have 2 or more duplicates. */
/*********************************************************************/
{
SHORT ltIndex, ltPair, i;
for (ltIndex = 0; ltIndex <= sNumLines[0]+1; ltIndex++)
{
/***************************************************************************/
/* pretend there are unique pairs before and after the files, so the first */
/* and last lines will be paired if identical */
/***************************************************************************/
if (ltIndex == 0)
ltPair = sNumLines[0];
else if (ltIndex > sNumLines[0])
ltPair = sTotalLines+1;
else
ltPair = ltLinePair[ltIndex];
if (ltPair != 0)
{
/* move forwards looking for identical lines */
i = 1;
while (ltPair + i <= sTotalLines
&& ltIndex + i <= sNumLines[0]
&& ulLineHash[ltIndex + i] == ulLineHash[ltPair + i]
&& ltLinePair[ltIndex + i] == 0
&& ltLinePair[ltPair + i] == 0)
{
ltLinePair[ltIndex + i] = ltPair + i;
ltLinePair[ltPair + i] = ltIndex + i;
i++;
}
/* and move backwards */
i = -1;
while (ltPair + i > sNumLines[0]
&& ltIndex + i > 0
&& ulLineHash[ltIndex + i] == ulLineHash[ltPair + i]
&& ltLinePair[ltIndex + i] == 0
&& ltLinePair[ltPair + i] == 0)
{
ltLinePair[ltIndex + i] = ltPair + i;
ltLinePair[ltPair + i] = ltIndex + i;
i--;
}
}
}
return;
} /* end of ExpandUniquePairs () */
void SetColours (void)
/***************************************************************************/
/* function : Finds colours to display matching lines in, so that adjacent */
/* blocks have different colours. */
/***************************************************************************/
{
SHORT ltTopABlock, ltTopBBlock, ltBottomBBlock, ltIndex, ltPair, ltLoop;
USHORT usColIndex;
ltTopABlock = 0;
ltTopBBlock = ltBottomBBlock = 0;
for (ltIndex = 1; ltIndex <= sNumLines[0] + 1; ltIndex++)
{
if ((ltPair = ltLinePair[ltIndex]) == ltBottomBBlock+1)
ltBottomBBlock = ltPair; /* continuation of block */
else
{
if (ltBottomBBlock != 0) /* blocks to be coloured */
{
/* find colour different from adjacent lines */
usColIndex = 0;
while (ltTopABlock > 1
&& usLineCol[ltTopABlock - 1] == usColIndex
|| ltTopBBlock > sNumLines[0] + 1
&& usLineCol[ltTopBBlock - 1] == usColIndex
|| ltBottomBBlock < sTotalLines
&& usLineCol[ltBottomBBlock + 1] == usColIndex)
usColIndex++;
/* set colours in line table entries */
for (ltLoop = ltTopABlock; ltLoop < ltIndex; ltLoop++)
usLineCol[ltLoop] = usColIndex;
for (ltLoop = ltTopBBlock; ltLoop <= ltBottomBBlock; ltLoop++)
usLineCol[ltLoop] = usColIndex;
/* reset bottom of B block, i.e. no block current */
ltBottomBBlock = 0;
}
if (ltPair != 0) /* new block */
{
ltTopABlock = ltIndex;
ltTopBBlock = ltBottomBBlock = ltPair;
}
}
}
return;
} /* end of SetColours () */
void Composite (SHORT ltTopA, SHORT ltBottomA, SHORT ltTopB,
SHORT ltBottomB, SHORT ltPrecLine, SHORT ltFolLine)
/*****************************************************************************/
/* expected parameters: ltTopA, ltBottomA, ltTopB, ltBottomB = lines */
/* bounding section to be composed (input) */
/* ltPrecLine, ltFolLine = lines composite section */
/* is to be fitted into (input) */
/* */
/* function : Finds the largest matching block of lines in the section given */
/* to it, puts them in the composite file, and then calls itself */
/* to do the same for the lines above the matching section, and */
/* then those below it. If no matching sections are found, it */
/* puts in unmatched lines, or those which match with lines in */
/* another section. */
/*****************************************************************************/
{
SHORT sMaxBlockLength, sCurBlockLength, ltA, ltB,
ltMaxBlockStart, ltMaxBlockStop, ltCurBlockStart, ltIndex;
USHORT usCurCol;
/* find largest matching block */
sMaxBlockLength = 0;
for (ltIndex = ltTopA; ltIndex <= ltBottomA; ltIndex++)
{
if (ltLinePair[ltIndex] < ltTopB
|| ltLinePair[ltIndex] >ltBottomB)
continue; /* no match, or matches outside this section */
if (ltIndex == ltTopA
|| usLineCol[ltIndex] != usLineCol[ltIndex - 1])
{ /* start of matching block */
sCurBlockLength = 1;
ltCurBlockStart = ltIndex;
}
else
sCurBlockLength++;
if (sCurBlockLength > sMaxBlockLength)
{
sMaxBlockLength = sCurBlockLength;
ltMaxBlockStart = ltCurBlockStart;
}
}
if (sMaxBlockLength > 0)
{ /* matching block found */
ltMaxBlockStop = ltMaxBlockStart + sMaxBlockLength - 1;
/* link lines in block, and change background colour to indicate they are
matched lines. Increase count of number of lines in composite file.
*/
for (ltIndex = ltMaxBlockStart;
ltIndex <= ltMaxBlockStop;
ltIndex++)
{
if (ltIndex != ltMaxBlockStart)
ltLinePrev[ltIndex] = ltIndex - 1;
if (ltIndex != ltMaxBlockStop)
ltLineNext[ltIndex] = ltIndex + 1;
usBackCol[ltIndex] = usBackCol[ltLinePair[ltIndex]] = COL_MATCHED_BGRND;
sNumLines[2]++;
}
/* compose lines above matched section */
Composite (ltTopA, ltMaxBlockStart - 1,
ltTopB, ltLinePair[ltMaxBlockStart] - 1,
ltPrecLine, ltMaxBlockStart);
/* now do those below it */
Composite (ltMaxBlockStop + 1, ltBottomA,
ltLinePair[ltMaxBlockStop] + 1, ltBottomB,
ltMaxBlockStop, ltFolLine);
return;
}
else
{ /* no matching lines found, so put in the odd ones */
ltA = ltTopA;
ltB = ltTopB;
/* do until all lines have been added */
while (ltA <= ltBottomA || ltB <= ltBottomB)
{
if (uoUserOptions.fInterleaveUniqueLines)
{
/* add any unique lines to the composite, alternately */
while (ltA <= ltBottomA
&& ltB <= ltBottomB
&& ltLinePair[ltA] == 0
&& ltLinePair[ltB] == 0)
{
Add (<A, <PrecLine);
Add (<B, <PrecLine);
}
}
/* add remaining unique lines from file A */
while (ltA <= ltBottomA && ltLinePair[ltA] == 0)
Add (<A, <PrecLine);
/* and add those from file B */
while (ltB <= ltBottomB && ltLinePair[ltB] == 0)
Add (<B, <PrecLine);
if (ltA <= ltBottomA)
{
usCurCol = usLineCol[ltA];
/* add any lines from file A that have matches, but not in this section */
while (ltA <= ltBottomA
&& usLineCol[ltA] == usCurCol)
Add (<A, <PrecLine);
}
/* and do the same for file B */
if (ltB <= ltBottomB)
{
usCurCol = usLineCol[ltB];
while (ltB <= ltBottomB
&& usLineCol[ltB] == usCurCol)
Add (<B, <PrecLine);
}
}
/* link the lines to the next section of the composite file */
ltLineNext[ltPrecLine] = ltFolLine;
ltLinePrev[ltFolLine] = ltPrecLine;
}
return;
} /* end of Composite () */
void Add (PSHORT pltIndex, PSHORT pltPrecLine)
/* expected parameters: pltIndex = pointer to line table entry to be
added to composite file (input/output)
pltPrecLine = preceding line in composite (input/output)
*/
{
ltLinePrev[*pltIndex] = *pltPrecLine;
ltLineNext[*pltPrecLine] = *pltIndex;
*pltPrecLine = *pltIndex;
(*pltIndex)++;
sNumLines[2]++;
} /* end of Add () */
PVOID AllocMem (USHORT usSize)
/* allocates memory for 2nd (non-PM) thread */
{
SEL Selector;
if (DosAllocSeg (usSize, &Selector, 0))
{
strcpy (pWriteLine, "can't allocate memory");
return NULL;
}
return MAKEP (Selector, 0);
} /* end of AllocMem () */