// GrepView.cpp : implementation file
//

#include "stdafx.h"
#include "VwBar.h"
#include "GrepView.h"
#include "AboutDlg.h"
#include "Dlg.h"
#include "GrepThread.h"
#include "GrepList.h"

#include "Grep.h"
#include "cntxmap.h"

#include <WinReg.h>
#include <afxpriv.h>

#include <winnetwk.h>
#include <shlobj.h>
#include <winnls.h>

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

#define MAX_SPECS_HISTORY	100

#define LEFT_MARGIN   50   // 1/2 Inch Left Margin
#define RIGHT_MARGIN  50   // 1/2 Inch Right Margin
#define TOP_MARGIN    10 
#define BOTTOM_MARGIN 50


void RegFree(); // In search.cpp

/////////////////////////////////////////////////////////////////////////////
// CGrepView

IMPLEMENT_DYNCREATE(CGrepView, CFormView)

CGrepView::CGrepView()	: CFormView(CGrepView::IDD)
//-------------------------------------------------
{
	//{{AFX_DATA_INIT(CGrepView)
	m_strAddDir = _T("");
	m_strGrep = _T("GNU E-Grep");
	m_strSpecs = _T("");
	m_strLinesAfter = _T("");
	m_strLinesBefore = _T("");
	m_strPattern = _T("");
	m_bMatchWord = FALSE;
	m_bMatchLine = FALSE;
	m_bOutputByte = FALSE;
	m_bCountMatchesOnly = FALSE;
	m_bNamesOnly = FALSE;
	m_bMatchCase = FALSE;
	m_bNoMatchFiles = FALSE;
	m_bNonMatching = FALSE;
	m_bSuppressErrors = FALSE;
	m_bRecurseDirectories = FALSE;
	//}}AFX_DATA_INIT
	m_bClosing = m_bStopGrep = m_bGrepping = FALSE;
}

CGrepView::~CGrepView()
//---------------------
{
}

void CGrepView::DoDataExchange(CDataExchange* pDX)
//------------------------------------------------
{
	CFormView::DoDataExchange(pDX);
	//{{AFX_DATA_MAP(CGrepView)
	DDX_Control(pDX, IDOK, m_buttonStart);
	DDX_Control(pDX, IDC_SPIN_BEFORE, m_spinBefore);
	DDX_Control(pDX, IDC_SPIN_AFTER, m_spinAfter);
	DDX_Control(pDX, IDC_PATTERN, m_editPattern);
	DDX_Control(pDX, IDC_LINES_BEFORE, m_editLinesBefore);
	DDX_Control(pDX, IDC_LINES_AFTER, m_editLinesAfter);
	DDX_Control(pDX, IDC_GREP_SPECS, m_comboSpecs);
	DDX_Control(pDX, IDC_GREP_OUTPUT, m_lstctlGrepOutput);
	DDX_Control(pDX, IDC_GREP_DIRS, m_listDirectories);
	DDX_Control(pDX, IDC_GREP_ALGORITHM, m_comboGrep);
	DDX_Control(pDX, IDC_GREP_ADDDIR, m_editAddDir);
	DDX_Text(pDX, IDC_GREP_ADDDIR, m_strAddDir);
	DDX_CBString(pDX, IDC_GREP_ALGORITHM, m_strGrep);
	DDX_CBString(pDX, IDC_GREP_SPECS, m_strSpecs);
	DDX_Text(pDX, IDC_LINES_AFTER, m_strLinesAfter);
	DDX_Text(pDX, IDC_LINES_BEFORE, m_strLinesBefore);
	DDX_Text(pDX, IDC_PATTERN, m_strPattern);
	DDX_Check(pDX, IDC_MATCH_WORDS, m_bMatchWord);
	DDX_Check(pDX, IDC_MATCH_LINES, m_bMatchLine);
	DDX_Check(pDX, IDC_BYTE_COUNT, m_bOutputByte);
	DDX_Check(pDX, IDC_COUNT_ONLY, m_bCountMatchesOnly);
	DDX_Check(pDX, IDC_FILES_ONLY, m_bNamesOnly);
	DDX_Check(pDX, IDC_MATCH_CASE, m_bMatchCase);
	DDX_Check(pDX, IDC_NAMES_NOMATCH, m_bNoMatchFiles);
	DDX_Check(pDX, IDC_NON_MATCHING, m_bNonMatching);
	DDX_Check(pDX, IDC_SUPRESS_ERROR, m_bSuppressErrors);
	DDX_Check(pDX, IDC_RECURSE_DIRS, m_bRecurseDirectories);
	//}}AFX_DATA_MAP
}


BEGIN_MESSAGE_MAP(CGrepView, CFormView)
	//{{AFX_MSG_MAP(CGrepView)
	ON_BN_CLICKED(IDOK, OnGrep)
	ON_BN_CLICKED(IDC_GREP_BROWSEDIRS, OnGrepBrowsedDirs)
	ON_BN_CLICKED(IDC_GREP_CLEAR_ALL, OnGrepClearAll)
	ON_WM_SIZE()
	ON_EN_KILLFOCUS(IDC_GREP_ADDDIR, OnKillFocusAddDir)
	ON_WM_VKEYTOITEM()
	ON_BN_CLICKED(IDC_GREP_DELDIR, OnGrepDelDir)
	ON_BN_CLICKED(ID_CLOSE, OnCloseGrep)
	ON_COMMAND(ID_GREP_HELP, OnGrepHelp)
	ON_COMMAND(ID_APP_EXIT, OnAppExit)
	ON_COMMAND(ID_APP_ABOUT, OnAppAbout)
	ON_WM_DESTROY()
	ON_COMMAND(ID_FILE_PRINT, OnFilePrint)
	ON_BN_CLICKED(IDC_GREP_HELP, OnGrepHelp)
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CGrepView diagnostics

#ifdef _DEBUG
void CGrepView::AssertValid() const
{
	CFormView::AssertValid();
}

void CGrepView::Dump(CDumpContext& dc) const
{
	CFormView::Dump(dc);
}
#endif //_DEBUG

/////////////////////////////////////////////////////////////////////////////
// CGrepView message handlers

void CGrepView::OnInitialUpdate() 
//-------------------------------
{	CFormView::OnInitialUpdate();
	m_spinBefore.SetBuddy(&m_editLinesBefore);
	m_spinAfter.SetBuddy(&m_editLinesAfter);
	m_spinBefore.SetRange(0,100); 
	m_spinBefore.SetPos(0);
	m_spinAfter.SetRange(0,100);
	m_spinAfter.SetPos(0);
	m_comboGrep.SetCurSel(0);
	m_lstctlGrepOutput.Initialize(this); 
	GetDocument()->SetTitle("ViewBar");
	HKEY hkey;
	LONG l = ::RegOpenKeyEx(HKEY_CURRENT_USER, "Software\\ViewBar",
													 0, KEY_QUERY_VALUE, &hkey);
	if (l == ERROR_SUCCESS) 
		{	DWORD cbData, dwType =REG_DWORD;
			int nNoSpecsHist =0;
			cbData = sizeof(int);
			if (::RegQueryValueEx(hkey, "GrepSpecsCntHistory", NULL, &dwType, (PBYTE) &nNoSpecsHist, &cbData) != ERROR_SUCCESS)
				nNoSpecsHist = 0;
			char szValue[2048], szName[60];;
			dwType = REG_SZ;
			for (int i=0; i<nNoSpecsHist; i++)
				{	wsprintf(szName,"GrepSpec%d",i);
					cbData = 2047;
					::RegQueryValueEx(hkey, szName, NULL, &dwType, (PBYTE) szValue, &cbData);		
					if (i < nNoSpecsHist)
						m_comboSpecs.AddString(szValue);
					else
						::RegDeleteValue(hkey,szName);
				}

			nNoSpecsHist = 0;
			cbData = sizeof(int);
			if (::RegQueryValueEx(hkey, "GrepDirsCnt", NULL, &dwType, (PBYTE) &nNoSpecsHist, &cbData) != ERROR_SUCCESS)
				nNoSpecsHist = 0;
			dwType = REG_SZ;
			for (i=0; i<nNoSpecsHist; i++)
				{	wsprintf(szName,"GrepDir%d",i);
					cbData = 2047;
					::RegQueryValueEx(hkey, szName, NULL, &dwType, (PBYTE) szValue, &cbData);		
					if (i < nNoSpecsHist)
						m_listDirectories.AddString(szValue);
					else
						::RegDeleteValue(hkey,szName);
				}
			cbData = 2047;
			*szValue = 0;
			if (::RegQueryValueEx(hkey, "GrepPattern", NULL, &dwType, (PBYTE) szValue, &cbData) == ERROR_SUCCESS)
				m_strPattern = CString(szValue);
			*szValue = 0;
			cbData = 2047;
			if (::RegQueryValueEx(hkey, "GrepAlgorithm", NULL, &dwType, (PBYTE) szValue, &cbData) == ERROR_SUCCESS)
				m_strGrep = CString(szValue);
			else
				m_strGrep = "GNU E-Grep";
			*szValue = 0;
			cbData = 2047;
			if (::RegQueryValueEx(hkey, "GrepContextBefore", NULL, &dwType, (PBYTE) szValue, &cbData) == ERROR_SUCCESS)
				m_strLinesBefore = CString(szValue);
			*szValue = 0;
			cbData = 2047;
			if (::RegQueryValueEx(hkey, "GrepContextAfter", NULL, &dwType, (PBYTE) szValue, &cbData) == ERROR_SUCCESS)
				m_strLinesAfter = CString(szValue);
			DWORD dwValue;
			cbData = sizeof(int);
			dwType = REG_DWORD;
			if (::RegQueryValueEx(hkey, "GrepMatchWord", NULL, &dwType, (PBYTE) &dwValue, &cbData) == ERROR_SUCCESS)
				m_bMatchWord = (BOOL)dwValue;
			if (::RegQueryValueEx(hkey, "GrepMatchLine", NULL, &dwType, (PBYTE) &dwValue, &cbData) == ERROR_SUCCESS)
				m_bMatchLine = (BOOL)dwValue;
			if (::RegQueryValueEx(hkey, "GrepOutputByte", NULL, &dwType, (PBYTE) &dwValue, &cbData) == ERROR_SUCCESS)
				m_bOutputByte = (BOOL)dwValue;
			if (::RegQueryValueEx(hkey, "GrepCountMatches", NULL, &dwType, (PBYTE) &dwValue, &cbData) == ERROR_SUCCESS)
				m_bCountMatchesOnly = (BOOL)dwValue;
			if (::RegQueryValueEx(hkey, "GrepNamesOnly", NULL, &dwType, (PBYTE) &dwValue, &cbData) == ERROR_SUCCESS)
				m_bNamesOnly = (BOOL)dwValue;
			if (::RegQueryValueEx(hkey, "GrepMatchCase", NULL, &dwType, (PBYTE) &dwValue, &cbData) == ERROR_SUCCESS)
				m_bMatchCase = (BOOL)dwValue;
			if (::RegQueryValueEx(hkey, "GrepNoMatchFile", NULL, &dwType, (PBYTE) &dwValue, &cbData) == ERROR_SUCCESS)
				m_bNoMatchFiles = (BOOL)dwValue;
			if (::RegQueryValueEx(hkey, "GrepNonMatching", NULL, &dwType, (PBYTE) &dwValue, &cbData) == ERROR_SUCCESS)
				m_bNonMatching = (BOOL)dwValue;
			if (::RegQueryValueEx(hkey, "GrepNoErrors", NULL, &dwType, (PBYTE) &dwValue, &cbData) == ERROR_SUCCESS)
				m_bSuppressErrors = (BOOL)dwValue;
			if (::RegQueryValueEx(hkey, "GrepRecurseDirs", NULL, &dwType, (PBYTE) &dwValue, &cbData) == ERROR_SUCCESS)
				m_bRecurseDirectories = (BOOL)dwValue;
			UpdateData(FALSE);
			::RegCloseKey(hkey);
		}
	if (m_comboSpecs.GetCount() > 0) 
		m_comboSpecs.SetCurSel(0);
	PostMessage(WM_SIZE);
}

void CGrepView::OnDestroy() 
//-------------------------
{	UpdateData();
	CFormView::OnDestroy();
	HKEY hkey;
	DWORD dwDisposition;
	LONG l = ::RegCreateKeyEx(HKEY_CURRENT_USER, "Software\\ViewBar", 0, NULL,
														REG_OPTION_NON_VOLATILE, KEY_SET_VALUE, NULL, &hkey, 
														&dwDisposition);

	if (l == ERROR_SUCCESS) 
		{	DWORD dwType =REG_DWORD;
			char szName[60];
			int nSpecs = m_comboSpecs.GetCount();
			::RegSetValueEx(hkey, "GrepSpecsCntHistory", NULL, REG_DWORD, (PBYTE) &nSpecs, sizeof(int));
			dwType = REG_SZ;
			for (int i=m_comboSpecs.GetCount()-1; i>=0; i--)
				{	wsprintf(szName,"GrepSpec%d",i);
					m_comboSpecs.SetCurSel(i);
					CString strSpec;
					m_comboSpecs.GetWindowText(strSpec);
					if (i < m_comboSpecs.GetCount())
						{	::RegSetValueEx(hkey, szName, NULL, REG_SZ, (PBYTE) strSpec.GetBuffer(2048), strSpec.GetLength());		
							strSpec.ReleaseBuffer();
						}
					else
						::RegDeleteValue(hkey,szName);

					nSpecs = m_listDirectories.GetCount();
					::RegSetValueEx(hkey, "GrepDirsCnt", NULL, REG_DWORD, (PBYTE) &nSpecs, sizeof(int));
					dwType = REG_SZ;
					for (int i=nSpecs-1; i>=0; i--)
						{	wsprintf(szName,"GrepDir%d",i);
							CString strSpec;
							m_listDirectories.GetText(i, strSpec);
							if (i < nSpecs)
								{	::RegSetValueEx(hkey, szName, NULL, REG_SZ, (PBYTE) strSpec.GetBuffer(2048), strSpec.GetLength());		
									strSpec.ReleaseBuffer();
								}
							else
								::RegDeleteValue(hkey,szName);
						}
				}
			::RegSetValueEx(hkey, "GrepPattern", NULL, REG_SZ, (PBYTE) m_strPattern.GetBuffer(2048), m_strPattern.GetLength());		
			::RegSetValueEx(hkey, "GrepAlgorithm", NULL, REG_SZ, (PBYTE) m_strGrep.GetBuffer(2048), m_strGrep.GetLength());		
			::RegSetValueEx(hkey, "GrepContextBefore", NULL, REG_SZ, (PBYTE) m_strLinesBefore.GetBuffer(2048), m_strLinesBefore.GetLength());		
			::RegSetValueEx(hkey, "GrepContextAfter", NULL, REG_SZ, (PBYTE) m_strLinesAfter.GetBuffer(2048), m_strLinesAfter.GetLength());		
			::RegSetValueEx(hkey, "GrepMatchWord", NULL, REG_DWORD, (PBYTE) &m_bMatchWord, sizeof(int));
			::RegSetValueEx(hkey, "GrepMatchLine", NULL, REG_DWORD, (PBYTE) &m_bMatchLine, sizeof(int));
			::RegSetValueEx(hkey, "GrepOutputByte", NULL, REG_DWORD, (PBYTE) &m_bOutputByte, sizeof(int));
			::RegSetValueEx(hkey, "GrepCountMatches", NULL, REG_DWORD, (PBYTE) &m_bCountMatchesOnly, sizeof(int));
			::RegSetValueEx(hkey, "GrepNamesOnly", NULL, REG_DWORD, (PBYTE) &m_bNamesOnly, sizeof(int));
			::RegSetValueEx(hkey, "GrepMatchCase", NULL, REG_DWORD, (PBYTE) &m_bMatchCase, sizeof(int));
			::RegSetValueEx(hkey, "GrepNoMatchFile", NULL, REG_DWORD, (PBYTE) &m_bNoMatchFiles, sizeof(int));
			::RegSetValueEx(hkey, "GrepNonMatching", NULL, REG_DWORD, (PBYTE) &m_bNonMatching, sizeof(int));
			::RegSetValueEx(hkey, "GrepNoErrors", NULL, REG_DWORD, (PBYTE) &m_bSuppressErrors, sizeof(int));
			::RegSetValueEx(hkey, "GrepRecurseDirs", NULL, REG_DWORD, (PBYTE) &m_bRecurseDirectories, sizeof(int));
			::RegCloseKey(hkey);
		}
}

void CGrepView::OnGrepBrowsedDirs() 
//---------------------------------
{	LPITEMIDLIST  pidl =NULL;
	LPSHELLFOLDER pDesktopFolder;
	char          szPath[MAX_PATH];
	//OLECHAR       olePath[MAX_PATH];
	//ULONG         chEaten;
	//ULONG         dwAttributes;
	GetCurrentDirectory(MAX_PATH, szPath);
	if (SUCCEEDED(SHGetDesktopFolder(&pDesktopFolder)))
		{	/*OSVERSIONINFO		WinVer;
			WinVer.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
			GetVersionEx(&WinVer);
			if (WinVer.dwPlatformId == VER_PLATFORM_WIN32_NT)
				{	MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, szPath, -1,
															olePath, MAX_PATH);
					HRESULT hr = pDesktopFolder->ParseDisplayName(NULL,NULL,olePath,&chEaten,
																												&pidl,&dwAttributes);
					if (FAILED(hr))
						pidl = NULL;
				}*/
		}
 
	BROWSEINFO browseInfo;
	char szResult[MAX_PATH+1];
	browseInfo.hwndOwner = m_hWnd;
	browseInfo.pidlRoot = pidl;
	browseInfo.pszDisplayName = szResult;
	browseInfo.lpszTitle = "Select Directory";
	browseInfo.ulFlags = BIF_RETURNONLYFSDIRS | BIF_BROWSEFORCOMPUTER;
	browseInfo.lpfn = NULL;
	LPITEMIDLIST pItemList = SHBrowseForFolder(&browseInfo);
	if (pItemList == NULL) 
		{	if (pDesktopFolder != NULL)
				pDesktopFolder->Release();			
			return;
		}
	STRRET strretDir;
	HRESULT hr = pDesktopFolder->GetDisplayNameOf(pItemList,SHGDN_FORPARSING ,&strretDir);
	pDesktopFolder->Release();
	if (! SUCCEEDED(hr))
		{	TRACE0("CFindView::OnBrowseDirs() : GetDisplayNameOf failed !");
			return;
		}
	
	LPMALLOC pMalloc;
	if (SUCCEEDED(SHGetMalloc(&pMalloc)))
		pMalloc->Free(pItemList);
	if (strretDir.uType == STRRET_CSTR)
		m_strAddDir = CString(strretDir.cStr);
	else
		if (strretDir.uType == STRRET_WSTR)
			{	m_strAddDir = CString((LPCWSTR) strretDir.pOleStr);
				TRACE("%s\n\t%c %c %c %c %c %c %c\n",strretDir.pOleStr,strretDir.pOleStr[0],strretDir.pOleStr[1],strretDir.pOleStr[2],strretDir.pOleStr[3],strretDir.pOleStr[4],strretDir.pOleStr[5],strretDir.pOleStr[6]);
			}
		else
			m_strAddDir = "";
	int nFind =-1;		
	if ( (nFind = m_strAddDir.Find(":)")) > 1)
		if (m_strAddDir[nFind - 2] == '(')
			m_strAddDir = m_strAddDir[nFind - 1] + CString(":\\");
	UpdateData(FALSE);
	m_editAddDir.SetFocus();
}

void CGrepView::OnGrepClearAll() 
//------------------------------
{	m_listDirectories.ResetContent();
	m_listDirectories.InvalidateRect(NULL);
}


void CGrepView::OnSize(UINT nType, int cx, int cy) 
//------------------------------------------------
{ CFormView::OnSize(nType, cx, cy);
	if (m_lstctlGrepOutput.m_hWnd == NULL) 
	  return;
  CRect rectView, rectGrid;
  GetWindowRect(&rectView);  
  m_lstctlGrepOutput.GetWindowRect(&rectGrid);
  if (rectView.right < rectGrid.left) return;  
  rectGrid.bottom = rectView.bottom - GetSystemMetrics(SM_CYHSCROLL);
  if ( (rectGrid.Height() - 20) < 0) return;
  rectGrid.right = rectView.right - GetSystemMetrics(SM_CXHSCROLL);;
  ScreenToClient(rectGrid);
  m_lstctlGrepOutput.MoveWindow(&rectGrid);
}

void CGrepView::OnAddDir() 
//------------------------
{	UpdateData();
	m_strAddDir.TrimLeft(); m_strAddDir.TrimRight(); 
	if (m_strAddDir.IsEmpty())
		return;
	CString strCurDir;
	char *pszCurDir = strCurDir.GetBuffer(1024);
	GetCurrentDirectory(1023,pszCurDir);
	strCurDir.ReleaseBuffer();
	UpdateData();
	m_strAddDir.TrimLeft(); m_strAddDir.TrimRight(); 
	if (m_strAddDir.IsEmpty())
		return;
	if (SetCurrentDirectory(m_strAddDir))
		{	pszCurDir = m_strAddDir.GetBuffer(1024);
			GetCurrentDirectory(1023, pszCurDir);
			m_strAddDir.ReleaseBuffer();
			m_strAddDir.MakeUpper(); 
			if (m_listDirectories.FindStringExact(-1, m_strAddDir) == LB_ERR)
				m_listDirectories.AddString(m_strAddDir);
		}
	else
		{	CString strErr;
			if (m_strAddDir.GetLength() >= 2)
				if ( (m_strAddDir[1] != ':') && (m_strAddDir.Find("\\\\") == -1) )
					{	if (m_strAddDir[0] != '\\')
							m_strAddDir = "\\" + 	m_strAddDir;
						m_strAddDir = strCurDir.Left(2) + m_strAddDir;
					}
			strErr.Format("ERROR : %s is an invalid directory",m_strAddDir);
			AfxMessageBox(strErr);
		}
	SetCurrentDirectory(strCurDir);
	m_strAddDir = "";
	UpdateData(FALSE);
}

BOOL InsertString(CGrepView *pView, CString strLine)
//--------------------------------------------------
{	strLine.TrimLeft(); strLine.TrimRight();
	CString strFullPath ="", strPath ="", strFileName ="", strLineNo ="", 
					strLineContents ="";
	char *pszLine = strLine.GetBuffer(strLine.GetLength()+1);
	char *szToken = strtok(pszLine,"|");
	if (szToken != NULL)
		strFullPath = CString(szToken);
	szToken = strtok(NULL,"|");
	if (szToken != NULL)
		strLineNo = CString(szToken);
	szToken = strtok(NULL,"|");
	CString strByte ="";
	if (szToken != NULL)
		strByte = CString(szToken);
	szToken = strtok(NULL,"|");
	if (szToken != NULL)
		strLineContents = CString(szToken);
	int nSlash =-1;
	if ( ( (nSlash = strFullPath.ReverseFind('\\')) >= 0) || (strFullPath.Find(':') >= 0) )
		{	strFileName = strFullPath.Mid(nSlash+1);
			strPath = strFullPath.Mid(0,nSlash);
			if (strPath.Right(1) == ":")
				strPath += "\\";
		}
	strLine.ReleaseBuffer();
	CString strLineOut ="";
	for (int i=0; i<strLineContents.GetLength(); i++)
		{	char ch = strLineContents[i];
			if (ch != '\t')
				strLineOut += ch;
			else
				strLineOut += "  ";
		}
	if (pView->m_bOutputByte)
		strLineNo += CString(" (") + strByte + CString(")");
	pView->m_lstctlGrepOutput.Insert(strFileName,strPath, strLineNo, strLineOut);
	return TRUE;
}

int CGrepView::_ParseSpecs_(CString strSpecs)
//-------------------------------------------
{	m_strarrFileSpecs.RemoveAll();
	m_strarrFileSpecs.SetSize(0,10);
	CString strSpec;
	int nSpecCnt =0;
	strSpecs.TrimLeft(); strSpecs.TrimRight();
	while (! strSpecs.IsEmpty())
		{	int nSep = strSpecs.Find(';');
			if (nSep == -1)
				nSep = strSpecs.GetLength();
			strSpec = strSpecs.Left(nSep);
			strSpecs = strSpecs.Mid(nSep+1);
			strSpec.TrimLeft(); strSpec.TrimRight();
			strSpecs.TrimLeft(); strSpecs.TrimRight();
			if (! strSpec.IsEmpty())
				m_strarrFileSpecs.SetAtGrow(nSpecCnt++, strSpec);
		}
	return nSpecCnt;
}

void CGrepView::OnGrep() 
//----------------------
{	if (m_bClosing) return;
	UpdateData();
	if (m_bGrepping)
		{	m_bStopGrep = TRUE;
			m_bGrepping = FALSE;
			m_buttonStart.SetWindowText("Grep");
			return;
		}
	if (GetFocus() == &m_editAddDir)
		{	m_strAddDir.TrimLeft(); m_strAddDir.TrimRight();
			if (! m_strAddDir.IsEmpty())
				{	OnAddDir();
					return;
				}
		}
	m_buttonStart.SetWindowText("Abort Grep");
	m_lstctlGrepOutput.DeleteAllItems();
	Grep(this, &InsertString);
	m_bStopGrep = m_bGrepping = FALSE;
	m_buttonStart.SetWindowText("Grep");
	CString strSpecs;
	m_comboSpecs.GetWindowText(strSpecs);
	strSpecs.TrimLeft(); strSpecs.TrimRight();
	_ParseSpecs_(strSpecs); 
	if (m_strarrFileSpecs.GetSize() <= 0)
		{	AfxMessageBox("ERROR : Error in Search Specifications");
			m_bGrepping = FALSE;
			m_buttonStart.SetWindowText("Grep");
			m_comboSpecs.SetFocus();
			return;
		}
	int nIndex = m_comboSpecs.FindStringExact(-1,strSpecs);
	if (nIndex != CB_ERR)
		m_comboSpecs.DeleteString(nIndex);
	m_comboSpecs.InsertString(0,strSpecs);
	if (m_comboSpecs.GetCount() > MAX_SPECS_HISTORY)
		m_comboSpecs.DeleteString(m_comboSpecs.GetCount() - 1);
	UpdateData();
}


void CGrepView::OnKillFocusAddDir() 
//---------------------------------
{	UpdateData();
	m_strAddDir.TrimLeft(); m_strAddDir.TrimRight(); 
	if (! m_strAddDir.IsEmpty())
		OnAddDir();
}


int CGrepView::OnVKeyToItem(UINT nKey, CListBox* pListBox, UINT nIndex) 
//---------------------------------------------------------------------
{	if (pListBox == &m_listDirectories)
		{	if (nKey == VK_DELETE)
				{	nIndex = m_listDirectories.GetCurSel(); 
					if (nIndex != LB_ERR)
						m_listDirectories.DeleteString(nIndex);
				}
		}
	return CFormView::OnVKeyToItem(nKey, pListBox, nIndex);
}

void CGrepView::OnGrepDelDir() 
//----------------------------
{	int nIndex = m_listDirectories.GetCurSel(); 
	if (nIndex != LB_ERR)
		m_listDirectories.DeleteString(nIndex);
	else
		AfxMessageBox("You must first select a Directory in the Directories List Box");
}

void CGrepView::OnCloseGrep() 
//---------------------------
{	m_bClosing = TRUE;
	((CGrepThread *)AfxGetThread())->m_pMainWnd->PostMessage(WM_SYSCOMMAND,SC_CLOSE,0);
}

void CGrepView::OnSelect(int nRow)
//--------------------------------
{	CGrepThread *pThread = (CGrepThread *)AfxGetThread();
	LV_ITEM lvItem;
	lvItem.iItem = nRow;
	lvItem.iSubItem = 0;
	lvItem.mask = LVIF_TEXT;
	lvItem.pszText = new char[_MAX_PATH + 1];
	lvItem.cchTextMax = _MAX_PATH;
	if (! m_lstctlGrepOutput.GetItem(&lvItem))
		{ TRACE0("CFindView::OnSelect : GetItem Failed\n");
			ASSERT(FALSE);
			return;
		}
	CString strName(lvItem.pszText);
	lvItem.iItem = nRow;
	lvItem.iSubItem = 3;
	lvItem.mask = LVIF_TEXT;
	lvItem.cchTextMax = _MAX_PATH;
	if (! m_lstctlGrepOutput.GetItem(&lvItem))
		{ TRACE0("CFindView::OnSelect : GetItem Failed\n");
			ASSERT(FALSE);
			return;
		}
	CString strPath(lvItem.pszText);
	lvItem.iItem = nRow;
	lvItem.iSubItem = 1;
	lvItem.mask = LVIF_TEXT;
	lvItem.cchTextMax = _MAX_PATH;
	if (! m_lstctlGrepOutput.GetItem(&lvItem))
		{ TRACE0("CFindView::OnSelect : GetItem Failed\n");
			ASSERT(FALSE);
			return;
		}
	CString strLineNo(lvItem.pszText);
	delete [] lvItem.pszText;
	strPath.TrimLeft(); strPath.TrimRight(); 
	pThread->m_pDlg->m_strCurDir = strPath;
	pThread->m_pDlg->m_strPath = strPath;
	if (strPath.Right(1) != "\\")
		pThread->m_pDlg->m_strPath += "\\";
	pThread->m_pDlg->m_strPath += strName;
	int nLineNo = atoi(strLineNo);
	// Cannot do inter-thread UpdateData, so send message.
	DWORD dwResult;
	// Use no word wrap for grep because the @#^!*)$ (@=F) Rich Edit Control counts wrapped lines as additional lines
	LPARAM lParam = MAKELPARAM(1,CRichEditView::WrapNone); 
	::SendMessageTimeout(pThread->m_pDlg->m_hWnd, WM_UPDATEDLG, 1, nLineNo, SMTO_NORMAL, 10000, &dwResult); 
}

void CGrepView::OnGrepHelp() 
//--------------------------
{	AfxGetApp()->WinHelp(IDH_grep);
}

void CGrepView::OnAppExit() 
//-------------------------
{	OnCloseGrep();
}

void CGrepView::OnAppAbout() 
//--------------------------
{	CAboutDlg dlgAbout;
	dlgAbout.DoModal();
}

//********************* Print Support **********************

void CGrepView::PrintTop(CDC* pDC, CPrintInfo* pInfo)
//---------------------------------------------------
{	int x=0,y=0, yinc;
	CFont font;                  
	CString strMeasure('X',80);

	memset(&m_lf,0,sizeof(LOGFONT));
  m_lf.lfHeight         = -12;
  m_lf.lfOutPrecision   = OUT_TT_PRECIS;
  m_lf.lfClipPrecision  = CLIP_LH_ANGLES;
  m_lf.lfQuality        = PROOF_QUALITY;
  m_lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
  strcpy(m_lf.lfFaceName,"Arial");      
  font.CreateFontIndirect(&m_lf);
  CFont* oldfont = pDC->SelectObject(&font);
	CSize TextSize = pDC->GetTextExtent("Y",1);
  yinc = -TextSize.cy;
	int nCol = pInfo->m_rectDraw.left;      
	CString strText;
	strText.Format("Grep Pattern : %s",m_strPattern);
  pDC->TextOut(nCol, pInfo->m_rectDraw.top + y, strText);
	y += yinc + yinc/2;
	strText.Format("File Specifications : %s",m_strSpecs);
	pDC->TextOut(nCol, pInfo->m_rectDraw.top + y, strText);
	y += yinc + yinc/2;
	strText = "Directories :";
	TextSize = pDC->GetTextExtent(strText);
	pDC->TextOut(nCol, pInfo->m_rectDraw.top + y, strText);
	nCol += TextSize.cx;	
	for (int i=0; i<m_listDirectories.GetCount(); i++)
		{	m_listDirectories.GetText(i,strText);
			pDC->TextOut(nCol, pInfo->m_rectDraw.top + y, strText);
			y += yinc;
		}
	y += yinc/2;
	nCol = pInfo->m_rectDraw.left;      
	strText = "Match Case = ";
	if (m_bMatchCase)
		strText += "On";
	else
		strText += "Off";
	pDC->TextOut(nCol, pInfo->m_rectDraw.top + y, strText);
	TextSize = pDC->GetTextExtent(strMeasure,30);
	nCol += TextSize.cx;	
	strText = "Match Whole Words Only = ";
	if (m_bMatchWord)
		strText += "On";
	else
		strText += "Off";
	pDC->TextOut(nCol, pInfo->m_rectDraw.top + y, strText);
	TextSize = pDC->GetTextExtent(strMeasure,30);
	nCol += TextSize.cx;	
	strText = "Match Whole Lines Only = ";
	if (m_bMatchLine)
		strText += "On";
	else
		strText += "Off";
	pDC->TextOut(nCol, pInfo->m_rectDraw.top + y, strText);
	y += yinc + yinc/2;

	nCol = pInfo->m_rectDraw.left;      
	strText = "Output Byte Offset = ";
	if (m_bOutputByte)
		strText += "On";
	else
		strText += "Off";
	pDC->TextOut(nCol, pInfo->m_rectDraw.top + y, strText);
	TextSize = pDC->GetTextExtent(strMeasure,30);
	nCol += TextSize.cx;	
	strText = "Display FileName Only = ";
	if (m_bNamesOnly)
		strText += "On";
	else
		strText += "Off";
	pDC->TextOut(nCol, pInfo->m_rectDraw.top + y, strText);
	TextSize = pDC->GetTextExtent(strMeasure,30);
	nCol += TextSize.cx;	
	strText = "Count Matches Only = ";
	if (m_bCountMatchesOnly)
		strText += "On";
	else
		strText += "Off";
	pDC->TextOut(nCol, pInfo->m_rectDraw.top + y, strText);
	y += yinc + yinc/2;

	nCol = pInfo->m_rectDraw.left;      
	strText = "Display non Matching Lines = ";
	if (m_bNonMatching)
		strText += "On";
	else
		strText += "Off";
	pDC->TextOut(nCol, pInfo->m_rectDraw.top + y, strText);
	TextSize = pDC->GetTextExtent(strMeasure,30);
	nCol += TextSize.cx;	
	strText = "Display Non Matching Files Only = ";
	if (m_bNoMatchFiles)
		strText += "On";
	else
		strText += "Off";
	pDC->TextOut(nCol, pInfo->m_rectDraw.top + y, strText);
	TextSize = pDC->GetTextExtent(strMeasure,30);
	nCol += TextSize.cx;	
	strText = "Suppress Error Output = ";
	if (m_bSuppressErrors)
		strText += "On";
	else
		strText += "Off";
	pDC->TextOut(nCol, pInfo->m_rectDraw.top + y, strText);
	y += yinc + yinc/2;

	nCol = pInfo->m_rectDraw.left;      
	strText.Format("Context Lines Before = %s",m_strLinesBefore);
	pDC->TextOut(nCol, pInfo->m_rectDraw.top + y, strText);
	TextSize = pDC->GetTextExtent(strMeasure,30);
	nCol += TextSize.cx;	
	strText.Format("Context Lines After = %s",m_strLinesAfter);
	pDC->TextOut(nCol, pInfo->m_rectDraw.top + y, strText);
	TextSize = pDC->GetTextExtent(strMeasure,30);
	nCol += TextSize.cx;	
	strText.Format("Grep Algorithm = %s",m_strGrep);
	pDC->TextOut(nCol, pInfo->m_rectDraw.top + y, strText);
	y += yinc + yinc/2;

	nCol = pInfo->m_rectDraw.left;      
	strText = "Recurse Sub-Directories = ";
	if (m_bRecurseDirectories)
		strText += "On";
	else
		strText += "Off";
	pDC->TextOut(nCol, pInfo->m_rectDraw.top + y, strText);
	y += yinc + yinc/2;
	
	pInfo->m_rectDraw.top += y;
	pDC->MoveTo(pInfo->m_rectDraw.left,pInfo->m_rectDraw.top);
	pDC->LineTo(pInfo->m_rectDraw.right,pInfo->m_rectDraw.top);
	pInfo->m_rectDraw.top += yinc;
	pDC->SelectObject(oldfont);  
}

void CGrepView::PrintGrepPage(CDC* pDC, CPrintInfo* pInfo)
//--------------------------------------------------------
{	BOOL bEndOfPage = FALSE;
  CString strMeasure('X',80);
  int x=0,y=0, yinc;
  CSize TextSize;
  CFont font;                  
  
  int nOldmode = pDC->SetMapMode(MM_LOENGLISH);
  pDC->DPtoLP(&pInfo->m_rectDraw);
  pInfo->m_rectDraw.left   += LEFT_MARGIN; 
  pInfo->m_rectDraw.right  -= RIGHT_MARGIN; 
  pInfo->m_rectDraw.top    -= TOP_MARGIN;       
  pInfo->m_rectDraw.bottom += BOTTOM_MARGIN;
   
  PrintTop(pDC, pInfo);
	memset(&m_lf,0,sizeof(LOGFONT));
  m_lf.lfHeight         = -10;
  m_lf.lfOutPrecision   = OUT_TT_PRECIS;
  m_lf.lfClipPrecision  = CLIP_LH_ANGLES;
  m_lf.lfQuality        = PROOF_QUALITY;
  m_lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
	m_lf.lfWeight = FW_BOLD;
	m_lf.lfUnderline = TRUE;
  strcpy(m_lf.lfFaceName,"Arial");      
  font.CreateFontIndirect(&m_lf);
  CFont* oldfont = pDC->SelectObject(&font);
	TextSize = pDC->GetTextExtent("Y",1);
  yinc = -TextSize.cy;
	int nCol = pInfo->m_rectDraw.left;      
  TextSize = pDC->GetTextExtent(strMeasure,25);
  pDC->TextOut(nCol, pInfo->m_rectDraw.top + y, "FileName");
  nCol += TextSize.cx;
	TextSize = pDC->GetTextExtent(strMeasure,10);
  pDC->TextOut(nCol, pInfo->m_rectDraw.top + y, "Line");
  nCol += TextSize.cx;
	TextSize = pDC->GetTextExtent(strMeasure,50);
  pDC->TextOut(nCol, pInfo->m_rectDraw.top + y, "Line Contents");
  nCol += TextSize.cx;
	TextSize = pDC->GetTextExtent(strMeasure,50);
  pDC->TextOut(nCol, pInfo->m_rectDraw.top + y, "Path");
	y += yinc;
	memset(&m_lf,0,sizeof(LOGFONT));
  m_lf.lfHeight         = -10;
  m_lf.lfOutPrecision   = OUT_TT_PRECIS;
  m_lf.lfClipPrecision  = CLIP_LH_ANGLES;
  m_lf.lfQuality        = PROOF_QUALITY;
  m_lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
  strcpy(m_lf.lfFaceName,"Arial");      
	CFont fontBody;
  fontBody.CreateFontIndirect(&m_lf);
  pDC->SelectObject(&fontBody);
	for (; m_nPrintPos < m_lstctlGrepOutput.GetItemCount(); m_nPrintPos++)
		{	CString strText;
			if ((pInfo->m_rectDraw.top + y  + yinc - BOTTOM_MARGIN) < pInfo->m_rectDraw.bottom)
        break;
      nCol = pInfo->m_rectDraw.left;      
      TextSize = pDC->GetTextExtent(strMeasure,25);
			m_lstctlGrepOutput.GetText(m_nPrintPos,0,strText,23);
      pDC->TextOut(nCol, pInfo->m_rectDraw.top + y, strText);
      nCol += TextSize.cx;
			TextSize = pDC->GetTextExtent(strMeasure,10);
			m_lstctlGrepOutput.GetText(m_nPrintPos,1,strText,8);
      pDC->TextOut(nCol, pInfo->m_rectDraw.top + y, strText);
      nCol += TextSize.cx;
			TextSize = pDC->GetTextExtent(strMeasure,50);
			m_lstctlGrepOutput.GetText(m_nPrintPos,2,strText,48);
      pDC->TextOut(nCol, pInfo->m_rectDraw.top + y, strText);
      nCol += TextSize.cx;
			TextSize = pDC->GetTextExtent(strMeasure,50);
			m_lstctlGrepOutput.GetText(m_nPrintPos,3,strText,48);
      pDC->TextOut(nCol, pInfo->m_rectDraw.top + y, strText);
      nCol += TextSize.cx;
			y += yinc;
		}	
	pDC->SelectObject(oldfont);  
  pDC->SetMapMode(nOldmode);
  if (m_nPrintPos >= m_lstctlGrepOutput.GetItemCount())
    m_bPrinting = FALSE;
}

BOOL CGrepView::OnPreparePrinting(CPrintInfo* pInfo)
//--------------------------------------------------
{	return DoPreparePrinting(pInfo);
}

void CGrepView::OnBeginPrinting(CDC* pDC, CPrintInfo* pInfo)
//------------------------------------------------------------------
{ m_bPrinting = TRUE;
	m_nPrintPos = 0;
}

void CGrepView::OnPrepareDC(CDC* pDC, CPrintInfo* pInfo)
//-------------------------------------------------------
{ CFormView::OnPrepareDC(pDC, pInfo);
  if (pInfo == NULL)
    return;
  pInfo->m_bContinuePrinting = m_bPrinting;
}    

void CGrepView::OnPrint(CDC* pDC, CPrintInfo* pInfo)
//-----------------------------------------------------
{ PrintGrepPage(pDC, pInfo);
}

void CGrepView::OnEndPrinting(CDC* pDC, CPrintInfo* pInfo)
//--------------------------------------------------------
{ 
}

void CGrepView::OnFilePrint() 
//---------------------------
{	UpdateData();
	CFormView::OnFilePrint();
}
