mirror of
https://github.com/jmarshall23/DoomRTX.git
synced 2026-08-17 11:00:38 +02:00
Added the ability to hide/show entities in the outliner
This commit is contained in:
@@ -33,6 +33,7 @@ If you have questions concerning this license or the applicable additional terms
|
||||
#include "Radiant.h"
|
||||
#include "XYWnd.h"
|
||||
#include "CamWnd.h"
|
||||
#include "OutlinerDlg.h"
|
||||
#include "splines.h"
|
||||
#include <GL/glu.h>
|
||||
|
||||
@@ -826,6 +827,163 @@ static void CameraNav_DrawOverlay(CCamWnd* cam) {
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
========================
|
||||
Outliner-aware entity target/path lines
|
||||
|
||||
The original DrawPathLines() does not know about the outliner visibility list.
|
||||
When no entity is hidden, keep using the original function so the normal editor
|
||||
style is unchanged. When something is hidden, draw a filtered equivalent that
|
||||
skips links where either endpoint is hidden.
|
||||
========================
|
||||
*/
|
||||
static bool CamWnd_IsTargetLineKey(const char* key) {
|
||||
if (key == NULL || key[0] == '\0') {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (idStr::Icmp(key, "targetname") == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (idStr::Icmp(key, "next") == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return idStr::Icmpn(key, "target", 6) == 0;
|
||||
}
|
||||
|
||||
static bool CamWnd_EntityNameMatches(entity_t* ent, const char* name) {
|
||||
if (ent == NULL || name == NULL || name[0] == '\0') {
|
||||
return false;
|
||||
}
|
||||
|
||||
const char* entName = ValueForKey(ent, "name");
|
||||
if (entName != NULL && entName[0] != '\0' && idStr::Icmp(entName, name) == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const char* targetName = ValueForKey(ent, "targetname");
|
||||
if (targetName != NULL && targetName[0] != '\0' && idStr::Icmp(targetName, name) == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool CamWnd_GetEntityLinePoint(entity_t* ent, idVec3& point) {
|
||||
if (ent == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (GetVectorForKey(ent, "origin", point)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
idBounds bounds;
|
||||
bounds.Clear();
|
||||
bool haveBounds = false;
|
||||
|
||||
for (brush_t* brush = ent->brushes.onext; brush != NULL && brush != &ent->brushes; brush = brush->onext) {
|
||||
bounds.AddPoint(brush->mins);
|
||||
bounds.AddPoint(brush->maxs);
|
||||
haveBounds = true;
|
||||
}
|
||||
|
||||
if (haveBounds) {
|
||||
point = bounds.GetCenter();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void CamWnd_DrawVisibleTargetLine(entity_t* source, entity_t* target) {
|
||||
if (source == NULL || target == NULL || source == target) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Outliner_IsEntityVisible(source) || !Outliner_IsEntityVisible(target)) {
|
||||
return;
|
||||
}
|
||||
|
||||
idVec3 start;
|
||||
idVec3 end;
|
||||
if (!CamWnd_GetEntityLinePoint(source, start) || !CamWnd_GetEntityLinePoint(target, end)) {
|
||||
return;
|
||||
}
|
||||
|
||||
glVertex3fv(start.ToFloatPtr());
|
||||
glVertex3fv(end.ToFloatPtr());
|
||||
}
|
||||
|
||||
static void CamWnd_DrawVisibleTargetLinesForName(entity_t* source, const char* targetName) {
|
||||
if (targetName == NULL || targetName[0] == '\0') {
|
||||
return;
|
||||
}
|
||||
|
||||
if (world_entity != NULL && CamWnd_EntityNameMatches(world_entity, targetName)) {
|
||||
CamWnd_DrawVisibleTargetLine(source, world_entity);
|
||||
}
|
||||
|
||||
if (entities.next == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (entity_t* target = entities.next; target != &entities; target = target->next) {
|
||||
if (CamWnd_EntityNameMatches(target, targetName)) {
|
||||
CamWnd_DrawVisibleTargetLine(source, target);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void CamWnd_DrawVisibleTargetLinesForEntity(entity_t* source) {
|
||||
if (source == NULL || !Outliner_IsEntityVisible(source)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const int numKeys = source->epairs.GetNumKeyVals();
|
||||
for (int keyIndex = 0; keyIndex < numKeys; keyIndex++) {
|
||||
const idKeyValue* kv = source->epairs.GetKeyVal(keyIndex);
|
||||
if (kv == NULL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!CamWnd_IsTargetLineKey(kv->GetKey().c_str())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
CamWnd_DrawVisibleTargetLinesForName(source, kv->GetValue().c_str());
|
||||
}
|
||||
}
|
||||
|
||||
static void CamWnd_DrawVisiblePathLines() {
|
||||
if (!Outliner_HasHiddenEntities()) {
|
||||
DrawPathLines();
|
||||
return;
|
||||
}
|
||||
|
||||
glPushAttrib(GL_CURRENT_BIT);
|
||||
globalImages->BindNull();
|
||||
glDisable(GL_BLEND);
|
||||
glLineWidth(1.0f);
|
||||
glColor3f(1.0f, 0.75f, 0.0f);
|
||||
|
||||
glBegin(GL_LINES);
|
||||
if (world_entity != NULL) {
|
||||
CamWnd_DrawVisibleTargetLinesForEntity(world_entity);
|
||||
}
|
||||
if (entities.next != NULL) {
|
||||
for (entity_t* source = entities.next; source != &entities; source = source->next) {
|
||||
CamWnd_DrawVisibleTargetLinesForEntity(source);
|
||||
}
|
||||
}
|
||||
glEnd();
|
||||
|
||||
glPopAttrib();
|
||||
}
|
||||
|
||||
/*
|
||||
========================
|
||||
Camera window embedded menu bar
|
||||
@@ -1213,7 +1371,15 @@ static void CamWnd_UpdateRuntimeState(CCamWnd* cam, bool renderMode, bool rebuil
|
||||
}
|
||||
|
||||
static bool CamWnd_MenuFilterBrush(CCamWnd* cam, brush_t* brush) {
|
||||
if (!cam || !brush) {
|
||||
if (!brush) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (brush->owner != NULL && !Outliner_IsEntityVisible(brush->owner)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!cam) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -2475,7 +2641,7 @@ void CCamWnd::Cam_Draw() {
|
||||
// draw pointfile
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
|
||||
DrawPathLines();
|
||||
CamWnd_DrawVisiblePathLines();
|
||||
|
||||
if (g_qeglobals.d_pointfile_display_list) {
|
||||
Pointfile_Draw();
|
||||
|
||||
@@ -42,6 +42,144 @@ static const int OUTLINER_FILTER_LABEL_W = 42;
|
||||
static const int OUTLINER_FILTER_H = 22;
|
||||
static const int OUTLINER_GAP = 6;
|
||||
|
||||
|
||||
static CPtrArray s_outlinerHiddenEntities;
|
||||
static CString s_outlinerVisibilityMapName;
|
||||
static entity_t* s_outlinerVisibilityWorldEntity = NULL;
|
||||
static entity_t* s_outlinerVisibilityFirstEntity = NULL;
|
||||
static int s_outlinerVisibilityEntityCount = -1;
|
||||
static int s_outlinerVisibilityMapModified = -999999;
|
||||
static bool s_outlinerVisibilityInitialized = false;
|
||||
|
||||
static const char* Outliner_CurrentMapName() {
|
||||
return currentmap ? currentmap : "";
|
||||
}
|
||||
|
||||
static entity_t* Outliner_FirstMapEntity() {
|
||||
if (entities.next != NULL && entities.next != &entities) {
|
||||
return entities.next;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static bool Outliner_ArrayContainsPtr(const CPtrArray& array, entity_t* ent) {
|
||||
for (int i = 0; i < array.GetSize(); i++) {
|
||||
if ((entity_t*)array.GetAt(i) == ent) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static void Outliner_ClearHiddenEntities() {
|
||||
s_outlinerHiddenEntities.RemoveAll();
|
||||
}
|
||||
|
||||
static void Outliner_SyncVisibilityMapIdentity() {
|
||||
s_outlinerVisibilityInitialized = true;
|
||||
s_outlinerVisibilityWorldEntity = world_entity;
|
||||
s_outlinerVisibilityFirstEntity = Outliner_FirstMapEntity();
|
||||
s_outlinerVisibilityEntityCount = g_qeglobals.d_num_entities;
|
||||
s_outlinerVisibilityMapModified = mapModified;
|
||||
s_outlinerVisibilityMapName = Outliner_CurrentMapName();
|
||||
}
|
||||
|
||||
void Outliner_ResetEntityVisibility() {
|
||||
Outliner_ClearHiddenEntities();
|
||||
Outliner_SyncVisibilityMapIdentity();
|
||||
}
|
||||
|
||||
static void Outliner_EnsureVisibilityMap() {
|
||||
const char* mapName = Outliner_CurrentMapName();
|
||||
const entity_t* firstEntity = Outliner_FirstMapEntity();
|
||||
bool resetVisibility = false;
|
||||
|
||||
if (!s_outlinerVisibilityInitialized) {
|
||||
resetVisibility = true;
|
||||
}
|
||||
else if (s_outlinerVisibilityWorldEntity != world_entity) {
|
||||
resetVisibility = true;
|
||||
}
|
||||
else if (s_outlinerVisibilityMapName.CompareNoCase(mapName) != 0) {
|
||||
resetVisibility = true;
|
||||
}
|
||||
else if (mapModified < s_outlinerVisibilityMapModified) {
|
||||
// Loading or creating a map normally resets mapModified. Treat that as
|
||||
// a new editor visibility session so every entity starts checked.
|
||||
resetVisibility = true;
|
||||
}
|
||||
else if (mapModified <= 0 && s_outlinerVisibilityFirstEntity != firstEntity) {
|
||||
// Some map/new-map paths reuse the same worldspawn pointer and leave
|
||||
// currentmap empty. If the list head changed while the map is clean,
|
||||
// this is almost certainly a freshly loaded/new map, not an edit.
|
||||
resetVisibility = true;
|
||||
}
|
||||
|
||||
if (resetVisibility) {
|
||||
Outliner_ClearHiddenEntities();
|
||||
}
|
||||
|
||||
Outliner_SyncVisibilityMapIdentity();
|
||||
}
|
||||
|
||||
static int Outliner_FindHiddenEntity(entity_t* ent) {
|
||||
for (int i = 0; i < s_outlinerHiddenEntities.GetSize(); i++) {
|
||||
if ((entity_t*)s_outlinerHiddenEntities.GetAt(i) == ent) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void Outliner_PruneHiddenEntities(const CPtrArray& currentEntities) {
|
||||
Outliner_EnsureVisibilityMap();
|
||||
|
||||
for (int i = s_outlinerHiddenEntities.GetSize() - 1; i >= 0; i--) {
|
||||
entity_t* ent = (entity_t*)s_outlinerHiddenEntities.GetAt(i);
|
||||
if (!Outliner_ArrayContainsPtr(currentEntities, ent)) {
|
||||
s_outlinerHiddenEntities.RemoveAt(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Outliner_IsEntityVisible(entity_t* ent) {
|
||||
if (ent == NULL) {
|
||||
return true;
|
||||
}
|
||||
|
||||
Outliner_EnsureVisibilityMap();
|
||||
return Outliner_FindHiddenEntity(ent) < 0;
|
||||
}
|
||||
|
||||
bool Outliner_HasHiddenEntities() {
|
||||
Outliner_EnsureVisibilityMap();
|
||||
return s_outlinerHiddenEntities.GetSize() > 0;
|
||||
}
|
||||
|
||||
bool Outliner_IsEntityLinkVisible(entity_t* source, entity_t* target) {
|
||||
return Outliner_IsEntityVisible(source) && Outliner_IsEntityVisible(target);
|
||||
}
|
||||
|
||||
void Outliner_SetEntityVisible(entity_t* ent, bool visible) {
|
||||
if (ent == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
Outliner_EnsureVisibilityMap();
|
||||
|
||||
const int index = Outliner_FindHiddenEntity(ent);
|
||||
if (visible) {
|
||||
if (index >= 0) {
|
||||
s_outlinerHiddenEntities.RemoveAt(index);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (index < 0) {
|
||||
s_outlinerHiddenEntities.Add(ent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BEGIN_MESSAGE_MAP(COutlinerDlg, CWnd)
|
||||
ON_WM_CREATE()
|
||||
ON_WM_SIZE()
|
||||
@@ -53,6 +191,8 @@ BEGIN_MESSAGE_MAP(COutlinerDlg, CWnd)
|
||||
ON_EN_CHANGE(COutlinerDlg::IDC_OUTLINER_FILTER, OnFilterChanged)
|
||||
ON_NOTIFY(NM_DBLCLK, COutlinerDlg::IDC_OUTLINER_TREE, OnTreeDblClick)
|
||||
ON_NOTIFY(NM_RCLICK, COutlinerDlg::IDC_OUTLINER_TREE, OnTreeRightClick)
|
||||
ON_NOTIFY(NM_CLICK, COutlinerDlg::IDC_OUTLINER_TREE, OnTreeClick)
|
||||
ON_MESSAGE(COutlinerDlg::WM_OUTLINER_APPLY_CHECK, OnApplyTreeCheck)
|
||||
END_MESSAGE_MAP()
|
||||
|
||||
COutlinerDlg::COutlinerDlg() {
|
||||
@@ -133,7 +273,7 @@ int COutlinerDlg::OnCreate(LPCREATESTRUCT lpCreateStruct) {
|
||||
m_tree.Create(
|
||||
WS_CHILD | WS_VISIBLE | WS_TABSTOP |
|
||||
TVS_HASBUTTONS | TVS_HASLINES | TVS_LINESATROOT |
|
||||
TVS_SHOWSELALWAYS | TVS_DISABLEDRAGDROP,
|
||||
TVS_SHOWSELALWAYS | TVS_DISABLEDRAGDROP | TVS_CHECKBOXES,
|
||||
CRect(0, 0, 10, 10),
|
||||
this,
|
||||
IDC_OUTLINER_TREE
|
||||
@@ -299,6 +439,15 @@ BOOL COutlinerDlg::PreTranslateMessage(MSG* pMsg) {
|
||||
OpenEntityProperties(SelectedEntity());
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
if (pMsg->wParam == VK_SPACE && pMsg->hwnd == m_tree.GetSafeHwnd()) {
|
||||
entity_t* ent = SelectedEntity();
|
||||
if (ent != NULL) {
|
||||
ToggleEntityVisibility(ent, false);
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
if (pMsg->wParam == VK_F5) {
|
||||
Refresh(true);
|
||||
return TRUE;
|
||||
@@ -349,7 +498,30 @@ void COutlinerDlg::Refresh(bool force) {
|
||||
|
||||
entity_t* oldSelection = SelectedEntity();
|
||||
|
||||
const char* mapName = Outliner_CurrentMapName();
|
||||
const bool hadPreviousRefresh = (m_lastMapModified != -999999);
|
||||
const bool mapIdentityChanged =
|
||||
(!s_outlinerVisibilityInitialized) ||
|
||||
(m_lastWorldEntity != world_entity) ||
|
||||
(m_lastMapName.CompareNoCase(mapName) != 0) ||
|
||||
(hadPreviousRefresh && mapModified < m_lastMapModified) ||
|
||||
(hadPreviousRefresh && mapModified <= 0 && s_outlinerVisibilityFirstEntity != Outliner_FirstMapEntity());
|
||||
|
||||
// File->New in this editor can leave the same empty map name around.
|
||||
// When that happens, make the new map start from a clean all-visible state
|
||||
// instead of inheriting hidden pointers from the previous contents.
|
||||
const bool likelyNewBlankMap =
|
||||
(s_outlinerHiddenEntities.GetSize() > 0) &&
|
||||
(m_lastEntityCount > 1) &&
|
||||
(g_qeglobals.d_num_entities <= 1) &&
|
||||
(mapName[0] == '\0');
|
||||
|
||||
if (mapIdentityChanged || likelyNewBlankMap) {
|
||||
Outliner_ResetEntityVisibility();
|
||||
}
|
||||
|
||||
BuildGraph();
|
||||
Outliner_PruneHiddenEntities(m_entities);
|
||||
|
||||
m_tree.SetRedraw(FALSE);
|
||||
m_tree.DeleteAllItems();
|
||||
@@ -383,6 +555,7 @@ void COutlinerDlg::Refresh(bool force) {
|
||||
|
||||
if (loopGroup == NULL) {
|
||||
loopGroup = m_tree.InsertItem("Target loops / no root", TVI_ROOT, TVI_LAST);
|
||||
m_tree.SetItemState(loopGroup, 0, TVIS_STATEIMAGEMASK);
|
||||
insertedAny = true;
|
||||
}
|
||||
|
||||
@@ -395,10 +568,14 @@ void COutlinerDlg::Refresh(bool force) {
|
||||
}
|
||||
|
||||
if (!insertedAny) {
|
||||
HTREEITEM emptyItem = NULL;
|
||||
if (m_filterText.IsEmpty()) {
|
||||
m_tree.InsertItem("<no entities>", TVI_ROOT, TVI_LAST);
|
||||
emptyItem = m_tree.InsertItem("<no entities>", TVI_ROOT, TVI_LAST);
|
||||
} else {
|
||||
m_tree.InsertItem("<no matching entities>", TVI_ROOT, TVI_LAST);
|
||||
emptyItem = m_tree.InsertItem("<no matching entities>", TVI_ROOT, TVI_LAST);
|
||||
}
|
||||
if (emptyItem != NULL) {
|
||||
m_tree.SetItemState(emptyItem, 0, TVIS_STATEIMAGEMASK);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -425,7 +602,7 @@ void COutlinerDlg::Refresh(bool force) {
|
||||
m_lastWorldEntity = world_entity;
|
||||
m_lastEntityCount = g_qeglobals.d_num_entities;
|
||||
m_lastMapModified = mapModified;
|
||||
m_lastMapName = currentmap;
|
||||
m_lastMapName = Outliner_CurrentMapName();
|
||||
}
|
||||
|
||||
void COutlinerDlg::BuildGraph() {
|
||||
@@ -565,6 +742,10 @@ bool COutlinerDlg::IsTargetKey(const char* key) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (idStr::Icmp(key, "next") == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return idStr::Icmpn(key, "target", 6) == 0;
|
||||
}
|
||||
|
||||
@@ -723,6 +904,7 @@ HTREEITEM COutlinerDlg::InsertEntityRecursive(entity_t* ent, HTREEITEM parent, C
|
||||
|
||||
HTREEITEM item = m_tree.InsertItem(label, parent, TVI_LAST);
|
||||
m_tree.SetItemData(item, (DWORD_PTR)AllocItemData(ent, cycleRef));
|
||||
m_tree.SetCheck(item, Outliner_IsEntityVisible(ent) ? TRUE : FALSE);
|
||||
|
||||
const int nodeIndex = FindNode(ent);
|
||||
if (nodeIndex >= 0) {
|
||||
@@ -787,12 +969,170 @@ void COutlinerDlg::ExpandAll(HTREEITEM start) {
|
||||
}
|
||||
}
|
||||
|
||||
void COutlinerDlg::OnTreeClick(NMHDR* pNMHDR, LRESULT* pResult) {
|
||||
CPoint point;
|
||||
::GetCursorPos(&point);
|
||||
m_tree.ScreenToClient(&point);
|
||||
|
||||
UINT flags = 0;
|
||||
HTREEITEM item = m_tree.HitTest(point, &flags);
|
||||
|
||||
if (item != NULL && (flags & TVHT_ONITEMSTATEICON) && EntityFromItem(item) != NULL) {
|
||||
m_tree.SelectItem(item);
|
||||
|
||||
// Let the tree control finish changing the checkbox before we read it.
|
||||
PostMessage(WM_OUTLINER_APPLY_CHECK, (WPARAM)item, 0);
|
||||
}
|
||||
|
||||
*pResult = 0;
|
||||
}
|
||||
|
||||
LRESULT COutlinerDlg::OnApplyTreeCheck(WPARAM wParam, LPARAM lParam) {
|
||||
if (!m_tree.GetSafeHwnd()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
HTREEITEM item = (HTREEITEM)wParam;
|
||||
if (item == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
entity_t* ent = EntityFromItem(item);
|
||||
if (ent == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const bool visible = m_tree.GetCheck(item) != FALSE;
|
||||
Outliner_SetEntityVisible(ent, visible);
|
||||
|
||||
ApplyVisibilityChange();
|
||||
return 0;
|
||||
}
|
||||
|
||||
void COutlinerDlg::CollectEntityAndChildren(entity_t* ent, CPtrArray& out, CPtrArray& stack) const {
|
||||
if (ent == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (ArrayContains(out, ent)) {
|
||||
return;
|
||||
}
|
||||
|
||||
out.Add(ent);
|
||||
|
||||
if (ArrayContains(stack, ent)) {
|
||||
return;
|
||||
}
|
||||
|
||||
stack.Add(ent);
|
||||
|
||||
for (int i = 0; i < m_edges.GetSize(); i++) {
|
||||
const outlinerEdge_t& edge = m_edges.GetAt(i);
|
||||
if (edge.source != ent) {
|
||||
continue;
|
||||
}
|
||||
|
||||
CollectEntityAndChildren(edge.target, out, stack);
|
||||
}
|
||||
|
||||
stack.RemoveAt(stack.GetSize() - 1);
|
||||
}
|
||||
|
||||
void COutlinerDlg::SetEntityVisibility(entity_t* ent, bool visible, bool includeChildren) {
|
||||
if (ent == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (includeChildren) {
|
||||
CPtrArray entitiesToChange;
|
||||
CPtrArray stack;
|
||||
|
||||
CollectEntityAndChildren(ent, entitiesToChange, stack);
|
||||
|
||||
for (int i = 0; i < entitiesToChange.GetSize(); i++) {
|
||||
Outliner_SetEntityVisible((entity_t*)entitiesToChange.GetAt(i), visible);
|
||||
}
|
||||
}
|
||||
else {
|
||||
Outliner_SetEntityVisible(ent, visible);
|
||||
}
|
||||
|
||||
ApplyVisibilityChange();
|
||||
}
|
||||
|
||||
void COutlinerDlg::ToggleEntityVisibility(entity_t* ent, bool includeChildren) {
|
||||
if (ent == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
const bool newVisible = !Outliner_IsEntityVisible(ent);
|
||||
SetEntityVisibility(ent, newVisible, includeChildren);
|
||||
}
|
||||
|
||||
void COutlinerDlg::ShowOnlyEntityAndChildren(entity_t* ent) {
|
||||
if (ent == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
CPtrArray keepVisible;
|
||||
CPtrArray stack;
|
||||
|
||||
CollectEntityAndChildren(ent, keepVisible, stack);
|
||||
|
||||
for (int i = 0; i < m_entities.GetSize(); i++) {
|
||||
entity_t* candidate = (entity_t*)m_entities.GetAt(i);
|
||||
if (i == 0)
|
||||
{
|
||||
Outliner_SetEntityVisible(candidate, true);
|
||||
continue;
|
||||
}
|
||||
Outliner_SetEntityVisible(candidate, ArrayContains(keepVisible, candidate));
|
||||
}
|
||||
|
||||
ApplyVisibilityChange();
|
||||
}
|
||||
|
||||
void COutlinerDlg::ShowAllEntities() {
|
||||
Outliner_ResetEntityVisibility();
|
||||
ApplyVisibilityChange();
|
||||
}
|
||||
|
||||
void COutlinerDlg::UpdateAllCheckmarks(HTREEITEM start) {
|
||||
HTREEITEM item = start;
|
||||
while (item != NULL) {
|
||||
entity_t* ent = EntityFromItem(item);
|
||||
if (ent != NULL) {
|
||||
m_tree.SetCheck(item, Outliner_IsEntityVisible(ent) ? TRUE : FALSE);
|
||||
}
|
||||
|
||||
HTREEITEM child = m_tree.GetChildItem(item);
|
||||
if (child != NULL) {
|
||||
UpdateAllCheckmarks(child);
|
||||
}
|
||||
|
||||
item = m_tree.GetNextSiblingItem(item);
|
||||
}
|
||||
}
|
||||
|
||||
void COutlinerDlg::ApplyVisibilityChange() {
|
||||
if (m_tree.GetSafeHwnd()) {
|
||||
m_tree.SetRedraw(FALSE);
|
||||
UpdateAllCheckmarks(m_tree.GetRootItem());
|
||||
m_tree.SetRedraw(TRUE);
|
||||
m_tree.Invalidate();
|
||||
}
|
||||
|
||||
Sys_UpdateWindows(W_ALL);
|
||||
}
|
||||
|
||||
void COutlinerDlg::OnTreeDblClick(NMHDR* pNMHDR, LRESULT* pResult) {
|
||||
OpenEntityProperties(SelectedEntity());
|
||||
*pResult = 0;
|
||||
}
|
||||
|
||||
void COutlinerDlg::OnTreeRightClick(NMHDR* pNMHDR, LRESULT* pResult) {
|
||||
RefreshIfNeeded();
|
||||
|
||||
CPoint screenPoint;
|
||||
::GetCursorPos(&screenPoint);
|
||||
|
||||
@@ -806,12 +1146,23 @@ void COutlinerDlg::OnTreeRightClick(NMHDR* pNMHDR, LRESULT* pResult) {
|
||||
}
|
||||
|
||||
entity_t* ent = SelectedEntity();
|
||||
const bool entVisible = Outliner_IsEntityVisible(ent);
|
||||
|
||||
CString toggleSelfText;
|
||||
CString toggleChildrenText;
|
||||
toggleSelfText.Format("%s this entity", entVisible ? "Hide" : "Show");
|
||||
toggleChildrenText.Format("%s this entity + children", entVisible ? "Hide" : "Show");
|
||||
|
||||
CMenu menu;
|
||||
menu.CreatePopupMenu();
|
||||
menu.AppendMenu(MF_STRING | (ent ? 0 : MF_GRAYED), ID_OUTLINER_OPEN_PROPERTIES, "Open entity properties");
|
||||
menu.AppendMenu(MF_STRING | (ent ? 0 : MF_GRAYED), ID_OUTLINER_MOVE_TO_ENTITY, "Move to entity");
|
||||
menu.AppendMenu(MF_SEPARATOR);
|
||||
menu.AppendMenu(MF_STRING | (ent ? 0 : MF_GRAYED), ID_OUTLINER_TOGGLE_VISIBILITY, toggleSelfText);
|
||||
menu.AppendMenu(MF_STRING | (ent ? 0 : MF_GRAYED), ID_OUTLINER_TOGGLE_VISIBILITY_CHILDREN, toggleChildrenText);
|
||||
menu.AppendMenu(MF_STRING | (ent ? 0 : MF_GRAYED), ID_OUTLINER_SHOW_ONLY_THIS_AND_CHILDREN, "Show only this entity + children");
|
||||
menu.AppendMenu(MF_STRING, ID_OUTLINER_SHOW_ALL, "Show all entities");
|
||||
menu.AppendMenu(MF_SEPARATOR);
|
||||
menu.AppendMenu(MF_STRING, ID_OUTLINER_REFRESH, "Refresh");
|
||||
|
||||
const int command = menu.TrackPopupMenu(
|
||||
@@ -828,6 +1179,18 @@ void COutlinerDlg::OnTreeRightClick(NMHDR* pNMHDR, LRESULT* pResult) {
|
||||
case ID_OUTLINER_MOVE_TO_ENTITY:
|
||||
MoveToEntity(ent);
|
||||
break;
|
||||
case ID_OUTLINER_TOGGLE_VISIBILITY:
|
||||
ToggleEntityVisibility(ent, false);
|
||||
break;
|
||||
case ID_OUTLINER_TOGGLE_VISIBILITY_CHILDREN:
|
||||
ToggleEntityVisibility(ent, true);
|
||||
break;
|
||||
case ID_OUTLINER_SHOW_ONLY_THIS_AND_CHILDREN:
|
||||
ShowOnlyEntityAndChildren(ent);
|
||||
break;
|
||||
case ID_OUTLINER_SHOW_ALL:
|
||||
ShowAllEntities();
|
||||
break;
|
||||
case ID_OUTLINER_REFRESH:
|
||||
Refresh(true);
|
||||
break;
|
||||
|
||||
@@ -43,7 +43,12 @@ public:
|
||||
ID_OUTLINER_OPEN_PROPERTIES = 0x7310,
|
||||
ID_OUTLINER_MOVE_TO_ENTITY = 0x7311,
|
||||
ID_OUTLINER_REFRESH = 0x7312,
|
||||
OUTLINER_REFRESH_TIMER = 0x7320
|
||||
ID_OUTLINER_TOGGLE_VISIBILITY = 0x7313,
|
||||
ID_OUTLINER_TOGGLE_VISIBILITY_CHILDREN = 0x7314,
|
||||
ID_OUTLINER_SHOW_ONLY_THIS_AND_CHILDREN = 0x7315,
|
||||
ID_OUTLINER_SHOW_ALL = 0x7316,
|
||||
OUTLINER_REFRESH_TIMER = 0x7320,
|
||||
WM_OUTLINER_APPLY_CHECK = WM_APP + 0x321
|
||||
};
|
||||
|
||||
COutlinerDlg();
|
||||
@@ -71,6 +76,8 @@ protected:
|
||||
afx_msg void OnFilterChanged();
|
||||
void OnTreeDblClick(NMHDR* pNMHDR, LRESULT* pResult);
|
||||
void OnTreeRightClick(NMHDR* pNMHDR, LRESULT* pResult);
|
||||
void OnTreeClick(NMHDR* pNMHDR, LRESULT* pResult);
|
||||
afx_msg LRESULT OnApplyTreeCheck(WPARAM wParam, LPARAM lParam);
|
||||
|
||||
virtual BOOL PreTranslateMessage(MSG* pMsg);
|
||||
|
||||
@@ -128,7 +135,21 @@ private:
|
||||
HTREEITEM FindTreeItemForEntity(entity_t* ent, HTREEITEM start) const;
|
||||
void ExpandAll(HTREEITEM start);
|
||||
|
||||
void CollectEntityAndChildren(entity_t* ent, CPtrArray& out, CPtrArray& stack) const;
|
||||
void SetEntityVisibility(entity_t* ent, bool visible, bool includeChildren);
|
||||
void ToggleEntityVisibility(entity_t* ent, bool includeChildren);
|
||||
void ShowOnlyEntityAndChildren(entity_t* ent);
|
||||
void ShowAllEntities();
|
||||
void UpdateAllCheckmarks(HTREEITEM start);
|
||||
void ApplyVisibilityChange();
|
||||
|
||||
bool NeedsRefresh() const;
|
||||
bool GetEntityCenter(entity_t* ent, idVec3& center) const;
|
||||
void ApplyFont();
|
||||
};
|
||||
|
||||
bool Outliner_IsEntityVisible(entity_t* ent);
|
||||
void Outliner_SetEntityVisible(entity_t* ent, bool visible);
|
||||
void Outliner_ResetEntityVisibility();
|
||||
bool Outliner_HasHiddenEntities();
|
||||
bool Outliner_IsEntityLinkVisible(entity_t* source, entity_t* target);
|
||||
|
||||
Reference in New Issue
Block a user