// 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. |
||