|
|
1.1 root 1:
2: /******************************************************************************\
3: * This is a part of the Microsoft Source Code Samples.
4: * Copyright (C) 1993 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: "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.flags = 0;
546: wp.showCmd = GetProfileInt( APPNAME, "OutlineShowCmd",
547: SW_SHOWNORMAL);
548: wp.ptMaxPosition.x = GetProfileInt( APPNAME, "OutlineMaxX", 0);
549: wp.ptMaxPosition.y = GetProfileInt( APPNAME, "OutlineMaxY", 0);
550: wp.rcNormalPosition.left = (int)GetProfileInt( APPNAME, "OutlineNormLeft", (UINT)(-1));
551: wp.rcNormalPosition.top = (int)GetProfileInt( APPNAME, "OutlineNormTop", (UINT)(-1));
552: wp.rcNormalPosition.right = (int)GetProfileInt( APPNAME, "OutlineNormRight", (UINT)(-1));
553: wp.rcNormalPosition.bottom = (int)GetProfileInt( APPNAME, "OutlineNormBottom",(UINT)(-1));
554: SetWindowPlacement(hwndClient,&wp);
555: }
556: else ShowWindow(hwndClient, nMinMax);
557:
558: UpdateWindow(hwndClient);
559:
560:
561: /* initialise busy flag and status line to show we are idle
562: * (ie not comparing or scanning)
563: */
564: SetNotBusy();
565:
566: return(TRUE);
567:
568: } /* InitInstance */
569:
570: /***************************************************************************
571: * Function: windiff_usage
572: *
573: * Purpose:
574: *
575: * Complain to command line users about poor syntax,
576: * will be replaced by proper help file.
577: */
578: void
579: windiff_usage(LPSTR msg)
580: {
581: int retval;
582: if (msg==NULL)
583: msg = "windiff {-L} path1 {path2} {-s{slrd} savefile}";
584:
585: retval = MessageBox(hwndClient,
586: msg,
587: "Windiff Usage", MB_ICONINFORMATION|MB_OKCANCEL);
588: if (retval == IDCANCEL) {
589: exit(1);
590: }
591: }
592:
593: /***************************************************************************
594: * Function: ParseArgs
595: *
596: * Purpose:
597: *
598: * Parse command line arguments
599: *
600: * The user can give one or two paths. If only one, we assume the second
601: * is '.' for the current directory. If one of the two paths is a directory
602: * and the other a file, we compare a file of the same name in the two dirs.
603: *
604: * The command -s filename causes the outline list to be written to a file
605: * and then the program exits. -s{slrd} filename allows selection of which
606: * files are written out; by default, we assume -sld for files left and different.
607: *
608: * -T means tree. Go deep.
609: *
610: * The default is Deep, -L overrides and implies shallow, -T overrides -L
611: */
612: void
613: ParseArgs(int argc, char ** argv)
614: {
615: int i;
616: LPSTR chp;
617: PTHREADARGS ta;
618: DWORD threadid;
619:
620: /* thread args can't be on the stack since the stack will change
621: * before the thread completes execution
622: */
623: ta = (PTHREADARGS) gmem_get(hHeap, sizeof(THREADARGS));
624: ta->first = NULL;
625: ta->second = NULL;
626: ta->savelist = NULL;
627: ta->saveopts = 0;
628: ta->fDeep = FALSE; /* No -T option seen yet */
629:
630: for (i = 1; i < argc; i++) {
631:
632: /* is this an option ? */
633: if ((argv[i][0] == '-') || (argv[i][0] == '/')) {
634: switch(argv[i][1]) {
635:
636: case 's':
637: case 'S':
638: /* read letters for the save option: s,l,r,d */
639: for(chp = &argv[i][2]; *chp != '\0'; chp++) {
640: switch(*chp) {
641: case 's':
642: case 'S':
643: ta->saveopts |= INCLUDE_SAME;
644: break;
645: case 'l':
646: case 'L':
647: ta->saveopts |= INCLUDE_DIFFER;
648: break;
649: case 'r':
650: case 'R':
651: ta->saveopts |= INCLUDE_LEFTONLY;
652: break;
653: case 'd':
654: case 'D':
655: ta->saveopts |= INCLUDE_RIGHTONLY;
656: break;
657: default:
658: windiff_usage(NULL);
659: return;
660: }
661: }
662:
663: if (ta->saveopts == 0) {
664: /* default to left and differ */
665: ta->saveopts = (INCLUDE_LEFTONLY) | (INCLUDE_DIFFER);
666: }
667: ta->savelist = argv[++i];
668: break;
669: case 't':
670: case 'T':
671: ta->fDeep = TRUE;
672: break;
673: default:
674: windiff_usage(NULL);
675: return;
676: }
677: } else {
678: if (ta->first == NULL) {
679: ta->first = argv[i];
680: } else {
681: ta->second = argv[i];
682: }
683: }
684: }
685:
686: /* set the correct depth */
687: if (ta->fDeep)
688: ; /* explicitly set -- leave it alone */
689: else ta->fDeep = TRUE; /* global default */
690:
691: /* any paths to scan ? */
692: if (ta->first == NULL) {
693: return;
694: }
695:
696: if (ta->second == NULL) {
697: ta->second = ".";
698: }
699:
700: SetBusy();
701:
702: /* minimise the window if -s flag given */
703: if (ta->savelist != NULL) {
704: ShowWindow(hwndClient, SW_MINIMIZE);
705: }
706:
707: /* make an empty view */
708: current_view = view_new(hwndRCD);
709: DisplayMode = MODE_OUTLINE;
710:
711: ta->view = current_view;
712:
713: /* attempt to create a worker thread */
714:
715: ghThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)wd_initial, (LPVOID) ta,
716: 0, &threadid);
717: if (ghThread == NULL)
718: {
719: wd_initial( (LPVOID) ta);
720: }
721: } /* ParseArgs */
722:
723:
724: /***************************************************************************
725: * Function: CreateTools
726: *
727: * Purpose:
728: *
729: * Create any pens/brushes, and read defaults
730: * from the profile file for menu settings etc.
731: */
732: void
733: CreateTools(void)
734: {
735:
736: /* standard message that table class sends us for
737: * notifications and queries.
738: */
739: table_msgcode = RegisterWindowMessage(TableMessage);
740:
741: line_numbers = GetProfileInt(APPNAME, "LineNumbers", line_numbers);
742: outline_include = GetProfileInt(APPNAME, "FileInclude", outline_include);
743: ignore_blanks = GetProfileInt(APPNAME, "Blanks", ignore_blanks);
744: picture_mode = GetProfileInt(APPNAME, "Picture", picture_mode);
745:
746: GetProfileString(APPNAME, "Editor", editor_cmdline, (LPTSTR)editor_cmdline,
747: sizeof(editor_cmdline));
748: InitializeCriticalSection(&CSWindiff);
749:
750: }
751:
752: /***************************************************************************
753: * Function: DeleteTools
754: *
755: * Purpose:
756: *
757: * Delete any pens or brushes that were created in CreateTools
758: */
759: void
760: DeleteTools(void)
761: {
762:
763: DeleteCriticalSection(&CSWindiff);
764:
765: }
766:
767:
768: /***************************************************************************
769: * Function:
770: *
771: * Purpose:
772: *
773: * Check whether we have had an abort request (IDM_ABORT), and
774: * return TRUE if abort requested, otherwise FALSE
775: */
776: BOOL
777: Poll(void)
778: {
779: return(bAbort);
780: }
781:
782:
783: /***************************************************************************
784: * Function: DoResize
785: *
786: * Purpose:
787: *
788: * Position child windows on a resize of the main window
789: */
790: void
791: DoResize(HWND hWnd)
792: {
793: RECT rc;
794: int bar_width;
795:
796: GetClientRect(hWnd, &rc);
797: MoveWindow(hwndStatus, 0, 0, rc.right - rc.left, status_height, TRUE);
798:
799: bar_width = (rc.right - rc.left) * BAR_WIN_WIDTH / 100;
800:
801: /* bar window is hidden unless in expand mode */
802: if ((DisplayMode == MODE_EXPAND) && (picture_mode)) {
803: ShowWindow(hwndBar, SW_SHOW);
804: MoveWindow(hwndBar, 0, status_height,
805: bar_width, rc.bottom - status_height, TRUE);
806: MoveWindow(hwndRCD, bar_width, status_height,
807: (rc.right - rc.left) - bar_width,
808: rc.bottom - status_height, TRUE);
809: } else {
810: MoveWindow(hwndRCD, 0, status_height, (rc.right - rc.left),
811: rc.bottom - status_height, TRUE);
812: ShowWindow(hwndBar, SW_HIDE);
813: }
814:
815: }
816: /***************************************************************************
817: * Function: AboutBox
818: *
819: * Purpose:
820: *
821: * Standard processing for About box.
822: */
823: int APIENTRY
824: AboutBox(HWND hDlg, unsigned message, UINT wParam, LONG lParam)
825: {
826: char ch[256];
827:
828: switch (message) {
829:
830: case WM_INITDIALOG:
831: wsprintf((LPTSTR)ch, "%d.%02d", Version, SubVersion);
832: SetDlgItemText(hDlg, IDD_VERSION, ch);
833: return(TRUE);
834:
835: case WM_COMMAND:
836: switch (GET_WM_COMMAND_ID(wParam, lParam)) {
837: case IDOK:
838: EndDialog(hDlg, 0);
839: return(TRUE);
840: }
841: break;
842: }
843: return(FALSE);
844: }
845:
846:
847: /* -- menu commands ---------------------------------------------------*/
848:
849: /***************************************************************************
850: * Function: DoPrint
851: *
852: * Purpose:
853: *
854: * Print the current view
855: */
856: void
857: DoPrint(void)
858: {
859: Title head, foot;
860: PrintContext context;
861:
862: /* print context contains the header and footer. Use the
863: * default margins and printer selection
864: */
865:
866: /* we set the table id to be TABID_PRINTER. When the table calls
867: * back to get text and properties, we use this to indicate
868: * that the table refered to is the 'current_view', but in print
869: * mode, and thus we will use different colours/fonts.
870: */
871: context.head = &head;
872: context.foot = &foot;
873: context.margin = NULL;
874: context.pd = NULL;
875: context.id = TABID_PRINTER;
876:
877: /* header is filenames or just WinDiff if no names known*/
878: if (strlen(AppTitle) > 0) {
879: head.ptext = AppTitle;
880: } else {
881: head.ptext = "WinDiff";
882: }
883:
884: /* header is centred, footer is right-aligned and
885: * consists of the page number
886: */
887: head.props.valid = P_ALIGN;
888: head.props.alignment = P_CENTRE;
889: foot.ptext = "Page # of $";
890: foot.props.valid = P_ALIGN;
891: foot.props.alignment = P_RIGHT;
892:
893: SendMessage(hwndRCD, TM_PRINT, 0, (DWORD) (LPSTR) &context);
894: }
895:
896: /***************************************************************************
897: * Function: FindNextChange
898: *
899: * Purpose:
900: *
901: * Find the next line in the current view that is
902: * not STATE_SAME. Start from the current selection, if valid, or
903: * from the top of the window if no selection.
904: *
905: */
906: BOOL
907: FindNextChange(void)
908: {
909: long row;
910:
911: /* start from the selection or top of the window if no selection */
912: if (selection >= 0) {
913: row = selection + 1;
914: } else {
915: row = (int) SendMessage(hwndRCD, TM_TOPROW, FALSE, 0);
916: }
917:
918:
919: /* find the next 'interesting' line */
920: row = view_findchange(current_view, row, TRUE);
921: if (row >= 0) {
922: SetSelection(row);
923: return(TRUE);
924: } else {
925: windiff_UI(TRUE);
926: MessageBox(hwndClient, "No More Changes", "Windiff",
927: MB_ICONINFORMATION|MB_OK);
928: windiff_UI(FALSE);
929:
930: return(FALSE);
931: }
932: }
933:
934: /***************************************************************************
935: * Function: FindPrevChange
936: *
937: * Purpose:
938: *
939: * Find the previous line in the current view that is not STATE_SAME
940: */
941: BOOL
942: FindPrevChange(void)
943: {
944: long row;
945:
946: /* start from the selection or top of window if no selection */
947: if (selection >= 0) {
948: row = selection - 1;
949: } else {
950: row = (int) SendMessage(hwndRCD, TM_TOPROW, FALSE, 0);
951: }
952:
953: /* find the previous 'interesting' line */
954: row = view_findchange(current_view, row, FALSE);
955: if (row >= 0) {
956: SetSelection(row);
957: return(TRUE);
958: } else {
959: windiff_UI(TRUE);
960: MessageBox(hwndClient, "No Previous Changes", "Windiff",
961: MB_ICONINFORMATION|MB_OK);
962: windiff_UI(FALSE);
963:
964: return(FALSE);
965: }
966:
967: }
968: /***************************************************************************
969: * Function: WriteProfileInt
970: *
971: */
972:
973: BOOL WriteProfileInt(LPSTR AppName, LPSTR Key, int Int)
974: { char Str[40];
975:
976: wsprintf((LPTSTR)Str, "%d", Int);
977: return WriteProfileString(AppName, Key, Str);
978:
979: } /* WriteProfileInt */
980:
981:
982: /***************************************************************************
983: * Function: ToExpand
984: *
985: * Purpose:
986: *
987: * Switch to expand view of the selected line
988: */
989: BOOL
990: ToExpand(HWND hwnd)
991: {
992: if (selection < 0) {
993: return(FALSE);
994: }
995:
996: if (!view_isexpanded(current_view)) {
997: /* save the current outline size and position */
998: WINDOWPLACEMENT wp;
999: if (GetWindowPlacement(hwndClient,&wp)) {
1000: WriteProfileInt(APPNAME, "OutlineShowCmd", wp.showCmd);
1001: WriteProfileInt(APPNAME, "OutlineMaxX", wp.ptMaxPosition.x);
1002: WriteProfileInt(APPNAME, "OutlineMaxY", wp.ptMaxPosition.y);
1003: WriteProfileInt(APPNAME, "OutlineNormLeft", wp.rcNormalPosition.left);
1004: WriteProfileInt(APPNAME, "OutlineNormTop", wp.rcNormalPosition.top);
1005: WriteProfileInt(APPNAME, "OutlineNormRight", wp.rcNormalPosition.right);
1006: WriteProfileInt(APPNAME, "OutlineNormBottom", wp.rcNormalPosition.bottom);
1007: WriteProfileInt(APPNAME, "OutlineSaved", 1);
1008: }
1009:
1010: /* restore the previous expanded size and position, if any */
1011: if (GetProfileInt(APPNAME, "ExpandedSaved", 0)) {
1012: wp.flags = 0;
1013: wp.showCmd
1014: = GetProfileInt( APPNAME, "ExpandShowCmd"
1015: , SW_SHOWMAXIMIZED);
1016: wp.ptMaxPosition.x
1017: = GetProfileInt( APPNAME, "ExpandMaxX", 0);
1018: wp.ptMaxPosition.y
1019: = GetProfileInt( APPNAME, "ExpandMaxY", 0);
1020: wp.rcNormalPosition.left
1021: = GetProfileInt( APPNAME, "ExpandNormLeft"
1022: , wp.rcNormalPosition.left);
1023: wp.rcNormalPosition.top
1024: = GetProfileInt( APPNAME, "ExpandNormTop"
1025: , wp.rcNormalPosition.top);
1026: wp.rcNormalPosition.right
1027: = GetProfileInt( APPNAME, "ExpandNormRight"
1028: , wp.rcNormalPosition.right);
1029: wp.rcNormalPosition.bottom
1030: = GetProfileInt( APPNAME, "ExpandNormBottom"
1031: , wp.rcNormalPosition.bottom);
1032: SetWindowPlacement(hwndClient,&wp);
1033: }
1034: else ShowWindow(hwndClient, SW_SHOWMAXIMIZED);
1035: }
1036:
1037: /*change the view mapping to expand mode */
1038: if (view_expand(current_view, selection)) {
1039:
1040: /* ok - we now have an expanded view - change status
1041: * to show this
1042: */
1043:
1044: DisplayMode = MODE_EXPAND;
1045:
1046: /* resize to show the graphic bar picture */
1047: DoResize(hwndClient);
1048:
1049:
1050: /* change button,status text-if we are not still busy*/
1051: if (!fBusy) {
1052:
1053: /* the status field when we are expanded shows the
1054: * tag field (normally the file name) for the
1055: * item we are expanding
1056: */
1057: SetStatus(view_getcurrenttag(current_view) );
1058: SetButtonText("Outline");
1059: }
1060:
1061: return(TRUE);
1062: }
1063: return(FALSE);
1064: } /* ToExpand */
1065:
1066: /***************************************************************************
1067: * Function: ToOutline
1068: *
1069: * Purpose:
1070: *
1071: * Switch back to outline view - showing just the list of file names.
1072: */
1073: void
1074: ToOutline(HWND hwnd)
1075: {
1076: if (view_isexpanded(current_view)) {
1077: /* save the current expanded size and position */
1078: WINDOWPLACEMENT wp;
1079: if (GetWindowPlacement(hwndClient,&wp)) {
1080: WriteProfileInt(APPNAME, "ExpandShowCmd", wp.showCmd);
1081: WriteProfileInt(APPNAME, "ExpandMaxX", wp.ptMaxPosition.x);
1082: WriteProfileInt(APPNAME, "ExpandMaxY", wp.ptMaxPosition.y);
1083: WriteProfileInt(APPNAME, "ExpandNormLeft", wp.rcNormalPosition.left);
1084: WriteProfileInt(APPNAME, "ExpandNormTop", wp.rcNormalPosition.top);
1085: WriteProfileInt(APPNAME, "ExpandNormRight", wp.rcNormalPosition.right);
1086: WriteProfileInt(APPNAME, "ExpandNormBottom", wp.rcNormalPosition.bottom);
1087: WriteProfileInt(APPNAME, "ExpandedSaved", 1);
1088: }
1089:
1090: /* restore the previous expanded size and position, if any */
1091: if (GetProfileInt(APPNAME, "OutlineSaved", 0)) {
1092: wp.flags = 0;
1093: wp.showCmd
1094: = GetProfileInt( APPNAME, "OutlineShowCmd"
1095: , SW_SHOWNORMAL);
1096: wp.ptMaxPosition.x
1097: = GetProfileInt( APPNAME, "OutlineMaxX", 0);
1098: wp.ptMaxPosition.y
1099: = GetProfileInt( APPNAME, "OutlineMaxY", 0);
1100: wp.rcNormalPosition.left
1101: = GetProfileInt( APPNAME, "OutlineNormLeft"
1102: , wp.rcNormalPosition.left);
1103: wp.rcNormalPosition.top
1104: = GetProfileInt( APPNAME, "OutlineNormTop"
1105: , wp.rcNormalPosition.top);
1106: wp.rcNormalPosition.right
1107: = GetProfileInt( APPNAME, "OutlineNormRight"
1108: , wp.rcNormalPosition.right);
1109: wp.rcNormalPosition.bottom
1110: = GetProfileInt( APPNAME, "OutlineNormBottom"
1111: , wp.rcNormalPosition.bottom);
1112: SetWindowPlacement(hwndClient,&wp);
1113: }
1114: ShowWindow(hwndClient, SW_SHOWNORMAL);
1115: }
1116:
1117: DisplayMode = MODE_OUTLINE;
1118:
1119: /* switch mapping back to outline view */
1120: view_outline(current_view);
1121:
1122: /* hide bar window and resize to cover */
1123: DoResize(hwndClient);
1124:
1125:
1126: /* change label on button */
1127: if (!fBusy) {
1128: SetButtonText("Expand");
1129: SetStatus(NULL);
1130: }
1131: } /* ToOutline */
1132:
1133: /***************************************************************************
1134: * Function: ToMoved
1135: *
1136: * Purpose:
1137: *
1138: * If the user clicks on a MOVED line in expand mode, we jump to the
1139: * other line. We return TRUE if this was possible, or FALSE otherwise.
1140: */
1141: BOOL
1142: ToMoved(HWND hwnd)
1143: {
1144: BOOL bIsLeft;
1145: int linenr, state;
1146: long i, total;
1147:
1148: if (DisplayMode != MODE_EXPAND) {
1149: return(FALSE);
1150: }
1151: if (selection < 0) {
1152: return(FALSE);
1153: }
1154:
1155: state = view_getstate(current_view, selection);
1156: if (state == STATE_MOVEDLEFT) {
1157: bIsLeft = TRUE;
1158: /* get the linenr of the other copy */
1159: linenr = abs(view_getlinenr_right(current_view, selection));
1160: } else if (state == STATE_MOVEDRIGHT) {
1161: bIsLeft = FALSE;
1162: /* get the linenr of the other copy */
1163: linenr = abs(view_getlinenr_left(current_view, selection));
1164: } else {
1165: /* not a moved line - so we can't find another copy */
1166: return(FALSE);
1167: }
1168:
1169: /* search the view for this line nr */
1170: total = view_getrowcount(current_view);
1171: for (i = 0; i < total; i++) {
1172: if (bIsLeft) {
1173: if (linenr == view_getlinenr_right(current_view, i)) {
1174: /* found it */
1175: SetSelection(i);
1176: return(TRUE);
1177: }
1178: } else {
1179: if (linenr == view_getlinenr_left(current_view, i)) {
1180: SetSelection(i);
1181: return(TRUE);
1182: }
1183: }
1184: }
1185: return(FALSE);
1186: }
1187:
1188: /***************************************************************************
1189: * Function: do_editfile
1190: *
1191: * Purpose:
1192: *
1193: * Launch an editor on the current file (the file we are expanding, or
1194: * in outline mode the selected row. Option allows selection of the
1195: * left file, the right file or the composite view of this item.
1196: * pe points to a packet of parameters that must be freed before returning.
1197: * The return value is meaningless (just to conform to CreateThread).
1198: */
1199: LONG
1200: do_editfile(PEDITARGS pe)
1201: {
1202: VIEW view = pe->view;
1203: int option = pe->option;
1204: int selection = pe->selection;
1205:
1206: COMPITEM item;
1207: LPSTR fname;
1208: char cmdline[256];
1209: int currentline;
1210: char * pOut = cmdline;
1211: char * pIn = editor_cmdline;
1212:
1213: STARTUPINFO si;
1214: PROCESS_INFORMATION pi;
1215:
1216: item = view_getitem(view, selection);
1217: if (item == NULL) {
1218: return -1;
1219: }
1220:
1221: fname = compitem_getfilename(item, option);
1222:
1223: if ( 0 == fname )
1224: {
1225: windiff_UI(TRUE);
1226: MessageBox(hwndClient, "File does not exist.",
1227: "Windiff", MB_ICONSTOP|MB_OK);
1228: windiff_UI(FALSE);
1229: goto error;
1230: }
1231:
1232: switch ( option )
1233: {
1234: case CI_LEFT:
1235: currentline = view_getlinenr_left( view,
1236: selection > 0 ? selection : 1);
1237: break;
1238:
1239: case CI_RIGHT:
1240: currentline = view_getlinenr_right( view,
1241: selection > 0 ? selection : 1);
1242: break;
1243:
1244: default:
1245: currentline = 1;
1246: break;
1247: }
1248:
1249: while( *pIn )
1250: {
1251: switch( *pIn )
1252: {
1253: case '%':
1254: pIn++;
1255: switch ( *pIn )
1256: {
1257: case 'p':
1258: lstrcpy( (LPTSTR)pOut, fname );
1259: while ( *pOut )
1260: pOut++;
1261: break;
1262:
1263: case 'l':
1264: _ltoa( currentline, pOut, 10 );
1265: while ( *pOut )
1266: pOut++;
1267: break;
1268:
1269: default:
1270: *pOut++ = *pIn;
1271: break;
1272: }
1273: pIn++;
1274: break;
1275:
1276: default:
1277: *pOut++ = *pIn++;
1278: break;
1279: }
1280: }
1281:
1282:
1283: /* Launch the process and waits for it to complete */
1284:
1285: si.cb = sizeof(STARTUPINFO);
1286: si.lpReserved = NULL;
1287: si.lpReserved2 = NULL;
1288: si.cbReserved2 = 0;
1289: si.lpTitle = (LPSTR)cmdline;
1290: si.lpDesktop = (LPTSTR)NULL;
1291: si.dwFlags = STARTF_FORCEONFEEDBACK;
1292:
1293:
1294: if (!CreateProcess(NULL,
1295: cmdline,
1296: NULL,
1297: NULL,
1298: FALSE,
1299: NORMAL_PRIORITY_CLASS,
1300: NULL,
1301: (LPTSTR)NULL,
1302: &si,
1303: &pi)) {
1304: windiff_UI(TRUE);
1305: MessageBox(hwndClient, "Failed to launch editor",
1306: "Windiff", MB_ICONSTOP|MB_OK);
1307: windiff_UI(FALSE);
1308: goto error;
1309: }
1310:
1311: /* wait for completion. */
1312: WaitForSingleObject(pi.hProcess, INFINITE);
1313:
1314: /* close process and thread handles */
1315: CloseHandle(pi.hThread);
1316: CloseHandle(pi.hProcess);
1317:
1318: /* finished with the filename. deletes it if it was a temp. */
1319: compitem_freefilename(item, option, fname);
1320:
1321: /*
1322: * refresh cached view always . A common trick is to edit the
1323: * composite file and then save it as a new left or right file.
1324: * Equally the user can edit the left and save as a new right.
1325: */
1326:
1327: /* We want to force both files to be re-read, but it's not a terribly
1328: * good idea to throw the lines away on this thread. Someone might
1329: * be reading them on another thread!
1330: */
1331: /* file_discardlines(compitem_getleftfile(item)) */
1332: /* file_discardlines(compitem_getrightfile(item)) */
1333:
1334: /* force the compare to be re-done */
1335: PostMessage(hwndClient, WM_COMMAND, IDM_UPDATE, (LONG)item);
1336: error:
1337: gmem_free(hHeap, (LPSTR) pe, sizeof(EDITARGS));
1338:
1339: return 0;
1340:
1341: } /* do_editfile */
1342:
1343:
1344: /***************************************************************************
1345: * Function: do_editthread
1346: *
1347: * Purpose:
1348: *
1349: * Launch an editor on a separate thread. It will actually get a separate
1350: * process, but we want our own thread in this process. This thread will
1351: * wait until it's finished and then order up a refresh of the UI.
1352: * Need to give it its parameters as a gmem allocated packet because
1353: * it IS on a separate thread.
1354: */
1355: void do_editthread(VIEW view, int option)
1356: {
1357: PEDITARGS pe;
1358: HANDLE thread;
1359: DWORD threadid;
1360:
1361: pe = (PEDITARGS) gmem_get(hHeap, sizeof(EDITARGS));
1362: pe->view = view;
1363: pe->option = option;
1364: pe->selection = selection;
1365:
1366: thread = CreateThread( NULL
1367: , 0
1368: , (LPTHREAD_START_ROUTINE)do_editfile
1369: , (LPVOID) pe
1370: , 0
1371: , &threadid
1372: );
1373: if (thread == NULL)
1374: {
1375: /* The createthread failed, do without the extra thread - just
1376: * call the function synchronously
1377: */
1378: do_editfile(pe);
1379: }
1380: else CloseHandle(thread);
1381: } /* do_editthread */
1382:
1383:
1384: /* status bar and busy flags --------------------------------------------*/
1385:
1386:
1387: /***************************************************************************
1388: * Function: SetButtonText
1389: *
1390: * Purpose:
1391: *
1392: * Set the Text on the statusbar button to reflect the current state
1393: */
1394: void
1395: SetButtonText(LPSTR cmd)
1396: {
1397: SendMessage(hwndStatus, SM_SETTEXT, IDM_ABORT, (DWORD) cmd);
1398: }
1399:
1400: /***************************************************************************
1401: * Function: SetStatus
1402: *
1403: * Purpose:
1404: *
1405: * Set the status field (left-hand part) of the status bar.
1406: */
1407: void
1408: SetStatus(LPSTR cmd)
1409: {
1410: SendMessage(hwndStatus, SM_SETTEXT, IDL_STATLAB, (DWORD) cmd);
1411: }
1412:
1413:
1414: /***************************************************************************
1415: * Function: SetNames
1416: *
1417: * Purpose:
1418: *
1419: * Set the names field - the central box in the status bar
1420: */
1421: void
1422: SetNames(LPSTR names)
1423: {
1424: SendMessage(hwndStatus, SM_SETTEXT, IDL_NAMES, (DWORD) names);
1425: if (names == NULL) {
1426: AppTitle[0] = '\0';
1427: } else {
1428: strncpy(AppTitle, names, sizeof(AppTitle));
1429: }
1430: }
1431:
1432: /***************************************************************************
1433: * Function: SetBusy
1434: *
1435: * Purpose:
1436: *
1437: * If we are not already busy, set the busy flag.
1438: *
1439: * Enter critical section first.
1440: */
1441: BOOL
1442: SetBusy(void)
1443: {
1444: HMENU hmenu;
1445:
1446:
1447: WDEnter();
1448:
1449: if (fBusy) {
1450: WDLeave();
1451: return(FALSE);
1452: }
1453:
1454:
1455: fBusy = TRUE;
1456:
1457: SetStatus("Comparing...");
1458: /* status also on window text, so that you can see even from
1459: * the icon when the scan has finished
1460: */
1461: SetWindowText(hwndClient, "WinDiff: scanning");
1462:
1463: /* disable appropriate parts of menu */
1464: hmenu = GetMenu(hwndClient);
1465: EnableMenuItem(hmenu, IDM_FILE,MF_DISABLED|MF_GRAYED|MF_BYCOMMAND);
1466: EnableMenuItem(hmenu, IDM_DIR,MF_DISABLED|MF_GRAYED|MF_BYCOMMAND);
1467: EnableMenuItem(hmenu, IDM_PRINT,MF_DISABLED|MF_GRAYED|MF_BYCOMMAND);
1468:
1469: /* enable abort only when busy */
1470: EnableMenuItem(hmenu, IDM_ABORT,MF_ENABLED|MF_BYCOMMAND);
1471: SetButtonText("Abort"); /* leave DisplayMode unchanged */
1472:
1473: WDLeave();
1474: return(TRUE);
1475: } /* SetBusy */
1476: /***************************************************************************
1477: * Function: SetNotBusy
1478: *
1479: * Purpose:
1480: *
1481: * This function can be called from the worker thread.
1482: * Thus we must not cause any SendMessage calls to windows
1483: * owned by the main thread while holding the CritSec or we
1484: * could cause deadlock.
1485: *
1486: * The critsec is only needed to protect the fBusy flag - so
1487: * clear the busy flag last, and only get the crit sec as needed.
1488: */
1489: void
1490: SetNotBusy(void)
1491: {
1492: HMENU hmenu;
1493:
1494: /* reset button and status bar (clearing out busy flags) */
1495: if (current_view == NULL) {
1496: SetButtonText("Exit");
1497: SetStatus(NULL);
1498: DisplayMode = MODE_NULL;
1499: } else if (view_isexpanded(current_view)) {
1500: SetButtonText("Outline");
1501: SetStatus(view_getcurrenttag(current_view) );
1502: DisplayMode = MODE_EXPAND;
1503: } else {
1504: SetButtonText("Expand");
1505: SetStatus(NULL);
1506: DisplayMode = MODE_OUTLINE;
1507: }
1508:
1509: SetWindowText(hwndClient, "WinDiff");
1510:
1511: /* re-enable appropriate parts of menu */
1512: hmenu = GetMenu(hwndClient);
1513: EnableMenuItem(hmenu, IDM_FILE,MF_ENABLED|MF_BYCOMMAND);
1514: EnableMenuItem(hmenu, IDM_DIR,MF_ENABLED|MF_BYCOMMAND);
1515: EnableMenuItem(hmenu, IDM_PRINT,MF_ENABLED|MF_BYCOMMAND);
1516:
1517: /* disable abort now no longer busy */
1518: EnableMenuItem(hmenu, IDM_ABORT,MF_DISABLED|MF_GRAYED|MF_BYCOMMAND);
1519:
1520:
1521: /* clear the busy flag, protected by critical section */
1522: WDEnter();
1523:
1524: fBusy = FALSE;
1525: bAbort = FALSE;
1526:
1527: if (ghThread!=NULL){
1528: CloseHandle(ghThread);
1529: ghThread = NULL;
1530: }
1531: WDLeave();
1532: } /* SetNotBusy */
1533:
1534: /***************************************************************************
1535: * Function: IsBusy
1536: *
1537: * Purpose:
1538: *
1539: * Checks whether or not crit sec is open
1540: */
1541: BOOL
1542: IsBusy()
1543: {
1544: BOOL bOK;
1545:
1546: WDEnter();
1547: bOK = fBusy;
1548: WDLeave();
1549: return(bOK);
1550: } /* IsBusy */
1551:
1552: /***************************************************************************
1553: * Function: BusyError
1554: *
1555: * Purpose:
1556: *
1557: * Puts up message box that system is busy.
1558: */
1559: void
1560: BusyError(void)
1561: {
1562: windiff_UI(TRUE);
1563: MessageBox(hwndClient,
1564: "Please wait for current operation to finish",
1565: "WinDiff", MB_OK|MB_ICONSTOP);
1566: windiff_UI(FALSE);
1567: } /* BusyError */
1568:
1569: /* --- colour scheme --------------------------------------------------- */
1570:
1571: /***************************************************************************
1572: * Function: StateToColour
1573: *
1574: * Purpose:
1575: *
1576: * Map the state given into a foreground and a background colour
1577: * for states that are highlighted. Return P_FCOLOUR if the foreground
1578: * colour (put in *foreground) is to be used, return P_FCOLOUR|P_BCOLOUR if
1579: * both *foreground and *background are to be used, or 0 if the default
1580: * colours are to be used.
1581: */
1582: UINT
1583: StateToColour(int state, int col, DWORD FAR * foreground, DWORD FAR * background)
1584: {
1585:
1586:
1587: switch (state) {
1588:
1589: case STATE_DIFFER:
1590: /* files that differ are picked out in a foreground highlight,
1591: * with the default background
1592: */
1593: *foreground = rgb_outlinehi;
1594: return(P_FCOLOUR);
1595:
1596: case STATE_LEFTONLY:
1597: /* lines only in the left file */
1598: *foreground = rgb_leftfore;
1599: *background = rgb_leftback;
1600: return(P_FCOLOUR|P_BCOLOUR);
1601:
1602: case STATE_RIGHTONLY:
1603: /* lines only in the right file */
1604: *foreground = rgb_rightfore;
1605: *background = rgb_rightback;
1606: return(P_FCOLOUR|P_BCOLOUR);
1607:
1608: case STATE_MOVEDLEFT:
1609: /* displaced lines in both files - left file version */
1610: *foreground = rgb_mleftfore;
1611: *background = rgb_mleftback;
1612: return(P_FCOLOUR|P_BCOLOUR);
1613:
1614: case STATE_MOVEDRIGHT:
1615: /* displaced lines in both files - right file version */
1616: *foreground = rgb_mrightfore;
1617: *background = rgb_mrightback;
1618: return(P_FCOLOUR|P_BCOLOUR);
1619:
1620: default:
1621:
1622: /* no highlighting - default colours */
1623: return(0);
1624: }
1625:
1626: }
1627:
1628: /* table window communication routines ---------------------------------*/
1629:
1630: /***************************************************************************
1631: * Function: SetSelection
1632: *
1633: * Purpose:
1634: *
1635: * Set a given row as the selected row in the table window
1636: */
1637: void
1638: SetSelection(long rownr)
1639: {
1640: TableSelection select;
1641:
1642: select.startrow = rownr;
1643: select.startcell = 0;
1644: select.nrows = 1;
1645: select.ncells = 1;
1646: SendMessage(hwndRCD, TM_SELECT, 0, (long) (LPSTR)&select);
1647: }
1648:
1649:
1650: /***************************************************************************
1651: * Function: do_gethdr
1652: *
1653: * Purpose:
1654: *
1655: * Handle table class call back to get nr of rows and columns,
1656: * and properties for the whole table.
1657: * The 'table id' is either TABID_PRINTER - meaning we are
1658: * printing the current_view, or it is the view to
1659: * use for row/column nr information
1660: */
1661: long
1662: do_gethdr(HWND hwnd, lpTableHdr phdr)
1663: {
1664: VIEW view;
1665: BOOL bIsPrinter = FALSE;
1666:
1667: if (phdr->id == TABID_PRINTER) {
1668: view = current_view;
1669: bIsPrinter = TRUE;
1670: } else {
1671: view = (VIEW) phdr->id;
1672: }
1673: if (view == NULL) {
1674: return(FALSE);
1675: }
1676:
1677: phdr->nrows = view_getrowcount(view);
1678:
1679: /* three columns: line nr, tag and rest of line */
1680:
1681: /*
1682: * if IDM_NONRS (no line numbers) is selected, suppress the
1683: * line-nr column entirely to save screen space
1684: */
1685: if (line_numbers == IDM_NONRS) {
1686: phdr->ncols = 2;
1687: phdr->fixedcols = 0;
1688: } else {
1689: phdr->ncols = 3;
1690: phdr->fixedcols = 1;
1691: }
1692:
1693: phdr->fixedrows = 0;
1694: phdr->fixedselectable = FALSE;
1695: phdr->hseparator = TRUE;
1696: phdr->vseparator = TRUE;
1697:
1698: phdr->selectmode = TM_ROW | TM_SINGLE;
1699: /*
1700: * find if we are in expand mode - ask for the item we are expanding.
1701: */
1702: if (view_isexpanded(view) == TRUE) {
1703:
1704: /* use focus rect as selection mode in expand mode
1705: * so as not to interfere with background colours.
1706: */
1707: phdr->selectmode |= TM_FOCUS;
1708: } else {
1709: /* use default solid inversion when possible as it is clearer.*/
1710: phdr->selectmode |= TM_SOLID;
1711: }
1712:
1713: /* please send TQ_SCROLL notifications when the table is scrolled */
1714: phdr->sendscroll = TRUE;
1715: phdr->props.valid = 0;
1716:
1717: return TRUE;
1718: }
1719:
1720: /***************************************************************************
1721: * Function: do_getprops
1722: *
1723: * Purpose:
1724: *
1725: * Respond to table callback asking for the size and properties
1726: * of each column. Table id is either TABID_PRINTER (meaning the
1727: * current_view, for printing) or it is the view to be used.
1728: */
1729: long
1730: do_getprops(HWND hwnd, lpColPropsList propslist)
1731: {
1732: int i, cell;
1733: BOOL bIsPrinter = FALSE;
1734: VIEW view;
1735:
1736: if (propslist->id == TABID_PRINTER) {
1737: view = current_view;
1738: bIsPrinter = TRUE;
1739: } else {
1740: view = (VIEW) propslist->id;
1741: }
1742: if (view == NULL) {
1743: return(FALSE);
1744: }
1745:
1746: /* The table inteface is slightly confused here. we are not
1747: * guaranteed which columns we are being asked about, so instead
1748: * of just setting each column cols[0], cols[1] etc, we need
1749: * to loop through, looking at each column in the table and
1750: * seeing which it is.
1751: */
1752: for (i = 0; i < propslist->ncols; i++) {
1753: cell = i + propslist->startcol;
1754: propslist->plist[i].props.valid = 0;
1755:
1756: /* for all column widths, add on 1 for the NULL char. */
1757:
1758: /*
1759: * skip the line nr column if IDM_NONRS
1760: */
1761: if (line_numbers == IDM_NONRS) {
1762: cell++;
1763: }
1764:
1765: if (cell == 0) {
1766: /* properties for line nr column */
1767:
1768: propslist->plist[i].nchars = view_getwidth(view, 0)+1;
1769: propslist->plist[i].props.valid |= P_ALIGN;
1770: propslist->plist[i].props.alignment = P_CENTRE;
1771: } else if (cell == 1) {
1772:
1773: /* properties for tag field */
1774: propslist->plist[i].nchars = view_getwidth(view, 1)+1;
1775: propslist->plist[i].props.valid |= P_ALIGN;
1776: propslist->plist[i].props.alignment = P_LEFT;
1777: } else {
1778: /* properties for main text column -
1779: * use a fixed font unless printing (if
1780: * printing, best to use the default font, because
1781: * of resolution differences.
1782: * add on 8 chars to the width to ensure that
1783: * the width of lines beginning with tabs
1784: * works out ok
1785: */
1786: propslist->plist[i].nchars = view_getwidth(view, 2)+1;
1787: propslist->plist[i].props.valid |= P_ALIGN;
1788: propslist->plist[i].props.alignment = P_LEFT;
1789: if (!bIsPrinter) {
1790: propslist->plist[i].props.valid |= P_FONT;
1791: propslist->plist[i].props.hFont =
1792: GetStockObject(SYSTEM_FIXED_FONT);
1793: }
1794: }
1795: }
1796: return (TRUE);
1797: }
1798:
1799: /***************************************************************************
1800: * Function: do_getdata
1801: *
1802: * Purpose:
1803: *
1804: * Respond to a table callback asking for the contents of individual cells.
1805: * table id is either TABID_PRINTER, or it is a pointer to the view
1806: * to use for data. If going to the printer, don't set the
1807: * colours (stick to black and white).
1808: */
1809: long
1810: do_getdata(HWND hwnd, lpCellDataList cdlist)
1811: {
1812: int start, endcell, col, i;
1813: lpCellData cd;
1814: VIEW view;
1815: LPSTR textp;
1816: BOOL bIsPrinter = FALSE;
1817:
1818: if (cdlist->id == TABID_PRINTER) {
1819: view = current_view;
1820: bIsPrinter = TRUE;
1821: } else {
1822: view = (VIEW) cdlist->id;
1823: }
1824:
1825: start = cdlist->startcell;
1826: endcell = cdlist->ncells + start;
1827: if (cdlist->row >= view_getrowcount(view)) {
1828: return(FALSE);
1829: }
1830: for (i = start; i < endcell; i++) {
1831: cd = &cdlist->plist[i - start];
1832:
1833:
1834: /* skip the line number column if IDM_NONRS */
1835: if (line_numbers == IDM_NONRS) {
1836: col = i+1;
1837: } else {
1838: col = i;
1839: }
1840:
1841: /* set colour of text to mark out
1842: * lines that are changed, if not printer - for the
1843: * printer everything should stay in the default colours
1844: */
1845:
1846: if (!bIsPrinter) {
1847:
1848: /* convert the state of the requested row into a
1849: * colour scheme. returns P_FCOLOUR and/or
1850: * P_BCOLOUR if it sets either of the colours
1851: */
1852: cd->props.valid |=
1853: StateToColour(view_getstate(view, cdlist->row), col,
1854: &cd->props.forecolour,
1855: &cd->props.backcolour);
1856: }
1857:
1858: textp = view_gettext(view, cdlist->row, col);
1859: if (cd->nchars != 0) {
1860: if (textp == NULL) {
1861: cd->ptext[0] = '\0';
1862: } else {
1863: strncpy(cd->ptext, textp, cd->nchars -1);
1864: cd->ptext[cd->nchars - 1] = '\0';
1865: }
1866: }
1867:
1868: }
1869: return(TRUE);
1870: }
1871:
1872: /***************************************************************************
1873: * Function: SvrClose
1874: *
1875: * Purpose:
1876: *
1877: * Table window has finished with this view. It can be deleted.
1878: */
1879: void
1880: SvrClose(void)
1881: {
1882: view_delete(current_view);
1883: current_view = NULL;
1884:
1885: /* hide picture - only visible when we are in MODE_EXPAND */
1886: DisplayMode = MODE_NULL;
1887: DoResize(hwndClient);
1888:
1889: /* if we already busy when closing this view (ie
1890: * we are in the process of starting a new scan,
1891: * then leave the status bar alone, otherwise
1892: * we should clean up the state of the status bar
1893: */
1894: if (!fBusy) {
1895: SetButtonText("Exit");
1896: SetNames(NULL);
1897: SetStatus(NULL);
1898:
1899: }
1900:
1901: } /* SvrClose */
1902:
1903:
1904: /***************************************************************************
1905: * Function: TableServer
1906: *
1907: * Purpose:
1908: *
1909: * Handle callbacks and notifications from the table class
1910: */
1911: long
1912: TableServer(HWND hwnd, UINT cmd, long lParam)
1913: {
1914: lpTableHdr phdr;
1915: lpColPropsList proplist;
1916: lpCellDataList cdlist;
1917: lpTableSelection pselect;
1918:
1919: switch(cmd) {
1920: case TQ_GETSIZE:
1921: /* get the nr of rows and cols in this table */
1922: phdr = (lpTableHdr) lParam;
1923: return(do_gethdr(hwnd, phdr));
1924:
1925: case TQ_GETCOLPROPS:
1926: /* get the size and properties of each column */
1927: proplist = (lpColPropsList) lParam;
1928: return (do_getprops(hwnd, proplist));
1929:
1930: case TQ_GETDATA:
1931: /* get the contents of individual cells */
1932: cdlist = (lpCellDataList) lParam;
1933: return (do_getdata(hwnd, cdlist));
1934:
1935:
1936: case TQ_SELECT:
1937: /* selection has changed */
1938: case TQ_ENTER:
1939: /* user has double-clicked or pressed enter */
1940:
1941: pselect = (lpTableSelection) lParam;
1942:
1943: /* store location for use in later search (IDM_FCHANGE) */
1944: if (pselect->nrows < 1) {
1945: selection = -1;
1946: } else {
1947: selection = (int) pselect->startrow;
1948: if (cmd == TQ_ENTER) {
1949: /* try to expand this row */
1950: if (!ToExpand(hwnd)) {
1951: /* expand failed - maybe this
1952: * is a moved line- show the other
1953: * copy
1954: */
1955: ToMoved(hwnd);
1956: }
1957:
1958: }
1959: }
1960: break;
1961:
1962: case TQ_CLOSE:
1963: /* close this table - table class no longer needs data*/
1964: SvrClose();
1965: break;
1966:
1967: case TQ_SCROLL:
1968: /* notification that the rows visible in the window
1969: * have changed -change the current position lines in
1970: * the graphic bar view (the sections picture)
1971: */
1972: if (picture_mode) {
1973: BarDrawPosition(hwndBar, NULL, TRUE);
1974: }
1975: break;
1976:
1977: default:
1978: return(FALSE);
1979: }
1980: return(TRUE);
1981: }
1982:
1983:
1984: /***************************************************************************
1985: * Function: wd_initial
1986: *
1987: * Purpose:
1988: *
1989: * Called on worker thread (not UI thread) to handle the work
1990: * requested on the command line.
1991: * arg is a pointer to a THREADARGS block allocated from gmem_get(hHeap). This
1992: * needs to be freed before exiting.
1993: */
1994: DWORD
1995: wd_initial(LPVOID arg)
1996: {
1997: PTHREADARGS pta = (PTHREADARGS) arg;
1998: COMPLIST cl;
1999:
2000:
2001: /* build a complist from these args,
2002: * and register with the view we have made
2003: */
2004: cl = complist_args(pta->first, pta->second, pta->view, pta->fDeep);
2005:
2006: if (cl == NULL) {
2007: view_close(pta->view);
2008: gmem_free(hHeap, (LPSTR) pta, sizeof(THREADARGS));
2009: SetNotBusy();
2010: return 0;
2011: }
2012:
2013:
2014: /* if savelist was selected, write out the list and exit */
2015: if(pta->savelist != NULL) {
2016: complist_savelist(cl, pta->savelist, pta->saveopts);
2017: gmem_free(hHeap, (LPSTR) pta, sizeof(THREADARGS));
2018: SetNotBusy();
2019: exit(0);
2020: }
2021:
2022: /* if there was only one file, expand it */
2023: if (view_getrowcount(pta->view) == 1) {
2024: SetSelection(0);
2025: ToExpand(hwndClient);
2026: }
2027:
2028:
2029: gmem_free(hHeap, (LPSTR) pta, sizeof(THREADARGS));
2030: SetNotBusy();
2031: return(0);
2032: } /* wd_initial */
2033:
2034:
2035: /***************************************************************************
2036: * Function: wd_dirdialog
2037: *
2038: * Purpose:
2039: *
2040: * Called on worker thread (not UI thread) to handle a Dir request
2041: */
2042: DWORD
2043: wd_dirdialog(LPVOID arg)
2044: {
2045:
2046: VIEW view = (VIEW) arg;
2047:
2048: /* make a COMPLIST using the directory dialog,
2049: * and notify the view
2050: */
2051: if (complist_dirdialog(view) == NULL) {
2052: view_close(view);
2053: }
2054:
2055: /* all done! */
2056: SetNotBusy();
2057: return(0);
2058: }
2059:
2060:
2061: /***************************************************************************
2062: * Function: wd_copy
2063: *
2064: * Purpose:
2065: *
2066: * Called on worker thread to do a copy-files operation
2067: */
2068: DWORD
2069: wd_copy(LPVOID arg)
2070: {
2071:
2072: VIEW view = (VIEW) arg;
2073:
2074: complist_copyfiles(view_getcomplist(view), NULL, 0);
2075:
2076: SetNotBusy();
2077:
2078: return(0);
2079: }
2080:
2081:
2082: /***************************************************************************
2083: * Function: MainWndProc
2084: *
2085: * Purpose:
2086: *
2087: * Window processing for main window
2088: */
2089: long APIENTRY
2090: MainWndProc(HWND hWnd, UINT message, UINT wParam, LONG lParam)
2091: {
2092: char str[32];
2093: long ret;
2094: DWORD threadid;
2095:
2096: switch(message) {
2097:
2098:
2099: case WM_CREATE:
2100:
2101: /* initialise menu options to default/saved
2102: * option settings
2103: */
2104:
2105: CheckMenuItem(hMenu, IDM_INCSAME,
2106: (outline_include & INCLUDE_SAME) ?
2107: MF_CHECKED:MF_UNCHECKED);
2108:
2109: CheckMenuItem(hMenu, IDM_INCLEFT,
2110: (outline_include & INCLUDE_LEFTONLY) ?
2111: MF_CHECKED:MF_UNCHECKED);
2112:
2113: CheckMenuItem(hMenu, IDM_INCRIGHT,
2114: (outline_include & INCLUDE_RIGHTONLY) ?
2115: MF_CHECKED:MF_UNCHECKED);
2116: CheckMenuItem(hMenu, IDM_INCDIFFER,
2117: (outline_include & INCLUDE_DIFFER) ?
2118: MF_CHECKED:MF_UNCHECKED);
2119:
2120: CheckMenuItem(hMenu, line_numbers, MF_CHECKED);
2121: CheckMenuItem(hMenu, expand_mode, MF_CHECKED);
2122:
2123: CheckMenuItem(hMenu, IDM_IGNBLANKS,
2124: ignore_blanks ? MF_CHECKED : MF_UNCHECKED);
2125: CheckMenuItem(hMenu, IDM_PICTURE,
2126: picture_mode ? MF_CHECKED : MF_UNCHECKED);
2127:
2128: /* nothing currently displayed */
2129: DisplayMode = MODE_NULL;
2130:
2131: break;
2132:
2133:
2134: case WM_COMMAND:
2135: switch (GET_WM_COMMAND_ID(wParam, lParam)) {
2136: case IDM_EXIT:
2137: if (ghThread!=NULL) {
2138: extern CRITICAL_SECTION CSView;
2139: /* Stop any other thread from allocating things that we
2140: want to free! See the threads DOGMA at the top
2141: of this file.
2142: */
2143:
2144: /* Because the thread that we are about to kill might be in
2145: a critical section, we first grab them both. It is
2146: essential that anyone else who ever does this, does
2147: so in the same order!
2148: */
2149: WDEnter();
2150: EnterCriticalSection(&CSView);
2151: TerminateThread(ghThread, 31);
2152: CloseHandle(ghThread);
2153: ghThread = NULL;
2154: LeaveCriticalSection(&CSView);
2155: WDLeave();
2156: }
2157: if (!view_isexpanded(current_view)) {
2158: /* save the current outline size and position */
2159: WINDOWPLACEMENT wp;
2160: if (GetWindowPlacement(hwndClient,&wp)) {
2161: WriteProfileInt(APPNAME, "OutlineShowCmd", wp.showCmd);
2162: WriteProfileInt(APPNAME, "OutlineMaxX", wp.ptMaxPosition.x);
2163: WriteProfileInt(APPNAME, "OutlineMaxY", wp.ptMaxPosition.y);
2164: WriteProfileInt(APPNAME, "OutlineNormLeft", wp.rcNormalPosition.left);
2165: WriteProfileInt(APPNAME, "OutlineNormTop", wp.rcNormalPosition.top);
2166: WriteProfileInt(APPNAME, "OutlineNormRight", wp.rcNormalPosition.right);
2167: WriteProfileInt(APPNAME, "OutlineNormBottom", wp.rcNormalPosition.bottom);
2168: WriteProfileInt(APPNAME, "OutlineSaved", 1);
2169: }
2170: } else {
2171: /* save the current expanded size and position */
2172: WINDOWPLACEMENT wp;
2173: if (GetWindowPlacement(hwndClient,&wp)) {
2174: WriteProfileInt(APPNAME, "ExpandShowCmd", wp.showCmd);
2175: WriteProfileInt(APPNAME, "ExpandMaxX", wp.ptMaxPosition.x);
2176: WriteProfileInt(APPNAME, "ExpandMaxY", wp.ptMaxPosition.y);
2177: WriteProfileInt(APPNAME, "ExpandNormLeft", wp.rcNormalPosition.left);
2178: WriteProfileInt(APPNAME, "ExpandNormTop", wp.rcNormalPosition.top);
2179: WriteProfileInt(APPNAME, "ExpandNormRight", wp.rcNormalPosition.right);
2180: WriteProfileInt(APPNAME, "ExpandNormBottom", wp.rcNormalPosition.bottom);
2181: WriteProfileInt(APPNAME, "ExpandedSaved", 1);
2182: }
2183: }
2184: DestroyWindow(hWnd);
2185: break;
2186:
2187: case IDM_ABORT:
2188: /* abort menu item, or status bar button.
2189: * the status bar button text gives the appropriate
2190: * action depending on our state - abort, outline
2191: * or expand. But the command sent is always
2192: * IDM_ABORT. Thus we need to check the state
2193: * to see what to do. If we are busy, set the abort
2194: * flag. If there is nothing to view,
2195: * exit, otherwise switch outline<->expand
2196: */
2197: if (IsBusy()) {
2198: bAbort = TRUE;
2199: SetStatus("Abort Pending");
2200:
2201: } else if (DisplayMode == MODE_NULL) {
2202: DestroyWindow(hWnd);
2203: } else if (DisplayMode == MODE_EXPAND) {
2204: ToOutline(hWnd);
2205: } else {
2206: ToExpand(hWnd);
2207: }
2208: break;
2209:
2210: case IDM_FILE:
2211: /* select two files and compare them */
2212: if (SetBusy()) {
2213:
2214: /* close the current view */
2215: view_close(current_view);
2216:
2217: /* make a new empty view */
2218: current_view = view_new(hwndRCD);
2219:
2220: /* make a COMPLIST using the files dialog,
2221: * and notify the view
2222: */
2223: if (complist_filedialog(current_view) == NULL) {
2224: view_close(current_view);
2225: }
2226:
2227: /* all done! */
2228: SetNotBusy();
2229: } else {
2230: BusyError();
2231: }
2232: break;
2233:
2234: case IDM_DIR:
2235:
2236: /* read two directory names, scan them and
2237: * compare all the files and subdirs.
2238: */
2239: if (SetBusy()) {
2240:
2241: /* close the current view */
2242: view_close(current_view);
2243:
2244: /* make a new empty view */
2245: current_view = view_new(hwndRCD);
2246:
2247: ghThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)wd_dirdialog,
2248: (LPVOID) current_view, 0, &threadid);
2249:
2250: if (ghThread == NULL)
2251: {
2252: wd_dirdialog( (LPVOID) current_view);
2253: }
2254:
2255: } else {
2256: BusyError();
2257: }
2258: break;
2259:
2260: case IDM_CLOSE:
2261: /* close the output list -
2262: * discard all results so far
2263: */
2264: if (!IsBusy()) {
2265: view_close(current_view);
2266: }
2267: break;
2268:
2269: case IDM_PRINT:
2270: /* print the current view -
2271: * either the outline list of filenames,
2272: * or the currently expanded file.
2273: */
2274: if (!IsBusy()) {
2275: DoPrint();
2276: } else {
2277: BusyError();
2278: }
2279: break;
2280:
2281: case IDM_TIME:
2282: /* show time it took */
2283: { char msg[50];
2284: DWORD tim;
2285: if (IsBusy()) {
2286: BusyError();
2287: }
2288: else{
2289: tim = complist_querytime();
2290: wsprintf((LPTSTR)msg, "%d.%03d seconds", tim/1000, tim%1000);
2291: }
2292: }
2293: break;
2294:
2295: case IDM_SAVELIST:
2296: /* allow user to save list of same/different files
2297: * to a text file. dialog box to give filename
2298: * and select which types of file to include
2299: */
2300: complist_savelist(view_getcomplist(current_view), NULL, 0);
2301: break;
2302:
2303: case IDM_COPYFILES:
2304: /*
2305: * copy files that are same/different to a new
2306: * root directory. dialog box allows user
2307: * to select new root and inclusion options
2308: */
2309: if (current_view == NULL) {
2310: MessageBox(hWnd,
2311: "Please create a diff list first",
2312: "WinDiff", MB_OK|MB_ICONSTOP);
2313: break;
2314: }
2315:
2316: if (SetBusy()) {
2317: ghThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)wd_copy,
2318: (LPVOID) current_view, 0, &threadid);
2319: if (ghThread == NULL)
2320: {
2321: wd_copy( (LPVOID) current_view);
2322: }
2323:
2324: } else {
2325: BusyError();
2326: }
2327:
2328: break;
2329:
2330: case IDM_ABOUT:
2331:
2332: DialogBox( hInst, "About", hWnd, (DLGPROC)AboutBox);
2333: break;
2334:
2335: /* launch an editor on the current item - left, right or
2336: * composite view
2337: */
2338: case IDM_EDITLEFT:
2339: do_editthread(current_view, CI_LEFT);
2340: break;
2341:
2342: case IDM_EDITRIGHT:
2343: do_editthread(current_view, CI_RIGHT);
2344: break;
2345:
2346: case IDM_EDITCOMP:
2347: do_editthread(current_view, CI_COMP);
2348: break;
2349:
2350: /* allow customisation of the editor command line */
2351: case IDM_SETEDIT:
2352: if (StringInput(editor_cmdline, sizeof(editor_cmdline),
2353: "Please enter the editor command line",
2354: "Windiff", editor_cmdline)) {
2355: WriteProfileString(APPNAME, "Editor",
2356: (LPCSTR)editor_cmdline);
2357: }
2358: break;
2359:
2360:
2361: case IDM_LNRS:
2362: case IDM_RNRS:
2363: case IDM_NONRS:
2364:
2365: /* option selects whether the line nrs displayed
2366: * in expand mode are the line nrs in the left
2367: * file, the right file or none
2368: */
2369:
2370: CheckMenuItem(GetMenu(hWnd),
2371: line_numbers, MF_UNCHECKED);
2372: line_numbers = GET_WM_COMMAND_ID(wParam, lParam);
2373: CheckMenuItem(GetMenu(hWnd), line_numbers, MF_CHECKED);
2374: wsprintf((LPTSTR)str, "%d", line_numbers);
2375: WriteProfileString(APPNAME, "LineNumbers", str);
2376:
2377: /* change the display to show the line nr style
2378: * chosen
2379: */
2380:
2381: view_changeviewoptions(current_view);
2382:
2383:
2384: break;
2385:
2386: /*
2387: * options selecting which files to include in the
2388: * outline listing, based on their state
2389: */
2390: case IDM_INCLEFT:
2391:
2392:
2393: /* toggle flag in outline_include options */
2394: outline_include ^= INCLUDE_LEFTONLY;
2395:
2396: /* check/uncheck as necessary */
2397: CheckMenuItem(hMenu, IDM_INCLEFT,
2398: (outline_include & INCLUDE_LEFTONLY) ?
2399: MF_CHECKED:MF_UNCHECKED);
2400:
2401: wsprintf((LPTSTR)str, "%d", outline_include);
2402: WriteProfileString(APPNAME, "FileInclude", str);
2403: view_changeviewoptions(current_view);
2404:
2405:
2406: break;
2407:
2408: case IDM_INCRIGHT:
2409:
2410:
2411: outline_include ^= INCLUDE_RIGHTONLY;
2412:
2413: CheckMenuItem(hMenu, IDM_INCRIGHT,
2414: (outline_include & INCLUDE_RIGHTONLY) ?
2415: MF_CHECKED:MF_UNCHECKED);
2416: wsprintf((LPTSTR)str, "%d", outline_include);
2417: WriteProfileString(APPNAME, "FileInclude", str);
2418: view_changeviewoptions(current_view);
2419:
2420: break;
2421:
2422: case IDM_INCSAME:
2423:
2424:
2425: outline_include ^= INCLUDE_SAME;
2426:
2427: CheckMenuItem(hMenu, IDM_INCSAME,
2428: (outline_include & INCLUDE_SAME) ?
2429: MF_CHECKED:MF_UNCHECKED);
2430: wsprintf((LPTSTR)str, "%d", outline_include);
2431: WriteProfileString(APPNAME, "FileInclude", str);
2432: view_changeviewoptions(current_view);
2433:
2434:
2435: break;
2436:
2437:
2438: case IDM_INCDIFFER:
2439:
2440:
2441:
2442: outline_include ^= INCLUDE_DIFFER;
2443:
2444: CheckMenuItem(hMenu, IDM_INCDIFFER,
2445: (outline_include & INCLUDE_DIFFER) ?
2446: MF_CHECKED:MF_UNCHECKED);
2447:
2448: wsprintf((LPTSTR)str, "%d", outline_include);
2449: WriteProfileString(APPNAME, "FileInclude", str);
2450: view_changeviewoptions(current_view);
2451:
2452:
2453: break;
2454:
2455: case IDM_UPDATE:
2456: /* update the display. Options or files may have changed */
2457: /* discard lines (thereby forcing re-read).
2458: */
2459: file_discardlines(compitem_getleftfile( (COMPITEM)lParam) );
2460: file_discardlines(compitem_getrightfile( (COMPITEM)lParam) );
2461:
2462: view_changediffoptions(current_view);
2463:
2464: /* force repaint of bar window */
2465: InvalidateRect(hwndBar, NULL, TRUE);
2466: break;
2467:
2468:
2469:
2470: case IDM_LONLY:
2471: case IDM_RONLY:
2472: case IDM_BOTHFILES:
2473: /* option selects whether the expanded file
2474: * show is the combined file, or just one
2475: * or other of the input files.
2476: *
2477: * if we are not in expand mode, this also
2478: * causes us to expand the selection
2479: */
2480:
2481:
2482: CheckMenuItem(GetMenu(hWnd), expand_mode, MF_UNCHECKED);
2483: expand_mode = GET_WM_COMMAND_ID(wParam, lParam);
2484: CheckMenuItem(GetMenu(hWnd), expand_mode, MF_CHECKED);
2485:
2486: /* change the current view to show only the lines
2487: * of the selected type.
2488: */
2489: if (DisplayMode == MODE_OUTLINE) {
2490: ToExpand(hWnd);
2491: } else {
2492: view_changeviewoptions(current_view);
2493: }
2494:
2495:
2496: break;
2497:
2498:
2499: case IDM_IGNBLANKS:
2500:
2501: /* if selected, ignore all spaces and tabs on
2502: * comparison - expand view only: outline view
2503: * will still show that 'text files differ'
2504: */
2505:
2506: ignore_blanks = !ignore_blanks;
2507: CheckMenuItem(hMenu, IDM_IGNBLANKS,
2508: ignore_blanks? MF_CHECKED:MF_UNCHECKED);
2509: wsprintf((LPTSTR)str, "%d", ignore_blanks);
2510: WriteProfileString(APPNAME, "Blanks", str);
2511:
2512: /* invalidate all diffs since we have
2513: * changed diff options, and re-do and display the
2514: * current diff if we are in expand mode.
2515: */
2516: view_changediffoptions(current_view);
2517:
2518: /* force repaint of bar window */
2519: InvalidateRect(hwndBar, NULL, TRUE);
2520:
2521: break;
2522:
2523: case IDM_PICTURE:
2524: /* do we show the bar picture in expand mode ? */
2525: picture_mode = !picture_mode;
2526: CheckMenuItem(hMenu, IDM_PICTURE,
2527: picture_mode? MF_CHECKED:MF_UNCHECKED);
2528: wsprintf((LPTSTR)str, "%d", picture_mode);
2529: WriteProfileString(APPNAME, "Picture", str);
2530: DoResize(hWnd);
2531: break;
2532:
2533:
2534: case IDM_EXPAND:
2535:
2536: /* show the expanded view of the
2537: * selected file
2538: */
2539: if (current_view != NULL) {
2540: ToExpand(hWnd);
2541: }
2542:
2543: break;
2544:
2545: case IDM_OUTLINE:
2546: /* return to the outline view (list of filenames) */
2547: ToOutline(hWnd);
2548:
2549: break;
2550:
2551: case IDM_FCHANGE:
2552: /* find the next line in the current view
2553: * that is not the same in both files -
2554: * in outline view, finds the next filename that
2555: * is not identical
2556: */
2557: FindNextChange();
2558:
2559: break;
2560:
2561: case IDM_FPCHANGE:
2562: /* same as IDM_FCHANGE, but going backwards from
2563: * current position
2564: */
2565: FindPrevChange();
2566:
2567: break;
2568: }
2569: break;
2570:
2571: case WM_SIZE:
2572: DoResize(hWnd);
2573: break;
2574:
2575: case WM_SETFOCUS:
2576: /* set the focus on the table class so it can process
2577: * page-up /pagedown keys etc.
2578: */
2579: SetFocus(hwndRCD);
2580: break;
2581:
2582: case WM_KEYDOWN:
2583: /* although the table window has the focus, he passes
2584: * back to us any keys he doesn't understand
2585: * We handle escape here to mean 'return to outline view'
2586: */
2587: if (wParam == VK_ESCAPE) {
2588: ToOutline(hWnd);
2589: }
2590: break;
2591:
2592: case WM_CLOSE:
2593: /* don't allow close when busy - process this message in
2594: * order to ensure this
2595: */
2596: if (IsBusy()) {
2597: return(TRUE);
2598: } else {
2599: return(DefWindowProc(hWnd, message, wParam, lParam));
2600: }
2601: break;
2602:
2603: case WM_DESTROY:
2604:
2605: DeleteTools();
2606: PostQuitMessage(0);
2607: break;
2608:
2609: case TM_CURRENTVIEW:
2610: /* allow other people such as the bar window to query the
2611: * current view
2612: */
2613: return((DWORD) current_view);
2614:
2615: default:
2616: /* handle registered table messages */
2617: if (message == table_msgcode) {
2618: ret = TableServer(hWnd, wParam, lParam);
2619: return(ret);
2620: }
2621: return(DefWindowProc(hWnd, message, wParam, lParam));
2622: }
2623: return(0);
2624: }
2625:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.