// // Copyright 1997 Nicholas J. Irias. All rights reserved. // // // JobListCtrl.cpp : implementation file // #include "stdafx.h" #include "p4win.h" #include "JobListCtrl.h" #include "JobsConfigure.h" #include "MainFrm.h" #include "TokenString.h" #include "JobDescribe.h" #include "SpecDescDlg.h" #include "cmd_editspec.h" #include "cmd_jobs.h" #include "cmd_delete.h" #include "catchalldlg.h" #include "RegKeyEx.h" #include "ImageList.h" static LPCTSTR sRegKey = _T("Software\\Perforce\\P4Win\\Layout\\Job List"); static LPCTSTR sRegValue_ColumnWidths = _T("Column Widths"); static LPCTSTR sRegValue_SortColumns = _T("Sort Columns"); #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif #define IMG_INDEX(x) (x-IDB_PERFORCE) ///////////////////////////////////////////////////////////////////////////// // CJobListCtrl IMPLEMENT_DYNCREATE(CJobListCtrl, CP4ListCtrl) BEGIN_MESSAGE_MAP(CJobListCtrl, CP4ListCtrl) ON_WM_CREATE() ON_UPDATE_COMMAND_UI(ID_VIEW_UPDATE_RIGHT, OnUpdateViewUpdate) ON_WM_CONTEXTMENU() ON_UPDATE_COMMAND_UI(ID_JOB_DELETE, OnUpdateJobDelete) ON_COMMAND(ID_JOB_DELETE, OnJobDelete) ON_UPDATE_COMMAND_UI(ID_JOB_EDITSPEC, OnUpdateJobEditspec) ON_COMMAND(ID_JOB_EDITSPEC, OnJobEditspec) ON_NOTIFY_REFLECT(LVN_DELETEITEM, OnDeleteitem) ON_UPDATE_COMMAND_UI(ID_JOB_DESCRIBE, OnUpdateJobDescribe) ON_WM_LBUTTONDBLCLK() ON_COMMAND(ID_SETFILTER_JOBS, OnJobSetFilter) ON_COMMAND(ID_CLEARFILTER_JOBS, OnJobRemovefilter) ON_UPDATE_COMMAND_UI(ID_JOB_SETFILTER, OnUpdateJobSetFilter) ON_COMMAND(ID_JOB_SETFILTER, OnJobSetFilter) ON_UPDATE_COMMAND_UI(ID_JOB_REMOVEFILTER, OnUpdateJobRemovefilter) ON_COMMAND(ID_JOB_REMOVEFILTER, OnJobRemovefilter) ON_UPDATE_COMMAND_UI(ID_JOB_SETFILEFILTER, OnUpdateJobSetFileFilter) ON_COMMAND(ID_JOB_SETFILEFILTER, OnJobSetFileFilter) ON_UPDATE_COMMAND_UI(ID_JOB_SETFILEFILTERINTEG, OnUpdateJobSetFileFilter) ON_COMMAND(ID_JOB_SETFILEFILTERINTEG, OnJobSetFileFilterInteg) ON_UPDATE_COMMAND_UI(ID_JOB_REMOVEFILEFILTER, OnUpdateJobRemoveFileFilter) ON_COMMAND(ID_JOB_REMOVEFILEFILTER, OnJobRemoveFileFilter) ON_UPDATE_COMMAND_UI(ID_JOB_NEW, OnUpdateJobNew) ON_COMMAND(ID_JOB_NEW, OnJobNew) ON_COMMAND(ID_JOB_DESCRIBE, OnDescribe) ON_COMMAND(ID_VIEW_UPDATE_RIGHT, OnViewUpdate) ON_UPDATE_COMMAND_UI(ID_JOB_CONFIGURE, OnUpdateJobConfigure) ON_COMMAND(ID_JOB_CONFIGURE, OnJobConfigure) ON_COMMAND(ID_PERFORCE_OPTIONS, OnPerforceOptions) ON_MESSAGE(WM_P4JOBS, OnP4JobList ) ON_MESSAGE(WM_P4EDITSPEC, OnP4JobSpec ) ON_MESSAGE(WM_P4ENDSPECEDIT, OnP4EndSpecEdit ) ON_MESSAGE(WM_P4DELETE, OnP4Delete ) ON_MESSAGE(WM_P4JOBSPEC, OnP4JobSpecColumnNames ) ON_MESSAGE(WM_P4DESCRIBE, OnP4Describe ) ON_MESSAGE(WM_P4ENDDESCRIBE, OnP4EndDescribe ) ON_MESSAGE(WM_FETCHJOBS, OnFetchJobs ) ON_MESSAGE(WM_QUERYJOBS, OnQueryJobs ) ON_MESSAGE(WM_QUERYJOBSPEC, OnQueryJobSpec ) ON_MESSAGE(WM_QUERYJOBFIELDS, OnQueryJobFields ) ON_MESSAGE(WM_QUERYJOBCOLS, OnQueryJobColumns ) ON_MESSAGE( WM_QUERYJOBSELECTION, OnQueryJobSelection ) ON_MESSAGE(WM_JOB_FILTER, OnJobFilter2) ON_MESSAGE(WM_CLEARLIST, OnClear) END_MESSAGE_MAP() CJobListCtrl::CJobListCtrl() { m_SortAscending = m_FilterIncIntegs = FALSE; m_LastSortCol=0; m_viewType = P4JOB_SPEC; m_bAlreadyGotColumns = m_Need2CallOnJobConfigure = FALSE; m_PostListToChangeNum= 0; m_PostListToChangeWnd= 0; m_Need2DoNew = FALSE; m_NewJob = FALSE; m_ColCodes.RemoveAll(); m_captionplain = LoadStringResource(IDS_PERFORCE_JOBS); m_FastJobs = GetSavedColumnNames(m_DesiredCols, _T("Job List")); m_CF_JOB = static_cast<CLIPFORMAT>(RegisterClipboardFormat(LoadStringResource(IDS_DRAGFROMJOB))); m_CF_DEPOT = static_cast<CLIPFORMAT>(RegisterClipboardFormat(LoadStringResource(IDS_DRAGFROMDEPOT))); } CJobListCtrl::~CJobListCtrl() { } ///////////////////////////////////////////////////////////////////////////// // CJobListCtrl diagnostics #ifdef _DEBUG void CJobListCtrl::AssertValid() const { CP4ListCtrl::AssertValid(); } void CJobListCtrl::Dump(CDumpContext& dc) const { CP4ListCtrl::Dump(dc); } #endif //_DEBUG ///////////////////////////////////////////////////////////////////////////// // CJobListCtrl message handlers void CJobListCtrl::OnLButtonDblClk(UINT nFlags, CPoint point) { int index = GetHitItem ( point ); if(index != -1) { SetItemState( index, LVIS_SELECTED|LVIS_FOCUSED, LVIS_SELECTED|LVIS_FOCUSED ); OnDescribe(); } else CP4ListCtrl::OnLButtonDblClk(nFlags, point); } LRESULT CJobListCtrl::OnClear( WPARAM wParam, LPARAM lParam ) { m_sFilter.Empty( ); PersistentJobFilter( KEY_READ ); Clear(); return 0; } void CJobListCtrl::Clear() { SetRedraw(FALSE); DeleteAllItems(); SetRedraw(TRUE); CP4ListCtrl::Clear(); } ///////////////////////////////// // Three functions to allow the changelist pane to fetch the job list // and the current job codes list ///////////////////////////////// LRESULT CJobListCtrl::OnFetchJobs( WPARAM wParam, LPARAM lParam ) { m_PostListToChangeNum= wParam; m_PostListToChangeWnd= (HWND)lParam; if( GetItemCount() > 0 ) { ::SendMessage(m_PostListToChangeWnd, WM_P4JOBS, m_PostListToChangeNum, 0); m_PostListToChangeNum=0; m_PostListToChangeWnd=0; } else OnViewUpdate(); return 0; } LRESULT CJobListCtrl::OnQueryJobs( WPARAM wParam, LPARAM lParam ) { CObList *m_pJobList= new CObList; // Make a copy of the joblist, so the consumer doesnt need // to worry about any refresh that happens here at a later // time. (They just need to delete the list when done) for( int i = 0; i < GetItemCount(); i++ ) { CP4Job *job= (CP4Job *) GetItemData(i); CP4Job *newJob= new CP4Job; newJob->Create(job); m_pJobList->AddHead(newJob); } return (LRESULT) m_pJobList; } LRESULT CJobListCtrl::OnQueryJobSpec( WPARAM wParam, LPARAM lParam ) { return (LRESULT)&m_Spec; } LRESULT CJobListCtrl::OnQueryJobFields( WPARAM wParam, LPARAM lParam ) { // The caller has provided an array. Just transpose our array // into that array, to give the caller a snapshot. CArray<int,int> *array= ( CArray<int,int> *) wParam; array->RemoveAll(); for(int i=0; i < m_ColCodes.GetSize(); i++) array->Add( m_ColCodes[i] ); return (LRESULT) array; } LRESULT CJobListCtrl::OnQueryJobColumns( WPARAM wParam, LPARAM lParam ) { return (LRESULT)&m_ColNames; } LRESULT CJobListCtrl::OnQueryJobSelection( WPARAM wParam, LPARAM lParam ) { m_Active = GetSelectedItemText(); return (LRESULT)&m_Active; } void CJobListCtrl::OnUpdateJobNew(CCmdUI* pCmdUI) { pCmdUI->SetText ( LoadStringResource( IDS_NEW ) ); pCmdUI->Enable(!SERVER_BUSY() && !m_EditInProgress); m_Need2DoNew = FALSE; } /* _________________________________________________________________ since users can now define their own types in the job tracker, we have to get the names from the server. _________________________________________________________________ */ LRESULT CJobListCtrl::OnP4JobSpecColumnNames( WPARAM wParam, LPARAM lParam ) { CCmd_JobSpec *pCmd = ( CCmd_JobSpec *) wParam; ASSERT_KINDOF(CCmd_JobSpec, pCmd); if(!pCmd->GetError()) { // Delete the old column names int i; for( i= m_ColCodes.GetUpperBound(); i>=0; i-- ) DeleteColumn(i); pCmd->GetSpec( m_Spec ); m_FieldNames.SetSize(0); GetFldNames( m_FieldNames, m_Spec ); // Default column widths based on all 5 default columns being present // int width[MAX_JOBS_COLUMNS] = { 90,80,60,90,200 }; RestoreSavedWidths( width, max(MAX_JOBS_COLUMNS, m_ColCodes.GetSize()), _T("Job List") ); for (i = -1; ++i < MAX_JOBS_COLUMNS; ) { if (width[i] > 5000) width[i] = 30; } InsertColumnHeaders( m_ColNames, width ); m_bAlreadyGotColumns = TRUE; // okay, we got all the job column names. // now get the job list or start the configure dialog // if (m_Need2CallOnJobConfigure) OnJobConfigure(); else GetJobs( ); } else if(m_PostListToChangeNum != 0) { ::SendMessage(m_PostListToChangeWnd, WM_P4JOBS, m_PostListToChangeNum, 0); m_PostListToChangeNum=0; m_PostListToChangeWnd=0; } delete pCmd; MainFrame()->ClearStatus(); return 0; } /* _________________________________________________________________ Simple parse code to check jobspec for spec codes 101-105, which are reserved codes for default fields that may be present in the output of p4 jobs. Record the column headings for these codes. _________________________________________________________________ */ BOOL CJobListCtrl::GetFldNames( CStringArray &fldNames, const CString &spec ) { int i; CString errorText; CString fields; m_SpecNames.RemoveAll(); m_ColNames.RemoveAll(); m_ColNames.SetSize(0); m_ColNames.Copy(m_DesiredCols); // Get the field names from the spec. they are between the word // "Fields:" and the word "Required" strip the separator. Don't // crash if the jobspec is not recognized. int start= spec.Find(_T("Fields:")) + lstrlen(_T("Fields:")); int end= spec.Find(_T("Required:")); if( start == -1 || end == -1 || end < start || end >= spec.GetLength()) errorText=LoadStringResource(IDS_UNABLE_TO_FIND_FIELD_DELIMITERS); if( errorText.IsEmpty() ) { fields = spec.Mid(start, end - start); start= fields.Find( 0x09 ); if( start == -1 ) errorText=LoadStringResource(IDS_UNABLE_TO_FIND_INITIAL_FIELD_DELIMITER); } if( errorText.IsEmpty() ) { m_ColCodes.RemoveAll(); fields = fields.Mid( start + 1 ); // Pick out the requested field names if present int code; CString field; int max = m_ColNames.GetSize(); do { // get the field spec (e.g., "101 Job word 32") // and truncate the fields. // int delim = fields.Find( 0x0a); if(delim < 9) // need at least "n Job x n" or something's wrong break; field = fields.Left ( delim + 1 ); fields = fields.Right ( fields.GetLength( ) - (delim + 1) ); // get the "101" part of the spec in code // delim = field.Find(_T(' ')); if(delim < 1) break; code = _ttoi ( field.Left ( delim ) ) ; field = field.Right ( field.GetLength ( ) - (delim + 1) ); // get the "Job" part in field. // delim = field.Find(_T(' ')); if(delim < 1) break; field = field.Left ( delim ); m_SpecNames.Add(field); // save all field names from the spec here. if (code == 101) // if this is the jobname field, capture its name for 1st column m_ColNames.SetAt(0, field); else if (code == 103) m_ReportedByTitle = field; for (i = -1; ++i < max; ) { CString colname = m_ColNames.GetAt(i); if (colname == field) { fldNames.Add( field ); m_ColCodes.Add( code ); break; } else if ((colname.GetAt(0) == _T(':')) && (colname.GetAt(1) == _T('1')) && (colname.GetAt(2) == _T('0'))) { TCHAR digit = colname.GetAt(3); int colcode = 100 + (digit & 0x0F); if (colcode == code) { m_ColNames.SetAt(i, field); fldNames.Add( field ); m_ColCodes.Add( code ); break; } } } } while ( !field.IsEmpty( ) ); } if( !errorText.IsEmpty() ) { // Very unlikely that we will ever report this error, but an error // message is always preferable to an inexplicable crash AddToStatus(CString(_T("JobView::GetFldNames()") + errorText), SV_ERROR); return FALSE; } else return TRUE; } /* _________________________________________________________________ */ void CJobListCtrl::InsertJob(CP4Job *job, int index) { LV_ITEM lvItem; int iActualItem = -1; CString txt; CString colName; ASSERT(job != NULL); m_iImage = CP4ViewImageList::VI_JOB; int maxcols = m_ColNames.GetSize(); for(int subItem=0; subItem < maxcols; subItem++) { lvItem.mask=LVIF_TEXT | ((subItem==0) ? LVIF_IMAGE : 0) | ((subItem==0) ? LVIF_PARAM : 0); lvItem.iItem= (subItem==0) ? index : 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=InsertItem(&lvItem); else SetItem(&lvItem); } } /* _________________________________________________________________ After a spec edit, update the appropriate tree item _________________________________________________________________ */ void CJobListCtrl::UpdateJob(CP4Job *job, int index) { // First, switch the user data CP4Job *oldJob= (CP4Job *) GetItemData(index); delete oldJob; SetItemData(index, (LPARAM) job); CString txt; CString colName; // Then update the text for any fields that are present int maxCols = m_ColNames.GetSize(); for( int subItem=0; subItem < maxCols; subItem++ ) { txt=PadCRs(job->GetJobField(subItem)); SetItemText(index, subItem, const_cast<LPTSTR>((LPCTSTR)txt)); } } /* _________________________________________________________________ */ void CJobListCtrl::OnUpdateJobDescribe(CCmdUI* pCmdUI) { pCmdUI->Enable( OnUpdateShowMenuItem( pCmdUI, IDS_DESCRIBEIT_s ) ); } /* _________________________________________________________________ */ LRESULT CJobListCtrl::OnP4JobList(WPARAM wParam, LPARAM lParam) { POSITION pos; CP4Job *job; int index; CObList *jobs; CCmd_Jobs *pCmd= (CCmd_Jobs *) wParam; if(!pCmd->GetError()) { SET_BUSYCURSOR(); jobs= pCmd->GetList(); SetRedraw(FALSE); CString bigjob = _T(""); for(pos= jobs->GetHeadPosition(), index=0; pos != NULL; index++) { job=(CP4Job *) jobs->GetNext(pos); if (m_FilterView.GetCount() > 1) // check for duplicates { // if filtering on more than 1 file CString jobname = job->GetJobName(); if (jobname <= bigjob) { if (jobname == bigjob || FindInList(jobname) != -1) { delete job; continue; } } else bigjob = jobname; } InsertJob(job, index); } SetRedraw(TRUE); CString msg; msg.FormatMessage(IDS_NUMBER_OF_JOBS_n, index ); AddToStatus( msg, SV_COMPLETION ); ////jobs->RemoveAll(); ReSort(); if(jobs->GetCount() > 0) { int i = FindInList(m_Active); if (i < 0) i=0; SetItemState(i, LVIS_SELECTED|LVIS_FOCUSED, LVIS_SELECTED|LVIS_FOCUSED); EnsureVisible(i, FALSE); } CP4ListCtrl::SetUpdateDone(); if (m_Need2DoNew) OnJobNew(); ::SetCursor(::LoadCursor(NULL, IDC_ARROW)); } else { CP4ListCtrl::SetUpdateFailed(); m_Need2DoNew = FALSE; } if(m_PostListToChangeNum != 0) { ::SendMessage(m_PostListToChangeWnd, WM_P4JOBS, m_PostListToChangeNum, 0); m_PostListToChangeNum= 0; m_PostListToChangeWnd= 0; } delete pCmd; MainFrame()->ClearStatus(); // Notify the mainframe that we have finished getting the jobs, // hence the entire set of async command have finished. MainFrame()->ExpandDepotIfNeedBe(); return 0; } /* _________________________________________________________________ */ void CJobListCtrl::OnUpdateViewUpdate(CCmdUI* pCmdUI) { pCmdUI->Enable(!SERVER_BUSY() && !MainFrame()->IsModlessUp()); } void CJobListCtrl::OnViewUpdate( ) { MainFrame()->SetJobUpdateTime(GetTickCount()); m_Active = GetSelectedItemText(); SetCaption( ); if( m_ReadSavedWidths ) SaveColumnWidths(); if( GET_SERVERLEVEL() > 3 ) GetJobSpec( ) ; //which also calls GetJobs( ) else { // Against 97.3 server, the column names are always the same m_ColNames.RemoveAll(); m_ColNames.Add(LoadStringResource(IDS_P4JOB)); m_ColNames.Add(LoadStringResource(IDS_P4STATUS)); m_ColNames.Add(LoadStringResource(IDS_REPORTEDBY)); m_ColNames.Add(LoadStringResource(IDS_REPORTEDDATE)); m_ColNames.Add(LoadStringResource(IDS_DESCRIPTION)); m_ColCodes.RemoveAll(); int i; for(i=101; i <= 105; i++) m_ColCodes.Add(i); // there is no default for the jobs now that the fields // are user-defined. // int width[ MAX_JOBS_COLUMNS ] = { 90,80,60,90,200 }; RestoreSavedWidths( width, m_ColCodes.GetSize(), _T("Job List") ); for (i = -1; ++i < MAX_JOBS_COLUMNS; ) { if (width[i] > 5000) width[i] = 30; } InsertColumnHeaders( m_ColNames, width ); m_bAlreadyGotColumns = TRUE; GetJobs(); } } void CJobListCtrl::GetJobSpec( ) { CCmd_JobSpec *pCmd = new CCmd_JobSpec; pCmd->Init( m_hWnd, RUN_ASYNC ); if( !pCmd->Run( ) ) delete pCmd; } void CJobListCtrl::GetJobs( ) { CCmd_Jobs *pCmd = new CCmd_Jobs; pCmd->Init( m_hWnd, RUN_ASYNC); if( GET_SERVERLEVEL() == 3) { m_sFilter.Empty(); pCmd->SetFilter( FALSE ); } else pCmd->SetFilter( !m_sFilter.IsEmpty( ) ); ASSERT(m_bAlreadyGotColumns); int maxFlds = m_FieldNames.GetSize(); int maxCols = m_ColNames.GetSize(); int i; for (i = -1; ++i < maxCols; ) // add the fields desired in column order { CString colName = m_ColNames.GetAt(i); int j; for (j = -1; ++j < maxFlds; ) { if (m_FieldNames.GetAt(j) == colName) { pCmd->GetFieldNames().Add(colName); pCmd->GetFieldCodes().Add(m_ColCodes.GetAt(j)); break; } } if (j >= maxFlds) { pCmd->GetFieldNames().Add(_T("")); pCmd->GetFieldCodes().Add(0); } } // Make a copy of the filter view, because CCmd_Jobs will // destroy that copy POSITION pos=m_FilterView.GetHeadPosition(); m_StrList.RemoveAll(); while(pos != NULL) m_StrList.AddTail(m_FilterView.GetNext(pos)); if( pCmd->Run( m_sFilter, m_FastJobs, &m_StrList, m_FilterIncIntegs ) ) { Clear(); CP4ListCtrl::OnViewUpdate(); MainFrame()->UpdateStatus( LoadStringResource(IDS_REQUESTING_JOBS_LISTING) ); } else delete pCmd; } /* _________________________________________________________________ */ CString CJobListCtrl::SetCaption() { if( (m_sFilter.IsEmpty() && m_FilterView.IsEmpty()) || GET_SERVERLEVEL() < 4) m_caption = LoadStringResource(IDS_PERFORCE_JOBS); else { CString filter; if (!m_sFilter.IsEmpty() && !m_FilterView.IsEmpty()) filter.FormatMessage(IDS_FILTERED_BOTH, m_sFilter); else if (!m_FilterView.IsEmpty()) { CString filelist=m_FilterView.GetHead(); if (m_FilterView.GetCount() > 1) { POSITION pos= m_FilterView.GetHeadPosition(); m_FilterView.GetNext(pos); while( pos != NULL ) filelist += _T(", ") + m_FilterView.GetNext(pos); } filter.FormatMessage(IDS_FILTERED_BY_FILES, filelist); } else filter.FormatMessage(IDS_FILTERED, m_sFilter); m_caption = LoadStringResource(IDS_PERFORCE_JOBS) + filter; } CP4PaneContent::GetView()->SetCaption(); return m_caption; } /* _________________________________________________________________ */ void CJobListCtrl::OnContextMenu(CWnd* pWnd, CPoint point) { // make sure window is active GetParentFrame()->ActivateFrame(); /////////////////////////////// // See ContextMenuRules.txt for order of menu commands! // create an empty context menu CP4Menu popMenu; popMenu.CreatePopupMenu(); int index; SetIndexAndPoint( index, point ); // Can always create new job popMenu.AppendMenu(MF_ENABLED | MF_STRING, ID_JOB_NEW ); if(index != -1) { popMenu.AppendMenu(MF_ENABLED | MF_STRING, ID_JOB_EDITSPEC ); popMenu.AppendMenu(MF_ENABLED | MF_STRING, ID_JOB_DESCRIBE ); popMenu.AppendMenu(MF_ENABLED | MF_STRING, ID_JOB_DELETE ); } popMenu.AppendMenu(MF_SEPARATOR); popMenu.AppendMenu(MF_ENABLED | MF_STRING, ID_JOB_SETFILTER ); popMenu.AppendMenu(MF_ENABLED | MF_STRING, ID_JOB_REMOVEFILTER ); popMenu.AppendMenu(MF_SEPARATOR); popMenu.AppendMenu(MF_ENABLED | MF_STRING, ID_JOB_SETFILEFILTER, LoadStringResource(IDS_FILTER_JOBVIEW) ); if (GET_P4REGPTR()->GetEnableSubChgIntegFilter()) popMenu.AppendMenu(MF_ENABLED | MF_STRING, ID_JOB_SETFILEFILTERINTEG, LoadStringResource(IDS_FILTERINTEG_JOBVIEW) ); popMenu.AppendMenu(MF_ENABLED | MF_STRING, ID_JOB_REMOVEFILEFILTER, LoadStringResource(IDS_JOB_REMOVEFILEFILTER) ); popMenu.AppendMenu(MF_SEPARATOR); popMenu.AppendMenu(MF_ENABLED | MF_STRING, ID_JOB_CONFIGURE, LoadStringResource(IDS_JOB_CONFIGURE) ); popMenu.AppendMenu(MF_ENABLED | MF_STRING, ID_VIEW_UPDATE, LoadStringResource(IDS_REFRESH)); MainFrame()->AddToolsToContextMenu(&popMenu); popMenu.TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, point.x, point.y, AfxGetMainWnd()); } /* _________________________________________________________________ */ void CJobListCtrl::OnUpdateJobDelete(CCmdUI* pCmdUI) { pCmdUI->Enable( OnUpdateShowMenuItem( pCmdUI, IDS_DELETE_s ) ); } void CJobListCtrl::OnJobDelete() { OnDelete( P4JOB_DEL ); } /* _________________________________________________________________ */ void CJobListCtrl::OnUpdateJobEditspec( CCmdUI* pCmdUI ) { pCmdUI->Enable( OnUpdateShowMenuItem(pCmdUI, IDS_EDITSPEC_s) && !m_EditInProgress ); } void CJobListCtrl::EditTheSpec(CString *name) { OnJobEditspec(name); } void CJobListCtrl::OnJobEditspec() { CString str = GetSelectedItemText(); OnJobEditspec(&str); } void CJobListCtrl::OnJobEditspec(CString *jobname) { m_Active = *jobname; m_NewJob = FALSE; EditSpec( ) ; } void CJobListCtrl::OnJobNew() { MainFrame()->ViewJobs(); if (SERVER_BUSY()) m_Need2DoNew = TRUE; else { m_Need2DoNew = FALSE; m_Active.Empty(); m_NewJob = TRUE; EditSpec( ) ; } } void CJobListCtrl::EditSpec() { if (m_EditInProgress) { CantEditRightNow(IDS_JOB); return; } m_pNewSpec = new CP4Job; CCmd_EditSpec *pCmd = new CCmd_EditSpec; pCmd->Init( m_hWnd, RUN_ASYNC, HOLD_LOCK ); if( pCmd->Run( P4JOB_SPEC, m_Active, m_pNewSpec ) )//fanny:is this okay for edit and new? MainFrame()->UpdateStatus( LoadStringResource(IDS_EDITING_JOB_SPEC) ); else { delete pCmd; delete m_pNewSpec; } } LRESULT CJobListCtrl::OnP4JobSpec(WPARAM wParam, LPARAM lParam) { CCmd_EditSpec *pCmd= (CCmd_EditSpec *) wParam; // let the dialogbox know whether this is a new job or an edit of an existing one pCmd->SetIsRequestingNew(m_NewJob); pCmd->SetCaller(DYNAMIC_DOWNCAST(CView, GetParent())); if(!pCmd->GetError() && !m_EditInProgress && pCmd->DoSpecDlg(this)) { m_EditInProgress = TRUE; m_EditInProgressWnd = pCmd->GetSpecSheet(); } else { delete m_pNewSpec; if (pCmd->HaveServerLock()) pCmd->ReleaseServerLock(); delete pCmd; } MainFrame()->ClearStatus(); return 0; } LRESULT CJobListCtrl::OnP4EndSpecEdit( WPARAM wParam, LPARAM lParam ) { CCmd_EditSpec *pCmd= (CCmd_EditSpec *) wParam; int index; if (lParam != IDCANCEL && lParam != IDABORT && lParam != IDRETRY) { m_pNewSpec->SetJobName(pCmd->GetNewJobName()); if(m_NewJob) { LPCTSTR statustext = m_pNewSpec->GetStatusText(); if (!*statustext) m_pNewSpec->SetJobStatus(JOB_OPEN); } m_pNewSpec->ConvertToColumns(m_ColCodes, m_ColNames, m_FieldNames); if(m_NewJob & (FindInList(pCmd->GetNewJobName()) == -1) ) { InsertJob(m_pNewSpec, GetItemCount()); ReSort(); index= FindInList(pCmd->GetNewJobName()); } else { index= FindInList(pCmd->GetNewJobName()); if (index != -1) UpdateJob(m_pNewSpec, index); else if (m_ColNames.GetSize()) // Has job pane been initialized? { InsertJob(m_pNewSpec, GetItemCount()); ReSort(); index= FindInList(pCmd->GetNewJobName()); } else delete m_pNewSpec; } EnsureVisible( index, TRUE ); SetItemState( index, LVIS_SELECTED|LVIS_FOCUSED, LVIS_SELECTED|LVIS_FOCUSED ); } else delete m_pNewSpec; if (lParam != IDABORT) { MainFrame()->ClearStatus(); if (pCmd->HaveServerLock()) pCmd->ReleaseServerLock(); CDialog *dlg = (CDialog *)pCmd->GetSpecSheet(); dlg->DestroyWindow(); } delete pCmd; m_EditInProgress = FALSE; return 0; } ////////////////////////////////////////////////////////////////////////// // Sort callback, not in class int CJobListCtrl::OnCompareItems(LPARAM lParam1, LPARAM lParam2, int subItem) { ASSERT(lParam1 && lParam2); CString txt1= ((CP4Job const *)lParam1)->GetJobField(subItem); CString txt2= ((CP4Job const *)lParam2)->GetJobField(subItem); int rc; if(m_SortAscending) rc = txt1.Compare(txt2); else rc = txt2.Compare(txt1); return rc; } /* _________________________________________________________________ */ void CJobListCtrl::OnDeleteitem(NMHDR* pNMHDR, LRESULT* pResult) { NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR; delete (CP4Job *) GetItemData(pNMListView->iItem); *pResult = 0; } /* _________________________________________________________________ */ void CJobListCtrl::OnUpdateJobSetFilter(CCmdUI* pCmdUI) { pCmdUI->SetText ( LoadStringResource( IDS_FILTER ) ); pCmdUI->Enable(MainFrame()->SetMenuIcon(pCmdUI, GET_SERVERLEVEL() > 3 && !SERVER_BUSY())); } LRESULT CJobListCtrl::OnJobFilter2(WPARAM, LPARAM) { OnJobSetFilter(); return 0; } void CJobListCtrl::OnJobSetFilter() { 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); // 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 ); OnViewUpdate( ); } } void CJobListCtrl::OnUpdateJobRemovefilter(CCmdUI* pCmdUI) { pCmdUI->SetText ( LoadStringResource( IDS_CLEARFILTER ) ); pCmdUI->Enable(MainFrame()->SetMenuIcon(pCmdUI, GET_SERVERLEVEL() > 3 && !SERVER_BUSY() && !m_sFilter.IsEmpty())); } void CJobListCtrl::OnJobRemovefilter() { m_sFilter.Empty ( ); PersistentJobFilter( KEY_WRITE ); OnViewUpdate( ); } void CJobListCtrl::PersistentJobFilter( REGSAM accessmask ) { HKEY hKey = NULL; CString sKey = _T("Software\\Perforce\\P4Win\\"); CString sEntry = _T("JobFilter"); 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'); m_sFilter = buf; } } RegCloseKey( hKey ); } } void CJobListCtrl::OnUpdateJobConfigure(CCmdUI* pCmdUI) { pCmdUI->Enable(MainFrame()->SetMenuIcon(pCmdUI, !SERVER_BUSY())); } int CJobListCtrl::GetFieldNbr( CString str, const CString &spec ) { // Get the field names from the spec. they are between the word // "Fields:" and the word "Required" strip the separator. Don't // crash if the jobspec is not recognized. CString errorText; CString fields; int start= spec.Find(_T("Fields:")) + lstrlen(_T("Fields:")); int end= spec.Find(_T("Required:")); if( start == -1 || end == -1 || end < start || end >= spec.GetLength()) errorText=LoadStringResource(IDS_UNABLE_TO_FIND_FIELD_DELIMITERS); if( errorText.IsEmpty() ) { fields = spec.Mid(start, end - start); start= fields.Find( _T('\t') ); if( start == -1 ) errorText=LoadStringResource(IDS_UNABLE_TO_FIND_INITIAL_FIELD_DELIMITER); } if( errorText.IsEmpty() ) { fields = fields.Mid( start + 1 ); // Pick out the requested field names if present int code; CString field; do { // get the field spec (e.g., "101 Job word 32") // and truncate the fields. // field = fields.Left ( fields.Find( _T('\n') ) + 1 ); fields = fields.Right ( fields.GetLength( ) - field.Find ( _T('\n') ) - 1 ); // get the "101" part of the spec in code // and the "Job" part in field. // int i = field.Find( _T(' ') ); if (i == -1) return 0x7FFF; code = _ttoi ( field.Left ( i ) ) ; field = field.Right ( field.GetLength ( ) - field.Find( _T(' ') ) - 1 ); field = field.Left ( field.Find ( _T(' ') ) ); if (str == field) return code; } while ( !field.IsEmpty( ) ); } if( !errorText.IsEmpty() ) { // Very unlikely that we will ever report this error, but an error // message is always preferable to an inexplicable crash AddToStatus(CString(_T("JobView::GetFldNbr()") + errorText), SV_ERROR); } return 0x7FFF; } void CJobListCtrl::OnJobConfigure() { int rc; CJobsConfigure dlg; int maxcols = m_ColNames.GetSize(); if (!maxcols && !m_Need2CallOnJobConfigure) { m_Need2CallOnJobConfigure = TRUE; GetJobSpec( ); return; } m_Need2CallOnJobConfigure = FALSE; CStringArray colNames; colNames.SetSize(maxcols); CArray<int, int> colWidths; colWidths.SetSize(maxcols); int subItem; for(subItem=0; subItem < maxcols; subItem++) { dlg.m_ColNames += m_ColNames.GetAt(subItem) + _T(' '); colWidths.SetAt(subItem, GetColumnWidth(subItem)); colNames.SetAt(subItem, m_ColNames.GetAt(subItem)); } int maxflds = m_SpecNames.GetSize(); for(subItem=0; subItem < maxflds; subItem++) dlg.m_SpecNames += m_SpecNames.GetAt(subItem) + _T(' '); if ((rc = dlg.DoModal( )) != IDCANCEL) { BOOL need2save = FALSE; CString newWidths; CString newSorts; if (rc == IDOK) { int i, j; CString num; CString newNames; int newSortCols[MAX_SORT_COLUMNS] = {0,0,0,0}; int tokenctr = 1; CTokenString tokstr; CString token; tokstr.Create(dlg.m_ColNames); tokstr.PrepareParse( ); token=tokstr.GetToken(); while(!token.IsEmpty()) { num = _T("50"); for (i = -1; ++i < maxcols; ) { if (m_ColNames.GetAt(i) == token) { int k; for (k=-1; ++k < maxcols; ) { if (colNames.GetAt(k) == token) break; } if (k < maxcols) num.Format(_T("%d"), colWidths.GetAt(k)); for (j=-1, ++i; ++j < MAX_SORT_COLUMNS; ) { if (abs(m_SortColumns[j]) == i) { newSortCols[j] = m_SortColumns[j] < 0 ? -tokenctr : tokenctr; break; } } break; } } if(!newWidths.IsEmpty()) newWidths += _T(","); newWidths += num; int n = GetFieldNbr(token, m_Spec); if (n < 106) token.Format(_T(":%d"), n); newNames += token + _T(' '); token=tokstr.GetToken(); tokenctr++; } SaveColumnNames(newNames, _T("Job List")); for (i = -1; ++i < MAX_SORT_COLUMNS; ) { num.Format(_T("%d"), newSortCols[i]); if(i) newSorts+=_T(","); newSorts+=num; } need2save = TRUE; } else if (rc == IDIGNORE) { DeleteColumnNames(_T("Job List")); newWidths = _T("90,80,60,90,200"); newSorts = _T("-1,0,0,0"); need2save = TRUE; } if (need2save) { // Save the column widths CString theKey = sRegKey; CRegKeyEx key; if(ERROR_SUCCESS == key.Create(HKEY_CURRENT_USER, theKey)) { key.SetValueString(newWidths, sRegValue_ColumnWidths); key.SetValueString(newSorts, sRegValue_SortColumns); m_ReadSavedWidths = FALSE; } } m_DesiredCols.RemoveAll(); m_DesiredCols.SetSize(0); m_FastJobs = GetSavedColumnNames(m_DesiredCols, _T("Job List")); m_FieldNames.SetSize(0); GetFldNames( m_FieldNames, m_Spec ); OnViewUpdate( ); } } void CJobListCtrl::OnDescribeJob() { // let user type in the job name. if it's blank the user bailed. // CJobDescribe dlg; dlg.m_JobStr = m_Describing; if( dlg.DoModal( ) == IDCANCEL ) return; m_Describing = dlg.GetJobStr( ) ; if ( m_Describing.IsEmpty( ) ) return; m_Active = GetSelectedItemText(); // so we can see whether to show the Edit button or not CCmd_Describe *pCmd= new CCmd_Describe; pCmd->Init( m_hWnd, RUN_ASYNC); if( pCmd->Run( P4JOB_SPEC, m_Describing) ) MainFrame()->UpdateStatus(LoadStringResource(IDS_FETCHING_JOB_SPEC)); else delete pCmd; } BOOL CJobListCtrl::TryDragDrop( ) { // Store the job this is from m_DragFromItemName = GetSelectedItemText(); m_OLESource.DelayRenderData( (unsigned short) m_CF_JOB); return m_OLESource.DoDragDrop(DROPEFFECT_COPY, &m_DragSourceRect, NULL) == DROPEFFECT_NONE ? FALSE : TRUE; } ///////////////////////////////////////////////////////////////////// // OLE drag-drop support, to accept depot files or folders // or accept user or client names which will // define a view to be used to filter the submitted // changes that this window displays. // Also can drop Jobs to be Fixed. ///////////////////////////////////////////////////////////////////// DROPEFFECT CJobListCtrl::OnDragEnter(COleDataObject* pDataObject, DWORD dwKeyState, CPoint point) { m_DropEffect=DROPEFFECT_NONE; m_DragDataFormat=0; // Dont allow a drop if the server is busy, since a drop immediately attempts to // invoke a server command if(SERVER_BUSY()) return DROPEFFECT_NONE; if(pDataObject->IsDataAvailable( (unsigned short) m_CF_DEPOT)) { m_DropEffect=DROPEFFECT_COPY; m_DragDataFormat=m_CF_DEPOT; } #ifdef UNICODE else if(pDataObject->IsDataAvailable( (unsigned short) CF_UNICODETEXT)) { m_DropEffect=DROPEFFECT_COPY; m_DragDataFormat=CF_UNICODETEXT; } #else else if(pDataObject->IsDataAvailable( (unsigned short) CF_TEXT)) { m_DropEffect=DROPEFFECT_COPY; m_DragDataFormat=CF_TEXT; } #endif return m_DropEffect; } DROPEFFECT CJobListCtrl::OnDragOver(COleDataObject* pDataObject, DWORD dwKeyState, CPoint point) { // Dont allow a drop if the server is busy, since a drop immediately attempts to // invoke a server command if(SERVER_BUSY()) m_DropEffect= DROPEFFECT_NONE; return m_DropEffect; } BOOL CJobListCtrl::OnDrop(COleDataObject* pDataObject, DROPEFFECT dropEffect, CPoint point) { CString fname; if(SERVER_BUSY()) { // OnDragEnter() and OnDragOver() should avoid a drop at // the wrong time! ASSERT(0); return FALSE; } if(m_DragDataFormat == m_CF_DEPOT) { ClientToScreen(&point); ::SendMessage(m_depotWnd, WM_DROPTARGET, JOBVIEW, MAKELPARAM(point.x,point.y)); return TRUE; } #ifdef UNICODE if(m_DragDataFormat == CF_UNICODETEXT) { HGLOBAL hGlob = pDataObject->GetGlobalData(CF_UNICODETEXT); #else if(m_DragDataFormat == CF_TEXT) { HGLOBAL hGlob = pDataObject->GetGlobalData(CF_TEXT); #endif LPCTSTR p; if ((hGlob != NULL) && ((p = (LPCTSTR)::GlobalLock(hGlob)) != NULL)) { CString itemStr = p; ::GlobalUnlock(hGlob); OnEditPaste( itemStr ); } return TRUE; } // Return false, so depot window doesnt start a file-open operation return FALSE; } int CJobListCtrl::OnCreate(LPCREATESTRUCT lpCreateStruct) { if (CP4ListCtrl::OnCreate(lpCreateStruct) == -1) return -1; m_sFilter.Empty( ); PersistentJobFilter( KEY_READ ); return 0; } void CJobListCtrl::OnJobSetFileFilter() { if( !SERVER_BUSY() ) { ::SendMessage(m_depotWnd, WM_GETSELLIST, (WPARAM) &m_FilterView, 0); m_FilterIncIntegs = FALSE; OnViewUpdate(); } } void CJobListCtrl::OnJobSetFileFilterInteg() { if( !SERVER_BUSY() ) { ::SendMessage(m_depotWnd, WM_GETSELLIST, (WPARAM) &m_FilterView, 0); m_FilterIncIntegs = GET_P4REGPTR()->GetEnableSubChgIntegFilter(); OnViewUpdate(); } } void CJobListCtrl::OnUpdateJobSetFileFilter(CCmdUI* pCmdUI) { pCmdUI->Enable( !SERVER_BUSY() && (pCmdUI->m_nID != ID_JOB_SETFILEFILTERINTEG || GET_P4REGPTR()->GetEnableSubChgIntegFilter()) && ::SendMessage(m_depotWnd, WM_GETSELCOUNT, 0, 0) > 0 ); } void CJobListCtrl::OnUpdateJobRemoveFileFilter(CCmdUI* pCmdUI) { pCmdUI->Enable(!SERVER_BUSY() && !m_FilterView.IsEmpty()); } void CJobListCtrl::OnJobRemoveFileFilter() { m_FilterView.RemoveAll(); m_FilterIncIntegs = FALSE; OnViewUpdate( ); } void CJobListCtrl::OnUpdateSetFilterJobs(CCmdUI* pCmdUI) { pCmdUI->Enable( !SERVER_BUSY() && // can do expression filtering (GET_SERVERLEVEL() > 3 || // or files selected for filter by files ::SendMessage(m_depotWnd, WM_GETSELCOUNT, 0, 0) > 0 )); } void CJobListCtrl::OnUpdateClearFilterJobs(CCmdUI* pCmdUI) { pCmdUI->Enable(!SERVER_BUSY() && (!m_FilterView.IsEmpty() || !m_sFilter.IsEmpty())); } void CJobListCtrl::OnPerforceOptions() { MainFrame()->OnPerforceOptions(TRUE, FALSE, IDS_PAGE_JOB); }
# | Change | User | Description | Committed | |
---|---|---|---|---|---|
#1 | 9617 | Ben_Key |
Populate //guest/Ben_Key/p4win/trunk/... from //guest/perforce_software/p4win/.... |
||
//guest/perforce_software/p4win/gui/JobListCtrl.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. |