1773 lines
48 KiB
C++
1773 lines
48 KiB
C++
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
|
|
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
|
|
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
|
|
// PARTICULAR PURPOSE.
|
|
//
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
|
|
/*
|
|
* view.cpp
|
|
*
|
|
* map rows in window to items in COMPLIST
|
|
*
|
|
*
|
|
* A view owns a COMPLIST, and talks to a table window. The table window
|
|
* shows 3 columns: line nr, tag and text. We also need to supply a state
|
|
* for each row (used to select colour scheme).
|
|
*
|
|
* The COMPLIST can give us a list of its COMPITEMs. Each of these can give
|
|
* us a tag (eg the filenames compared) and the text (usually the compare
|
|
* result), and the state. We make the line number from the
|
|
* COMPITEM's place in the list.
|
|
*
|
|
* If we are asked to switch to 'expand' mode, we ask the selected COMPITEM
|
|
* for its composite section list. We can then get the state (and thus
|
|
* the tag) from each SECTION, and the line nr and text from the LINEs within
|
|
* each section.
|
|
*
|
|
* When moving between expand and outline, and when refreshing the view
|
|
* for some option change, we have to be careful to keep the current row
|
|
* and the selected row in the table what the user would expect (!)
|
|
*
|
|
*
|
|
* WIN32: Functions in this module can be called from the UI thread (to refresh
|
|
* the display) and simultaneously from a worker thread to update the
|
|
* view mapping (view_setcomplist, view_newitem). We use a critical section
|
|
* to manage the synchronisation. We need to protect all access/modification
|
|
* to the view structure elements (particularly bExpand, rows, pLines and
|
|
* pItems), BUT we must not hold the critical section over any calls
|
|
* to SendMessage.
|
|
*
|
|
*/
|
|
|
|
#include "precomp.h"
|
|
#include "table.h"
|
|
|
|
#include "state.h"
|
|
#include "sdkdiff.h"
|
|
#include "wdiffrc.h"
|
|
|
|
#include "list.h"
|
|
#include "line.h"
|
|
#include "scandir.h"
|
|
#include "file.h"
|
|
#include "section.h"
|
|
#include "compitem.h"
|
|
#include "complist.h"
|
|
#include "findgoto.h"
|
|
|
|
#include "view.h"
|
|
|
|
/*
|
|
* data structures
|
|
*/
|
|
#ifdef WIN32
|
|
#define huge
|
|
#endif
|
|
|
|
/* in expand mode, we keep an array of one of these per screen line. */
|
|
typedef struct viewline {
|
|
LINE line; /* handle to LINE for this row */
|
|
SECTION section; /* handle to section containing this line */
|
|
int nr_left; /* line nr in left file */
|
|
int nr_right; /* line nr in right file */
|
|
} VIEWLINE, * PVIEWLINE;
|
|
|
|
|
|
/*
|
|
* The users VIEW handle is in fact a pointer to this structure
|
|
*/
|
|
struct view {
|
|
|
|
HWND hwnd; /* the table window to send notifies to */
|
|
|
|
COMPLIST cl; /* the complist that we own */
|
|
|
|
BOOL bExpand; /* true if we are in expand mode */
|
|
BOOL bExpanding; /* set by view_expandstart, reset by view_expand
|
|
interrogated in sdkdiff.cpp, causes keystrokes
|
|
to be ignored. Protects against mappings being
|
|
messed up by another thread.
|
|
(I have doubts about this - Laurie).
|
|
*/
|
|
BOOL bExpandGuard; /* Protects against two threads both trying to
|
|
expand the same item.
|
|
*/
|
|
|
|
COMPITEM ciSelect; /* selected compitem (in expand mode) */
|
|
|
|
int rows; /* number of rows in this view */
|
|
|
|
char nrtext[12]; /* we use this in view_gettext for the line
|
|
* number column. overwritten on each call
|
|
*/
|
|
int maxtag, maxrest;/* column widths in characters for cols 1, 2 */
|
|
|
|
/* if we are in outline mode, we map the row number to one entry
|
|
* in this array of COMPITEM handles. this pointer will
|
|
* be NULL in expand mode
|
|
*/
|
|
COMPITEM * pItems;
|
|
|
|
/* in expand mode we use this array of line and section handles */
|
|
PVIEWLINE pLines;
|
|
};
|
|
|
|
CRITICAL_SECTION CSView; /* also known to Sdkdiff.cpp WM_EXIT processing */
|
|
static BOOL bDoneInit = FALSE;
|
|
|
|
#define ViewEnter() EnterCriticalSection(&CSView);
|
|
#define ViewLeave() LeaveCriticalSection(&CSView);
|
|
|
|
extern long selection;
|
|
extern long selection_nrows;
|
|
|
|
/*------- forward declaration of internal functions ----------------*/
|
|
|
|
void view_outline_opt(VIEW view, BOOL bRedraw, COMPITEM ci, int* prow);
|
|
void view_freemappings(VIEW view);
|
|
int view_findrow(VIEW view, int number, BOOL bRight);
|
|
BOOL view_expand_item(VIEW view, COMPITEM ci);
|
|
|
|
|
|
/* ----- externally-called functions---------------------------*/
|
|
/* view_new
|
|
*
|
|
* create a new view. at this point, we are told the table window handle,
|
|
* and nothing else.
|
|
*
|
|
*/
|
|
VIEW
|
|
view_new(HWND hwndTable)
|
|
{
|
|
VIEW view;
|
|
|
|
if (!bDoneInit) {
|
|
InitializeCriticalSection(&CSView);
|
|
bDoneInit = TRUE;
|
|
}
|
|
|
|
/* alloc the view using HeapAlloc */
|
|
view = (VIEW) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(struct view));
|
|
if (view == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
/* set the default fields */
|
|
view->hwnd = hwndTable;
|
|
view->cl = NULL;
|
|
view->bExpand = FALSE;
|
|
view->bExpandGuard = FALSE;
|
|
view->ciSelect = NULL;
|
|
view->rows = 0;
|
|
view->pItems = NULL;
|
|
view->pLines = NULL;
|
|
|
|
// erase the message in the names field - the central box in the status bar
|
|
SetNames("");
|
|
return(view);
|
|
}
|
|
|
|
|
|
/*
|
|
* view_setcomplist.
|
|
*
|
|
* We have to separate view_new and view_setcomplist because we need
|
|
* to give the view handle to the complist and the complist handle to the
|
|
* view. So do a view_new to create a null view; then complist_new() to
|
|
* which you pass a view handle. The complist will then register itself
|
|
* with the view by calling this function. During the build of the complist,
|
|
* it will also update us by calling view_additem, so that we can refresh
|
|
* the display.
|
|
*
|
|
* Here we should initialise an outline view of the complist.
|
|
*
|
|
* We also talk to the status bar using SetNames to set the names of
|
|
* the two items.
|
|
*/
|
|
BOOL
|
|
view_setcomplist(VIEW view, COMPLIST cl)
|
|
{
|
|
LPSTR both;
|
|
|
|
if (view == NULL) {
|
|
return(FALSE);
|
|
}
|
|
|
|
/* there can be only one call to this per VIEW */
|
|
if (view->cl != NULL) {
|
|
return(FALSE);
|
|
}
|
|
|
|
ViewEnter();
|
|
|
|
view->cl = cl;
|
|
|
|
/* set names on status bar to root names of left and right trees */
|
|
both = complist_getdescription(cl);
|
|
ViewLeave(); // LKGHACK
|
|
SetNames(both);
|
|
ViewEnter(); // LKGHACK
|
|
complist_freedescription(cl, both);
|
|
|
|
ViewLeave();
|
|
|
|
view_outline(view);
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/*
|
|
* return a handle to the complist owned by this view
|
|
*/
|
|
COMPLIST
|
|
view_getcomplist(VIEW view)
|
|
{
|
|
if (view == NULL) {
|
|
return(NULL);
|
|
}
|
|
|
|
return(view->cl);
|
|
}
|
|
|
|
|
|
/*
|
|
* close a view. notify the table window that this view should be
|
|
* closed. When the table window has finished with it, it will send
|
|
* a TQ_CLOSE notify that should result in view_delete being called
|
|
* and the memory being freed.
|
|
*/
|
|
void
|
|
view_close(VIEW view)
|
|
{
|
|
if (view == NULL) {
|
|
return;
|
|
}
|
|
|
|
SendMessage(view->hwnd, TM_NEWID, 0, 0);
|
|
}
|
|
|
|
|
|
/*
|
|
* delete a view and all associated data.
|
|
*
|
|
* This function should only be called in response to the table window
|
|
* sending a TQ_CLOSE message. To close the view, call view_close and
|
|
* wait for the TQ_CLOSE before calling this.
|
|
*
|
|
* We delete the associated COMPLIST and all its associated structures.
|
|
*/
|
|
void
|
|
view_delete(VIEW view)
|
|
{
|
|
if (view == NULL) {
|
|
return;
|
|
}
|
|
|
|
/* we have two arrays that are used for the mapping - an array
|
|
* of compitem handles in outline mode, and an array of
|
|
* VIEWLINE structures in expand mode
|
|
*/
|
|
|
|
view_freemappings(view);
|
|
|
|
complist_delete(view->cl);
|
|
|
|
HeapFree(GetProcessHeap(), NULL, view);
|
|
}
|
|
|
|
|
|
/*
|
|
* view_outline
|
|
*
|
|
* build an outline mode mapping where one row represents one COMPITEM in
|
|
* the list. check the global option flag outline_include to see which items
|
|
* we should include.
|
|
*
|
|
* If we were in expand mode, then set as the selection the row in outline mode
|
|
* that we were expanding. Also remember to free up the expand mode mapping
|
|
* array
|
|
*
|
|
* once we have built the new mapping, notify the table window to
|
|
* redraw itself.
|
|
*/
|
|
void
|
|
view_outline(VIEW view)
|
|
{
|
|
if (view == NULL) {
|
|
return;
|
|
}
|
|
|
|
/* all work done by view_outline_opt - this function
|
|
* gives us the option of not updating the display
|
|
*/
|
|
view_outline_opt(view, TRUE, NULL, NULL);
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* switch to expand mode, expanding the given row into a view
|
|
* of the differences in that file.
|
|
*
|
|
* map the given row nr into a compitem handle, and then
|
|
* call the internal function with that.
|
|
*
|
|
* It is legal (and a no-op) if this function is called with
|
|
* row==-1.
|
|
*/
|
|
BOOL
|
|
view_expand(VIEW view, long row)
|
|
{
|
|
COMPITEM ci;
|
|
BOOL bRet;
|
|
|
|
if (row<0) return FALSE;
|
|
|
|
ViewEnter();
|
|
|
|
if ((view == NULL) || (view->bExpand)) {
|
|
/* no view, or already expanded */
|
|
ViewLeave();
|
|
return(FALSE);
|
|
}
|
|
|
|
if (row >= view->rows) {
|
|
/* no such row */
|
|
ViewLeave();
|
|
return FALSE;
|
|
}
|
|
|
|
/* remember the compitem we are expanding */
|
|
ci = view->pItems[row];
|
|
|
|
bRet = view_expand_item(view, ci);
|
|
return(bRet);
|
|
}
|
|
|
|
|
|
/*
|
|
* return the text associated with a given column of a given row.
|
|
* Return a pointer that does not need to be freed after use - ie
|
|
* a pointer into our data somewhere, not a copy
|
|
*/
|
|
LPSTR
|
|
view_gettext(VIEW view, long row, int col)
|
|
{
|
|
int line;
|
|
int state;
|
|
LPSTR pstr;
|
|
HRESULT hr;
|
|
|
|
pstr = NULL; /* kill spurious diagnostic */
|
|
if (view == NULL) {
|
|
return(NULL);
|
|
}
|
|
|
|
ViewEnter();
|
|
|
|
if ((0 > row) || (row >= view->rows)) {
|
|
ViewLeave();
|
|
return(NULL);
|
|
}
|
|
|
|
if (view->bExpand) {
|
|
/* we are in expand mode */
|
|
|
|
state = section_getstate(view->pLines[row].section);
|
|
|
|
switch (col) {
|
|
case 0:
|
|
/* row nr */
|
|
|
|
/* line numbers can be from either original file
|
|
* this is a menu-selectable option
|
|
*/
|
|
line = 0;
|
|
switch (line_numbers) {
|
|
case IDM_NONRS:
|
|
pstr = NULL;
|
|
break;
|
|
|
|
case IDM_LNRS:
|
|
line = view->pLines[row].nr_left;
|
|
if (state == STATE_MOVEDRIGHT
|
|
|| state == STATE_SIMILARRIGHT) {
|
|
line = -line;
|
|
}
|
|
break;
|
|
|
|
case IDM_RNRS:
|
|
line = view->pLines[row].nr_right;
|
|
if (state == STATE_MOVEDLEFT
|
|
|| state == STATE_SIMILARLEFT) {
|
|
line = -line;
|
|
}
|
|
break;
|
|
}
|
|
if (line == 0) {
|
|
ViewLeave();
|
|
return(NULL);
|
|
}
|
|
|
|
if (line < 0) {
|
|
/* lines that are moved appear twice.
|
|
* show the correct-sequence line nr
|
|
* for the out-of-seq. copy in brackets.
|
|
*/
|
|
hr = StringCchPrintf(view->nrtext, 12, "(%d)", abs(line));
|
|
if (FAILED(hr)) {
|
|
OutputError(hr, IDS_SAFE_PRINTF);
|
|
return(NULL);
|
|
}
|
|
|
|
} else {
|
|
hr = StringCchPrintf(view->nrtext, 12, "%d", line);
|
|
if (FAILED(hr)) {
|
|
OutputError(hr, IDS_SAFE_PRINTF);
|
|
return(NULL);
|
|
}
|
|
}
|
|
pstr = view->nrtext;
|
|
break;
|
|
|
|
case 1:
|
|
/* tag text - represents the state of the line */
|
|
|
|
|
|
switch (state) {
|
|
case STATE_SAME:
|
|
pstr = " ";
|
|
break;
|
|
|
|
case STATE_LEFTONLY:
|
|
case STATE_SIMILARLEFT:
|
|
pstr = " <! ";
|
|
break;
|
|
|
|
case STATE_RIGHTONLY:
|
|
case STATE_SIMILARRIGHT:
|
|
pstr = " !> ";
|
|
break;
|
|
|
|
case STATE_MOVEDLEFT:
|
|
pstr = " <- ";
|
|
break;
|
|
|
|
case STATE_MOVEDRIGHT:
|
|
pstr = " -> ";
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case 2:
|
|
/* main text - line */
|
|
pstr = line_gettext(view->pLines[row].line);
|
|
break;
|
|
}
|
|
} else {
|
|
/* outline mode */
|
|
switch (col) {
|
|
case 0:
|
|
/* row number - just the line number */
|
|
hr = StringCchPrintf(view->nrtext, 12, "%d", row+1);
|
|
if (FAILED(hr)) {
|
|
OutputError(hr, IDS_SAFE_PRINTF);
|
|
return(NULL);
|
|
}
|
|
pstr = view->nrtext;
|
|
break;
|
|
|
|
case 1:
|
|
/* tag */
|
|
pstr = compitem_gettext_tag(view->pItems[row]);
|
|
break;
|
|
|
|
case 2:
|
|
/* result text */
|
|
pstr = compitem_gettext_result(view->pItems[row]);
|
|
break;
|
|
}
|
|
}
|
|
ViewLeave();
|
|
return(pstr);
|
|
}
|
|
|
|
|
|
/*
|
|
* return the text associated with a given column of a given row.
|
|
* Return a pointer that does not need to be freed after use - ie
|
|
* a pointer into our data somewhere, not a copy
|
|
*/
|
|
LPWSTR
|
|
view_gettextW(VIEW view, long row, int col)
|
|
{
|
|
int state;
|
|
LPWSTR pwz;
|
|
|
|
pwz = NULL; /* kill spurious diagnostic */
|
|
if (view == NULL) {
|
|
return(NULL);
|
|
}
|
|
|
|
ViewEnter();
|
|
|
|
if ((0 > row) || (row >= view->rows)) {
|
|
ViewLeave();
|
|
return(NULL);
|
|
}
|
|
|
|
if (view->bExpand) {
|
|
/* we are in expand mode */
|
|
|
|
state = section_getstate(view->pLines[row].section);
|
|
|
|
switch (col) {
|
|
case 2:
|
|
/* main text - line */
|
|
pwz = line_gettextW(view->pLines[row].line);
|
|
break;
|
|
}
|
|
}
|
|
ViewLeave();
|
|
return(pwz);
|
|
}
|
|
|
|
/*
|
|
* return the line number that this row had in the original left
|
|
* file. 0 if not in expand mode. 0 if this row was not in the left file.
|
|
* -(linenr) if this row is a MOVED line, and this is the right file
|
|
* copy
|
|
*/
|
|
int
|
|
view_getlinenr_left(VIEW view, long row)
|
|
{
|
|
int state, line;
|
|
|
|
if ((0> row) || (view == NULL) || (row >= view->rows) || !view->bExpand) {
|
|
return 0;
|
|
}
|
|
|
|
ViewEnter();
|
|
state = section_getstate(view->pLines[row].section);
|
|
line = view->pLines[row].nr_left;
|
|
if (state == STATE_MOVEDRIGHT || state == STATE_SIMILARRIGHT) {
|
|
line = -line;
|
|
}
|
|
ViewLeave();
|
|
|
|
return(line);
|
|
}
|
|
|
|
/*
|
|
* return the line number that this row had in the original right
|
|
* file. 0 if not in expand mode. 0 if this row was not in the right file.
|
|
* -(linenr) if this row is a MOVED line, and this is the left file
|
|
* copy
|
|
*/
|
|
int
|
|
view_getlinenr_right(VIEW view, long row)
|
|
{
|
|
int state, line;
|
|
|
|
if ((0 > row) || (view == NULL) || (row > view->rows) || !view->bExpand) {
|
|
return 0;
|
|
}
|
|
|
|
ViewEnter();
|
|
|
|
state = section_getstate(view->pLines[row].section);
|
|
line = view->pLines[row].nr_right;
|
|
if (state == STATE_MOVEDLEFT || state == STATE_SIMILARLEFT) {
|
|
line = -line;
|
|
}
|
|
ViewLeave();
|
|
|
|
return(line);
|
|
}
|
|
|
|
|
|
|
|
/* find the maximum width in characters for the given column */
|
|
int
|
|
view_getwidth(VIEW view, int col)
|
|
{
|
|
if (view == NULL) {
|
|
return(0);
|
|
}
|
|
|
|
switch (col) {
|
|
case 0:
|
|
/* line nr column - always 5 characters wide */
|
|
return(5);
|
|
|
|
case 1:
|
|
/* this is a proportional font field, so add on a margin
|
|
* for error
|
|
*/
|
|
return(view->maxtag + (view->maxtag / 20));
|
|
case 2:
|
|
/* this now includes the tab expansion allowance */
|
|
return(view->maxrest);
|
|
default:
|
|
return(0);
|
|
}
|
|
}
|
|
|
|
/* how many rows are there in this view ? */
|
|
long
|
|
view_getrowcount(VIEW view)
|
|
{
|
|
if (view == NULL) {
|
|
return(0);
|
|
}
|
|
|
|
return(view->rows);
|
|
}
|
|
|
|
/* return the state for the current row. This is used
|
|
* to select the text colour for the row
|
|
*
|
|
* states for sections are obtained from section_getstate (and apply, and
|
|
* to all lines in that section. States for compitems are obtained
|
|
* from compitem_getstate.
|
|
*/
|
|
int
|
|
view_getstate(VIEW view, long row)
|
|
{
|
|
int state;
|
|
|
|
if (view == NULL) {
|
|
return(0);
|
|
}
|
|
|
|
ViewEnter();
|
|
if ( (row >= view->rows) || (row < 0)) {
|
|
state = 0;
|
|
} else if (view->bExpand) {
|
|
/* its a line state that's needed */
|
|
state = section_getstate(view->pLines[row].section);
|
|
} else {
|
|
|
|
/* its a compitem state */
|
|
state = compitem_getstate(view->pItems[row]);
|
|
}
|
|
ViewLeave();
|
|
return(state);
|
|
}
|
|
|
|
/*
|
|
* return the marked state for a given row. Only compitems can be marked,
|
|
* so it will be FALSE unless it is a compitem on which view_setmark or
|
|
* compitem_setmark have previously set the mark state to TRUE.
|
|
*/
|
|
BOOL
|
|
view_getmarkstate(VIEW view, long row)
|
|
{
|
|
BOOL bMark = FALSE;
|
|
|
|
if (view != NULL) {
|
|
ViewEnter();
|
|
if ( (0 < row) && (row < view->rows) && (!view->bExpand)) {
|
|
bMark = compitem_getmark(view->pItems[row]);
|
|
}
|
|
ViewLeave();
|
|
}
|
|
return(bMark);
|
|
}
|
|
|
|
/*
|
|
* set the mark state for a given row. This is only possible for compitem rows.
|
|
* The mark set can be retrieved by view_getmarkstate or compitem_getmark.
|
|
*
|
|
* We return FALSE if the state could not be set - eg because the
|
|
* row to set is not a compitem row.
|
|
*/
|
|
BOOL
|
|
view_setmarkstate(VIEW view, long row, BOOL bMark)
|
|
{
|
|
BOOL bOK = FALSE;
|
|
|
|
if (view != NULL) {
|
|
ViewEnter();
|
|
if ( (0 < row) && (0 <= view->rows) && (row < view->rows) && !view->bExpand) {
|
|
compitem_setmark(view->pItems[row], bMark);
|
|
bOK = TRUE;
|
|
}
|
|
ViewLeave();
|
|
}
|
|
return(bOK);
|
|
}
|
|
|
|
|
|
/* return a handle to the current compitem. in expand mode,
|
|
* returns the handle to the compitem we are expanding. In outline
|
|
* mode, returns the handle to the compitem for the given row, if valid,
|
|
* or NULL otherwise. row is only used if not in expand mode.
|
|
*/
|
|
COMPITEM
|
|
view_getitem(VIEW view, long row)
|
|
{
|
|
COMPITEM ci;
|
|
|
|
if (view == NULL) {
|
|
return(NULL);
|
|
}
|
|
|
|
ViewEnter();
|
|
|
|
if (!view->bExpand) {
|
|
if ((row >= 0) && (row < view->rows)) {
|
|
ci = view->pItems[row];
|
|
} else {
|
|
ci = NULL;
|
|
}
|
|
} else {
|
|
ci = view->ciSelect;
|
|
}
|
|
|
|
ViewLeave();
|
|
return(ci);
|
|
}
|
|
|
|
/*
|
|
* return TRUE if the current mapping is expanded mode
|
|
*/
|
|
BOOL
|
|
view_isexpanded(VIEW view)
|
|
{
|
|
if (view == NULL) {
|
|
return(FALSE);
|
|
}
|
|
return(view->bExpand);
|
|
}
|
|
|
|
|
|
/*
|
|
* return a text string describing the view. This is NULL in outline mode,
|
|
* or the tag text for the current compitem in expanded mode
|
|
*/
|
|
LPSTR
|
|
view_getcurrenttag(VIEW view)
|
|
{
|
|
LPSTR str;
|
|
|
|
if ((view == NULL) || (!view->bExpand)) {
|
|
return(NULL);
|
|
} else {
|
|
ViewEnter();
|
|
|
|
str = compitem_gettext_tag(view->ciSelect);
|
|
|
|
ViewLeave();
|
|
return(str);
|
|
|
|
}
|
|
}
|
|
|
|
|
|
/* notify that CompItems have been added to the complist.
|
|
*
|
|
* rebuild the view (if in outline mode), and refresh the table. Use
|
|
* the table message TM_APPEND if possible (if column widths have not
|
|
* change). If we have to do TM_NEWLAYOUT, then ensure we scroll
|
|
* back to the right row afterwards.
|
|
*
|
|
* This causes a Poll() to take place. We return TRUE if an abort is
|
|
* pending - in this case, the caller should abandon the scan loop.
|
|
*
|
|
* WIN32: enter the critical section for this function since this can be
|
|
* called from the worker thread while the UI thread is using the
|
|
* view that we are about to change.
|
|
*
|
|
* We cannot ever call SendMessage from the
|
|
* worker thread within CSView. If there is conflict, it will hang.
|
|
*
|
|
*/
|
|
BOOL
|
|
view_newitem(VIEW view)
|
|
{
|
|
int maxtag, maxrest;
|
|
int rownr;
|
|
TableSelection Select;
|
|
BOOL bSelect;
|
|
COMPITEM ciTop = NULL;
|
|
BOOL bRedraw = FALSE;
|
|
BOOL bAppend = FALSE;
|
|
|
|
// get the top row before remapping in case we need it
|
|
/* find the row at the top of the window */
|
|
rownr = (long) SendMessage(view->hwnd, TM_TOPROW, FALSE, 0);
|
|
// also remember the selection
|
|
bSelect = (BOOL) SendMessage(view->hwnd, TM_GETSELECTION, 0, (LPARAM) &Select);
|
|
|
|
// *important*:no critsec over SendMessage
|
|
ViewEnter();
|
|
|
|
if ((view != NULL) &&
|
|
!(view->bExpand) &&
|
|
!(view->bExpanding)) {
|
|
|
|
/* save some state about the present mapping */
|
|
maxtag = view->maxtag;
|
|
maxrest = view->maxrest;
|
|
|
|
|
|
// remember the compitem this corresponds to
|
|
if (view->pItems && (rownr >= 0) && (rownr < view->rows)) {
|
|
ciTop = view->pItems[rownr];
|
|
}
|
|
|
|
|
|
// re-do the outline mapping, but don't tell the table class.
|
|
// ask it to check for the visible row closest to ciTop in case
|
|
// we need to refresh the display
|
|
//
|
|
// since we are holding the critsec, the redraw param
|
|
// *must* be false.
|
|
view_outline_opt(view, FALSE, ciTop, &rownr);
|
|
|
|
/* have the column widths changed ? */
|
|
if ((maxtag < view->maxtag) || (maxrest < view->maxrest)) {
|
|
/* yes - need complete redraw */
|
|
bRedraw = TRUE;
|
|
} else {
|
|
bAppend = TRUE;
|
|
}
|
|
}
|
|
|
|
ViewLeave();
|
|
|
|
|
|
if (bRedraw) {
|
|
|
|
/* switch to new mapping */
|
|
SendMessage(view->hwnd, TM_NEWLAYOUT, 0, (LPARAM) view);
|
|
|
|
// go to the visible row closest to the old top row
|
|
if ((rownr >= 0) && (rownr < view->rows)) {
|
|
SendMessage(view->hwnd, TM_TOPROW, TRUE, rownr);
|
|
}
|
|
|
|
// select the old selection too (if the table class allowed
|
|
// us to get it)
|
|
if (bSelect) {
|
|
SendMessage(view->hwnd, TM_SELECT,0, (LPARAM) &Select);
|
|
}
|
|
|
|
} else if (bAppend) {
|
|
/* we can just append */
|
|
|
|
/*
|
|
* in the WIN32 multiple threads case, the mapping may have
|
|
* changed since we released the critsec. however we are still
|
|
* safe. The table will not allow us to reduce the number of
|
|
* rows, so the worst that can happen is that the table will
|
|
* think there are too many rows, and the table message handler
|
|
* will handle this correctly (return null for the text).
|
|
* The only visible effect is therefore that the scrollbar
|
|
* position is wrong.
|
|
*/
|
|
|
|
SendMessage(view->hwnd, TM_APPEND, view->rows, (LPARAM) view);
|
|
}
|
|
|
|
return(Poll());
|
|
}
|
|
|
|
/*
|
|
* the view mapping options (eg outline_include, expand_mode) have changed -
|
|
* re-do the mapping and then scroll back to the same position in the window
|
|
* if possible.
|
|
*/
|
|
void
|
|
view_changeviewoptions(VIEW view)
|
|
{
|
|
long row;
|
|
int state, number;
|
|
BOOL bRight;
|
|
|
|
if (view == NULL) {
|
|
return;
|
|
}
|
|
|
|
/* find what row we are currently on. Do this BEFORE we enter CSView */
|
|
row = (long) SendMessage(view->hwnd, TM_TOPROW, FALSE, 0);
|
|
|
|
ViewEnter();
|
|
|
|
if (!view->bExpand) {
|
|
|
|
|
|
// view_outline_opt allows us to find the first visible row
|
|
// after a given COMPITEM. Do this to look for the old top-row
|
|
// compitem, so that even if it is no longer visible, we can
|
|
// still go to just after it.
|
|
|
|
INT newrow = -1;
|
|
if (row < view->rows) {
|
|
|
|
COMPITEM ciTop = view->pItems[row];
|
|
|
|
view_outline_opt(view, TRUE, ciTop, &newrow);
|
|
} else {
|
|
view_outline_opt(view, TRUE, NULL, NULL);
|
|
}
|
|
ViewLeave();
|
|
|
|
// row now has the visible row that corresponds to
|
|
// ciTop or where it would have been
|
|
if ((newrow >=0) && (newrow < view->rows)) {
|
|
SendMessage(view->hwnd, TM_TOPROW, TRUE, newrow);
|
|
}
|
|
return;
|
|
}
|
|
|
|
/* expanded mode */
|
|
|
|
|
|
bRight = FALSE; /* arbitrarily - avoid strange diagnostic */
|
|
/* save the line number on one side (and remember which side) */
|
|
if (row >= view->rows) {
|
|
number = -1;
|
|
} else {
|
|
state = section_getstate(view->pLines[row].section);
|
|
if ((state == STATE_MOVEDRIGHT) ||
|
|
(state == STATE_RIGHTONLY)) {
|
|
bRight = TRUE;
|
|
number = view->pLines[row].nr_right;
|
|
} else {
|
|
bRight = FALSE;
|
|
number = view->pLines[row].nr_left;
|
|
}
|
|
}
|
|
|
|
/* make the new mapping */
|
|
view_expand_item(view, view->ciSelect);
|
|
|
|
/* things may happen now due to simultaneous scrolling from
|
|
* two threads. At least we won't deadlock.
|
|
*/
|
|
/* find the nearest row in the new view */
|
|
if (number >= 0) {
|
|
|
|
ViewEnter();
|
|
row = view_findrow(view, number, bRight);
|
|
ViewLeave();
|
|
|
|
/* scroll this row to top of window */
|
|
if (row >= 0) {
|
|
|
|
/* things may happen now due to simultaneous scrolling from
|
|
* two threads. At least we won't deadlock.
|
|
*/
|
|
SendMessage(view->hwnd, TM_TOPROW, TRUE, row);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* the compare options have changed - re-do the compare completely
|
|
* and make the new mapping. Retain current position in the file.
|
|
*/
|
|
void
|
|
view_changediffoptions(VIEW view)
|
|
{
|
|
int state, number;
|
|
long row;
|
|
BOOL bRight = FALSE;
|
|
LIST li;
|
|
COMPITEM ci;
|
|
|
|
number = 0;
|
|
if (view == NULL) {
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* get current row before entering critsec.
|
|
*/
|
|
row = (long) SendMessage(view->hwnd, TM_TOPROW, FALSE, 0);
|
|
|
|
ViewEnter();
|
|
|
|
/* find the current line number so we can go back to it
|
|
* (only if we are in expanded mode
|
|
*/
|
|
if (view->bExpand) {
|
|
|
|
state = section_getstate(view->pLines[row].section);
|
|
if ((state == STATE_MOVEDRIGHT) ||
|
|
(state == STATE_SIMILARRIGHT) ||
|
|
(state == STATE_RIGHTONLY)) {
|
|
bRight = TRUE;
|
|
number = view->pLines[row].nr_right;
|
|
} else {
|
|
bRight = FALSE;
|
|
number = view->pLines[row].nr_left;
|
|
}
|
|
}
|
|
|
|
/* to force a recompare using the new options, we must
|
|
* tell each compitem to discard its current compare result.
|
|
* we need to traverse the list of compitems calling this
|
|
* for each compare.
|
|
*/
|
|
li = complist_getitems(view->cl);
|
|
|
|
for (ci = (COMPITEM) List_First(li); ci != NULL; ci = (COMPITEM) List_Next(ci)) {
|
|
compitem_discardsections(ci);
|
|
}
|
|
|
|
if (!view->bExpand) {
|
|
ViewLeave();
|
|
|
|
// we are in outline mode. Refreshing the outline view
|
|
// will pick up any tag and tag width changes
|
|
view_outline(view);
|
|
|
|
// now scroll to the previous position if still there
|
|
if (row < view->rows) {
|
|
SendMessage(view->hwnd, TM_TOPROW, TRUE, row);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
view_expand_item(view, view->ciSelect);
|
|
|
|
/* find the nearest row in the new view */
|
|
ViewEnter();
|
|
row = view_findrow(view, number, bRight);
|
|
ViewLeave();
|
|
|
|
/* scroll this row to top of window */
|
|
if (row >= 0) {
|
|
SendMessage(view->hwnd, TM_TOPROW, TRUE, row);
|
|
}
|
|
}
|
|
|
|
|
|
/* find the next changed - ie non-same - row in a given direction.
|
|
* for outline mode we find the next STATE_DIFFER. for expand mode, we
|
|
* find the next section
|
|
*/
|
|
long
|
|
view_findchange(VIEW view, long startrow, BOOL bForward)
|
|
{
|
|
long i;
|
|
|
|
if (view == NULL) {
|
|
return(0);
|
|
}
|
|
|
|
if (view->rows <= 0) {
|
|
return(-1);
|
|
}
|
|
|
|
ViewEnter();
|
|
|
|
if (bForward) {
|
|
|
|
if (startrow >= view->rows) {
|
|
ViewLeave();
|
|
return(-1);
|
|
}
|
|
|
|
if (!view->bExpand) {
|
|
|
|
/* look for next compitem with an expandable state*/
|
|
for (i = startrow; i < view->rows; i++) {
|
|
if (compitem_getstate(view->pItems[i]) == STATE_DIFFER) {
|
|
ViewLeave();
|
|
return(i);
|
|
}
|
|
}
|
|
/* none found */
|
|
ViewLeave();
|
|
return(-1);
|
|
} else {
|
|
/*
|
|
* find the next line that matches, then go on to the
|
|
* next line that does not match
|
|
*
|
|
*/
|
|
for (i= startrow; i < view->rows; i++) {
|
|
if (section_getstate(view->pLines[i].section)
|
|
== STATE_SAME) {
|
|
break;
|
|
}
|
|
}
|
|
for ( ; i < view->rows; i++) {
|
|
if (section_getstate(view->pLines[i].section)
|
|
!= STATE_SAME) {
|
|
ViewLeave();
|
|
return(i);
|
|
}
|
|
}
|
|
|
|
ViewLeave();
|
|
|
|
return(-1);
|
|
}
|
|
} else {
|
|
/* same search backwards */
|
|
if (startrow < 0) {
|
|
ViewLeave();
|
|
return(-1);
|
|
}
|
|
if (view->bExpand) {
|
|
/* search backwards for first row that is not
|
|
* changed (has state SAME). then carry on for
|
|
* the next changed row.
|
|
*/
|
|
for (i = startrow; i >= 0; i--) {
|
|
if (section_getstate(view->pLines[i].section)
|
|
== STATE_SAME) {
|
|
break;
|
|
}
|
|
}
|
|
for ( ; i >= 0; i--) {
|
|
if (section_getstate(view->pLines[i].section)
|
|
!= STATE_SAME) {
|
|
ViewLeave();
|
|
return(i);
|
|
}
|
|
}
|
|
ViewLeave();
|
|
return(-1);
|
|
} else {
|
|
for (i = startrow; i >= 0; i--) {
|
|
if (compitem_getstate(view->pItems[i]) == STATE_DIFFER) {
|
|
ViewLeave();
|
|
return(i);
|
|
}
|
|
}
|
|
ViewLeave();
|
|
return(-1);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
int view_getrowstate(VIEW view, long row)
|
|
{
|
|
int state;
|
|
|
|
if (view == NULL) {
|
|
return(0);
|
|
}
|
|
|
|
if ((view->rows) <= 0 || (row >= view->rows) || (row <= 0) ) {
|
|
return(STATE_SAME);
|
|
}
|
|
|
|
ViewEnter();
|
|
|
|
state = section_getstate(view->pLines[row].section);
|
|
|
|
ViewLeave();
|
|
|
|
return state;
|
|
}
|
|
|
|
/*
|
|
* the WIN32 multithread version can try view_expand and view_outline
|
|
* (or view_newitem) at the same time. This is not correctly protected by
|
|
* the critical section, since there are major restrictions about holding
|
|
* critsecs when sending messages from the worker thread.
|
|
*
|
|
* To avoid contention, we call this function to notify that we are
|
|
* starting expanding. view_newitem and ToOutline will return without
|
|
* doing anything if this function has been called and view_expand has not
|
|
* completed.
|
|
*/
|
|
void
|
|
view_expandstart(VIEW view)
|
|
{
|
|
view->bExpanding = TRUE;
|
|
}
|
|
|
|
// are we in the middle of expansion ?
|
|
BOOL
|
|
view_expanding(VIEW view)
|
|
{
|
|
return(view->bExpanding);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* ---- internal functions ------------------------------------------ */
|
|
|
|
|
|
/* find the new row number for the line numbered 'number'
|
|
* or the nearest line if possible. if bRight is true, number is
|
|
* a right file number; otherwise it is a left file number.
|
|
*
|
|
* we must be in expand mode
|
|
*/
|
|
int
|
|
view_findrow(
|
|
VIEW view,
|
|
int number,
|
|
BOOL bRight
|
|
)
|
|
{
|
|
int i;
|
|
|
|
if (!view->bExpand) {
|
|
return(0);
|
|
}
|
|
|
|
for (i = 0; i < view->rows; i++) {
|
|
|
|
if (bRight) {
|
|
if (view->pLines[i].nr_right == number) {
|
|
|
|
/* found the exact number */
|
|
return(i);
|
|
|
|
} else if (view->pLines[i].nr_right > number) {
|
|
|
|
/* passed our line -stop here */
|
|
return(i);
|
|
}
|
|
} else {
|
|
if (view->pLines[i].nr_left == number) {
|
|
|
|
/* found the exact number */
|
|
return(i);
|
|
|
|
} else if (view->pLines[i].nr_left > number) {
|
|
|
|
/* passed our line -stop here */
|
|
return(i);
|
|
}
|
|
}
|
|
}
|
|
return(-1);
|
|
}
|
|
|
|
/* free memory associated with the expand mode or outline mode mappings
|
|
* called whenever we rebuild the mapping, and on deletion
|
|
*/
|
|
void
|
|
view_freemappings(
|
|
VIEW view
|
|
)
|
|
{
|
|
|
|
if (view->pLines) {
|
|
|
|
HeapFree(GetProcessHeap(), NULL, view->pLines);
|
|
view->pLines = NULL;
|
|
} else if (view->pItems) {
|
|
|
|
/* previous outline mapping array is still there - free it
|
|
* before we build a new one
|
|
*/
|
|
|
|
HeapFree(GetProcessHeap(), NULL, view->pItems);
|
|
view->pItems = NULL;
|
|
}
|
|
view->rows = 0;
|
|
}
|
|
|
|
/* build a view outline to map one row to a COMPITEM handle by traversing
|
|
* the list of COMPITEMs obtained from our complist.
|
|
* optionally tell the table class to redraw (if bRedraw), and if so,
|
|
* scroll the new table to select the row that represents the
|
|
* file we were expanding, if possible
|
|
*
|
|
* *important*: if you are holding the view critsec when you call this, you
|
|
* must pass bRedraw as FALSE or you could deadlock
|
|
*
|
|
* if a COMPITEM ci is passed in, then return in *prow the row number that
|
|
* corresponds to this item in the new view, or if not visible, the first
|
|
* visible row after it (to retain current scroll position)
|
|
*/
|
|
void
|
|
view_outline_opt(
|
|
VIEW view,
|
|
BOOL bRedraw,
|
|
COMPITEM ciFind,
|
|
int * prow
|
|
)
|
|
{
|
|
int prev_row = -1; /* the row nr of the previously-expanded row*/
|
|
int i; /* nr of includable items */
|
|
LIST li;
|
|
COMPITEM ci;
|
|
int state;
|
|
TableSelection select;
|
|
|
|
/*
|
|
* check that view_setcomplist has already been called. if not,
|
|
* nothing to do
|
|
*/
|
|
if (view->cl == NULL) {
|
|
return;
|
|
}
|
|
|
|
ViewEnter();
|
|
|
|
/* clear the mode flag and free up memory associated with expand mode */
|
|
view->bExpand = FALSE;
|
|
view_freemappings(view);
|
|
|
|
/* traverse the list of compitems counting up the number of
|
|
* includable items
|
|
*/
|
|
li = complist_getitems(view->cl);
|
|
|
|
ci = (COMPITEM) List_First(li);
|
|
for (i = 0; ci != NULL; ci = (COMPITEM) List_Next(ci)) {
|
|
|
|
if ((ciFind != NULL) && (prow != NULL)) {
|
|
if (ci == ciFind) {
|
|
// now that we have found the requested item,
|
|
// the next visible row is the one we want,
|
|
// whether it is ci or a later one
|
|
*prow = i;
|
|
}
|
|
}
|
|
|
|
state = compitem_getstate(ci);
|
|
|
|
if (((outline_include & INCLUDE_SAME) && (state == STATE_SAME)) ||
|
|
((outline_include & INCLUDE_DIFFER) && (state == STATE_DIFFER)) ||
|
|
((outline_include & INCLUDE_LEFTONLY) && (state == STATE_FILELEFTONLY)) ||
|
|
((outline_include & INCLUDE_RIGHTONLY) && (state == STATE_FILERIGHTONLY))) {
|
|
if (!compitem_getmark(ci) || !hide_markedfiles) {
|
|
i++;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/* allocate an array big enough for all of these */
|
|
{ /* DO NOT link in any storage with garbage pointers in it */
|
|
COMPITEM * temp;
|
|
temp = (COMPITEM *)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, i * sizeof(COMPITEM));
|
|
if (temp == NULL)
|
|
{
|
|
return;
|
|
}
|
|
view->pItems = temp;
|
|
}
|
|
view->rows = i;
|
|
|
|
/* keep track of the column widths */
|
|
view->maxtag = 0;
|
|
view->maxrest = 0;
|
|
|
|
/* loop through again filling the array, and at the same time looking
|
|
* out for the handle of the previously expanded item
|
|
*/
|
|
ci = (COMPITEM) List_First(li);
|
|
for (i = 0; ci != NULL; ci = (COMPITEM) List_Next(ci)) {
|
|
|
|
state = compitem_getstate(ci);
|
|
|
|
if (((outline_include & INCLUDE_SAME) && (state == STATE_SAME)) ||
|
|
((outline_include & INCLUDE_DIFFER) && (state == STATE_DIFFER)) ||
|
|
((outline_include & INCLUDE_LEFTONLY) && (state == STATE_FILELEFTONLY)) ||
|
|
((outline_include & INCLUDE_RIGHTONLY) && (state == STATE_FILERIGHTONLY))) {
|
|
|
|
if (!compitem_getmark(ci) || !hide_markedfiles) {
|
|
|
|
view->pItems[i] = ci;
|
|
|
|
if (ci == view->ciSelect) {
|
|
prev_row = i;
|
|
}
|
|
|
|
/* check the column widths in characters */
|
|
view->maxtag = max(view->maxtag,
|
|
lstrlen(compitem_gettext_tag(ci)));
|
|
view->maxrest = max(view->maxrest,
|
|
lstrlen(compitem_gettext_result(ci)));
|
|
|
|
|
|
i++;
|
|
}
|
|
|
|
}
|
|
}
|
|
ViewLeave();
|
|
|
|
/* inform table of new layout of table - force refresh */
|
|
if (bRedraw) {
|
|
SendMessage(view->hwnd, TM_NEWLAYOUT, 0, (LPARAM) view);
|
|
|
|
/* scroll to and highlight the row that represents the file
|
|
* we were previously expanding
|
|
*/
|
|
if (prev_row != -1) {
|
|
select.startrow = prev_row;
|
|
select.startcell = 0;
|
|
select.nrows = 1;
|
|
select.ncells = 1;
|
|
SendMessage(view->hwnd, TM_SELECT, 0, (LPARAM) &select);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/* expand a view - given the handle to the compitem to expand.
|
|
*
|
|
* called from view_expand, and also to re-do an expanded view
|
|
* after options change in view_changediffoptions and _changeviewoptions
|
|
*
|
|
* we get the composite section list from the compitem,
|
|
* and pick out all the sections that are includable (according
|
|
* to the global option expand_mode: we include all sections, or
|
|
* just those in one side left or right). Once we know the count of rows,
|
|
* allocate the mapping array: in each element of the array we keep
|
|
* a handle to the section for that row (to get the state and hence the
|
|
* tag text), and a handle to the line within that section (for the line text).
|
|
*
|
|
* We no longer insist on only expanding text files that differ - if the
|
|
* compitem can give us a composite section list, we will map it.
|
|
*
|
|
* We need to be able to give a line number for a line, in either of
|
|
* the original files according to which option is in force. Each section
|
|
* can give us its base line number (number of first line in section) in
|
|
* each of the two files or 0 if not present, and we track these here.
|
|
*
|
|
* MUST BE INSIDE CSView BEFORE CALLING HERE.
|
|
*/
|
|
BOOL
|
|
view_expand_item(
|
|
VIEW view,
|
|
COMPITEM ci
|
|
)
|
|
{
|
|
LIST li;
|
|
SECTION sh;
|
|
LINE line1, line2;
|
|
int i, base_left, base_right, state;
|
|
|
|
// We could be on a second thread trying to expand while it's
|
|
// already going on. That ain't clever!
|
|
if (view->bExpandGuard) {
|
|
Trace_Error(NULL, "Expansion in progress. Please wait.", FALSE);
|
|
ViewLeave();
|
|
return FALSE;
|
|
}
|
|
|
|
// Ensure that the world knows that we are expanding
|
|
// before we leave the critical section.
|
|
// This is the only way into getcomposite.
|
|
view->bExpandGuard = TRUE;
|
|
|
|
// the compitem_getcomposite could take a long time
|
|
// if the file is large and remote. We need to
|
|
// release the critsec during this operation.
|
|
|
|
ViewLeave();
|
|
/* get the composite section list */
|
|
li = compitem_getcomposite(ci);
|
|
if (li == NULL) {
|
|
view->bExpanding = FALSE;
|
|
view->bExpandGuard = FALSE;
|
|
return FALSE;
|
|
}
|
|
|
|
ViewEnter();
|
|
|
|
/* remember the compitem we are expanding */
|
|
view->ciSelect = ci;
|
|
|
|
/* switch modes and free the current mapping
|
|
*
|
|
* NOTE: must do this AFTER the compitem_getcomposite,
|
|
* since that can fail: if it fails it could put up a
|
|
* message box, and that could cause a queued paint message
|
|
* to be processed, which would cause us to use these mappings
|
|
* and gpfault if they had been cleared first.
|
|
*/
|
|
view->bExpand = TRUE;
|
|
view->bExpanding = FALSE;
|
|
view->bExpandGuard = FALSE;
|
|
view_freemappings(view);
|
|
|
|
|
|
/* loop through totalling the lines in sections
|
|
* that we should include
|
|
*/
|
|
view->rows = 0;
|
|
for ( sh = (SECTION) List_First(li); sh != NULL;
|
|
sh = (SECTION) List_Next(sh)) {
|
|
|
|
state = section_getstate(sh);
|
|
|
|
if (expand_mode == IDM_RONLY) {
|
|
if ((state == STATE_LEFTONLY) ||
|
|
(state == STATE_SIMILARLEFT) ||
|
|
(state == STATE_MOVEDLEFT)) {
|
|
continue;
|
|
}
|
|
} else if (expand_mode == IDM_LONLY) {
|
|
if ((state == STATE_RIGHTONLY) ||
|
|
(state == STATE_SIMILARRIGHT) ||
|
|
(state == STATE_MOVEDRIGHT)) {
|
|
continue;
|
|
}
|
|
}
|
|
|
|
/* include all lines in this section
|
|
if the section meets the include criteria */
|
|
if ( ((state == STATE_SAME) && (expand_include & INCLUDE_SAME))
|
|
|| ((state == STATE_LEFTONLY) && (expand_include & INCLUDE_LEFTONLY))
|
|
|| ((state == STATE_RIGHTONLY) && (expand_include & INCLUDE_RIGHTONLY))
|
|
|| ((state == STATE_MOVEDLEFT) && (expand_include & INCLUDE_MOVEDLEFT))
|
|
|| ((state == STATE_MOVEDRIGHT) && (expand_include & INCLUDE_MOVEDRIGHT))
|
|
|| ((state == STATE_SIMILARLEFT) && (expand_include & INCLUDE_SIMILARLEFT))
|
|
|| ((state == STATE_SIMILARRIGHT) && (expand_include & INCLUDE_SIMILARRIGHT))) {
|
|
view->rows += section_getlinecount(sh);
|
|
}
|
|
}
|
|
|
|
/* allocate the memory for the mapping array */
|
|
{ /* DO NOT chain in any storage with garbage pointers in it */
|
|
PVIEWLINE temp;
|
|
temp = (PVIEWLINE) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, view->rows * sizeof(VIEWLINE));
|
|
if (temp == NULL)
|
|
{
|
|
return FALSE;
|
|
}
|
|
view->pLines = temp;
|
|
}
|
|
|
|
/* loop through the sections again filling in the mapping array */
|
|
i = 0;
|
|
view->maxtag = 5;
|
|
view->maxrest = 0;
|
|
for (sh = (SECTION) List_First(li); sh != NULL;
|
|
sh = (SECTION) List_Next(sh)) {
|
|
|
|
state = section_getstate(sh);
|
|
|
|
if (expand_mode == IDM_RONLY) {
|
|
if ((state == STATE_LEFTONLY) ||
|
|
(state == STATE_SIMILARLEFT) ||
|
|
(state == STATE_MOVEDLEFT)) {
|
|
continue;
|
|
}
|
|
} else if (expand_mode == IDM_LONLY) {
|
|
if ((state == STATE_RIGHTONLY) ||
|
|
(state == STATE_SIMILARRIGHT) ||
|
|
(state == STATE_MOVEDRIGHT)) {
|
|
continue;
|
|
}
|
|
}
|
|
|
|
/* include all lines in this section
|
|
if the section meets the include criteria */
|
|
if ( ((state == STATE_SAME) && (expand_include & INCLUDE_SAME))
|
|
|| ((state == STATE_LEFTONLY) && (expand_include & INCLUDE_LEFTONLY))
|
|
|| ((state == STATE_RIGHTONLY) && (expand_include & INCLUDE_RIGHTONLY))
|
|
|| ((state == STATE_MOVEDLEFT) && (expand_include & INCLUDE_MOVEDLEFT))
|
|
|| ((state == STATE_MOVEDRIGHT) && (expand_include & INCLUDE_MOVEDRIGHT))
|
|
|| ((state == STATE_SIMILARLEFT) && (expand_include & INCLUDE_SIMILARLEFT))
|
|
|| ((state == STATE_SIMILARRIGHT) && (expand_include & INCLUDE_SIMILARRIGHT))) {
|
|
|
|
/* find the base line number in each file */
|
|
base_left = section_getleftbasenr(sh);
|
|
base_right = section_getrightbasenr(sh);
|
|
|
|
/* add each line in section to the view. section_getfirst()
|
|
* returns us to a handle that is in a list. We can
|
|
* call List_Next and will eventually get to the
|
|
* line returned by section_getlast(). Sections always have
|
|
* at least one line
|
|
*/
|
|
line1 = section_getfirstline(sh);
|
|
line2 = section_getlastline(sh);
|
|
|
|
for (; line1 != NULL; line1 = (LINE) List_Next(line1)) {
|
|
|
|
view->pLines[i].line = line1;
|
|
view->pLines[i].section = sh;
|
|
|
|
/* calculate the line number for this line by
|
|
* incrementing the base nr for this section.
|
|
* Note SIMILAR_RIGHT (or LEFT) lines DO have
|
|
* left (or right) numbers, but they are dummies.
|
|
*/
|
|
|
|
view->pLines[i].nr_left = base_left;
|
|
if (state!=STATE_SIMILARRIGHT && base_left != 0) {
|
|
base_left++;
|
|
}
|
|
|
|
view->pLines[i].nr_right = base_right;
|
|
if (state!=STATE_SIMILARLEFT && base_right != 0) {
|
|
base_right++;
|
|
}
|
|
|
|
/* increment index into view */
|
|
i++;
|
|
|
|
/* check the column widths */
|
|
view->maxrest = max(view->maxrest,
|
|
(line_gettabbedlength(line1, g_tabwidth)));
|
|
|
|
/* end of section ? */
|
|
if (line1 == line2) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* We must NOT hold a critical section here as SendMessage may hang */
|
|
ViewLeave();
|
|
|
|
/*inform table window of revised mapping */
|
|
SendMessage(view->hwnd, TM_NEWLAYOUT, 0, (LPARAM) view);
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
|
|
/*
|
|
* view_gethwnd
|
|
*
|
|
*/
|
|
HWND
|
|
view_gethwnd(VIEW view)
|
|
{
|
|
return(view) ? view->hwnd : NULL;
|
|
}
|
|
|
|
|
|
/*
|
|
* view_gototableline
|
|
*
|
|
*/
|
|
void
|
|
view_gototableline(VIEW view, LONG iLine)
|
|
{
|
|
if (view)
|
|
{
|
|
const LONG cLines = view_getrowcount(view);
|
|
if ( (iLine >= 0) && (iLine < cLines))
|
|
{
|
|
TableSelection select;
|
|
memset(&select, 0, sizeof(TableSelection));
|
|
select.startrow = iLine;
|
|
select.nrows = 1L;
|
|
select.ncells = 1L;
|
|
|
|
SendMessage(view_gethwnd(view), TM_SELECT, 0, (LPARAM) &select);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
typedef PUCHAR (*STRSUBFUNC)(PUCHAR, PUCHAR, PUCHAR*);
|
|
extern PUCHAR My_mbsistr(PUCHAR, PUCHAR, PUCHAR*);
|
|
extern PUCHAR My_mbsstr(PUCHAR, PUCHAR, PUCHAR*);
|
|
|
|
BOOL
|
|
view_findstring(VIEW view, LONG iCol, LPCSTR pszFind, BOOL fSearchDown, BOOL fMatchCase, BOOL fWholeWord)
|
|
{
|
|
const LONG cRows = view_getrowcount(view);
|
|
BOOL fFound = FALSE;
|
|
|
|
if (cRows > 0)
|
|
{
|
|
STRSUBFUNC pfnSub = (fMatchCase) ? My_mbsstr : My_mbsistr;
|
|
const char *pszRow = NULL;
|
|
const char *pszFound = NULL;
|
|
char *pszEnd = NULL;
|
|
LONG iEnd = 0;
|
|
LONG iRow = 0;
|
|
LONG nStep = 0;
|
|
LONG iWrapAt = 0;
|
|
LONG iWrapTo = 0;
|
|
|
|
if (fSearchDown)
|
|
{
|
|
nStep = 1;
|
|
iRow = selection + selection_nrows - 1;
|
|
iWrapAt = cRows;
|
|
iWrapTo = 0;
|
|
}
|
|
else
|
|
{
|
|
nStep = -1;
|
|
iRow = selection;
|
|
iWrapAt = -1;
|
|
iWrapTo = cRows - 1;
|
|
}
|
|
|
|
iRow += nStep;
|
|
if (iRow < 0 || iRow >= cRows)
|
|
{
|
|
iRow = iWrapTo;
|
|
}
|
|
|
|
iEnd = iRow;
|
|
|
|
for (;;)
|
|
{
|
|
pszRow = view_gettext(view, iRow, iCol);
|
|
if (pszRow)
|
|
{
|
|
pszEnd = NULL;
|
|
pszFound = (const char*)pfnSub((PUCHAR)pszRow, (PUCHAR)pszFind, (PUCHAR*)&pszEnd);
|
|
if (pszFound)
|
|
{
|
|
if (!fWholeWord)
|
|
{
|
|
fFound = TRUE;
|
|
}
|
|
else
|
|
{
|
|
/* check end of string */
|
|
if (!pszEnd || !*pszEnd || (!IsDBCSLeadByte(*pszEnd) && !isalpha((UCHAR)*pszEnd) && !isdigit((UCHAR)*pszEnd)))
|
|
{
|
|
/* check beginning of string */
|
|
if (pszFound == pszRow)
|
|
{
|
|
fFound = TRUE;
|
|
}
|
|
else
|
|
{
|
|
const char *pchT = CharPrev(pszRow, pszFound);
|
|
if (!pchT || !*pchT || (!IsDBCSLeadByte(*pchT) && !isalpha((UCHAR)*pchT) && !isdigit((UCHAR)*pchT)))
|
|
{
|
|
fFound = TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (fFound)
|
|
{
|
|
view_gototableline(view, iRow);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
iRow += nStep;
|
|
if (iRow == iWrapAt)
|
|
{
|
|
iRow = iWrapTo;
|
|
}
|
|
|
|
if (iRow == iEnd)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return fFound;
|
|
}
|