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

  1.  
  2. /******************************************************************************\
  3. *       This is a part of the Microsoft Source Code Samples. 
  4. *       Copyright (C) 1993-1997 Microsoft Corporation.
  5. *       All rights reserved. 
  6. *       This source code is only intended as a supplement to 
  7. *       Microsoft Development Tools and/or WinHelp documentation.
  8. *       See these sources for detailed information regarding the 
  9. *       Microsoft samples programs.
  10. \******************************************************************************/
  11.  
  12. /****************************** Module Header *******************************
  13. * Module Name: 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.  
  1431.