//
// Copyright 1997 Nicholas J. Irias.  All rights reserved.
//
//

// HistoryDlg.cpp : implementation file
//

#include "stdafx.h"
#include "p4win.h"
#include "HistoryDlg.h"
#include "viewerdlg.h"
#include "Mainfrm.h"
#include <process.h>
#include "cmd_diff.h"
#include "cmd_diff2.h"
#include "cmd_get.h"
#include "cmd_history.h"
#include "cmd_prepbrowse.h"
#include "DepotTreeCtrl.h"
#include "SpecDescDlg.h"
#include "hlp\p4win.hh"
#include "RegKeyEx.h"
#include "StringUtil.h"
#include "strops.h"

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

static LPCTSTR sRegKey = _T("Software\\Perforce\\P4Win\\Layout\\RevisionHistoryDlg");
static LPCTSTR sRegValue_ColumnWidths = _T("Column Widths");

#define FILELOGdashH 1
#define HOLD_LOCK_IF_HAVE_KEY (m_Key ? HOLD_LOCK : LOSE_LOCK)
#define UPDATE_STATUS(x) ((CMainFrame *)AfxGetMainWnd())->UpdateStatus(x)
int CALLBACK ListSort(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort);

static	BOOL	bReady = FALSE;

// Module global for use in sort callback
CHistoryDlg *pDlg;

/////////////////////////////////////////////////////////////////////////////
// HistoryDlg dialog

CHistoryDlg::CHistoryDlg(CWnd* pParent /*=NULL*/)
	: CDialog(CHistoryDlg::IDD, pParent)
{
	//{{AFX_DATA_INIT(CHistoryDlg)
	m_CompleteHist = m_CompleteHistIC = m_CompleteHistI = FALSE;
	//}}AFX_DATA_INIT
	m_pParent = pParent;
	m_Busy = m_Rerun = m_InitiallyNotBusy = FALSE;
	m_SizeSet = FALSE;
	m_SortAscending=FALSE;
	m_LastSortColumn=1;
	pDlg=this;
	m_InitRect.SetRect(0,0,100,100);
	m_WinPos.SetWindow( this, _T("RevisionHistoryDlg") );
	m_listHeight = m_dlgHeight = m_lorgHeight = m_Key = 0;
	m_EnableShowIntegs = TheApp()->m_RevHistEnableShowIntegs;
	m_IsMinimized = FALSE;
	m_More = FALSE;
	m_pFRDlg = NULL;
	m_FindWhatFlags = FR_DOWN | FR_HIDEWHOLEWORD;
}

CHistoryDlg::~CHistoryDlg()
{
	// can't use MainFrame()-> construct
	// because mainfram might have closed.
	CMainFrame * mainWnd = MainFrame();
	if (mainWnd)
		mainWnd->SetGotUserInput( );
}

void CHistoryDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
	//{{AFX_DATA_MAP(CHistoryDlg)
	DDX_Control(pDX, IDC_REVFILETYPE, m_RevFileType);
	DDX_Control(pDX, IDC_REVISIONACTION, m_RevisionAction);
	DDX_Control(pDX, IDC_REVISIONINFO, m_RevisionInfo);
	DDX_Control(pDX, IDC_REVISIONLIST, m_ListCtl);
	DDX_Check(pDX, IDC_COMPLETEHIST, m_CompleteHistI);
	DDX_Radio(pDX, IDC_RADIO1, m_CompleteHistIC);
	//}}AFX_DATA_MAP
}


BEGIN_MESSAGE_MAP(CHistoryDlg, CDialog)
	//{{AFX_MSG_MAP(CHistoryDlg)
	ON_NOTIFY(LVN_COLUMNCLICK, IDC_REVISIONLIST, OnColumnclickRevlist)
	ON_NOTIFY(LVN_ITEMCHANGED, IDC_REVISIONLIST, OnItemchangedRevlist)
	ON_UPDATE_COMMAND_UI(ID_FILEREVISION_DIFF2, OnUpdateDiff2)
	ON_UPDATE_COMMAND_UI(ID_FILE_RECOVER, OnUpdateRecover)
	ON_UPDATE_COMMAND_UI(ID_FILEREVISION_GET, OnUpdateGet)
	ON_UPDATE_COMMAND_UI(ID_CHANGE_DESCRIBE, OnUpdateDescribeChg)
	ON_UPDATE_COMMAND_UI(ID_POSITIONDEPOT, OnUpdatePositionDepot)
	ON_UPDATE_COMMAND_UI(ID_EDIT_COPY, OnUpdateCopyRows)
	ON_UPDATE_COMMAND_UI(ID_FILE_REVISIONTREE, OnUpdateFileRevisiontree)
	ON_UPDATE_COMMAND_UI(ID_FILE_ANNOTATIONS, OnUpdateFileAnnotate)
	ON_UPDATE_COMMAND_UI(ID_FILE_ANNOTATE, OnUpdateFileAnnotate)
	ON_UPDATE_COMMAND_UI(ID_FILE_ANNOTATEALL, OnUpdateFileAnnotate)
	ON_UPDATE_COMMAND_UI(ID_FILE_ANNOTATECHG, OnUpdateFileAnnotate)
	ON_UPDATE_COMMAND_UI(ID_FILE_ANNOTATECHGALL, OnUpdateFileAnnotate)
	ON_COMMAND(ID_FILEREVISION_DIFF2, OnDiff2)
	ON_COMMAND(ID_FILE_RECOVER, OnRecover)
	ON_COMMAND(ID_FILEREVISION_GET, OnGet)
	ON_COMMAND(ID_CHANGE_DESCRIBE, OnDescribeChg)
	ON_COMMAND(ID_POSITIONDEPOT, OnPositionDepot)
	ON_COMMAND(ID_EDIT_COPY, OnCopyRows)
	ON_COMMAND(ID_FILE_REVISIONTREE, OnFileRevisionTree)
	ON_COMMAND(ID_FILE_ANNOTATIONS, OnFileTimeLapseView)
	ON_COMMAND(ID_FILE_ANNOTATE, OnFileAnnotate)
	ON_COMMAND(ID_FILE_ANNOTATEALL, OnFileAnnotateAll)
	ON_COMMAND(ID_FILE_ANNOTATECHG, OnFileAnnotateChg)
	ON_COMMAND(ID_FILE_ANNOTATECHGALL, OnFileAnnotateChgAll)
	ON_COMMAND(ID_PERFORCE_OPTIONS, OnPerforceOptions)
	ON_UPDATE_COMMAND_UI(ID_POSITIONTOPATTERN, OnUpdatePositionToPattern)
	ON_COMMAND(ID_POSITIONTOPATTERN, OnPositionToPattern)
	ON_UPDATE_COMMAND_UI(ID_FINDNEXT, OnUpdatePositionToNext)
	ON_COMMAND(ID_FINDNEXT, OnPositionToNext)
	ON_UPDATE_COMMAND_UI(ID_FINDPREV, OnUpdatePositionToPrev)
	ON_COMMAND(ID_FINDPREV, OnPositionToPrev)
	ON_WM_SETCURSOR()
	ON_WM_SIZE()
	ON_WM_GETMINMAXINFO()
	ON_BN_CLICKED(IDB_DIFFVSCLIENT, OnDiffvsclient)
	ON_BN_CLICKED(IDC_COMPLETEHIST, OnCompletehist)
	ON_BN_CLICKED(IDC_RADIO1, OnCompletehist)
	ON_BN_CLICKED(IDC_RADIO2, OnCompletehist)
	ON_BN_CLICKED(IDC_RADIO3, OnCompletehist)
	ON_BN_CLICKED(IDHELP, OnHelp)
	ON_BN_CLICKED(IDC_MORE, OnMore)
	ON_BN_CLICKED(IDB_BROWSE, OnFileAutobrowse)
	ON_BN_CLICKED(IDB_DIFFREVISIONS, OnDiff2)
	ON_BN_CLICKED(IDB_GET, OnGet)
	ON_NOTIFY(LVN_KEYDOWN, IDC_REVISIONLIST, OnKeydownRevisionlist)
	ON_WM_CLOSE()
 	ON_WM_DESTROY()
	ON_WM_SYSCOMMAND()
	//}}AFX_MSG_MAP
	ON_COMMAND_RANGE(ID_FILE_BROWSER_1, ID_FILE_BROWSER_1+MAX_MRU_VIEWERS-1, OnFileMRUBrowser)
	ON_COMMAND(ID_FILE_NEWBROWSER, OnFileNewBrowser)
	ON_COMMAND(ID_FILE_QUICKBROWSE, OnFileQuickbrowse)
	ON_MESSAGE(WM_P4PREPBROWSE, OnP4ViewFile )
	ON_MESSAGE(WM_P4GET, OnP4Get )
	ON_MESSAGE(WM_P4DIFF, OnP4Diff )
	ON_MESSAGE(WM_P4DIFF2, OnP4Diff2 )
	ON_MESSAGE(WM_P4DESCRIBE, OnP4Describe )
	ON_MESSAGE(WM_P4ENDDESCRIBE, OnP4EndDescribe )
	ON_MESSAGE(WM_UPDATEHAVEREV, OnUpdateHaveRev )
	ON_MESSAGE(WM_QUITTING, OnQuitting )
	ON_WM_HELPINFO()
    ON_REGISTERED_MESSAGE( WM_FINDREPLACE, OnFindReplace )
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// HistoryDlg message handlers

int CHistoryDlg::DoModal() 
{
	return CDialog::DoModal();
}

void CHistoryDlg::Init(CHistory *hist, CString &ftype, BOOL isText, 
					   HWND depotWnd, int haveRev,
					   BOOL myOpenFile, const int initialRev /*=-1*/, 
					   LPCTSTR initialName /*=NULL*/, int key /*=0*/)
{
	ASSERT(IsWindow(depotWnd));
	ASSERT( haveRev > 0 || !myOpenFile );

	m_pHistory=hist;
	m_FileType=ftype;
	m_ViewFileIsText= isText;
	m_DepotWnd=depotWnd;
	m_HaveRev= haveRev;
	m_MyOpenFile= myOpenFile;
	m_InitialRev= initialRev;
	m_InitialName = initialName ? initialName : _T("");
	m_Key = key;
}

void CHistoryDlg::OnContextMenu(CPoint screen, int index) 
{
	if(m_Busy)
		return;

	// make a new selection new if reqd
	if(m_ListCtl.GetItemState(index,LVIS_SELECTED) != LVIS_SELECTED)
	{
		for(int i=m_ListCtl.GetItemCount(); i>=0; i-- )
			m_ListCtl.SetItemState(i, 0, LVIS_SELECTED);
		
		m_ListCtl.SetItemState(index, LVIS_SELECTED, LVIS_SELECTED);
	}

	// create an empty context menu
	CMenu popMenu;
	popMenu.CreatePopupMenu();
	CMenu viewMenu;
	viewMenu.CreatePopupMenu();
	int i;
	int actualMRUs=0;

	switch(m_ListCtl.GetSelectedCount())
	{
	case 1:
	  {
		// If revision is not deleted, create a view file submenu
		CRevision *rev= (CRevision *) m_ListCtl.GetItemData(index);
		ASSERT(rev);
		int  bAnn = 0;
		BOOL bTxt = FALSE;
		if ((rev->m_ChangeType.Find(_T("delete")) != 0) 
		 && (rev->m_ChangeType.Find(_T("purge")) != 0))
		{
			for(i=0; i < MAX_MRU_VIEWERS; i++)
			{
				if( GET_P4REGPTR()->GetMRUViewerName(i).GetLength() > 0 )
				{
					viewMenu.AppendMenu(MF_ENABLED | MF_STRING, ID_FILE_BROWSER_1+i, 
										CString ( _T("&") + GET_P4REGPTR()->GetMRUViewerName(i)) );
					actualMRUs++;
				}
			}

			if( actualMRUs > 0)
				viewMenu.AppendMenu(MF_SEPARATOR);

			viewMenu.AppendMenu(MF_ENABLED | MF_STRING, ID_FILE_QUICKBROWSE, 
								LoadStringResource( IDS_ASSOCVIEWER ));
			viewMenu.AppendMenu(MF_ENABLED | MF_STRING, ID_FILE_NEWBROWSER, 
								LoadStringResource( IDS_OTHERVIEWER ));
			if (GET_SERVERLEVEL() >= 14)
			{
				bAnn = MainFrame()->HaveTLV();
				if (bAnn)
				{
					CString fname = GetSelectedFileName();
					bAnn = ((CDepotTreeCtrl *)m_pParent)->IsInRemoteDepot(&fname) ? 0 : bAnn;
				}
				bTxt = ((m_FileType.Find(_T("text")) != -1) 
					 || (m_FileType.Find(_T("symlink")) != -1)) ? TRUE : FALSE;
				if (bTxt)
				{
					viewMenu.AppendMenu(MF_SEPARATOR);
					viewMenu.AppendMenu( stringsON, ID_FILE_ANNOTATE, 
							  LoadStringResource ( IDS_FILE_ANNOTATE ) );
					viewMenu.AppendMenu( stringsON, ID_FILE_ANNOTATEALL, 
							  LoadStringResource ( IDS_FILE_ANNOTATEALL ) );
					viewMenu.AppendMenu( stringsON, ID_FILE_ANNOTATECHG, 
							  LoadStringResource ( IDS_FILE_ANNOTATECHG ) );
					viewMenu.AppendMenu( stringsON, ID_FILE_ANNOTATECHGALL, 
							  LoadStringResource ( IDS_FILE_ANNOTATECHGALL ) );
				}
			}
			popMenu.AppendMenu(MF_POPUP, (UINT) viewMenu.GetSafeHmenu(), 
						  LoadStringResource(IDS_VIEW_THIS_REVISION_USING));
		}

		if (rev->m_FName == m_LatestName)
			popMenu.AppendMenu(MF_ENABLED | MF_STRING, ID_FILEREVISION_GET, LoadStringResource(IDS_SYNC_TO_THIS_REVISION));
		if (m_LatestIsDeleted && rev->m_ChangeType.Find(_T("delete")) != 0)
			popMenu.AppendMenu(MF_ENABLED | MF_STRING, ID_FILE_RECOVER, LoadStringResource(IDS_RECOVERDELFILE));
		if (!((CDepotTreeCtrl *)m_pParent)->IsInRemoteDepot(&(rev->m_FName)))
			popMenu.AppendMenu(MF_ENABLED | MF_STRING, ID_CHANGE_DESCRIBE, LoadStringResource(IDS_DESCRIBESUBMITTED) );
		if (bAnn == 1)
		{
			popMenu.AppendMenu( stringsON, ID_FILE_REVISIONTREE, LoadStringResource( IDS_REVISIONTREE ));
			if (bTxt)
				popMenu.AppendMenu( stringsON, ID_FILE_ANNOTATIONS, LoadStringResource ( IDS_ANNOTATIONS ) );
		}
		popMenu.AppendMenu(MF_ENABLED | MF_STRING, ID_POSITIONDEPOT, LoadStringResource(IDS_POSITIONDEPOT) );
		break;
	  }
	case 2:
	  {
		CRevision *rev1 = 0;
		CRevision *rev2 = 0;
		for(int i= 0; i < m_ListCtl.GetItemCount(); i++)
		{
			if(m_ListCtl.GetItemState(i, LVIS_SELECTED) == LVIS_SELECTED)
			{
				if (!rev1)
				{
					rev1= (CRevision *) m_ListCtl.GetItemData(i);
				}
				else
				{
					rev2= (CRevision *) m_ListCtl.GetItemData(i);
					break;
				}
			}
		}
		ASSERT(rev1);
		ASSERT(rev2);
		if (rev1->m_ChangeType.Find(_T("delete")) != 0 
		 && rev1->m_ChangeType.Find(_T("purge")) != 0 
		 && rev2->m_ChangeType.Find(_T("delete")) != 0
		 && rev2->m_ChangeType.Find(_T("purge")) != 0)
			popMenu.AppendMenu(MF_ENABLED | MF_STRING, ID_FILEREVISION_DIFF2, LoadStringResource(IDS_DIFF_TWO_REVISIONS));
		break;
	  }
	default:
		break;
	}

	if (m_ListCtl.GetSelectedCount())
		popMenu.AppendMenu(MF_ENABLED | MF_STRING, ID_EDIT_COPY, LoadStringResource(IDS_amp_Copy_tab_Ctrl_C));
	popMenu.AppendMenu(MF_ENABLED | MF_STRING, ID_POSITIONTOPATTERN, LoadStringResource(IDS_FINDPATTERN));

	// Finally blast the menu onto the screen
	if(popMenu.GetMenuItemCount() > 0)
		popMenu.TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON,	screen.x, screen.y, this);
	
}

void CHistoryDlg::OnUpdateDiff2(CCmdUI* pCmdUI) 
{
	pCmdUI->Enable(!m_Busy && m_ListCtl.GetSelectedCount()==2);
}

void CHistoryDlg::OnUpdateRecover(CCmdUI* pCmdUI) 
{
	pCmdUI->Enable(!m_Busy && m_ListCtl.GetSelectedCount()==1 
		                   && m_LatestIsDeleted);
}

void CHistoryDlg::OnUpdateGet(CCmdUI* pCmdUI) 
{
	pCmdUI->Enable(!m_Busy && m_ListCtl.GetSelectedCount()==1);
}

void CHistoryDlg::OnUpdateDescribeChg(CCmdUI* pCmdUI) 
{
	pCmdUI->Enable(!m_Busy && m_ListCtl.GetSelectedCount()==1);
}

void CHistoryDlg::OnUpdatePositionDepot(CCmdUI* pCmdUI) 
{
	pCmdUI->Enable(!m_Busy && m_ListCtl.GetSelectedCount());
}

void CHistoryDlg::OnUpdateCopyRows(CCmdUI* pCmdUI) 
{
	pCmdUI->Enable(!m_Busy && m_ListCtl.GetSelectedCount());
}

void CHistoryDlg::OnUpdateFileRevisiontree(CCmdUI* pCmdUI) 
{
	pCmdUI->Enable(!SERVER_BUSY() && GET_SERVERLEVEL() >= 14
					&& m_ListCtl.GetSelectedCount()==1);
}

void CHistoryDlg::OnUpdateFileAnnotate(CCmdUI* pCmdUI) 
{
	pCmdUI->Enable(!SERVER_BUSY() && GET_SERVERLEVEL() >= 14
					&& m_ListCtl.GetSelectedCount()==1);
}

void CHistoryDlg::OnFileMRUBrowser(UINT  nID)
{
	if(m_ListCtl.GetSelectedCount() != 1)
		{ ASSERT(0); return; }

	// Note which viewer is to be used
	m_Viewer= GET_P4REGPTR()->GetMRUViewer( nID - ID_FILE_BROWSER_1 );
	GET_P4REGPTR()->AddMRUViewer(m_Viewer);

	// Fetch the head revision of the file to a temp filename
	CCmd_PrepBrowse *pCmd= new CCmd_PrepBrowse;
	pCmd->Init( m_hWnd, RUN_ASYNC, HOLD_LOCK_IF_HAVE_KEY, m_Key);
	if( pCmd->Run( GetSelectedFileName(), m_FileType, GetSelectedRevision() ) )
	{
		m_Busy=TRUE;
		EnableButtons();
		UPDATE_STATUS(LoadStringResource(IDS_FETCHING_FILE));
	}
	else
		delete pCmd;
}

void CHistoryDlg::OnFileNewBrowser()
{
	if(m_ListCtl.GetSelectedCount() != 1)
		{ ASSERT(0); return; }

	// Fire up a common dlg to find new file
	CFileDialog fDlg(TRUE, _T("exe"), NULL,  
		OFN_FILEMUSTEXIST | OFN_HIDEREADONLY | OFN_NONETWORKBUTTON | OFN_PATHMUSTEXIST,
		LoadStringResource(IDS_HISTORY_BROWSE_FILTER), this,
		MainFrame()->m_osVer.dwMajorVersion < 5 ? OPENFILENAME_SIZE_VERSION_400 : sizeof(OPENFILENAME)); 

	if(fDlg.DoModal() == IDOK)
	{
		m_Viewer= fDlg.GetPathName();
		if(m_Viewer.GetLength() > 0)
		{
			// User selected a valid viewer, so try to run it
			GET_P4REGPTR()->AddMRUViewer(m_Viewer);
			OnFileMRUBrowser(ID_FILE_BROWSER_1);
		}
	}
}

void CHistoryDlg::OnFileQuickbrowse()
{
	FileQuickbrowse(FALSE);
}

void CHistoryDlg::FileQuickbrowse(BOOL ckifdeleted)
{
	if(m_ListCtl.GetSelectedCount() != 1)
		{ ASSERT(0); return; }

	if(ckifdeleted)
	{
		// don't allow quickbrowse for deleted files
		CRevision *rev = 0;
		for(int i= 0; i < m_ListCtl.GetItemCount(); i++)
		{
			if(m_ListCtl.GetItemState(i, LVIS_SELECTED) == LVIS_SELECTED)
			{
				rev= (CRevision *) m_ListCtl.GetItemData(i);
				break;
			}
		}
		ASSERT(rev);
		if (rev->m_ChangeType.Find(_T("delete")) == 0
		 || rev->m_ChangeType.Find(_T("purge")) == 0)
		{
			MessageBeep(0);
			return;
		}
	}

	m_Viewer=_T("SHELLEXEC");

	// Fetch the selected revision of the file to a temp filename
	CCmd_PrepBrowse *pCmd= new CCmd_PrepBrowse;
	pCmd->Init( m_hWnd, RUN_ASYNC, HOLD_LOCK_IF_HAVE_KEY, m_Key);
	if( pCmd->Run( GetSelectedFileName(), m_FileType, GetSelectedRevision() ) )
	{
		m_Busy=TRUE;
		EnableButtons();
		UPDATE_STATUS( LoadStringResource(IDS_FETCHING_FILE) );
	}
	else
		delete pCmd;
}


void CHistoryDlg::OnFileAutobrowse() 
{
	if(m_ListCtl.GetSelectedCount() != 1)
		{ ASSERT(0); return; }
	
	// Ask the user to pick a viewer
	CViewerDlg dlg;
	if(dlg.DoModal() == IDCANCEL)
		return;

	m_Viewer=dlg.GetViewer();
	if(m_Viewer != _T("SHELLEXEC"))
		GET_P4REGPTR()->AddMRUViewer(m_Viewer);

	// Fetch the head revision of the file to a temp filename
	CCmd_PrepBrowse *pCmd= new CCmd_PrepBrowse;
	pCmd->Init( m_hWnd, RUN_ASYNC, HOLD_LOCK_IF_HAVE_KEY, m_Key);
	if( pCmd->Run( GetSelectedFileName(), m_FileType, GetSelectedRevision() ) )
	{
		m_Busy=TRUE;
		EnableButtons();
		UPDATE_STATUS( LoadStringResource(IDS_FETCHING_FILE) );
	}
	else
		delete pCmd;
}

void CHistoryDlg::OnFileRevisionTree() 
{
	if(m_ListCtl.GetSelectedCount() != 1)
		{ ASSERT(0); return; }
	
	CString filename = GetSelectedFileName();
	TheApp()->CallP4RevisionTree(filename);	// use p4v.exe for revision tree
}

void CHistoryDlg::OnFileTimeLapseView() 
{
	if(m_ListCtl.GetSelectedCount() != 1)
		{ ASSERT(0); return; }
	
	CString filename = GetSelectedFileName();
	TheApp()->CallP4A(filename, _T(""), 0);	// use p4v.exe for annotate
}

void CHistoryDlg::OnFileAnnotate() 
{
	FileAnnotate(FALSE);
}

void CHistoryDlg::OnFileAnnotateAll() 
{
	FileAnnotate(TRUE);
}

void CHistoryDlg::OnFileAnnotateChg() 
{
	FileAnnotate(FALSE, TRUE);
}

void CHistoryDlg::OnFileAnnotateChgAll() 
{
	FileAnnotate(TRUE, TRUE);
}

void CHistoryDlg::FileAnnotate(BOOL bAll, BOOL bChg/*=FALSE*/) 
{
	if(m_ListCtl.GetSelectedCount() != 1)
		{ ASSERT(0); return; }
	
	CString filename = GetSelectedFileName();
	CString tempfilelogname;

	// Fetch the annotated file to a temp filename
	CCmd_PrepBrowse *pCmd= new CCmd_PrepBrowse;
	if (tempfilelogname.GetLength() > 0)
	{
		pCmd->Init(MainFrame()->GetDepotWnd(), RUN_ASYNC, HOLD_LOCK_IF_HAVE_KEY, m_Key);
		pCmd->SetTempFilelog(tempfilelogname);
		bAll = TRUE;
		bChg = FALSE;
	}
	else
		pCmd->Init( m_hWnd, RUN_ASYNC, HOLD_LOCK_IF_HAVE_KEY, m_Key );
	if( pCmd->Run( FALSE, filename, m_FileType, 
		bAll, bChg, FALSE, GetSelectedRevision(), 
				GET_P4REGPTR()->GetAnnotateWhtSpace(),
				bChg ? GET_P4REGPTR()->GetAnnotateIncInteg() : FALSE ) ) 
	{
		m_Busy = tempfilelogname.GetLength() > 0 ? FALSE : TRUE;
		EnableButtons();
		UPDATE_STATUS( LoadStringResource(IDS_FETCHING_FILE) );
		m_Viewer=GET_P4REGPTR()->GetEditApp();
	}
	else
		delete pCmd;
}

CString CHistoryDlg::WriteTempHistFile()
{
	Error e;
	FileSys *pOutputFile= FileSys::Create( FST_TEXT );
	CString TempPath= GET_P4REGPTR()->GetTempDir();
	CString TempName;
	CString FileName = m_InitialName;
	int i;
	if (((i = FileName.ReverseFind(_T('\\'))) != -1)
		|| ((i = FileName.ReverseFind(_T('/'))) != -1))
		FileName = FileName.Mid(i+1);

	for(i=0; i< 100; i++)
	{
		e.Clear();
		TempName.Format(_T("%s\\FileLog-Rev-%d-%s.txt"), TempPath, i, FileName);
		pOutputFile->Set(CharFromCString(TempName));
		
		if( !e.Test() )
			pOutputFile->MkDir( &e );// Prepare write (makes dir as required)
		if( !e.Test() )
		{
			pOutputFile->Perms( FPM_RW );			// Open it
			pOutputFile->Open( FOM_WRITE, &e );
		}
		if(!e.Test())
			break;
	}
	if(e.Test())
	{
		CString txt;
		txt.Format(_T("Error opening temporary file:\n %s"), TempName);
		TheApp()->StatusAdd(txt, SV_ERROR);

		delete pOutputFile;
		return _T("");;
	}

	StrBuf sptr;
	CString str = m_InitialName + _T('\n');
	sptr.Set(const_cast<char*>((const char*)CharFromCString(str)));
	pOutputFile->Write( &sptr, &e );
	if(e.Test())
	{
		CString txt;
		txt.Format(_T("Error writing temporary file header:\n %s"), TempName);
		TheApp()->StatusAdd(txt, SV_ERROR);
		delete pOutputFile;
		return _T("");
	}
	BOOL b = FALSE;
	CRevision *rev=m_pHistory->GetHeadRevision();
	int prevrev;
	do
	{
		prevrev = rev->m_RevisionNum;
		if (m_InitialName == rev->m_FName)
		{
			b = TRUE;
			CString chgType = rev->m_ChangeType;
			CString integs = _T("");
			if ((i = chgType.Find(_T(": "))) != -1)
			{
				integs = chgType.Mid(i + 2);
				chgType = chgType.Left(i);
			}
			str.Format(_T("... #%d change %d %s on %s by %s %s"), 
				rev->m_RevisionNum, rev->m_ChangeNum, chgType, 
				rev->m_Date, rev->m_User, rev->m_ChangeDescription);
			str.Replace(_T("\n"), _T("\n        "));
			if (integs.GetLength() > 0)
			{
				integs.Replace(_T("; "), _T("\n... ... "));
				str += _T("\n\n... ... ") + integs;
			}
			str += _T("\n\n");
			str.Remove(_T('\r'));
			sptr.Set(const_cast<char*>((const char*)CharFromCString(str)));
			pOutputFile->Write( &sptr, &e );
			if(e.Test())
			{
				CString txt;
				txt.Format(_T("Error writing temporary file:\n %s"), TempName);
				TheApp()->StatusAdd(txt, SV_ERROR);
				delete pOutputFile;
				return _T("");
			}
		}
		rev=m_pHistory->GetPrevRevision();
	} while (rev && (rev->m_RevisionNum < prevrev));

	pOutputFile->ClearDeleteOnClose();
	pOutputFile->Close(&e);
	delete pOutputFile;
	return TempName;
}

LRESULT CHistoryDlg::OnP4Diff(WPARAM wParam, LPARAM lParam)
{
	CCmd_Diff *pCmd= (CCmd_Diff *) wParam;
	if(pCmd->GetDiffRunCount() == 0)
	{
		AfxMessageBox(pCmd->GetDiffErrCount() ? pCmd->GetDiffErrBuf() : LoadStringResource(IDS_FILES_ARE_IDENTICAL), 
			MB_ICONINFORMATION);
	}
	
	UPDATE_STATUS(_T(""));
	m_Busy=FALSE;
	EnableButtons();
	delete pCmd;
	return 0;
}

LRESULT CHistoryDlg::OnP4Diff2(WPARAM wParam, LPARAM lParam)
{
	CCmd_Diff2 *pCmd= (CCmd_Diff2 *) wParam;
	CString msg= pCmd->GetInfoText();
	if( ! msg.IsEmpty() )
	{
		AfxMessageBox( msg, MB_ICONINFORMATION);
	}
	
	UPDATE_STATUS(_T(""));
	m_Busy=FALSE;
	EnableButtons();
	delete pCmd;
	return 0;
}


LRESULT CHistoryDlg::OnP4Get(WPARAM wParam, LPARAM lParam)
{
	UPDATE_STATUS(_T(""));
		
	::SendMessage(m_DepotWnd, WM_P4GET, wParam, lParam);
	m_Busy=FALSE;
	EnableButtons();
	return 0;
}

// TODO: This code is pretty much a copy of the code in CDepotView::RunViewer()
// Might want to craft a single file viewing class that can be instantiated from
// anywhere, or perhaps make CMainFrame be responsible for all file viewing.
LRESULT CHistoryDlg::OnP4ViewFile(WPARAM wParam, LPARAM lParam)
{
	UPDATE_STATUS(_T(""));
	CString tempName;
	CString msg;

	m_Busy=FALSE;
	EnableButtons();

	CCmd_PrepBrowse *pCmd= (CCmd_PrepBrowse *) wParam;

	if(!pCmd->GetError())
	{
		m_ViewFilePath= pCmd->GetTempName();

		// First, get the file extension, if any, and find out if
		// its a text file
		CString extension;
		int slash= m_ViewFilePath.ReverseFind(_T('\\'));
		if(slash != -1)
			extension=m_ViewFilePath.Mid(slash+1);
		else
			extension=m_ViewFilePath;

		int dot= extension.ReverseFind(_T('.'));
		if(dot == -1)
			extension=_T("");
		else
			extension=extension.Mid(dot+1);

		// We have the file, m_ViewFilePath, try to display it
		while(1)
		{
			if(m_Viewer == _T("SHELLEXEC"))
			{
				CString assocViewer=_T("");

				// First, see if there a P4win file association
				if(!extension.IsEmpty())
					assocViewer= GET_P4REGPTR()->GetAssociatedApp(extension);
			
				// If we still havent found a viewer, set viewer to default text app
				// if user wishes to ignore windows associations
				if(assocViewer.IsEmpty() && m_ViewFileIsText && GET_P4REGPTR()->GetIgnoreWinAssoc())
					assocViewer= GET_P4REGPTR()->GetEditApp();
			
				// Let windows take a crack at finding a viewer
				if(assocViewer.IsEmpty() && !extension.IsEmpty())
				{
					// Quick check for executeable extension, which will make ShellExec try to run the file
					HINSTANCE hinst=0;
					if( extension.CompareNoCase(_T("com")) != 0 && extension.CompareNoCase(_T("exe")) != 0 &&
						extension.CompareNoCase(_T("bat")) != 0 && extension.CompareNoCase(_T("cmd")) != 0)
					{
						// first try the non-standard M$ IDE's - Have to be done first they're so BAAAD
						// give VS .NET (non-standard!) a try.
						hinst= ShellExecute( m_hWnd, _T("Open.VisualStudio.7.1"), m_ViewFilePath, NULL, NULL, SW_SHOWNORMAL);
						if( (int) hinst > 32 ) 
							break;  // successfull VS .NET editor launch
						if( (int) hinst == SE_ERR_NOASSOC)	// give MSDEV (non-standard!) a try
						{
							hinst= ShellExecute( m_hWnd, _T("&Open with MSDEV"), m_ViewFilePath, NULL, NULL, SW_SHOWNORMAL);
							if( (int) hinst > 32 ) 
								break;  // successfull MSDEV editor launch
						}
						hinst= ShellExecute( m_hWnd, _T("open"), m_ViewFilePath, NULL, NULL, SW_SHOWNORMAL);
						if( (int) hinst > 32)
						{
							break;  // successfull viewer launch
						}
					}
				}

				// If windows doesnt have an associated viewer for a text file, we use the 
				// default text editor
				if(assocViewer.IsEmpty() && m_ViewFileIsText)
					assocViewer= GET_P4REGPTR()->GetEditApp();
				

				if ( TheApp()->RunViewerApp( assocViewer, m_ViewFilePath ) )
					break;  // successfull viewer launch
			}
			else
			{
				if ( TheApp()->RunViewerApp( m_Viewer, m_ViewFilePath ) )
					break;  // successfull viewer launch
			}

			CString msg;
			msg.FormatMessage(IDS_UNABLE_TO_LAUNCH_VIEWER_s, m_ViewFilePath);
			if(AfxMessageBox(msg, MB_YESNO | MB_ICONEXCLAMATION) == IDNO)
				break;

			// Try to find an alternate viewer
			CViewerDlg dlg;
			if(dlg.DoModal() == IDCANCEL)
				break;

			m_Viewer=dlg.GetViewer();
			if(m_Viewer != _T("SHELLEXEC"))
				GET_P4REGPTR()->AddMRUViewer(m_Viewer);
		} // while
	} // no command error
	
	delete pCmd;
	UPDATE_STATUS(_T(""));
	return 0;
}


LRESULT CHistoryDlg::OnUpdateHaveRev(WPARAM wParam, LPARAM lParam) 
{
	int ctr=2;
	m_HaveRev = lParam;
	CString revStr;
	revStr.Format(_T("%d"), m_HaveRev);
	for(int iItem=0; ctr && iItem < m_pHistory->GetRevisionCount(); iItem++)
	{
		if (m_InitialName != m_ListCtl.GetItemText(iItem, 1))
			continue;
		CString txt = m_ListCtl.GetItemText(iItem, 0);
		if (txt.Find(_T('*')) != -1)
		{
			txt.TrimRight(_T("* "));
			m_ListCtl.SetItemText(iItem, 0, txt);
			ctr--;
		}
		CString txtr = txt;
		txtr.TrimLeft();
		if (txtr == revStr)
		{
			txt += _T('*');
			m_ListCtl.SetItemText(iItem, 0, txt);
			ctr--;
		}
	}
	return 0;
}

LRESULT CHistoryDlg::OnQuitting(WPARAM wParam, LPARAM lParam) 
{
	if (m_pParent)
		m_pParent->SendMessage(WM_P4ENDHISTORY, 0, (LPARAM)this);
	return 0;
}

void CHistoryDlg::OnClose() 
{
	OnOK();
}

void CHistoryDlg::OnCancel() 
{
	// What happens when the user accidentally hits escape
	OnOK();	// Now if they hit Escape, the window closes.
}

void CHistoryDlg::OnDiff2() 
{
	if(m_ListCtl.GetSelectedCount() != 2)
		{ ASSERT(0); return; }

	CRevision *rev[2];
	int i, revindex;

	// Get the two file revs - (in reverse order so newer is the 2nd diff arg)
	for(revindex=0, i=m_ListCtl.GetItemCount()-1; revindex < 2 && i >=0; i--)
	{
		if(m_ListCtl.GetItemState(i, LVIS_SELECTED) == LVIS_SELECTED)
		{
			rev[revindex]= (CRevision *) m_ListCtl.GetItemData(i);
			revindex++;
		}
	}

	ASSERT(revindex==2);

	CString ft0;
	CString ft1;
	if ((i = rev[0]->m_ChangeDescription.Find(_T(')'))) != -1)
		 ft0 = rev[0]->m_ChangeDescription.Left(i+1);
	else ft0 = m_FileType;
	if ((i = rev[1]->m_ChangeDescription.Find(_T(')'))) != -1)
		 ft1 = rev[1]->m_ChangeDescription.Left(i+1);
	else ft1 = m_FileType;
	if (!GET_P4REGPTR()->GetDiffAppIsBinary())
	{
		if (((ft0.Find(_T("text")) != -1) && (ft1.Find(_T("binary")) != -1))
		 || ((ft0.Find(_T("binary")) != -1) && (ft1.Find(_T("text")) != -1)))
		{
			CString txt;
			txt.FormatMessage(IDS_ONLY_DIFF_SAME_TYPES_s_n_s_s_n_s, 
				rev[0]->m_FName, rev[0]->m_RevisionNum, ft0,
				rev[1]->m_FName, rev[1]->m_RevisionNum, ft1);
			AfxMessageBox(txt);
			return;
		}
	}
	if ((ft0.GetAt(0) == _T('(')) && (ft0.Find(_T(' ')) == -1))
	{
		ft0.TrimLeft(_T('('));
		ft0.TrimRight(_T(')'));
	}
	else ft0 = m_FileType;
	if ((ft1.GetAt(0) == _T('(')) && (ft1.Find(_T(' ')) == -1))
	{
		ft1.TrimLeft(_T('('));
		ft1.TrimRight(_T(')'));
	}
	else ft1 = m_FileType;

	CCmd_Diff2 *pCmd= new CCmd_Diff2;
	pCmd->Init( m_hWnd, RUN_ASYNC, HOLD_LOCK_IF_HAVE_KEY, m_Key);
	if( pCmd->Run( rev[0]->m_FName, rev[1]->m_FName, 
		rev[0]->m_RevisionNum, rev[1]->m_RevisionNum, ft0, ft1) )
	{
		m_Busy=TRUE;
		EnableButtons();
		UPDATE_STATUS( LoadStringResource(IDS_DIFFING_FILES) );
	}
	else
		delete pCmd;
}

void CHistoryDlg::OnDiffvsclient() 
{
	if( m_ListCtl.GetSelectedCount() != 1 || m_HaveRev == 0)
		{ ASSERT(0); return; }

	if (!m_CompleteHist || (GetSelectedFileName() == m_LatestName))
	{
		CString depotName;
		depotName.Format(_T("%s#%d"), GetSelectedFileName(), GetSelectedRevision());

		m_FileList.RemoveAll();
		m_FileList.AddHead(depotName);

		CCmd_Diff *pCmd= new CCmd_Diff;
		pCmd->Init( m_hWnd, RUN_ASYNC, HOLD_LOCK_IF_HAVE_KEY, m_Key);
		if( pCmd->Run( &m_FileList, ( !m_MyOpenFile && m_HaveRev == GetSelectedRevision() ) 
									? _T("-f") : NULL ) )
		{
			m_Busy=TRUE;
			EnableButtons();
			UPDATE_STATUS( LoadStringResource(IDS_DIFFING_FILE) );
		}
		else
			delete pCmd;
	}
	else
	{
		// Get the rev
		CRevision *rev = 0;
		int i;
		for(i= 0; i < m_ListCtl.GetItemCount(); i++)
		{
			if(m_ListCtl.GetItemState(i, LVIS_SELECTED) == LVIS_SELECTED)
			{
				rev= (CRevision *) m_ListCtl.GetItemData(i);
				break;
			}
		}
		CString ft0;
		if ((i = rev->m_ChangeDescription.Find(_T(')'))) != -1)
			ft0 = rev->m_ChangeDescription.Left(i+1);
		else ft0 = m_FileType;
		if (!GET_P4REGPTR()->GetDiffAppIsBinary())
		{
			if (((ft0.Find(_T("text")) != -1) && (m_FileType.Find(_T("binary")) != -1))
			 || ((ft0.Find(_T("binary")) != -1) && (m_FileType.Find(_T("text")) != -1)))
			{
				CString txt;
				txt.FormatMessage(IDS_ONLY_DIFF_SAME_TYPES_s_n_s_s_n_s, 
					rev->m_FName, rev->m_RevisionNum, ft0,
					m_LatestName, m_HaveRev, m_FileType);
				AfxMessageBox(txt);
				return;
			}
		}
		if ((ft0.GetAt(0) == _T('(')) && (ft0.Find(_T(' ')) == -1))
		{
			ft0.TrimLeft(_T('('));
			ft0.TrimRight(_T(')'));
		}
		else ft0 = m_FileType;
		CCmd_Diff2 *pCmd= new CCmd_Diff2;
		pCmd->Init( m_hWnd, RUN_ASYNC, HOLD_LOCK_IF_HAVE_KEY, m_Key);
		if( pCmd->Run( rev->m_FName, m_LatestName, 
			rev->m_RevisionNum, m_HaveRev, ft0, m_FileType, FALSE, TRUE) )
		{
			m_Busy=TRUE;
			EnableButtons();
			UPDATE_STATUS( LoadStringResource(IDS_DIFFING_FILES) );
		}
		else
			delete pCmd;
	}
}


void CHistoryDlg::OnRecover() 
{
	if(m_ListCtl.GetSelectedCount() != 1)
		{ ASSERT(0); return; }

	m_Recover.AddHead(m_LatestName);
	OnGet();
}
	
void CHistoryDlg::OnGet() 
{
	if(m_ListCtl.GetSelectedCount() != 1)
		{ ASSERT(0); m_Recover.RemoveAll(); return; }

	CRevision *rev = 0;
	for(int i= 0; i < m_ListCtl.GetItemCount(); i++)
	{
		if(m_ListCtl.GetItemState(i, LVIS_SELECTED) == LVIS_SELECTED)
		{
			rev= (CRevision *) m_ListCtl.GetItemData(i);
			break;
		}
	}

    ASSERT(rev);

	CString name;
	name.Format(_T("%s#%ld"), rev->m_FName, rev->m_RevisionNum);
	
	m_FileList.AddHead(name);
	
	CCmd_Get *pCmd= new CCmd_Get;
	pCmd->Init( m_hWnd, RUN_ASYNC, HOLD_LOCK_IF_HAVE_KEY, m_Key);
	if( m_Recover.GetCount() > 0 )
	{
		for (POSITION pos = m_Recover.GetHeadPosition(); pos != NULL; )
		{
			CString str = m_Recover.GetNext(pos);
			if (str.Find(_T('%')) != -1)
			{
				StrBuf b;
				StrBuf f;
				f << CharFromCString(str);
				StrPtr *p = &f;
				StrOps::StrToWild(*p, b);
				str = CharToCString(b.Value());
			}
			pCmd->Add2Recover(str);
		}
		m_Recover.RemoveAll();
	}
	pCmd->SetRevHistWnd(m_hWnd);
	pCmd->SetRevReq(rev->m_RevisionNum);
	if( pCmd->Run( &m_FileList, FALSE ) )
	{
		m_Busy=TRUE;
		EnableButtons();
	}
	else
		delete pCmd;
	GotoDlgCtrl(GetDlgItem(IDC_REVISIONLIST));
}

void CHistoryDlg::OnDescribeChg() 
{
	long changeNumber = GetSelectedChgNbr();

	if(changeNumber != 0)
	{
		CString nbr;
		nbr.Format(_T("%ld"), changeNumber);
		CCmd_Describe *pCmd= new CCmd_Describe;
		pCmd->Init( m_hWnd, RUN_ASYNC, HOLD_LOCK_IF_HAVE_KEY, m_Key);
		if( pCmd->Run( P4DESCRIBE, nbr) )
			UPDATE_STATUS(LoadStringResource(IDS_FETCHING_CHANGELIST_DESCRIPTION));
		else
			delete pCmd;
	}
}

void CHistoryDlg::OnDescribeChgLong(long changeNumber, int flag /*= 0*/) 
{
	if(changeNumber != 0)
	{
		CString nbr;
		nbr.Format(_T("%ld"), changeNumber);
		CCmd_Describe *pCmd= new CCmd_Describe;
		pCmd->Init( m_hWnd, RUN_ASYNC, HOLD_LOCK_IF_HAVE_KEY, m_Key);
		if( pCmd->Run( P4DESCRIBELONG, nbr, NULL, FALSE, flag) )
			UPDATE_STATUS(LoadStringResource(IDS_FETCHING_CHANGELIST_DESCRIPTION));
		else
			delete pCmd;
	}
}

LRESULT CHistoryDlg::OnP4Describe(WPARAM wParam, LPARAM lParam)
{
	CCmd_Describe *pCmd= (CCmd_Describe *) wParam;

	if(!pCmd->GetError())
	{
		CString desc= MakeCRs(pCmd->GetDescription());
		
		int key;
		CSpecDescDlg *dlg = new CSpecDescDlg(this);
		dlg->SetIsModeless(TRUE);
		dlg->SetKey(key = pCmd->HaveServerLock()? pCmd->GetServerKey() : 0);
		dlg->SetItemName( pCmd->GetReference() );
		dlg->SetDescription(desc);
		dlg->SetCaption(LoadStringResource(IDS_PERFORCE_CHANGELIST_DESCRIPTION));
		dlg->SetShowNextPrev(TRUE);
		dlg->SetShowShowDiffs(TRUE);
		dlg->SetDiffFlag(pCmd->GetFlag());
		dlg->SetViewType(P4CHANGE_SPEC);
		dlg->SetFindStrFlags(&m_FindWhatStr, m_FindWhatFlags);
		dlg->SetKey(m_Key);
		if (!dlg->Create(IDD_SPECDESC, this))	// display the description dialog box
		{
			dlg->DestroyWindow();	// some error! clean up
			delete dlg;
		}
	}
	else	// had an error - need to turn painting back on
	{
		EnumChildWindows(AfxGetMainWnd()->m_hWnd, ChildSetRedraw, TRUE);
	}
	
	delete pCmd;
	UPDATE_STATUS(_T(""));
	return 0;
}

LRESULT CHistoryDlg::OnP4EndDescribe(WPARAM wParam, LPARAM lParam)
{
	CSpecDescDlg *dlg = (CSpecDescDlg *)lParam;
	CString ref = dlg->GetItemName();
	ASSERT(!ref.IsEmpty());

	switch(wParam)				// which button did they click to close the box?
	{
	case ID_SHOWDIFFS_NORMAL:
	case ID_SHOWDIFFS_SUMMARY:
	case ID_SHOWDIFFS_UNIFIED:
	case ID_SHOWDIFFS_CONTEXT:
	case ID_SHOWDIFFS_RCS:
	case ID_SHOWDIFFS_NONE:
	{
		long l = _ttol(ref);
		OnDescribeChgLong(l, wParam);
		break;
	}
	case IDC_NEXTITEM:
	case IDC_PREVITEM:
	{
		BOOL b = FALSE;
		CRevision *rev;
		long l = _ttol(ref);

		// Get the rev
		int np = wParam == IDC_NEXTITEM ? 1 : -1;
		int count = m_ListCtl.GetItemCount();
		for(int ix = -1; ++ix < count; )
		{
			rev = (CRevision *) m_ListCtl.GetItemData(ix);
			if (rev && rev->m_ChangeNum == l)
			{
				if((ix + np < 0) || (ix + np >= count))
					break;
				// clear all previous selections
				for (int i= 0; m_ListCtl.GetSelectedCount(); i++)
					m_ListCtl.SetItemState(i, 0, LVIS_SELECTED|LVIS_FOCUSED);
				// select the one we want
				m_ListCtl.SetItemState(ix + np, LVIS_SELECTED|LVIS_FOCUSED,
												LVIS_SELECTED|LVIS_FOCUSED);
				OnDescribeChg();	// display the next/prev in the list on the screen
				b = TRUE;
				break;
			}
		}
		if (b)
			break;
		// fall thru to turn painting back on
	}
	default:	// clicked OK, pressed ESC or ENTER - need to turn painting back on
		EnumChildWindows(AfxGetMainWnd()->m_hWnd, ChildSetRedraw, TRUE);
		break;
	}
	dlg->DestroyWindow();
	return TRUE;
}

int CHistoryDlg::GetSelectedRevision() 
{
	if(m_ListCtl.GetSelectedCount() != 1)
		{ ASSERT(0); return 0; }

	CRevision *rev = 0;

	// Get the rev
	for(int i= 0; i < m_ListCtl.GetItemCount(); i++)
	{
		if(m_ListCtl.GetItemState(i, LVIS_SELECTED) == LVIS_SELECTED)
		{
			rev= (CRevision *) m_ListCtl.GetItemData(i);
			break;
		}
	}
    ASSERT(rev);
	return rev->m_RevisionNum;
}

CString CHistoryDlg::GetSelectedFileName() 
{
	if(m_ListCtl.GetSelectedCount() != 1)
		{ ASSERT(0); return m_pHistory->GetFileName(); }

	CRevision *rev = 0;

	// Get the rev
	for(int i= 0; i < m_ListCtl.GetItemCount(); i++)
	{
		if(m_ListCtl.GetItemState(i, LVIS_SELECTED) == LVIS_SELECTED)
		{
			rev= (CRevision *) m_ListCtl.GetItemData(i);
			break;
		}
	}

    ASSERT(rev);
	return rev->m_FName;
}

int CHistoryDlg::GetSelectedChgNbr() 
{
	if(m_ListCtl.GetSelectedCount() != 1)
		{ ASSERT(0); return 0; }

	CRevision *rev = 0;

	// Get the rev
	for(int i= 0; i < m_ListCtl.GetItemCount(); i++)
	{
		if(m_ListCtl.GetItemState(i, LVIS_SELECTED) == LVIS_SELECTED)
		{
			rev= (CRevision *) m_ListCtl.GetItemData(i);
			break;
		}
	}

    ASSERT(rev);
	return rev->m_ChangeNum;
}


BOOL CHistoryDlg::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message) 
{
	if(SERVER_BUSY() && m_InitiallyNotBusy)
		return SET_BUSYCURSOR();
	else
		return CDialog::OnSetCursor(pWnd, nHitTest, message);
}


BOOL CHistoryDlg::OnInitDialog()
{
	CRect rect;

	bReady = FALSE;
    CDialog::OnInitDialog();

	if (m_pParent)
 		MainFrame()->SetModelessWnd(this);

	m_listHeight = GET_P4REGPTR()->GetHistListHeight();
	if (!m_EnableShowIntegs || !GET_P4REGPTR()->GetEnableRevHistShowIntegs( ))
	{
		GetDlgItem(IDC_COMPLETEHIST)->EnableWindow(FALSE);
		GetDlgItem(IDC_COMPLETEHIST)->ShowWindow(SW_HIDE);
		m_CompleteHist = FALSE;
	}
	else
	{
		m_CompleteHist = m_CompleteHistIC = m_CompleteHistI = GET_P4REGPTR()->GetFetchCompleteHist();
#ifdef	FILELOGdashH
		if (GET_SERVERLEVEL() >= 24)	// 2007.3 or later?
		{
			GetDlgItem(IDC_COMPLETEHIST)->EnableWindow(FALSE);
			GetDlgItem(IDC_COMPLETEHIST)->ShowWindow(SW_HIDE);
			if (!m_Key)
			{
				GetDlgItem(IDC_STATIC_1)->EnableWindow(TRUE);
				GetDlgItem(IDC_STATIC_1)->ShowWindow(SW_SHOW);
				GetDlgItem(IDC_RADIO1)->EnableWindow(TRUE);
				GetDlgItem(IDC_RADIO1)->ShowWindow(SW_SHOW);
				GetDlgItem(IDC_RADIO2)->EnableWindow(TRUE);
				GetDlgItem(IDC_RADIO2)->ShowWindow(SW_SHOW);
				GetDlgItem(IDC_RADIO3)->EnableWindow(TRUE);
				GetDlgItem(IDC_RADIO3)->ShowWindow(SW_SHOW);
			}
		}
		else 
#endif
			if (m_CompleteHist > 1)
			m_CompleteHist = m_CompleteHistI = 1;
	}
	UpdateData(FALSE);

	// if this is being called from an in-progress command (such as p4 resolve)
	// don't show the Complete History checkbox because it's too difficult
	// to close and reopen this dialog without dropping the lock
	if (m_Key)
	{
		GetDlgItem(IDC_COMPLETEHIST)->EnableWindow(FALSE);
		GetDlgItem(IDC_COMPLETEHIST)->ShowWindow(SW_HIDE);
	}

	// Set the dialog caption
	CString caption;
	caption.FormatMessage(IDS_REVISION_HISTORY_FOR_s, 
		!TheApp()->m_RevHistPath.IsEmpty() ? TheApp()->m_RevHistPath
			: !m_InitialName.IsEmpty() ? m_InitialName : m_pHistory->GetFileName());
	SetWindowText(caption);

	// Modify the list control style so that the entire selected row is highlighted
	DWORD dwStyle = ::SendMessage(m_ListCtl.m_hWnd,LVM_GETEXTENDEDLISTVIEWSTYLE,0,0);
	dwStyle |= LVS_EX_FULLROWSELECT;
	::SendMessage(m_ListCtl.m_hWnd,LVM_SETEXTENDEDLISTVIEWSTYLE,0,dwStyle);

	// Make sure list control shows selection when not the focused control
	m_ListCtl.ModifyStyle(0, LVS_SHOWSELALWAYS, 0);

	// Get original size of controls
	CWnd *pBtn=GetDlgItem(IDHELP);
	pBtn->GetWindowRect(&rect);
	m_SmlWidth = rect.Width();

	m_ListCtl.GetWindowRect(&rect);
	m_lorgHeight = rect.Height();

	int colwidth[7] = {16,5,18,18,18,10,15};	// these start out as %-ages and are converted to pixels
	colwidth[0] = colwidth[0] * rect.Width()/100;	// 1st 5 cols widths are based on orig size
	colwidth[1] = colwidth[1] * rect.Width()/100;
	colwidth[2] = colwidth[2] * rect.Width()/100;
	colwidth[3] = colwidth[3] * rect.Width()/100;
	colwidth[4] = colwidth[4] * rect.Width()/100;

	// Record the initial window size, then see if there is a registry preference
	GetWindowRect(&m_InitRect);
	m_WinPos.RestoreWindowPosition();

	// Get new size of control after resized as specified in the registry
	m_ListCtl.GetWindowRect(&rect);
	colwidth[5] = colwidth[5] * rect.Width()/100;	// last 2 col widths are based on reg size
	colwidth[6] = colwidth[6] * rect.Width()/100;

	// Get any saved column widths from registry
	RestoreSavedWidths(colwidth, 7);

	// Make sure no column completely disappeared (because you can't get it back then)
	for (int i=-1; ++i < 7; )
	{
		if (colwidth[i] < 5)
			colwidth[i] = 5;
	}

	// Handle one-time case of going from fewer than 7 cols to 7 cols
	if (!colwidth[6])
	{
		int j = 0;
		for (int i = -1; ++i < 6; )
		{
			if (colwidth[i] > 7)
			{
				colwidth[i] -= colwidth[i]/8;
				j += colwidth[i]/8;
			}
			else if (!colwidth[i] && j)
			{
				colwidth[i] += j/(7-i);
				j -= j/(7-i);
			}
		}
		colwidth[6] = j;
	}

	// Insert the columns 
	int headers[7] = {IDS_P4REVISION, IDS_P4FILE_NAME, IDS_CHANGELIST, IDS_DATE, IDS_P4USER, IDS_P4ACTION, IDS_DESCRIPTION};
	int width= GetSystemMetrics(SM_CXVSCROLL);
	int retval;
	LV_COLUMN lvCol;
	int subItem;
	for(subItem=0; subItem < 7; subItem++)
	{
		lvCol.mask= LVCF_FMT | LVCF_SUBITEM | LVCF_TEXT |LVCF_WIDTH;
		lvCol.fmt=LVCFMT_LEFT;
        CString header = LoadStringResource(headers[subItem]); 
		lvCol.pszText=const_cast<LPTSTR>((LPCTSTR)header);
		lvCol.iSubItem=subItem;
		if(subItem < 6)
		{
			lvCol.cx=colwidth[subItem];
			width+=lvCol.cx;
		}
		else
			lvCol.cx=rect.Width() - width - 4;  // expand last column to fill window
		retval=m_ListCtl.InsertColumn(subItem, &lvCol);
	}
 
	// Add the data
	CRevision *rev = 0;
	LV_ITEM lvItem;
	int iActualItem = -1;
	CString txt;
	BOOL bMultiFileNames = FALSE;
	int	fnbr= 0;
	for(int iItem=0; iItem < m_pHistory->GetRevisionCount(); iItem++)
	{
		if(iItem==0)
		{
			rev=m_pHistory->GetLatestRevision();
			lvItem.pszText= const_cast<LPTSTR>((LPCTSTR)txt); 
			txt=rev->m_ChangeDescription;
			if (txt.GetAt(0) == _T('('))
			{
				int i = txt.Find(_T(')'));
				if (i != -1)
				{
					txt.SetAt(0, _T(' '));
					m_RevFileType.SetWindowText(LoadStringResource(IDS_FILETYPE) + (LPCTSTR)txt.Left(i));
					txt = txt.Right(txt.GetLength() - i - 1);
					txt.TrimLeft();
				}
			}
			else m_RevFileType.SetWindowText(_T(""));
			m_RevisionInfo.SetWindowText((LPCTSTR)txt);
			txt=rev->m_ChangeType;
			txt.Replace(_T("; "), _T("\r\n"));
			m_RevisionAction.SetWindowText(txt);
			fnbr = rev->m_FNbr;
		}
		else
		{
			rev=m_pHistory->GetNextRevision();
			if (fnbr != rev->m_FNbr)
				bMultiFileNames = TRUE;
		}

		for(subItem=0; subItem < 7; subItem++)
		{
			lvItem.mask=LVIF_TEXT | 
					((subItem==0) ? LVIF_PARAM : 0);
			lvItem.iItem= (subItem==0) ? iItem : iActualItem;
            ASSERT(lvItem.iItem != -1);
			lvItem.iSubItem= subItem;
			lvItem.lParam=(LPARAM) rev;
			switch(subItem)
			{
			case 0:
				{
				int r = rev->m_RevisionNum;
				txt.Format(_T("%4d"), r);
				if (r == m_HaveRev && rev->m_FName == m_InitialName)
					txt += _T('*');
				lvItem.pszText= const_cast<LPTSTR>((LPCTSTR)txt); break;
				}
			case 1: lvItem.pszText= const_cast<LPTSTR>((LPCTSTR)rev->m_FName); break;
			case 2: 
				if (rev->m_ChangeNum >= 0)	// chg# == -1 is a flag to NOT display chg#
					 txt.Format(_T("%5d"),rev->m_ChangeNum);
				else txt.Empty();
				lvItem.pszText= const_cast<LPTSTR>((LPCTSTR)txt); break;
			case 3: lvItem.pszText= const_cast<LPTSTR>((LPCTSTR)rev->m_Date); break;
			case 4: lvItem.pszText= const_cast<LPTSTR>((LPCTSTR)rev->m_User); break;
			case 5: lvItem.pszText= const_cast<LPTSTR>((LPCTSTR)rev->m_ChangeType); break;
			case 6: txt=PadCRs(rev->m_ChangeDescription);
					if (txt.GetAt(0) == _T('('))
					{
						int i = txt.Find(_T(')'));
						if (i != -1)
						{
							txt = txt.Right(txt.GetLength() - i - 1);
							txt.TrimLeft();
						}
					}
					lvItem.pszText= const_cast<LPTSTR>((LPCTSTR)txt);
					break;
			}
			
			if(subItem==0)
				iActualItem=m_ListCtl.InsertItem(&lvItem);
			else
				m_ListCtl.SetItem(&lvItem);
		}
	}
	// remember the latest name because that's the only
	// name we can use to diff a revision against the client
	m_LatestName = rev->m_FName;
	m_LatestIsDeleted = rev->m_ChangeType.Find(_T("delete")) == 0;
	
	// Sort the list
	m_ListCtl.Sort( m_LastSortColumn = bMultiFileNames ? 1 : 0, m_SortAscending );
	
	// Make sure desired item is selected
	int iPos = 0;
	if (m_InitialRev != -1)
	{
		TCHAR initrev[16];
		_stprintf(initrev, _T("%4d"), m_InitialRev);
		LVFINDINFO lvfindinfo;
		lvfindinfo.flags = LVFI_STRING;
		lvfindinfo.psz = initrev;
		lvfindinfo.lParam = 0;
		lvfindinfo.pt.x = 0;
		lvfindinfo.pt.y = 0;
		lvfindinfo.vkDirection = VK_DOWN;
		iPos = m_ListCtl.FindItem( &lvfindinfo, -1 );
		if (iPos < 0)
			iPos = 0;
		else
		{
			rev= (CRevision *) m_ListCtl.GetItemData(iPos);
			if (rev->m_FName != m_InitialName)	// != means duplicate rev#s - gotta check names
			{
				int cnt = m_ListCtl.GetItemCount();
				for (iPos = -1; ++iPos < cnt; )
				{
					rev= (CRevision *) m_ListCtl.GetItemData(iPos);
					if ((rev->m_RevisionNum == m_InitialRev)
					 && (rev->m_FName == m_InitialName))
						break;
				}
				if (iPos >= m_ListCtl.GetItemCount())
					iPos = 0;
			}
		}
	}
	m_ListCtl.SetItemState(iPos, LVIS_SELECTED|LVIS_FOCUSED, LVIS_SELECTED|LVIS_FOCUSED);
	if (iPos)
		m_ListCtl.EnsureVisible(iPos, FALSE);

	// Set change description to match selected item in list
	rev= (CRevision *) m_ListCtl.GetItemData(iPos);
	txt= rev->m_ChangeDescription;
	if (txt.GetAt(0) == _T('('))
	{
		int i = txt.Find(_T(')'));
		if (i != -1)
		{
			txt.SetAt(0, _T(' '));
			m_RevFileType.SetWindowText(LoadStringResource(IDS_FILETYPE) + (LPCTSTR)txt.Left(i));
			txt = txt.Right(txt.GetLength() - i - 1);
			txt.TrimLeft();
		}
	}
	else m_RevFileType.SetWindowText(_T(""));
	m_RevisionInfo.SetWindowText(txt);
	txt=rev->m_ChangeType;
	txt.Replace(_T("; "), _T("\r\n"));
	m_RevisionAction.SetWindowText(txt);

	m_RevHistCount = m_ListCtl.GetItemCount();
	// if we are showing all records for this file 
	// or this is being called from an in-progress command (such as p4 resolve)
	// remove the More button.
	if (!TheApp()->m_RevHistLast || TheApp()->m_RevHistLast > m_RevHistCount || m_Key)
	{
		GetDlgItem(IDC_MORE)->EnableWindow(FALSE);
		GetDlgItem(IDC_MORE)->ShowWindow(SW_HIDE);
	}

	// And finally, set focus to the list control so that the first 'down'
	// keystroke can be used to scroll down
	m_ListCtl.SetFocus();

	EnableButtons();

	m_InitiallyNotBusy = !SERVER_BUSY();
	bReady = TRUE;
	ShowWindow(SW_SHOW);
	return TRUE;
}

void CHistoryDlg::OnColumnclickRevlist(NMHDR* pNMHDR, LRESULT* pResult) 
{
	NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR;
	
	if(pNMListView->iSubItem != m_LastSortColumn)
		m_LastSortColumn=pNMListView->iSubItem;
	else
		m_SortAscending= !m_SortAscending;

	if(pNMListView->iItem == -1)
		m_ListCtl.Sort( m_LastSortColumn, m_SortAscending );
		
	POSITION pos = m_ListCtl.GetFirstSelectedItemPosition();
	if (pos != NULL)
		m_ListCtl.EnsureVisible(m_ListCtl.GetNextSelectedItem(pos), FALSE);

	*pResult = 0;
}

int CALLBACK ListSort(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
{
    CRevision const *rev1 = (CRevision const *)lParam1;
    CRevision const *rev2 = (CRevision const *)lParam2;
	CString txt1, txt2;
	int retval;

	switch(lParamSort)
	{
	case 0:  // rev Number
		retval=( rev1->m_RevisionNum - 
				 rev2->m_RevisionNum );
		break;
	case 1:	 // filename
		retval= 0 - ( rev1->m_FNbr - 
					  rev2->m_FNbr );
		if (!retval)
			 retval=( rev1->m_ChangeNum - 
					  rev2->m_ChangeNum );
		break;
	case 2:  // change Number
	case 3:	 // date
		retval=( rev1->m_ChangeNum - 
				 rev2->m_ChangeNum );
		break;
	case 4:	 // user
		txt1= rev2->m_User;
		txt2= rev1->m_User;
		retval= txt2.Compare(txt1);
		break;
	case 5:	 // action
		txt1= rev2->m_ChangeType;
		txt2= rev1->m_ChangeType;
		retval= txt2.Compare(txt1);
		break;
	case 6:	 // description
		txt1= rev2->m_ChangeDescription;
		txt2= rev1->m_ChangeDescription;
		retval= txt2.Compare(txt1);
		break;
	default:
		ASSERT(0);
		return 0;
	}
	if(pDlg->m_SortAscending)
		return retval;
	else
		return -retval;
}

void CHistoryDlg::OnItemchangedRevlist(NMHDR* pNMHDR, LRESULT* pResult) 
{
	NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR;
	CString txt, txt1;

	// Update display of revision description
	if(pNMListView->uNewState==3)
	{
		txt=((CRevision *) pNMListView->lParam)->m_ChangeDescription;
		if (txt.GetAt(0) == _T('('))
		{
			int i = txt.Find(_T(')'));
			if (i != -1)
			{
				txt.SetAt(0, _T(' '));
				m_RevFileType.SetWindowText(LoadStringResource(IDS_FILETYPE) + (LPCTSTR)txt.Left(i));
				txt = txt.Right(txt.GetLength() - i - 1);
				txt.TrimLeft();
			}
		}
		else m_RevFileType.SetWindowText(_T(""));
		m_RevisionInfo.SetWindowText(txt);
		txt=((CRevision *) pNMListView->lParam)->m_ChangeType;
		txt.Replace(_T("; "), _T("\r\n"));
		m_RevisionAction.SetWindowText(txt);
		txt.FormatMessage(IDS_ACTIONLABEL, ((CRevision *) pNMListView->lParam)->m_ChangeNum);
		GetDlgItem(IDC_ACTIONLABEL)->SetWindowText(txt);
		txt.FormatMessage(IDS_SUMMARYLABEL, ((CRevision *) pNMListView->lParam)->m_ChangeNum);
		GetDlgItem(IDC_SUMMARYLABEL)->SetWindowText(txt);
	}

	EnableButtons();
	
	*pResult = 0;
}


void CHistoryDlg::EnableButtons()
{
	if(m_Busy)
	{
		EnableCtrl(	GetDlgItem(IDB_BROWSE), FALSE );
		EnableCtrl( GetDlgItem(IDB_GET), FALSE );
		EnableCtrl( GetDlgItem(IDB_DIFFVSCLIENT), FALSE );
		EnableCtrl( GetDlgItem(IDB_DIFFREVISIONS), FALSE );
		EnableCtrl( GetDlgItem(ID_CHANGE_DESCRIBE), FALSE );
		EnableCtrl( GetDlgItem(ID_FILE_RECOVER), FALSE );
		EnableCtrl( GetDlgItem(IDOK), FALSE );
	}
	else
	{
		BOOL bDeleted  = FALSE;
		CRevision *rev = 0;
		EnableCtrl( GetDlgItem(IDOK), TRUE );

		switch(m_ListCtl.GetSelectedCount())
		{
		case 1:
			for(int i= 0; i < m_ListCtl.GetItemCount(); i++)
			{
				if(m_ListCtl.GetItemState(i, LVIS_SELECTED) == LVIS_SELECTED)
				{
					rev= (CRevision *) m_ListCtl.GetItemData(i);
					break;
				}
			}
			ASSERT(rev);
			bDeleted = (rev->m_ChangeType.Find(_T("delete")) == 0) 
					|| (rev->m_ChangeType.Find(_T("purge")) == 0);
			EnableCtrl(	GetDlgItem(IDB_BROWSE), !bDeleted );
			EnableCtrl( GetDlgItem(IDB_GET), !m_CompleteHist || rev->m_FName == m_LatestName );
			EnableCtrl( GetDlgItem(IDB_DIFFVSCLIENT), m_HaveRev && !bDeleted );
			EnableCtrl( GetDlgItem(IDB_DIFFREVISIONS), FALSE );
			EnableCtrl( GetDlgItem(ID_CHANGE_DESCRIBE), 
				!((CDepotTreeCtrl *)m_pParent)->IsInRemoteDepot(&(rev->m_FName)) );
			EnableCtrl( GetDlgItem(ID_FILE_RECOVER), m_LatestIsDeleted && !bDeleted);
			break;
		case 2:
		  {
			CRevision *rev1 = 0;
			CRevision *rev2 = 0;
			for(int i= 0; i < m_ListCtl.GetItemCount(); i++)
			{
				if(m_ListCtl.GetItemState(i, LVIS_SELECTED) == LVIS_SELECTED)
				{
					if (!rev1)
					{
						rev1= (CRevision *) m_ListCtl.GetItemData(i);
					}
					else
					{
						rev2= (CRevision *) m_ListCtl.GetItemData(i);
						break;
					}
				}
			}
			ASSERT(rev1);
			ASSERT(rev2);
			EnableCtrl(	GetDlgItem(IDB_BROWSE), FALSE );
			EnableCtrl( GetDlgItem(IDB_GET), FALSE );
			EnableCtrl( GetDlgItem(IDB_DIFFVSCLIENT), FALSE );
			EnableCtrl( GetDlgItem(IDB_DIFFREVISIONS), 
					   rev1->m_ChangeType.Find(_T("delete")) != 0 
					&& rev1->m_ChangeType.Find(_T("purge")) != 0 
					&& rev2->m_ChangeType.Find(_T("delete")) != 0
					&& rev2->m_ChangeType.Find(_T("purge")) != 0 );
			EnableCtrl( GetDlgItem(ID_CHANGE_DESCRIBE), FALSE );
			EnableCtrl( GetDlgItem(ID_FILE_RECOVER), FALSE );
			break;
		  }
		default:
			EnableCtrl(	GetDlgItem(IDB_BROWSE), FALSE );
			EnableCtrl( GetDlgItem(IDB_GET), FALSE );
			EnableCtrl( GetDlgItem(IDB_DIFFVSCLIENT), FALSE );
			EnableCtrl( GetDlgItem(IDB_DIFFREVISIONS), FALSE );
			EnableCtrl( GetDlgItem(ID_CHANGE_DESCRIBE), FALSE );
			EnableCtrl( GetDlgItem(ID_FILE_RECOVER), FALSE );
		}
	}
}

void CHistoryDlg::EnableCtrl( CWnd *ctl, BOOL enable )
{
	// Avoid flicker by only calling EnableWindow as reqd
	if(enable != ctl->IsWindowEnabled())
		ctl->EnableWindow(enable);
}

void CHistoryDlg::OnCompletehist() 
{
	OnOK();
}

/////////////////////////////////////////////////////////////////////////////
// CHistList

CHistList::CHistList()
{
}

CHistList::~CHistList()
{
}


BEGIN_MESSAGE_MAP(CHistList, CListCtrl)
	//{{AFX_MSG_MAP(CHistList)
	ON_WM_RBUTTONUP()
	ON_WM_RBUTTONDOWN()
	ON_WM_GETMINMAXINFO()
	ON_WM_WINDOWPOSCHANGING()
	ON_WM_SIZE()
	ON_WM_LBUTTONDBLCLK()
	//}}AFX_MSG_MAP
	ON_MESSAGE( WM_FINDPATTERN, OnFindPattern )
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CHistList message handlers


void CHistList::OnLButtonDblClk(UINT nFlags, CPoint point) 
{
	CListCtrl::OnLButtonDblClk(nFlags, point);
	((CHistoryDlg *)GetParent())->FileQuickbrowse(TRUE);
}

void CHistList::OnRButtonUp(UINT nFlags, CPoint local) 
{
	// find out what was hit
	LV_HITTESTINFO ht;
	ht.pt=local;
	int index= HitTest(&ht);

	if(index == -1)
		return;

	CPoint screen=local;
	ClientToScreen(&screen);
	((CHistoryDlg *)GetParent())->OnContextMenu(screen, index);
}


void CHistList::OnRButtonDown(UINT nFlags, CPoint point) 
{
	
 // Just eat the event, so a dbl click not required	

}

void CHistList::Sort( int column, BOOL ascending )
{
	SortItems( ListSort, column );
	m_headerctrl.SetSortImage(column, ascending);
}

void CHistList::OnGetMinMaxInfo(MINMAXINFO FAR* lpMMI) 
{
	RECT rectW, rectB;
	((CHistoryDlg *)GetParent())->GetWindowRect(&rectW);
	((CHistoryDlg *)GetParent())->GetDlgItem(IDOK)->GetWindowRect(&rectB);
	lpMMI->ptMinTrackSize.x = lpMMI->ptMaxSize.x + lpMMI->ptMaxPosition.x * 2;
	lpMMI->ptMinTrackSize.y = ((CHistoryDlg *)GetParent())->GetListTop() + 20;
	lpMMI->ptMaxTrackSize.y = (rectW.bottom - rectW.top) - (rectB.bottom - rectB.top) * 7;
	CListCtrl::OnGetMinMaxInfo(lpMMI);
}

void CHistList::OnWindowPosChanging(WINDOWPOS FAR* lpwndpos) 
{
	if (bReady)
		lpwndpos->flags |= SWP_NOMOVE;
	CListCtrl::OnWindowPosChanging(lpwndpos);
}

void CHistList::OnSize(UINT nType, int cx, int cy) 
{
	CListCtrl::OnSize(nType, cx, cy);
	((CHistoryDlg *)GetParent())->RePaintMiddleWindows();
}

LRESULT CHistList::OnFindPattern(WPARAM wParam, LPARAM lParam)
{
	TCHAR str[ 1024 ];
	CString text;
	CString what = (TCHAR *)lParam;
	int flags = (int)wParam;
	int cnt = GetItemCount();
	int prv = -1;

	if (!(flags & FR_MATCHCASE))
		what.MakeLower();
	int i = -1;
	for( int k = 0;  k < cnt; k++ )
	{
		if( GetItemState( k, LVIS_SELECTED ) == LVIS_SELECTED )
		{
			SetItemState( k, 0, LVIS_SELECTED|LVIS_FOCUSED );
			prv = i = k;
		};
	}
	int j;
	if (i == -1)
		i = 0;
	int columns = GetHeaderCtrl()->GetItemCount();
	if (flags & FR_DOWN)
	{
		if (flags < 0)
			i++;
		while( i < cnt )
		{
			for (j =-1; ++j < columns; )
			{
				GetItemText( i, j, str, sizeof(str)/sizeof(TCHAR) );
				text = str;
				if (!(flags & FR_MATCHCASE))
					text.MakeLower();
				if (text.Find(what) != -1)
				{
					SetItemState( i, LVIS_SELECTED|LVIS_FOCUSED, LVIS_SELECTED|LVIS_FOCUSED );
					EnsureVisible(i, FALSE);
					MainFrame()->SetMessageText(LoadStringResource(IDS_FOUND));
					return 0;
				}
			}
			i++;
		}
	}
	else
	{
		if (flags < 0)
			i--;
		while( i >= 0 )
		{
			for (j =-1; ++j < columns; )
			{
				GetItemText( i, j, str, sizeof(str)/sizeof(TCHAR) );
				text = str;
				if (!(flags & FR_MATCHCASE))
					text.MakeLower();
				if (text.Find(what) != -1)
				{
					SetItemState( i, LVIS_SELECTED|LVIS_FOCUSED, LVIS_SELECTED|LVIS_FOCUSED );
					EnsureVisible(i, FALSE);
					MainFrame()->SetMessageText(LoadStringResource(IDS_FOUND));
					return 0;
				}
			}
			i--;
		}
	}
	MessageBeep(0);
	MainFrame()->SetMessageText(LoadStringResource(IDS_NOT_FOUND));
	if (prv != -1)
		SetItemState( prv, LVIS_SELECTED|LVIS_FOCUSED, LVIS_SELECTED|LVIS_FOCUSED );
	return 0;
}

//
/////////////////////////////////////////////////////////////////////////////

void CHistoryDlg::OnUpdatePositionToPattern(CCmdUI* pCmdUI) 
{
	CString txt = LoadStringResource(IDS_FINDPATTERN);
	pCmdUI->SetText( txt );
	pCmdUI->Enable( !m_Busy && NULL == m_pFRDlg );
}

void CHistoryDlg::OnPositionToPattern() 
{
	if ( NULL == m_pFRDlg )
	{
		m_pFRDlg = new CFindReplaceDialog();  // Must be created on the heap

		m_pFRDlg->m_fr.lStructSize = sizeof(FINDREPLACE);
		m_pFRDlg->m_fr.hwndOwner = this->m_hWnd;
		m_pFRDlg->Create( TRUE, m_FindWhatStr, _T(""), m_FindWhatFlags | FR_HIDEWHOLEWORD, this ); 
	}
}

void CHistoryDlg::OnUpdatePositionToNext(CCmdUI* pCmdUI) 
{
	pCmdUI->Enable(!m_Busy);
}

void CHistoryDlg::OnPositionToNext() 
{
	if (!m_FindWhatStr.IsEmpty())
	{
		::PostMessage(m_ListCtl.m_hWnd, WM_FINDPATTERN, 
				(WPARAM)m_FindWhatFlags | 0x80000000, (LPARAM)m_FindWhatStr.GetBuffer(0));
	}
}

void CHistoryDlg::OnUpdatePositionToPrev(CCmdUI* pCmdUI) 
{
	pCmdUI->Enable(!m_Busy);
}

void CHistoryDlg::OnPositionToPrev() 
{
	if (!m_FindWhatStr.IsEmpty())
	{
		::PostMessage(m_ListCtl.m_hWnd, WM_FINDPATTERN, 
				((WPARAM)m_FindWhatFlags | 0x80000000) ^ FR_DOWN, 
				(LPARAM)m_FindWhatStr.GetBuffer(0));
	}
}

LRESULT CHistoryDlg::OnFindReplace(WPARAM wParam, LPARAM lParam)
{
	LPFINDREPLACE lpfp = (LPFINDREPLACE)lParam;
	if (m_pFRDlg->FindNext() || m_pFRDlg->IsTerminating())
	{
		m_FindWhatStr = lpfp->lpstrFindWhat;
		m_FindWhatFlags = lpfp->Flags;
		if (m_pFRDlg->FindNext())
		{
			::PostMessage(m_ListCtl.m_hWnd, WM_FINDPATTERN, 
				(WPARAM)(lpfp->Flags), (LPARAM)m_FindWhatStr.GetBuffer(0));
			delete m_pFRDlg;
		}
		m_pFRDlg = NULL;
	}
	return 0;
}

void CHistoryDlg::RePaintMiddleWindows()
{
	CWnd *pWin=GetDlgItem(IDC_ACTIONLABEL);
	pWin->InvalidateRect( NULL, TRUE );
	pWin=GetDlgItem(IDC_COMPLETEHIST);
	pWin->InvalidateRect( NULL, TRUE );
#ifdef	FILELOGdashH
	if (GET_SERVERLEVEL() >= 24)		// 2007.3 or later?
	{
		pWin=GetDlgItem(IDC_STATIC_1);
		pWin->InvalidateRect( NULL, TRUE );
		pWin=GetDlgItem(IDC_RADIO1);
		pWin->InvalidateRect( NULL, TRUE );
		pWin=GetDlgItem(IDC_RADIO2);
		pWin->InvalidateRect( NULL, TRUE );
		pWin=GetDlgItem(IDC_RADIO3);
		pWin->InvalidateRect( NULL, TRUE );
	}
#endif
}

void CHistoryDlg::OnSize(UINT nType, int cx, int cy) 
{
	CRect rect;

	CDialog::OnSize(nType, cx, cy);
	if (nType == SIZE_MINIMIZED)
	{
		m_IsMinimized = TRUE;
		return;
	}
	else if (m_IsMinimized)
	{
		m_IsMinimized = FALSE;
		return;
	}

	GetClientRect(&rect);
	int x=rect.Width();
	int y=rect.Height();

	// find the bottom of the buttons
	CWnd *pBtn=GetDlgItem(IDB_GET);
	if (!pBtn)
		return;
	pBtn->GetWindowRect(&rect);
	ScreenToClient(&rect);
	int bottomButtons= rect.bottom;

	// Increase the size of the list below buttons
	if (!m_listHeight)
		 m_listHeight = y*4/10;
	else if ((m_dlgHeight != y) && m_dlgHeight)
		m_listHeight = max(m_lorgHeight, 
						   (m_listHeight + (y > m_dlgHeight ? 1 : 0)) * y/m_dlgHeight);
	m_dlgHeight = y;
	CWnd *pList=GetDlgItem(IDC_REVISIONLIST);
	pList->MoveWindow(0, m_listTop = bottomButtons+4, x, m_listHeight, TRUE);
	pList->GetWindowRect(&rect);
	m_listHeight = rect.Height();

	SizeBottonOfHistory(x, y);

	InvalidateRect(NULL, TRUE);
}

void CHistoryDlg::SizeBottonOfHistory(int x, int y)
{
	int bottomList= m_listTop + m_listHeight - 1;
	CRect rect;
	int h;
	int w;

	// Position the rev file type text box
	CWnd *pText=GetDlgItem(IDC_REVFILETYPE);
	pText->GetWindowRect(&rect);
	pText->ScreenToClient(&rect);
	pText->MoveWindow(x - rect.Width() - 6, bottomList + rect.Height()/2 - 2, 
						  rect.Width(), rect.Height(), TRUE);

	// Position the action label
	CWnd *pLabel=GetDlgItem(IDC_ACTIONLABEL);
	pLabel->GetClientRect(&rect);
	pLabel->MoveWindow(4, bottomList+rect.Height()/2, rect.Width(), rect.Height(), TRUE);
	int bottomLabel= bottomList + rect.Height()*3/2;

	// Calculate some heights
	CWnd *pBtn=GetDlgItem(IDOK);
	pBtn->GetWindowRect(&rect);
	int btnWidth = rect.Width();
	int btnHeight= rect.Height();
	h = (y-bottomLabel)/2 - btnHeight - 6;

	// Position the action text box
	pText=GetDlgItem(IDC_REVISIONACTION);
	pText->MoveWindow(2, bottomLabel+4, x-4, h, TRUE);
	bottomList = bottomLabel+4 + h;

	// Position the description label
	pLabel=GetDlgItem(IDC_SUMMARYLABEL);
	pLabel->GetClientRect(&rect);
	pLabel->MoveWindow(4, bottomList+rect.Height()/2, x, rect.Height(), TRUE);
	bottomLabel= bottomList + rect.Height()*3/2;

	// Position the description text box
	pText=GetDlgItem(IDC_REVISIONINFO);
	pText->MoveWindow(2, bottomLabel+4, x-4, h, TRUE);
	int bottomDesc = bottomLabel+4 + h + 4;

	// Position the complete history checkbox
	pLabel=GetDlgItem(IDC_COMPLETEHIST);
	pLabel->GetClientRect(&rect);
	pLabel->MoveWindow(4, bottomDesc, rect.Width(), rect.Height(), TRUE);
	if (GET_SERVERLEVEL() < 8)
		pLabel->ShowWindow(SW_HIDE);

	// Position the branching history radio buttons
	pLabel=GetDlgItem(IDC_STATIC_1);
	pLabel->GetClientRect(&rect);
	pLabel->MoveWindow(4, bottomDesc, w = rect.Width(), rect.Height(), TRUE);
	if (GET_SERVERLEVEL() < 24)	// 2007.3 or later?
		pLabel->ShowWindow(SW_HIDE);

	pBtn=GetDlgItem(IDC_RADIO1);
	pBtn->GetClientRect(&rect);
	pBtn->MoveWindow(4 + w + 4, bottomDesc, rect.Width(), rect.Height(), TRUE);
	if (GET_SERVERLEVEL() < 24)	// 2007.3 or later?
		pBtn->ShowWindow(SW_HIDE);
	w += rect.Width();

	pBtn=GetDlgItem(IDC_RADIO2);
	pBtn->GetClientRect(&rect);
	pBtn->MoveWindow(4 + w + 8, bottomDesc, rect.Width(), rect.Height(), TRUE);
	if (GET_SERVERLEVEL() < 24)	// 2007.3 or later?
		pBtn->ShowWindow(SW_HIDE);
	w += rect.Width();

	pBtn=GetDlgItem(IDC_RADIO3);
	pBtn->GetClientRect(&rect);
	pBtn->MoveWindow(4 + w + 12, bottomDesc, rect.Width(), rect.Height(), TRUE);
	if (GET_SERVERLEVEL() < 24)	// 2007.3 or later?
		pBtn->ShowWindow(SW_HIDE);
	w += rect.Width();

	// Position the More, Close and Help buttons
	pBtn=GetDlgItem(IDHELP);
	pBtn->GetWindowRect(&rect);
	ScreenToClient(&rect);
	x = x - btnWidth - 5;
	pBtn->SetWindowPos(NULL, x, bottomDesc, btnWidth, rect.Height(), SWP_NOZORDER);

	pBtn=GetDlgItem(IDOK);
	pBtn->SetWindowPos(NULL, x-btnWidth-5, bottomDesc, btnWidth, rect.Height(), SWP_NOZORDER);

	pBtn=GetDlgItem(IDC_MORE);
	pBtn->SetWindowPos(NULL, x-(btnWidth*2)-10, bottomDesc, btnWidth, rect.Height(), SWP_NOZORDER);

	m_SizeSet = TRUE;
}

BOOL CHistoryDlg::OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult) 
{
	if ((wParam == IDC_REVISIONLIST) && m_SizeSet)
	{
		CRect rect;
		CWnd *pList=GetDlgItem(IDC_REVISIONLIST);
		pList->GetWindowRect(&rect);
		if (m_listHeight != rect.Height())
		{
			m_listHeight = rect.Height();
			GetClientRect(&rect);
			int x=rect.Width();
			int y=rect.Height();
			SizeBottonOfHistory(x, y);
		}
	}
	return CDialog::OnNotify(wParam, lParam, pResult);
}

void CHistoryDlg::OnGetMinMaxInfo(MINMAXINFO FAR* lpMMI) 
{
	lpMMI->ptMinTrackSize.x= m_InitRect.Width();
	lpMMI->ptMinTrackSize.y= m_InitRect.Height();
}

void CHistList::PreSubclassWindow() 
{
	CListCtrl::PreSubclassWindow();

	// Add initialization code
	m_headerctrl.SubclassWindow( ::GetDlgItem(m_hWnd,0) );
}


void CHistoryDlg::OnOK() 
{
	UpdateData(TRUE);
#ifdef	FILELOGdashH
	m_CompleteHist = GET_SERVERLEVEL() >= 24 ? m_CompleteHistIC : m_CompleteHistI;	// 2007.3 or later?
#else
	m_CompleteHist = m_CompleteHistI;
#endif
	m_SizeSet = FALSE;
	m_WinPos.SaveWindowPosition();
	SaveColumnWidths();
	if(!GET_P4REGPTR()->SetHistListHeight( m_listHeight ) )
		AfxMessageBox( IDS_BAD_REGISTRY,  MB_ICONSTOP );
	if (m_pParent)
		m_pParent->PostMessage(WM_P4ENDHISTORY, 0, (LPARAM)this);
	CDialog::OnOK();
}

void CHistoryDlg::SaveColumnWidths() 
{
	// Save the column widths
	CString str;

	for(int i=0; i < 10; i++)
	{
		// Note that GetColumnWidth returns zero if i > numcols
    	CString num;
		num.Format(_T("%d"), m_ListCtl.GetColumnWidth(i));
		if(i)
			str+=_T(",");
		str+=num;
	}
	
    CRegKeyEx key;
    if(ERROR_SUCCESS == key.Create(HKEY_CURRENT_USER, sRegKey))
        key.SetValueString(str, sRegValue_ColumnWidths);
}

// Check the registry to see if we have recorded the column widths last
// used for this list view
void CHistoryDlg::RestoreSavedWidths(int *width, int numcols)
{
    CRegKeyEx key;
    if(ERROR_SUCCESS == key.Open(HKEY_CURRENT_USER, sRegKey, KEY_READ))
    {
        CString result = key.QueryValueString(sRegValue_ColumnWidths);
        if(!result.IsEmpty())
        {
			//		things can go wrong with the registry setting of the 
			//		widths. Use the defaults if the entry is all zeroes.
			//
			if ( result != _T("0,0,0,0,0,0,0,0,0,0") )
				for(int i=0; i< numcols; i++)
					width[i]= GetPositiveNumber(result);
        }
    }
}

void CHistoryDlg::OnHelp() 
{
	GotoDlgCtrl(GetDlgItem(IDC_REVISIONLIST));
	AfxGetApp()->WinHelp(TASK_DISPLAYING_REVISION_HISTORY);
}

BOOL CHistoryDlg::OnHelpInfo(HELPINFO* pHelpInfo) 
{
	OnHelp();
	return(FALSE);
}

void CHistoryDlg::OnMore() 
{
	m_More = m_Rerun = TRUE;
	OnOK();
}

void CHistoryDlg::OnPositionDepot()
{
	if(m_ListCtl.GetSelectedCount() != 1)
		{ ASSERT(0); return; }

	for(int i= 0; i < m_ListCtl.GetItemCount(); i++)
	{
		if(m_ListCtl.GetItemState(i, LVIS_SELECTED) == LVIS_SELECTED)
		{
			CRevision *rev= (CRevision *) m_ListCtl.GetItemData(i);
			CString itemStr = rev->m_FName;
			if ((i = itemStr.Find(_T('#'))) != -1)
				itemStr = itemStr.Left( i );  // trim off rev# info
			else if ((i = itemStr.Find(_T("/..."))) != -1)
				itemStr = itemStr.Left( i );  // trim off "/..."
			((CMainFrame *) AfxGetMainWnd())->ExpandDepotString( itemStr, TRUE );
			break;
		}
	}
}

void CHistoryDlg::OnCopyRows() 
{
	CString txt;
	if(!m_ListCtl.GetSelectedCount())
		{ ASSERT(0); return; }

	CRevision *rev;

	// Get the rev
	for(int i= 0; i < m_ListCtl.GetItemCount(); i++)
	{
		if(m_ListCtl.GetItemState(i, LVIS_SELECTED) == LVIS_SELECTED)
		{
			rev= (CRevision *) m_ListCtl.GetItemData(i);
			CString item;
			item.Format(_T("%d\t%s\t%d\t%s\t%s\t%s\t%s\r\n"), rev->m_RevisionNum,
							rev->m_FName, rev->m_ChangeNum,  rev->m_Date,
							rev->m_User,  rev->m_ChangeType, PadCRs(rev->m_ChangeDescription));
			txt += item;
		}
	}

	CopyTextToClipboard(txt);
}

void CHistoryDlg::OnKeydownRevisionlist(NMHDR* pNMHDR, LRESULT* pResult) 
{
	LV_KEYDOWN* pLVKeyDow = (LV_KEYDOWN*)pNMHDR;
	if ((pLVKeyDow->wVKey == _T('C')) && ( ::GetKeyState(VK_CONTROL) & 0x8000 ))
		OnCopyRows();
	else if ((pLVKeyDow->wVKey == _T('F')) && ( ::GetKeyState(VK_CONTROL) & 0x8000 ))
		OnPositionToPattern();
	else if ((pLVKeyDow->wVKey == VK_F3) && ( !(::GetKeyState(VK_SHIFT) & 0x8000 )))
		OnPositionToNext();
	else if ((pLVKeyDow->wVKey == VK_F3) && ( ::GetKeyState(VK_SHIFT) & 0x8000 ))
		OnPositionToPrev();
	else if ((pLVKeyDow->wVKey == _T('T')) && ( ::GetKeyState(VK_CONTROL) & 0x8000 ))
		OnPerforceOptions();
	*pResult = 0;
}

void CHistoryDlg::OnPerforceOptions()
{
	if (m_Key)
	{
		MessageBeep(0);
		return;
	}

	BOOL allState = GET_P4REGPTR()->GetFetchAllHist();
	int  histCount = GET_P4REGPTR()->GetFetchHistCount();
	BOOL enableRevHistShowIntegs = GET_P4REGPTR()->GetEnableRevHistShowIntegs( );

	MainFrame()->OnPerforceOptions(TRUE, FALSE, 0, IDS_PAGE_REVISIONHISTORY);

	if (enableRevHistShowIntegs != GET_P4REGPTR()->GetEnableRevHistShowIntegs( )
	 || histCount != GET_P4REGPTR()->GetFetchHistCount()
	 || allState != GET_P4REGPTR()->GetFetchAllHist())
	{
		m_Rerun = TRUE;
		OnOK();
	}
}

// This signals the closing of a modeless dialog
// to MainFrame which will delete the 'this' object
void CHistoryDlg::OnDestroy()
{
	if (m_pParent)
		::PostMessage(MainFrame()->m_hWnd, WM_P4DLGDESTROY, 0, (LPARAM)this);
}

void CHistoryDlg::OnSysCommand(UINT nID, LPARAM lParam) 
{
	switch(nID)
	{
	case SC_MINIMIZE:
		GetDesktopWindow()->ArrangeIconicWindows();
		break;
	}

	CDialog::OnSysCommand(nID, lParam);
}