VIEW.C
上传用户:bangxh
上传日期:2007-01-31
资源大小:42235k
文件大小:44k
源码类别:

Windows编程

开发平台:

Visual C++

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