HistoryDlg.cpp. #1

  • //
  • guest/
  • YourUncleBob/
  • p4win/
  • main/
  • gui/
  • HistoryDlg.cpp.
  • View
  • Commits
  • Open Download .zip Download (63 KB)
//
// 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);
}
# Change User Description Committed
#1 19924 YourUncleBob Populate -o //guest/perforce_software/p4win/...
//guest/YourUncleBob/p4win/.....
//guest/perforce_software/p4win/main/gui/HistoryDlg.cpp
#1 16169 perforce_software Move files to follow new path scheme for branches.
//guest/perforce_software/p4win/gui/HistoryDlg.cpp
#1 8562 Matt Attaway These feet never stop running.

Initial commit of the P4Win source code.  To the best of our knowledge this
compiles and runs using the 2013.3 P4 API and VS 2010. Expect a few changes
as we refine the build process. Please post any build issues to the forums.