|
|
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: VIEW.C
14: *
15: * Maps rows in window to items in COMPLIST
16: *
17: * Functions:
18: *
19: * view_new()
20: * view_setcomplist()
21: * view_getcomplist()
22: * view_close()
23: * view_delete()
24: * view_outline()
25: * view_expand()
26: * view_gettext()
27: * view_getlinenr_left()
28: * view_getlinenr_right()
29: * view_getwidth()
30: * view_getrowcount()
31: * view_getstate()
32: * view_getitem()
33: * view_isexpanded()
34: * view_getcurrenttag()
35: * view_newitem()
36: * view_changeviewoptions()
37: * view_changediffoptions()
38: * view_findchange()
39: * view_outline_opt()
40: * view_freemappings()
41: * view_findrow()
42: * view_expand_item()
43: *
44: * Comments:
45: *
46: * A view owns a COMPLIST, and talks to a table window. The table window
47: * shows 3 columns: line nr, tag and text. We also need to supply a state
48: * for each row (used to select colour scheme).
49: *
50: * The COMPLIST can give us a list of its COMPITEMs. Each of these can give
51: * us a tag (eg the filenames compared) and the text (usually the compare
52: * result), and the state. We make the line number from the
53: * COMPITEM's place in the list.
54: *
55: * If we are asked to switch to 'expand' mode, we ask the selected COMPITEM
56: * for its composite section list. We can then get the state (and thus
57: * the tag) from each SECTION, and the line nr and text from the LINEs within
58: * each section.
59: *
60: * When moving between expand and outline, and when refreshing the view
61: * for some option change, we have to be careful to keep the current row
62: * and the selected row in the table what the user would expect.
63: *
64: * Functions in this module can be called from the UI thread (to refresh
65: * the display) and simultaneously from a worker thread to update the
66: * view mapping (view_setcomplist, view_newitem). We use a critical section
67: * to manage the synchronisation. We need to protect all access/modification
68: * to the view structure elements (particularly bExpand, rows, pLines and
69: * pItems), BUT we must not hold the critical section over any calls
70: * to SendMessage.
71: *
72: * We use the global options in windiff.h, and we allocate memory from the
73: * heap hHeap which has been initialised elsewhere. Points in time-intensive
74: * loops call Poll() defined elsewhere.
75: *
76: ****************************************************************************/
77:
78: #include <windows.h>
79: #include <stdlib.h>
80: #include <commdlg.h>
81:
82: #include "gutils.h"
83: #include "table.h"
84: #include "state.h"
85: #include "windiff.h"
86: #include "wdiffrc.h"
87: #include "list.h"
88: #include "line.h"
89: #include "scandir.h"
90: #include "file.h"
91: #include "section.h"
92: #include "compitem.h"
93: #include "complist.h"
94: #include "view.h"
95:
96: /*
97: * data structures
98: */
99:
100: /* in expand mode, we keep an array of one of these per screen line. */
101: typedef struct viewline {
102: LINE line; /* handle to LINE for this row */
103: SECTION section; /* handle to section containing this line */
104: int nr_left; /* line nr in left file */
105: int nr_right; /* line nr in right file */
106: } VIEWLINE, FAR * PVIEWLINE;
107:
108:
109: /*
110: * The users VIEW handle is in fact a pointer to this structure
111: */
112: struct view {
113:
114: HWND hwnd; /* the table window to send notifies to */
115:
116: COMPLIST cl; /* the complist that we own */
117:
118: BOOL bExpand; /* true if we are in expand mode */
119:
120: COMPITEM ciSelect; /* selected compitem (in expand mode) */
121:
122: int rows; /* number of rows in this view */
123:
124: char nrtext[12]; /* we use this in view_gettext for the line
125: * number column. overwritten on each call
126: */
127: int maxtag, maxrest;/* column widths in characters for cols 1, 2 */
128:
129: /* if we are in outline mode, we map the row number to one entry
130: * in this array of COMPITEM handles. this pointer will
131: * be NULL in expand mode
132: */
133: COMPITEM FAR * pItems;
134:
135: /* in expand mode we use this array of line and section handles */
136: PVIEWLINE pLines;
137: };
138:
139:
140: CRITICAL_SECTION CSView;
141: static BOOL bDoneInit = FALSE;
142:
143: #define ViewEnter() EnterCriticalSection(&CSView);
144: #define ViewLeave() LeaveCriticalSection(&CSView);
145:
146: void view_outline_opt(VIEW view, BOOL bRedraw);
147: void view_freemappings(VIEW view);
148: int view_findrow(VIEW view, int number, BOOL bRight);
149: BOOL view_expand_item(VIEW view, COMPITEM ci);
150:
151:
152: /***************************************************************************
153: * Function: view_new
154: *
155: * Purpose:
156: *
157: * Create a new view. At this point, we are told the table window handle,
158: * and nothing else.
159: *
160: */
161: VIEW
162: view_new(HWND hwndTable)
163: {
164: VIEW view;
165:
166: if (!bDoneInit) {
167: InitializeCriticalSection(&CSView);
168: bDoneInit = TRUE;
169: }
170:
171: /* alloc the view from the heap */
172: view = (VIEW) gmem_get(hHeap, sizeof(struct view));
173:
174: /* set the default fields */
175: view->hwnd = hwndTable;
176: view->cl = NULL;
177: view->bExpand = FALSE;
178: view->ciSelect = NULL;
179: view->rows = 0;
180: view->pItems = NULL;
181: view->pLines = NULL;
182:
183: return(view);
184: }
185:
186:
187: /***************************************************************************
188: * Function: view_setcomplist
189: *
190: * Purpose:
191: *
192: * We have to separate view_new and view_setcomplist because we need
193: * to give the view handle to the complist and the complist handle to the
194: * view. So do a view_new to create a null view; then complist_new() to
195: * which you pass a view handle. The complist will then register itself
196: * with the view by calling this function. During the build of the complist,
197: * it will also update us by calling view_additem, so that we can refresh
198: * the display.
199: *
200: * Here we should initialise an outline view of the complist.
201: *
202: * We also talk to the status bar using SetNames to set the names of
203: * the two items.
204: */
205: BOOL
206: view_setcomplist(VIEW view, COMPLIST cl)
207: {
208: LPSTR left, right, both;
209:
210: if (view == NULL) {
211: return(FALSE);
212: }
213:
214: /* there can be only one call to this per VIEW */
215: if (view->cl != NULL) {
216: return (FALSE);
217: }
218:
219: ViewEnter();
220:
221: view->cl = cl;
222:
223: /* set names on status bar to root names of left and right trees */
224: left = complist_getroot_left(cl);
225: right = complist_getroot_right(cl);
226: both = gmem_get(hHeap, lstrlen(left) + lstrlen(right) +4);
227: wsprintf((LPTSTR)both, "%s : %s", left, right);
228: ViewLeave();
229: SetNames(both);
230: ViewEnter();
231: gmem_free(hHeap, both, lstrlen(both)+1);
232: complist_freeroot_left(cl, left);
233: complist_freeroot_right(cl, right);
234:
235: ViewLeave();
236:
237: view_outline(view);
238: }
239:
240:
241: /***************************************************************************
242: * Function: view_getcomplist
243: *
244: * Purpose:
245: *
246: * Return a handle to the complist owned by this view
247: */
248: COMPLIST
249: view_getcomplist(VIEW view)
250: {
251: if (view == NULL) {
252: return(NULL);
253: }
254:
255: return(view->cl);
256: }
257:
258:
259: /***************************************************************************
260: * Function: view_close
261: *
262: * Purpose:
263: *
264: * Close a view. Notify the table window that this view should be
265: * closed. When the table window has finished with it, it will send
266: * a TQ_CLOSE notify that should result in view_delete being called
267: * and the memory being freed.
268: */
269: void
270: view_close(VIEW view)
271: {
272: if (view == NULL) {
273: return;
274: }
275:
276: SendMessage(view->hwnd, TM_NEWID, 0, 0);
277: }
278:
279:
280: /***************************************************************************
281: * Function: view_delete
282: *
283: * Purpose:
284: *
285: * Delete a view and all associated data.
286: *
287: * This function should only be called in response to the table window
288: * sending a TQ_CLOSE message. To close the view, call view_close and
289: * wait for the TQ_CLOSE before calling this.
290: *
291: * We delete the associated COMPLIST and all its associated structures.
292: */
293: void
294: view_delete(VIEW view)
295: {
296: if (view == NULL) {
297: return;
298: }
299:
300: /* we have two arrays that are used for the mapping - an array
301: * of compitem handles in outline mode, and an array of
302: * VIEWLINE structures in expand mode
303: */
304:
305: view_freemappings(view);
306:
307: complist_delete(view->cl);
308:
309: gmem_free(hHeap, (LPSTR) view, sizeof(struct view));
310: }
311:
312:
313: /***************************************************************************
314: * Function: view_outline
315: *
316: * Purpose:
317: *
318: * Build an outline mode mapping where one row represents one COMPITEM in
319: * the list. Check the global option flag outline_include to see which items
320: * we should include.
321: *
322: * If we were in expand mode, then set as the selection the row in outline mode
323: * that we were expanding. Also remember to free up the expand mode mapping
324: * array
325: *
326: * Once we have built the new mapping, notify the table window to
327: * redraw itself.
328: */
329: void
330: view_outline(VIEW view)
331: {
332: if (view == NULL) {
333: return;
334: }
335:
336: /* all work done by view_outline_opt - this function
337: * gives us the option of not updating the display
338: */
339: view_outline_opt(view, TRUE);
340: }
341:
342:
343:
344: /***************************************************************************
345: * Function: view_expand
346: *
347: * Purpose:
348: *
349: * Switch to expand mode, expanding the given row into a view
350: * of the differences in that file.
351: *
352: * Map the given row nr into a compitem handle, and then
353: * call the internal function with that.
354: */
355: BOOL
356: view_expand(VIEW view, long row)
357: {
358: COMPITEM ci;
359: BOOL bRet;
360:
361: ViewEnter();
362:
363: if ((view == NULL) || (view->bExpand)) {
364: /* no view, or already expanded */
365: ViewLeave();
366: return(FALSE);
367: }
368:
369: if (row >= view->rows) {
370: /* no such row */
371: ViewLeave();
372: return FALSE;
373: }
374:
375: /* remember the compitem we are expanding */
376: ci = view->pItems[row];
377:
378: bRet = view_expand_item(view, ci);
379: // view_expand_item does the...
380: // ViewLeave();
381: return(bRet);
382: }
383:
384:
385: /***************************************************************************
386: * Function: view_gettext
387: *
388: * Purpose:
389: *
390: * Return the text associated with a given column of a given row.
391: * Return a pointer that does not need to be freed after use - ie
392: * a pointer into our data somewhere, not a copy
393: */
394: LPSTR
395: view_gettext(VIEW view, long row, int col)
396: {
397: int line;
398: int state;
399: LPSTR pstr;
400:
401:
402: if (view == NULL) {
403: return (NULL);
404: }
405:
406: ViewEnter();
407:
408: if (row >= view->rows) {
409: ViewLeave();
410: return(NULL);
411: }
412:
413: if (view->bExpand) {
414: /* we are in expand mode */
415:
416: state = section_getstate(view->pLines[row].section);
417:
418: switch(col) {
419: case 0:
420: /* row nr */
421:
422: /* line numbers can be from either original file
423: * this is a menu-selectable option
424: */
425: switch(line_numbers) {
426: case IDM_NONRS:
427: pstr = NULL;
428: break;
429:
430: case IDM_LNRS:
431: line = view->pLines[row].nr_left;
432: if (state == STATE_MOVEDRIGHT) {
433: line = -line;
434: }
435: break;
436:
437: case IDM_RNRS:
438: line = view->pLines[row].nr_right;
439: if (state == STATE_MOVEDLEFT) {
440: line = -line;
441: }
442: break;
443: }
444: if (line == 0) {
445: ViewLeave();
446: return(NULL);
447: }
448:
449: if (line < 0) {
450: /* lines that are moved appear twice.
451: * show the correct-sequence line nr
452: * for the out-of-seq. copy in brackets.
453: */
454: wsprintf((LPTSTR)view->nrtext, "(%d)", abs(line));
455: } else {
456: wsprintf((LPTSTR)view->nrtext, "%d", line);
457: }
458: pstr = view->nrtext;
459: break;
460:
461: case 1:
462: /* tag text - represents the state of the line */
463:
464:
465: switch(state) {
466: case STATE_SAME:
467: pstr = " ";
468: break;
469:
470: case STATE_LEFTONLY:
471: pstr = " <! ";
472: break;
473:
474: case STATE_RIGHTONLY:
475: pstr = " !> ";
476: break;
477:
478: case STATE_MOVEDLEFT:
479: pstr = " <- ";
480: break;
481:
482: case STATE_MOVEDRIGHT:
483: pstr = " -> ";
484: break;
485: }
486: break;
487:
488: case 2:
489: /* main text - line */
490: pstr = line_gettext(view->pLines[row].line);
491: break;
492: }
493: } else {
494: /* outline mode */
495: switch(col) {
496: case 0:
497: /* row number - just the line number */
498: wsprintf((LPTSTR)view->nrtext, "%d", row+1);
499: pstr = view->nrtext;
500: break;
501:
502: case 1:
503: /* tag */
504: pstr = compitem_gettext_tag(view->pItems[row]);
505: break;
506:
507: case 2:
508: /* result text */
509: pstr = compitem_gettext_result(view->pItems[row]);
510: break;
511: }
512: }
513: ViewLeave();
514: return(pstr);
515: }
516:
517: /***************************************************************************
518: * Function: view_getlinenr_left
519: *
520: * Purpose:
521: *
522: * Return the line number that this row had in the original left
523: * file. 0 if not in expand mode. 0 if this row was not in the left file.
524: * -(linenr) if this row is a MOVED line, and this is the right file
525: * copy
526: */
527: int
528: view_getlinenr_left(VIEW view, long row)
529: {
530: int state, line;
531:
532: if ((view == NULL) || (row >= view->rows) || !view->bExpand) {
533: return 0;
534: }
535:
536: ViewEnter();
537: state = section_getstate(view->pLines[row].section);
538: line = view->pLines[row].nr_left;
539: if (state == STATE_MOVEDRIGHT) {
540: line = -line;
541: }
542: ViewLeave();
543:
544: return(line);
545: }
546:
547: /***************************************************************************
548: * Function: view_getlinenr_right
549: *
550: * Purpose:
551: *
552: * Return the line number that this row had in the original right
553: * file. 0 if not in expand mode. 0 if this row was not in the right file.
554: * -(linenr) if this row is a MOVED line, and this is the left file
555: * copy
556: */
557: int
558: view_getlinenr_right(VIEW view, long row)
559: {
560: int state, line;
561:
562: if ((view == NULL) || (row > view->rows) || !view->bExpand) {
563: return 0;
564: }
565:
566: ViewEnter();
567:
568: state = section_getstate(view->pLines[row].section);
569: line = view->pLines[row].nr_right;
570: if (state == STATE_MOVEDLEFT) {
571: line = -line;
572: }
573: ViewLeave();
574:
575: return(line);
576: }
577:
578:
579: /***************************************************************************
580: * Function: view_getwidth
581: *
582: * Purpose:
583: *
584: * Find the maximum width in characters for the given column
585: */
586: int
587: view_getwidth(VIEW view, int col)
588: {
589: if (view == NULL) {
590: return(0);
591: }
592:
593: switch(col) {
594: case 0:
595: /* line nr column - always 5 characters wide */
596: return(5);
597:
598: case 1:
599: /* this is a proportional font field, so add on a margin
600: * for error
601: */
602: return(view->maxtag + (view->maxtag / 20));
603: case 2:
604: /* this now includes the tab expansion allowance */
605: return(view->maxrest);
606: default:
607: return(0);
608: }
609: }
610:
611: /***************************************************************************
612: * Function: view_getrowcount
613: *
614: * Purpose:
615: *
616: * How many rows are there in this view ?
617: */
618: long
619: view_getrowcount(VIEW view)
620: {
621: if (view == NULL) {
622: return(0);
623: }
624:
625: return(view->rows);
626: }
627:
628: /***************************************************************************
629: * Function: view_getstate
630: *
631: * Purpose:
632: *
633: * Return the state for the current row. This is used
634: * to select the text colour for the row
635: *
636: * States for sections are obtained from section_getstate (and apply, and
637: * to all lines in that section. States for compitems are obtained
638: * from compitem_getstate.
639: */
640: int
641: view_getstate(VIEW view, long row)
642: {
643: int state;
644:
645: if (view == NULL) {
646: return(0);
647: }
648:
649: ViewEnter();
650: if (row >= view->rows) {
651: state = 0;
652: } else if (view->bExpand) {
653: /* its a line state that's needed */
654: state = section_getstate(view->pLines[row].section);
655: } else {
656:
657: /* its a compitem state */
658: state = compitem_getstate(view->pItems[row]);
659: }
660: ViewLeave();
661: return(state);
662: }
663:
664: /***************************************************************************
665: * Function: view_gethandle
666: *
667: * Purpose:
668: *
669: * Return a handle to the current compitem. In expand mode,
670: * returns the handle to the compitem we are expanding. In outline
671: * mode, returns the handle to the compitem for the given row, if valid,
672: * or NULL otherwise. row is only used if not in expand mode.
673: */
674: COMPITEM
675: view_getitem(VIEW view, long row)
676: {
677: COMPITEM ci;
678:
679: if (view == NULL) {
680: return(NULL);
681: }
682:
683: ViewEnter();
684:
685: if (!view->bExpand) {
686: if ((row >= 0) && (row < view->rows)) {
687: ci = view->pItems[row];
688: } else {
689: ci = NULL;
690: }
691: } else {
692: ci = view->ciSelect;
693: }
694:
695: ViewLeave();
696: return(ci);
697: }
698:
699: /***************************************************************************
700: * Function: view_isexpanded
701: *
702: * Purpose:
703: *
704: * Return TRUE if the current mapping is expanded mode
705: */
706: BOOL
707: view_isexpanded(VIEW view)
708: {
709: if (view == NULL) {
710: return(FALSE);
711: }
712: return(view->bExpand);
713: }
714:
715:
716: /***************************************************************************
717: * Function: view_getcurrenttag
718: *
719: * Purpose:
720: *
721: * Return a text string describing the view. This is NULL in outline mode,
722: * or the tag text for the current compitem in expanded mode
723: */
724: LPSTR
725: view_getcurrenttag(VIEW view)
726: {
727: LPSTR str;
728:
729: if ((view == NULL) || (!view->bExpand)) {
730: return(NULL);
731: } else {
732: ViewEnter();
733:
734: str = compitem_gettext_tag(view->ciSelect);
735:
736: ViewLeave();
737: return(str);
738:
739: }
740: }
741:
742:
743: /***************************************************************************
744: * Function: view_newitem
745: *
746: * Purpose:
747: *
748: * Notify that CompItems have been added to the complist.
749: *
750: * Rebuild the view (if in outline mode), and refresh the table. Use
751: * the table message TM_APPEND if possible (if column widths have not
752: * change). If we have to do TM_NEWLAYOUT, then ensure we scroll
753: * back to the right row afterwards.
754: *
755: * This causes a Poll() to take place. We return TRUE if an abort is
756: * pending - in this case, the caller should abandon the scan loop.
757: *
758: * Enter the critical section for this function since this can be
759: * called from the worker thread while the UI thread is using the
760: * view that we are about to change.
761: *
762: * EXCEPT THAT WE DON'T DARE. We cannot ever call SendMessage from the
763: * worker thread within CSView. If there is conflict, it will hang.
764: */
765: BOOL
766: view_newitem(VIEW view)
767: {
768: int maxtag, maxrest;
769: long rownr;
770:
771: if ((view == NULL) || (view->bExpand)) {
772: /* not in outline mode - nothing to do */
773: return(Poll());
774: }
775:
776: /* save some state about the present mapping */
777: maxtag = view->maxtag;
778: maxrest = view->maxrest;
779:
780: /* re-do the outline mapping, but don't tell the table
781: * class.
782: */
783: view_outline_opt(view, FALSE);
784:
785: /* have the column widths changed ? */
786: if ((maxtag < view->maxtag) || (maxrest < view->maxrest)) {
787: /* yes - need complete redraw */
788:
789: /* find the row at the top of the window */
790: rownr = SendMessage(view->hwnd, TM_TOPROW, FALSE, 0);
791:
792: /* switch to new mapping */
793: SendMessage(view->hwnd, TM_NEWLAYOUT, 0, (DWORD) view);
794:
795: /* return to old row if possible - we know
796: * that row is still there since we have only added
797: * rows, and not changed any of the existing mapping
798: *
799: * Alas this is no longer true. However the table class
800: * will defend itself against calls for a bogus top row.
801: */
802: if (rownr >= 0) {
803: SendMessage(view->hwnd, TM_TOPROW, TRUE, rownr);
804: }
805: } else {
806: /* no - we can just append */
807:
808: /*
809: * The mapping may have
810: * changed since we released the critsec. however we are still
811: * safe. The table will not allow us to reduce the number of
812: * rows, so the worst that can happen is that the table will
813: * think there are too many rows, and the table message handler
814: * will handle this correctly (return null for the text).
815: * The only visible effect is therefore that the scrollbar
816: * position is wrong.
817: */
818:
819: SendMessage(view->hwnd, TM_APPEND, view->rows, (DWORD) view);
820: }
821:
822:
823: /* Poll to keep the UI updated on NT. Returns true if abort pending.
824: */
825: return(Poll());
826: }
827:
828: /***************************************************************************
829: * Function: view_changeviewoptions
830: *
831: * Purpose:
832: *
833: * The view mapping options (eg outline_include, expand_mode) have changed -
834: * re-do the mapping and then scroll back to the same position in the window
835: * if possible.
836: */
837: void
838: view_changeviewoptions(VIEW view)
839: {
840: long row;
841: int state, number;
842: BOOL bRight;
843:
844: if (view == NULL) {
845: return;
846: }
847:
848: /* find what row we are currently on. Do this BEFORE we enter CSView */
849: row = SendMessage(view->hwnd, TM_TOPROW, FALSE, 0);
850:
851: ViewEnter();
852:
853: if (!view->bExpand) {
854:
855: /* outline mode. maintaining current position is
856: * unimportant
857: */
858: view_outline(view);
859: ViewLeave();
860: return;
861: }
862:
863: /* expanded mode */
864:
865:
866: /* save the line number on one side (and remember which side) */
867: if (row >= view->rows) {
868: number = -1;
869: } else {
870: state = section_getstate(view->pLines[row].section);
871: if ((state == STATE_MOVEDRIGHT) ||
872: (state == STATE_RIGHTONLY)) {
873: bRight = TRUE;
874: number = view->pLines[row].nr_right;
875: } else {
876: bRight = FALSE;
877: number = view->pLines[row].nr_left;
878: }
879: }
880:
881: /* make the new mapping */
882: view_expand_item(view, view->ciSelect);
883:
884: /* find the nearest row in the new view */
885: if (number >= 0) {
886:
887: ViewEnter();
888: row = view_findrow(view, number, bRight);
889: ViewLeave();
890:
891: /* scroll this row to top of window */
892: if (row >= 0) {
893:
894: SendMessage(view->hwnd, TM_TOPROW, TRUE, row);
895: return;
896: }
897: }
898: }
899:
900: /***************************************************************************
901: * Function: view_changediffoptions
902: *
903: * Purpose:
904: *
905: * The compare options have changed - re-do the compare completely
906: * and make the new mapping. Retain current position in the file.
907: */
908: void
909: view_changediffoptions(VIEW view)
910: {
911: int state, number;
912: long row;
913: BOOL bRight;
914: LIST li;
915: COMPITEM ci;
916:
917: if (view == NULL) {
918: return;
919: }
920:
921: /*
922: * get current row before entering critsec.
923: */
924: row = SendMessage(view->hwnd, TM_TOPROW, FALSE, 0);
925:
926: ViewEnter();
927:
928: /* find the current line number so we can go back to it
929: * (only if we are in expanded mode
930: */
931: if (view->bExpand) {
932:
933: state = section_getstate(view->pLines[row].section);
934: if ((state == STATE_MOVEDRIGHT) ||
935: (state == STATE_RIGHTONLY)) {
936: bRight = TRUE;
937: number = view->pLines[row].nr_right;
938: } else {
939: bRight = FALSE;
940: number = view->pLines[row].nr_left;
941: }
942: }
943:
944: /* To force a recompare using the new options, we must
945: * tell each compitem to discard its current compare result.
946: * We need to traverse the list of compitems calling this
947: * for each compare.
948: */
949: li = complist_getitems(view->cl);
950:
951: for (ci = (COMPITEM) List_First(li); ci != NULL; ci = (COMPITEM) List_Next(ci)) {
952: compitem_discardsections(ci);
953: }
954:
955: /* if we are in outline mode, we have nothing more to do */
956: if (!view->bExpand) {
957: ViewLeave();
958: return;
959: }
960:
961: view_expand_item(view, view->ciSelect);
962:
963: /* find the nearest row in the new view */
964: ViewEnter();
965: row = view_findrow(view, number, bRight);
966: ViewLeave();
967:
968: /* scroll this row to top of window */
969: if (row >= 0) {
970: SendMessage(view->hwnd, TM_TOPROW, TRUE, row);
971: }
972: }
973:
974:
975: /***************************************************************************
976: * Function: view_findchange
977: *
978: * Purpose:
979: *
980: * Find the next changed - ie non-same - row in a given direction.
981: * For outline mode we find the next STATE_DIFFER. For expand mode, we
982: * find the next section
983: */
984: long
985: view_findchange(VIEW view, long startrow, BOOL bForward)
986: {
987: long i;
988:
989: if (view == NULL) {
990: return(0);
991: }
992:
993: ViewEnter();
994:
995: if (bForward) {
996:
997: if (startrow >= view->rows) {
998: ViewLeave();
999: return(-1);
1000: }
1001:
1002: if (!view->bExpand) {
1003:
1004: /* look for next compitem with an expandable state*/
1005: for (i = startrow; i < view->rows; i++) {
1006: if (compitem_getstate(view->pItems[i]) == STATE_DIFFER) {
1007: ViewLeave();
1008: return(i);
1009: }
1010: }
1011: /* none found */
1012: ViewLeave();
1013: return(-1);
1014: } else {
1015: /*
1016: * find the next line that matches, then go on to the
1017: * next line that does not match
1018: *
1019: */
1020: for (i= startrow; i < view->rows; i++) {
1021: if (section_getstate(view->pLines[i].section)
1022: == STATE_SAME) {
1023: break;
1024: }
1025: }
1026: for ( ; i < view->rows; i++) {
1027: if (section_getstate(view->pLines[i].section)
1028: != STATE_SAME) {
1029: ViewLeave();
1030: return(i);
1031: }
1032: }
1033:
1034: ViewLeave();
1035:
1036: return(-1);
1037: }
1038: } else {
1039: /* same search backwards */
1040: if (startrow <= 0) {
1041: ViewLeave();
1042: return(-1);
1043: }
1044: if (view->bExpand) {
1045: /* search backwards for first row that is not
1046: * changed (has state SAME). then carry on for
1047: * the next changed row.
1048: */
1049: for (i = startrow; i >= 0; i--) {
1050: if (section_getstate(view->pLines[i].section)
1051: == STATE_SAME) {
1052: break;
1053: }
1054: }
1055: for ( ; i >= 0; i--) {
1056: if (section_getstate(view->pLines[i].section)
1057: != STATE_SAME) {
1058: ViewLeave();
1059: return(i);
1060: }
1061: }
1062: ViewLeave();
1063: return(-1);
1064: } else {
1065: for (i = startrow; i >= 0; i--) {
1066: if(compitem_getstate(view->pItems[i]) == STATE_DIFFER) {
1067: ViewLeave();
1068: return(i);
1069: }
1070: }
1071: ViewLeave();
1072: return(-1);
1073: }
1074: }
1075: }
1076:
1077:
1078:
1079: /***************************************************************************
1080: * Function: view_findrow
1081: *
1082: * Purpose:
1083: *
1084: * Find the new row number for the line numbered 'number'
1085: * or the nearest line if possible. If bRight is true, number is
1086: * a right file number; otherwise it is a left file number.
1087: *
1088: * We must be in expand mode
1089: */
1090: int
1091: view_findrow(VIEW view, int number, BOOL bRight)
1092: {
1093: int i;
1094:
1095: if (!view->bExpand) {
1096: return(0);
1097: }
1098:
1099: for (i = 0; i < view->rows; i++) {
1100:
1101: if (bRight) {
1102: if (view->pLines[i].nr_right == number) {
1103:
1104: /* found the exact number */
1105: return(i);
1106:
1107: } else if (view->pLines[i].nr_right > number) {
1108:
1109: /* passed our line -stop here */
1110: return(i);
1111: }
1112: } else {
1113: if (view->pLines[i].nr_left == number) {
1114:
1115: /* found the exact number */
1116: return(i);
1117:
1118: } else if (view->pLines[i].nr_left > number) {
1119:
1120: /* passed our line -stop here */
1121: return(i);
1122: }
1123: }
1124: }
1125: return(-1);
1126: }
1127:
1128: /***************************************************************************
1129: * Function: view_freemappings
1130: *
1131: * Purpose:
1132: *
1133: * Free memory associated with the expand mode or outline mode mappings
1134: * called whenever we rebuild the mapping, and on deletion
1135: */
1136: void
1137: view_freemappings(VIEW view)
1138: {
1139:
1140: if (view->pLines) {
1141: gmem_free(hHeap, (LPSTR) view->pLines,
1142: view->rows * sizeof(VIEWLINE));
1143: view->pLines = NULL;
1144: } else if (view->pItems) {
1145:
1146: /* previous outline mapping array is still there - free it
1147: * before we build a new one
1148: */
1149:
1150: gmem_free(hHeap, (LPSTR) view->pItems,
1151: view->rows * sizeof(COMPLIST));
1152: view->pItems = NULL;
1153: }
1154: }
1155:
1156: /***************************************************************************
1157: * Function: view_outline_opt
1158: *
1159: * Purpose:
1160: *
1161: * Build a view outline to map one row to a COMPITEM handle by traversing
1162: * the list of COMPITEMs obtained from our complist.
1163: * Optionally tell the table class to redraw (if bRedraw), and if so,
1164: * scroll the new table to select the row that represents the
1165: * file we were expanding, if possible
1166: */
1167: void
1168: view_outline_opt(VIEW view, BOOL bRedraw)
1169: {
1170: int prev_row = -1; /* the row nr of the previously-expanded row*/
1171: int i; /* nr of includable items */
1172: LIST li;
1173: COMPITEM ci;
1174: int state;
1175: TableSelection select;
1176:
1177: /*
1178: * check that view_setcomplist has already been called. if not,
1179: * nothing to do
1180: */
1181: if (view->cl == NULL) {
1182: return;
1183: }
1184:
1185: ViewEnter();
1186:
1187: /* clear the mode flag and free up memory associated with expand mode */
1188: view->bExpand = FALSE;
1189: view_freemappings(view);
1190:
1191: /* traverse the list of compitems counting up the number of
1192: * includable items
1193: */
1194: li = complist_getitems(view->cl);
1195:
1196: ci = (COMPITEM) List_First(li);
1197: for (i = 0; ci != NULL; ci = (COMPITEM) List_Next(ci)) {
1198:
1199: state = compitem_getstate(ci);
1200:
1201: if (((outline_include & INCLUDE_SAME) && (state == STATE_SAME)) ||
1202: ((outline_include & INCLUDE_DIFFER) && (state == STATE_DIFFER)) ||
1203: ((outline_include & INCLUDE_LEFTONLY) && (state == STATE_FILELEFTONLY)) ||
1204: ((outline_include & INCLUDE_RIGHTONLY) && (state == STATE_FILERIGHTONLY))) {
1205: i++;
1206: }
1207: }
1208:
1209:
1210: /* allocate an array big enough for all of these */
1211: view->pItems = (COMPITEM FAR *) gmem_get(hHeap, i * sizeof(COMPITEM));
1212: view->rows = i;
1213:
1214: /* keep track of the column widths */
1215: view->maxtag = 0;
1216: view->maxrest = 0;
1217:
1218: /* loop through again filling the array, and at the same time looking
1219: * out for the handle of the previously expanded item
1220: */
1221: ci = (COMPITEM) List_First(li);
1222: for (i = 0; ci != NULL; ci = (COMPITEM) List_Next(ci)) {
1223:
1224: state = compitem_getstate(ci);
1225:
1226: if (((outline_include & INCLUDE_SAME) && (state == STATE_SAME)) ||
1227: ((outline_include & INCLUDE_DIFFER) && (state == STATE_DIFFER)) ||
1228: ((outline_include & INCLUDE_LEFTONLY) && (state == STATE_FILELEFTONLY)) ||
1229: ((outline_include & INCLUDE_RIGHTONLY) && (state == STATE_FILERIGHTONLY))) {
1230:
1231: view->pItems[i] = ci;
1232:
1233: if (ci == view->ciSelect) {
1234: prev_row = i;
1235: }
1236:
1237: /* check the column widths in characters */
1238: view->maxtag = max(view->maxtag,
1239: lstrlen(compitem_gettext_tag(ci)));
1240: view->maxrest = max(view->maxrest,
1241: lstrlen(compitem_gettext_result(ci)));
1242:
1243:
1244: i++;
1245:
1246: }
1247: }
1248: ViewLeave();
1249:
1250: /* inform table of new layout of table - force refresh */
1251: if (bRedraw) {
1252: SendMessage(view->hwnd, TM_NEWLAYOUT, 0, (DWORD) view);
1253:
1254: /* scroll to and highlight the row that represents the file
1255: * we were previously expanding
1256: */
1257: if (prev_row != -1) {
1258: select.startrow = prev_row;
1259: select.startcell = 0;
1260: select.nrows = 1;
1261: select.ncells = 1;
1262: SendMessage(view->hwnd, TM_SELECT, 0,
1263: (DWORD) (LPSTR) &select);
1264: }
1265: }
1266: }
1267:
1268:
1269: /***************************************************************************
1270: * Function: view_expand_item
1271: *
1272: * Purpose:
1273: *
1274: * Expand a view - given the handle to the compitem to expand.
1275: *
1276: * Called from view_expand, and also to re-do an expanded view
1277: * after options change in view_changediffoptions and _changeviewoptions
1278: *
1279: * We get the composite section list from the compitem,
1280: * and pick out all the sections that are includable (according
1281: * to the global option expand_mode: we include all sections, or
1282: * just those in one side left or right). Once we know the count of rows,
1283: * allocate the mapping array: in each element of the array we keep
1284: * a handle to the section for that row (to get the state and hence the
1285: * tag text), and a handle to the line within that section (for the line text).
1286: *
1287: * We no longer insist on only expanding text files that differ - if the
1288: * compitem can give us a composite section list, we will map it.
1289: *
1290: * We need to be able to give a line number for a line, in either of
1291: * the original files according to which option is in force. Each section
1292: * can give us its base line number (number of first line in section) in
1293: * each of the two files or 0 if not present, and we track these here.
1294: *
1295: * MUST BE INSIDE CSView BEFORE CALLING HERE.
1296: */
1297: BOOL
1298: view_expand_item(VIEW view, COMPITEM ci)
1299: {
1300: LIST li;
1301: SECTION sh;
1302: LINE line1, line2;
1303: int i, base_left, base_right, state;
1304:
1305: /* remember the compitem we are expanding */
1306: view->ciSelect = ci;
1307:
1308: /* get the composite section list */
1309: li = compitem_getcomposite(view->ciSelect);
1310: if (li == NULL) {
1311: ViewLeave();
1312: return FALSE;
1313: }
1314:
1315: /* switch modes and free the current mapping
1316: *
1317: * NOTE: must do this AFTER the compitem_getcomposite,
1318: * since that can fail: if it fails it could put up a
1319: * message box, and that could cause a queued paint message
1320: * to be processed, which would cause us to use these mappings
1321: * and gpfault if they had been cleared first.
1322: */
1323: view->bExpand = TRUE;
1324: view_freemappings(view);
1325:
1326:
1327: /* loop through totalling the lines in sections
1328: * that we should include
1329: */
1330: view->rows = 0;
1331: for (sh = (SECTION) List_First(li); sh != NULL;
1332: sh = (SECTION) List_Next(sh)) {
1333:
1334: state = section_getstate(sh);
1335:
1336: if (expand_mode == IDM_RONLY) {
1337: if ((state == STATE_LEFTONLY) ||
1338: (state == STATE_MOVEDLEFT)) {
1339: continue;
1340: }
1341: } else if (expand_mode == IDM_LONLY) {
1342: if ((state == STATE_RIGHTONLY) ||
1343: (state == STATE_MOVEDRIGHT)) {
1344: continue;
1345: }
1346: }
1347:
1348: /* include all lines in this section */
1349: view->rows += section_getlinecount(sh);
1350: }
1351:
1352: /* allocate the memory for the mapping array */
1353: view->pLines = (PVIEWLINE) gmem_get(hHeap, view->rows * sizeof(VIEWLINE));
1354:
1355: /* loop through the sections again filling in the mapping array */
1356: i = 0;
1357: view->maxtag = 5;
1358: view->maxrest = 0;
1359: for (sh = (SECTION) List_First(li); sh != NULL;
1360: sh = (SECTION) List_Next(sh)) {
1361:
1362: state = section_getstate(sh);
1363:
1364: if (expand_mode == IDM_RONLY) {
1365: if ((state == STATE_LEFTONLY) ||
1366: (state == STATE_MOVEDLEFT)) {
1367: continue;
1368: }
1369: } else if (expand_mode == IDM_LONLY) {
1370: if ((state == STATE_RIGHTONLY) ||
1371: (state == STATE_MOVEDRIGHT)) {
1372: continue;
1373: }
1374: }
1375:
1376: /* find the base line number in each file */
1377: base_left = section_getleftbasenr(sh);
1378: base_right = section_getrightbasenr(sh);
1379:
1380: /* add each line in section to the view. section_getfirst()
1381: * returns us to a handle that is in a list. We can
1382: * call List_Next and will eventually get to the
1383: * line returned by section_getlast(). Sections always have
1384: * at least one line
1385: */
1386: line1 = section_getfirstline(sh);
1387: line2 = section_getlastline(sh);
1388:
1389: for (; line1 != NULL; line1 = (LINE) List_Next(line1)) {
1390:
1391: view->pLines[i].line = line1;
1392: view->pLines[i].section = sh;
1393:
1394: /* calculate the line number for this line by
1395: * incrementing the base nr for this section
1396: */
1397:
1398: view->pLines[i].nr_left = base_left;
1399: if (base_left != 0) {
1400: base_left++;
1401: }
1402:
1403: view->pLines[i].nr_right = base_right;
1404: if (base_right != 0) {
1405: base_right++;
1406: }
1407:
1408: /* increment index into view */
1409: i++;
1410:
1411: /* check the column widths */
1412: view->maxrest = max(view->maxrest,
1413: (line_gettabbedlength(line1, 8)));
1414:
1415: /* end of section ? */
1416: if (line1 == line2) {
1417: break;
1418: }
1419: }
1420: }
1421:
1422: /* We must NOT hold a critical section here as SendMessage may hang */
1423: ViewLeave();
1424:
1425: /*inform table window of revised mapping */
1426: SendMessage(view->hwnd, TM_NEWLAYOUT, 0, (DWORD) view);
1427:
1428: return(TRUE);
1429: }
1430:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.