You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
498 lines
15 KiB
498 lines
15 KiB
//========= Copyright Valve Corporation, All rights reserved. ============// |
|
// |
|
// Purpose: |
|
// |
|
//============================================================================= |
|
|
|
#include "stdafx.h" |
|
|
|
#include "vgui_controls/TreeView.h" |
|
#include "vgui_controls/ListPanel.h" |
|
#include "vgui_controls/SectionedListPanel.h" |
|
#include "vgui_controls/ComboBox.h" |
|
#include "vgui_controls/PropertySheet.h" |
|
#include "vgui_controls/PropertyPage.h" |
|
|
|
#include <ctype.h> |
|
|
|
using namespace vgui; |
|
extern IP4* p4; |
|
|
|
// list of all tree view icons |
|
enum |
|
{ |
|
IMAGE_FOLDER = 1, |
|
IMAGE_OPENFOLDER, |
|
IMAGE_FILE, |
|
}; |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Handles file view |
|
//----------------------------------------------------------------------------- |
|
class CFileTreeView : public TreeView |
|
{ |
|
DECLARE_CLASS_SIMPLE( CFileTreeView, TreeView ); |
|
public: |
|
CFileTreeView(Panel *parent, const char *name) : BaseClass(parent, name) |
|
{ |
|
} |
|
|
|
// override to incremental request and show p4 directories |
|
virtual void GenerateChildrenOfNode(int itemIndex) |
|
{ |
|
KeyValues *pkv = GetItemData(itemIndex); |
|
if (!pkv->GetInt("dir")) |
|
return; |
|
|
|
const char *pFilePath = pkv->GetString("path", ""); |
|
if (!pFilePath[0]) |
|
return; |
|
|
|
surface()->SetCursor(dc_waitarrow); |
|
// get the list of files |
|
CUtlVector<P4File_t> &files = p4->GetFileList(pFilePath); |
|
|
|
// generate children |
|
// add all the items |
|
for (int i = 0; i < files.Count(); i++) |
|
{ |
|
if (files[i].m_bDeleted) |
|
continue; |
|
|
|
KeyValues *kv = new KeyValues("node", "text", p4->String( files[i].m_sName ) ); |
|
|
|
if (files[i].m_bDir) |
|
{ |
|
kv->SetInt("expand", 1); |
|
kv->SetInt("dir", 1); |
|
kv->SetInt("image", IMAGE_FOLDER); |
|
} |
|
else |
|
{ |
|
kv->SetInt("image", IMAGE_FILE); |
|
} |
|
|
|
// get the files path |
|
char szPath[MAX_PATH]; |
|
if (files[i].m_bDir) |
|
{ |
|
Q_snprintf(szPath, sizeof(szPath), "%s/%s/%%%%1", p4->String( files[i].m_sPath ), p4->String( files[i].m_sName ) ); |
|
} |
|
else |
|
{ |
|
Q_snprintf(szPath, sizeof(szPath), "%s/%s", p4->String( files[i].m_sPath ), p4->String( files[i].m_sName )); |
|
} |
|
|
|
// translate the files path from a depot path into a local path |
|
char szLocalPath[MAX_PATH]; |
|
p4->GetLocalFilePath(szLocalPath, szPath, sizeof(szLocalPath)); |
|
kv->SetString("path", szLocalPath); |
|
|
|
// now change the items text to match the local paths file name... |
|
char *pLocalName = Q_strrchr(szLocalPath, '\\'); |
|
if (pLocalName) |
|
{ |
|
*pLocalName = 0; |
|
++pLocalName; |
|
} |
|
kv->SetString("text", pLocalName); |
|
|
|
int itemID = AddItem(kv, itemIndex); |
|
|
|
// mark out of date files in red |
|
if (files[i].m_iHaveRevision < files[i].m_iHeadRevision) |
|
{ |
|
SetItemFgColor(itemID, Color(255, 0, 0, 255)); |
|
} |
|
} |
|
} |
|
|
|
// setup a context menu whenever a directory is clicked on |
|
virtual void GenerateContextMenu( int itemIndex, int x, int y ) |
|
{ |
|
KeyValues *pkv = GetItemData(itemIndex); |
|
const char *pFilePath = pkv->GetString("path", ""); |
|
if (!pFilePath[0]) |
|
return; |
|
|
|
Menu *pContext = new Menu(this, "FileContext"); |
|
|
|
if (pkv->GetInt("dir")) |
|
{ |
|
pContext->AddMenuItem("Cloak folder", new KeyValues("CloakFolder", "item", itemIndex), GetParent(), NULL); |
|
} |
|
else |
|
{ |
|
pContext->AddMenuItem("Open for edit", new KeyValues("EditFile", "item", itemIndex), GetParent(), NULL); |
|
pContext->AddMenuItem("Open for delete", new KeyValues("DeleteFile", "item", itemIndex), GetParent(), NULL); |
|
} |
|
|
|
// show the context menu |
|
pContext->SetPos(x, y); |
|
pContext->SetVisible(true); |
|
} |
|
|
|
|
|
|
|
// setup a smaller font |
|
virtual void ApplySchemeSettings(IScheme *pScheme) |
|
{ |
|
BaseClass::ApplySchemeSettings(pScheme); |
|
SetFont( pScheme->GetFont("DefaultSmall") ); |
|
} |
|
}; |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Revision list |
|
//----------------------------------------------------------------------------- |
|
class CSmallTextListPanel : public ListPanel |
|
{ |
|
DECLARE_CLASS_SIMPLE( CSmallTextListPanel, ListPanel ); |
|
public: |
|
CSmallTextListPanel(Panel *parent, const char *name) : BaseClass(parent, name) |
|
{ |
|
} |
|
|
|
virtual void ApplySchemeSettings(IScheme *pScheme) |
|
{ |
|
BaseClass::ApplySchemeSettings(pScheme); |
|
|
|
SetFont( pScheme->GetFont("DefaultSmall") ); |
|
} |
|
}; |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Constructor |
|
//----------------------------------------------------------------------------- |
|
CVP4Dialog::CVP4Dialog() : BaseClass(NULL, "vp4dialog"), m_Images(false) |
|
{ |
|
SetSize(1024, 768); |
|
SetTitle("VP4", true); |
|
|
|
// clientspec selection |
|
m_pClientCombo = new ComboBox(this, "ClientCombo", 16, false); |
|
|
|
// file browser tree controls |
|
m_pFileTree = new CFileTreeView(this, "FileTree"); |
|
// build our list of images |
|
m_Images.AddImage(scheme()->GetImage("resource/icon_folder", false)); |
|
m_Images.AddImage(scheme()->GetImage("resource/icon_folder_selected", false)); |
|
m_Images.AddImage(scheme()->GetImage("resource/icon_file", false)); |
|
m_pFileTree->SetImageList(&m_Images, false); |
|
|
|
// property sheet - revisions, changes, etc. |
|
m_pViewsSheet = new PropertySheet(this, "ViewsSheet"); |
|
|
|
// changes |
|
m_pChangesPage = new PropertyPage(m_pViewsSheet, "ChangesPage"); |
|
m_pViewsSheet->AddPage(m_pChangesPage, "Changes"); |
|
m_pChangesList = new SectionedListPanel(m_pChangesPage, "ChangesList"); |
|
|
|
// layout changes |
|
int x, y, wide, tall; |
|
m_pChangesPage->GetBounds(x, y, wide, tall); |
|
m_pChangesList->SetAutoResize( Panel::PIN_TOPLEFT, Panel::AUTORESIZE_DOWNANDRIGHT, 6, 6, -12, -12 ); |
|
|
|
// revisions |
|
m_pRevisionsPage = new PropertyPage(m_pViewsSheet, "RevisionsPage"); |
|
m_pViewsSheet->AddPage(m_pRevisionsPage, "History"); |
|
m_pRevisionList = new CSmallTextListPanel(m_pRevisionsPage, "RevisionList"); |
|
m_pRevisionList->SetEmptyListText("No file or directory currently selected."); |
|
m_pRevisionList->AddColumnHeader(0, "change", "change", 52, ListPanel::COLUMN_HIDDEN); |
|
m_pRevisionList->AddColumnHeader(1, "date", "date", 52, 0); |
|
m_pRevisionList->AddColumnHeader(2, "time", "time", 52, ListPanel::COLUMN_HIDDEN); |
|
m_pRevisionList->AddColumnHeader(3, "user", "user", 64, 0); |
|
m_pRevisionList->AddColumnHeader(4, "description", "description", 32, ListPanel::COLUMN_RESIZEWITHWINDOW); |
|
m_pRevisionList->SetAllowUserModificationOfColumns(true); |
|
|
|
// layout revisions |
|
m_pRevisionsPage->GetBounds(x, y, wide, tall); |
|
m_pRevisionList->SetAutoResize( Panel::PIN_TOPLEFT, Panel::AUTORESIZE_DOWNANDRIGHT, 6, 6, -12, -12 ); |
|
|
|
LoadControlSettingsAndUserConfig("//PLATFORM/resource/vp4dialog.res"); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Destructor |
|
//----------------------------------------------------------------------------- |
|
CVP4Dialog::~CVP4Dialog() |
|
{ |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: stops app on close |
|
//----------------------------------------------------------------------------- |
|
void CVP4Dialog::OnClose() |
|
{ |
|
BaseClass::OnClose(); |
|
ivgui()->Stop(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: called to open |
|
//----------------------------------------------------------------------------- |
|
void CVP4Dialog::Activate() |
|
{ |
|
BaseClass::Activate(); |
|
|
|
char szTitle[256]; |
|
Q_snprintf(szTitle, sizeof(szTitle), "VP4 - %s", p4->String( p4->GetActiveClient().m_sUser )); |
|
|
|
SetTitle(szTitle, true); |
|
|
|
RefreshFileList(); |
|
RefreshClientList(); |
|
RefreshChangesList(); |
|
|
|
p4->GetClientList(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Refreshes the active file list |
|
//----------------------------------------------------------------------------- |
|
void CVP4Dialog::RefreshFileList() |
|
{ |
|
m_pFileTree->RemoveAll(); |
|
m_pFileTree->SetFgColor(Color(216, 222, 211, 255)); |
|
|
|
// add the base node |
|
KeyValues *pkv = new KeyValues("root"); |
|
pkv->SetString("text", p4->String( p4->GetActiveClient().m_sLocalRoot )); |
|
pkv->SetString("path", p4->String( p4->GetActiveClient().m_sLocalRoot )); |
|
pkv->SetInt("dir", 1); |
|
int iRoot = m_pFileTree->AddItem(pkv, m_pFileTree->GetRootItemIndex()); |
|
m_pFileTree->ExpandItem(iRoot, true); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: utility function used in qsort |
|
//----------------------------------------------------------------------------- |
|
int __cdecl IntSortFunc(const void *elem1, const void *elem2) |
|
{ |
|
if ( *((int *)elem1) < *((int *)elem2) ) |
|
return -1; |
|
else if ( *((int *)elem1) > *((int *)elem2) ) |
|
return 1; |
|
|
|
return 0; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: rebuilds the list of changes |
|
//----------------------------------------------------------------------------- |
|
void CVP4Dialog::RefreshChangesList() |
|
{ |
|
m_pChangesList->RemoveAll(); |
|
m_pChangesList->RemoveAllSections(); |
|
|
|
CUtlVector<P4File_t> files; |
|
p4->GetOpenedFileList( files ); |
|
CUtlVector<int> sections; |
|
|
|
// find out all the changelists |
|
for (int i = 0; i < files.Count(); i++) |
|
{ |
|
if (!sections.IsValidIndex(sections.Find(files[i].m_iChangelist))) |
|
{ |
|
// add a new section |
|
sections.AddToTail(files[i].m_iChangelist); |
|
} |
|
} |
|
|
|
// sort the changelists |
|
qsort(sections.Base(), sections.Count(), sizeof(int), &IntSortFunc); |
|
|
|
// add the changeslists |
|
{for (int i = 0; i < sections.Count(); i++) |
|
{ |
|
m_pChangesList->AddSection(sections[i], ""); |
|
|
|
char szChangelistName[256]; |
|
if (sections[i] > 0) |
|
{ |
|
Q_snprintf(szChangelistName, sizeof(szChangelistName), "CHANGE: %d", sections[i]); |
|
} |
|
else |
|
{ |
|
Q_snprintf(szChangelistName, sizeof(szChangelistName), "CHANGE: DEFAULT"); |
|
} |
|
|
|
m_pChangesList->AddColumnToSection(sections[i], "file", szChangelistName, SectionedListPanel::COLUMN_BRIGHT, 512); |
|
}} |
|
|
|
// add the files to the changelists |
|
{for (int i = 0; i < files.Count(); i++) |
|
{ |
|
char szFile[_MAX_PATH]; |
|
Q_snprintf(szFile, sizeof(szFile), "%s/%s", p4->String( files[i].m_sPath ) + p4->GetDepotRootLength(), p4->String( files[i].m_sName )); |
|
KeyValues *pkv = new KeyValues("node", "file", szFile); |
|
m_pChangesList->AddItem(files[i].m_iChangelist, pkv); |
|
}} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Refreshes the client selection combo |
|
//----------------------------------------------------------------------------- |
|
void CVP4Dialog::RefreshClientList() |
|
{ |
|
m_pClientCombo->RemoveAll(); |
|
|
|
CUtlVector<P4Client_t> &clients = p4->GetClientList(); |
|
P4Client_t &activeClient = p4->GetActiveClient(); |
|
|
|
// go through all clients |
|
for (int i = 0; i < clients.Count(); i++) |
|
{ |
|
// only add clients that are defined for this machine (host) |
|
if (clients[i].m_sHost != activeClient.m_sHost) |
|
continue; |
|
|
|
// add in the new item |
|
char szText[256]; |
|
Q_snprintf(szText, sizeof(szText), "%s %s", p4->String( clients[i].m_sName ), p4->String( clients[i].m_sLocalRoot )); |
|
m_pClientCombo->AddItem(szText, new KeyValues("client", "client", p4->String( clients[i].m_sName ))); |
|
} |
|
|
|
m_pClientCombo->SetText( p4->String( activeClient.m_sName )); |
|
|
|
if (m_pClientCombo->GetItemCount() > 1) |
|
{ |
|
m_pClientCombo->SetEnabled(true); |
|
} |
|
else |
|
{ |
|
m_pClientCombo->SetEnabled(false); |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: refreshes dialog on text changing |
|
//----------------------------------------------------------------------------- |
|
void CVP4Dialog::OnTextChanged() |
|
{ |
|
KeyValues *pkv = m_pClientCombo->GetActiveItemUserData(); |
|
if (!pkv) |
|
return; |
|
|
|
// set the new client |
|
p4->SetActiveClient(pkv->GetString("client")); |
|
|
|
// refresh the UI |
|
Activate(); |
|
m_pRevisionList->RemoveAll(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Cloaks a folder |
|
//----------------------------------------------------------------------------- |
|
void CVP4Dialog::CloakFolder(int iItem) |
|
{ |
|
KeyValues *pkv = m_pFileTree->GetItemData(iItem); |
|
if (!pkv) |
|
return; |
|
|
|
// change the clientspec to remove the folder |
|
p4->RemovePathFromActiveClientspec(pkv->GetString("path")); |
|
|
|
// remove the folder from the tree view |
|
m_pFileTree->RemoveItem(0-iItem, false); |
|
m_pFileTree->InvalidateLayout(); |
|
m_pFileTree->Repaint(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: open for edit |
|
//----------------------------------------------------------------------------- |
|
void CVP4Dialog::OpenFileForEdit(int iItem) |
|
{ |
|
KeyValues *pkv = m_pFileTree->GetItemData(iItem); |
|
if (!pkv) |
|
return; |
|
|
|
p4->OpenFileForEdit(pkv->GetString("path")); |
|
|
|
RefreshChangesList(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: open for delete |
|
//----------------------------------------------------------------------------- |
|
void CVP4Dialog::OpenFileForDelete(int iItem) |
|
{ |
|
KeyValues *pkv = m_pFileTree->GetItemData(iItem); |
|
if (!pkv) |
|
return; |
|
|
|
p4->OpenFileForDelete(pkv->GetString("path")); |
|
|
|
RefreshChangesList(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: updates revision view on a file being selected |
|
//----------------------------------------------------------------------------- |
|
void CVP4Dialog::OnFileSelected() |
|
{ |
|
m_pRevisionList->RemoveAll(); |
|
|
|
// only update if reviews page is visible |
|
if (!m_pRevisionsPage->IsVisible()) |
|
return; |
|
|
|
// update list |
|
int iItem = m_pFileTree->GetFirstSelectedItem(); |
|
if (iItem < 0) |
|
{ |
|
m_pRevisionList->SetEmptyListText("No file or directory currently selected."); |
|
return; |
|
} |
|
|
|
surface()->SetCursor(dc_waitarrow); |
|
|
|
m_pRevisionList->SetEmptyListText("There is no revision history for the selected file."); |
|
|
|
KeyValues *pkv = m_pFileTree->GetItemData(iItem); |
|
|
|
CUtlVector<P4Revision_t> &revisions = p4->GetRevisionList(pkv->GetString("path"), pkv->GetInt("dir")); |
|
|
|
// add all the revisions |
|
for (int i = 0; i < revisions.Count(); i++) |
|
{ |
|
KeyValues *pkv = new KeyValues("node"); |
|
pkv->SetString("user", p4->String( revisions[i].m_sUser )); |
|
pkv->SetInt("change", revisions[i].m_iChange); |
|
|
|
char szDate[256]; |
|
Q_snprintf(szDate, sizeof(szDate), "%d/%d/%d", revisions[i].m_nYear, revisions[i].m_nMonth, revisions[i].m_nDay); |
|
pkv->SetString("date", szDate); |
|
|
|
char szTime[256]; |
|
Q_snprintf(szTime, sizeof(szTime), "%d:%d:%d", revisions[i].m_nHour, revisions[i].m_nMinute, revisions[i].m_nSecond); |
|
pkv->SetString("time", szTime); |
|
|
|
// take just the first line of the description |
|
char *pDesc = revisions[i].m_Description.GetForModify(); |
|
// move to the first non-whitespace |
|
while (*pDesc && (isspace(*pDesc) || iscntrl(*pDesc))) |
|
{ |
|
++pDesc; |
|
} |
|
|
|
char szShortDescription[256]; |
|
Q_strncpy(szShortDescription, pDesc, sizeof(szShortDescription)); |
|
|
|
// truncate to last terminator |
|
char *pTerm = szShortDescription; |
|
while (*pTerm && !iscntrl(*pTerm)) |
|
{ |
|
++pTerm; |
|
} |
|
*pTerm = 0; |
|
|
|
pkv->SetString("description", szShortDescription); |
|
|
|
m_pRevisionList->AddItem(pkv, 0, false, false); |
|
} |
|
}
|
|
|