home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Windows Gam…ming Gurus (2nd Edition) / Disc2.iso / msdn_vcb / samples / vc98 / sdk / sdktools / windiff / windiff.c < prev    next >
C/C++ Source or Header  |  1997-10-05  |  95KB  |  2,761 lines

  1.  
  2. /******************************************************************************\
  3. *       This is a part of the Microsoft Source Code Samples. 
  4. *       Copyright (C) 1993-1997 Microsoft Corporation.
  5. *       All rights reserved. 
  6. *       This source code is only intended as a supplement to 
  7. *       Microsoft Development Tools and/or WinHelp documentation.
  8. *       See these sources for detailed information regarding the 
  9. *       Microsoft samples programs.
  10. \******************************************************************************/
  11.  
  12. /****************************** Module Header *******************************
  13. * Module Name: WINDIFF.C
  14. *
  15. * File and directory comparisions.
  16. *
  17. * Functions:
  18. *
  19. * windiff_UI()
  20. * WinMain()
  21. * windiff_usage()
  22. * Poll()
  23. * DoResize()
  24. * AboutBox()
  25. * DoPrint()
  26. * FindNextChange()
  27. * FindPrevChange()
  28. * WriteProfileInt()
  29. * ToOutline()
  30. * ToMoved()
  31. * do_editfile()
  32. * do_editthread()
  33. * SetStatus()
  34. * SetNames()
  35. * IsBusy()
  36. * BusyError()
  37. * StateToColour()
  38. * SetSelection()
  39. * do_gethdr()
  40. * do_getprops()
  41. * do_getdata()
  42. * SvrClose()
  43. * TableServer()
  44. * wd_dirdialog()
  45. * wd_copy()
  46. * InitApplication()
  47. * InitInstance()
  48. * CreateTools()
  49. * DeleteTools()
  50. * MainWndProc()
  51. * SetBusy()
  52. * SetNotBusy()
  53. * SetSelection()
  54. * SetButtonText()
  55. * ToExpand()
  56. * ParseArgs()
  57. * wd_initial()
  58. *
  59. * Comments:
  60. *
  61. * Compare two directories (including all files and subdirs). Look for names
  62. * that are present in both (report all that are not). For files that
  63. * are present in both, produce a line-by-line comparison of the differences
  64. * between the two files (if any).
  65. *
  66. * Overview of Windiff internals - the whole program.
  67. *
  68. * Windiff is built from several modules (a "module" has a .h file
  69. * which describes its interface and a .c file which implements it).
  70. * Apart from THIS comment which tries to give an overview of the whole
  71. * scheme of things, each module is as self-contained as possible.
  72. * This is enforced by the use of opaque data types.  Modules cannot
  73. * see each others' internal data structures.  Modules are abstract
  74. * data types.  The term "Module" (from Modula2) and "Class" (from C++)
  75. * are used synonymously.
  76. *
  77. *    Windiff  - main program - parse arguments, put up main window,
  78. *               handle input, calling other modules as needed
  79. *               invoke table class to create the main display and
  80. *               service callbacks from the table class.
  81. *               Contains global flags for options (e.g. ignore_blanks)
  82. *    list     - (in gutils) a generalised LIST of anything data type
  83. *               has full set of operations for insert, delete, join etc.
  84. *    line     - a LINE is a numbered line of text.  Information is kept to
  85. *               allow fast comparisons of LINEs.  A LINE can hold a
  86. *               link to another LINE.  The links are used to connect
  87. *               lines in one file to matching lines in the other file.
  88. *    file     - a FILEDATA represents a file as a file name in the form
  89. *               of a DIRITEM and a LIST of LINEs
  90. *    scandir  - a DIRITEM represents information about a file.  (for
  91. *               instance its name, whether it has a local copy).
  92. *    compitem - a COMPITEM is a pair of files together with information
  93. *               on how they compare in the form of a breakdown of the
  94. *               files into a LIST of matching or non-matching sections.
  95. *               Either file can be absent.  This module contains the
  96. *               file "contrast" algorithm used for the actual comparison
  97. *    tree       (in gutils) A binary tree.  Important because it is what
  98. *               gives the file comparison its speed as it makes it
  99. *               an "N log N" algorithm rather than "N squared"
  100. *    complist - a COMPLIST is the master data structure.  It has a DIRLIST
  101. *               of the left hand files, a DIRLIST of the right hand files
  102. *               and a LIST of COMPITEMs. The left and right hand DIRLISTs
  103. *               are working data used to produce the COMPLIST.  The LIST
  104. *               is displayed as the outline table.  Any given COMPITEM can
  105. *               be displayed as an expanded item.
  106. *    section  - a SECTION is a section of a file (first line, last line)
  107. *               and information as to what it matches in the other file.
  108. *    bar.c    - the picture down the left of the screen
  109. *               has a WNDPROC.  
  110. *    view     - Although the COMPLIST is the master state, it doesn't do
  111. *               all the work itself.  The data is actually displayed by
  112. *               the table class which is highly generalised.  View
  113. *               owns a COMPLIST (and therefore calls upon the functions
  114. *               in complist to fill it and interrogate it) and calls
  115. *               upon (and is called back by) the functions in table to
  116. *               actually display it.  Read about table in gutils.h
  117. *    table.c    (in gutils) a highly generalised system for displaying
  118. *               data in rows and columns.  The interface is in gutils.h.
  119. *    status.c   (in gutils) the status line at the top. See gutils.h
  120. *************************************************************************
  121. *
  122. * Overview of this file:
  123. *
  124. *   We create a table window (gutils.dll) to show the files and the
  125. *   results of their comparisons. We create a COMPLIST object representing
  126. *   a list of files and their differences, and a VIEW object to map between
  127. *   the rows of the table window and the COMPLIST.
  128. *
  129. *   This module is responsible for creating and managing the main window,
  130. *   placing the child windows (table, status window etc) within it, and
  131. *   handling all menu items. We maintain global option flags set by
  132. *   menu commands.
  133. *
  134. *   Creating a COMPLIST creates a list of unmatched files, and of matching
  135. *   files that are compared with each other (these are COMPITEMS).
  136. *   The VIEW provides a mapping between rows on the screen, and items in
  137. *   the COMPLIST.
  138. *
  139. *   This version tries to maintain a responsive user interface by
  140. *   creating worker threads to do long jobs.  This potentially creates
  141. *   conflicts between the threads as they will both want to update common
  142. *   variables (for instance the UI thread may be changing the options to
  143. *   exclude identical files while the worker thread is adding in the
  144. *   results of new comparisons).  Critical sections are used to manage
  145. *   the conflicts.
  146. *
  147. *   The Edit options invoke an editor on a separate thread.  This allows
  148. *   us to repaint our window and thereby allow the user to refer back to
  149. *   what he saw before invoking the editor.  When he's finished editing,
  150. *   we would of course like to refresh things and if this is still on the
  151. *   separate thread it might clash. We avoid this clash by POSTing ourselves
  152. *   a (WM_COMMAND, IDM_UPDATE) message.
  153. *
  154. ****************************************************************************/
  155.  
  156. #include <windows.h>
  157. #include <shellapi.h>
  158. #include <stdlib.h>
  159. #include <commdlg.h>
  160. #include <string.h>
  161.  
  162. #include "gutils.h"
  163. #include "table.h"
  164. #include "list.h"
  165. #include "scandir.h"            /* needed for file.h     */
  166. #include "file.h"               /* needed for compitem.h */
  167. #include "compitem.h"           /* needed for view.h     */
  168. #include "complist.h"
  169. #include "view.h"
  170. #include "state.h"
  171. #include "windiff.h"
  172. #include "wdiffrc.h"
  173.  
  174.  
  175. /*--constants and data types--------------------------------------------*/
  176.  
  177. int Version = 2;
  178. int SubVersion = 01;
  179.  
  180. /* When we print the current table, we pass this id as the table id
  181.  * When we are queried for the properties of this table, we know they
  182.  * want the printing properties for the current view. We use this to
  183.  * select different fonts and colours for the printer.
  184.  */
  185. #define TABID_PRINTER   1
  186.  
  187.  
  188.  
  189. /*
  190.  * structure containing args passed to worker thread in initial
  191.  * case (executing command line instructions). 
  192.  */
  193. typedef struct {
  194.  
  195.         LPSTR first;
  196.         LPSTR second;
  197.         LPSTR savelist;
  198.         UINT saveopts;
  199.         VIEW view;
  200.         BOOL fDeep;
  201. } THREADARGS, FAR * PTHREADARGS;
  202.  
  203.  
  204. /* Structure containing all the arguments we'd like to give to do_editfile
  205.    Need a structure because CreateThread only allows for one argument.
  206. */
  207. typedef struct {
  208.         VIEW view;
  209.         int option;
  210.         int selection;
  211. } EDITARGS, FAR * PEDITARGS;
  212.  
  213. /*---- colour scheme------------------------------- */
  214.  
  215. /* outline */
  216. DWORD rgb_outlinehi = RGB(255, 0, 0);   /* hilighted files in outline mode  */
  217.  
  218. /* expand view */
  219. DWORD rgb_leftfore =   RGB(  0,   0,   0);         /* foregrnd for left lines */
  220. DWORD rgb_leftback  =  RGB(255,   0,   0);         /* backgrnd for left lines */
  221. DWORD rgb_rightfore =  RGB(  0,   0,   0);         /* foregrnd for right lines*/
  222. DWORD rgb_rightback =  RGB(255, 255,   0);         /* backgrnd for right lines*/
  223.  
  224. /* moved lines */
  225. DWORD rgb_mleftfore =  RGB(  0,   0, 128);         /* foregrnd for moved-left */
  226. DWORD rgb_mleftback =  RGB(255,   0,   0);         /* backgrnd for moved-left */
  227. DWORD rgb_mrightfore = RGB(  0,   0, 255);         /* foregrnd for moved-right*/
  228. DWORD rgb_mrightback = RGB(255, 255,   0);         /* backgrnd for moved-right*/
  229.  
  230. /* bar window */
  231. DWORD rgb_barleft =    RGB(255,   0,   0);         /* bar sections in left only  */
  232. DWORD rgb_barright =   RGB(255, 255,   0);         /* bar sections in right only */
  233. DWORD rgb_barcurrent = RGB(  0,   0, 255);         /* current pos markers in bar */
  234.  
  235.  
  236. /* module static data -------------------------------------------------*/
  237.  
  238.  
  239. /* current value of window title */
  240. char AppTitle[256];
  241.  
  242.  
  243. HWND hwndClient;        /* main window */
  244. HWND hwndRCD;           /* table window */
  245. HWND hwndStatus;        /* status bar across top */
  246. HWND hwndBar;           /* graphic of sections as vertical bars */
  247.  
  248. HACCEL haccel;
  249.  
  250. /* The status bar told us it should be this high. Rest of client area
  251.  * goes to the hwndBar and hwndRCD.
  252.  */
  253. int status_height;
  254.  
  255. HINSTANCE hInst;   /* handle to current app instance */
  256. HMENU hMenu;    /* handle to menu for hwndClient */
  257.  
  258. int nMinMax = SW_SHOWNORMAL;         /* default state of window normal */
  259.  
  260. /* The message sent to us as a callback by the table window needs to be
  261.  * registered - table_msgcode is the result of the RegisterMessage call
  262.  */
  263. UINT table_msgcode;
  264.  
  265. /* True if we are currently doing some scan or comparison.
  266.  * Must get critical section before checking/changing this (call
  267.  * SetBusy.
  268.  */
  269. BOOL fBusy = FALSE;
  270.  
  271. int     selection       =       -1;     /* selected row in table*/
  272.  
  273. /* Options for DisplayMode field indicating what is currently shown.
  274.  * We use this to know whether or not to show the graphic bar window.
  275.  */
  276. #define MODE_NULL       0       /* nothing displayed */
  277. #define MODE_OUTLINE    1       /* a list of files displayed */
  278. #define MODE_EXPAND     2       /* view is expanded view of one file */
  279.  
  280. int DisplayMode = MODE_NULL;    /* indicates whether we are in expand mode */
  281.  
  282. VIEW current_view = NULL;
  283.  
  284. /* command line parameters */
  285. extern int __argc;
  286. extern char ** __argv;
  287.  
  288. BOOL bAbort = FALSE;    /* set to request abort of current operation */
  289.  
  290. char editor_cmdline[256] = "notepad %p";  /* editor cmdline */
  291.                           /* slick version is "s %p -#%l" */
  292.  
  293. /* app-wide global data --------------------------------------------- */
  294.  
  295. /* Handle returned from gmem_init - we use this for all memory allocations */
  296. HANDLE hHeap;
  297.  
  298. /* Current state of menu options */
  299. int line_numbers = IDM_LNRS;
  300. int expand_mode = IDM_BOTHFILES;
  301. int outline_include = INCLUDE_LEFTONLY|INCLUDE_RIGHTONLY|INCLUDE_SAME|INCLUDE_DIFFER;
  302. BOOL ignore_blanks = TRUE;
  303. BOOL picture_mode = TRUE;
  304.  
  305. /* function prototypes ---------------------------------------------*/
  306.  
  307. BOOL InitApplication(HINSTANCE hInstance);
  308. BOOL InitInstance(HINSTANCE hInstance, int nCmdShow);
  309. void CreateTools(void);
  310. void DeleteTools(void);
  311. long APIENTRY MainWndProc(HWND hWnd, UINT message, UINT wParam, LONG lParam);
  312. BOOL SetBusy(void);
  313. void SetNotBusy(void);
  314. void SetSelection(long rownr);
  315. void SetButtonText(LPSTR cmd);
  316. BOOL ToExpand(HWND hwnd);
  317. void ParseArgs(int argc, char ** argv);
  318.  
  319.  
  320. DWORD wd_initial(LPVOID arg);
  321.  
  322. static HANDLE ghThread = NULL;
  323.  
  324. static DWORD gdwMainThreadId;     /* threadid of main (user interface) thread
  325.                                      initialised in winmain(), thereafter constant.
  326.                                      See windiff_UI()
  327.                                   */
  328.  
  329. /***************************************************************************
  330.  * Function: windiff_UI
  331.  *
  332.  * Purpose:
  333.  *
  334.  * If you are about to put up a dialog box or in fact process input in any way
  335.  * on any thread other than the main thread - or if you MIGHT be on a thread other
  336.  * than the main thread, then you must call this function with TRUE before doing
  337.  * it and with FALSE immediately afterwards.  Otherwise you will get one of a
  338.  * number of flavours of not-very-responsiveness
  339.  */
  340. void windiff_UI(BOOL bAttach)
  341. {
  342.         DWORD dwThreadId = GetCurrentThreadId();
  343.         if (dwThreadId==gdwMainThreadId) return;
  344.  
  345.         if (bAttach) GetDesktopWindow();
  346.         AttachThreadInput(dwThreadId, gdwMainThreadId, bAttach);
  347. } /* windiff_UI */
  348.  
  349. /***************************************************************************
  350.  * Function: WinMain
  351.  *
  352.  * Purpose:
  353.  *
  354.  * Main entry point. Register window classes, create windows,
  355.  * parse command line arguments and then perform a message loop
  356.  */
  357. int WINAPI
  358. WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
  359. {
  360.  
  361.         MSG msg;
  362.  
  363.         gdwMainThreadId = GetCurrentThreadId();
  364.  
  365.         /* create any pens/brushes etc and read in profile defaults */
  366.         CreateTools();
  367.  
  368.         /* init window class unless other instances running */
  369.         if (!hPrevInstance)
  370.             if (!InitApplication(hInstance))
  371.                 return(FALSE);
  372.  
  373.  
  374.         /* init this instance - create all the windows */
  375.         if (!InitInstance(hInstance, nCmdShow))
  376.             return(FALSE);
  377.  
  378.         ParseArgs(__argc, __argv);
  379.  
  380.  
  381.         /* message loop */
  382.         while(GetMessage(&msg, NULL, 0, 0)) {
  383.                 if (!TranslateAccelerator(hwndClient, haccel, &msg)) {
  384.                     TranslateMessage(&msg);
  385.                     DispatchMessage(&msg);
  386.                 }
  387.         }
  388.  
  389.         return (msg.wParam);
  390.  
  391. }
  392.  
  393. /***************************************************************************
  394.  * Function: InitApplication
  395.  *
  396.  * Purpose:
  397.  *
  398.  * Register window class for the main window and the bar window.
  399.  */
  400. BOOL
  401. InitApplication(HINSTANCE hInstance)
  402. {
  403.         WNDCLASS    wc;
  404.         BOOL resp;
  405.  
  406.  
  407.         /* register the bar window class */
  408.         InitBarClass(hInstance);
  409.  
  410.         wc.style = 0;
  411.         wc.lpfnWndProc = MainWndProc;
  412.         wc.cbClsExtra = 0;
  413.         wc.cbWndExtra = 0;
  414.         wc.hInstance = hInstance;
  415.         wc.hIcon = LoadIcon(hInstance, "WinDiff");
  416.         wc.hCursor = LoadCursor(NULL, IDC_ARROW);
  417.         wc.hbrBackground = NULL;
  418.         wc.lpszClassName =  "WinDiffViewerClass";
  419.         wc.lpszMenuName = NULL;
  420.  
  421.         resp = RegisterClass(&wc);
  422.  
  423.         return(resp);
  424. }
  425.  
  426. /***************************************************************************
  427.  * Function: InitInstance
  428.  *
  429.  * Purpose:
  430.  *
  431.  * Create and show the windows
  432.  */
  433. BOOL
  434. InitInstance(HINSTANCE hInstance, int nCmdShow)
  435. {
  436.         RECT rect;
  437.         HANDLE hstatus;
  438.         int bar_width;
  439.         RECT childrc;
  440.  
  441.         hInst = hInstance;
  442.  
  443.         /* initialise a heap. we use this one heap throughout
  444.          * the app. for all memory requirements
  445.          */
  446.         hHeap = gmem_init();
  447.         /* initialise the list package */
  448.         List_Init();
  449.  
  450.  
  451.         hMenu = LoadMenu(hInstance, "WinDiffMenu");
  452.         haccel = LoadAccelerators(hInstance, "WinDiffAccel");
  453.  
  454.         /* create the main window */
  455.         hwndClient = CreateWindow("WinDiffViewerClass",
  456.                             "WinDiff",
  457.                             WS_OVERLAPPEDWINDOW,
  458.                             CW_USEDEFAULT,
  459.                             CW_USEDEFAULT,
  460.                             CW_USEDEFAULT,
  461.                             CW_USEDEFAULT,
  462.                             NULL,
  463.                             hMenu,
  464.                             hInstance,
  465.                             NULL
  466.                 );
  467.  
  468.  
  469.  
  470.         if (!hwndClient) {
  471.             return(FALSE);
  472.         }
  473.  
  474.         /* create 3 child windows, one status, one table and one bar
  475.          * Initially, the bar window is hidden and covered by the table.
  476.          */
  477.  
  478.         /* create a status bar window as
  479.          * a child of the main window.
  480.          */
  481.  
  482.         /* build a status struct for two labels and an abort button */
  483.         hstatus = StatusAlloc(3);
  484.         StatusAddItem(hstatus, 0, SF_STATIC, SF_LEFT|SF_VAR|SF_SZMIN, IDL_STATLAB, 14, NULL);
  485.         StatusAddItem(hstatus, 1, SF_BUTTON, SF_RIGHT|SF_RAISE, IDM_ABORT, 8,
  486.                 LoadRcString(IDS_EXIT));
  487.         StatusAddItem(hstatus, 2, SF_STATIC, SF_LOWER|SF_LEFT|SF_VAR,
  488.                         IDL_NAMES, 60, NULL);
  489.  
  490.         /* ask the status bar how high it should be for the controls
  491.          * we have chosen, and save this value for re-sizing.
  492.          */
  493.         status_height = StatusHeight(hstatus);
  494.  
  495.         /* create a window of this height */
  496.         GetClientRect(hwndClient, &rect);
  497.         childrc = rect;
  498.         childrc.bottom = status_height;
  499.         hwndStatus = StatusCreate(hInst, hwndClient, IDC_STATUS, &childrc,
  500.                         hstatus);
  501.  
  502.         /* layout constants are stated as percentages of the window width */
  503.         bar_width = (rect.right - rect.left) * BAR_WIN_WIDTH / 100;
  504.  
  505.         /* create the table class covering all the remaining part of
  506.          * the main window
  507.          */
  508.         hwndRCD = CreateWindow(TableClassName,
  509.                         NULL,
  510.                         WS_CHILD | WS_VISIBLE | WS_HSCROLL | WS_VSCROLL,
  511.                         0,
  512.                         status_height,
  513.                         (int)(rect.right - rect.left),
  514.                         (int)(rect.bottom - status_height),
  515.                         hwndClient,
  516.                         (HANDLE) IDC_RCDISP1,
  517.                         hInst,
  518.                         NULL);
  519.  
  520.         /* create a bar window as a child of the main window.
  521.          * this window remains hidden until we switch into MODE_EXPAND
  522.          */
  523.         hwndBar = CreateWindow("BarClass",
  524.                         NULL,
  525.                         WS_CHILD | WS_VISIBLE,
  526.                         0,
  527.                         status_height,
  528.                         bar_width,
  529.                         (int)(rect.bottom - status_height),
  530.                         hwndClient,
  531.                         (HANDLE) IDC_BAR,
  532.                         hInst,
  533.                         NULL);
  534.  
  535.         /* nMinMax indicates whether we are to be minimised on startup,
  536.          * on command line parameters
  537.          */
  538.         ShowWindow(hwndBar, SW_HIDE);
  539.  
  540.         if (GetProfileInt(APPNAME, "OutlineSaved", 0))
  541.         {
  542.                 WINDOWPLACEMENT wp;
  543.  
  544.                 /* restore the previous expanded size and position */
  545.                 wp.length                  = sizeof( WINDOWPLACEMENT );
  546.                 wp.flags                   = 0;
  547.                 wp.showCmd                 = GetProfileInt( APPNAME, "OutlineShowCmd",
  548.                                                             SW_SHOWNORMAL);
  549.                 wp.ptMaxPosition.x         = GetProfileInt( APPNAME, "OutlineMaxX",       0);
  550.                 wp.ptMaxPosition.y         = GetProfileInt( APPNAME, "OutlineMaxY",       0);
  551.                 wp.rcNormalPosition.left   = (int)GetProfileInt( APPNAME, "OutlineNormLeft",  (UINT)(-1));
  552.                 wp.rcNormalPosition.top    = (int)GetProfileInt( APPNAME, "OutlineNormTop",   (UINT)(-1));
  553.                 wp.rcNormalPosition.right  = (int)GetProfileInt( APPNAME, "OutlineNormRight", (UINT)(-1));
  554.                 wp.rcNormalPosition.bottom = (int)GetProfileInt( APPNAME, "OutlineNormBottom",(UINT)(-1));
  555.                 SetWindowPlacement(hwndClient,&wp);
  556.         }
  557.         else ShowWindow(hwndClient, nMinMax);
  558.  
  559.         UpdateWindow(hwndClient);
  560.  
  561.  
  562.         /* initialise busy flag and status line to show we are idle
  563.          * (ie not comparing or scanning)
  564.          */
  565.         SetNotBusy();
  566.  
  567.         return(TRUE);
  568.  
  569. } /* InitInstance */
  570.  
  571. /***************************************************************************
  572.  * Function: windiff_usage
  573.  *
  574.  * Purpose:
  575.  *
  576.  * Complain to command line users about poor syntax,
  577.  * will be replaced by proper help file.
  578.  */
  579. void
  580. windiff_usage(LPSTR msg)
  581. {
  582.         int retval;
  583.         TCHAR szBuf[MAX_PATH];
  584.  
  585.         if (msg==NULL)
  586.                 msg = LoadRcString(IDS_USAGE_STR);
  587.  
  588.         LoadString(hInst, IDS_WINDIFF_USAGE, szBuf, sizeof(szBuf));
  589.         retval = MessageBox(hwndClient,
  590.                 msg,
  591.                 szBuf, MB_ICONINFORMATION|MB_OKCANCEL);
  592.         if (retval == IDCANCEL) {
  593.                 exit(1);
  594.         }
  595. }
  596.  
  597. /***************************************************************************
  598.  * Function: ParseArgs
  599.  *
  600.  * Purpose:
  601.  *
  602.  * Parse command line arguments
  603.  *
  604.  * The user can give one or two paths. If only one, we assume the second
  605.  * is '.' for the current directory. If one of the two paths is a directory
  606.  * and the other a file, we compare a file of the same name in the two dirs.
  607.  *
  608.  * The command -s filename causes the outline list to be written to a file
  609.  * and then the program exits. -s{slrd} filename allows selection of which
  610.  * files are written out; by default, we assume -sld for files left and different.
  611.  *
  612.  * -T means tree.  Go deep.
  613.  *
  614.  * The default is Deep, -L overrides and implies shallow, -T overrides -L
  615.  */
  616. void
  617. ParseArgs(int argc, char ** argv)
  618. {
  619.         int i;
  620.         LPSTR chp;
  621.         PTHREADARGS ta;
  622.         DWORD threadid;
  623.  
  624.         /* thread args can't be on the stack since the stack will change
  625.          * before the thread completes execution
  626.          */
  627.         ta = (PTHREADARGS) gmem_get(hHeap, sizeof(THREADARGS));
  628.         ta->first = NULL;
  629.         ta->second = NULL;
  630.         ta->savelist = NULL;
  631.         ta->saveopts = 0;
  632.         ta->fDeep = FALSE;  /* No -T option seen yet */
  633.  
  634.         for (i = 1; i < argc; i++) {
  635.  
  636.                 /* is this an option ? */
  637.                 if ((argv[i][0] == '-') || (argv[i][0] == '/')) {
  638.                         switch(argv[i][1]) {
  639.  
  640.                         case 's':
  641.                         case 'S':
  642.                                 /* read letters for the save option: s,l,r,d */
  643.                                 for(chp = &argv[i][2]; *chp != '\0'; chp++) {
  644.                                         switch(*chp) {
  645.                                         case 's':
  646.                                         case 'S':
  647.                                                 ta->saveopts |= INCLUDE_SAME;
  648.                                                 break;
  649.                                         case 'l':
  650.                                         case 'L':
  651.                                                 ta->saveopts |= INCLUDE_LEFTONLY;
  652.                                                 break;
  653.                                         case 'r':
  654.                                         case 'R':
  655.                                                 ta->saveopts |= INCLUDE_RIGHTONLY;
  656.                                                 break;
  657.                                         case 'd':
  658.                                         case 'D':
  659.                                                 ta->saveopts |= INCLUDE_DIFFER;
  660.                                                 break;
  661.                                         default:
  662.                                                 windiff_usage(NULL);
  663.                                                 return;
  664.                                         }
  665.                                 }
  666.  
  667.                                 if (ta->saveopts == 0) {
  668.                                         /* default to left and differ */
  669.                                         ta->saveopts = (INCLUDE_LEFTONLY) | (INCLUDE_DIFFER);
  670.                                 }
  671.                                 ta->savelist = argv[++i];
  672.                                 break;
  673.                         case 't':
  674.                         case 'T':
  675.                                 ta->fDeep = TRUE;
  676.                                 break;
  677.                         default:
  678.                                 windiff_usage(NULL);
  679.                                 return;
  680.                         }
  681.                 } else {
  682.                         if (ta->first == NULL) {
  683.                                 ta->first = argv[i];
  684.                         } else {
  685.                                 ta->second = argv[i];
  686.                         }
  687.                 }
  688.         }
  689.  
  690.         /* set the correct depth */
  691.         if (ta->fDeep)
  692.                 ;                       /* explicitly set -- leave it alone */
  693.         else ta->fDeep = TRUE;          /* global default */
  694.  
  695.         /* any paths to scan ? */
  696.         if (ta->first == NULL) {
  697.                 return;
  698.         }
  699.  
  700.         if (ta->second == NULL) {
  701.                 ta->second = ".";
  702.         }
  703.  
  704.         SetBusy();
  705.  
  706.         /* minimise the window if -s flag given */
  707.         if (ta->savelist != NULL) {
  708.                 ShowWindow(hwndClient, SW_MINIMIZE);
  709.         }
  710.  
  711.         /* make an empty view */
  712.         current_view = view_new(hwndRCD);
  713.         DisplayMode = MODE_OUTLINE;
  714.  
  715.         ta->view = current_view;
  716.  
  717.         /* attempt to create a worker thread */
  718.  
  719.         ghThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)wd_initial, (LPVOID) ta,
  720.                         0, &threadid);
  721.         if (ghThread == NULL)
  722.         {
  723.                 wd_initial( (LPVOID) ta);
  724.         }
  725. } /* ParseArgs */
  726.  
  727.  
  728. /***************************************************************************
  729.  * Function: CreateTools
  730.  *
  731.  * Purpose:
  732.  *
  733.  * Create any pens/brushes, and read defaults
  734.  * from the profile file for menu settings etc.
  735.  */
  736. void
  737. CreateTools(void)
  738. {
  739.  
  740.         /* standard message that table class sends us for
  741.          * notifications and queries.
  742.          */
  743.         table_msgcode = RegisterWindowMessage(TableMessage);
  744.  
  745.         line_numbers = GetProfileInt(APPNAME, "LineNumbers", line_numbers);
  746.         outline_include = GetProfileInt(APPNAME, "FileInclude", outline_include);
  747.         ignore_blanks = GetProfileInt(APPNAME, "Blanks", ignore_blanks);
  748.         picture_mode = GetProfileInt(APPNAME, "Picture", picture_mode);
  749.  
  750.         GetProfileString(APPNAME, "Editor", editor_cmdline, (LPTSTR)editor_cmdline,
  751.                         sizeof(editor_cmdline));
  752.         InitializeCriticalSection(&CSWindiff);
  753.  
  754. }
  755.  
  756. /***************************************************************************
  757.  * Function: DeleteTools
  758.  *
  759.  * Purpose:
  760.  *
  761.  * Delete any pens or brushes that were created in CreateTools 
  762.  */
  763. void
  764. DeleteTools(void)
  765. {
  766.  
  767.         DeleteCriticalSection(&CSWindiff);
  768.  
  769. }
  770.  
  771.  
  772. /***************************************************************************
  773.  * Function:
  774.  *
  775.  * Purpose:
  776.  *
  777.  * Check whether we have had an abort request (IDM_ABORT), and
  778.  * return TRUE if abort requested, otherwise FALSE
  779.  */
  780. BOOL
  781. Poll(void)
  782. {
  783.     return(bAbort);
  784. }
  785.  
  786.  
  787. /***************************************************************************
  788.  * Function: DoResize
  789.  *
  790.  * Purpose:
  791.  *
  792.  * Position child windows on a resize of the main window
  793.  */
  794. void
  795. DoResize(HWND hWnd)
  796. {
  797.         RECT rc;
  798.         int bar_width;
  799.  
  800.         GetClientRect(hWnd, &rc);
  801.         MoveWindow(hwndStatus, 0, 0, rc.right - rc.left, status_height, TRUE);
  802.  
  803.         bar_width = (rc.right - rc.left) * BAR_WIN_WIDTH / 100;
  804.  
  805.         /* bar window is hidden unless in expand mode */
  806.         if ((DisplayMode == MODE_EXPAND) && (picture_mode)) {
  807.                 ShowWindow(hwndBar, SW_SHOW);
  808.                 MoveWindow(hwndBar, 0, status_height,
  809.                         bar_width, rc.bottom - status_height, TRUE);
  810.                 MoveWindow(hwndRCD, bar_width, status_height,
  811.                         (rc.right - rc.left) - bar_width,
  812.                         rc.bottom - status_height, TRUE);
  813.         } else {
  814.                 MoveWindow(hwndRCD, 0, status_height, (rc.right - rc.left),
  815.                         rc.bottom - status_height, TRUE);
  816.                 ShowWindow(hwndBar, SW_HIDE);
  817.         }
  818.  
  819. }
  820. /***************************************************************************
  821.  * Function: AboutBox
  822.  *
  823.  * Purpose:
  824.  *
  825.  * Standard processing for About box.
  826.  */
  827. int APIENTRY
  828. AboutBox(HWND hDlg, unsigned message, UINT wParam, LONG lParam)
  829. {
  830.         char ch[256];
  831.  
  832.         switch (message) {
  833.  
  834.         case WM_INITDIALOG:
  835.                 wsprintf((LPTSTR)ch, "%d.%02d", Version, SubVersion);
  836.                 SetDlgItemText(hDlg, IDD_VERSION, ch);
  837.                 return(TRUE);
  838.  
  839.         case WM_COMMAND:
  840.                 switch (GET_WM_COMMAND_ID(wParam, lParam)) {
  841.                 case IDOK:
  842.                         EndDialog(hDlg, 0);
  843.                         return(TRUE);
  844.                 }
  845.                 break;
  846.         }
  847.         return(FALSE);
  848. }
  849.  
  850.  
  851. /* -- menu commands ---------------------------------------------------*/
  852.  
  853. /***************************************************************************
  854.  * Function: DoPrint
  855.  *
  856.  * Purpose:
  857.  *
  858.  * Print the current view 
  859.  */
  860. void
  861. DoPrint(void)
  862. {
  863.         Title head, foot;
  864.         PrintContext context;
  865.         TCHAR szPage[50];
  866.  
  867.         /* print context contains the header and footer. Use the
  868.          * default margins and printer selection
  869.          */
  870.  
  871.         /* we set the table id to be TABID_PRINTER. When the table calls
  872.          * back to get text and properties, we use this to indicate
  873.          * that the table refered to is the 'current_view', but in print
  874.          * mode, and thus we will use different colours/fonts.
  875.          */
  876.         context.head = &head;
  877.         context.foot = &foot;
  878.         context.margin = NULL;
  879.         context.pd = NULL;
  880.         context.id = TABID_PRINTER;
  881.  
  882.         /* header is filenames or just WinDiff if no names known*/
  883.         if (strlen(AppTitle) > 0) {
  884.                 head.ptext = AppTitle;
  885.         } else {
  886.                 head.ptext = "WinDiff";
  887.         }
  888.  
  889.         /* header is centred, footer is right-aligned and
  890.          * consists of the page number
  891.          */
  892.         head.props.valid = P_ALIGN;
  893.         head.props.alignment = P_CENTRE;
  894.         lstrcpy(szPage,LoadRcString(IDS_PAGE));
  895.         foot.ptext = (LPSTR)szPage;
  896.         foot.props.valid = P_ALIGN;
  897.         foot.props.alignment = P_RIGHT;
  898.  
  899.         SendMessage(hwndRCD, TM_PRINT, 0, (DWORD) (LPSTR) &context);
  900. }
  901.  
  902. /***************************************************************************
  903.  * Function: FindNextChange
  904.  *
  905.  * Purpose:
  906.  *
  907.  * Find the next line in the current view that is
  908.  * not STATE_SAME. Start from the current selection, if valid, or
  909.  * from the top of the window if no selection.
  910.  *
  911.  */
  912. BOOL
  913. FindNextChange(void)
  914. {
  915.         long row;
  916.  
  917.         /* start from the selection or top of the window if no selection */
  918.         if (selection >= 0) {
  919.                 row = selection;
  920.         } else {
  921.                 row = (int) SendMessage(hwndRCD, TM_TOPROW, FALSE, 0);
  922.         }
  923.  
  924.  
  925.         /* find the next 'interesting' line */
  926.         row = view_findchange(current_view, row, TRUE);
  927.         if (row >= 0) {
  928.                 SetSelection(row);
  929.                 return(TRUE);
  930.         } else {
  931.                 windiff_UI(TRUE);
  932.                 MessageBox(hwndClient, LoadRcString(IDS_NO_MORE_CHANGES), "Windiff",
  933.                         MB_ICONINFORMATION|MB_OK);
  934.                 windiff_UI(FALSE);
  935.  
  936.                 return(FALSE);
  937.         }
  938. }
  939.  
  940. /***************************************************************************
  941.  * Function: FindPrevChange
  942.  *
  943.  * Purpose:
  944.  *
  945.  * Find the previous line in the current view that is not STATE_SAME
  946.  */
  947. BOOL
  948. FindPrevChange(void)
  949. {
  950.         long row;
  951.  
  952.         /* start from the selection or top of window if no selection */
  953.         if (selection >= 0) {
  954.                 row = selection;
  955.         } else {
  956.                 row = (int) SendMessage(hwndRCD, TM_TOPROW, FALSE, 0);
  957.         }
  958.  
  959.         /* find the previous 'interesting' line */
  960.         row = view_findchange(current_view, row, FALSE);
  961.         if (row >= 0) {
  962.                 SetSelection(row);
  963.                 return(TRUE);
  964.         } else {
  965.                 windiff_UI(TRUE);
  966.                 MessageBox(hwndClient, LoadRcString(IDS_NO_PREV_CHANGES), "Windiff",
  967.                         MB_ICONINFORMATION|MB_OK);
  968.                 windiff_UI(FALSE);
  969.  
  970.                 return(FALSE);
  971.         }
  972.  
  973. }
  974. /***************************************************************************
  975.  * Function: WriteProfileInt
  976.  *
  977.  */
  978.  
  979. BOOL WriteProfileInt(LPSTR AppName, LPSTR Key, int Int)
  980. {       char Str[40];
  981.  
  982.         wsprintf((LPTSTR)Str, "%d", Int);
  983.         return WriteProfileString(AppName, Key, Str);
  984.  
  985. } /* WriteProfileInt */
  986.  
  987.  
  988. /***************************************************************************
  989.  * Function: ToExpand
  990.  *
  991.  * Purpose:
  992.  *
  993.  * Switch to expand view of the selected line 
  994.  */
  995. BOOL
  996. ToExpand(HWND hwnd)
  997. {
  998.         if (selection < 0) {
  999.                 return(FALSE);
  1000.         }
  1001.  
  1002.         if (!view_isexpanded(current_view)) {
  1003.                 /* save the current outline size and position */
  1004.                 WINDOWPLACEMENT wp;
  1005.                 if (GetWindowPlacement(hwndClient,&wp)) {
  1006.                         WriteProfileInt(APPNAME, "OutlineShowCmd", wp.showCmd);
  1007.                         WriteProfileInt(APPNAME, "OutlineMaxX", wp.ptMaxPosition.x);
  1008.                         WriteProfileInt(APPNAME, "OutlineMaxY", wp.ptMaxPosition.y);
  1009.                         WriteProfileInt(APPNAME, "OutlineNormLeft", wp.rcNormalPosition.left);
  1010.                         WriteProfileInt(APPNAME, "OutlineNormTop", wp.rcNormalPosition.top);
  1011.                         WriteProfileInt(APPNAME, "OutlineNormRight", wp.rcNormalPosition.right);
  1012.                         WriteProfileInt(APPNAME, "OutlineNormBottom", wp.rcNormalPosition.bottom);
  1013.                         WriteProfileInt(APPNAME, "OutlineSaved", 1);
  1014.                 }
  1015.  
  1016.                 /* restore the previous expanded size and position, if any */
  1017.                 if (GetProfileInt(APPNAME, "ExpandedSaved", 0)) {
  1018.                         wp.flags                   = 0;
  1019.                         wp.showCmd
  1020.                                 = GetProfileInt( APPNAME, "ExpandShowCmd"
  1021.                                                , SW_SHOWMAXIMIZED);
  1022.                         wp.ptMaxPosition.x
  1023.                                 = GetProfileInt( APPNAME, "ExpandMaxX", 0);
  1024.                         wp.ptMaxPosition.y
  1025.                                 = GetProfileInt( APPNAME, "ExpandMaxY", 0);
  1026.                         wp.rcNormalPosition.left
  1027.                                 = GetProfileInt( APPNAME, "ExpandNormLeft"
  1028.                                                , wp.rcNormalPosition.left);
  1029.                         wp.rcNormalPosition.top
  1030.                                 = GetProfileInt( APPNAME, "ExpandNormTop"
  1031.                                                , wp.rcNormalPosition.top);
  1032.                         wp.rcNormalPosition.right
  1033.                                 = GetProfileInt( APPNAME, "ExpandNormRight"
  1034.                                                , wp.rcNormalPosition.right);
  1035.                         wp.rcNormalPosition.bottom
  1036.                                 = GetProfileInt( APPNAME, "ExpandNormBottom"
  1037.                                                , wp.rcNormalPosition.bottom);
  1038.                         SetWindowPlacement(hwndClient,&wp);
  1039.                 }
  1040.                 else ShowWindow(hwndClient, SW_SHOWMAXIMIZED);
  1041.         }
  1042.  
  1043.         /*change the view mapping to expand mode */
  1044.         if (view_expand(current_view, selection)) {
  1045.  
  1046.                 /* ok - we now have an expanded view - change status
  1047.                  * to show this
  1048.                  */
  1049.  
  1050.                 DisplayMode = MODE_EXPAND;
  1051.  
  1052.                 /* resize to show the graphic bar picture */
  1053.                 DoResize(hwndClient);
  1054.  
  1055.  
  1056.                 /* change button,status text-if we are not still busy*/
  1057.                 if (!fBusy) {
  1058.                         TCHAR szBuf[10];
  1059.                         lstrcpy(szBuf,LoadRcString(IDS_OUTLINE));
  1060.                         /* the status field when we are expanded shows the
  1061.                          * tag field (normally the file name) for the
  1062.                          * item we are expanding
  1063.                          */
  1064.                         SetStatus(view_getcurrenttag(current_view) );
  1065.                         SetButtonText(szBuf);
  1066.                 }
  1067.  
  1068.                 return(TRUE);
  1069.         }
  1070.         return(FALSE);
  1071. } /* ToExpand */
  1072.  
  1073. /***************************************************************************
  1074.  * Function: ToOutline
  1075.  *
  1076.  * Purpose:
  1077.  *
  1078.  * Switch back to outline view - showing just the list of file names.
  1079.  */
  1080. void
  1081. ToOutline(HWND hwnd)
  1082. {
  1083.         if (view_isexpanded(current_view)) {
  1084.                 /* save the current expanded size and position */
  1085.                 WINDOWPLACEMENT wp;
  1086.                 if (GetWindowPlacement(hwndClient,&wp)) {
  1087.                         WriteProfileInt(APPNAME, "ExpandShowCmd", wp.showCmd);
  1088.                         WriteProfileInt(APPNAME, "ExpandMaxX", wp.ptMaxPosition.x);
  1089.                         WriteProfileInt(APPNAME, "ExpandMaxY", wp.ptMaxPosition.y);
  1090.                         WriteProfileInt(APPNAME, "ExpandNormLeft", wp.rcNormalPosition.left);
  1091.                         WriteProfileInt(APPNAME, "ExpandNormTop", wp.rcNormalPosition.top);
  1092.                         WriteProfileInt(APPNAME, "ExpandNormRight", wp.rcNormalPosition.right);
  1093.                         WriteProfileInt(APPNAME, "ExpandNormBottom", wp.rcNormalPosition.bottom);
  1094.                         WriteProfileInt(APPNAME, "ExpandedSaved", 1);
  1095.                 }
  1096.  
  1097.                 /* restore the previous expanded size and position, if any */
  1098.                 if (GetProfileInt(APPNAME, "OutlineSaved", 0))  {
  1099.                         wp.flags = 0;
  1100.                         wp.showCmd
  1101.                                 = GetProfileInt( APPNAME, "OutlineShowCmd"
  1102.                                                , SW_SHOWNORMAL);
  1103.                         wp.ptMaxPosition.x
  1104.                                 = GetProfileInt( APPNAME, "OutlineMaxX", 0);
  1105.                         wp.ptMaxPosition.y
  1106.                                 = GetProfileInt( APPNAME, "OutlineMaxY", 0);
  1107.                         wp.rcNormalPosition.left
  1108.                                 = GetProfileInt( APPNAME, "OutlineNormLeft"
  1109.                                                , wp.rcNormalPosition.left);
  1110.                         wp.rcNormalPosition.top
  1111.                                 = GetProfileInt( APPNAME, "OutlineNormTop"
  1112.                                                , wp.rcNormalPosition.top);
  1113.                         wp.rcNormalPosition.right
  1114.                                 = GetProfileInt( APPNAME, "OutlineNormRight"
  1115.                                                , wp.rcNormalPosition.right);
  1116.                         wp.rcNormalPosition.bottom
  1117.                                 = GetProfileInt( APPNAME, "OutlineNormBottom"
  1118.                                                , wp.rcNormalPosition.bottom);
  1119.                         SetWindowPlacement(hwndClient,&wp);
  1120.                 }
  1121.                 ShowWindow(hwndClient, SW_SHOWNORMAL);
  1122.         }
  1123.  
  1124.         DisplayMode = MODE_OUTLINE;
  1125.  
  1126.         /* switch mapping back to outline view */
  1127.         view_outline(current_view);
  1128.  
  1129.         /* hide bar window and resize to cover */
  1130.         DoResize(hwndClient);
  1131.  
  1132.  
  1133.         /* change label on button */
  1134.         if (!fBusy) {
  1135.                 TCHAR szBuf[8];
  1136.                 lstrcpy(szBuf,LoadRcString(IDS_EXPAND));
  1137.                 SetButtonText(szBuf);
  1138.                 SetStatus(NULL);
  1139.         }
  1140. } /* ToOutline */
  1141.  
  1142. /***************************************************************************
  1143.  * Function: ToMoved
  1144.  *
  1145.  * Purpose:
  1146.  *
  1147.  * If the user clicks on a MOVED line in expand mode, we jump to the
  1148.  * other line. We return TRUE if this was possible,  or FALSE otherwise.
  1149.  */
  1150. BOOL
  1151. ToMoved(HWND hwnd)
  1152. {
  1153.         BOOL bIsLeft;
  1154.         int linenr, state;
  1155.         long i, total;
  1156.  
  1157.         if (DisplayMode != MODE_EXPAND) {
  1158.                 return(FALSE);
  1159.         }
  1160.         if (selection < 0) {
  1161.                 return(FALSE);
  1162.         }
  1163.  
  1164.         state = view_getstate(current_view, selection);
  1165.         if (state == STATE_MOVEDLEFT) {
  1166.                 bIsLeft = TRUE;
  1167.                 /* get the linenr of the other copy */
  1168.                 linenr = abs(view_getlinenr_right(current_view, selection));
  1169.         } else if (state == STATE_MOVEDRIGHT) {
  1170.                 bIsLeft = FALSE;
  1171.                 /* get the linenr of the other copy */
  1172.                 linenr = abs(view_getlinenr_left(current_view, selection));
  1173.         } else {
  1174.                 /* not a moved line - so we can't find another copy */
  1175.                 return(FALSE);
  1176.         }
  1177.  
  1178.         /* search the view for this line nr */
  1179.         total = view_getrowcount(current_view);
  1180.         for (i = 0; i < total; i++) {
  1181.                 if (bIsLeft) {
  1182.                         if (linenr == view_getlinenr_right(current_view, i)) {
  1183.                                 /* found it */
  1184.                                 SetSelection(i);
  1185.                                 return(TRUE);
  1186.                         }
  1187.                 } else {
  1188.                         if (linenr == view_getlinenr_left(current_view, i)) {
  1189.                                 SetSelection(i);
  1190.                                 return(TRUE);
  1191.                         }
  1192.                 }
  1193.         }
  1194.         return(FALSE);
  1195. }
  1196.  
  1197. /***************************************************************************
  1198.  * Function: do_editfile
  1199.  *
  1200.  * Purpose:
  1201.  *
  1202.  * Launch an editor on the current file (the file we are expanding, or
  1203.  * in outline mode the selected row. Option allows selection of the
  1204.  * left file, the right file or the composite view of this item.
  1205.  * pe points to a packet of parameters that must be freed before returning.
  1206.  * The return value is meaningless (just to conform to CreateThread).
  1207.  */
  1208. LONG
  1209. do_editfile(PEDITARGS pe)
  1210. {
  1211.         VIEW view = pe->view;
  1212.         int option = pe->option;
  1213.         int selection = pe->selection;
  1214.  
  1215.         COMPITEM item;
  1216.         LPSTR fname;
  1217.         char cmdline[256];
  1218.         int currentline;
  1219.         char * pOut = cmdline;
  1220.         char * pIn = editor_cmdline;
  1221.  
  1222.         STARTUPINFO si;
  1223.         PROCESS_INFORMATION pi;
  1224.  
  1225.         item = view_getitem(view, selection);
  1226.         if (item == NULL) {
  1227.                 return -1;
  1228.         }
  1229.  
  1230.         fname = compitem_getfilename(item, option);
  1231.  
  1232.         if ( 0 == fname )
  1233.         {
  1234.             windiff_UI(TRUE);
  1235.             MessageBox(hwndClient, LoadRcString(IDS_FILE_DOESNT_EXIST),
  1236.                        "Windiff", MB_ICONSTOP|MB_OK);
  1237.             windiff_UI(FALSE);
  1238.             goto error;
  1239.         }
  1240.  
  1241.        switch ( option )
  1242.         {
  1243.         case CI_LEFT:
  1244.             currentline = view_getlinenr_left( view,
  1245.                                                selection > 0 ? selection : 1);
  1246.             break;
  1247.  
  1248.         case CI_RIGHT:
  1249.             currentline = view_getlinenr_right( view,
  1250.                                                 selection > 0 ? selection : 1);
  1251.             break;
  1252.  
  1253.         default:
  1254.             currentline = 1;
  1255.             break;
  1256.         }
  1257.  
  1258.         while( *pIn )
  1259.         {
  1260.             switch( *pIn )
  1261.             {
  1262.             case '%':
  1263.                 pIn++;
  1264.                 switch ( *pIn )
  1265.                 {
  1266.                 case 'p':
  1267.                     lstrcpy( (LPTSTR)pOut, fname );
  1268.                     while ( *pOut )
  1269.                         pOut++;
  1270.                     break;
  1271.  
  1272.                 case 'l':
  1273.                     _ltoa( currentline, pOut, 10 );
  1274.                     while ( *pOut )
  1275.                         pOut++;
  1276.                     break;
  1277.  
  1278.                 default:
  1279.                     if (IsDBCSLeadByte(*pIn) && *(pIn+1)) {
  1280.                         *pOut++ = *pIn++;
  1281.                     }
  1282.                     *pOut++ = *pIn;
  1283.                     break;
  1284.                 }
  1285.                 pIn++;
  1286.                 break;
  1287.  
  1288.             default:
  1289.                 if (IsDBCSLeadByte(*pIn) && *(pIn+1)) {
  1290.                     *pOut++ = *pIn++;
  1291.                 }
  1292.                 *pOut++ = *pIn++;
  1293.                 break;
  1294.             }
  1295.         }
  1296.  
  1297.  
  1298.         /* Launch the process and waits for it to complete */
  1299.  
  1300.         si.cb = sizeof(STARTUPINFO);
  1301.         si.lpReserved = NULL;
  1302.         si.lpReserved2 = NULL;
  1303.         si.cbReserved2 = 0;
  1304.         si.lpTitle = (LPSTR)cmdline; 
  1305.         si.lpDesktop = (LPTSTR)NULL;
  1306.         si.dwFlags = STARTF_FORCEONFEEDBACK;
  1307.  
  1308.  
  1309.         if (!CreateProcess(NULL,
  1310.                         cmdline,
  1311.                         NULL,
  1312.                         NULL,
  1313.                         FALSE,
  1314.                         NORMAL_PRIORITY_CLASS,
  1315.                         NULL,
  1316.                         (LPTSTR)NULL,
  1317.                         &si,
  1318.                         &pi)) {
  1319.                 windiff_UI(TRUE);
  1320.                 MessageBox(hwndClient, LoadRcString(IDS_FAILED_TO_LAUNCH_EDT),
  1321.                         "Windiff", MB_ICONSTOP|MB_OK);
  1322.                 windiff_UI(FALSE);
  1323.                 goto error;
  1324.         }
  1325.  
  1326.         /* wait for completion. */
  1327.         WaitForSingleObject(pi.hProcess, INFINITE);
  1328.  
  1329.         /* close process and thread handles */
  1330.         CloseHandle(pi.hThread);
  1331.         CloseHandle(pi.hProcess);
  1332.  
  1333.         /* finished with the filename. deletes it if it was a temp. */
  1334.         compitem_freefilename(item, option, fname);
  1335.  
  1336.         /*
  1337.          * refresh cached view always .  A common trick is to edit the
  1338.          * composite file and then save it as a new left or right file.
  1339.          * Equally the user can edit the left and save as a new right.
  1340.          */
  1341.  
  1342.         /* We want to force both files to be re-read, but it's not a terribly
  1343.          * good idea to throw the lines away on this thread.  Someone might
  1344.          * be reading them on another thread!
  1345.          */
  1346.         /* file_discardlines(compitem_getleftfile(item)) */
  1347.         /* file_discardlines(compitem_getrightfile(item)) */
  1348.  
  1349.         /* force the compare to be re-done */
  1350.         PostMessage(hwndClient, WM_COMMAND, IDM_UPDATE, (LONG)item);
  1351. error:
  1352.         gmem_free(hHeap, (LPSTR) pe, sizeof(EDITARGS));
  1353.  
  1354.         return 0;
  1355.  
  1356. } /* do_editfile */
  1357.  
  1358.  
  1359. /***************************************************************************
  1360.  * Function: do_editthread
  1361.  *
  1362.  * Purpose:
  1363.  *
  1364.  * Launch an editor on a separate thread.  It will actually get a separate
  1365.  * process, but we want our own thread in this process.  This thread will
  1366.  * wait until it's finished and then order up a refresh of the UI.
  1367.  * Need to give it its parameters as a gmem allocated packet because
  1368.  * it IS on a separate thread.
  1369.  */
  1370. void do_editthread(VIEW view, int option)
  1371. {
  1372.         PEDITARGS pe;
  1373.         HANDLE thread;
  1374.         DWORD threadid;
  1375.  
  1376.         pe = (PEDITARGS) gmem_get(hHeap, sizeof(EDITARGS));
  1377.         pe->view = view;
  1378.         pe->option = option;
  1379.         pe->selection = selection;
  1380.  
  1381.         thread = CreateThread( NULL
  1382.                              , 0
  1383.                              , (LPTHREAD_START_ROUTINE)do_editfile
  1384.                              , (LPVOID) pe
  1385.                              , 0
  1386.                              , &threadid
  1387.                              );
  1388.         if (thread == NULL)
  1389.         {
  1390.                 /* The createthread failed, do without the extra thread - just
  1391.                  * call the function synchronously
  1392.                  */
  1393.                  do_editfile(pe);
  1394.         }
  1395.         else CloseHandle(thread);
  1396. } /* do_editthread */
  1397.  
  1398.  
  1399. /* status bar and busy flags --------------------------------------------*/
  1400.  
  1401.  
  1402. /***************************************************************************
  1403.  * Function: SetButtonText
  1404.  *
  1405.  * Purpose:
  1406.  *
  1407.  * Set the Text on the statusbar button to reflect the current state 
  1408.  */
  1409. void
  1410. SetButtonText(LPSTR cmd)
  1411. {
  1412.         SendMessage(hwndStatus, SM_SETTEXT, IDM_ABORT, (DWORD) cmd);
  1413. }
  1414.  
  1415. /***************************************************************************
  1416.  * Function: SetStatus
  1417.  *
  1418.  * Purpose:
  1419.  *
  1420.  * Set the status field (left-hand part) of the status bar. 
  1421.  */
  1422. void
  1423. SetStatus(LPSTR cmd)
  1424. {
  1425.         SendMessage(hwndStatus, SM_SETTEXT, IDL_STATLAB, (DWORD) cmd);
  1426. }
  1427.  
  1428.  
  1429. /***************************************************************************
  1430.  * Function: SetNames
  1431.  *
  1432.  * Purpose:
  1433.  *
  1434.  * Set the names field - the central box in the status bar 
  1435.  */
  1436. void
  1437. SetNames(LPSTR names)
  1438. {
  1439.         SendMessage(hwndStatus, SM_SETTEXT, IDL_NAMES, (DWORD) names);
  1440.         if (names == NULL) {
  1441.                 AppTitle[0] = '\0';
  1442.         } else {
  1443.                 strncpy(AppTitle, names, sizeof(AppTitle));
  1444.         }
  1445. }
  1446.  
  1447. /***************************************************************************
  1448.  * Function: SetBusy
  1449.  *
  1450.  * Purpose:
  1451.  *
  1452.  * If we are not already busy, set the busy flag.
  1453.  *
  1454.  * Enter critical section first.
  1455.  */
  1456. BOOL
  1457. SetBusy(void)
  1458. {
  1459.         HMENU hmenu;
  1460.  
  1461.  
  1462.         WDEnter();
  1463.  
  1464.         if (fBusy) {
  1465.                 WDLeave();
  1466.                 return(FALSE);
  1467.         }
  1468.  
  1469.  
  1470.         fBusy = TRUE;
  1471.  
  1472.         SetStatus(LoadRcString(IDS_COMPARING));
  1473.         /* status also on window text, so that you can see even from
  1474.          * the icon when the scan has finished
  1475.          */
  1476.         SetWindowText(hwndClient, LoadRcString(IDS_SCANNING));
  1477.  
  1478.         /* disable appropriate parts of menu */
  1479.         hmenu = GetMenu(hwndClient);
  1480.         EnableMenuItem(hmenu, IDM_FILE,MF_DISABLED|MF_GRAYED|MF_BYCOMMAND);
  1481.         EnableMenuItem(hmenu, IDM_DIR,MF_DISABLED|MF_GRAYED|MF_BYCOMMAND);
  1482.         EnableMenuItem(hmenu, IDM_PRINT,MF_DISABLED|MF_GRAYED|MF_BYCOMMAND);
  1483.  
  1484.         /* enable abort only when busy */
  1485.         EnableMenuItem(hmenu, IDM_ABORT,MF_ENABLED|MF_BYCOMMAND);
  1486.         SetButtonText(LoadRcString(IDS_ABORT));  /* leave DisplayMode unchanged */
  1487.  
  1488.         WDLeave();
  1489.         return(TRUE);
  1490. } /* SetBusy */
  1491. /***************************************************************************
  1492.  * Function: SetNotBusy
  1493.  *
  1494.  * Purpose:
  1495.  *
  1496.  * This function can be called from the worker thread.
  1497.  * Thus we must not cause any SendMessage calls to windows
  1498.  * owned by the main thread while holding the CritSec or we
  1499.  * could cause deadlock.
  1500.  *
  1501.  * The critsec is only needed to protect the fBusy flag - so
  1502.  * clear the busy flag last, and only get the crit sec as needed.
  1503.  */
  1504. void
  1505. SetNotBusy(void)
  1506. {
  1507.         HMENU hmenu;
  1508.  
  1509.         /* reset button and status bar (clearing out busy flags) */
  1510.         if (current_view == NULL) {
  1511.                 SetButtonText(LoadRcString(IDS_EXIT));
  1512.                 SetStatus(NULL);
  1513.                 DisplayMode = MODE_NULL;
  1514.         } else if (view_isexpanded(current_view)) {
  1515.                 TCHAR szBuf[10];
  1516.                 lstrcpy(szBuf,LoadRcString(IDS_OUTLINE));
  1517.                 SetButtonText(szBuf);
  1518.                 SetStatus(view_getcurrenttag(current_view) );
  1519.                 DisplayMode = MODE_EXPAND;
  1520.         } else {
  1521.                 TCHAR szBuf[8];
  1522.                 lstrcpy(szBuf,LoadRcString(IDS_EXPAND));
  1523.                 SetButtonText(szBuf);
  1524.                 SetStatus(NULL);
  1525.                 DisplayMode = MODE_OUTLINE;
  1526.         }
  1527.  
  1528.         SetWindowText(hwndClient, "WinDiff");
  1529.  
  1530.         /* re-enable appropriate parts of menu */
  1531.         hmenu = GetMenu(hwndClient);
  1532.         EnableMenuItem(hmenu, IDM_FILE,MF_ENABLED|MF_BYCOMMAND);
  1533.         EnableMenuItem(hmenu, IDM_DIR,MF_ENABLED|MF_BYCOMMAND);
  1534.         EnableMenuItem(hmenu, IDM_PRINT,MF_ENABLED|MF_BYCOMMAND);
  1535.  
  1536.         /* disable abort now no longer busy */
  1537.         EnableMenuItem(hmenu, IDM_ABORT,MF_DISABLED|MF_GRAYED|MF_BYCOMMAND);
  1538.  
  1539.  
  1540.         /* clear the busy flag, protected by critical section */
  1541.         WDEnter();
  1542.  
  1543.         fBusy = FALSE;
  1544.         bAbort = FALSE;
  1545.  
  1546.         if (ghThread!=NULL){
  1547.             CloseHandle(ghThread);
  1548.             ghThread = NULL;
  1549.         }
  1550.         WDLeave();
  1551. } /* SetNotBusy */
  1552.  
  1553. /***************************************************************************
  1554.  * Function: IsBusy
  1555.  *
  1556.  * Purpose:
  1557.  *
  1558.  * Checks whether or not crit sec is open
  1559.  */
  1560. BOOL
  1561. IsBusy()
  1562. {
  1563.         BOOL bOK;
  1564.  
  1565.         WDEnter();
  1566.         bOK = fBusy;
  1567.         WDLeave();
  1568.         return(bOK);
  1569. } /* IsBusy */
  1570.  
  1571. /***************************************************************************
  1572.  * Function: BusyError
  1573.  *
  1574.  * Purpose:
  1575.  *
  1576.  * Puts up message box that system is busy.
  1577.  */
  1578. void
  1579. BusyError(void)
  1580. {
  1581.         windiff_UI(TRUE);
  1582.         MessageBox(hwndClient,
  1583.                 LoadRcString(IDS_PLEASE_WAIT),
  1584.                 "WinDiff", MB_OK|MB_ICONSTOP);
  1585.         windiff_UI(FALSE);
  1586. } /* BusyError */
  1587.  
  1588. /* --- colour scheme --------------------------------------------------- */
  1589.  
  1590. /***************************************************************************
  1591.  * Function: StateToColour
  1592.  *
  1593.  * Purpose:
  1594.  *
  1595.  * Map the state given into a foreground and a background colour
  1596.  * for states that are highlighted. Return P_FCOLOUR if the foreground
  1597.  * colour (put in *foreground) is to be used, return P_FCOLOUR|P_BCOLOUR if
  1598.  * both *foreground and *background are to be used, or 0 if the default
  1599.  * colours are to be used.
  1600.  */
  1601. UINT
  1602. StateToColour(int state, int col, DWORD FAR * foreground, DWORD FAR * background)
  1603. {
  1604.  
  1605.  
  1606.         switch (state) {
  1607.  
  1608.         case STATE_DIFFER:
  1609.                 /* files that differ are picked out in a foreground highlight,
  1610.                  * with the default background
  1611.                  */
  1612.                 *foreground = rgb_outlinehi;
  1613.                 return(P_FCOLOUR);
  1614.  
  1615.         case STATE_LEFTONLY:
  1616.                 /* lines only in the left file */
  1617.                 *foreground = rgb_leftfore;
  1618.                 *background = rgb_leftback;
  1619.                 return(P_FCOLOUR|P_BCOLOUR);
  1620.  
  1621.         case STATE_RIGHTONLY:
  1622.                 /* lines only in the right file */
  1623.                 *foreground = rgb_rightfore;
  1624.                 *background = rgb_rightback;
  1625.                 return(P_FCOLOUR|P_BCOLOUR);
  1626.  
  1627.         case STATE_MOVEDLEFT:
  1628.                 /* displaced lines in both files - left file version */
  1629.                 *foreground = rgb_mleftfore;
  1630.                 *background = rgb_mleftback;
  1631.                 return(P_FCOLOUR|P_BCOLOUR);
  1632.  
  1633.         case STATE_MOVEDRIGHT:
  1634.                 /* displaced lines in both files - right file version */
  1635.                 *foreground = rgb_mrightfore;
  1636.                 *background = rgb_mrightback;
  1637.                 return(P_FCOLOUR|P_BCOLOUR);
  1638.  
  1639.         default:
  1640.  
  1641.                 /* no highlighting - default colours */
  1642.                 return(0);
  1643.         }
  1644.  
  1645. }
  1646.  
  1647. /* table window communication routines ---------------------------------*/
  1648.  
  1649. /***************************************************************************
  1650.  * Function: SetSelection
  1651.  *
  1652.  * Purpose:
  1653.  *
  1654.  * Set a given row as the selected row in the table window 
  1655.  */
  1656. void
  1657. SetSelection(long rownr)
  1658. {
  1659.         TableSelection select;
  1660.  
  1661.         select.startrow = rownr;
  1662.         select.startcell = 0;
  1663.         select.nrows = 1;
  1664.         select.ncells = 1;
  1665.         SendMessage(hwndRCD, TM_SELECT, 0, (long) (LPSTR)&select);
  1666. }
  1667.  
  1668.  
  1669. /***************************************************************************
  1670.  * Function: do_gethdr
  1671.  *
  1672.  * Purpose:
  1673.  *
  1674.  * Handle table class call back to get nr of rows and columns,
  1675.  * and properties for the whole table.
  1676.  * The 'table id' is either TABID_PRINTER - meaning we are
  1677.  * printing the current_view, or it is the view to
  1678.  * use for row/column nr information
  1679.  */
  1680. long
  1681. do_gethdr(HWND hwnd, lpTableHdr phdr)
  1682. {
  1683.         VIEW view;
  1684.         BOOL bIsPrinter = FALSE;
  1685.  
  1686.         if (phdr->id == TABID_PRINTER) {
  1687.                 view = current_view;
  1688.                 bIsPrinter = TRUE;
  1689.         } else {
  1690.                 view = (VIEW) phdr->id;
  1691.         }
  1692.         if (view == NULL) {
  1693.                 return(FALSE);
  1694.         }
  1695.  
  1696.         phdr->nrows = view_getrowcount(view);
  1697.  
  1698.         /*  three columns: line nr, tag and rest of line */
  1699.  
  1700.         /*
  1701.          * if IDM_NONRS (no line numbers) is selected, suppress the
  1702.          * line-nr column entirely to save screen space
  1703.          */
  1704.         if (line_numbers == IDM_NONRS) {
  1705.                 phdr->ncols = 2;
  1706.                 phdr->fixedcols = 0;
  1707.         } else {
  1708.                 phdr->ncols = 3;
  1709.                 phdr->fixedcols = 1;
  1710.         }
  1711.  
  1712.         phdr->fixedrows = 0;
  1713.         phdr->fixedselectable = FALSE;
  1714.         phdr->hseparator = TRUE;
  1715.         phdr->vseparator = TRUE;
  1716.  
  1717.         phdr->selectmode = TM_ROW | TM_SINGLE;
  1718.         /*
  1719.          * find if we are in expand mode - ask for the item we are expanding.
  1720.          */
  1721.         if (view_isexpanded(view) == TRUE) {
  1722.  
  1723.                 /* use focus rect as selection mode in expand mode
  1724.                  * so as not to interfere with background colours.
  1725.                  */
  1726.                 phdr->selectmode |= TM_FOCUS;
  1727.         } else {
  1728.                 /* use default solid inversion when possible as it is clearer.*/
  1729.                 phdr->selectmode |= TM_SOLID;
  1730.         }
  1731.  
  1732.         /* please send TQ_SCROLL notifications when the table is scrolled */
  1733.         phdr->sendscroll = TRUE;
  1734.         phdr->props.valid = 0;
  1735.  
  1736.         return TRUE;
  1737. }
  1738.  
  1739. /***************************************************************************
  1740.  * Function: do_getprops
  1741.  *
  1742.  * Purpose:
  1743.  *
  1744.  * Respond to table callback asking for the size and properties
  1745.  * of each column. Table id is either TABID_PRINTER (meaning the
  1746.  * current_view, for printing) or it is the view to be used.
  1747.  */
  1748. long
  1749. do_getprops(HWND hwnd, lpColPropsList propslist)
  1750. {
  1751.         int i, cell;
  1752.         BOOL bIsPrinter = FALSE;
  1753.         VIEW view;
  1754.  
  1755.         if (propslist->id == TABID_PRINTER) {
  1756.                 view = current_view;
  1757.                 bIsPrinter = TRUE;
  1758.         } else {
  1759.                 view = (VIEW) propslist->id;
  1760.         }
  1761.         if (view == NULL) {
  1762.                 return(FALSE);
  1763.         }
  1764.  
  1765.         /* The table inteface is slightly confused here. we are not
  1766.          * guaranteed which columns we are being asked about, so instead
  1767.          * of just setting each column cols[0], cols[1] etc, we need
  1768.          * to loop through, looking at each column in the table and
  1769.          * seeing which it is.
  1770.          */
  1771.         for (i = 0; i < propslist->ncols; i++) {
  1772.                 cell = i + propslist->startcol;
  1773.                 propslist->plist[i].props.valid = 0;
  1774.  
  1775.                 /* for all column widths, add on 1 for the NULL char. */
  1776.  
  1777.                 /*
  1778.                  * skip the line nr column if IDM_NONRS
  1779.                  */
  1780.                 if (line_numbers == IDM_NONRS) {
  1781.                         cell++;
  1782.                 }
  1783.  
  1784.                 if (cell == 0) {
  1785.                         /* properties for line nr column */
  1786.  
  1787.                         propslist->plist[i].nchars = view_getwidth(view, 0)+1;
  1788.                         propslist->plist[i].props.valid |= P_ALIGN;
  1789.                         propslist->plist[i].props.alignment = P_CENTRE;
  1790.                 } else if (cell == 1) {
  1791.  
  1792.                         /* properties for tag field */
  1793.                         propslist->plist[i].nchars = view_getwidth(view, 1)+1;
  1794.                         propslist->plist[i].props.valid |= P_ALIGN;
  1795.                         propslist->plist[i].props.alignment = P_LEFT;
  1796.                 } else {
  1797.                         /* properties for main text column -
  1798.                          * use a fixed font unless printing (if
  1799.                          * printing, best to use the default font, because
  1800.                          * of resolution differences.
  1801.                          * add on 8 chars to the width to ensure that
  1802.                          * the width of lines beginning with tabs
  1803.                          * works out ok
  1804.                          */
  1805.                         propslist->plist[i].nchars = view_getwidth(view, 2)+1;
  1806.                         propslist->plist[i].props.valid |= P_ALIGN;
  1807.                         propslist->plist[i].props.alignment = P_LEFT;
  1808.                         if (!bIsPrinter) {
  1809.                                 propslist->plist[i].props.valid |= P_FONT;
  1810.                                 propslist->plist[i].props.hFont =
  1811.                                         GetStockObject(SYSTEM_FIXED_FONT);
  1812.                         }
  1813.                 }
  1814.         }
  1815.         return (TRUE);
  1816. }
  1817.  
  1818. /***************************************************************************
  1819.  * Function: do_getdata
  1820.  *
  1821.  * Purpose:
  1822.  *
  1823.  * Respond to a table callback asking for the contents of individual cells.
  1824.  * table id is either TABID_PRINTER, or it is a pointer to the view
  1825.  * to use for data. If going to the printer, don't set the
  1826.  * colours (stick to black and white).
  1827.  */
  1828. long
  1829. do_getdata(HWND hwnd, lpCellDataList cdlist)
  1830. {
  1831.         int start, endcell, col, i;
  1832.         lpCellData cd;
  1833.         VIEW view;
  1834.         LPSTR textp;
  1835.         BOOL bIsPrinter = FALSE;
  1836.  
  1837.         if (cdlist->id == TABID_PRINTER) {
  1838.                 view = current_view;
  1839.                 bIsPrinter = TRUE;
  1840.         } else {
  1841.                 view = (VIEW) cdlist->id;
  1842.         }
  1843.  
  1844.         start = cdlist->startcell;
  1845.         endcell = cdlist->ncells + start;
  1846.         if (cdlist->row >= view_getrowcount(view)) {
  1847.                 return(FALSE);
  1848.         }
  1849.         for (i = start; i < endcell; i++) {
  1850.                 cd = &cdlist->plist[i - start];
  1851.  
  1852.  
  1853.                 /* skip the line number column if IDM_NONRS */
  1854.                 if (line_numbers == IDM_NONRS) {
  1855.                         col = i+1;
  1856.                 } else {
  1857.                         col = i;
  1858.                 }
  1859.  
  1860.                 /* set colour of text to mark out
  1861.                  * lines that are changed, if not printer - for the
  1862.                  * printer everything should stay in the default colours
  1863.                  */
  1864.  
  1865.                 if (!bIsPrinter) {
  1866.  
  1867.                         /* convert the state of the requested row into a
  1868.                          * colour scheme. returns P_FCOLOUR and/or
  1869.                          * P_BCOLOUR if it sets either of the colours
  1870.                          */
  1871.                         cd->props.valid |=
  1872.                             StateToColour(view_getstate(view, cdlist->row), col,
  1873.                                         &cd->props.forecolour,
  1874.                                         &cd->props.backcolour);
  1875.                 }
  1876.  
  1877.                 textp = view_gettext(view, cdlist->row, col);
  1878.                 if (cd->nchars != 0) {
  1879.                         if (textp == NULL) {
  1880.                                 cd->ptext[0] = '\0';
  1881.                         } else {
  1882.                                 strncpy(cd->ptext, textp, cd->nchars -1);
  1883.                                 cd->ptext[cd->nchars - 1] = '\0';
  1884.                         }
  1885.                 }
  1886.  
  1887.         }
  1888.         return(TRUE);
  1889. }
  1890.  
  1891. /***************************************************************************
  1892.  * Function: SvrClose
  1893.  *
  1894.  * Purpose:
  1895.  *
  1896.  * Table window has finished with this view. It can be deleted.
  1897.  */
  1898. void
  1899. SvrClose(void)
  1900. {
  1901.         view_delete(current_view);
  1902.         current_view = NULL;
  1903.  
  1904.         /* hide picture - only visible when we are in MODE_EXPAND */
  1905.         DisplayMode = MODE_NULL;
  1906.         DoResize(hwndClient);
  1907.  
  1908.         /* if we already busy when closing this view (ie
  1909.          * we are in the process of starting a new scan,
  1910.          * then leave the status bar alone, otherwise
  1911.          * we should clean up the state of the status bar
  1912.          */
  1913.         if (!fBusy) {
  1914.                 SetButtonText(LoadRcString(IDS_EXIT));
  1915.                 SetNames(NULL);
  1916.                 SetStatus(NULL);
  1917.  
  1918.         }
  1919.  
  1920. } /* SvrClose */
  1921.  
  1922.  
  1923. /***************************************************************************
  1924.  * Function: TableServer
  1925.  *
  1926.  * Purpose:
  1927.  *
  1928.  * Handle callbacks and notifications from the table class 
  1929.  */
  1930. long
  1931. TableServer(HWND hwnd, UINT cmd, long lParam)
  1932. {
  1933.         lpTableHdr phdr;
  1934.         lpColPropsList proplist;
  1935.         lpCellDataList cdlist;
  1936.         lpTableSelection pselect;
  1937.  
  1938.         switch(cmd) {
  1939.         case TQ_GETSIZE:
  1940.                 /* get the nr of rows and cols in this table */
  1941.                 phdr = (lpTableHdr) lParam;
  1942.                 return(do_gethdr(hwnd, phdr));
  1943.  
  1944.         case TQ_GETCOLPROPS:
  1945.                 /* get the size and properties of each column */
  1946.                 proplist = (lpColPropsList) lParam;
  1947.                 return (do_getprops(hwnd, proplist));
  1948.  
  1949.         case TQ_GETDATA:
  1950.                 /* get the contents of individual cells */
  1951.                 cdlist = (lpCellDataList) lParam;
  1952.                 return (do_getdata(hwnd, cdlist));
  1953.  
  1954.  
  1955.         case TQ_SELECT:
  1956.                 /* selection has changed */
  1957.         case TQ_ENTER:
  1958.                 /* user has double-clicked or pressed enter */
  1959.  
  1960.                 pselect = (lpTableSelection) lParam;
  1961.  
  1962.                 /* store location for use in later search (IDM_FCHANGE) */
  1963.                 if (pselect->nrows < 1) {
  1964.                         selection = -1;
  1965.                 } else {
  1966.                         selection = (int) pselect->startrow;
  1967.                         if (cmd == TQ_ENTER) {
  1968.                                 /* try to expand this row */
  1969.                                 if (!ToExpand(hwnd)) {
  1970.                                         /* expand failed - maybe this
  1971.                                          * is a moved line- show the other
  1972.                                          * copy
  1973.                                          */
  1974.                                         ToMoved(hwnd);
  1975.                                 }
  1976.  
  1977.                         }
  1978.                 }
  1979.                 break;
  1980.  
  1981.         case TQ_CLOSE:
  1982.                 /* close this table - table class no longer needs data*/
  1983.                 SvrClose();
  1984.                 break;
  1985.  
  1986.         case TQ_SCROLL:
  1987.                 /* notification that the rows visible in the window
  1988.                  * have changed -change the current position lines in
  1989.                  * the graphic bar view (the sections picture)
  1990.                  */
  1991.                 if (picture_mode) {
  1992.                         BarDrawPosition(hwndBar, NULL, TRUE);
  1993.                 }
  1994.                 break;
  1995.  
  1996.         default:
  1997.                 return(FALSE);
  1998.         }
  1999.         return(TRUE);
  2000. }
  2001.  
  2002.  
  2003. /***************************************************************************
  2004.  * Function: wd_initial
  2005.  *
  2006.  * Purpose:
  2007.  *
  2008.  * Called on worker thread (not UI thread) to handle the work
  2009.  * requested on the command line. 
  2010.  * arg is a pointer to a THREADARGS block allocated from gmem_get(hHeap). This
  2011.  * needs to be freed before exiting.
  2012.  */
  2013. DWORD
  2014. wd_initial(LPVOID arg)
  2015. {
  2016.         PTHREADARGS pta = (PTHREADARGS) arg;
  2017.         COMPLIST cl;
  2018.  
  2019.  
  2020.         /* build a complist from these args,
  2021.          * and register with the view we have made
  2022.          */
  2023.         cl = complist_args(pta->first, pta->second, pta->view, pta->fDeep);
  2024.  
  2025.         if (cl == NULL) {
  2026.                 view_close(pta->view);
  2027.                 gmem_free(hHeap, (LPSTR) pta, sizeof(THREADARGS));
  2028.                 SetNotBusy();
  2029.                 return 0;
  2030.         }
  2031.  
  2032.  
  2033.         /* if savelist was selected, write out the list and exit */
  2034.         if(pta->savelist != NULL) {
  2035.                 complist_savelist(cl, pta->savelist, pta->saveopts);
  2036.                 gmem_free(hHeap, (LPSTR) pta, sizeof(THREADARGS));
  2037.                 SetNotBusy();
  2038.                 exit(0);
  2039.         }
  2040.  
  2041.         /* if there was only one file, expand it */
  2042.         if (view_getrowcount(pta->view) == 1) {
  2043.                 SetSelection(0);
  2044.                 ToExpand(hwndClient);
  2045.         }
  2046.  
  2047.  
  2048.         gmem_free(hHeap, (LPSTR) pta, sizeof(THREADARGS));
  2049.         SetNotBusy();
  2050.         return(0);
  2051. } /* wd_initial */
  2052.  
  2053.  
  2054. /***************************************************************************
  2055.  * Function: wd_dirdialog
  2056.  *
  2057.  * Purpose:
  2058.  *
  2059.  * Called on worker thread (not UI thread) to handle a Dir request
  2060.  */
  2061. DWORD
  2062. wd_dirdialog(LPVOID arg)
  2063. {
  2064.  
  2065.         VIEW view = (VIEW) arg;
  2066.  
  2067.         /* make a COMPLIST using the directory dialog,
  2068.          * and notify the view
  2069.          */
  2070.         if (complist_dirdialog(view) == NULL) {
  2071.                 view_close(view);
  2072.         }
  2073.  
  2074.         /* all done! */
  2075.         SetNotBusy();
  2076.         return(0);
  2077. }
  2078.  
  2079.  
  2080. /***************************************************************************
  2081.  * Function: wd_copy
  2082.  *
  2083.  * Purpose:
  2084.  *
  2085.  * Called on worker thread to do a copy-files operation
  2086.  */
  2087. DWORD
  2088. wd_copy(LPVOID arg)
  2089. {
  2090.  
  2091.         VIEW view = (VIEW) arg;
  2092.  
  2093.         complist_copyfiles(view_getcomplist(view), NULL, 0);
  2094.  
  2095.         SetNotBusy();
  2096.  
  2097.         return(0);
  2098. }
  2099.  
  2100.  
  2101. /***************************************************************************
  2102.  * Function: MainWndProc
  2103.  *
  2104.  * Purpose:
  2105.  *
  2106.  * Window processing for main window
  2107.  */
  2108. long APIENTRY
  2109. MainWndProc(HWND hWnd, UINT message, UINT wParam, LONG lParam)
  2110. {
  2111.         char str[32];
  2112.         long ret;
  2113.         DWORD threadid;
  2114.  
  2115.         switch(message) {
  2116.  
  2117.  
  2118.         case WM_CREATE:
  2119.  
  2120.                 /* initialise menu options to default/saved
  2121.                  * option settings
  2122.                  */
  2123.  
  2124.                 CheckMenuItem(hMenu, IDM_INCSAME,
  2125.                       (outline_include & INCLUDE_SAME) ?
  2126.                                 MF_CHECKED:MF_UNCHECKED);
  2127.  
  2128.                 CheckMenuItem(hMenu, IDM_INCLEFT,
  2129.                       (outline_include & INCLUDE_LEFTONLY) ?
  2130.                                 MF_CHECKED:MF_UNCHECKED);
  2131.  
  2132.                 CheckMenuItem(hMenu, IDM_INCRIGHT,
  2133.                       (outline_include & INCLUDE_RIGHTONLY) ?
  2134.                                 MF_CHECKED:MF_UNCHECKED);
  2135.                 CheckMenuItem(hMenu, IDM_INCDIFFER,
  2136.                       (outline_include & INCLUDE_DIFFER) ?
  2137.                                 MF_CHECKED:MF_UNCHECKED);
  2138.  
  2139.                 CheckMenuItem(hMenu, line_numbers, MF_CHECKED);
  2140.                 CheckMenuItem(hMenu, expand_mode, MF_CHECKED);
  2141.  
  2142.                 CheckMenuItem(hMenu, IDM_IGNBLANKS,
  2143.                         ignore_blanks ? MF_CHECKED : MF_UNCHECKED);
  2144.                 CheckMenuItem(hMenu, IDM_PICTURE,
  2145.                         picture_mode ? MF_CHECKED : MF_UNCHECKED);
  2146.  
  2147.                 /* nothing currently displayed */
  2148.                 DisplayMode = MODE_NULL;
  2149.  
  2150.                 break;
  2151.  
  2152.  
  2153.         case WM_COMMAND:
  2154.                 switch (GET_WM_COMMAND_ID(wParam, lParam)) {
  2155.                 case IDM_EXIT:
  2156.                         if (ghThread!=NULL) {
  2157.                                 extern CRITICAL_SECTION CSView;
  2158.                                 /* Stop any other thread from allocating things that we
  2159.                                    want to free!  See the threads DOGMA at the top
  2160.                                    of this file.
  2161.                                 */
  2162.  
  2163.                                 /* Because the thread that we are about to kill might be in
  2164.                                    a critical section, we first grab them both.  It is
  2165.                                    essential that anyone else who ever does this, does
  2166.                                    so in the same order!
  2167.                                 */
  2168.                                 WDEnter();
  2169.                                 EnterCriticalSection(&CSView);
  2170.                                 TerminateThread(ghThread, 31);
  2171.                                 CloseHandle(ghThread);
  2172.                                 ghThread = NULL;
  2173.                                 LeaveCriticalSection(&CSView);
  2174.                                 WDLeave();
  2175.                         }
  2176.                         if (!view_isexpanded(current_view)) {
  2177.                                 /* save the current outline size and position */
  2178.                                 WINDOWPLACEMENT wp;
  2179.                                 if (GetWindowPlacement(hwndClient,&wp)) {
  2180.                                         WriteProfileInt(APPNAME, "OutlineShowCmd", wp.showCmd);
  2181.                                         WriteProfileInt(APPNAME, "OutlineMaxX", wp.ptMaxPosition.x);
  2182.                                         WriteProfileInt(APPNAME, "OutlineMaxY", wp.ptMaxPosition.y);
  2183.                                         WriteProfileInt(APPNAME, "OutlineNormLeft", wp.rcNormalPosition.left);
  2184.                                         WriteProfileInt(APPNAME, "OutlineNormTop", wp.rcNormalPosition.top);
  2185.                                         WriteProfileInt(APPNAME, "OutlineNormRight", wp.rcNormalPosition.right);
  2186.                                         WriteProfileInt(APPNAME, "OutlineNormBottom", wp.rcNormalPosition.bottom);
  2187.                                         WriteProfileInt(APPNAME, "OutlineSaved", 1);
  2188.                                 }
  2189.                         } else {
  2190.                                 /* save the current expanded size and position */
  2191.                                 WINDOWPLACEMENT wp;
  2192.                                 if (GetWindowPlacement(hwndClient,&wp)) {
  2193.                                         WriteProfileInt(APPNAME, "ExpandShowCmd", wp.showCmd);
  2194.                                         WriteProfileInt(APPNAME, "ExpandMaxX", wp.ptMaxPosition.x);
  2195.                                         WriteProfileInt(APPNAME, "ExpandMaxY", wp.ptMaxPosition.y);
  2196.                                         WriteProfileInt(APPNAME, "ExpandNormLeft", wp.rcNormalPosition.left);
  2197.                                         WriteProfileInt(APPNAME, "ExpandNormTop", wp.rcNormalPosition.top);
  2198.                                         WriteProfileInt(APPNAME, "ExpandNormRight", wp.rcNormalPosition.right);
  2199.                                         WriteProfileInt(APPNAME, "ExpandNormBottom", wp.rcNormalPosition.bottom);
  2200.                                         WriteProfileInt(APPNAME, "ExpandedSaved", 1);
  2201.                                 }
  2202.                         }
  2203.                         DestroyWindow(hWnd);
  2204.                         break;
  2205.  
  2206.                 case IDM_ABORT:
  2207.                         /* abort menu item, or status bar button.
  2208.                          * the status bar button text gives the appropriate
  2209.                          * action depending on our state - abort, outline
  2210.                          * or expand. But the command sent is always
  2211.                          * IDM_ABORT. Thus we need to check the state
  2212.                          * to see what to do. If we are busy, set the abort
  2213.                          * flag. If there is nothing to view,
  2214.                          * exit, otherwise switch outline<->expand
  2215.                          */
  2216.                         if (IsBusy()) {
  2217.                                 bAbort = TRUE;
  2218.                                 SetStatus(LoadRcString(IDS_ABORT_PENDING));
  2219.  
  2220.                         } else if (DisplayMode == MODE_NULL) {
  2221.                                 DestroyWindow(hWnd);
  2222.                         } else if (DisplayMode == MODE_EXPAND) {
  2223.                                 ToOutline(hWnd);
  2224.                         } else {
  2225.                                 ToExpand(hWnd);
  2226.                         }
  2227.                         break;
  2228.  
  2229.                 case IDM_FILE:
  2230.                         /* select two files and compare them */
  2231.                         if (SetBusy()) {
  2232.  
  2233.                                /* close the current view */
  2234.                                 view_close(current_view);
  2235.  
  2236.                                 /* make a new empty view */
  2237.                                 current_view = view_new(hwndRCD);
  2238.  
  2239.                                 /* make a COMPLIST using the files dialog,
  2240.                                  * and notify the view
  2241.                                  */
  2242.                                 if (complist_filedialog(current_view) == NULL) {
  2243.                                         view_close(current_view);
  2244.                                 }
  2245.  
  2246.                                 /* all done! */
  2247.                                 SetNotBusy();
  2248.                         } else {
  2249.                                 BusyError();
  2250.                         }
  2251.                         break;
  2252.  
  2253.                 case IDM_DIR:
  2254.  
  2255.                         /* read two directory names, scan them and
  2256.                          * compare all the files and subdirs.
  2257.                          */
  2258.                         if (SetBusy()) {
  2259.  
  2260.                                 /* close the current view */
  2261.                                 view_close(current_view);
  2262.  
  2263.                                 /* make a new empty view */
  2264.                                 current_view = view_new(hwndRCD);
  2265.  
  2266.                                 ghThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)wd_dirdialog,
  2267.                                         (LPVOID) current_view, 0, &threadid);
  2268.  
  2269.                                 if (ghThread == NULL)
  2270.                                 {
  2271.                                         wd_dirdialog( (LPVOID) current_view);
  2272.                                 }
  2273.  
  2274.                         } else {
  2275.                                 BusyError();
  2276.                         }
  2277.                         break;
  2278.  
  2279.                 case IDM_CLOSE:
  2280.                         /* close the output list -
  2281.                          * discard all results so far
  2282.                          */
  2283.                         if (!IsBusy()) {
  2284.                                 view_close(current_view);
  2285.                         }
  2286.                         break;
  2287.  
  2288.                 case IDM_PRINT:
  2289.                         /* print the current view -
  2290.                          * either the outline list of filenames,
  2291.                          * or the currently expanded file.
  2292.                          */
  2293.                         if (!IsBusy()) {
  2294.                                 DoPrint();
  2295.                         } else {
  2296.                                 BusyError();
  2297.                         }
  2298.                         break;
  2299.  
  2300.                 case IDM_TIME:
  2301.                         /* show time it took */
  2302.                         {       char msg[50];
  2303.                                 DWORD tim;
  2304.                                 if (IsBusy()) {
  2305.                                          BusyError();
  2306.                                 }
  2307.                                 else{
  2308.                                         tim = complist_querytime();
  2309.                                         wsprintf((LPTSTR)msg, LoadRcString(IDS_SECONDS), tim/1000, tim%1000);
  2310.                                 }
  2311.                         }
  2312.                         break;
  2313.  
  2314.                 case IDM_SAVELIST:
  2315.                         /* allow user to save list of same/different files
  2316.                          * to a text file. dialog box to give filename
  2317.                          * and select which types of file to include
  2318.                          */
  2319.                         complist_savelist(view_getcomplist(current_view), NULL, 0);
  2320.                         break;
  2321.  
  2322.                 case IDM_COPYFILES:
  2323.                         /*
  2324.                          * copy files that are same/different to a new
  2325.                          * root directory. dialog box allows user
  2326.                          * to select new root and inclusion options
  2327.                          */
  2328.                         if (current_view == NULL) {
  2329.                                 MessageBox(hWnd,
  2330.                                     LoadRcString(IDS_CREATE_DIFF_LIST),
  2331.                                     "WinDiff", MB_OK|MB_ICONSTOP);
  2332.                                 break;
  2333.                         }
  2334.  
  2335.                         if (SetBusy()) {
  2336.                                 ghThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)wd_copy,
  2337.                                         (LPVOID) current_view, 0, &threadid);
  2338.                                 if (ghThread == NULL)
  2339.                                 {
  2340.                                         wd_copy( (LPVOID) current_view);
  2341.                                 }
  2342.  
  2343.                         } else {
  2344.                                 BusyError();
  2345.                         }
  2346.  
  2347.                         break;
  2348.  
  2349.                 case IDM_ABOUT:
  2350.  
  2351.                         DialogBox( hInst, "About", hWnd, (DLGPROC)AboutBox);
  2352.                         break;
  2353.  
  2354.                 /* launch an editor on the current item - left, right or
  2355.                  * composite view
  2356.                  */
  2357.                 case IDM_EDITLEFT:
  2358.                         do_editthread(current_view, CI_LEFT);
  2359.                         break;
  2360.  
  2361.                 case IDM_EDITRIGHT:
  2362.                         do_editthread(current_view, CI_RIGHT);
  2363.                         break;
  2364.  
  2365.                 case IDM_EDITCOMP:
  2366.                         do_editthread(current_view, CI_COMP);
  2367.                         break;
  2368.  
  2369.                 /* allow customisation of the editor command line */
  2370.                 case IDM_SETEDIT:
  2371.                         if (StringInput(editor_cmdline, sizeof(editor_cmdline),
  2372.                                         LoadRcString(IDS_ENTER_EDT_CMD_LINE),
  2373.                                         "Windiff", editor_cmdline))  {
  2374.                                 WriteProfileString(APPNAME, "Editor",
  2375.                                         (LPCSTR)editor_cmdline);
  2376.                         }
  2377.                         break;
  2378.  
  2379.  
  2380.                 case IDM_LNRS:
  2381.                 case IDM_RNRS:
  2382.                 case IDM_NONRS:
  2383.  
  2384.                         /* option selects whether the line nrs displayed
  2385.                          * in expand mode are the line nrs in the left
  2386.                          * file, the right file or none
  2387.                          */
  2388.  
  2389.                         CheckMenuItem(GetMenu(hWnd),
  2390.                                 line_numbers, MF_UNCHECKED);
  2391.                         line_numbers = GET_WM_COMMAND_ID(wParam, lParam);
  2392.                         CheckMenuItem(GetMenu(hWnd), line_numbers, MF_CHECKED);
  2393.                         wsprintf((LPTSTR)str, "%d", line_numbers);
  2394.                         WriteProfileString(APPNAME, "LineNumbers", str);
  2395.  
  2396.                         /* change the display to show the line nr style
  2397.                          * chosen
  2398.                          */
  2399.  
  2400.                         view_changeviewoptions(current_view);
  2401.  
  2402.  
  2403.                         break;
  2404.  
  2405.                 /*
  2406.                  * options selecting which files to include in the
  2407.                  * outline listing, based on their state
  2408.                  */
  2409.                 case IDM_INCLEFT:
  2410.  
  2411.  
  2412.                         /* toggle flag in outline_include options */
  2413.                         outline_include ^= INCLUDE_LEFTONLY;
  2414.  
  2415.                         /* check/uncheck as necessary */
  2416.                         CheckMenuItem(hMenu, IDM_INCLEFT,
  2417.                               (outline_include & INCLUDE_LEFTONLY) ?
  2418.                                         MF_CHECKED:MF_UNCHECKED);
  2419.  
  2420.                         wsprintf((LPTSTR)str, "%d", outline_include);
  2421.                         WriteProfileString(APPNAME, "FileInclude", str);
  2422.                         view_changeviewoptions(current_view);
  2423.  
  2424.  
  2425.                         break;
  2426.  
  2427.                 case IDM_INCRIGHT:
  2428.  
  2429.  
  2430.                         outline_include ^= INCLUDE_RIGHTONLY;
  2431.  
  2432.                         CheckMenuItem(hMenu, IDM_INCRIGHT,
  2433.                               (outline_include & INCLUDE_RIGHTONLY) ?
  2434.                                         MF_CHECKED:MF_UNCHECKED);
  2435.                         wsprintf((LPTSTR)str, "%d", outline_include);
  2436.                         WriteProfileString(APPNAME, "FileInclude", str);
  2437.                         view_changeviewoptions(current_view);
  2438.  
  2439.                         break;
  2440.  
  2441.                 case IDM_INCSAME:
  2442.  
  2443.  
  2444.                         outline_include ^= INCLUDE_SAME;
  2445.  
  2446.                         CheckMenuItem(hMenu, IDM_INCSAME,
  2447.                               (outline_include & INCLUDE_SAME) ?
  2448.                                         MF_CHECKED:MF_UNCHECKED);
  2449.                         wsprintf((LPTSTR)str, "%d", outline_include);
  2450.                         WriteProfileString(APPNAME, "FileInclude", str);
  2451.                         view_changeviewoptions(current_view);
  2452.  
  2453.  
  2454.                         break;
  2455.  
  2456.  
  2457.                 case IDM_INCDIFFER:
  2458.  
  2459.  
  2460.  
  2461.                         outline_include ^= INCLUDE_DIFFER;
  2462.  
  2463.                         CheckMenuItem(hMenu, IDM_INCDIFFER,
  2464.                               (outline_include & INCLUDE_DIFFER) ?
  2465.                                         MF_CHECKED:MF_UNCHECKED);
  2466.  
  2467.                         wsprintf((LPTSTR)str, "%d", outline_include);
  2468.                         WriteProfileString(APPNAME, "FileInclude", str);
  2469.                         view_changeviewoptions(current_view);
  2470.  
  2471.  
  2472.                         break;
  2473.  
  2474.                 case IDM_UPDATE:
  2475.                         /* update the display.  Options or files may have changed */
  2476.                         /* discard lines  (thereby forcing re-read).
  2477.                          */
  2478.                         file_discardlines(compitem_getleftfile( (COMPITEM)lParam) );
  2479.                         file_discardlines(compitem_getrightfile( (COMPITEM)lParam) );
  2480.  
  2481.                         view_changediffoptions(current_view);
  2482.  
  2483.                         /* force repaint of bar window */
  2484.                         InvalidateRect(hwndBar, NULL, TRUE);
  2485.                         break;
  2486.  
  2487.  
  2488.  
  2489.                 case IDM_LONLY:
  2490.                 case IDM_RONLY:
  2491.                 case IDM_BOTHFILES:
  2492.                         /* option selects whether the expanded file
  2493.                          * show is the combined file, or just one
  2494.                          * or other of the input files.
  2495.                          *
  2496.                          * if we are not in expand mode, this also
  2497.                          * causes us to expand the selection
  2498.                          */
  2499.  
  2500.  
  2501.                         CheckMenuItem(GetMenu(hWnd), expand_mode, MF_UNCHECKED);
  2502.                         expand_mode = GET_WM_COMMAND_ID(wParam, lParam);
  2503.                         CheckMenuItem(GetMenu(hWnd), expand_mode, MF_CHECKED);
  2504.  
  2505.                         /* change the current view to show only the lines
  2506.                          * of the selected type.
  2507.                          */
  2508.                         if (DisplayMode == MODE_OUTLINE) {
  2509.                                 ToExpand(hWnd);
  2510.                         } else {
  2511.                                 view_changeviewoptions(current_view);
  2512.                         }
  2513.  
  2514.  
  2515.                         break;
  2516.  
  2517.  
  2518.                 case IDM_IGNBLANKS:
  2519.  
  2520.                         /* if selected, ignore all spaces and tabs on
  2521.                          * comparison - expand view only: outline view
  2522.                          * will still show that 'text files differ'
  2523.                          */
  2524.  
  2525.                         ignore_blanks = !ignore_blanks;
  2526.                         CheckMenuItem(hMenu, IDM_IGNBLANKS,
  2527.                                 ignore_blanks? MF_CHECKED:MF_UNCHECKED);
  2528.                         wsprintf((LPTSTR)str, "%d", ignore_blanks);
  2529.                         WriteProfileString(APPNAME, "Blanks", str);
  2530.  
  2531.                         /* invalidate all diffs since we have
  2532.                          * changed diff options, and re-do and display the
  2533.                          * current diff if we are in expand mode.
  2534.                          */
  2535.                         view_changediffoptions(current_view);
  2536.  
  2537.                         /* force repaint of bar window */
  2538.                         InvalidateRect(hwndBar, NULL, TRUE);
  2539.  
  2540.                         break;
  2541.  
  2542.                 case IDM_PICTURE:
  2543.                         /* do we show the bar picture in expand mode ? */
  2544.                         picture_mode = !picture_mode;
  2545.                         CheckMenuItem(hMenu, IDM_PICTURE,
  2546.                                 picture_mode? MF_CHECKED:MF_UNCHECKED);
  2547.                         wsprintf((LPTSTR)str, "%d", picture_mode);
  2548.                         WriteProfileString(APPNAME, "Picture", str);
  2549.                         DoResize(hWnd);
  2550.                         break;
  2551.  
  2552.  
  2553.                 case IDM_EXPAND:
  2554.  
  2555.                         /* show the expanded view of the
  2556.                          * selected file
  2557.                          */
  2558.                         if (current_view != NULL) {
  2559.                                 ToExpand(hWnd);
  2560.                         }
  2561.  
  2562.                         break;
  2563.  
  2564.                 case IDM_OUTLINE:
  2565.                         /* return to the outline view (list of filenames) */
  2566.                         ToOutline(hWnd);
  2567.  
  2568.                         break;
  2569.  
  2570.                 case IDM_FCHANGE:
  2571.                         /* find the next line in the current view
  2572.                          * that is not the same in both files -
  2573.                          * in outline view, finds the next filename that
  2574.                          * is not identical
  2575.                          */
  2576.                         FindNextChange();
  2577.  
  2578.                         break;
  2579.  
  2580.                 case IDM_FPCHANGE:
  2581.                         /* same as IDM_FCHANGE, but going backwards from
  2582.                          * current position
  2583.                          */
  2584.                         FindPrevChange();
  2585.  
  2586.                         break;
  2587.                 }
  2588.                 break;
  2589.  
  2590.         case WM_SIZE:
  2591.                 DoResize(hWnd);
  2592.                 break;
  2593.  
  2594.         case WM_SETFOCUS:
  2595.                 /* set the focus on the table class so it can process
  2596.                  * page-up /pagedown keys etc.
  2597.                  */
  2598.                 SetFocus(hwndRCD);
  2599.                 break;
  2600.  
  2601.         case WM_KEYDOWN:
  2602.                 /* although the table window has the focus, he passes
  2603.                  * back to us any keys he doesn't understand
  2604.                  * We handle escape here to mean 'return to outline view'
  2605.                  */
  2606.                 if (wParam == VK_ESCAPE) {
  2607.                         ToOutline(hWnd);
  2608.                 }
  2609.                 break;
  2610.  
  2611.         case WM_CLOSE:
  2612.                 /* don't allow close when busy - process this message in
  2613.                  * order to ensure this
  2614.                  */
  2615.                 if (IsBusy()) {
  2616.                         return(TRUE);
  2617.                 } else {
  2618.                         return(DefWindowProc(hWnd, message, wParam, lParam));
  2619.                 }
  2620.                 break;
  2621.  
  2622.         case WM_DESTROY:
  2623.  
  2624.                 DeleteTools();
  2625.                 PostQuitMessage(0);
  2626.                 break;
  2627.  
  2628.         case TM_CURRENTVIEW:
  2629.                 /* allow other people such as the bar window to query the
  2630.                  * current view
  2631.                  */
  2632.                 return((DWORD) current_view);
  2633.  
  2634.         default:
  2635.                 /* handle registered table messages */
  2636.                 if (message == table_msgcode) {
  2637.                         ret = TableServer(hWnd, wParam, lParam);
  2638.                         return(ret);
  2639.                 }
  2640.                 return(DefWindowProc(hWnd, message, wParam, lParam));
  2641.         }
  2642.         return(0);
  2643. }
  2644.  
  2645. /***************************************************************************
  2646.  * Function: My_mbschr
  2647.  *
  2648.  * Purpose:
  2649.  *
  2650.  * DBCS version of strchr
  2651.  *
  2652.  */
  2653. unsigned char * _CRTAPI1 My_mbschr(
  2654.     unsigned char *psz, unsigned short uiSep)
  2655. {
  2656.     while (*psz != '\0' && *psz != uiSep) {
  2657.         psz = CharNext(psz);
  2658.     }
  2659.     return *psz == uiSep ? psz : NULL;
  2660. }
  2661. /***************************************************************************
  2662.  * Function: My_mbsncpy
  2663.  *
  2664.  * Purpose:
  2665.  *
  2666.  * DBCS version of strncpy
  2667.  *
  2668.  */
  2669. unsigned char * _CRTAPI1 My_mbsncpy(
  2670.     unsigned char *psz1, const unsigned char *psz2, size_t Length)
  2671. {
  2672.         int nLen = (int)Length;
  2673.     unsigned char *pszSv = psz1;
  2674.  
  2675.     while (0 < nLen) {
  2676.         if (*psz2 == '\0') {
  2677.             *psz1++ = '\0';
  2678.             nLen--;
  2679.         } else if (IsDBCSLeadByte(*psz2)) {
  2680.             if (nLen == 1) {
  2681.                 *psz1 = '\0';
  2682.             } else {
  2683.                 *psz1++ = *psz2++;
  2684.                 *psz1++ = *psz2++;
  2685.             }
  2686.             nLen -= 2;
  2687.         } else {
  2688.             *psz1++ = *psz2++;
  2689.             nLen--;
  2690.         }
  2691.     }
  2692.     return pszSv;
  2693. }
  2694. /***************************************************************************
  2695.  * Function: My_mbsrchr
  2696.  *
  2697.  * Purpose:
  2698.  *
  2699.  * DBCS version of strrchr
  2700.  *
  2701.  */
  2702. unsigned char * _CRTAPI1 My_mbsrchr(
  2703.     unsigned char *psz, unsigned short uiSep)
  2704. {
  2705.     unsigned char *pszHead;
  2706.  
  2707.     pszHead = psz;
  2708.  
  2709.     while (*psz != '\0') {
  2710.         psz++;
  2711.     }
  2712.     if (uiSep == '\0') {
  2713.         return psz;
  2714.     }
  2715.  
  2716.     while (psz > pszHead) {
  2717.         psz = CharPrev(pszHead, psz);
  2718.         if (*psz == uiSep) {
  2719.             break;
  2720.         }
  2721.     }
  2722.     return *psz == uiSep ? psz : NULL;
  2723. }
  2724. /***************************************************************************
  2725.  * Function: My_mbsncmp
  2726.  *
  2727.  * Purpose:
  2728.  *
  2729.  * DBCS version of strncmp
  2730.  * If 'nLen' splits a DBC, this function compares the DBC's 2nd byte also.
  2731.  *
  2732.  */
  2733. int _CRTAPI1 My_mbsncmp(
  2734.     const unsigned char *psz1, const unsigned char *psz2, size_t nLen)
  2735. {
  2736.     int Length = (int)nLen;
  2737.  
  2738.     while (0 < Length) {
  2739.         if ('\0' == *psz1 || '\0' == *psz2) {
  2740.             return *psz1 - *psz2;
  2741.         }
  2742.         if (IsDBCSLeadByte(*psz1) || IsDBCSLeadByte(*psz2)) {
  2743.             if (*psz1 != *psz2 || *(psz1+1) != *(psz2+1)) {
  2744.                 return *psz1 - *psz2;
  2745.             }
  2746.             psz1 += 2;
  2747.             psz2 += 2;
  2748.             Length -= 2;
  2749.         } else {
  2750.             if (*psz1 != *psz2) {
  2751.                 return *psz1 - *psz2;
  2752.             }
  2753.             psz1++;
  2754.             psz2++;
  2755.             Length--;
  2756.         }
  2757.     }
  2758.     return 0;
  2759. }
  2760.  
  2761.