JobListDlg.cpp. #1

  • //
  • guest/
  • YourUncleBob/
  • p4win/
  • main/
  • gui/
  • JobListDlg.cpp.
  • View
  • Commits
  • Open Download .zip Download (17 KB)
// JobListDlg.cpp : implementation file
//

#include "stdafx.h"
#include "p4win.h"
#include "JobListDlg.h"
#include "JobView.h"
#include "P4Job.h"
#include "catchalldlg.h"
#include "MainFrm.h"
#include "StringUtil.h"
#include "RegKeyEx.h"
#include "hlp\p4win.hh"
#include "ImageList.h"

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


static LPCTSTR sRegKey = _T("Software\\Perforce\\P4Win\\Layout\\Job List");
static LPCTSTR sRegValue_ColumnWidths = _T("Column Widths");
static LPCTSTR sRegValue_SortColumns = _T("Sort Columns");

#define UPDATE_STATUS(x) ((CMainFrame *)AfxGetMainWnd())->UpdateStatus(x)
int CALLBACK JobListDlgSort(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort);
#define IMG_INDEX(x) (x-IDB_PERFORCE)


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

/////////////////////////////////////////////////////////////////////////////
// CJobListDlg dialog


CJobListDlg::CJobListDlg(CWnd* pParent /*=NULL*/)
	: CDialog(CJobListDlg::IDD, pParent)
{
	//{{AFX_DATA_INIT(CJobListDlg)
	//}}AFX_DATA_INIT
	m_SortAscending=FALSE;
	m_LastSortColumn=0;
	pDlg=this;
	m_InitRect.SetRect(0,0,100,100);
	m_WinPos.SetWindow( this, _T("JobListDlg") );
	for (int i = -1; ++i < MAX_SORT_COLUMNS; )
		m_SortColumns[i] = 0;
}


void CJobListDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
	//{{AFX_DATA_MAP(CJobListDlg)
	DDX_Control(pDX, IDC_JOBSTATUS, m_JobStatus);
	DDX_Control(pDX, IDC_JOBLIST, m_JobListCtrl);
	DDX_Control(pDX, IDC_JOBDESC, m_JobDesc);
	//}}AFX_DATA_MAP
}


BEGIN_MESSAGE_MAP(CJobListDlg, CDialog)
	//{{AFX_MSG_MAP(CJobListDlg)
	ON_WM_SIZE()
	ON_WM_GETMINMAXINFO()
	ON_NOTIFY(LVN_ITEMCHANGED, IDC_JOBLIST, OnItemchangedJoblist)
	ON_NOTIFY(LVN_COLUMNCLICK, IDC_JOBLIST, OnColumnclickJoblist)
	ON_NOTIFY(NM_DBLCLK, IDC_JOBLIST, OnDblclickJoblist)
	ON_BN_CLICKED(IDHELP, OnHelp)
	ON_BN_CLICKED(IDC_FILTER, OnJobFilter)
	ON_BN_CLICKED(ID_CLEARFILTER, OnClearFilter)
	ON_WM_HELPINFO()
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CJobListDlg message handlers

BOOL CJobListDlg::OnInitDialog() 
{
	CDialog::OnInitDialog();
	CP4Job *job;
	CString str;

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

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

	// Set the imagelist
	m_JobListCtrl.SetImageList(TheApp()->GetImageList(), LVSIL_SMALL);

	// Get original size of control
	CRect rect;
	m_JobListCtrl.GetWindowRect(&rect);

	int colwidth[MAX_JOBS_COLUMNS]={90,80,60,90,200};

	// Record the initial window size, and then see if there is a registry preference
	GetWindowRect(&m_InitRect);
	m_WinPos.RestoreWindowPosition();
	// make sure OnSize gets called to reposition controls
	// if restored position is default, this won't happen unless
	// we force it
	GetClientRect(&rect);
	SendMessage(WM_SIZE, 0, MAKELONG(rect.Width(), rect.Height()));

	// Get new size of control after resized as specified in the registry
	m_JobListCtrl.GetWindowRect(&rect);

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

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

	// Show the current job filter
	CString txt;
	m_sFilter = PersistentJobFilter(KEY_READ);
	if(m_sFilter.GetLength() > 0)
	{
		txt.FormatMessage(IDS_JOBFILTERSTR, m_sFilter);
	}
	else
	{
		txt = LoadStringResource(IDS_ALLJOBS);
		GetDlgItem(ID_CLEARFILTER)->EnableWindow(FALSE);
	}
	GetDlgItem(IDC_FILTER_TEXT)->SetWindowText(txt);

	// Use the same font as the calling window
	m_JobListCtrl.SetFont(m_Font);

	// Insert the columns 
	int maxcols = m_ColNames->GetSize();
	int width=GetSystemMetrics(SM_CXVSCROLL);;
	int retval;
	LV_COLUMN lvCol;
	int subItem;
	for(subItem=0; subItem < maxcols; subItem++)
	{
		lvCol.mask= LVCF_FMT | LVCF_SUBITEM | LVCF_TEXT |LVCF_WIDTH;
		lvCol.fmt=LVCFMT_LEFT;
		lvCol.pszText=( LPTSTR )( LPCTSTR ) m_ColNames->GetAt( subItem );
		lvCol.iSubItem=subItem;
		if(subItem < maxcols-1)
		{
			lvCol.cx=colwidth[subItem];
			width+=lvCol.cx;
		}
		else
			lvCol.cx=rect.Width() - width - 4;  // expand last column to fill window
		retval=m_JobListCtrl.InsertColumn(subItem, &lvCol);
	}
 
	// Add the data
	LV_ITEM lvItem;
	int iActualItem = -1;
	POSITION pos= m_pJobList->GetHeadPosition();
	int iItem;
	for(iItem=0; iItem < m_pJobList->GetCount(); iItem++)
	{
		job= (CP4Job *) m_pJobList->GetNext(pos);
		
		for(subItem=0; subItem < maxcols; subItem++)
		{
			lvItem.mask=LVIF_TEXT | 
					((subItem==0) ? LVIF_IMAGE : 0) |
					((subItem==0) ? LVIF_PARAM : 0);
			lvItem.iItem= (subItem==0) ? iItem : iActualItem;
            ASSERT(lvItem.iItem != -1);
			lvItem.iSubItem= subItem;
			lvItem.iImage = CP4ViewImageList::VI_JOB;
			lvItem.lParam=(LPARAM) job;
			txt=PadCRs(job->GetJobField(subItem));
			lvItem.pszText = const_cast<LPTSTR>( (LPCTSTR ) txt);

			if(subItem==0)
				iActualItem=m_JobListCtrl.InsertItem(&lvItem);
			else
				m_JobListCtrl.SetItem(&lvItem);
		}
	}

	if(m_pJobList->GetCount())
	{
		// Sort the jobs list the same way the JobView is sorted
		m_JobListCtrl.SortItems( JobListDlgSort, m_LastSortColumn );

		// Find the job currently selected in the jobs pane
		iItem=0;
		if (!m_CurrJob->IsEmpty())
		{
			TCHAR cur[ LISTVIEWNAMEBUFSIZE + 1 ];
			TCHAR str[ LISTVIEWNAMEBUFSIZE + 1 ];

			lstrcpy(cur, m_CurrJob->GetBuffer(m_CurrJob->GetLength()+1));
			m_CurrJob->ReleaseBuffer(-1);
			for( ; iItem < ListView_GetItemCount( m_JobListCtrl.m_hWnd ); iItem++ )
			{
				ListView_GetItemText( m_JobListCtrl.m_hWnd, iItem, 0, str, LISTVIEWNAMEBUFSIZE );
				if( !Compare(cur, str) )
					break;
			}
			if (iItem >= ListView_GetItemCount( m_JobListCtrl.m_hWnd ))
				iItem = 0;
		}

		// Make sure the same job is selected and visible
		m_JobListCtrl.SetItemState(iItem, LVIS_SELECTED|LVIS_FOCUSED, LVIS_SELECTED|LVIS_FOCUSED);
		m_JobListCtrl.EnsureVisible( iItem, FALSE );
		
		// Set change description to match selected item in list
		job= (CP4Job *) m_JobListCtrl.GetItemData(iItem);
		str= job->GetDescription();
		str.Replace(_T("\n"), _T("\r\n"));
		m_JobDesc.SetWindowText(str);
	}
	else if( m_sFilter.IsEmpty() )
	{
        AfxMessageBox(IDS_NO_JOBS_AVAILABLE_FOR_FIXING, MB_ICONEXCLAMATION);
		EndDialog(IDCANCEL);
	}
	else
		AddToStatus(LoadStringResource(IDS_USEJOBFILTERBUTTON), SV_MSG);

	if (!LoadJobStatusComboBox())
	{
		GetDlgItem(IDC_JOBSTATUSPROMPT)->ShowWindow( SW_HIDE );
		GetDlgItem(IDC_JOBSTATUS)->ShowWindow( SW_HIDE );
	}

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

	return TRUE;  // return TRUE unless you set the focus to a control
	              // EXCEPTION: OCX Property Pages should return FALSE
}

CString CJobListDlg::PersistentJobFilter( REGSAM accessmask )
{
	HKEY hKey = NULL;
	const CString sKey = _T("Software\\Perforce\\P4Win\\");
	const CString sEntry = _T("JobFilter");
	CString filter = _T("");
	DWORD disposition;

	if ( RegCreateKeyEx( HKEY_CURRENT_USER, sKey,
							0, NULL,
							REG_OPTION_NON_VOLATILE,
							accessmask, NULL,
							&hKey, &disposition ) == ERROR_SUCCESS )
	{
		const DWORD lenFilter = 512;
		if ( accessmask == KEY_WRITE )
		{
			RegSetValueEx( hKey, sEntry, NULL, REG_SZ
				, (LPBYTE)(LPCTSTR) m_sFilter, m_sFilter.GetLength( ) * sizeof(TCHAR) + 1);
		}
		else
		{
			TCHAR buf[ lenFilter ];
			DWORD cbData = sizeof(buf);
			if ( (RegQueryValueEx( hKey, sEntry, NULL, NULL, 
									(LPBYTE)buf, &cbData ) == ERROR_SUCCESS) && cbData )
			{
				if(!cbData)
					cbData = 1;
				buf[cbData-1] = _T('\0');
				filter = buf;
			}
		}
		RegCloseKey( hKey );
	}
	return filter;
}

BOOL CJobListDlg::LoadJobStatusComboBox()
{
	int i;
	m_JobStatus.AddString(_T("(default)"));
	if (GET_SERVERLEVEL() < 10)
		return FALSE;

	i = m_pJobSpec->Find(_T("\nFields:\n\t"));
	if (i == -1)
		return FALSE;
	i = m_pJobSpec->Find(_T("\n\t102 "), i);
	if (i == -1)
		return FALSE;
	i += sizeof(_T("\n\t102 "))/sizeof(TCHAR) - 1;
	int j = m_pJobSpec->Find(_T(' '), i);
	if (j == -1)
		return FALSE;
	CString name = m_pJobSpec->Mid(i, j-i);
	i = m_pJobSpec->Find(_T("\nValues:"), j);
	if (i == -1)
	{
		CString dashed = _T("\nValues");
		dashed += _T("-") + name;
		i = m_pJobSpec->Find(dashed, j);
		if (i == -1)
			return FALSE;
	}
	i = m_pJobSpec->Find(name, i);
	if (i == -1)
		return FALSE;
	i += name.GetLength();
	while (1)
	{
		TCHAR c = m_pJobSpec->GetAt(++i);
		if ((c != _T(':')) && (c != _T(' ')) && (c != _T('\t')))
			break;
	}
	j = m_pJobSpec->Find(_T('\n'), i);
	if (j == -1)
		return FALSE;
	CString values = m_pJobSpec->Mid(i, j-i);

	while((i = values.Find(_T('/'))) != -1)
	{
		CString value = values.Mid(0,i);
		m_JobStatus.AddString(value);
		values = values.Mid(i+1);
	}
	m_JobStatus.AddString(values);
	return TRUE;
}

void CJobListDlg::SetJobList(CObList *joblist)
{
	ASSERT_KINDOF(CObList,joblist);
	m_pJobList= joblist;
}

void CJobListDlg::SetJobSpec(CString *jobSpec)
{
	m_pJobSpec= jobSpec;
}

void CJobListDlg::SetJobCols(CStringArray *jobCols)
{
	m_ColNames= jobCols;
}

void CJobListDlg::SetJobCurr(CString *jobName)
{
	m_CurrJob= jobName;
}

void CJobListDlg::OnOK() 
{
	int cursel = m_JobStatus.GetCurSel();
	if (cursel != CB_ERR)
		m_JobStatus.GetLBText(cursel, m_JobStatusValue);
	if (m_JobStatusValue == _T("(default)"))
		m_JobStatusValue.Empty();

	m_WinPos.SaveWindowPosition();
	m_SelectedJobs.RemoveAll();

	int nItem=m_JobListCtrl.GetNextItem( -1, LVNI_ALL | LVNI_SELECTED );
	while(nItem != -1)
	{
		TCHAR str[ LISTVIEWNAMEBUFSIZE + 1 ];
		ListView_GetItemText( m_JobListCtrl.m_hWnd, nItem, 0, str, LISTVIEWNAMEBUFSIZE );
		m_SelectedJobs.AddHead(str);		 
		nItem=m_JobListCtrl.GetNextItem( nItem, LVNI_ALL | LVNI_SELECTED );
	}
	
	EndDialog(IDOK);
}

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

	// Update display of revision description
	if(pNMListView->uNewState==3)
	{
		txt=((CP4Job *) pNMListView->lParam)->GetDescription();
		txt.Replace(_T("\n"), _T("\r\n"));
		m_JobDesc.SetWindowText(txt);
	}

	*pResult = 0;
}

void CJobListDlg::OnCancel() 
{
	m_WinPos.SaveWindowPosition();	
	CDialog::OnCancel();
}

void CJobListDlg::OnSize(UINT nType, int cx, int cy) 
{
	CDialog::OnSize(nType, cx, cy);
	
	// Slide the OK and Cancel buttons to the bottom of dlg
	CWnd *pOK = GetDlgItem(IDOK);
	CWnd *pCancel = GetDlgItem(IDCANCEL);
	CWnd *pHelp = GetDlgItem(IDHELP);
	CWnd *pFilter = GetDlgItem(IDC_FILTER);
	CWnd *pClear = GetDlgItem(ID_CLEARFILTER);

	if(!pOK)
		return;

	CRect rect;
	int fw, fh;
	int x = cx - 5;
	int y = cy - 5;
	pFilter->GetClientRect(&rect);
	pFilter->MoveWindow(x - (rect.Width()*2) - 5, 0, 
		fw = rect.Width(), fh = rect.Height(), TRUE);
	pClear->GetClientRect(&rect);
	pClear->MoveWindow(x - rect.Width(), 0, 
		rect.Width(), rect.Height(), TRUE);
	pHelp->GetClientRect(&rect);
	pHelp->MoveWindow(x - rect.Width(), y - rect.Height(), 
		rect.Width(), rect.Height(), TRUE);
	x -= rect.Width() + 5;
	pCancel->GetClientRect(&rect);
	pCancel->MoveWindow(x - rect.Width(), y - rect.Height(), 
		rect.Width(), rect.Height(), TRUE);
	x -= rect.Width() + 5;
	pOK->GetClientRect(&rect);
	pOK->MoveWindow(x - rect.Width(), y - rect.Height(), 
		rect.Width(), rect.Height(), TRUE);

	// leave space between buttons and description edit control
	y -= rect.Height() + 5;	

	// position the filter string
	CWnd *pFltTxt = GetDlgItem(IDC_FILTER_TEXT);
	pFltTxt->GetWindowRect(&rect);
	pFltTxt->MoveWindow(5, (fh-rect.Height())/2+1, cx - 10 - (fw*2) - 6, rect.Height(), TRUE);

	// position the label
	CWnd *pLabel = GetDlgItem(IDC_SUMMARYLABEL);
	pLabel->GetClientRect(&rect);

	int remainder = y - (rect.Height() + 5 + 5);

	// Position the text box
	CWnd *pText = GetDlgItem(IDC_JOBDESC);
	int h = remainder / 4;
	pText->MoveWindow(2, y - h, cx-4, h, TRUE);	
	y -= h + 5;

	// position the label
	pLabel->GetClientRect(&rect);
	pLabel->MoveWindow(2, y - rect.Height(), rect.Width(), rect.Height(), TRUE);

	// Position the Job Status fields
	CWnd *pList = GetDlgItem(IDC_JOBSTATUS);
	pList->GetClientRect(&rect);
	x = cx - 2;
	pList->MoveWindow(x - rect.Width(), y - rect.Height(), 
		rect.Width(), rect.Height(), TRUE);
	x -= rect.Width() + 1;
	pLabel = GetDlgItem(IDC_JOBSTATUSPROMPT);
	pLabel->GetClientRect(&rect);
	pLabel->MoveWindow(x - rect.Width(), y - rect.Height(), 
		rect.Width(), rect.Height(), TRUE);
	y -= rect.Height() + 5;

	// Increase the size of the list 
	pList = GetDlgItem(IDC_JOBLIST);
	pList->MoveWindow(0, fh+5, cx, y-fh-5, TRUE);


	RedrawWindow();
}

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

void CJobListDlg::OnDblclickJoblist(NMHDR* pNMHDR, LRESULT* pResult)
{
	*pResult = 0;
	OnOK();
}

void CJobListDlg::OnColumnclickJoblist(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_JobListCtrl.SortItems( JobListDlgSort,(DWORD)pNMListView->iSubItem);

	*pResult = 0;
}

// Since a sort column can only appear once in our saved column array,
// given the current column, we can determine the next column.
// Note that incoming 'colnbr' is a non-negative 0-relative number and must be incremented
// The return value is a signed 1-relative number; negative for descending, positive for ascending
int CJobListDlg::NextSortColumn(int lastcol)
{
	if (lastcol == 0)
		return 0;		// 0 => no next column
	lastcol++;
	for (int i = -1; ++i < (MAX_SORT_COLUMNS-1); )
	{
		if (abs(m_SortColumns[i]) == lastcol)
			return m_SortColumns[i+1];
	}
	return 0;			// 0 => no next column
}

int CALLBACK JobListDlgSort(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
{
	CString txt1= ((CP4Job const *)lParam1)->GetJobField(lParamSort);
	CString txt2= ((CP4Job const *)lParam2)->GetJobField(lParamSort);

	int rc;
	int nextcol;

	if(pDlg->IsSortAscending())
		rc = txt1.Compare(txt2);
	else
		rc = txt2.Compare(txt1);

	// check for duplicate keys; if so, sort on next sort columns
	if (!rc && lParamSort && ((nextcol = pDlg->NextSortColumn(lParamSort)) != 0))
	{
		// nextcol now contains a value like 1 for col-0-ascending or like -1 for col-0-decending
		BOOL saveSortAscending = pDlg->IsSortAscending();
		if (nextcol < 0)
		{
			nextcol = 0 - nextcol;
			pDlg->SetSortAscending(FALSE);
		}
		else
			pDlg->SetSortAscending(TRUE);
		rc = JobListDlgSort(lParam1, lParam2, (LPARAM)(nextcol-1));
		pDlg->SetSortAscending(saveSortAscending);
	}
	return rc;
}

// Check the registry to see if we have recorded the
// column widths lastused for the job pane's list view.
// Note that "HKEY_CURRENT_USER\Software\perforce\P4win\Layout\JobListDlg\Column Widths"
// is no longer used because we use the "...\Job List\Column Widths" now.
void CJobListDlg::RestoreSavedWidths(int *width, int numcols)
{
    CRegKeyEx key;

    if(ERROR_SUCCESS == key.Open(HKEY_CURRENT_USER, sRegKey, KEY_READ))
    {
        CString result = key.QueryValueString(sRegValue_ColumnWidths);
		CString sortCols = key.QueryValueString(sRegValue_SortColumns);
        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);
        }
        if(!sortCols.IsEmpty())
        {
			for(int i=0; i< MAX_SORT_COLUMNS; i++)
            {
				m_SortColumns[i]= GetANumber(sortCols);
                if(!i && !m_SortColumns[i])
                    m_SortColumns[i] = 1;
            }
			m_LastSortColumn= abs(m_SortColumns[0]) - 1; // SortColumns are signed & 1-relative
			m_SortAscending = m_SortColumns[0] >= 0 ? TRUE : FALSE;
        }
    }
}

void CJobListDlg::OnJobFilter() 
{
	RECT	rect;

	CJobFilter dlg;
	dlg.SetFilterString ( m_sFilter );
	GetWindowRect(&rect);										// we want to position filter dialog box
	dlg.m_top  = rect.top + GetSystemMetrics(SM_CYCAPTION)*2;	// at the top left of this pane, so pass
	dlg.m_left = rect.left + 2;									// it our current screen location
	dlg.m_right= rect.right;
	MainFrame()->DoNotAutoPoll();
	dlg.DoModal( );
	MainFrame()->ResumeAutoPoll();
	if ( m_sFilter != dlg.GetFilterString ( ) )
	{
		m_sFilter = dlg.GetFilterString ( );
		PersistentJobFilter( KEY_WRITE );
		// Turn off all painting in children of main window to prevent flashing	
		EnumChildWindows(AfxGetMainWnd()->m_hWnd, ChildSetRedraw, FALSE);
		SET_BUSYCURSOR();
		EndDialog(IDRETRY);
	}
}

void CJobListDlg::OnClearFilter() 
{
	if (!m_sFilter.IsEmpty())
	{
		m_sFilter.Empty();
		PersistentJobFilter( KEY_WRITE );
		// Turn off all painting in children of main window to prevent flashing	
		EnumChildWindows(AfxGetMainWnd()->m_hWnd, ChildSetRedraw, FALSE);
		SET_BUSYCURSOR();
		EndDialog(IDRETRY);
	}
	else
	{
		GotoDlgCtrl(GetDlgItem(IDC_JOBLIST));
	}
}

void CJobListDlg::OnHelp() 
{
	AfxGetApp()->WinHelp(TASK_CLOSING_JOBS_USING_CHANGELISTS);
}

BOOL CJobListDlg::OnHelpInfo(HELPINFO* pHelpInfo) 
{
	OnHelp();
	return TRUE;
}
# Change User Description Committed
#1 19924 YourUncleBob Populate -o //guest/perforce_software/p4win/...
//guest/YourUncleBob/p4win/.....
//guest/perforce_software/p4win/main/gui/JobListDlg.cpp
#1 16169 perforce_software Move files to follow new path scheme for branches.
//guest/perforce_software/p4win/gui/JobListDlg.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.