mirror of
https://github.com/jmarshall23/DoomRTX.git
synced 2026-08-12 16:21:04 +02:00
2469 lines
64 KiB
C++
2469 lines
64 KiB
C++
/*
|
|
===========================================================================
|
|
|
|
IceTech GPL Source Code
|
|
Copyright (C) 2026 Justin Marshall
|
|
|
|
Modernized Entity Inspector UI pass by Justin / IceBridge workflow.
|
|
|
|
===========================================================================
|
|
*/
|
|
|
|
#include "precompiled.h"
|
|
#pragma hdrstop
|
|
|
|
#include "qe3.h"
|
|
#include "Radiant.h"
|
|
#include "GLWidget.h"
|
|
#include "PropertyList.h"
|
|
#include "entitydlg.h"
|
|
#include "PreviewDlg.h"
|
|
#include "CurveDlg.h"
|
|
|
|
#include "../../models/model_local.h" // for idRenderModelPrt
|
|
|
|
void Select_Ungroup();
|
|
|
|
// CEntityDlg dialog
|
|
|
|
IMPLEMENT_DYNAMIC(CEntityDlg, CDialog)
|
|
|
|
|
|
#ifndef BS_TYPEMASK
|
|
#define BS_TYPEMASK 0x0000000F
|
|
#endif
|
|
|
|
static const COLORREF ENTITY_DARK_BG = RGB(17, 19, 23);
|
|
static const COLORREF ENTITY_DARK_PANEL = RGB(24, 27, 32);
|
|
static const COLORREF ENTITY_DARK_PANEL_ALT = RGB(29, 33, 39);
|
|
static const COLORREF ENTITY_DARK_FIELD = RGB(14, 16, 20);
|
|
static const COLORREF ENTITY_DARK_FIELD_ALT = RGB(18, 21, 26);
|
|
static const COLORREF ENTITY_DARK_PROP_NAME = RGB(31, 36, 44);
|
|
static const COLORREF ENTITY_DARK_PROP_NAME_SEL = RGB(54, 76, 105);
|
|
static const COLORREF ENTITY_DARK_BORDER = RGB(55, 62, 72);
|
|
static const COLORREF ENTITY_DARK_BORDER_HOT = RGB(87, 166, 255);
|
|
static const COLORREF ENTITY_DARK_TEXT = RGB(226, 232, 240);
|
|
static const COLORREF ENTITY_DARK_TEXT_MUTED = RGB(151, 163, 184);
|
|
static const COLORREF ENTITY_DARK_TEXT_DISABLED = RGB(103, 112, 128);
|
|
static const COLORREF ENTITY_DARK_ACCENT = RGB(87, 166, 255);
|
|
|
|
static const char* ENTITY_DARK_BUTTON_OLDPROC = "IceTech.Entity.DarkButtonOldProc";
|
|
static const char* ENTITY_DARK_PROP_OLDPROC = "IceTech.Entity.DarkPropertyOldProc";
|
|
static const char* ENTITY_DARK_PROP_DIVIDER = "IceTech.Entity.DarkPropertyDivider";
|
|
static const char* ENTITY_DARK_FIELD_OLDPROC = "IceTech.Entity.DarkFieldOldProc";
|
|
static const char* ENTITY_DARK_COMBO_OLDPROC = "IceTech.Entity.DarkComboOldProc";
|
|
|
|
static void EntityDark_FillRect(HDC hDC, const RECT& rc, COLORREF color) {
|
|
HBRUSH brush = ::CreateSolidBrush(color);
|
|
::FillRect(hDC, &rc, brush);
|
|
::DeleteObject(brush);
|
|
}
|
|
|
|
static void EntityDark_FrameRect(HDC hDC, const RECT& rc, COLORREF color) {
|
|
HBRUSH brush = ::CreateSolidBrush(color);
|
|
::FrameRect(hDC, &rc, brush);
|
|
::DeleteObject(brush);
|
|
}
|
|
|
|
static void EntityDark_DrawButton(HWND hWnd, HDC hDC) {
|
|
RECT rc;
|
|
::GetClientRect(hWnd, &rc);
|
|
|
|
const UINT style = (UINT)::GetWindowLong(hWnd, GWL_STYLE);
|
|
const UINT type = style & BS_TYPEMASK;
|
|
const bool isCheck = (type == BS_CHECKBOX || type == BS_AUTOCHECKBOX || type == BS_3STATE || type == BS_AUTO3STATE);
|
|
const bool enabled = ::IsWindowEnabled(hWnd) ? true : false;
|
|
const bool pushed = (::SendMessage(hWnd, BM_GETSTATE, 0, 0) & BST_PUSHED) != 0;
|
|
const bool checked = (::SendMessage(hWnd, BM_GETCHECK, 0, 0) == BST_CHECKED);
|
|
|
|
char text[256];
|
|
text[0] = '\0';
|
|
::GetWindowTextA(hWnd, text, sizeof(text));
|
|
|
|
HFONT font = (HFONT)::SendMessage(hWnd, WM_GETFONT, 0, 0);
|
|
HFONT oldFont = font ? (HFONT)::SelectObject(hDC, font) : NULL;
|
|
::SetBkMode(hDC, TRANSPARENT);
|
|
::SetTextColor(hDC, enabled ? ENTITY_DARK_TEXT : ENTITY_DARK_TEXT_DISABLED);
|
|
|
|
if (isCheck) {
|
|
EntityDark_FillRect(hDC, rc, ENTITY_DARK_PANEL);
|
|
|
|
RECT box = rc;
|
|
box.left += 3;
|
|
box.right = box.left + 14;
|
|
box.top += max(0, (rc.bottom - rc.top - 14) / 2);
|
|
box.bottom = box.top + 14;
|
|
|
|
EntityDark_FillRect(hDC, box, enabled ? ENTITY_DARK_FIELD : RGB(20, 22, 26));
|
|
EntityDark_FrameRect(hDC, box, checked ? ENTITY_DARK_ACCENT : ENTITY_DARK_BORDER);
|
|
|
|
if (checked) {
|
|
HPEN pen = ::CreatePen(PS_SOLID, 2, enabled ? ENTITY_DARK_ACCENT : ENTITY_DARK_TEXT_DISABLED);
|
|
HPEN oldPen = (HPEN)::SelectObject(hDC, pen);
|
|
::MoveToEx(hDC, box.left + 3, box.top + 7, NULL);
|
|
::LineTo(hDC, box.left + 6, box.top + 10);
|
|
::LineTo(hDC, box.right - 3, box.top + 3);
|
|
::SelectObject(hDC, oldPen);
|
|
::DeleteObject(pen);
|
|
}
|
|
|
|
RECT textRect = rc;
|
|
textRect.left = box.right + 7;
|
|
textRect.right -= 2;
|
|
::DrawTextA(hDC, text, -1, &textRect, DT_LEFT | DT_VCENTER | DT_SINGLELINE | DT_END_ELLIPSIS);
|
|
}
|
|
else {
|
|
COLORREF face = pushed ? RGB(22, 25, 30) : ENTITY_DARK_PANEL_ALT;
|
|
EntityDark_FillRect(hDC, rc, face);
|
|
EntityDark_FrameRect(hDC, rc, enabled ? ENTITY_DARK_BORDER : RGB(39, 44, 52));
|
|
|
|
RECT inner = rc;
|
|
::InflateRect(&inner, -1, -1);
|
|
if (!pushed) {
|
|
EntityDark_FrameRect(hDC, inner, RGB(35, 40, 47));
|
|
}
|
|
|
|
RECT textRect = rc;
|
|
if (pushed) {
|
|
::OffsetRect(&textRect, 1, 1);
|
|
}
|
|
::DrawTextA(hDC, text, -1, &textRect, DT_CENTER | DT_VCENTER | DT_SINGLELINE | DT_END_ELLIPSIS);
|
|
}
|
|
|
|
if (oldFont) {
|
|
::SelectObject(hDC, oldFont);
|
|
}
|
|
}
|
|
|
|
static LRESULT CALLBACK EntityDarkButtonProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
|
|
WNDPROC oldProc = (WNDPROC)::GetPropA(hWnd, ENTITY_DARK_BUTTON_OLDPROC);
|
|
if (!oldProc) {
|
|
return ::DefWindowProc(hWnd, uMsg, wParam, lParam);
|
|
}
|
|
|
|
switch (uMsg) {
|
|
case WM_ERASEBKGND:
|
|
return 1;
|
|
|
|
case WM_PAINT:
|
|
{
|
|
PAINTSTRUCT ps;
|
|
HDC hDC = ::BeginPaint(hWnd, &ps);
|
|
EntityDark_DrawButton(hWnd, hDC);
|
|
::EndPaint(hWnd, &ps);
|
|
return 0;
|
|
}
|
|
|
|
case WM_PRINTCLIENT:
|
|
EntityDark_DrawButton(hWnd, (HDC)wParam);
|
|
return 0;
|
|
|
|
case WM_MOUSEMOVE:
|
|
case WM_LBUTTONDOWN:
|
|
case WM_LBUTTONUP:
|
|
case WM_ENABLE:
|
|
case WM_SETTEXT:
|
|
case BM_SETCHECK:
|
|
case BM_SETSTATE:
|
|
{
|
|
LRESULT result = ::CallWindowProc(oldProc, hWnd, uMsg, wParam, lParam);
|
|
::InvalidateRect(hWnd, NULL, FALSE);
|
|
return result;
|
|
}
|
|
|
|
case WM_NCDESTROY:
|
|
{
|
|
::SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)oldProc);
|
|
::RemovePropA(hWnd, ENTITY_DARK_BUTTON_OLDPROC);
|
|
return ::CallWindowProc(oldProc, hWnd, uMsg, wParam, lParam);
|
|
}
|
|
}
|
|
|
|
return ::CallWindowProc(oldProc, hWnd, uMsg, wParam, lParam);
|
|
}
|
|
|
|
static void EntityDark_SubclassButton(CWnd& wnd) {
|
|
HWND hWnd = wnd.GetSafeHwnd();
|
|
if (!hWnd || ::GetPropA(hWnd, ENTITY_DARK_BUTTON_OLDPROC)) {
|
|
return;
|
|
}
|
|
WNDPROC oldProc = (WNDPROC)::GetWindowLongPtr(hWnd, GWLP_WNDPROC);
|
|
::SetPropA(hWnd, ENTITY_DARK_BUTTON_OLDPROC, (HANDLE)oldProc);
|
|
::SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)EntityDarkButtonProc);
|
|
::InvalidateRect(hWnd, NULL, TRUE);
|
|
}
|
|
|
|
static void EntityDark_ApplyNativeTheme(HWND hWnd) {
|
|
if (!hWnd) {
|
|
return;
|
|
}
|
|
|
|
typedef HRESULT(WINAPI* SetWindowThemeProc)(HWND, LPCWSTR, LPCWSTR);
|
|
typedef BOOL(WINAPI* AllowDarkModeForWindowProc)(HWND, BOOL);
|
|
|
|
static HMODULE hUxTheme = ::LoadLibraryA("uxtheme.dll");
|
|
static SetWindowThemeProc pSetWindowTheme = hUxTheme ? (SetWindowThemeProc)::GetProcAddress(hUxTheme, "SetWindowTheme") : NULL;
|
|
static AllowDarkModeForWindowProc pAllowDarkModeForWindow = hUxTheme ? (AllowDarkModeForWindowProc)::GetProcAddress(hUxTheme, MAKEINTRESOURCEA(133)) : NULL;
|
|
|
|
if (pAllowDarkModeForWindow) {
|
|
pAllowDarkModeForWindow(hWnd, TRUE);
|
|
}
|
|
if (pSetWindowTheme) {
|
|
pSetWindowTheme(hWnd, L"DarkMode_Explorer", NULL);
|
|
}
|
|
::SendMessage(hWnd, WM_THEMECHANGED, 0, 0);
|
|
}
|
|
|
|
|
|
static void EntityDark_RemoveClientEdge(HWND hWnd) {
|
|
if (!hWnd) {
|
|
return;
|
|
}
|
|
LONG exStyle = ::GetWindowLong(hWnd, GWL_EXSTYLE);
|
|
if (exStyle & WS_EX_CLIENTEDGE) {
|
|
::SetWindowLong(hWnd, GWL_EXSTYLE, exStyle & ~WS_EX_CLIENTEDGE);
|
|
::SetWindowPos(hWnd, NULL, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED);
|
|
}
|
|
}
|
|
|
|
static void EntityDark_DrawFrame(HWND hWnd, COLORREF color) {
|
|
HDC hDC = ::GetWindowDC(hWnd);
|
|
if (!hDC) {
|
|
return;
|
|
}
|
|
RECT rc;
|
|
::GetWindowRect(hWnd, &rc);
|
|
::OffsetRect(&rc, -rc.left, -rc.top);
|
|
EntityDark_FrameRect(hDC, rc, color);
|
|
::ReleaseDC(hWnd, hDC);
|
|
}
|
|
|
|
static LRESULT CALLBACK EntityDarkFieldProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
|
|
WNDPROC oldProc = (WNDPROC)::GetPropA(hWnd, ENTITY_DARK_FIELD_OLDPROC);
|
|
if (!oldProc) {
|
|
return ::DefWindowProc(hWnd, uMsg, wParam, lParam);
|
|
}
|
|
|
|
switch (uMsg) {
|
|
case WM_NCPAINT:
|
|
case WM_PAINT:
|
|
{
|
|
LRESULT result = ::CallWindowProc(oldProc, hWnd, uMsg, wParam, lParam);
|
|
EntityDark_DrawFrame(hWnd, (::GetFocus() == hWnd) ? ENTITY_DARK_BORDER_HOT : ENTITY_DARK_BORDER);
|
|
return result;
|
|
}
|
|
case WM_SETFOCUS:
|
|
case WM_KILLFOCUS:
|
|
case WM_ENABLE:
|
|
{
|
|
LRESULT result = ::CallWindowProc(oldProc, hWnd, uMsg, wParam, lParam);
|
|
::RedrawWindow(hWnd, NULL, NULL, RDW_INVALIDATE | RDW_FRAME | RDW_UPDATENOW);
|
|
return result;
|
|
}
|
|
case WM_NCDESTROY:
|
|
{
|
|
::SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)oldProc);
|
|
::RemovePropA(hWnd, ENTITY_DARK_FIELD_OLDPROC);
|
|
return ::CallWindowProc(oldProc, hWnd, uMsg, wParam, lParam);
|
|
}
|
|
}
|
|
|
|
return ::CallWindowProc(oldProc, hWnd, uMsg, wParam, lParam);
|
|
}
|
|
|
|
static void EntityDark_DrawCombo(HWND hWnd, HDC hDC) {
|
|
RECT rc;
|
|
::GetClientRect(hWnd, &rc);
|
|
const bool enabled = ::IsWindowEnabled(hWnd) ? true : false;
|
|
const bool focused = (::GetFocus() == hWnd || ::IsChild(hWnd, ::GetFocus())) ? true : false;
|
|
|
|
EntityDark_FillRect(hDC, rc, enabled ? ENTITY_DARK_FIELD : RGB(20, 22, 26));
|
|
EntityDark_FrameRect(hDC, rc, focused ? ENTITY_DARK_BORDER_HOT : ENTITY_DARK_BORDER);
|
|
|
|
RECT arrow = rc;
|
|
arrow.left = arrow.right - 22;
|
|
EntityDark_FillRect(hDC, arrow, ENTITY_DARK_PANEL_ALT);
|
|
EntityDark_FrameRect(hDC, arrow, ENTITY_DARK_BORDER);
|
|
|
|
POINT pts[3];
|
|
int cx = arrow.left + (arrow.right - arrow.left) / 2;
|
|
int cy = arrow.top + (arrow.bottom - arrow.top) / 2 + 1;
|
|
pts[0].x = cx - 4; pts[0].y = cy - 2;
|
|
pts[1].x = cx + 4; pts[1].y = cy - 2;
|
|
pts[2].x = cx; pts[2].y = cy + 3;
|
|
HBRUSH arrowBrush = ::CreateSolidBrush(enabled ? ENTITY_DARK_TEXT : ENTITY_DARK_TEXT_DISABLED);
|
|
HBRUSH oldBrush = (HBRUSH)::SelectObject(hDC, arrowBrush);
|
|
HPEN arrowPen = ::CreatePen(PS_SOLID, 1, enabled ? ENTITY_DARK_TEXT : ENTITY_DARK_TEXT_DISABLED);
|
|
HPEN oldPen = (HPEN)::SelectObject(hDC, arrowPen);
|
|
::Polygon(hDC, pts, 3);
|
|
::SelectObject(hDC, oldPen);
|
|
::DeleteObject(arrowPen);
|
|
::SelectObject(hDC, oldBrush);
|
|
::DeleteObject(arrowBrush);
|
|
|
|
char text[512];
|
|
text[0] = '\0';
|
|
int sel = (int)::SendMessage(hWnd, CB_GETCURSEL, 0, 0);
|
|
if (sel >= 0) {
|
|
::SendMessageA(hWnd, CB_GETLBTEXT, (WPARAM)sel, (LPARAM)text);
|
|
}
|
|
else {
|
|
::GetWindowTextA(hWnd, text, sizeof(text));
|
|
}
|
|
|
|
HFONT font = (HFONT)::SendMessage(hWnd, WM_GETFONT, 0, 0);
|
|
HFONT oldFont = font ? (HFONT)::SelectObject(hDC, font) : NULL;
|
|
::SetBkMode(hDC, TRANSPARENT);
|
|
::SetTextColor(hDC, enabled ? ENTITY_DARK_TEXT : ENTITY_DARK_TEXT_DISABLED);
|
|
RECT textRect = rc;
|
|
textRect.left += 7;
|
|
textRect.right = arrow.left - 3;
|
|
::DrawTextA(hDC, text, -1, &textRect, DT_LEFT | DT_VCENTER | DT_SINGLELINE | DT_END_ELLIPSIS);
|
|
if (oldFont) {
|
|
::SelectObject(hDC, oldFont);
|
|
}
|
|
}
|
|
|
|
static LRESULT CALLBACK EntityDarkComboProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
|
|
WNDPROC oldProc = (WNDPROC)::GetPropA(hWnd, ENTITY_DARK_COMBO_OLDPROC);
|
|
if (!oldProc) {
|
|
return ::DefWindowProc(hWnd, uMsg, wParam, lParam);
|
|
}
|
|
|
|
switch (uMsg) {
|
|
case WM_ERASEBKGND:
|
|
return 1;
|
|
case WM_PAINT:
|
|
{
|
|
UINT comboType = (UINT)::GetWindowLong(hWnd, GWL_STYLE) & 0x00000003;
|
|
if (comboType == CBS_DROPDOWNLIST) {
|
|
PAINTSTRUCT ps;
|
|
HDC hDC = ::BeginPaint(hWnd, &ps);
|
|
EntityDark_DrawCombo(hWnd, hDC);
|
|
::EndPaint(hWnd, &ps);
|
|
return 0;
|
|
}
|
|
LRESULT result = ::CallWindowProc(oldProc, hWnd, uMsg, wParam, lParam);
|
|
EntityDark_DrawFrame(hWnd, (::GetFocus() == hWnd || ::IsChild(hWnd, ::GetFocus())) ? ENTITY_DARK_BORDER_HOT : ENTITY_DARK_BORDER);
|
|
return result;
|
|
}
|
|
case WM_SETFOCUS:
|
|
case WM_KILLFOCUS:
|
|
case WM_ENABLE:
|
|
case WM_SETTEXT:
|
|
case CB_SETCURSEL:
|
|
case CB_ADDSTRING:
|
|
case CB_DELETESTRING:
|
|
case CB_RESETCONTENT:
|
|
{
|
|
LRESULT result = ::CallWindowProc(oldProc, hWnd, uMsg, wParam, lParam);
|
|
::InvalidateRect(hWnd, NULL, FALSE);
|
|
return result;
|
|
}
|
|
case WM_NCDESTROY:
|
|
{
|
|
::SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)oldProc);
|
|
::RemovePropA(hWnd, ENTITY_DARK_COMBO_OLDPROC);
|
|
return ::CallWindowProc(oldProc, hWnd, uMsg, wParam, lParam);
|
|
}
|
|
}
|
|
|
|
return ::CallWindowProc(oldProc, hWnd, uMsg, wParam, lParam);
|
|
}
|
|
|
|
static void EntityDark_SubclassField(CWnd& wnd) {
|
|
HWND hWnd = wnd.GetSafeHwnd();
|
|
if (!hWnd || ::GetPropA(hWnd, ENTITY_DARK_FIELD_OLDPROC)) {
|
|
return;
|
|
}
|
|
EntityDark_RemoveClientEdge(hWnd);
|
|
EntityDark_ApplyNativeTheme(hWnd);
|
|
WNDPROC oldProc = (WNDPROC)::GetWindowLongPtr(hWnd, GWLP_WNDPROC);
|
|
::SetPropA(hWnd, ENTITY_DARK_FIELD_OLDPROC, (HANDLE)oldProc);
|
|
::SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)EntityDarkFieldProc);
|
|
::InvalidateRect(hWnd, NULL, TRUE);
|
|
}
|
|
|
|
static void EntityDark_SubclassCombo(CWnd& wnd) {
|
|
HWND hWnd = wnd.GetSafeHwnd();
|
|
if (!hWnd || ::GetPropA(hWnd, ENTITY_DARK_COMBO_OLDPROC)) {
|
|
return;
|
|
}
|
|
EntityDark_RemoveClientEdge(hWnd);
|
|
EntityDark_ApplyNativeTheme(hWnd);
|
|
WNDPROC oldProc = (WNDPROC)::GetWindowLongPtr(hWnd, GWLP_WNDPROC);
|
|
::SetPropA(hWnd, ENTITY_DARK_COMBO_OLDPROC, (HANDLE)oldProc);
|
|
::SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)EntityDarkComboProc);
|
|
::InvalidateRect(hWnd, NULL, TRUE);
|
|
}
|
|
|
|
static void EntityDark_SetPropertyListRowHeight(HWND hWnd) {
|
|
if (!hWnd) {
|
|
return;
|
|
}
|
|
|
|
// The old resource-sized rows were too tight after the dark-theme font pass.
|
|
// Give every row enough breathing room so descenders (g, y, p, q) are not clipped at the bottom.
|
|
::SendMessage(hWnd, LB_SETITEMHEIGHT, 0, 32);
|
|
}
|
|
|
|
static void EntityDark_CopyPropText(char* out, int outSize, CPropertyItem* propItem, int index, HWND hWnd, bool wantValue) {
|
|
if (!out || outSize <= 0) {
|
|
return;
|
|
}
|
|
out[0] = '\0';
|
|
|
|
if (propItem && propItem != (CPropertyItem*)LB_ERR) {
|
|
const char* src = wantValue ? propItem->m_curValue : propItem->m_propName;
|
|
if (src && src[0]) {
|
|
idStr::Copynz(out, src, outSize);
|
|
return;
|
|
}
|
|
}
|
|
|
|
char text[2048];
|
|
text[0] = '\0';
|
|
::SendMessageA(hWnd, LB_GETTEXT, (WPARAM)index, (LPARAM)text);
|
|
|
|
char* tab = strchr(text, '\t');
|
|
if (tab) {
|
|
if (wantValue) {
|
|
idStr::Copynz(out, tab + 1, outSize);
|
|
}
|
|
else {
|
|
*tab = '\0';
|
|
idStr::Copynz(out, text, outSize);
|
|
}
|
|
}
|
|
else if (!wantValue) {
|
|
idStr::Copynz(out, text, outSize);
|
|
}
|
|
}
|
|
|
|
static void EntityDark_DrawPropertyList(HWND hWnd, HDC hDC) {
|
|
RECT client;
|
|
::GetClientRect(hWnd, &client);
|
|
|
|
EntityDark_FillRect(hDC, client, ENTITY_DARK_FIELD);
|
|
|
|
const int count = (int)::SendMessage(hWnd, LB_GETCOUNT, 0, 0);
|
|
if (count <= 0) {
|
|
return;
|
|
}
|
|
|
|
int divider = (int)(INT_PTR)::GetPropA(hWnd, ENTITY_DARK_PROP_DIVIDER);
|
|
if (divider <= 0) {
|
|
divider = 170;
|
|
}
|
|
if (divider > client.right - 80) {
|
|
divider = max(80, client.right / 2);
|
|
}
|
|
|
|
const int topIndex = (int)::SendMessage(hWnd, LB_GETTOPINDEX, 0, 0);
|
|
const int curSel = (int)::SendMessage(hWnd, LB_GETCURSEL, 0, 0);
|
|
const bool focused = (::GetFocus() == hWnd || ::IsChild(hWnd, ::GetFocus())) ? true : false;
|
|
|
|
HFONT font = (HFONT)::SendMessage(hWnd, WM_GETFONT, 0, 0);
|
|
HFONT oldFont = font ? (HFONT)::SelectObject(hDC, font) : NULL;
|
|
::SetBkMode(hDC, TRANSPARENT);
|
|
|
|
for (int i = max(0, topIndex); i < count; i++) {
|
|
RECT item;
|
|
if ((int)::SendMessage(hWnd, LB_GETITEMRECT, (WPARAM)i, (LPARAM)&item) == LB_ERR) {
|
|
break;
|
|
}
|
|
if (item.top >= client.bottom) {
|
|
break;
|
|
}
|
|
if (item.bottom <= client.top) {
|
|
continue;
|
|
}
|
|
|
|
const bool selected = (i == curSel);
|
|
|
|
RECT rowRect = item;
|
|
rowRect.left = client.left;
|
|
rowRect.right = client.right;
|
|
EntityDark_FillRect(hDC, rowRect, selected ? RGB(31, 41, 55) : ENTITY_DARK_FIELD);
|
|
|
|
RECT nameRect = item;
|
|
nameRect.left = client.left;
|
|
nameRect.right = min(client.right, divider);
|
|
EntityDark_FillRect(hDC, nameRect, selected ? ENTITY_DARK_PROP_NAME_SEL : ENTITY_DARK_PROP_NAME);
|
|
|
|
RECT valueRect = item;
|
|
valueRect.left = min(client.right, divider + 1);
|
|
valueRect.right = client.right;
|
|
EntityDark_FillRect(hDC, valueRect, selected ? RGB(30, 43, 59) : ENTITY_DARK_FIELD);
|
|
|
|
CPropertyItem* propItem = (CPropertyItem*)::SendMessage(hWnd, LB_GETITEMDATA, (WPARAM)i, 0);
|
|
|
|
char nameText[1024];
|
|
char valueText[2048];
|
|
EntityDark_CopyPropText(nameText, sizeof(nameText), propItem, i, hWnd, false);
|
|
EntityDark_CopyPropText(valueText, sizeof(valueText), propItem, i, hWnd, true);
|
|
|
|
RECT nameTextRect = nameRect;
|
|
nameTextRect.left += 10;
|
|
nameTextRect.right -= 10;
|
|
nameTextRect.top += 3;
|
|
nameTextRect.bottom -= 1;
|
|
|
|
RECT valueTextRect = valueRect;
|
|
valueTextRect.left += 12;
|
|
valueTextRect.right -= 10;
|
|
valueTextRect.top += 3;
|
|
valueTextRect.bottom -= 1;
|
|
|
|
::SetTextColor(hDC, selected ? RGB(255, 255, 255) : ENTITY_DARK_TEXT);
|
|
::DrawTextA(hDC, nameText, -1, &nameTextRect, DT_LEFT | DT_VCENTER | DT_SINGLELINE | DT_END_ELLIPSIS);
|
|
|
|
::SetTextColor(hDC, selected ? RGB(235, 245, 255) : RGB(206, 214, 226));
|
|
::DrawTextA(hDC, valueText, -1, &valueTextRect, DT_LEFT | DT_VCENTER | DT_SINGLELINE | DT_END_ELLIPSIS);
|
|
|
|
// Dark, vertical-only split. Do not draw horizontal row dividers; they were cutting through the value text.
|
|
RECT dividerRect = item;
|
|
dividerRect.left = min(client.right - 1, divider);
|
|
dividerRect.right = dividerRect.left + 1;
|
|
EntityDark_FillRect(hDC, dividerRect, RGB(42, 48, 58));
|
|
}
|
|
|
|
if (focused) {
|
|
RECT focusRect = client;
|
|
::InflateRect(&focusRect, -1, -1);
|
|
EntityDark_FrameRect(hDC, focusRect, ENTITY_DARK_BORDER_HOT);
|
|
}
|
|
else {
|
|
RECT frameRect = client;
|
|
::InflateRect(&frameRect, -1, -1);
|
|
EntityDark_FrameRect(hDC, frameRect, ENTITY_DARK_BORDER);
|
|
}
|
|
|
|
if (oldFont) {
|
|
::SelectObject(hDC, oldFont);
|
|
}
|
|
}
|
|
|
|
static LRESULT CALLBACK EntityDarkPropertyListProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
|
|
WNDPROC oldProc = (WNDPROC)::GetPropA(hWnd, ENTITY_DARK_PROP_OLDPROC);
|
|
if (!oldProc) {
|
|
return ::DefWindowProc(hWnd, uMsg, wParam, lParam);
|
|
}
|
|
|
|
switch (uMsg) {
|
|
case WM_PAINT:
|
|
{
|
|
PAINTSTRUCT ps;
|
|
HDC hDC = ::BeginPaint(hWnd, &ps);
|
|
EntityDark_DrawPropertyList(hWnd, hDC);
|
|
::EndPaint(hWnd, &ps);
|
|
return 0;
|
|
}
|
|
|
|
case WM_PRINTCLIENT:
|
|
EntityDark_DrawPropertyList(hWnd, (HDC)wParam);
|
|
return 0;
|
|
|
|
case WM_ERASEBKGND:
|
|
{
|
|
RECT rc;
|
|
::GetClientRect(hWnd, &rc);
|
|
EntityDark_FillRect((HDC)wParam, rc, ENTITY_DARK_FIELD);
|
|
return 1;
|
|
}
|
|
|
|
case WM_VSCROLL:
|
|
case WM_HSCROLL:
|
|
case WM_MOUSEWHEEL:
|
|
case WM_SETFOCUS:
|
|
case WM_KILLFOCUS:
|
|
case LB_ADDSTRING:
|
|
case LB_INSERTSTRING:
|
|
case LB_DELETESTRING:
|
|
case LB_RESETCONTENT:
|
|
case LB_SETCURSEL:
|
|
{
|
|
LRESULT result = ::CallWindowProc(oldProc, hWnd, uMsg, wParam, lParam);
|
|
if (uMsg == LB_ADDSTRING || uMsg == LB_INSERTSTRING || uMsg == LB_RESETCONTENT) {
|
|
EntityDark_SetPropertyListRowHeight(hWnd);
|
|
}
|
|
::InvalidateRect(hWnd, NULL, FALSE);
|
|
return result;
|
|
}
|
|
|
|
case WM_NCDESTROY:
|
|
{
|
|
::SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)oldProc);
|
|
::RemovePropA(hWnd, ENTITY_DARK_PROP_OLDPROC);
|
|
::RemovePropA(hWnd, ENTITY_DARK_PROP_DIVIDER);
|
|
return ::CallWindowProc(oldProc, hWnd, uMsg, wParam, lParam);
|
|
}
|
|
}
|
|
|
|
return ::CallWindowProc(oldProc, hWnd, uMsg, wParam, lParam);
|
|
}
|
|
|
|
static void EntityDark_SubclassPropertyList(CWnd& wnd, int divider) {
|
|
HWND hWnd = wnd.GetSafeHwnd();
|
|
if (!hWnd) {
|
|
return;
|
|
}
|
|
::SetPropA(hWnd, ENTITY_DARK_PROP_DIVIDER, (HANDLE)(INT_PTR)divider);
|
|
EntityDark_SetPropertyListRowHeight(hWnd);
|
|
if (::GetPropA(hWnd, ENTITY_DARK_PROP_OLDPROC)) {
|
|
::InvalidateRect(hWnd, NULL, TRUE);
|
|
return;
|
|
}
|
|
WNDPROC oldProc = (WNDPROC)::GetWindowLongPtr(hWnd, GWLP_WNDPROC);
|
|
::SetPropA(hWnd, ENTITY_DARK_PROP_OLDPROC, (HANDLE)oldProc);
|
|
::SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)EntityDarkPropertyListProc);
|
|
::InvalidateRect(hWnd, NULL, TRUE);
|
|
}
|
|
|
|
CEntityDlg::CEntityDlg(CWnd* pParent /*=NULL*/)
|
|
: CDialog(CEntityDlg::IDD, pParent) {
|
|
editEntity = NULL;
|
|
multipleEntities = false;
|
|
currentAnimation = NULL;
|
|
currentAnimationFrame = 0;
|
|
dict = NULL;
|
|
themeInitialized = false;
|
|
|
|
colorBackground = RGB(17, 19, 23);
|
|
colorPanel = RGB(26, 29, 34);
|
|
colorPanelAlt = RGB(29, 33, 39);
|
|
colorBorder = RGB(55, 62, 72);
|
|
colorText = RGB(226, 232, 240);
|
|
colorTextMuted = RGB(151, 163, 184);
|
|
colorAccent = RGB(87, 166, 255);
|
|
colorEditBg = RGB(14, 16, 20);
|
|
}
|
|
|
|
CEntityDlg::~CEntityDlg() {
|
|
DestroyModernTheme();
|
|
}
|
|
|
|
void CEntityDlg::DoDataExchange(CDataExchange* pDX) {
|
|
CDialog::DoDataExchange(pDX);
|
|
|
|
DDX_Control(pDX, IDC_LIST_KEYVAL, listKeyVal);
|
|
DDX_Control(pDX, IDC_COMBO_CLASS, comboClass);
|
|
DDX_Control(pDX, IDC_EDIT_KEY, editKey);
|
|
DDX_Control(pDX, IDC_EDIT_VAL, editVal);
|
|
|
|
DDX_Control(pDX, IDC_STATIC_TITLE, staticTitle);
|
|
DDX_Control(pDX, IDC_STATIC_KEY, staticKey);
|
|
DDX_Control(pDX, IDC_STATIC_VAL, staticVal);
|
|
|
|
DDX_Control(pDX, IDC_BUTTON_BROWSE, btnBrowse);
|
|
|
|
DDX_Control(pDX, IDC_E_135, btn135);
|
|
DDX_Control(pDX, IDC_E_90, btn90);
|
|
DDX_Control(pDX, IDC_E_45, btn45);
|
|
DDX_Control(pDX, IDC_E_180, btn180);
|
|
DDX_Control(pDX, IDC_E_0, btn360);
|
|
DDX_Control(pDX, IDC_E_225, btn225);
|
|
DDX_Control(pDX, IDC_E_270, btn270);
|
|
DDX_Control(pDX, IDC_E_315, btn315);
|
|
DDX_Control(pDX, IDC_E_UP, btnUp);
|
|
DDX_Control(pDX, IDC_E_DOWN, btnDown);
|
|
|
|
DDX_Control(pDX, IDC_BUTTON_MODEL, btnModel);
|
|
DDX_Control(pDX, IDC_BUTTON_SOUND, btnSound);
|
|
DDX_Control(pDX, IDC_BUTTON_GUI, btnGui);
|
|
DDX_Control(pDX, IDC_BUTTON_PARTICLE, btnParticle);
|
|
DDX_Control(pDX, IDC_BUTTON_SKIN, btnSkin);
|
|
DDX_Control(pDX, IDC_BUTTON_CURVE, btnCurve);
|
|
DDX_Control(pDX, IDC_BUTTON_CREATE, btnCreate);
|
|
|
|
DDX_Control(pDX, IDC_LIST_VARS, listVars);
|
|
|
|
DDX_Control(pDX, IDC_ENTITY_ANIMATIONS, cbAnimations);
|
|
DDX_Control(pDX, IDC_ANIMATION_SLIDER, slFrameSlider);
|
|
DDX_Control(pDX, IDC_ENTITY_CURRENT_ANIM, staticFrame);
|
|
DDX_Control(pDX, IDC_ENTITY_PLAY_ANIM, btnPlayAnim);
|
|
DDX_Control(pDX, IDC_ENTITY_STOP_ANIM, btnStopAnim);
|
|
}
|
|
|
|
BEGIN_MESSAGE_MAP(CEntityDlg, CDialog)
|
|
ON_WM_SIZE()
|
|
ON_WM_CTLCOLOR()
|
|
ON_WM_ERASEBKGND()
|
|
ON_WM_PAINT()
|
|
|
|
ON_CBN_SELCHANGE(IDC_COMBO_CLASS, OnCbnSelchangeComboClass)
|
|
ON_LBN_SELCHANGE(IDC_LIST_KEYVAL, OnLbnSelchangeListkeyval)
|
|
|
|
ON_BN_CLICKED(IDC_E_135, OnBnClickedE135)
|
|
ON_BN_CLICKED(IDC_E_90, OnBnClickedE90)
|
|
ON_BN_CLICKED(IDC_E_45, OnBnClickedE45)
|
|
ON_BN_CLICKED(IDC_E_180, OnBnClickedE180)
|
|
ON_BN_CLICKED(IDC_E_0, OnBnClickedE0)
|
|
ON_BN_CLICKED(IDC_E_225, OnBnClickedE225)
|
|
ON_BN_CLICKED(IDC_E_270, OnBnClickedE270)
|
|
ON_BN_CLICKED(IDC_E_315, OnBnClickedE315)
|
|
ON_BN_CLICKED(IDC_E_UP, OnBnClickedEUp)
|
|
ON_BN_CLICKED(IDC_E_DOWN, OnBnClickedEDown)
|
|
|
|
ON_BN_CLICKED(IDC_BUTTON_MODEL, OnBnClickedButtonModel)
|
|
ON_BN_CLICKED(IDC_BUTTON_SOUND, OnBnClickedButtonSound)
|
|
ON_BN_CLICKED(IDC_BUTTON_GUI, OnBnClickedButtonGui)
|
|
ON_BN_CLICKED(IDC_BUTTON_BROWSE, OnBnClickedButtonBrowse)
|
|
ON_CBN_DBLCLK(IDC_COMBO_CLASS, OnCbnDblclkComboClass)
|
|
ON_BN_CLICKED(IDC_BUTTON_CREATE, OnBnClickedButtonCreate)
|
|
|
|
ON_LBN_DBLCLK(IDC_LIST_KEYVAL, OnLbnDblclkListkeyval)
|
|
ON_LBN_SELCHANGE(IDC_LIST_VARS, OnLbnSelchangeListVars)
|
|
ON_LBN_DBLCLK(IDC_LIST_VARS, OnLbnDblclkListVars)
|
|
|
|
ON_NOTIFY(NM_RELEASEDCAPTURE, IDC_ANIMATION_SLIDER, OnNMReleasedcaptureSlider1)
|
|
|
|
ON_BN_CLICKED(IDC_BUTTON_PARTICLE, OnBnClickedButtonParticle)
|
|
ON_BN_CLICKED(IDC_BUTTON_SKIN, OnBnClickedButtonSkin)
|
|
ON_BN_CLICKED(IDC_BUTTON_CURVE, OnBnClickedButtonCurve)
|
|
|
|
ON_CBN_SELCHANGE(IDC_ENTITY_ANIMATIONS, OnCbnAnimationChange)
|
|
ON_BN_CLICKED(IDC_ENTITY_PLAY_ANIM, OnBnClickedStartAnimation)
|
|
ON_BN_CLICKED(IDC_ENTITY_STOP_ANIM, OnBnClickedStopAnimation)
|
|
|
|
ON_WM_TIMER()
|
|
ON_BN_CLICKED(IDOK, OnOK)
|
|
END_MESSAGE_MAP()
|
|
|
|
// --------------------------------------------------------------------------
|
|
// Modern theme helpers
|
|
// --------------------------------------------------------------------------
|
|
|
|
void CEntityDlg::DestroyModernTheme() {
|
|
if (fontTitle.GetSafeHandle()) {
|
|
fontTitle.DeleteObject();
|
|
}
|
|
if (fontSection.GetSafeHandle()) {
|
|
fontSection.DeleteObject();
|
|
}
|
|
if (fontNormal.GetSafeHandle()) {
|
|
fontNormal.DeleteObject();
|
|
}
|
|
if (fontMono.GetSafeHandle()) {
|
|
fontMono.DeleteObject();
|
|
}
|
|
|
|
if (brushBackground.GetSafeHandle()) {
|
|
brushBackground.DeleteObject();
|
|
}
|
|
if (brushPanel.GetSafeHandle()) {
|
|
brushPanel.DeleteObject();
|
|
}
|
|
if (brushPanelAlt.GetSafeHandle()) {
|
|
brushPanelAlt.DeleteObject();
|
|
}
|
|
if (brushEdit.GetSafeHandle()) {
|
|
brushEdit.DeleteObject();
|
|
}
|
|
if (brushStatic.GetSafeHandle()) {
|
|
brushStatic.DeleteObject();
|
|
}
|
|
|
|
themeInitialized = false;
|
|
}
|
|
|
|
void CEntityDlg::InitModernTheme() {
|
|
if (themeInitialized) {
|
|
return;
|
|
}
|
|
|
|
NONCLIENTMETRICS ncm;
|
|
memset(&ncm, 0, sizeof(ncm));
|
|
ncm.cbSize = sizeof(ncm);
|
|
SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(ncm), &ncm, 0);
|
|
|
|
LOGFONT lf = ncm.lfMessageFont;
|
|
strcpy(lf.lfFaceName, "Segoe UI");
|
|
lf.lfHeight = -12;
|
|
lf.lfWeight = FW_NORMAL;
|
|
fontNormal.CreateFontIndirect(&lf);
|
|
|
|
lf.lfHeight = -17;
|
|
lf.lfWeight = FW_SEMIBOLD;
|
|
fontTitle.CreateFontIndirect(&lf);
|
|
|
|
lf.lfHeight = -12;
|
|
lf.lfWeight = FW_SEMIBOLD;
|
|
fontSection.CreateFontIndirect(&lf);
|
|
|
|
strcpy(lf.lfFaceName, "Consolas");
|
|
lf.lfHeight = -12;
|
|
lf.lfWeight = FW_NORMAL;
|
|
fontMono.CreateFontIndirect(&lf);
|
|
|
|
brushBackground.CreateSolidBrush(colorBackground);
|
|
brushPanel.CreateSolidBrush(colorPanel);
|
|
brushPanelAlt.CreateSolidBrush(colorPanelAlt);
|
|
brushEdit.CreateSolidBrush(colorEditBg);
|
|
brushStatic.CreateSolidBrush(colorPanel);
|
|
|
|
themeInitialized = true;
|
|
}
|
|
|
|
void CEntityDlg::SetCtrlFont(CWnd& wnd, CFont* font) {
|
|
if (wnd.GetSafeHwnd() && font && font->GetSafeHandle()) {
|
|
wnd.SetFont(font, FALSE);
|
|
}
|
|
}
|
|
|
|
void CEntityDlg::SetCtrlFont(int id, CFont* font) {
|
|
CWnd* wnd = GetDlgItem(id);
|
|
if (wnd && wnd->GetSafeHwnd() && font && font->GetSafeHandle()) {
|
|
wnd->SetFont(font, FALSE);
|
|
}
|
|
}
|
|
|
|
void CEntityDlg::ApplyModernTheme() {
|
|
InitModernTheme();
|
|
|
|
SetCtrlFont(staticTitle, &fontTitle);
|
|
SetCtrlFont(staticKey, &fontSection);
|
|
SetCtrlFont(staticVal, &fontSection);
|
|
SetCtrlFont(staticFrame, &fontNormal);
|
|
|
|
SetCtrlFont(comboClass, &fontNormal);
|
|
SetCtrlFont(cbAnimations, &fontNormal);
|
|
|
|
SetCtrlFont(editKey, &fontMono);
|
|
SetCtrlFont(editVal, &fontMono);
|
|
SetCtrlFont(listKeyVal, &fontMono);
|
|
SetCtrlFont(listVars, &fontNormal);
|
|
|
|
SetCtrlFont(btnCreate, &fontSection);
|
|
SetCtrlFont(btnBrowse, &fontSection);
|
|
|
|
SetCtrlFont(btnModel, &fontNormal);
|
|
SetCtrlFont(btnSound, &fontNormal);
|
|
SetCtrlFont(btnGui, &fontNormal);
|
|
SetCtrlFont(btnParticle, &fontNormal);
|
|
SetCtrlFont(btnSkin, &fontNormal);
|
|
SetCtrlFont(btnCurve, &fontNormal);
|
|
|
|
SetCtrlFont(btn135, &fontNormal);
|
|
SetCtrlFont(btn90, &fontNormal);
|
|
SetCtrlFont(btn45, &fontNormal);
|
|
SetCtrlFont(btn180, &fontNormal);
|
|
SetCtrlFont(btn360, &fontNormal);
|
|
SetCtrlFont(btn225, &fontNormal);
|
|
SetCtrlFont(btn270, &fontNormal);
|
|
SetCtrlFont(btn315, &fontNormal);
|
|
SetCtrlFont(btnUp, &fontNormal);
|
|
SetCtrlFont(btnDown, &fontNormal);
|
|
|
|
SetCtrlFont(btnPlayAnim, &fontNormal);
|
|
SetCtrlFont(btnStopAnim, &fontNormal);
|
|
|
|
if (editKey.GetSafeHwnd()) {
|
|
editKey.SetMargins(6, 6);
|
|
}
|
|
if (editVal.GetSafeHwnd()) {
|
|
editVal.SetMargins(6, 6);
|
|
}
|
|
|
|
EntityDark_SubclassField(editKey);
|
|
EntityDark_SubclassField(editVal);
|
|
EntityDark_SubclassField(listKeyVal);
|
|
EntityDark_SubclassField(listVars);
|
|
EntityDark_SubclassField(slFrameSlider);
|
|
EntityDark_SubclassCombo(comboClass);
|
|
EntityDark_SubclassCombo(cbAnimations);
|
|
|
|
EntityDark_SubclassPropertyList(listKeyVal, 170);
|
|
EntityDark_SubclassPropertyList(listVars, 170);
|
|
|
|
CWnd* darkButtons[] = {
|
|
&btnCreate, &btnBrowse,
|
|
&btnModel, &btnSound, &btnGui, &btnParticle, &btnSkin, &btnCurve,
|
|
&btn135, &btn90, &btn45, &btn180, &btn360, &btn225, &btn270, &btn315, &btnUp, &btnDown,
|
|
&btnPlayAnim, &btnStopAnim
|
|
};
|
|
for (int i = 0; i < (int)(sizeof(darkButtons) / sizeof(darkButtons[0])); i++) {
|
|
if (darkButtons[i] && darkButtons[i]->GetSafeHwnd()) {
|
|
EntityDark_ApplyNativeTheme(darkButtons[i]->GetSafeHwnd());
|
|
EntityDark_SubclassButton(*darkButtons[i]);
|
|
}
|
|
}
|
|
|
|
staticTitle.SetWindowText("Entity Inspector");
|
|
staticKey.SetWindowText("Key");
|
|
staticVal.SetWindowText("Value");
|
|
}
|
|
|
|
void CEntityDlg::MoveCtrl(CWnd& wnd, int x, int y, int w, int h, UINT flags) {
|
|
if (wnd.GetSafeHwnd()) {
|
|
wnd.SetWindowPos(NULL, x, y, max(1, w), max(1, h), flags | SWP_NOZORDER);
|
|
}
|
|
}
|
|
|
|
void CEntityDlg::MoveCtrl(int id, int x, int y, int w, int h, UINT flags) {
|
|
CWnd* wnd = GetDlgItem(id);
|
|
if (wnd && wnd->GetSafeHwnd()) {
|
|
wnd->SetWindowPos(NULL, x, y, max(1, w), max(1, h), flags | SWP_NOZORDER);
|
|
}
|
|
}
|
|
|
|
void CEntityDlg::DrawPanel(CDC& dc, const CRect& r, const char* title, bool accent) {
|
|
CBrush* oldBrush = dc.SelectObject(&brushPanel);
|
|
CPen borderPen(PS_SOLID, 1, accent ? colorAccent : colorBorder);
|
|
CPen* oldPen = dc.SelectObject(&borderPen);
|
|
|
|
dc.RoundRect(r.left, r.top, r.right, r.bottom, 8, 8);
|
|
|
|
if (title && title[0]) {
|
|
CRect titleRect = r;
|
|
titleRect.left += 10;
|
|
titleRect.top += 6;
|
|
titleRect.bottom = titleRect.top + 18;
|
|
|
|
CFont* oldFont = dc.SelectObject(&fontSection);
|
|
dc.SetBkMode(TRANSPARENT);
|
|
dc.SetTextColor(accent ? colorAccent : colorTextMuted);
|
|
dc.DrawText(title, -1, &titleRect, DT_LEFT | DT_VCENTER | DT_SINGLELINE);
|
|
dc.SelectObject(oldFont);
|
|
}
|
|
|
|
dc.SelectObject(oldPen);
|
|
dc.SelectObject(oldBrush);
|
|
}
|
|
|
|
void CEntityDlg::DrawModernBackground(CDC& dc) {
|
|
CRect rect;
|
|
GetClientRect(&rect);
|
|
|
|
dc.FillSolidRect(&rect, colorBackground);
|
|
|
|
const int pad = 8;
|
|
CRect header(rect.left + pad, rect.top + pad, rect.right - pad, rect.top + 42);
|
|
CRect classPanel(rect.left + pad, header.bottom + pad, rect.right - pad, header.bottom + 52 + pad);
|
|
|
|
int contentTop = classPanel.bottom + pad;
|
|
int contentBottom = rect.bottom - pad;
|
|
int width = rect.Width();
|
|
|
|
if (width >= 500) {
|
|
const int editorH = 106;
|
|
const int minToolH = 112;
|
|
const int availableH = max(1, contentBottom - contentTop);
|
|
// Give the two property grids the extra vertical space instead of capping them at 420px.
|
|
// This keeps the edit/tools panels underneath and prevents the list controls from stacking over each other.
|
|
int listH = max(180, availableH - editorH - minToolH - pad * 3);
|
|
int leftW = (width - pad * 3) / 2;
|
|
CRect vars(rect.left + pad, contentTop, rect.left + pad + leftW, contentTop + listH);
|
|
CRect keyvals(vars.right + pad, contentTop, rect.right - pad, contentTop + listH);
|
|
CRect editor(rect.left + pad, vars.bottom + pad, rect.right - pad, vars.bottom + editorH);
|
|
CRect tools(rect.left + pad, editor.bottom + pad, rect.right - pad, contentBottom);
|
|
|
|
DrawPanel(dc, header, "", true);
|
|
DrawPanel(dc, classPanel, "Class", false);
|
|
DrawPanel(dc, vars, "Spawn Args / Help", false);
|
|
DrawPanel(dc, keyvals, "Key / Value Pairs", true);
|
|
DrawPanel(dc, editor, "Edit Property", false);
|
|
DrawPanel(dc, tools, "Tools", false);
|
|
}
|
|
else {
|
|
const int editorH = 106;
|
|
const int minToolH = 112;
|
|
const int availableH = max(1, contentBottom - contentTop);
|
|
int h = max(140, (availableH - editorH - minToolH - pad * 3) / 2);
|
|
|
|
CRect vars(rect.left + pad, contentTop, rect.right - pad, contentTop + h);
|
|
CRect keyvals(rect.left + pad, vars.bottom + pad, rect.right - pad, vars.bottom + h);
|
|
CRect editor(rect.left + pad, keyvals.bottom + pad, rect.right - pad, keyvals.bottom + editorH);
|
|
CRect tools(rect.left + pad, editor.bottom + pad, rect.right - pad, contentBottom);
|
|
|
|
DrawPanel(dc, header, "", true);
|
|
DrawPanel(dc, classPanel, "Class", false);
|
|
DrawPanel(dc, vars, "Spawn Args / Help", false);
|
|
DrawPanel(dc, keyvals, "Key / Value Pairs", true);
|
|
DrawPanel(dc, editor, "Edit Property", false);
|
|
DrawPanel(dc, tools, "Tools", false);
|
|
}
|
|
}
|
|
|
|
BOOL CEntityDlg::OnEraseBkgnd(CDC* pDC) {
|
|
if (pDC) {
|
|
DrawModernBackground(*pDC);
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
void CEntityDlg::OnPaint() {
|
|
CPaintDC dc(this);
|
|
DrawModernBackground(dc);
|
|
}
|
|
|
|
HBRUSH CEntityDlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor) {
|
|
HBRUSH hbr = CDialog::OnCtlColor(pDC, pWnd, nCtlColor);
|
|
|
|
if (!themeInitialized) {
|
|
return hbr;
|
|
}
|
|
|
|
const int id = pWnd ? pWnd->GetDlgCtrlID() : 0;
|
|
|
|
switch (nCtlColor) {
|
|
case CTLCOLOR_STATIC:
|
|
pDC->SetTextColor((id == IDC_STATIC_TITLE) ? colorText : colorTextMuted);
|
|
pDC->SetBkMode(TRANSPARENT);
|
|
return (HBRUSH)brushPanel.GetSafeHandle();
|
|
|
|
case CTLCOLOR_EDIT:
|
|
pDC->SetTextColor(colorText);
|
|
pDC->SetBkColor(colorEditBg);
|
|
return (HBRUSH)brushEdit.GetSafeHandle();
|
|
|
|
case CTLCOLOR_LISTBOX:
|
|
pDC->SetTextColor(colorText);
|
|
pDC->SetBkColor(colorEditBg);
|
|
return (HBRUSH)brushEdit.GetSafeHandle();
|
|
|
|
case CTLCOLOR_BTN:
|
|
pDC->SetTextColor(colorText);
|
|
pDC->SetBkColor(colorPanelAlt);
|
|
return (HBRUSH)brushPanelAlt.GetSafeHandle();
|
|
|
|
case CTLCOLOR_DLG:
|
|
return (HBRUSH)brushBackground.GetSafeHandle();
|
|
}
|
|
|
|
return hbr;
|
|
}
|
|
|
|
// --------------------------------------------------------------------------
|
|
// Dialog init / layout
|
|
// --------------------------------------------------------------------------
|
|
|
|
BOOL CEntityDlg::OnInitDialog() {
|
|
CDialog::OnInitDialog();
|
|
|
|
InitModernTheme();
|
|
|
|
listKeyVal.SetUpdateInspectors(true);
|
|
listKeyVal.SetDivider(170);
|
|
listVars.SetDivider(170);
|
|
::SendMessage(listKeyVal.GetSafeHwnd(), LB_SETITEMHEIGHT, 0, 32);
|
|
::SendMessage(listVars.GetSafeHwnd(), LB_SETITEMHEIGHT, 0, 32);
|
|
|
|
staticFrame.SetWindowText("0");
|
|
|
|
ApplyModernTheme();
|
|
|
|
ModifyStyleEx(0, WS_EX_CONTROLPARENT);
|
|
Invalidate(FALSE);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
INT_PTR CEntityDlg::OnToolHitTest(CPoint point, TOOLINFO* pTI) const {
|
|
return CDialog::OnToolHitTest(point, pTI);
|
|
}
|
|
|
|
void CEntityDlg::OnSize(UINT nType, int cx, int cy) {
|
|
CDialog::OnSize(nType, cx, cy);
|
|
|
|
if (staticTitle.GetSafeHwnd() == NULL) {
|
|
return;
|
|
}
|
|
|
|
const int pad = 8;
|
|
const int inner = 10;
|
|
const int headerH = 42;
|
|
const int classH = 52;
|
|
const int editorH = 106;
|
|
const int minToolH = 112;
|
|
|
|
CRect rect;
|
|
GetClientRect(&rect);
|
|
|
|
int w = max(1, rect.Width());
|
|
int h = max(1, rect.Height());
|
|
|
|
CRect header(rect.left + pad, rect.top + pad, rect.right - pad, rect.top + pad + headerH);
|
|
MoveCtrl(staticTitle, header.left + 12, header.top + 8, header.Width() - 24, 24);
|
|
|
|
CRect classPanel(rect.left + pad, header.bottom + pad, rect.right - pad, header.bottom + pad + classH);
|
|
int classY = classPanel.top + 22;
|
|
int createW = 82;
|
|
MoveCtrl(comboClass, classPanel.left + inner, classY, classPanel.Width() - inner * 3 - createW, 22);
|
|
MoveCtrl(btnCreate, classPanel.right - inner - createW, classY, createW, 22);
|
|
|
|
int contentTop = classPanel.bottom + pad;
|
|
int contentBottom = rect.bottom - pad;
|
|
|
|
if (w >= 500) {
|
|
const int availableH = max(1, contentBottom - contentTop);
|
|
// No upper cap: the grids now consume the extra dock/inspector height instead of staying squeezed at the top.
|
|
int listH = max(180, availableH - editorH - minToolH - pad * 3);
|
|
int leftW = (w - pad * 3) / 2;
|
|
|
|
CRect varsPanel(rect.left + pad, contentTop, rect.left + pad + leftW, contentTop + listH);
|
|
CRect keyPanel(varsPanel.right + pad, contentTop, rect.right - pad, contentTop + listH);
|
|
|
|
MoveCtrl(listVars, varsPanel.left + inner, varsPanel.top + 28, varsPanel.Width() - inner * 2, varsPanel.Height() - 38);
|
|
MoveCtrl(listKeyVal, keyPanel.left + inner, keyPanel.top + 28, keyPanel.Width() - inner * 2, keyPanel.Height() - 38);
|
|
|
|
CRect editPanel(rect.left + pad, varsPanel.bottom + pad, rect.right - pad, varsPanel.bottom + pad + editorH);
|
|
|
|
int labelW = 52;
|
|
int browseW = 28;
|
|
int rowH = 22;
|
|
int rowY = editPanel.top + 30;
|
|
|
|
MoveCtrl(staticKey, editPanel.left + inner, rowY + 2, labelW, rowH);
|
|
MoveCtrl(editKey, editPanel.left + inner + labelW + 6, rowY, editPanel.Width() - inner * 2 - labelW - 6, rowH);
|
|
|
|
rowY += rowH + 10;
|
|
|
|
MoveCtrl(staticVal, editPanel.left + inner, rowY + 2, labelW, rowH);
|
|
MoveCtrl(editVal, editPanel.left + inner + labelW + 6, rowY, editPanel.Width() - inner * 2 - labelW - 6 - browseW - 6, rowH);
|
|
MoveCtrl(btnBrowse, editPanel.right - inner - browseW, rowY, browseW, rowH);
|
|
|
|
CRect toolsPanel(rect.left + pad, editPanel.bottom + pad, rect.right - pad, contentBottom);
|
|
|
|
int toolY = toolsPanel.top + 28;
|
|
int toolX = toolsPanel.left + inner;
|
|
|
|
int b = 30;
|
|
MoveCtrl(btn135, toolX, toolY, b, b);
|
|
MoveCtrl(btn90, toolX + b + 4, toolY, b, b);
|
|
MoveCtrl(btn45, toolX + (b + 4) * 2, toolY, b, b);
|
|
|
|
MoveCtrl(btn180, toolX, toolY + b + 4, b, b);
|
|
MoveCtrl(btn360, toolX + (b + 4) * 2, toolY + b + 4, b, b);
|
|
|
|
MoveCtrl(btn225, toolX, toolY + (b + 4) * 2, b, b);
|
|
MoveCtrl(btn270, toolX + b + 4, toolY + (b + 4) * 2, b, b);
|
|
MoveCtrl(btn315, toolX + (b + 4) * 2, toolY + (b + 4) * 2, b, b);
|
|
|
|
MoveCtrl(btnUp, toolX + (b + 4) * 3 + 8, toolY + 16, b + 8, b);
|
|
MoveCtrl(btnDown, toolX + (b + 4) * 3 + 8, toolY + 16 + b + 6, b + 8, b);
|
|
|
|
int mediaX = toolX + 190;
|
|
int mediaW = 76;
|
|
int mediaH = 24;
|
|
MoveCtrl(btnModel, mediaX, toolY, mediaW, mediaH);
|
|
MoveCtrl(btnSound, mediaX, toolY + mediaH + 6, mediaW, mediaH);
|
|
MoveCtrl(btnGui, mediaX, toolY + (mediaH + 6) * 2, mediaW, mediaH);
|
|
|
|
MoveCtrl(btnCurve, mediaX + mediaW + 8, toolY, mediaW, mediaH);
|
|
MoveCtrl(btnSkin, mediaX + mediaW + 8, toolY + mediaH + 6, mediaW, mediaH);
|
|
MoveCtrl(btnParticle, mediaX + mediaW + 8, toolY + (mediaH + 6) * 2, mediaW, mediaH);
|
|
|
|
int animX = mediaX + (mediaW + 8) * 2 + 18;
|
|
int animW = max(120, toolsPanel.right - animX - inner);
|
|
MoveCtrl(cbAnimations, animX, toolY, animW, 24);
|
|
MoveCtrl(btnPlayAnim, animX, toolY + 32, 54, 24);
|
|
MoveCtrl(btnStopAnim, animX + 60, toolY + 32, 54, 24);
|
|
MoveCtrl(slFrameSlider, animX, toolY + 66, max(80, animW - 44), 24);
|
|
MoveCtrl(staticFrame, animX + animW - 38, toolY + 68, 36, 20);
|
|
}
|
|
else {
|
|
int available = max(1, contentBottom - contentTop);
|
|
int listH = max(140, (available - editorH - minToolH - pad * 3) / 2);
|
|
|
|
CRect varsPanel(rect.left + pad, contentTop, rect.right - pad, contentTop + listH);
|
|
CRect keyPanel(rect.left + pad, varsPanel.bottom + pad, rect.right - pad, varsPanel.bottom + pad + listH);
|
|
|
|
MoveCtrl(listVars, varsPanel.left + inner, varsPanel.top + 28, varsPanel.Width() - inner * 2, varsPanel.Height() - 38);
|
|
MoveCtrl(listKeyVal, keyPanel.left + inner, keyPanel.top + 28, keyPanel.Width() - inner * 2, keyPanel.Height() - 38);
|
|
|
|
CRect editPanel(rect.left + pad, keyPanel.bottom + pad, rect.right - pad, keyPanel.bottom + pad + editorH);
|
|
|
|
int labelW = 48;
|
|
int browseW = 28;
|
|
int rowH = 22;
|
|
int rowY = editPanel.top + 30;
|
|
|
|
MoveCtrl(staticKey, editPanel.left + inner, rowY + 2, labelW, rowH);
|
|
MoveCtrl(editKey, editPanel.left + inner + labelW + 6, rowY, editPanel.Width() - inner * 2 - labelW - 6, rowH);
|
|
|
|
rowY += rowH + 10;
|
|
|
|
MoveCtrl(staticVal, editPanel.left + inner, rowY + 2, labelW, rowH);
|
|
MoveCtrl(editVal, editPanel.left + inner + labelW + 6, rowY, editPanel.Width() - inner * 2 - labelW - 6 - browseW - 6, rowH);
|
|
MoveCtrl(btnBrowse, editPanel.right - inner - browseW, rowY, browseW, rowH);
|
|
|
|
CRect toolsPanel(rect.left + pad, editPanel.bottom + pad, rect.right - pad, contentBottom);
|
|
|
|
int toolY = toolsPanel.top + 28;
|
|
int toolX = toolsPanel.left + inner;
|
|
int b = 26;
|
|
|
|
MoveCtrl(btn135, toolX, toolY, b, b);
|
|
MoveCtrl(btn90, toolX + b + 4, toolY, b, b);
|
|
MoveCtrl(btn45, toolX + (b + 4) * 2, toolY, b, b);
|
|
|
|
MoveCtrl(btn180, toolX, toolY + b + 4, b, b);
|
|
MoveCtrl(btn360, toolX + (b + 4) * 2, toolY + b + 4, b, b);
|
|
|
|
MoveCtrl(btn225, toolX, toolY + (b + 4) * 2, b, b);
|
|
MoveCtrl(btn270, toolX + b + 4, toolY + (b + 4) * 2, b, b);
|
|
MoveCtrl(btn315, toolX + (b + 4) * 2, toolY + (b + 4) * 2, b, b);
|
|
|
|
MoveCtrl(btnUp, toolX + (b + 4) * 3 + 6, toolY + 12, b + 8, b);
|
|
MoveCtrl(btnDown, toolX + (b + 4) * 3 + 6, toolY + 12 + b + 6, b + 8, b);
|
|
|
|
int mediaX = toolX + 152;
|
|
int mediaW = max(58, (toolsPanel.right - mediaX - inner - 8) / 2);
|
|
int mediaH = 22;
|
|
|
|
MoveCtrl(btnModel, mediaX, toolY, mediaW, mediaH);
|
|
MoveCtrl(btnSound, mediaX, toolY + mediaH + 5, mediaW, mediaH);
|
|
MoveCtrl(btnGui, mediaX, toolY + (mediaH + 5) * 2, mediaW, mediaH);
|
|
|
|
MoveCtrl(btnCurve, mediaX + mediaW + 8, toolY, mediaW, mediaH);
|
|
MoveCtrl(btnSkin, mediaX + mediaW + 8, toolY + mediaH + 5, mediaW, mediaH);
|
|
MoveCtrl(btnParticle, mediaX + mediaW + 8, toolY + (mediaH + 5) * 2, mediaW, mediaH);
|
|
|
|
int animY = toolY + 88;
|
|
MoveCtrl(cbAnimations, toolX, animY, toolsPanel.Width() - inner * 2, 22);
|
|
MoveCtrl(btnPlayAnim, toolX, animY + 28, 54, 22);
|
|
MoveCtrl(btnStopAnim, toolX + 60, animY + 28, 54, 22);
|
|
MoveCtrl(slFrameSlider, toolX + 120, animY + 28, toolsPanel.Width() - inner * 2 - 162, 22);
|
|
MoveCtrl(staticFrame, toolsPanel.right - inner - 36, animY + 30, 36, 18);
|
|
}
|
|
|
|
Invalidate(FALSE);
|
|
}
|
|
|
|
// --------------------------------------------------------------------------
|
|
// Entity/class/keyval behavior
|
|
// --------------------------------------------------------------------------
|
|
|
|
void CEntityDlg::AddClassNames() {
|
|
comboClass.ResetContent();
|
|
|
|
for (eclass_t* pec = eclass; pec; pec = pec->next) {
|
|
comboClass.AddString(pec->name);
|
|
}
|
|
}
|
|
|
|
void CEntityDlg::OnCbnSelchangeComboClass() {
|
|
int index = comboClass.GetCurSel();
|
|
|
|
if (index != LB_ERR) {
|
|
CString str;
|
|
comboClass.GetLBText(index, str);
|
|
|
|
eclass_t* ent = Eclass_ForName(str, false);
|
|
if (ent) {
|
|
if (selected_brushes.next == &selected_brushes) {
|
|
editEntity = world_entity;
|
|
multipleEntities = false;
|
|
}
|
|
else {
|
|
editEntity = selected_brushes.next->owner;
|
|
multipleEntities = false;
|
|
|
|
for (brush_t* b = selected_brushes.next->next; b != &selected_brushes; b = b->next) {
|
|
if (b->owner != editEntity) {
|
|
multipleEntities = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
listVars.ResetContent();
|
|
|
|
CPropertyItem* pi = new CPropertyItem("Usage:", ent->desc.c_str(), PIT_VAR, "");
|
|
listVars.AddPropItem(pi);
|
|
|
|
int c = ent->vars.Num();
|
|
for (int i = 0; i < c; i++) {
|
|
pi = new CPropertyItem(ent->vars[i].name.c_str(), ent->vars[i].desc.c_str(), PIT_VAR, "");
|
|
pi->SetData(ent->vars[i].type);
|
|
listVars.AddPropItem(pi);
|
|
}
|
|
|
|
listVars.Invalidate();
|
|
SetKeyValPairs();
|
|
}
|
|
}
|
|
}
|
|
|
|
const char* CEntityDlg::TranslateString(const char* buf) {
|
|
static char buf2[32768];
|
|
|
|
int l = strlen(buf);
|
|
char* out = buf2;
|
|
|
|
for (int i = 0; i < l; i++) {
|
|
if (buf[i] == '\n') {
|
|
*out++ = '\r';
|
|
*out++ = '\n';
|
|
}
|
|
else {
|
|
*out++ = buf[i];
|
|
}
|
|
}
|
|
|
|
*out++ = 0;
|
|
return buf2;
|
|
}
|
|
|
|
void CEntityDlg::UpdateFromListBox() {
|
|
if (editEntity == NULL) {
|
|
return;
|
|
}
|
|
|
|
int c = listKeyVal.GetCount();
|
|
for (int i = 0; i < c; i++) {
|
|
CPropertyItem* pItem = (CPropertyItem*)listKeyVal.GetItemDataPtr(i);
|
|
if (pItem) {
|
|
editEntity->epairs.Set(pItem->m_propName, pItem->m_curValue);
|
|
}
|
|
}
|
|
|
|
SetKeyValPairs();
|
|
}
|
|
|
|
void CEntityDlg::SetKeyValPairs(bool updateAnims) {
|
|
if (editEntity) {
|
|
listKeyVal.ResetContent();
|
|
|
|
int c = editEntity->epairs.GetNumKeyVals();
|
|
for (int i = 0; i < c; i++) {
|
|
const idKeyValue* kv = editEntity->epairs.GetKeyVal(i);
|
|
CPropertyItem* pi = new CPropertyItem(kv->GetKey().c_str(), kv->GetValue().c_str(), PIT_EDIT, "");
|
|
|
|
bool found = false;
|
|
int vc = editEntity->eclass->vars.Num();
|
|
|
|
for (int j = 0; j < vc; j++) {
|
|
if (editEntity->eclass->vars[j].name.Icmp(kv->GetKey()) == 0) {
|
|
switch (editEntity->eclass->vars[j].type) {
|
|
case EVAR_STRING:
|
|
case EVAR_INT:
|
|
case EVAR_FLOAT:
|
|
pi->m_nItemType = PIT_EDIT;
|
|
break;
|
|
|
|
case EVAR_BOOL:
|
|
pi->m_nItemType = PIT_EDIT;
|
|
break;
|
|
|
|
case EVAR_COLOR:
|
|
pi->m_nItemType = PIT_COLOR;
|
|
break;
|
|
|
|
case EVAR_MATERIAL:
|
|
pi->m_nItemType = PIT_MATERIAL;
|
|
break;
|
|
|
|
case EVAR_MODEL:
|
|
pi->m_nItemType = PIT_MODEL;
|
|
break;
|
|
|
|
case EVAR_GUI:
|
|
pi->m_nItemType = PIT_GUI;
|
|
break;
|
|
|
|
case EVAR_SOUND:
|
|
pi->m_nItemType = PIT_SOUND;
|
|
break;
|
|
}
|
|
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!found) {
|
|
if (kv->GetKey().Icmp("model") == 0) {
|
|
pi->m_nItemType = PIT_MODEL;
|
|
}
|
|
if (kv->GetKey().Icmp("_color") == 0) {
|
|
pi->m_nItemType = PIT_COLOR;
|
|
}
|
|
if (kv->GetKey().Icmp("gui") == 0) {
|
|
pi->m_nItemType = PIT_GUI;
|
|
}
|
|
if (kv->GetKey().Icmp("gui2") == 0) {
|
|
pi->m_nItemType = PIT_GUI;
|
|
}
|
|
if (kv->GetKey().Icmp("gui3") == 0) {
|
|
pi->m_nItemType = PIT_GUI;
|
|
}
|
|
if (kv->GetKey().Icmp("s_shader") == 0) {
|
|
pi->m_nItemType = PIT_SOUND;
|
|
}
|
|
}
|
|
|
|
listKeyVal.AddPropItem(pi);
|
|
}
|
|
|
|
if (updateAnims) {
|
|
int i, num;
|
|
|
|
cbAnimations.ResetContent();
|
|
|
|
num = gameEdit->ANIM_GetNumAnimsFromEntityDef(&editEntity->eclass->defArgs);
|
|
for (i = 0; i < num; i++) {
|
|
cbAnimations.AddString(gameEdit->ANIM_GetAnimNameFromEntityDef(&editEntity->eclass->defArgs, i));
|
|
}
|
|
|
|
const idKeyValue* kv = editEntity->epairs.FindKey("anim");
|
|
if (kv) {
|
|
int selIndex = cbAnimations.FindStringExact(0, kv->GetValue().c_str());
|
|
if (selIndex != -1) {
|
|
cbAnimations.SetCurSel(selIndex);
|
|
OnCbnAnimationChange();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void CEntityDlg::UpdateEntitySel(eclass_t* ent) {
|
|
assert(ent);
|
|
assert(ent->name);
|
|
|
|
int index = comboClass.FindString(-1, ent->name);
|
|
if (index != LB_ERR) {
|
|
comboClass.SetCurSel(index);
|
|
OnCbnSelchangeComboClass();
|
|
}
|
|
}
|
|
|
|
void CEntityDlg::OnLbnSelchangeListkeyval() {
|
|
int index = listKeyVal.GetCurSel();
|
|
|
|
if (index != LB_ERR) {
|
|
CString str;
|
|
listKeyVal.GetText(index, str);
|
|
|
|
int i;
|
|
for (i = 0; str[i] != '\t' && str[i] != '\0'; i++) {
|
|
}
|
|
|
|
idStr key = str.Left(i);
|
|
|
|
while (str[i] == '\t' && str[i] != '\0') {
|
|
i++;
|
|
}
|
|
|
|
idStr val = str.Right(str.GetLength() - i);
|
|
|
|
editKey.SetWindowText(key);
|
|
editVal.SetWindowText(val);
|
|
}
|
|
}
|
|
|
|
static int TabOrder[] = {
|
|
IDC_COMBO_CLASS,
|
|
IDC_BUTTON_CREATE,
|
|
IDC_LIST_KEYVAL,
|
|
IDC_EDIT_KEY,
|
|
IDC_EDIT_VAL,
|
|
IDC_BUTTON_BROWSE,
|
|
IDC_E_135,
|
|
IDC_E_90,
|
|
IDC_E_45,
|
|
IDC_E_180,
|
|
IDC_E_0,
|
|
IDC_E_225,
|
|
IDC_E_270,
|
|
IDC_E_315,
|
|
IDC_E_UP,
|
|
IDC_E_DOWN,
|
|
IDC_BUTTON_MODEL,
|
|
IDC_BUTTON_SOUND,
|
|
IDC_BUTTON_GUI,
|
|
IDC_ENTITY_ANIMATIONS
|
|
};
|
|
|
|
int TabCount = sizeof(TabOrder) / sizeof(int);
|
|
|
|
void CEntityDlg::DelProp() {
|
|
CString key;
|
|
|
|
if (editEntity == NULL) {
|
|
return;
|
|
}
|
|
|
|
editKey.GetWindowText(key);
|
|
|
|
if (multipleEntities) {
|
|
for (brush_t* b = selected_brushes.next; b != &selected_brushes; b = b->next) {
|
|
DeleteKey(b->owner, key);
|
|
Entity_UpdateCurveData(b->owner);
|
|
}
|
|
}
|
|
else {
|
|
DeleteKey(editEntity, key);
|
|
Entity_UpdateCurveData(editEntity);
|
|
}
|
|
|
|
SetKeyValPairs();
|
|
Sys_UpdateWindows(W_ENTITY | W_XY | W_CAMERA);
|
|
}
|
|
|
|
BOOL CEntityDlg::PreTranslateMessage(MSG* pMsg) {
|
|
if (pMsg->hwnd == editVal.GetSafeHwnd()) {
|
|
if (pMsg->message == WM_LBUTTONDOWN) {
|
|
editVal.SetFocus();
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
if (pMsg->hwnd == editKey.GetSafeHwnd()) {
|
|
if (pMsg->message == WM_LBUTTONDOWN) {
|
|
editKey.SetFocus();
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
if (GetFocus() == &editVal || GetFocus() == &editKey) {
|
|
if (pMsg->message == WM_KEYDOWN && pMsg->wParam == VK_RETURN) {
|
|
AddProp();
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
if (GetFocus() == listKeyVal.GetEditBox()) {
|
|
if (pMsg->message == WM_KEYDOWN && pMsg->wParam == VK_RETURN) {
|
|
listKeyVal.OnChangeEditBox();
|
|
listKeyVal.OnSelchange();
|
|
listKeyVal.OnKillfocusEditBox();
|
|
AddProp();
|
|
SetKeyValPairs();
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
if (GetFocus() == &listKeyVal) {
|
|
if (pMsg->message == WM_KEYDOWN && pMsg->wParam == VK_DELETE && editEntity) {
|
|
DelProp();
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
if (pMsg->message == WM_KEYDOWN && pMsg->wParam == VK_ESCAPE) {
|
|
g_pParentWnd->GetCamera()->SetFocus();
|
|
Select_Deselect();
|
|
return TRUE;
|
|
}
|
|
|
|
if (pMsg->message == WM_KEYDOWN && pMsg->wParam == VK_RETURN) {
|
|
return TRUE;
|
|
}
|
|
|
|
if (pMsg->message == WM_KEYDOWN && pMsg->wParam == VK_TAB) {
|
|
if (GetFocus()) {
|
|
int id = GetFocus()->GetDlgCtrlID();
|
|
|
|
for (int i = 0; i < TabCount; i++) {
|
|
if (TabOrder[i] == id) {
|
|
i++;
|
|
if (i >= TabCount) {
|
|
i = 0;
|
|
}
|
|
|
|
CWnd* next = GetDlgItem(TabOrder[i]);
|
|
if (next) {
|
|
next->SetFocus();
|
|
|
|
if (TabOrder[i] == IDC_EDIT_VAL) {
|
|
editVal.SetSel(0, -1);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (pMsg->message == WM_KEYDOWN && pMsg->wParam == VK_RIGHT && pMsg->hwnd == slFrameSlider.GetSafeHwnd()) {
|
|
int maxRange = slFrameSlider.GetRangeMax();
|
|
if (maxRange <= 0) {
|
|
return TRUE;
|
|
}
|
|
|
|
int pos = slFrameSlider.GetPos() + 1;
|
|
pos = (pos % maxRange);
|
|
slFrameSlider.SetPos(pos);
|
|
UpdateFromAnimationFrame();
|
|
return TRUE;
|
|
}
|
|
|
|
if (pMsg->message == WM_KEYDOWN && pMsg->wParam == VK_LEFT && pMsg->hwnd == slFrameSlider.GetSafeHwnd()) {
|
|
int maxRange = slFrameSlider.GetRangeMax();
|
|
if (maxRange <= 0) {
|
|
return TRUE;
|
|
}
|
|
|
|
int pos = slFrameSlider.GetPos() - 1;
|
|
|
|
if (pos < 1) {
|
|
pos = maxRange;
|
|
}
|
|
|
|
slFrameSlider.SetPos(pos);
|
|
UpdateFromAnimationFrame();
|
|
return TRUE;
|
|
}
|
|
|
|
return CDialog::PreTranslateMessage(pMsg);
|
|
}
|
|
|
|
void CEntityDlg::AddProp() {
|
|
if (editEntity == NULL) {
|
|
return;
|
|
}
|
|
|
|
CString Key, Value;
|
|
editKey.GetWindowText(Key);
|
|
editVal.GetWindowText(Value);
|
|
|
|
bool isName = (stricmp(Key, "name") == 0);
|
|
bool isModel = static_cast<bool>((stricmp(Key, "model") == 0 && Value.GetLength() > 0));
|
|
bool isOrigin = (idStr::Icmp(Key, "origin") == 0);
|
|
|
|
if (multipleEntities) {
|
|
for (brush_t* b = selected_brushes.next; b != &selected_brushes; b = b->next) {
|
|
if (isName) {
|
|
Entity_SetName(b->owner, Value);
|
|
}
|
|
else {
|
|
if (!((isModel || isOrigin) && (b->owner->eclass->nShowFlags & ECLASS_WORLDSPAWN))) {
|
|
SetKeyValue(b->owner, Key, Value);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
if (isName) {
|
|
Entity_SetName(editEntity, Value);
|
|
}
|
|
else {
|
|
if (!((isModel || isOrigin) && (editEntity->eclass->nShowFlags & ECLASS_WORLDSPAWN))) {
|
|
SetKeyValue(editEntity, Key, Value);
|
|
}
|
|
}
|
|
|
|
if (isModel && !(editEntity->eclass->nShowFlags & ECLASS_WORLDSPAWN)) {
|
|
idBounds bo;
|
|
idVec3 mins, maxs;
|
|
|
|
selected_brushes.next->modelHandle = renderModelManager->FindModel(Value);
|
|
|
|
if (dynamic_cast<idRenderModelPrt*>(selected_brushes.next->modelHandle) ||
|
|
dynamic_cast<idRenderModelLiquid*>(selected_brushes.next->modelHandle)) {
|
|
bo.Zero();
|
|
bo.ExpandSelf(12.0f);
|
|
}
|
|
else {
|
|
bo = selected_brushes.next->modelHandle->Bounds(NULL);
|
|
}
|
|
|
|
VectorCopy(bo[0], mins);
|
|
VectorCopy(bo[1], maxs);
|
|
VectorAdd(mins, editEntity->origin, mins);
|
|
VectorAdd(maxs, editEntity->origin, maxs);
|
|
|
|
Brush_RebuildBrush(selected_brushes.next, mins, maxs, false);
|
|
Brush_Build(selected_brushes.next, false, false, false, true);
|
|
}
|
|
}
|
|
|
|
SetKeyValPairs();
|
|
Sys_UpdateWindows(W_ALL);
|
|
}
|
|
|
|
const char* CEntityDlg::AngleKey() {
|
|
if (editEntity == NULL) {
|
|
return "";
|
|
}
|
|
|
|
if (editEntity->eclass->nShowFlags & ECLASS_MOVER) {
|
|
return "movedir";
|
|
}
|
|
|
|
return "angle";
|
|
}
|
|
|
|
void CEntityDlg::OnBnClickedE135() {
|
|
if (editEntity == NULL) {
|
|
return;
|
|
}
|
|
|
|
editKey.SetWindowText(AngleKey());
|
|
editVal.SetWindowText("135");
|
|
AddProp();
|
|
}
|
|
|
|
void CEntityDlg::OnBnClickedE90() {
|
|
if (editEntity == NULL) {
|
|
return;
|
|
}
|
|
|
|
editKey.SetWindowText(AngleKey());
|
|
editVal.SetWindowText("90");
|
|
AddProp();
|
|
}
|
|
|
|
void CEntityDlg::OnBnClickedE45() {
|
|
if (editEntity == NULL) {
|
|
return;
|
|
}
|
|
|
|
editKey.SetWindowText(AngleKey());
|
|
editVal.SetWindowText("45");
|
|
AddProp();
|
|
}
|
|
|
|
void CEntityDlg::OnBnClickedE180() {
|
|
if (editEntity == NULL) {
|
|
return;
|
|
}
|
|
|
|
editKey.SetWindowText(AngleKey());
|
|
editVal.SetWindowText("180");
|
|
AddProp();
|
|
}
|
|
|
|
void CEntityDlg::OnBnClickedE0() {
|
|
if (editEntity == NULL) {
|
|
return;
|
|
}
|
|
|
|
editKey.SetWindowText(AngleKey());
|
|
editVal.SetWindowText("0");
|
|
AddProp();
|
|
}
|
|
|
|
void CEntityDlg::OnBnClickedE225() {
|
|
if (editEntity == NULL) {
|
|
return;
|
|
}
|
|
|
|
editKey.SetWindowText(AngleKey());
|
|
editVal.SetWindowText("225");
|
|
AddProp();
|
|
}
|
|
|
|
void CEntityDlg::OnBnClickedE270() {
|
|
if (editEntity == NULL) {
|
|
return;
|
|
}
|
|
|
|
editKey.SetWindowText(AngleKey());
|
|
editVal.SetWindowText("270");
|
|
AddProp();
|
|
}
|
|
|
|
void CEntityDlg::OnBnClickedE315() {
|
|
if (editEntity == NULL) {
|
|
return;
|
|
}
|
|
|
|
editKey.SetWindowText(AngleKey());
|
|
editVal.SetWindowText("315");
|
|
AddProp();
|
|
}
|
|
|
|
void CEntityDlg::OnBnClickedEUp() {
|
|
if (editEntity == NULL) {
|
|
return;
|
|
}
|
|
|
|
editKey.SetWindowText(AngleKey());
|
|
editVal.SetWindowText("-1");
|
|
AddProp();
|
|
}
|
|
|
|
void CEntityDlg::OnBnClickedEDown() {
|
|
if (editEntity == NULL) {
|
|
return;
|
|
}
|
|
|
|
editKey.SetWindowText(AngleKey());
|
|
editVal.SetWindowText("-2");
|
|
AddProp();
|
|
}
|
|
|
|
// --------------------------------------------------------------------------
|
|
// Choosers
|
|
// --------------------------------------------------------------------------
|
|
|
|
CPreviewDlg* CEntityDlg::ShowModelChooser() {
|
|
static CPreviewDlg modelDlg;
|
|
modelDlg.SetMode(CPreviewDlg::MODELS);
|
|
modelDlg.SetModal();
|
|
|
|
if (modelDlg.GetSafeHwnd() == NULL) {
|
|
modelDlg.Create(MAKEINTRESOURCE(IDD_DIALOG_PREVIEW));
|
|
}
|
|
|
|
modelDlg.ShowWindow(SW_SHOW);
|
|
modelDlg.BringWindowToTop();
|
|
|
|
while (modelDlg.Waiting()) {
|
|
}
|
|
|
|
return &modelDlg;
|
|
}
|
|
|
|
CPreviewDlg* CEntityDlg::ShowParticleChooser() {
|
|
static CPreviewDlg modelDlg;
|
|
modelDlg.SetMode(CPreviewDlg::PARTICLES);
|
|
modelDlg.SetModal();
|
|
|
|
if (modelDlg.GetSafeHwnd() == NULL) {
|
|
modelDlg.Create(MAKEINTRESOURCE(IDD_DIALOG_PREVIEW));
|
|
}
|
|
|
|
modelDlg.ShowWindow(SW_SHOW);
|
|
modelDlg.BringWindowToTop();
|
|
|
|
while (modelDlg.Waiting()) {
|
|
}
|
|
|
|
return &modelDlg;
|
|
}
|
|
|
|
CPreviewDlg* CEntityDlg::ShowSkinChooser(entity_t* ent) {
|
|
static CPreviewDlg modelDlg;
|
|
modelDlg.SetMode(CPreviewDlg::SKINS);
|
|
modelDlg.SetModal();
|
|
|
|
if (modelDlg.GetSafeHwnd() == NULL) {
|
|
modelDlg.Create(MAKEINTRESOURCE(IDD_DIALOG_PREVIEW));
|
|
}
|
|
|
|
modelDlg.RebuildTree((ent) ? ent->epairs.GetString("model") : "");
|
|
modelDlg.ShowWindow(SW_SHOW);
|
|
modelDlg.BringWindowToTop();
|
|
|
|
while (modelDlg.Waiting()) {
|
|
}
|
|
|
|
return &modelDlg;
|
|
}
|
|
|
|
CPreviewDlg* CEntityDlg::ShowGuiChooser() {
|
|
static CPreviewDlg guiDlg;
|
|
guiDlg.SetMode(CPreviewDlg::GUIS);
|
|
guiDlg.SetModal();
|
|
|
|
if (guiDlg.GetSafeHwnd() == NULL) {
|
|
guiDlg.Create(MAKEINTRESOURCE(IDD_DIALOG_PREVIEW));
|
|
}
|
|
|
|
guiDlg.ShowWindow(SW_SHOW);
|
|
guiDlg.BringWindowToTop();
|
|
|
|
while (guiDlg.Waiting()) {
|
|
}
|
|
|
|
return &guiDlg;
|
|
}
|
|
|
|
CPreviewDlg* CEntityDlg::ShowSoundChooser() {
|
|
static CPreviewDlg soundDlg;
|
|
soundDlg.SetMode(CPreviewDlg::SOUNDS);
|
|
soundDlg.SetModal();
|
|
|
|
if (soundDlg.GetSafeHwnd() == NULL) {
|
|
soundDlg.Create(MAKEINTRESOURCE(IDD_DIALOG_PREVIEW));
|
|
}
|
|
|
|
soundDlg.ShowWindow(SW_SHOW);
|
|
|
|
while (soundDlg.Waiting()) {
|
|
}
|
|
|
|
return &soundDlg;
|
|
}
|
|
|
|
CPreviewDlg* CEntityDlg::ShowMaterialChooser() {
|
|
static CPreviewDlg matDlg;
|
|
matDlg.SetMode(CPreviewDlg::MATERIALS);
|
|
matDlg.SetModal();
|
|
|
|
if (matDlg.GetSafeHwnd() == NULL) {
|
|
matDlg.Create(MAKEINTRESOURCE(IDD_DIALOG_PREVIEW));
|
|
}
|
|
|
|
matDlg.ShowWindow(SW_SHOW);
|
|
matDlg.BringWindowToTop();
|
|
|
|
while (matDlg.Waiting()) {
|
|
}
|
|
|
|
return &matDlg;
|
|
}
|
|
|
|
void CEntityDlg::AssignModel() {
|
|
OnBnClickedButtonModel();
|
|
}
|
|
|
|
void CEntityDlg::OnBnClickedButtonModel() {
|
|
CPreviewDlg* dlg = ShowModelChooser();
|
|
|
|
if (dlg->returnCode == IDOK) {
|
|
editKey.SetWindowText("model");
|
|
editVal.SetWindowText(dlg->mediaName);
|
|
AddProp();
|
|
}
|
|
}
|
|
|
|
void CEntityDlg::OnBnClickedButtonSound() {
|
|
CPreviewDlg* dlg = ShowSoundChooser();
|
|
|
|
if (dlg->returnCode == IDOK) {
|
|
editKey.SetWindowText("s_shader");
|
|
editVal.SetWindowText(dlg->mediaName);
|
|
AddProp();
|
|
}
|
|
}
|
|
|
|
void CEntityDlg::OnBnClickedButtonGui() {
|
|
CPreviewDlg* dlg = ShowGuiChooser();
|
|
|
|
if (dlg->returnCode == IDOK) {
|
|
editKey.SetWindowText("gui");
|
|
editVal.SetWindowText(dlg->mediaName);
|
|
AddProp();
|
|
}
|
|
}
|
|
|
|
void CEntityDlg::OnBnClickedButtonParticle() {
|
|
CPreviewDlg* dlg = ShowParticleChooser();
|
|
|
|
if (dlg->returnCode == IDOK) {
|
|
editKey.SetWindowText("model");
|
|
editVal.SetWindowText(dlg->mediaName);
|
|
AddProp();
|
|
}
|
|
}
|
|
|
|
void CEntityDlg::OnBnClickedButtonSkin() {
|
|
CPreviewDlg* dlg = ShowSkinChooser(editEntity);
|
|
|
|
if (dlg->returnCode == IDOK) {
|
|
editKey.SetWindowText("skin");
|
|
editVal.SetWindowText(dlg->mediaName);
|
|
AddProp();
|
|
}
|
|
}
|
|
|
|
void CEntityDlg::OnBnClickedButtonCurve() {
|
|
CCurveDlg dlg;
|
|
|
|
if (dlg.DoModal() == IDOK) {
|
|
if (editEntity) {
|
|
idStr str = "curve_" + dlg.strCurveType;
|
|
editKey.SetWindowText(str);
|
|
|
|
idVec3 org = editEntity->origin;
|
|
str = "3 ( ";
|
|
str += org.ToString();
|
|
|
|
org.x += 64;
|
|
str += " ";
|
|
str += org.ToString();
|
|
|
|
org.y += 64;
|
|
str += " ";
|
|
str += org.ToString();
|
|
|
|
str += " )";
|
|
|
|
editVal.SetWindowText(str);
|
|
AddProp();
|
|
Entity_SetCurveData(editEntity);
|
|
}
|
|
}
|
|
}
|
|
|
|
void CEntityDlg::OnBnClickedButtonBrowse() {
|
|
DelProp();
|
|
}
|
|
|
|
void CEntityDlg::OnCbnDblclkComboClass() {
|
|
}
|
|
|
|
// --------------------------------------------------------------------------
|
|
// Entity creation
|
|
// --------------------------------------------------------------------------
|
|
|
|
void CEntityDlg::CreateEntity() {
|
|
entity_t* petNew;
|
|
bool forceFixed = false;
|
|
|
|
CXYWnd* pWnd = g_pParentWnd->ActiveXY();
|
|
if (pWnd) {
|
|
CRect rctZ;
|
|
pWnd->GetClientRect(rctZ);
|
|
|
|
if (selected_brushes.next == &selected_brushes) {
|
|
CreateEntityBrush(g_nSmartX, rctZ.Height() - 1 - g_nSmartY, pWnd);
|
|
forceFixed = true;
|
|
}
|
|
}
|
|
else {
|
|
if (selected_brushes.next == &selected_brushes) {
|
|
MessageBox("You must have a selected brush to create an entity", "info", 0);
|
|
return;
|
|
}
|
|
}
|
|
|
|
int index = comboClass.GetCurSel();
|
|
if (index == LB_ERR) {
|
|
MessageBox("You must have a selected class to create an entity", "info", 0);
|
|
return;
|
|
}
|
|
|
|
CString str;
|
|
comboClass.GetLBText(index, str);
|
|
|
|
if (!stricmp(str, "worldspawn")) {
|
|
MessageBox("Can't create an entity with worldspawn.", "info", 0);
|
|
return;
|
|
}
|
|
|
|
eclass_t* pecNew = Eclass_ForName(str, false);
|
|
|
|
if ((GetAsyncKeyState(VK_CONTROL) & 0x8000)) {
|
|
extern void Brush_CopyList(brush_t * pFrom, brush_t * pTo);
|
|
|
|
brush_t temp_brushes;
|
|
temp_brushes.next = &temp_brushes;
|
|
|
|
Brush_CopyList(&selected_brushes, &temp_brushes);
|
|
Select_Deselect();
|
|
|
|
brush_t* pBrush = temp_brushes.next;
|
|
while (pBrush != NULL && pBrush != &temp_brushes) {
|
|
brush_t* pNext = pBrush->next;
|
|
|
|
Brush_RemoveFromList(pBrush);
|
|
Brush_AddToList(pBrush, &selected_brushes);
|
|
|
|
pBrush = pNext;
|
|
|
|
petNew = Entity_Create(pecNew, forceFixed);
|
|
Select_Deselect();
|
|
}
|
|
}
|
|
else if ((GetAsyncKeyState(VK_SHIFT) & 0x8000)) {
|
|
Select_Ungroup();
|
|
petNew = Entity_Create(pecNew, forceFixed);
|
|
}
|
|
else {
|
|
petNew = Entity_Create(pecNew, forceFixed);
|
|
}
|
|
|
|
if (petNew == NULL) {
|
|
MessageBox("Failed to create entity.", "info", 0);
|
|
return;
|
|
}
|
|
|
|
if (selected_brushes.next == &selected_brushes) {
|
|
editEntity = world_entity;
|
|
}
|
|
else {
|
|
editEntity = selected_brushes.next->owner;
|
|
}
|
|
|
|
SetKeyValPairs();
|
|
Select_Deselect();
|
|
Select_Brush(editEntity->brushes.onext);
|
|
Sys_UpdateWindows(W_ALL);
|
|
}
|
|
|
|
void CEntityDlg::OnBnClickedButtonCreate() {
|
|
CreateEntity();
|
|
}
|
|
|
|
void CEntityDlg::OnLbnDblclkListkeyval() {
|
|
CString Key, Value;
|
|
idStr work;
|
|
|
|
editKey.GetWindowText(Key);
|
|
editVal.GetWindowText(Value);
|
|
|
|
if (stricmp(Key, "script") == 0) {
|
|
Key = Value;
|
|
Value = "script/" + Key;
|
|
|
|
if (fileSystem->ReadFile(Value, NULL, NULL) == -1) {
|
|
sprintf(work, "// Script for %s\n// \n\nvoid main() {\n\n}\n\n", currentmap);
|
|
fileSystem->WriteFile(Value, work.c_str(), work.Length(), "fs_devpath");
|
|
}
|
|
|
|
work = fileSystem->RelativePathToOSPath(Value);
|
|
WinExec(va("notepad.exe %s", work.c_str()), SW_SHOW);
|
|
}
|
|
}
|
|
|
|
void CEntityDlg::OnLbnSelchangeListVars() {
|
|
}
|
|
|
|
void CEntityDlg::OnLbnDblclkListVars() {
|
|
if (editEntity == NULL) {
|
|
return;
|
|
}
|
|
|
|
int sel = listVars.GetCurSel();
|
|
if (sel == LB_ERR) {
|
|
return;
|
|
}
|
|
|
|
CPropertyItem* pi = (CPropertyItem*)listVars.GetItemDataPtr(sel);
|
|
if (pi) {
|
|
if (editEntity->epairs.FindKey(pi->m_propName) == NULL) {
|
|
editKey.SetWindowText(pi->m_propName);
|
|
editVal.SetWindowText("");
|
|
editVal.SetFocus();
|
|
}
|
|
}
|
|
}
|
|
|
|
void CEntityDlg::UpdateKeyVal(const char* key, const char* val) {
|
|
if (editEntity) {
|
|
editEntity->epairs.Set(key, val);
|
|
SetKeyValPairs();
|
|
|
|
g_pParentWnd->GetCamera()->BuildEntityRenderState(editEntity, true);
|
|
Entity_UpdateSoundEmitter(editEntity);
|
|
}
|
|
}
|
|
|
|
// --------------------------------------------------------------------------
|
|
// Animation
|
|
// --------------------------------------------------------------------------
|
|
|
|
void CEntityDlg::OnNMReleasedcaptureSlider1(NMHDR* pNMHDR, LRESULT* pResult) {
|
|
if (!editEntity) {
|
|
return;
|
|
}
|
|
|
|
UpdateFromAnimationFrame();
|
|
|
|
*pResult = 0;
|
|
}
|
|
|
|
void CEntityDlg::UpdateFromAnimationFrame(bool updateKeyValueDisplay) {
|
|
if (!editEntity) {
|
|
return;
|
|
}
|
|
|
|
int frame = slFrameSlider.GetPos();
|
|
|
|
editEntity->epairs.SetInt("frame", frame);
|
|
SetDlgItemText(IDC_ENTITY_CURRENT_ANIM, va("%i", frame));
|
|
|
|
if (updateKeyValueDisplay) {
|
|
SetKeyValPairs();
|
|
}
|
|
|
|
g_pParentWnd->GetCamera()->BuildEntityRenderState(editEntity, true);
|
|
Sys_UpdateWindows(W_ALL);
|
|
}
|
|
|
|
void CEntityDlg::OnCbnAnimationChange() {
|
|
if (!editEntity) {
|
|
return;
|
|
}
|
|
|
|
int sel = cbAnimations.GetCurSel();
|
|
CString animName;
|
|
|
|
currentAnimation = NULL;
|
|
|
|
int currFrame = 0;
|
|
|
|
if (sel != -1) {
|
|
cbAnimations.GetLBText(sel, animName);
|
|
|
|
if (animName.GetLength() > 0) {
|
|
currFrame = editEntity->epairs.GetInt("frame", "1");
|
|
|
|
editEntity->epairs.Set("anim", animName.GetBuffer(0));
|
|
SetKeyValPairs(false);
|
|
|
|
currentAnimation = gameEdit->ANIM_GetAnimFromEntityDef(editEntity->eclass->name, animName.GetBuffer(0));
|
|
currentAnimationFrame = 0;
|
|
|
|
if (currentAnimation) {
|
|
slFrameSlider.SetRange(1, gameEdit->ANIM_GetNumFrames(currentAnimation), TRUE);
|
|
slFrameSlider.SetPos(currFrame);
|
|
currentAnimationFrame = currFrame;
|
|
}
|
|
|
|
Sys_UpdateWindows(W_ALL);
|
|
}
|
|
}
|
|
}
|
|
|
|
void CEntityDlg::OnBnClickedStartAnimation() {
|
|
if (!editEntity) {
|
|
return;
|
|
}
|
|
|
|
SetTimer(0, 1000 / 24, NULL);
|
|
}
|
|
|
|
void CEntityDlg::OnBnClickedStopAnimation() {
|
|
KillTimer(0);
|
|
}
|
|
|
|
void CEntityDlg::OnTimer(UINT_PTR nIDEvent) {
|
|
if (!editEntity) {
|
|
OnBnClickedStopAnimation();
|
|
return;
|
|
}
|
|
|
|
if (currentAnimation) {
|
|
int numFrames = gameEdit->ANIM_GetNumFrames(currentAnimation);
|
|
if (numFrames <= 0) {
|
|
return;
|
|
}
|
|
|
|
currentAnimationFrame = ((currentAnimationFrame++) % numFrames);
|
|
|
|
editEntity->epairs.SetInt("frame", currentAnimationFrame);
|
|
slFrameSlider.SetPos(currentAnimationFrame);
|
|
|
|
UpdateFromAnimationFrame(false);
|
|
|
|
Sys_UpdateWindows(W_CAMERA | W_XY);
|
|
}
|
|
}
|
|
|
|
// --------------------------------------------------------------------------
|
|
// Curves
|
|
// --------------------------------------------------------------------------
|
|
|
|
void CEntityDlg::AddCurvePoints() {
|
|
if (editEntity == NULL || editEntity->curve == NULL) {
|
|
return;
|
|
}
|
|
|
|
int c = editEntity->curve->GetNumValues();
|
|
|
|
idVec3 start;
|
|
idVec3 end;
|
|
|
|
if (c > 1) {
|
|
start = editEntity->curve->GetValue(c - 2);
|
|
end = editEntity->curve->GetValue(c - 1);
|
|
|
|
idVec3 dir = end - start;
|
|
dir.Normalize();
|
|
|
|
start = end + 64 * dir;
|
|
}
|
|
else if (c > 0) {
|
|
start = editEntity->curve->GetValue(0);
|
|
start.x += 64;
|
|
start.y += 64;
|
|
}
|
|
else {
|
|
start = editEntity->origin;
|
|
}
|
|
|
|
editEntity->curve->AddValue(editEntity->curve->GetNumValues() * 100, start);
|
|
|
|
if (g_qeglobals.d_select_mode == sel_editpoint) {
|
|
g_qeglobals.d_select_mode = sel_brush;
|
|
EditCurvePoints();
|
|
}
|
|
|
|
Sys_UpdateWindows(W_CAMERA | W_XY);
|
|
}
|
|
|
|
void CEntityDlg::EditCurvePoints() {
|
|
if (editEntity == NULL || editEntity->curve == NULL) {
|
|
return;
|
|
}
|
|
|
|
if (g_qeglobals.d_select_mode == sel_editpoint) {
|
|
g_qeglobals.d_select_mode = sel_brush;
|
|
return;
|
|
}
|
|
|
|
g_qeglobals.d_select_mode = sel_editpoint;
|
|
|
|
g_qeglobals.d_numpoints = 0;
|
|
g_qeglobals.d_num_move_points = 0;
|
|
|
|
int c = editEntity->curve->GetNumValues();
|
|
|
|
for (int i = 0; i < c; i++) {
|
|
if (g_qeglobals.d_numpoints < MAX_POINTS - 1) {
|
|
g_qeglobals.d_points[g_qeglobals.d_numpoints++] = editEntity->curve->GetValue(i);
|
|
}
|
|
}
|
|
|
|
Sys_UpdateWindows(W_XY | W_CAMERA);
|
|
}
|
|
|
|
void CEntityDlg::InsertCurvePoint() {
|
|
if (editEntity == NULL || editEntity->curve == NULL) {
|
|
return;
|
|
}
|
|
|
|
if (g_qeglobals.d_select_mode != sel_editpoint) {
|
|
return;
|
|
}
|
|
|
|
if (g_qeglobals.d_num_move_points == 0) {
|
|
return;
|
|
}
|
|
|
|
for (int i = 0; i < editEntity->curve->GetNumValues(); i++) {
|
|
if (PointInMoveList(editEntity->curve->GetValueAddress(i)) >= 0) {
|
|
if (i == editEntity->curve->GetNumValues() - 1) {
|
|
AddCurvePoints();
|
|
}
|
|
else {
|
|
idCurve<idVec3>* newCurve = Entity_MakeCurve(editEntity);
|
|
|
|
if (newCurve == NULL) {
|
|
return;
|
|
}
|
|
|
|
for (int j = 0; j < editEntity->curve->GetNumValues(); j++) {
|
|
if (j == i) {
|
|
idVec3 start;
|
|
idVec3 end;
|
|
|
|
if (i > 0) {
|
|
start = editEntity->curve->GetValue(i - 1);
|
|
end = editEntity->curve->GetValue(i);
|
|
|
|
start += end;
|
|
start *= 0.5f;
|
|
}
|
|
else {
|
|
start = editEntity->curve->GetValue(0);
|
|
|
|
if (editEntity->curve->GetNumValues() > 1) {
|
|
end = start;
|
|
start = editEntity->curve->GetValue(1);
|
|
|
|
idVec3 dir = end - start;
|
|
dir.Normalize();
|
|
|
|
start = end + 64 * dir;
|
|
}
|
|
else {
|
|
end = start;
|
|
end.x += 64;
|
|
end.y += 64;
|
|
}
|
|
}
|
|
|
|
newCurve->AddValue(newCurve->GetNumValues() * 100, start);
|
|
}
|
|
|
|
newCurve->AddValue(newCurve->GetNumValues() * 100, editEntity->curve->GetValue(j));
|
|
}
|
|
|
|
delete editEntity->curve;
|
|
editEntity->curve = newCurve;
|
|
}
|
|
|
|
g_qeglobals.d_num_move_points = 0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
UpdateEntityCurve();
|
|
|
|
Sys_UpdateWindows(W_XY | W_CAMERA);
|
|
}
|
|
|
|
void CEntityDlg::DeleteCurvePoint() {
|
|
if (editEntity == NULL || editEntity->curve == NULL) {
|
|
return;
|
|
}
|
|
|
|
if (g_qeglobals.d_select_mode != sel_editpoint) {
|
|
return;
|
|
}
|
|
|
|
if (g_qeglobals.d_num_move_points == 0) {
|
|
return;
|
|
}
|
|
|
|
for (int i = 0; i < editEntity->curve->GetNumValues(); i++) {
|
|
if (PointInMoveList(editEntity->curve->GetValueAddress(i)) >= 0) {
|
|
editEntity->curve->RemoveIndex(i);
|
|
g_qeglobals.d_num_move_points = 0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
UpdateEntityCurve();
|
|
|
|
Sys_UpdateWindows(W_XY | W_CAMERA);
|
|
}
|
|
|
|
void CEntityDlg::UpdateEntityCurve() {
|
|
if (editEntity == NULL) {
|
|
return;
|
|
}
|
|
|
|
Entity_UpdateCurveData(editEntity);
|
|
|
|
if (g_qeglobals.d_select_mode == sel_editpoint) {
|
|
g_qeglobals.d_numpoints = 0;
|
|
|
|
int c = editEntity->curve->GetNumValues();
|
|
|
|
for (int i = 0; i < c; i++) {
|
|
if (g_qeglobals.d_numpoints < MAX_POINTS - 1) {
|
|
g_qeglobals.d_points[g_qeglobals.d_numpoints++] = editEntity->curve->GetValue(i);
|
|
}
|
|
}
|
|
}
|
|
|
|
Sys_UpdateWindows(W_ENTITY);
|
|
}
|
|
|
|
void CEntityDlg::SelectCurvePointByRay(const idVec3& org, const idVec3& dir, int buttons) {
|
|
if (editEntity == NULL) {
|
|
return;
|
|
}
|
|
|
|
int besti = -1;
|
|
|
|
float scale = g_pParentWnd->ActiveXY()->Scale();
|
|
float bestd = 8 / scale / 2;
|
|
|
|
for (int i = 0; i < g_qeglobals.d_numpoints; i++) {
|
|
idVec3 temp = g_qeglobals.d_points[i] - org;
|
|
|
|
float d = temp * dir;
|
|
|
|
temp = org + d * dir;
|
|
temp = g_qeglobals.d_points[i] - temp;
|
|
|
|
d = temp.Length();
|
|
|
|
if (d <= bestd) {
|
|
bestd = d;
|
|
besti = i;
|
|
}
|
|
}
|
|
|
|
if (besti == -1) {
|
|
return;
|
|
}
|
|
|
|
g_qeglobals.d_num_move_points = 0;
|
|
|
|
assert(besti < editEntity->curve->GetNumValues());
|
|
|
|
g_qeglobals.d_move_points[g_qeglobals.d_num_move_points++] = editEntity->curve->GetValueAddress(besti);
|
|
} |