source-engine/hammer/objectproperties.cpp

1437 lines
36 KiB
C++
Raw Normal View History

2020-04-23 00:56:21 +08:00
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#include "stdafx.h"
#include "hammer.h"
#include "ObjectProperties.h"
#include "ObjectPage.h"
#include "OP_Flags.h"
#include "OP_Groups.h"
#include "OP_Entity.h"
#include "OP_Output.h"
#include "OP_Model.h"
#include "OP_Input.h"
#include "MapDoc.h"
#include "MapView.h"
#include "MapEntity.h"
#include "MapGroup.h"
#include "MapInstance.h"
#include "MapSolid.h"
#include "MapStudioModel.h"
#include "MapWorld.h"
#include "History.h"
#include "GlobalFunctions.h"
#include "Selection.h"
#include "CustomMessages.h"
#include "Camera.h"
#include "Manifest.h"
// memdbgon must be the last include file in a .cpp file!!!
#include <tier0/memdbgon.h>
//
// Layout types for remembering the last layout of the dialog. We could
// also remember this as an array of booleans for which pages were visible.
//
enum LayoutType_t
{
ltZero, // Special enums for initialization
ltNone,
ltSolid, // Enable groups only
ltSolidMulti, // Enable none
ltEntity, // Enable entity, flags, groups
ltEntityMulti, // Enable entity, flags
ltWorld, // Enable entity, flags, groups
ltModelEntity, // Enable entity, flags, groups, model,
ltMulti // Enable none
};
IMPLEMENT_DYNAMIC(CObjectProperties, CPropertySheet)
BEGIN_MESSAGE_MAP(CObjectProperties, CPropertySheet)
//{{AFX_MSG_MAP(CObjectProperties)
ON_WM_KILLFOCUS()
ON_WM_ACTIVATE()
ON_WM_CLOSE()
ON_WM_PAINT()
ON_WM_SIZE()
ON_WM_SHOWWINDOW()
ON_WM_CREATE()
ON_COMMAND(IDOK, OnApply )
ON_COMMAND(ID_APPLY_NOW, OnApply )
ON_COMMAND(IDCANCEL, OnCancel)
ON_COMMAND(IDI_INPUT, OnInputs)
ON_COMMAND(IDI_OUTPUT, OnOutputs)
ON_COMMAND(IDD_EDIT_INSTANCE, OnEditInstance)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
IMPLEMENT_DYNAMIC(editCMapClass, CObject);
IMPLEMENT_DYNAMIC(editCEditGameClass, CObject);
static editCMapClass e_CMapClass;
static editCEditGameClass e_CEditGameClass;
//-----------------------------------------------------------------------------
// Purpose: Constructor.
//-----------------------------------------------------------------------------
CObjectProperties::CObjectProperties(void) :
CPropertySheet()
{
m_bDummy = false;
m_pDummy = NULL;
m_pInputButton = NULL;
m_pOutputButton = NULL;
m_pInstanceButton = NULL;
m_pOrgObjects = NULL;
m_bDataDirty = false;
m_bCanEdit = false;
CreatePages();
}
//-----------------------------------------------------------------------------
// Purpose: Constructor.
// Input : nIDCaption -
// pParentWnd -
// iSelectPage -
//-----------------------------------------------------------------------------
CObjectProperties::CObjectProperties(UINT nIDCaption, CWnd* pParentWnd, UINT iSelectPage)
:CPropertySheet(nIDCaption, pParentWnd, iSelectPage)
{
m_bDummy = false;
m_pDummy = NULL;
m_pInputButton = NULL;
m_pOutputButton = NULL;
m_pInstanceButton = NULL;
m_bCanEdit = false;
CreatePages();
}
//-----------------------------------------------------------------------------
// Purpose: Constructor.
// Input : pszCaption -
// pParentWnd -
// iSelectPage -
//-----------------------------------------------------------------------------
CObjectProperties::CObjectProperties(LPCTSTR pszCaption, CWnd* pParentWnd, UINT iSelectPage)
:CPropertySheet(pszCaption, pParentWnd, iSelectPage)
{
m_bDummy = false;
m_pDummy = NULL;
m_pInputButton = NULL;
m_pOutputButton = NULL;
m_pInstanceButton = NULL;
m_bCanEdit = false;
CreatePages();
}
//-----------------------------------------------------------------------------
// Purpose: Destructor.
//-----------------------------------------------------------------------------
CObjectProperties::~CObjectProperties()
{
delete m_pDummy;
delete m_pEntity;
delete m_pFlags;
delete m_pGroups;
delete m_pOutput;
delete m_pInput;
delete m_pModel;
delete m_pInputButton;
delete m_pOutputButton;
delete m_pInstanceButton;
delete[] m_ppPages;
}
//-----------------------------------------------------------------------------
// Purpose: Creates all possible pages and attaches our object list to them.
// Not all will be used depending on the types of objects being edited.
//-----------------------------------------------------------------------------
void CObjectProperties::CreatePages(void)
{
//VPROF_BUDGET( "CObjectProperties::CreatePages", "Object Properties" );
m_pEntity = new COP_Entity;
m_pEntity->SetObjectList(&m_DstObjects);
m_pFlags = new COP_Flags;
m_pFlags->SetObjectList(&m_DstObjects);
// There are some dependencies between the entity and flags tabs since
// they both edit the spawnflags property.
m_pEntity->SetFlagsPage( m_pFlags );
m_pFlags->SetEntityPage( m_pEntity );
m_pGroups = new COP_Groups;
m_pGroups->SetObjectList(&m_DstObjects);
m_pOutput = new COP_Output;
m_pOutput->SetObjectList(&m_DstObjects);
m_pInput = new COP_Input;
m_pInput->SetObjectList(&m_DstObjects);
m_pModel = new COP_Model;
m_pModel->SetObjectList(&m_DstObjects);
m_pDummy = new CPropertyPage(IDD_OBJPAGE_DUMMY);
m_ppPages = NULL;
m_nPages = 0;
m_pLastActivePage = NULL;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : pType -
//-----------------------------------------------------------------------------
PVOID CObjectProperties::GetEditObject(CRuntimeClass *pType)
{
//VPROF_BUDGET( "CObjectProperties::GetEditObject", "Object Properties" );
if (pType == RUNTIME_CLASS(editCMapClass))
{
return PVOID((CMapClass*)&e_CMapClass);
}
else if (pType == RUNTIME_CLASS(editCEditGameClass))
{
return PVOID((CEditGameClass*)&e_CEditGameClass);
}
Assert(0);
return NULL;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : pobj -
// pType -
//-----------------------------------------------------------------------------
PVOID CObjectProperties::GetEditObjectFromMapObject(CMapClass *pobj, CRuntimeClass *pType)
{
//VPROF_BUDGET( "CObjectProperties::GetEditObjectFromMapObject", "Object Properties" );
if (pType == RUNTIME_CLASS(editCMapClass))
{
return PVOID(pobj);
}
else if (pType == RUNTIME_CLASS(editCEditGameClass))
{
if (pobj->IsMapClass(MAPCLASS_TYPE(CMapEntity)))
{
return PVOID((CEditGameClass*)((CMapEntity*)pobj));
}
if (pobj->IsMapClass(MAPCLASS_TYPE(CMapWorld)))
{
return PVOID((CEditGameClass*)((CMapWorld*)pobj));
}
}
return NULL;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *pobj -
//-----------------------------------------------------------------------------
void CObjectProperties::CopyDataToEditObjects(CMapClass *pobj)
{
//VPROF_BUDGET( "CObjectProperties::CopyDataToEditObjects", "Object Properties" );
//
// All copies here are done without updating object dependencies, because
// we're copying to a place that is outside of the world.
//
e_CMapClass.CopyFrom(pobj, false);
if (pobj->IsMapClass(MAPCLASS_TYPE(CMapEntity)))
{
e_CEditGameClass.CopyFrom((CEditGameClass *)((CMapEntity *)pobj));
}
else if (pobj->IsMapClass(MAPCLASS_TYPE(CMapWorld)))
{
e_CEditGameClass.CopyFrom((CEditGameClass *)((CMapWorld *)pobj));
}
}
//------------------------------------------------------------------------------
// Purpose:
// Input : nState -
//------------------------------------------------------------------------------
void CObjectProperties::SetOutputButtonState(int nState)
{
//VPROF_BUDGET( "CObjectProperties::SetOutputButtonState", "Object Properties" );
if (nState == CONNECTION_GOOD)
{
m_pOutputButton->SetIcon(m_hIconOutputGood);
m_pOutputButton->ShowWindow(SW_SHOW);
m_pOutputButton->Invalidate();
m_pOutputButton->UpdateWindow();
}
else if (nState == CONNECTION_BAD)
{
m_pOutputButton->SetIcon(m_hIconOutputBad);
m_pOutputButton->ShowWindow(SW_SHOW);
m_pOutputButton->Invalidate();
m_pOutputButton->UpdateWindow();
}
else
{
m_pOutputButton->ShowWindow(SW_HIDE);
m_pOutputButton->Invalidate();
m_pOutputButton->UpdateWindow();
}
}
//------------------------------------------------------------------------------
// Purpose:
// Input : nState -
//------------------------------------------------------------------------------
void CObjectProperties::SetInputButtonState(int nState)
{
//VPROF_BUDGET( "CObjectProperties::SetInputButtonState", "Object Properties" );
if (nState == CONNECTION_GOOD)
{
m_pInputButton->SetIcon(m_hIconInputGood);
m_pInputButton->ShowWindow(SW_SHOW);
m_pInputButton->Invalidate();
m_pInputButton->UpdateWindow();
}
else if (nState == CONNECTION_BAD)
{
m_pInputButton->SetIcon(m_hIconInputBad);
m_pInputButton->ShowWindow(SW_SHOW);
m_pInputButton->Invalidate();
m_pInputButton->UpdateWindow();
}
else
{
m_pInputButton->ShowWindow(SW_HIDE);
m_pInputButton->Invalidate();
m_pInputButton->UpdateWindow();
}
}
//------------------------------------------------------------------------------
// Purpose: Set icon being displayed on output button.
//------------------------------------------------------------------------------
void CObjectProperties::UpdateOutputButton(void)
{
//VPROF_BUDGET( "CObjectProperties::UpdateOutputButton", "Object Properties" );
if (!m_pOutputButton)
{
return;
}
bool bHaveConnection = false;
bool bIgnoreHiddenTargets = false;
if ( m_pOutput )
bIgnoreHiddenTargets = !m_pOutput->ShouldShowHiddenTargets();
FOR_EACH_OBJ( m_DstObjects, pos )
{
CMapClass *pObject = m_DstObjects.Element(pos);
if ((pObject != NULL) && (pObject->IsMapClass(MAPCLASS_TYPE(CMapEntity))))
{
CMapEntity *pEntity = (CMapEntity *)pObject;
int nStatus = CEntityConnection::ValidateOutputConnections(pEntity, true, bIgnoreHiddenTargets);
if (nStatus == CONNECTION_BAD)
{
SetOutputButtonState(CONNECTION_BAD);
return;
}
else if (nStatus == CONNECTION_GOOD)
{
bHaveConnection = true;
}
}
}
if (bHaveConnection)
{
SetOutputButtonState(CONNECTION_GOOD);
}
else
{
SetOutputButtonState(CONNECTION_NONE);
}
}
//------------------------------------------------------------------------------
// Purpose: Set icon being displayed on input button.
//------------------------------------------------------------------------------
void CObjectProperties::UpdateInputButton()
{
//VPROF_BUDGET( "CObjectProperties::UpdateInputButton", "Object Properties" );
if (!m_pInputButton)
{
return;
}
bool bHaveConnection = false;
FOR_EACH_OBJ( m_DstObjects, pos )
{
CMapClass *pObject = m_DstObjects.Element(pos);
if ((pObject != NULL) && (pObject->IsMapClass(MAPCLASS_TYPE(CMapEntity))))
{
CMapEntity *pEntity = (CMapEntity *)pObject;
int nStatus = CEntityConnection::ValidateInputConnections(pEntity, false);
if (nStatus == CONNECTION_BAD)
{
SetInputButtonState(CONNECTION_BAD);
return;
}
else if (nStatus == CONNECTION_GOOD)
{
bHaveConnection = true;
}
}
}
if (bHaveConnection)
{
SetInputButtonState(CONNECTION_GOOD);
}
else
{
SetInputButtonState(CONNECTION_NONE);
}
}
//-----------------------------------------------------------------------------
// Purpose: Finds/Creates the buttons.
//-----------------------------------------------------------------------------
void CObjectProperties::CreateButtons(void)
{
//VPROF_BUDGET( "CObjectProperties::CreateButtons", "Object Properties" );
#if 0
// Get the screen location of the hidden apply button(ID_APPLY_NOW)
rect rcButton;
pApplyButton->GetWindowRect( &rcButton );
// Grab, enable and rename the OK button to be Apply
// (Because <enter> only accelerates IDOK)
// and we dont want "OK" (apply+close) functionality
CButton *pOKButton = reinterpret_cast<CButton *>(GetDlgItem(IDOK));
pOKButton->SetWindowTextA("Apply");
pOKButton->EnableWindow();
pOKButton->ShowWindow(SW_SHOWNA);
pOKButton->MoveWindow(&rcButton);
#else
// Grab, enable and DONT show the OK button
// (Because <enter> only accelerates IDOK)
// and we dont want "OK" (apply+close) functionality
CButton *pOKButton = reinterpret_cast<CButton *>(GetDlgItem(IDOK));
pOKButton->EnableWindow();
// Dont show the window, just make it active to forward <enter> -> IDOK -> OnApply
// Grab and enable & show the hidden Apply button too
CButton *pApplyButton = reinterpret_cast<CButton *>(GetDlgItem(ID_APPLY_NOW));
pApplyButton->SetButtonStyle( pApplyButton->GetButtonStyle() | BS_DEFPUSHBUTTON );
pApplyButton->EnableWindow();
pApplyButton->ShowWindow(SW_SHOWNA);
#endif
// Grab and enable & show the hidden Cancel button too
CButton *pCancelButton = reinterpret_cast<CButton *>(GetDlgItem(IDCANCEL));
pCancelButton->EnableWindow();
pCancelButton->ShowWindow(SW_SHOWNA);
//
// Load Icons
//
CWinApp *pApp = AfxGetApp();
m_hIconOutputGood = pApp->LoadIcon(IDI_OUTPUT);
m_hIconOutputBad = pApp->LoadIcon(IDI_OUTPUTBAD);
m_hIconInputGood = pApp->LoadIcon(IDI_INPUT);
m_hIconInputBad = pApp->LoadIcon(IDI_INPUTBAD);
// Create buttons to display connection status icons
CRect rect;
GetWindowRect(&rect);
rect.InflateRect(0, 0, 0, 32);
MoveWindow(&rect, FALSE);
GetClientRect(&rect);
m_pInputButton = new CButton;
m_pInputButton->Create(_T("My button"), WS_CHILD|WS_VISIBLE|BS_ICON|BS_FLAT, CRect(6,rect.bottom - 34,38,rect.bottom - 2), this, IDI_INPUT);
m_pOutputButton = new CButton;
m_pOutputButton->Create(_T("My button"), WS_CHILD|WS_VISIBLE|BS_ICON|BS_FLAT, CRect(40,rect.bottom - 34,72,rect.bottom - 2), this, IDI_OUTPUT);
m_pInstanceButton = new CButton;
m_pInstanceButton->Create( _T( "Edit Instance" ), WS_CHILD|WS_VISIBLE|BS_TEXT, CRect( 6, rect.bottom - 28, 140, rect.bottom - 4 ), this, IDD_EDIT_INSTANCE );
}
//-----------------------------------------------------------------------------
// Purpose: Returns the appropriate page layout for the current object list.
//-----------------------------------------------------------------------------
void CObjectProperties::GetTabsForLayout(LayoutType_t eLayoutType, bool &bEntity, bool &bGroups, bool &bFlags, bool &bModel)
{
//VPROF_BUDGET( "CObjectProperties::GetTabsForLayout", "Object Properties" );
bEntity = bGroups = bFlags = bModel = false;
switch (eLayoutType)
{
case ltEntity:
case ltEntityMulti:
case ltModelEntity:
{
bFlags = true;
bEntity = true;
bGroups = true;
bModel = (eLayoutType == ltModelEntity);
break;
}
case ltSolid:
{
bGroups = true;
break;
}
case ltWorld:
{
bEntity = true;
break;
}
case ltMulti:
case ltSolidMulti:
{
bGroups = true;
break;
}
}
}
//-----------------------------------------------------------------------------
// Purpose: Returns the appropriate page layout for the current object list.
//-----------------------------------------------------------------------------
LayoutType_t CObjectProperties::GetLayout(void)
{
//VPROF_BUDGET( "CObjectProperties::GetLayout", "Object Properties" );
LayoutType_t eLayoutType = ltNone;
if ((m_DstObjects.Count() == 0) || (CMapDoc::GetActiveMapDoc() == NULL))
{
eLayoutType = ltNone;
}
else
{
//
// Figure out which layout to use based on the objects being edited.
//
bool bFirst = true;
MAPCLASSTYPE PrevType = MAPCLASS_TYPE(CMapEntity);
FOR_EACH_OBJ( m_DstObjects, pos )
{
CMapClass *pObject = m_DstObjects.Element(pos);
MAPCLASSTYPE ThisType = pObject->GetType();
if (bFirst)
{
bFirst = false;
if (ThisType == MAPCLASS_TYPE(CMapEntity))
{
CMapEntity *pEntity = (CMapEntity *)pObject;
//
// Only show the model tab when we have a single entity selected that
// has a model helper.
//
if (m_DstObjects.Count() == 1)
{
if (pEntity->GetChildOfType((CMapStudioModel *)NULL))
{
eLayoutType = ltModelEntity;
}
else
{
eLayoutType = ltEntity;
}
}
else
{
eLayoutType = ltEntityMulti;
}
}
else if ((ThisType == MAPCLASS_TYPE(CMapSolid)) ||
(ThisType == MAPCLASS_TYPE(CMapGroup)))
{
eLayoutType = (m_DstObjects.Count() == 1) ? ltSolid : ltSolidMulti;
}
else if (ThisType == MAPCLASS_TYPE(CMapWorld))
{
eLayoutType = ltWorld;
}
}
else if (ThisType != PrevType)
{
eLayoutType = ltMulti;
}
PrevType = ThisType;
}
}
return eLayoutType;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CObjectProperties::RestoreActivePage(void)
{
//VPROF_BUDGET( "CObjectProperties::RestoreActivePage", "Object Properties" );
//
// Try to restore the previously active page. If it is not in the page list
// just activate page zero.
//
bool bPageSet = false;
for (int i = 0; i < m_nPages; i++)
{
if (m_ppPages[i] == m_pLastActivePage)
{
SetActivePage(m_pLastActivePage);
bPageSet = true;
break;
}
}
if (!bPageSet)
{
SetActivePage(0);
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CObjectProperties::SaveActivePage(void)
{
//VPROF_BUDGET( "CObjectProperties::SaveActivePage", "Object Properties" );
CObjectPage *pPage = (CObjectPage *)GetActivePage();
if (pPage != NULL)
{
m_pLastActivePage = pPage;
}
}
//-----------------------------------------------------------------------------
// Purpose: Sets up pages to display based on "m_DstObjects".
// Output : Returns TRUE if the page structure changed, FALSE if not.
//-----------------------------------------------------------------------------
BOOL CObjectProperties::SetupPages(void)
{
//VPROF_BUDGET( "CObjectProperties::SetupPages", "Object Properties" );
static bool bFirstTime = true;
static LayoutType_t eLastLayoutType = ltZero;
static LayoutType_t eLastValidLayoutType = ltZero;
//
// Save the current active page.
//
if ((eLastLayoutType != ltZero) && (eLastLayoutType != ltNone))
{
SaveActivePage();
}
//
// Determine the appropriate layout for the current object list.
//
LayoutType_t eLayoutType = GetLayout();
bool bEntity;
bool bGroups;
bool bFlags;
bool bModel;
GetTabsForLayout(eLayoutType, bEntity, bGroups, bFlags, bModel);
//
// If the layout has not changed, we're done. All the pages are already set up.
//
if (eLayoutType == eLastLayoutType)
{
//
// Try to restore the previously active page. If it has been deleted just
// activate page zero.
//
RestoreActivePage();
return(FALSE);
}
//
// Forget the last active page when the layout changes from one
// valid layout to another (such as from entity to solid).
// Don't reset when switching between model entities and non-model entities,
// because it's annoying to be switched away from the Outputs tab.
//
if ((eLayoutType != ltNone) && (eLayoutType != eLastValidLayoutType) &&
!((eLayoutType == ltEntity) && (eLastValidLayoutType == ltModelEntity)) &&
!((eLayoutType == ltModelEntity) && (eLastValidLayoutType == ltEntity)))
{
m_pLastActivePage = NULL;
eLastValidLayoutType = eLayoutType;
}
eLastLayoutType = eLayoutType;
CObjectPage::s_bRESTRUCTURING = TRUE;
UINT nAddPages = bEntity + bGroups + bFlags + bModel;
// don't want to change focus .. just pages!
CWnd *pActiveWnd = GetActiveWindow();
bool bDisabledraw = false;
if (::IsWindow(m_hWnd) && IsWindowVisible())
{
SetRedraw(FALSE);
bDisabledraw = true;
}
if (!m_bDummy && (nAddPages == 0))
{
AddPage(m_pDummy);
m_bDummy = true;
}
else if (m_bDummy && (nAddPages > 0))
{
RemovePage(m_pDummy);
m_bDummy = false;
}
struct
{
bool m_bIsVisible;
bool m_bWantVisible;
CObjectPage *m_pPage;
} pages[] =
{
{false, bEntity, m_pEntity},
{false, bEntity, m_pOutput},
{false, bEntity, m_pInput},
{false, bModel, m_pModel},
{false, bFlags, m_pFlags},
{false, bGroups, m_pGroups}
};
// First, remove pages that we don't want visible.
// Also store if they're visible.
for ( int i=0; i < ARRAYSIZE( pages ); i++ )
{
pages[i].m_bIsVisible = ( GetPageIndex( pages[i].m_pPage ) != -1 );
if ( pages[i].m_bIsVisible && !pages[i].m_bWantVisible)
{
// It's visible but they don't want it there.
RemovePage( pages[i].m_pPage );
pages[i].m_bIsVisible = false;
}
}
// We're about to add pages, but it'll only add them to the right of what's already there,
// so we must get rid of anything to the right of our leftmost addition.
for ( int i=0; i < ARRAYSIZE( pages ); i++ )
{
if ( !pages[i].m_bIsVisible && pages[i].m_bWantVisible )
{
// Ok, page i needs to be on, so nuke everything to the right of it.
for ( int j=i+1; j < ARRAYSIZE( pages ); j++ )
{
if ( pages[j].m_bIsVisible )
{
RemovePage( pages[j].m_pPage );
pages[j].m_bIsVisible = false;
}
}
break;
}
}
for ( int i=0; i < ARRAYSIZE( pages ); i++ )
{
if ( !pages[i].m_bIsVisible && pages[i].m_bWantVisible )
AddPage( pages[i].m_pPage );
}
//
// Store active pages in our array.
//
if (!m_bDummy)
{
delete[] m_ppPages;
m_nPages = GetPageCount();
m_ppPages = new CObjectPage*[m_nPages];
for (int i = 0; i < m_nPages; i++)
{
m_ppPages[i] = (CObjectPage *)GetPage(i);
m_ppPages[i]->m_bFirstTimeActive = true;
m_ppPages[i]->m_bHasUpdatedData = false;
}
}
CObjectPage::s_bRESTRUCTURING = FALSE;
//VPROF_BUDGET( "CObjectProperties::RestoreActivePage", "Object Properties" );
RestoreActivePage();
//
// Enable redraws if they were disabled above.
//
if (bDisabledraw)
{
SetRedraw(TRUE);
Invalidate(FALSE);
}
// Set button status
UpdateOutputButton();
UpdateInputButton();
if (pActiveWnd != NULL)
{
pActiveWnd->SetActiveWindow();
}
bFirstTime = false;
return TRUE; // pages changed - return true
}
//------------------------------------------------------------------------------
// Purpose: Set object properties dialogue to the Output tab and highlight
// the given item
// Input : pConnection -
//------------------------------------------------------------------------------
void CObjectProperties::SetPageToOutput(CEntityConnection *pConnection)
{
if ( m_bDataDirty )
ReloadData();
SetActivePage(m_pOutput);
m_pOutput->SetSelectedConnection(pConnection);
}
void CObjectProperties::SetPageToInput(CEntityConnection *pConnection)
{
if ( m_bDataDirty )
ReloadData();
SetActivePage(m_pInput);
m_pInput->SetSelectedConnection(pConnection);
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CObjectProperties::SaveData(void)
{
//VPROF_BUDGET( "CObjectProperties::SaveData", "Object Properties" );
//
// Make sure window is visible - don't want to save otherwise.
//
if (!IsWindowVisible())
{
return;
}
// we should never save in a dirty state
if ( m_bDataDirty )
return;
CMapDoc *pDoc = CMapDoc::GetActiveMapDoc();
if (!pDoc || !m_DstObjects.Count() || m_bDummy)
{
return;
}
//
// Transfer all page data to the objects being edited.
//
GetHistory()->MarkUndoPosition( pDoc->GetSelection()->GetList(), "Change Properties");
// Don't keep all the world's children when we're editing the world, because
// that's really slow (and pointless since all we're changing is keyvalues).
bool bKeptWorld = false;
if (m_DstObjects.Count() == 1)
{
CMapClass *pObject = m_DstObjects.Element( 0 );
if ( IsWorldObject(pObject) )
{
GetHistory()->KeepNoChildren(pObject);
bKeptWorld = true;
}
}
if (!bKeptWorld)
{
GetHistory()->Keep(&m_DstObjects);
}
for (int i = 0; i < m_nPages; i++)
{
//
// Pages that have never been shown have no hwnd.
//
if (IsWindow(m_ppPages[i]->m_hWnd) && m_ppPages[i]->m_bHasUpdatedData )
{
m_ppPages[i]->SaveData();
}
}
// Objects may have changed. Update the views.
pDoc->SetModifiedFlag();
}
//-----------------------------------------------------------------------------
// Purpose: Submits the objects to be edited to the property pages so they can
// update their controls.
// Input : iPage - Page index or -1 to update all pages.
//-----------------------------------------------------------------------------
void CObjectProperties::LoadDataForPages(int iPage)
{
//VPROF_BUDGET( "CObjectProperties::LoadDataForPages", "Object Properties" );
if (m_bDummy)
{
return;
}
//
// Determine whether we are editing multiple objects or not.
//
bool bMultiEdit = (m_DstObjects.Count() > 1);
m_bCanEdit = true;
//
// Submit the edit objects to each page one at a time.
//
int nMode = CObjectPage::LoadFirstData;
FOR_EACH_OBJ( m_DstObjects, pos )
{
CMapClass *pobj = m_DstObjects.Element(pos);
if ( pobj->IsEditable() == false )
{
m_bCanEdit = false;
}
if (iPage != -1)
{
//
// Specific page.
//
m_ppPages[iPage]->SetMultiEdit(bMultiEdit);
void *pObject = GetEditObjectFromMapObject(pobj, m_ppPages[iPage]->GetEditObjectRuntimeClass());
if (pObject != NULL)
{
m_ppPages[iPage]->UpdateData(nMode, pObject, m_bCanEdit);
m_ppPages[iPage]->m_bHasUpdatedData = true;
}
}
else for (int i = 0; i < m_nPages; i++)
{
//
// All pages.
//
m_ppPages[i]->SetMultiEdit(bMultiEdit);
// This page hasn't even been shown yet. Don't bother updating its data.
if (m_ppPages[i]->m_bFirstTimeActive)
continue;
void *pObject = GetEditObjectFromMapObject(pobj, m_ppPages[i]->GetEditObjectRuntimeClass());
if (pObject != NULL)
{
m_ppPages[i]->UpdateData(nMode, pObject, m_bCanEdit);
m_ppPages[i]->m_bHasUpdatedData = true;
}
}
nMode = CObjectPage::LoadData;
}
CButton *pApplyButton = reinterpret_cast<CButton *>(GetDlgItem(ID_APPLY_NOW));
pApplyButton->EnableWindow( ( m_bCanEdit ? TRUE : FALSE ) );
//
// Tell the pages that we are done submitting data.
//
if (iPage != -1)
{
//
// Specific page.
//
m_ppPages[iPage]->UpdateData(CObjectPage::LoadFinished, NULL, m_bCanEdit);
}
else for (int i = 0; i < m_nPages; i++)
{
//
// All pages.
//
// This page hasn't even been shown yet. Don't bother updating its data.
if (m_ppPages[i]->m_bFirstTimeActive)
continue;
m_ppPages[i]->UpdateData(CObjectPage::LoadFinished, NULL, m_bCanEdit);
}
//
// Update the input/output icons based on the new data.
//
UpdateOutputButton();
UpdateInputButton();
}
//-----------------------------------------------------------------------------
// Purpose: Adds the object to m_DstObjects unless it is a group, in which case
// it is expanded (recursively) to its children.
//-----------------------------------------------------------------------------
void CObjectProperties::AddObjectExpandGroups(CMapClass *pObject)
{
//VPROF_BUDGET( "CObjectProperties::AddObjectExpandGroups", "Object Properties" );
if (pObject->IsGroup())
{
const CMapObjectList *pChildren = pObject->GetChildren();
FOR_EACH_OBJ( *pChildren, pos )
{
AddObjectExpandGroups( pChildren->Element(pos) );
}
}
else
{
m_DstObjects.AddToTail(pObject);
}
}
//-----------------------------------------------------------------------------
// Purpose: Updates the property page data when the selection contents change.
// Input : pObjects - List of currently selected objects.
//-----------------------------------------------------------------------------
void CObjectProperties::ReloadData()
{
//VPROF_BUDGET( "CObjectProperties::LoadData", "Object Properties" );
CMapDoc *pDoc = CMapDoc::GetActiveMapDoc();
//
// Disable window so it does not gain focus during this operation.
//
EnableWindow(FALSE);
//
// Transfer the objects from pObjects to m_DstObjects, expanding
// groups to their member children.
//
m_DstObjects.RemoveAll();
if ( m_pOrgObjects )
{
FOR_EACH_OBJ( (*m_pOrgObjects), pos )
{
AddObjectExpandGroups( m_pOrgObjects->Element(pos) );
}
}
m_pInstanceButton->ShowWindow( SW_HIDE );
//
// If there is only one object selected, copy its data to our temporary
// edit objects.
//
if (m_DstObjects.Count() == 1)
{
//
// Copy the single destination object's data to our temporary
// edit objects.
//
CMapClass *pobj = m_DstObjects.Element(0);
CopyDataToEditObjects( pobj );
//
// Set the window title to include the object's description.
//
char szTitle[MAX_PATH];
sprintf(szTitle, "Object Properties: %s", pobj->GetDescription());
SetWindowText(szTitle);
CManifestInstance *pManifestInstance = dynamic_cast< CManifestInstance * >( pobj );
if ( pManifestInstance )
{
CManifest *pManifest = CMapDoc::GetManifest();
if ( pManifest )
{
ShowWindow( SW_HIDE );
if ( pDoc )
{
pDoc->UpdateAllViews( MAPVIEW_UPDATE_SELECTION | MAPVIEW_UPDATE_TOOL | MAPVIEW_RENDER_NOW );
}
pManifest->SetPrimaryMap( pManifestInstance->GetManifestMap() );
return;
}
}
CMapEntity *pEntity = dynamic_cast< CMapEntity * >( pobj );
if ( pEntity )
{
if ( strcmpi( pEntity->GetClassName(), "func_instance" ) == 0 )
{
pDoc->PopulateInstance( pEntity );
CMapInstance *pMapInstance = pEntity->GetChildOfType( ( CMapInstance * )NULL );
if ( pMapInstance && pMapInstance->GetInstancedMap() )
{
m_pInstanceButton->ShowWindow( SW_SHOW );
}
}
else if ( strcmpi( pEntity->GetClassName(), "func_instance_parms" ) == 0 )
{
if ( pDoc )
{
pDoc->PopulateInstanceParms( pEntity );
}
}
}
}
else if (m_DstObjects.Count() > 1)
{
SetWindowText("Object Properties: multiple objects");
}
else
{
SetWindowText("Object Properties");
}
SetupPages();
LoadDataForPages();
EnableWindow(TRUE);
m_bDataDirty = false;
}
BOOL CObjectProperties::OnInitDialog()
{
BOOL b = CPropertySheet::OnInitDialog();
SetWindowText("Object Properties");
CreateButtons();
UpdateAnchors( NULL );
return b;
}
void CObjectProperties::UpdateAnchors( CWnd *pPage )
{
if ( !GetSafeHwnd() )
return;
// Anchor stuff.
HWND hTab = NULL;
if ( GetTabControl() )
hTab = GetTabControl()->GetSafeHwnd();
CAnchorDef anchorDefs[] =
{
CAnchorDef( IDOK, k_eSimpleAnchorBottomRight ),
CAnchorDef( ID_APPLY_NOW, k_eSimpleAnchorBottomRight ),
CAnchorDef( IDCANCEL, k_eSimpleAnchorBottomRight ),
CAnchorDef( IDI_INPUT, k_eSimpleAnchorBottomRight ),
CAnchorDef( IDI_OUTPUT, k_eSimpleAnchorBottomRight ),
CAnchorDef( IDD_EDIT_INSTANCE, k_eSimpleAnchorBottomRight ),
CAnchorDef( hTab, k_eSimpleAnchorAllSides ),
CAnchorDef( pPage ? pPage->GetSafeHwnd() : (HWND)NULL, k_eSimpleAnchorAllSides )
};
m_AnchorMgr.Init( GetSafeHwnd(), anchorDefs, ARRAYSIZE( anchorDefs ) );
}
//-----------------------------------------------------------------------------
// Purpose: Closes the object properties dialog, saving changes.
//-----------------------------------------------------------------------------
void CObjectProperties::OnClose(void)
{
//VPROF_BUDGET( "CObjectProperties::OnClose", "Object Properties" );
OnApply();
ShowWindow(SW_HIDE);
}
void CObjectProperties::OnPaint()
{
CPaintDC dc(this); // device context for painting
if ( m_bDataDirty )
ReloadData();
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : bShow -
// nStatus -
//-----------------------------------------------------------------------------
void CObjectProperties::OnShowWindow(BOOL bShow, UINT nStatus)
{
//VPROF_BUDGET( "CObjectProperties::OnShowWindow", "Object Properties" );
// Forget the last active page when the window is hidden or shown.
// FIXME: SetupPages calls SaveActivePage, so we must switch to page 0 here
SetActivePage(0);
m_pLastActivePage = NULL;
CPropertySheet::OnShowWindow(bShow, nStatus);
for (int i = 0; i < m_nPages; i++)
{
m_ppPages[i]->OnShowPropertySheet(bShow, nStatus);
}
}
void CObjectProperties::OnSize( UINT nType, int cx, int cy )
{
m_AnchorMgr.OnSize();
}
//-----------------------------------------------------------------------------
// Purpose: Handles the Apply button.
//-----------------------------------------------------------------------------
void CObjectProperties::OnApply(void)
{
//VPROF_BUDGET( "CObjectProperties::OnApply", "Object Properties" );
if ( !m_bCanEdit )
{
return;
}
CMapDoc *pDoc = CMapDoc::GetActiveMapDoc();
if ( !pDoc )
return;
//We lock visgroup updates here because activities in the object properties dialog can
//change visgroups which, if updated, will change the object properties, causing problems.
//All visgroup updates will occur at the end of this apply operation.
bool bLocked = pDoc->VisGroups_LockUpdates( true );
for (int i = 0; i < m_nPages; i++)
{
if (!m_ppPages[i]->OnApply())
{
return;
}
}
//
// Save and reload the data so the GUI updates.
//
SaveData();
ReloadData();
// Pass along the apply message to the entities.
FOR_EACH_OBJ( m_DstObjects, pos )
{
CMapClass *pObject = m_DstObjects.Element( pos );
if ( pObject )
{
pObject->OnApply();
}
}
if ( bLocked )
{
pDoc->VisGroups_LockUpdates( false );
}
}
//-----------------------------------------------------------------------------
// Purpose: Handles <return> keys sent to OK -> apply instead
//-----------------------------------------------------------------------------
void CObjectProperties::OnOK(void)
{
//VPROF_BUDGET( "CObjectProperties::OnClose", "Object Properties" );
OnApply();
}
//-----------------------------------------------------------------------------
// Purpose: Handles the Apply button.
//-----------------------------------------------------------------------------
void CObjectProperties::OnCancel(void)
{
ShowWindow(SW_HIDE);
// reload original data and overwrite any changes made prio
ReloadData();
}
//-----------------------------------------------------------------------------
// Purpose: Handles the input icon button.
//-----------------------------------------------------------------------------
void CObjectProperties::OnInputs(void)
{
SetActivePage(m_pInput);
}
//-----------------------------------------------------------------------------
// Purpose: Handles the output icon button.
//-----------------------------------------------------------------------------
void CObjectProperties::OnOutputs(void)
{
SetActivePage(m_pOutput);
}
//-----------------------------------------------------------------------------
// Purpose: handle the pushing of the Edit Instance button. Will attempt to
// switch to the map document containing the instance.
// Input : none
// Output : none
//-----------------------------------------------------------------------------
void CObjectProperties::OnEditInstance(void)
{
if (m_DstObjects.Count() == 1)
{
CMapClass *pObj = m_DstObjects.Element( 0 );
CMapEntity *pEntity = dynamic_cast< CMapEntity * >( pObj );
if ( pEntity )
{
EnumChildrenPos_t pos;
CMapClass *pChild = pEntity->GetFirstDescendent( pos );
while ( pChild != NULL )
{
CMapInstance *pMapInstance = dynamic_cast< CMapInstance * >( pChild );
if ( pMapInstance != NULL )
{
OnClose();
pMapInstance->SwitchTo();
}
pChild = pEntity->GetNextDescendent( pos );
}
}
}
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
int CObjectProperties::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
//VPROF_BUDGET( "CObjectProperties::OnCreate", "Object Properties" );
lpCreateStruct->dwExStyle |= WS_EX_TOOLWINDOW;
if (CPropertySheet::OnCreate(lpCreateStruct) == -1)
{
return -1;
}
return 0;
}
void CObjectProperties::SetObjectList(const CMapObjectList *pObjectList)
{
m_pOrgObjects = pObjectList;
MarkDataDirty();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CObjectProperties::MarkDataDirty()
{
//VPROF_BUDGET( "CObjectProperties::RefreshData", "Object Properties" );
// if flag already set, dont touch anything
if ( m_bDataDirty )
return;
for (int i = 0; i < m_nPages; i++)
{
if (m_ppPages[i]->m_hWnd)
{
m_ppPages[i]->RememberState();
m_ppPages[i]->MarkDataDirty();
}
}
Invalidate( false );
m_DstObjects.RemoveAll();
m_bDataDirty = true;
}