mirror of
https://github.com/jmarshall23/DoomRTX.git
synced 2026-08-17 19:23:51 +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 "Radiant.h"
|
||||||
#include "XYWnd.h"
|
#include "XYWnd.h"
|
||||||
#include "CamWnd.h"
|
#include "CamWnd.h"
|
||||||
|
#include "OutlinerDlg.h"
|
||||||
#include "splines.h"
|
#include "splines.h"
|
||||||
#include <GL/glu.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
|
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) {
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2475,7 +2641,7 @@ void CCamWnd::Cam_Draw() {
|
|||||||
// draw pointfile
|
// draw pointfile
|
||||||
glEnable(GL_DEPTH_TEST);
|
glEnable(GL_DEPTH_TEST);
|
||||||
|
|
||||||
DrawPathLines();
|
CamWnd_DrawVisiblePathLines();
|
||||||
|
|
||||||
if (g_qeglobals.d_pointfile_display_list) {
|
if (g_qeglobals.d_pointfile_display_list) {
|
||||||
Pointfile_Draw();
|
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_FILTER_H = 22;
|
||||||
static const int OUTLINER_GAP = 6;
|
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)
|
BEGIN_MESSAGE_MAP(COutlinerDlg, CWnd)
|
||||||
ON_WM_CREATE()
|
ON_WM_CREATE()
|
||||||
ON_WM_SIZE()
|
ON_WM_SIZE()
|
||||||
@@ -53,6 +191,8 @@ BEGIN_MESSAGE_MAP(COutlinerDlg, CWnd)
|
|||||||
ON_EN_CHANGE(COutlinerDlg::IDC_OUTLINER_FILTER, OnFilterChanged)
|
ON_EN_CHANGE(COutlinerDlg::IDC_OUTLINER_FILTER, OnFilterChanged)
|
||||||
ON_NOTIFY(NM_DBLCLK, COutlinerDlg::IDC_OUTLINER_TREE, OnTreeDblClick)
|
ON_NOTIFY(NM_DBLCLK, COutlinerDlg::IDC_OUTLINER_TREE, OnTreeDblClick)
|
||||||
ON_NOTIFY(NM_RCLICK, COutlinerDlg::IDC_OUTLINER_TREE, OnTreeRightClick)
|
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()
|
END_MESSAGE_MAP()
|
||||||
|
|
||||||
COutlinerDlg::COutlinerDlg() {
|
COutlinerDlg::COutlinerDlg() {
|
||||||
@@ -133,7 +273,7 @@ int COutlinerDlg::OnCreate(LPCREATESTRUCT lpCreateStruct) {
|
|||||||
m_tree.Create(
|
m_tree.Create(
|
||||||
WS_CHILD | WS_VISIBLE | WS_TABSTOP |
|
WS_CHILD | WS_VISIBLE | WS_TABSTOP |
|
||||||
TVS_HASBUTTONS | TVS_HASLINES | TVS_LINESATROOT |
|
TVS_HASBUTTONS | TVS_HASLINES | TVS_LINESATROOT |
|
||||||
TVS_SHOWSELALWAYS | TVS_DISABLEDRAGDROP,
|
TVS_SHOWSELALWAYS | TVS_DISABLEDRAGDROP | TVS_CHECKBOXES,
|
||||||
CRect(0, 0, 10, 10),
|
CRect(0, 0, 10, 10),
|
||||||
this,
|
this,
|
||||||
IDC_OUTLINER_TREE
|
IDC_OUTLINER_TREE
|
||||||
@@ -299,6 +439,15 @@ BOOL COutlinerDlg::PreTranslateMessage(MSG* pMsg) {
|
|||||||
OpenEntityProperties(SelectedEntity());
|
OpenEntityProperties(SelectedEntity());
|
||||||
return TRUE;
|
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) {
|
if (pMsg->wParam == VK_F5) {
|
||||||
Refresh(true);
|
Refresh(true);
|
||||||
return TRUE;
|
return TRUE;
|
||||||
@@ -349,7 +498,30 @@ void COutlinerDlg::Refresh(bool force) {
|
|||||||
|
|
||||||
entity_t* oldSelection = SelectedEntity();
|
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();
|
BuildGraph();
|
||||||
|
Outliner_PruneHiddenEntities(m_entities);
|
||||||
|
|
||||||
m_tree.SetRedraw(FALSE);
|
m_tree.SetRedraw(FALSE);
|
||||||
m_tree.DeleteAllItems();
|
m_tree.DeleteAllItems();
|
||||||
@@ -383,6 +555,7 @@ void COutlinerDlg::Refresh(bool force) {
|
|||||||
|
|
||||||
if (loopGroup == NULL) {
|
if (loopGroup == NULL) {
|
||||||
loopGroup = m_tree.InsertItem("Target loops / no root", TVI_ROOT, TVI_LAST);
|
loopGroup = m_tree.InsertItem("Target loops / no root", TVI_ROOT, TVI_LAST);
|
||||||
|
m_tree.SetItemState(loopGroup, 0, TVIS_STATEIMAGEMASK);
|
||||||
insertedAny = true;
|
insertedAny = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -395,10 +568,14 @@ void COutlinerDlg::Refresh(bool force) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!insertedAny) {
|
if (!insertedAny) {
|
||||||
|
HTREEITEM emptyItem = NULL;
|
||||||
if (m_filterText.IsEmpty()) {
|
if (m_filterText.IsEmpty()) {
|
||||||
m_tree.InsertItem("<no entities>", TVI_ROOT, TVI_LAST);
|
emptyItem = m_tree.InsertItem("<no entities>", TVI_ROOT, TVI_LAST);
|
||||||
} else {
|
} 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_lastWorldEntity = world_entity;
|
||||||
m_lastEntityCount = g_qeglobals.d_num_entities;
|
m_lastEntityCount = g_qeglobals.d_num_entities;
|
||||||
m_lastMapModified = mapModified;
|
m_lastMapModified = mapModified;
|
||||||
m_lastMapName = currentmap;
|
m_lastMapName = Outliner_CurrentMapName();
|
||||||
}
|
}
|
||||||
|
|
||||||
void COutlinerDlg::BuildGraph() {
|
void COutlinerDlg::BuildGraph() {
|
||||||
@@ -565,6 +742,10 @@ bool COutlinerDlg::IsTargetKey(const char* key) const {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (idStr::Icmp(key, "next") == 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return idStr::Icmpn(key, "target", 6) == 0;
|
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);
|
HTREEITEM item = m_tree.InsertItem(label, parent, TVI_LAST);
|
||||||
m_tree.SetItemData(item, (DWORD_PTR)AllocItemData(ent, cycleRef));
|
m_tree.SetItemData(item, (DWORD_PTR)AllocItemData(ent, cycleRef));
|
||||||
|
m_tree.SetCheck(item, Outliner_IsEntityVisible(ent) ? TRUE : FALSE);
|
||||||
|
|
||||||
const int nodeIndex = FindNode(ent);
|
const int nodeIndex = FindNode(ent);
|
||||||
if (nodeIndex >= 0) {
|
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) {
|
void COutlinerDlg::OnTreeDblClick(NMHDR* pNMHDR, LRESULT* pResult) {
|
||||||
OpenEntityProperties(SelectedEntity());
|
OpenEntityProperties(SelectedEntity());
|
||||||
*pResult = 0;
|
*pResult = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void COutlinerDlg::OnTreeRightClick(NMHDR* pNMHDR, LRESULT* pResult) {
|
void COutlinerDlg::OnTreeRightClick(NMHDR* pNMHDR, LRESULT* pResult) {
|
||||||
|
RefreshIfNeeded();
|
||||||
|
|
||||||
CPoint screenPoint;
|
CPoint screenPoint;
|
||||||
::GetCursorPos(&screenPoint);
|
::GetCursorPos(&screenPoint);
|
||||||
|
|
||||||
@@ -806,12 +1146,23 @@ void COutlinerDlg::OnTreeRightClick(NMHDR* pNMHDR, LRESULT* pResult) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
entity_t* ent = SelectedEntity();
|
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;
|
CMenu menu;
|
||||||
menu.CreatePopupMenu();
|
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_OPEN_PROPERTIES, "Open entity properties");
|
||||||
menu.AppendMenu(MF_STRING | (ent ? 0 : MF_GRAYED), ID_OUTLINER_MOVE_TO_ENTITY, "Move to entity");
|
menu.AppendMenu(MF_STRING | (ent ? 0 : MF_GRAYED), ID_OUTLINER_MOVE_TO_ENTITY, "Move to entity");
|
||||||
menu.AppendMenu(MF_SEPARATOR);
|
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");
|
menu.AppendMenu(MF_STRING, ID_OUTLINER_REFRESH, "Refresh");
|
||||||
|
|
||||||
const int command = menu.TrackPopupMenu(
|
const int command = menu.TrackPopupMenu(
|
||||||
@@ -828,6 +1179,18 @@ void COutlinerDlg::OnTreeRightClick(NMHDR* pNMHDR, LRESULT* pResult) {
|
|||||||
case ID_OUTLINER_MOVE_TO_ENTITY:
|
case ID_OUTLINER_MOVE_TO_ENTITY:
|
||||||
MoveToEntity(ent);
|
MoveToEntity(ent);
|
||||||
break;
|
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:
|
case ID_OUTLINER_REFRESH:
|
||||||
Refresh(true);
|
Refresh(true);
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -43,7 +43,12 @@ public:
|
|||||||
ID_OUTLINER_OPEN_PROPERTIES = 0x7310,
|
ID_OUTLINER_OPEN_PROPERTIES = 0x7310,
|
||||||
ID_OUTLINER_MOVE_TO_ENTITY = 0x7311,
|
ID_OUTLINER_MOVE_TO_ENTITY = 0x7311,
|
||||||
ID_OUTLINER_REFRESH = 0x7312,
|
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();
|
COutlinerDlg();
|
||||||
@@ -71,6 +76,8 @@ protected:
|
|||||||
afx_msg void OnFilterChanged();
|
afx_msg void OnFilterChanged();
|
||||||
void OnTreeDblClick(NMHDR* pNMHDR, LRESULT* pResult);
|
void OnTreeDblClick(NMHDR* pNMHDR, LRESULT* pResult);
|
||||||
void OnTreeRightClick(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);
|
virtual BOOL PreTranslateMessage(MSG* pMsg);
|
||||||
|
|
||||||
@@ -128,7 +135,21 @@ private:
|
|||||||
HTREEITEM FindTreeItemForEntity(entity_t* ent, HTREEITEM start) const;
|
HTREEITEM FindTreeItemForEntity(entity_t* ent, HTREEITEM start) const;
|
||||||
void ExpandAll(HTREEITEM start);
|
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 NeedsRefresh() const;
|
||||||
bool GetEntityCenter(entity_t* ent, idVec3& center) const;
|
bool GetEntityCenter(entity_t* ent, idVec3& center) const;
|
||||||
void ApplyFont();
|
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