summaryrefslogtreecommitdiff
path: root/hammer/objectproperties.cpp
diff options
context:
space:
mode:
authorFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
committerFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
commit3bf9df6b2785fa6d951086978a3e66f49427166a (patch)
tree2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /hammer/objectproperties.cpp
downloadarchived-source-engine-2018-hl2-src-master.tar.xz
archived-source-engine-2018-hl2-src-master.zip
Diffstat (limited to 'hammer/objectproperties.cpp')
-rw-r--r--hammer/objectproperties.cpp1436
1 files changed, 1436 insertions, 0 deletions
diff --git a/hammer/objectproperties.cpp b/hammer/objectproperties.cpp
new file mode 100644
index 0000000..5fb9f3d
--- /dev/null
+++ b/hammer/objectproperties.cpp
@@ -0,0 +1,1436 @@
+//========= 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;
+}
+