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