Files
DoomRTX/neo/engine/tools/decl/DialogDeclEditor.cpp
T
2026-05-18 14:02:06 -07:00

1496 lines
43 KiB
C++

/*
===========================================================================
IceTech GPL Source Code
Copyright (C) 2026 Justin Marshall
This file is part of the IceTech GPL Source Code (?IceTech Source Code?).
IceTech Source Code is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
IceTech Source Code is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with IceTech Source Code. If not, see <http://www.gnu.org/licenses/>.
In addition, the IceTech Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the IceTech Source Code. If not, please request a copy in writing from id Software at the address below.
If you have questions concerning this license or the applicable additional terms, you may contact in writing Justin Marshall, justinmarshall20@gmail.com
===========================================================================
*/
#include "precompiled.h"
#pragma hdrstop
#include "../radiant/qe3.h"
#include "DialogDeclEditor.h"
#ifdef ID_DEBUG_MEMORY
#undef new
#undef DEBUG_NEW
#define DEBUG_NEW new
#endif
void DeclBrowserReloadDeclarations( void );
static const COLORREF DECL_EDITOR_BG = RGB( 17, 19, 23 );
static const COLORREF DECL_EDITOR_PANEL_BG = RGB( 23, 26, 31 );
static const COLORREF DECL_EDITOR_FIELD_BG = RGB( 14, 16, 20 );
static const COLORREF DECL_EDITOR_TEXT = RGB( 226, 232, 240 );
static const COLORREF DECL_EDITOR_MUTED_TEXT = RGB( 151, 163, 184 );
static const COLORREF DECL_EDITOR_BORDER = RGB( 58, 64, 72 );
static const COLORREF DECL_EDITOR_ACCENT = RGB( 87, 166, 255 );
static const COLORREF DECL_EDITOR_SELECTION = RGB( 37, 47, 60 );
static const int DECL_EDITOR_MARGIN = 8;
static const int DECL_EDITOR_ROW_HEIGHT = 24;
static const int DECL_EDITOR_BUTTON_HEIGHT = 24;
static const int DECL_EDITOR_STATUS_HEIGHT = 20;
static const UINT IDC_DECLEDITOR_CHILD_NAME = 0x5B00;
static const UINT IDC_DECLEDITOR_CHILD_LIST = 0x5B01;
static const UINT IDC_DECLEDITOR_CHILD_KEY = 0x5B02;
static const UINT IDC_DECLEDITOR_CHILD_VALUE = 0x5B03;
static const UINT IDC_DECLEDITOR_CHILD_RAW = 0x5B04;
static const UINT IDC_DECLEDITOR_CHILD_ADD = 0x5B05;
static const UINT IDC_DECLEDITOR_CHILD_REMOVE = 0x5B06;
static const UINT IDC_DECLEDITOR_CHILD_PARSE = 0x5B07;
static const UINT IDC_DECLEDITOR_CHILD_DELETE = 0x5B08;
static const UINT IDC_DECLEDITOR_CHILD_TEST = 0x5B09;
static const UINT IDC_DECLEDITOR_CHILD_SAVE = 0x5B0A;
static const UINT IDC_DECLEDITOR_CHILD_REVERT = 0x5B0B;
static const char *DECL_EDITOR_DARK_FIELD_OLDPROC = "IceTech.DeclEditor.DarkFieldOldProc";
static void DeclEditorFillRect( HDC hDC, const RECT &rc, COLORREF color ) {
HBRUSH brush = ::CreateSolidBrush( color );
::FillRect( hDC, &rc, brush );
::DeleteObject( brush );
}
static void DeclEditorFrameRect( HDC hDC, const RECT &rc, COLORREF color ) {
HBRUSH brush = ::CreateSolidBrush( color );
::FrameRect( hDC, &rc, brush );
::DeleteObject( brush );
}
static void DeclEditorApplyNativeDarkTheme( 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 bool DeclEditorIsClass( HWND hWnd, const char *className ) {
char actual[128];
actual[0] = '\0';
::GetClassNameA( hWnd, actual, sizeof( actual ) );
return ::lstrcmpiA( actual, className ) == 0;
}
static void DeclEditorRemoveClientEdge( 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 DeclEditorDrawWindowFrame( HWND hWnd ) {
HDC hDC = ::GetWindowDC( hWnd );
if ( !hDC ) {
return;
}
RECT rc;
::GetWindowRect( hWnd, &rc );
::OffsetRect( &rc, -rc.left, -rc.top );
DeclEditorFrameRect( hDC, rc, DECL_EDITOR_BORDER );
::ReleaseDC( hWnd, hDC );
}
static LRESULT CALLBACK DeclEditorDarkFieldProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam ) {
WNDPROC oldProc = (WNDPROC)::GetPropA( hWnd, DECL_EDITOR_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 );
DeclEditorDrawWindowFrame( hWnd );
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, DECL_EDITOR_DARK_FIELD_OLDPROC );
return ::CallWindowProc( oldProc, hWnd, uMsg, wParam, lParam );
}
}
return ::CallWindowProc( oldProc, hWnd, uMsg, wParam, lParam );
}
static void DeclEditorSubclassField( HWND hWnd ) {
if ( !hWnd || ::GetPropA( hWnd, DECL_EDITOR_DARK_FIELD_OLDPROC ) ) {
return;
}
WNDPROC oldProc = (WNDPROC)::GetWindowLongPtr( hWnd, GWLP_WNDPROC );
::SetPropA( hWnd, DECL_EDITOR_DARK_FIELD_OLDPROC, (HANDLE)oldProc );
::SetWindowLongPtr( hWnd, GWLP_WNDPROC, (LONG_PTR)DeclEditorDarkFieldProc );
}
static CString DeclEditorNormalizeToLF( const CString &text ) {
CString out = text;
out.Replace( "\r\n", "\n" );
out.Replace( "\r", "\n" );
out.Replace( "\v", "\n" );
return out;
}
static CString DeclEditorToCRLF( const CString &text ) {
CString out = DeclEditorNormalizeToLF( text );
out.Replace( "\n", "\r\n" );
return out;
}
static void DeclEditorTrimLineBreaks( CString &text ) {
while( text.GetLength() > 0 && ( text[0] == '\r' || text[0] == '\n' ) ) {
text.Delete( 0 );
}
while( text.GetLength() > 0 && ( text[text.GetLength() - 1] == '\r' || text[text.GetLength() - 1] == '\n' ) ) {
text.Delete( text.GetLength() - 1 );
}
}
static void DeclEditorSplitLines( const CString &text, CStringArray &lines ) {
lines.RemoveAll();
CString lf = DeclEditorNormalizeToLF( text );
int start = 0;
while( start <= lf.GetLength() ) {
int end = lf.Find( '\n', start );
if ( end < 0 ) {
lines.Add( lf.Mid( start ) );
break;
}
lines.Add( lf.Mid( start, end - start ) );
start = end + 1;
}
}
static CString DeclEditorJoinLines( const CStringArray &lines ) {
CString out;
for( int i = 0; i < lines.GetSize(); i++ ) {
if ( i > 0 ) {
out += "\r\n";
}
out += lines[i];
}
return out;
}
static void DeclEditorCountBraces( const CString &line, int &depth ) {
bool inQuote = false;
for( int i = 0; i < line.GetLength(); i++ ) {
const char ch = line[i];
if ( ch == '"' && ( i == 0 || line[i - 1] != '\\' ) ) {
inQuote = !inQuote;
continue;
}
if ( inQuote ) {
continue;
}
if ( ch == '{' ) {
depth++;
} else if ( ch == '}' && depth > 0 ) {
depth--;
}
}
}
static bool DeclEditorReadToken( const CString &line, int &cursor, CString &token, bool &quoted ) {
token.Empty();
quoted = false;
while( cursor < line.GetLength() && ( line[cursor] == ' ' || line[cursor] == '\t' ) ) {
cursor++;
}
if ( cursor >= line.GetLength() ) {
return false;
}
if ( line[cursor] == '/' && cursor + 1 < line.GetLength() && line[cursor + 1] == '/' ) {
return false;
}
if ( line[cursor] == '{' || line[cursor] == '}' || line[cursor] == ';' ) {
return false;
}
if ( line[cursor] == '"' ) {
quoted = true;
cursor++;
while( cursor < line.GetLength() ) {
const char ch = line[cursor++];
if ( ch == '"' ) {
break;
}
if ( ch == '\\' && cursor < line.GetLength() ) {
token += line[cursor++];
} else {
token += ch;
}
}
return true;
}
while( cursor < line.GetLength() ) {
const char ch = line[cursor];
if ( ch == ' ' || ch == '\t' || ch == '{' || ch == '}' ) {
break;
}
if ( ch == '/' && cursor + 1 < line.GetLength() && line[cursor + 1] == '/' ) {
break;
}
token += ch;
cursor++;
}
return token.GetLength() > 0;
}
static bool DeclEditorParsePropertyLine( const CString &line, CString &key, CString &value, bool &quotedKey, bool &quotedValue ) {
CString trimmed = line;
trimmed.TrimLeft();
trimmed.TrimRight();
if ( trimmed.IsEmpty() || trimmed[0] == '{' || trimmed[0] == '}' || trimmed[0] == ';' ) {
return false;
}
if ( trimmed.GetLength() >= 2 && trimmed[0] == '/' && trimmed[1] == '/' ) {
return false;
}
if ( trimmed.Find( '{' ) >= 0 || trimmed.Find( '}' ) >= 0 ) {
return false;
}
int cursor = 0;
if ( !DeclEditorReadToken( trimmed, cursor, key, quotedKey ) ) {
return false;
}
while( cursor < trimmed.GetLength() && ( trimmed[cursor] == ' ' || trimmed[cursor] == '\t' ) ) {
cursor++;
}
if ( cursor >= trimmed.GetLength() ) {
return false;
}
if ( trimmed[cursor] == '"' ) {
if ( !DeclEditorReadToken( trimmed, cursor, value, quotedValue ) ) {
return false;
}
} else {
quotedValue = false;
value = trimmed.Mid( cursor );
int comment = value.Find( "//" );
if ( comment >= 0 ) {
value = value.Left( comment );
}
value.TrimLeft();
value.TrimRight();
}
key.TrimLeft();
key.TrimRight();
value.TrimLeft();
value.TrimRight();
return key.GetLength() > 0;
}
static CString DeclEditorEscapeDeclString( const char *text ) {
CString in = text ? text : "";
CString out;
for( int i = 0; i < in.GetLength(); i++ ) {
if ( in[i] == '\\' || in[i] == '"' ) {
out += '\\';
}
out += in[i];
}
return out;
}
static bool DeclEditorNeedsQuotes( const char *text ) {
if ( !text || !text[0] ) {
return true;
}
for( const char *s = text; *s; s++ ) {
if ( *s == ' ' || *s == '\t' || *s == '"' || *s == '{' || *s == '}' || *s == ';' ) {
return true;
}
}
return false;
}
static CString DeclEditorFormatPropertyLine( declType_t type, const char *key, const char *value, bool preserveQuotedKey, bool preserveQuotedValue ) {
CString escapedKey = DeclEditorEscapeDeclString( key );
CString escapedValue = DeclEditorEscapeDeclString( value );
CString out;
const bool quoteKey = preserveQuotedKey || type == DECL_ENTITYDEF || DeclEditorNeedsQuotes( key );
const bool quoteValue = preserveQuotedValue || type == DECL_ENTITYDEF || DeclEditorNeedsQuotes( value );
if ( quoteKey ) {
out += "\"";
out += escapedKey;
out += "\"";
} else {
out += escapedKey;
}
out += "\t\t";
if ( quoteValue ) {
out += "\"";
out += escapedValue;
out += "\"";
} else {
out += escapedValue;
}
return out;
}
static CString DeclEditorExtractBody( const CString &declText ) {
CString text = DeclEditorNormalizeToLF( declText );
int open = -1;
int close = -1;
int depth = 0;
bool inQuote = false;
for( int i = 0; i < text.GetLength(); i++ ) {
const char ch = text[i];
if ( ch == '"' && ( i == 0 || text[i - 1] != '\\' ) ) {
inQuote = !inQuote;
continue;
}
if ( inQuote ) {
continue;
}
if ( ch == '{' ) {
if ( depth == 0 && open < 0 ) {
open = i;
}
depth++;
} else if ( ch == '}' ) {
depth--;
if ( depth == 0 ) {
close = i;
break;
}
}
}
CString body;
if ( open >= 0 && close > open ) {
body = text.Mid( open + 1, close - open - 1 );
} else {
body = text;
}
DeclEditorTrimLineBreaks( body );
return DeclEditorToCRLF( body );
}
IMPLEMENT_DYNAMIC(DialogDeclEditor, CWnd)
toolTip_t DialogDeclEditor::toolTips[] = {
{ IDC_DECLEDITOR_CHILD_LIST, "top-level properties parsed from the declaration body" },
{ IDC_DECLEDITOR_CHILD_KEY, "property key to add, update or remove" },
{ IDC_DECLEDITOR_CHILD_VALUE, "property value to add or update" },
{ IDC_DECLEDITOR_CHILD_ADD, "add a new property or update the selected property" },
{ IDC_DECLEDITOR_CHILD_REMOVE, "remove the selected property from the declaration body" },
{ IDC_DECLEDITOR_CHILD_PARSE, "re-parse the raw body into the property list" },
{ IDC_DECLEDITOR_CHILD_DELETE, "delete this declaration through the declaration manager" },
{ IDC_DECLEDITOR_CHILD_TEST, "test parse the declaration without saving" },
{ IDC_DECLEDITOR_CHILD_SAVE, "save the declaration through the declaration manager" },
{ IDC_DECLEDITOR_CHILD_REVERT, "revert edits by reloading the declaration text" },
{ 0, NULL }
};
DialogDeclEditor::DialogDeclEditor()
: CWnd()
, decl( NULL )
, firstLine( 0 )
, modified( false )
, updatingControls( false )
, controlsCreated( false ) {
}
DialogDeclEditor::~DialogDeclEditor() {
if ( modernFont.GetSafeHandle() ) {
modernFont.DeleteObject();
}
if ( monoFont.GetSafeHandle() ) {
monoFont.DeleteObject();
}
if ( bgBrush.GetSafeHandle() ) {
bgBrush.DeleteObject();
}
if ( panelBrush.GetSafeHandle() ) {
panelBrush.DeleteObject();
}
if ( editBrush.GetSafeHandle() ) {
editBrush.DeleteObject();
}
if ( staticBrush.GetSafeHandle() ) {
staticBrush.DeleteObject();
}
}
BOOL DialogDeclEditor::Create( CWnd *parent, UINT id ) {
if ( !parent ) {
return FALSE;
}
if ( id == 0 ) {
id = (UINT)-1;
}
CString className = AfxRegisterWndClass( CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW, ::LoadCursor( NULL, IDC_ARROW ), (HBRUSH)::GetStockObject( NULL_BRUSH ), NULL );
CRect rect( 0, 0, 100, 100 );
return CWnd::CreateEx( 0, className, "DeclEditorChild", WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN, rect, parent, id );
}
BEGIN_MESSAGE_MAP(DialogDeclEditor, CWnd)
ON_WM_CREATE()
ON_NOTIFY_EX_RANGE(TTN_NEEDTEXTW, 0, 0xFFFF, OnToolTipNotify)
ON_NOTIFY_EX_RANGE(TTN_NEEDTEXTA, 0, 0xFFFF, OnToolTipNotify)
ON_WM_CTLCOLOR()
ON_WM_ERASEBKGND()
ON_WM_PAINT()
ON_WM_DESTROY()
ON_WM_SIZE()
ON_WM_SETFOCUS()
ON_EN_CHANGE(IDC_DECLEDITOR_CHILD_NAME, OnNameChange)
ON_EN_CHANGE(IDC_DECLEDITOR_CHILD_RAW, OnRawBodyChange)
ON_NOTIFY(LVN_ITEMCHANGED, IDC_DECLEDITOR_CHILD_LIST, OnPropertyItemChanged)
ON_NOTIFY(NM_CUSTOMDRAW, IDC_DECLEDITOR_CHILD_LIST, OnPropertyCustomDraw)
ON_BN_CLICKED(IDC_DECLEDITOR_CHILD_ADD, OnBnClickedAddProperty)
ON_BN_CLICKED(IDC_DECLEDITOR_CHILD_REMOVE, OnBnClickedRemoveProperty)
ON_BN_CLICKED(IDC_DECLEDITOR_CHILD_PARSE, OnBnClickedParseRaw)
ON_BN_CLICKED(IDC_DECLEDITOR_CHILD_DELETE, OnBnClickedDeleteDecl)
ON_BN_CLICKED(IDC_DECLEDITOR_CHILD_TEST, OnBnClickedTest)
ON_BN_CLICKED(IDC_DECLEDITOR_CHILD_SAVE, OnBnClickedSave)
ON_BN_CLICKED(IDC_DECLEDITOR_CHILD_REVERT, OnBnClickedRevert)
END_MESSAGE_MAP()
BOOL DialogDeclEditor::PreTranslateMessage( MSG* pMsg ) {
return CWnd::PreTranslateMessage( pMsg );
}
int DialogDeclEditor::OnCreate( LPCREATESTRUCT lpCreateStruct ) {
if ( CWnd::OnCreate( lpCreateStruct ) == -1 ) {
return -1;
}
com_editors |= EDITOR_DECL;
CreateChildControls();
EnableToolTips( TRUE );
ApplyDarkTheme();
SetControlsEnabled( false );
SetModified( false );
UpdateStatusBar();
return 0;
}
void DialogDeclEditor::CreateChildControls( void ) {
if ( controlsCreated ) {
return;
}
CRect r( 0, 0, 0, 0 );
DWORD staticStyle = WS_CHILD | WS_VISIBLE | SS_LEFT | SS_NOPREFIX;
DWORD editStyle = WS_CHILD | WS_VISIBLE | WS_TABSTOP | ES_AUTOHSCROLL;
DWORD buttonStyle = WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON;
typeLabel.Create( "Type: -", staticStyle, r, this );
fileLabel.Create( "File: -", staticStyle, r, this );
nameLabel.Create( "Name", staticStyle, r, this );
propertiesLabel.Create( "Properties", staticStyle, r, this );
keyLabel.Create( "Key", staticStyle, r, this );
valueLabel.Create( "Value", staticStyle, r, this );
rawBodyLabel.Create( "Raw declaration body", staticStyle, r, this );
statusText.Create( "No declaration selected", WS_CHILD | WS_VISIBLE | SS_LEFT | SS_NOPREFIX, r, this );
nameEdit.CreateEx( WS_EX_CLIENTEDGE, "EDIT", "", editStyle, r, this, IDC_DECLEDITOR_CHILD_NAME );
keyEdit.CreateEx( WS_EX_CLIENTEDGE, "EDIT", "", editStyle, r, this, IDC_DECLEDITOR_CHILD_KEY );
valueEdit.CreateEx( WS_EX_CLIENTEDGE, "EDIT", "", editStyle, r, this, IDC_DECLEDITOR_CHILD_VALUE );
rawBodyEdit.CreateEx( WS_EX_CLIENTEDGE, "EDIT", "", WS_CHILD | WS_VISIBLE | WS_TABSTOP | WS_VSCROLL | WS_HSCROLL | ES_MULTILINE | ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_WANTRETURN, r, this, IDC_DECLEDITOR_CHILD_RAW );
rawBodyEdit.LimitText( 0x7fffffff );
propertyList.CreateEx(
WS_EX_CLIENTEDGE,
WS_CHILD | WS_VISIBLE | WS_TABSTOP | LVS_REPORT | LVS_SINGLESEL | LVS_SHOWSELALWAYS,
r,
this,
IDC_DECLEDITOR_CHILD_LIST
);
propertyList.SetExtendedStyle( LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES | LVS_EX_LABELTIP );
propertyList.InsertColumn( 0, "Property", LVCFMT_LEFT, 160 );
propertyList.InsertColumn( 1, "Value", LVCFMT_LEFT, 280 );
propertyList.InsertColumn( 2, "Source", LVCFMT_LEFT, 90 );
addPropertyButton.Create( "Add / Update", buttonStyle, r, this, IDC_DECLEDITOR_CHILD_ADD );
removePropertyButton.Create( "Remove", buttonStyle, r, this, IDC_DECLEDITOR_CHILD_REMOVE );
parseRawButton.Create( "Parse Raw", buttonStyle, r, this, IDC_DECLEDITOR_CHILD_PARSE );
deleteDeclButton.Create( "Delete Decl", buttonStyle, r, this, IDC_DECLEDITOR_CHILD_DELETE );
testButton.Create( "Test", buttonStyle, r, this, IDC_DECLEDITOR_CHILD_TEST );
saveButton.Create( "Save", buttonStyle, r, this, IDC_DECLEDITOR_CHILD_SAVE );
revertButton.Create( "Revert", buttonStyle, r, this, IDC_DECLEDITOR_CHILD_REVERT );
controlsCreated = true;
}
void DialogDeclEditor::ApplyFontRecursive( CWnd *wnd ) {
if ( !wnd || !wnd->GetSafeHwnd() || !modernFont.GetSafeHandle() ) {
return;
}
wnd->SetFont( &modernFont, FALSE );
CWnd *child = wnd->GetWindow( GW_CHILD );
while( child ) {
child->SetFont( &modernFont, FALSE );
child = child->GetNextWindow();
}
if ( rawBodyEdit.GetSafeHwnd() && monoFont.GetSafeHandle() ) {
rawBodyEdit.SetFont( &monoFont, FALSE );
}
}
void DialogDeclEditor::ApplyDarkTheme( void ) {
if ( !GetSafeHwnd() ) {
return;
}
if ( !modernFont.GetSafeHandle() ) {
LOGFONT lf;
memset( &lf, 0, sizeof( lf ) );
HDC hDC = ::GetDC( GetSafeHwnd() );
const int dpiY = GetDeviceCaps( hDC, LOGPIXELSY );
::ReleaseDC( GetSafeHwnd(), hDC );
lf.lfHeight = -MulDiv( 9, dpiY, 72 );
lf.lfWeight = FW_NORMAL;
lf.lfQuality = CLEARTYPE_QUALITY;
strncpy( lf.lfFaceName, "Segoe UI", sizeof( lf.lfFaceName ) - 1 );
modernFont.CreateFontIndirect( &lf );
lf.lfHeight = -MulDiv( 10, dpiY, 72 );
lf.lfWeight = FW_NORMAL;
strncpy( lf.lfFaceName, "Consolas", sizeof( lf.lfFaceName ) - 1 );
monoFont.CreateFontIndirect( &lf );
}
if ( !bgBrush.GetSafeHandle() ) {
bgBrush.CreateSolidBrush( DECL_EDITOR_BG );
}
if ( !panelBrush.GetSafeHandle() ) {
panelBrush.CreateSolidBrush( DECL_EDITOR_PANEL_BG );
}
if ( !editBrush.GetSafeHandle() ) {
editBrush.CreateSolidBrush( DECL_EDITOR_FIELD_BG );
}
if ( !staticBrush.GetSafeHandle() ) {
staticBrush.CreateSolidBrush( DECL_EDITOR_BG );
}
SetFont( &modernFont, FALSE );
ApplyFontRecursive( this );
DeclEditorApplyNativeDarkTheme( GetSafeHwnd() );
ModifyStyleEx( WS_EX_CLIENTEDGE, 0, SWP_FRAMECHANGED );
for( HWND child = ::GetWindow( GetSafeHwnd(), GW_CHILD ); child != NULL; child = ::GetWindow( child, GW_HWNDNEXT ) ) {
DeclEditorApplyNativeDarkTheme( child );
if ( DeclEditorIsClass( child, "Edit" ) || DeclEditorIsClass( child, "SysListView32" ) ) {
DeclEditorRemoveClientEdge( child );
DeclEditorSubclassField( child );
}
}
if ( propertyList.GetSafeHwnd() ) {
propertyList.SetBkColor( DECL_EDITOR_FIELD_BG );
propertyList.SetTextBkColor( DECL_EDITOR_FIELD_BG );
propertyList.SetTextColor( DECL_EDITOR_TEXT );
}
Invalidate( TRUE );
}
BOOL DialogDeclEditor::OnToolTipNotify( UINT id, NMHDR *pNMHDR, LRESULT *pResult ) {
return DefaultOnToolTipNotify( toolTips, id, pNMHDR, pResult );
}
HBRUSH DialogDeclEditor::OnCtlColor( CDC* pDC, CWnd* pWnd, UINT nCtlColor ) {
HBRUSH hbr = CWnd::OnCtlColor( pDC, pWnd, nCtlColor );
if ( !pDC ) {
return hbr;
}
pDC->SetTextColor( DECL_EDITOR_TEXT );
switch( nCtlColor ) {
case CTLCOLOR_DLG:
pDC->SetBkColor( DECL_EDITOR_BG );
return (HBRUSH)bgBrush.GetSafeHandle();
case CTLCOLOR_STATIC:
pDC->SetBkColor( DECL_EDITOR_BG );
pDC->SetTextColor( DECL_EDITOR_MUTED_TEXT );
return (HBRUSH)staticBrush.GetSafeHandle();
case CTLCOLOR_EDIT:
case CTLCOLOR_LISTBOX:
pDC->SetBkColor( DECL_EDITOR_FIELD_BG );
pDC->SetTextColor( DECL_EDITOR_TEXT );
return (HBRUSH)editBrush.GetSafeHandle();
case CTLCOLOR_BTN:
pDC->SetBkColor( DECL_EDITOR_BG );
pDC->SetTextColor( DECL_EDITOR_TEXT );
return (HBRUSH)bgBrush.GetSafeHandle();
default:
break;
}
return hbr;
}
BOOL DialogDeclEditor::OnEraseBkgnd( CDC* pDC ) {
CRect rc;
GetClientRect( &rc );
pDC->FillSolidRect( &rc, DECL_EDITOR_BG );
return TRUE;
}
void DialogDeclEditor::OnPaint() {
CPaintDC dc( this );
CRect rc;
GetClientRect( &rc );
dc.FillSolidRect( &rc, DECL_EDITOR_BG );
CRect panel = rc;
panel.DeflateRect( 4, 4 );
dc.FillSolidRect( &panel, DECL_EDITOR_PANEL_BG );
dc.Draw3dRect( &panel, DECL_EDITOR_BORDER, DECL_EDITOR_BORDER );
}
void DialogDeclEditor::OnSetFocus( CWnd *pOldWnd ) {
CWnd::OnSetFocus( pOldWnd );
if ( rawBodyEdit.GetSafeHwnd() && rawBodyEdit.IsWindowEnabled() ) {
rawBodyEdit.SetFocus();
}
}
void DialogDeclEditor::OnDestroy() {
com_editors &= ~EDITOR_DECL;
CWnd::OnDestroy();
}
void DialogDeclEditor::OnSize( UINT nType, int cx, int cy ) {
CWnd::OnSize( nType, cx, cy );
if ( !controlsCreated || cx <= 0 || cy <= 0 ) {
return;
}
const int margin = DECL_EDITOR_MARGIN;
const int row = DECL_EDITOR_ROW_HEIGHT;
const int buttonHeight = DECL_EDITOR_BUTTON_HEIGHT;
const int statusHeight = DECL_EDITOR_STATUS_HEIGHT;
const int statusTop = cy - statusHeight;
const int buttonRowTop = statusTop - margin - buttonHeight;
const int headerTop = margin + 4;
const int headerLeft = margin + 4;
const int headerRight = cx - margin - 4;
const int nameLabelWidth = 44;
const int typeWidth = 180;
LockWindowUpdate();
if ( typeLabel.GetSafeHwnd() ) {
typeLabel.MoveWindow( headerLeft, headerTop, typeWidth, row );
}
if ( nameLabel.GetSafeHwnd() ) {
nameLabel.MoveWindow( headerLeft + typeWidth + margin, headerTop + 3, nameLabelWidth, row );
}
if ( nameEdit.GetSafeHwnd() ) {
nameEdit.MoveWindow( headerLeft + typeWidth + margin + nameLabelWidth, headerTop, headerRight - ( headerLeft + typeWidth + margin + nameLabelWidth ), row );
}
if ( fileLabel.GetSafeHwnd() ) {
fileLabel.MoveWindow( headerLeft, headerTop + row, headerRight - headerLeft, row );
}
int propTop = headerTop + ( row * 2 ) + margin + 4;
int propHeight = ( cy - ( row * 2 ) - ( buttonHeight * 3 ) - statusHeight - margin * 7 ) / 3;
if ( propHeight < 90 ) {
propHeight = 90;
}
if ( propHeight > 185 ) {
propHeight = 185;
}
if ( propTop + propHeight > buttonRowTop - 120 ) {
propHeight = buttonRowTop - 120 - propTop;
if ( propHeight < 70 ) {
propHeight = 70;
}
}
if ( propertiesLabel.GetSafeHwnd() ) {
propertiesLabel.MoveWindow( margin + 4, propTop, cx - margin * 2 - 8, row );
}
if ( propertyList.GetSafeHwnd() ) {
propertyList.MoveWindow( margin + 4, propTop + row, cx - margin * 2 - 8, propHeight );
int listWidth = cx - margin * 2 - 30;
if ( listWidth > 0 ) {
propertyList.SetColumnWidth( 0, listWidth / 3 );
propertyList.SetColumnWidth( 1, ( listWidth * 2 ) / 3 - 90 );
propertyList.SetColumnWidth( 2, 90 );
}
}
int editTop = propTop + row + propHeight + margin;
int keyWidth = 180;
int buttonWidth = 92;
int parseWidth = 86;
int removeWidth = 76;
int keyLabelWidth = 32;
int valueLabelWidth = 42;
int rightButtons = buttonWidth + removeWidth + parseWidth + margin * 2;
int valueLeft = margin + 4 + keyLabelWidth + keyWidth + valueLabelWidth + margin * 2;
int valueRight = cx - margin - 4 - rightButtons - margin;
if ( valueRight < valueLeft + 100 ) {
valueRight = valueLeft + 100;
}
if ( keyLabel.GetSafeHwnd() ) {
keyLabel.MoveWindow( margin + 4, editTop + 3, keyLabelWidth, row );
}
if ( keyEdit.GetSafeHwnd() ) {
keyEdit.MoveWindow( margin + 4 + keyLabelWidth, editTop, keyWidth, row );
}
if ( valueLabel.GetSafeHwnd() ) {
valueLabel.MoveWindow( margin + 4 + keyLabelWidth + keyWidth + margin, editTop + 3, valueLabelWidth, row );
}
if ( valueEdit.GetSafeHwnd() ) {
valueEdit.MoveWindow( valueLeft, editTop, valueRight - valueLeft, row );
}
if ( addPropertyButton.GetSafeHwnd() ) {
addPropertyButton.MoveWindow( cx - margin - 4 - rightButtons, editTop, buttonWidth, buttonHeight );
}
if ( removePropertyButton.GetSafeHwnd() ) {
removePropertyButton.MoveWindow( cx - margin - 4 - removeWidth - parseWidth - margin, editTop, removeWidth, buttonHeight );
}
if ( parseRawButton.GetSafeHwnd() ) {
parseRawButton.MoveWindow( cx - margin - 4 - parseWidth, editTop, parseWidth, buttonHeight );
}
int rawLabelTop = editTop + row + margin;
int rawTop = rawLabelTop + row;
int rawBottom = buttonRowTop - margin;
if ( rawBottom < rawTop ) {
rawBottom = rawTop;
}
if ( rawBodyLabel.GetSafeHwnd() ) {
rawBodyLabel.MoveWindow( margin + 4, rawLabelTop, cx - margin * 2 - 8, row );
}
if ( rawBodyEdit.GetSafeHwnd() ) {
rawBodyEdit.MoveWindow( margin + 4, rawTop, cx - margin * 2 - 8, rawBottom - rawTop );
}
if ( testButton.GetSafeHwnd() ) {
testButton.MoveWindow( margin + 4, buttonRowTop, 68, buttonHeight );
}
if ( deleteDeclButton.GetSafeHwnd() ) {
deleteDeclButton.MoveWindow( margin + 4 + 68 + margin, buttonRowTop, 92, buttonHeight );
}
if ( revertButton.GetSafeHwnd() ) {
revertButton.MoveWindow( cx - margin - 4 - 76, buttonRowTop, 76, buttonHeight );
}
if ( saveButton.GetSafeHwnd() ) {
saveButton.MoveWindow( cx - margin - 4 - 76 - margin - 76, buttonRowTop, 76, buttonHeight );
}
if ( statusText.GetSafeHwnd() ) {
statusText.MoveWindow( margin + 4, statusTop + 2, cx - margin * 2 - 8, statusHeight - 2 );
}
UnlockWindowUpdate();
}
void DialogDeclEditor::SetControlsEnabled( bool enabled ) {
if ( nameEdit.GetSafeHwnd() ) {
nameEdit.EnableWindow( enabled );
}
if ( keyEdit.GetSafeHwnd() ) {
keyEdit.EnableWindow( enabled );
}
if ( valueEdit.GetSafeHwnd() ) {
valueEdit.EnableWindow( enabled );
}
if ( rawBodyEdit.GetSafeHwnd() ) {
rawBodyEdit.EnableWindow( enabled );
}
if ( propertyList.GetSafeHwnd() ) {
propertyList.EnableWindow( enabled );
}
if ( addPropertyButton.GetSafeHwnd() ) {
addPropertyButton.EnableWindow( enabled );
}
if ( removePropertyButton.GetSafeHwnd() ) {
removePropertyButton.EnableWindow( enabled );
}
if ( parseRawButton.GetSafeHwnd() ) {
parseRawButton.EnableWindow( enabled );
}
if ( deleteDeclButton.GetSafeHwnd() ) {
deleteDeclButton.EnableWindow( enabled );
}
if ( testButton.GetSafeHwnd() ) {
testButton.EnableWindow( enabled && modified );
}
if ( saveButton.GetSafeHwnd() ) {
saveButton.EnableWindow( enabled && modified );
}
if ( revertButton.GetSafeHwnd() ) {
revertButton.EnableWindow( enabled && modified );
}
}
void DialogDeclEditor::SetModified( bool isModified ) {
modified = isModified;
if ( testButton.GetSafeHwnd() ) {
testButton.EnableWindow( decl != NULL && modified );
}
if ( saveButton.GetSafeHwnd() ) {
saveButton.EnableWindow( decl != NULL && modified );
}
if ( revertButton.GetSafeHwnd() ) {
revertButton.EnableWindow( decl != NULL && modified );
}
UpdateStatusBar();
}
void DialogDeclEditor::ClearDecl( void ) {
updatingControls = true;
decl = NULL;
firstLine = 0;
properties.Clear();
if ( typeLabel.GetSafeHwnd() ) {
typeLabel.SetWindowText( "Type: -" );
}
if ( fileLabel.GetSafeHwnd() ) {
fileLabel.SetWindowText( "File: -" );
}
if ( nameEdit.GetSafeHwnd() ) {
nameEdit.SetWindowText( "" );
}
if ( keyEdit.GetSafeHwnd() ) {
keyEdit.SetWindowText( "" );
}
if ( valueEdit.GetSafeHwnd() ) {
valueEdit.SetWindowText( "" );
}
if ( rawBodyEdit.GetSafeHwnd() ) {
rawBodyEdit.SetWindowText( "" );
}
if ( propertyList.GetSafeHwnd() ) {
propertyList.DeleteAllItems();
}
updatingControls = false;
SetModified( false );
SetControlsEnabled( false );
UpdateStatusBar();
}
void DialogDeclEditor::LoadBodyText( const char *declText ) {
CString text = declText ? declText : "";
CString body = DeclEditorExtractBody( text );
idStr bodyText = (LPCTSTR)body;
SetBodyText( bodyText );
}
void DialogDeclEditor::LoadDecl( idDecl *decl ) {
if ( !decl ) {
ClearDecl();
return;
}
this->decl = decl;
firstLine = decl->GetLineNum();
updatingControls = true;
const char *typeName = declManager->GetDeclNameFromType( decl->GetType() );
if ( typeLabel.GetSafeHwnd() ) {
typeLabel.SetWindowText( va( "Type: %s", typeName ) );
}
if ( fileLabel.GetSafeHwnd() ) {
fileLabel.SetWindowText( va( "File: %s, line %d", decl->GetFileName(), decl->GetLineNum() ) );
}
if ( nameEdit.GetSafeHwnd() ) {
nameEdit.SetWindowText( decl->GetName() );
}
if ( keyEdit.GetSafeHwnd() ) {
keyEdit.SetWindowText( "" );
}
if ( valueEdit.GetSafeHwnd() ) {
valueEdit.SetWindowText( "" );
}
char *localDeclText = (char *)_alloca( ( decl->GetTextLength() + 1 ) * sizeof( char ) );
decl->GetText( localDeclText );
LoadBodyText( localDeclText );
SetWindowText( va( "Declaration Editor (%s, line %d)", decl->GetFileName(), decl->GetLineNum() ) );
updatingControls = false;
SyncPropertiesFromBody();
SetControlsEnabled( true );
SetModified( false );
UpdateStatusBar();
}
void DialogDeclEditor::GetBodyText( idStr &bodyText ) const {
CString body;
if ( rawBodyEdit.GetSafeHwnd() ) {
rawBodyEdit.GetWindowText( body );
}
body = DeclEditorToCRLF( body );
DeclEditorTrimLineBreaks( body );
bodyText = body;
}
void DialogDeclEditor::SetBodyText( const idStr &bodyText ) {
if ( !rawBodyEdit.GetSafeHwnd() ) {
return;
}
CString body = bodyText.c_str();
body = DeclEditorToCRLF( body );
updatingControls = true;
rawBodyEdit.SetWindowText( body );
updatingControls = false;
}
bool DialogDeclEditor::GetEditedDeclName( idStr &name ) const {
CString text;
if ( nameEdit.GetSafeHwnd() ) {
nameEdit.GetWindowText( text );
}
text.TrimLeft();
text.TrimRight();
if ( text.IsEmpty() ) {
return false;
}
name = text;
return true;
}
void DialogDeclEditor::BuildDeclText( idStr &declText ) const {
declText.Clear();
if ( !decl ) {
return;
}
idStr body;
idStr name;
GetBodyText( body );
GetEditedDeclName( name );
const char *typeName = declManager->GetDeclNameFromType( decl->GetType() );
declText = va( "%s %s\r\n{\r\n", typeName, name.c_str() );
if ( body.Length() ) {
declText += body;
declText += "\r\n";
}
declText += "}\r\n";
}
void DialogDeclEditor::SyncPropertiesFromBody( void ) {
properties.Clear();
CString body;
if ( rawBodyEdit.GetSafeHwnd() ) {
rawBodyEdit.GetWindowText( body );
}
CStringArray lines;
DeclEditorSplitLines( body, lines );
int depth = 0;
for( int i = 0; i < lines.GetSize(); i++ ) {
if ( depth == 0 ) {
CString key, value;
bool quotedKey = false;
bool quotedValue = false;
if ( DeclEditorParsePropertyLine( lines[i], key, value, quotedKey, quotedValue ) ) {
declProperty_t prop;
prop.key = key;
prop.value = value;
prop.lineIndex = i;
prop.quotedKey = quotedKey;
prop.quotedValue = quotedValue;
properties.Append( prop );
}
}
DeclEditorCountBraces( lines[i], depth );
}
RefreshPropertyList();
UpdateStatusBar();
}
void DialogDeclEditor::RefreshPropertyList( void ) {
if ( !propertyList.GetSafeHwnd() ) {
return;
}
propertyList.DeleteAllItems();
for( int i = 0; i < properties.Num(); i++ ) {
int item = propertyList.InsertItem( i, properties[i].key.c_str() );
propertyList.SetItemText( item, 1, properties[i].value.c_str() );
propertyList.SetItemText( item, 2, va( "line %d", properties[i].lineIndex + 1 ) );
propertyList.SetItemData( item, i );
}
}
void DialogDeclEditor::SelectProperty( int listIndex ) {
if ( listIndex < 0 || !propertyList.GetSafeHwnd() ) {
return;
}
int propIndex = (int)propertyList.GetItemData( listIndex );
if ( propIndex < 0 || propIndex >= properties.Num() ) {
return;
}
if ( keyEdit.GetSafeHwnd() ) {
keyEdit.SetWindowText( properties[propIndex].key.c_str() );
}
if ( valueEdit.GetSafeHwnd() ) {
valueEdit.SetWindowText( properties[propIndex].value.c_str() );
}
}
int DialogDeclEditor::FindProperty( const char *key ) const {
if ( !key || !key[0] ) {
return -1;
}
for( int i = 0; i < properties.Num(); i++ ) {
if ( properties[i].key.Icmp( key ) == 0 ) {
return i;
}
}
return -1;
}
bool DialogDeclEditor::UpsertPropertyInBody( const char *oldKey, const char *newKey, const char *value ) {
if ( !newKey || !newKey[0] ) {
return false;
}
CString body;
if ( rawBodyEdit.GetSafeHwnd() ) {
rawBodyEdit.GetWindowText( body );
}
CStringArray lines;
DeclEditorSplitLines( body, lines );
bool preserveQuotedKey = false;
bool preserveQuotedValue = false;
int depth = 0;
int matchedLine = -1;
for( int i = 0; i < lines.GetSize(); i++ ) {
if ( depth == 0 ) {
CString key, parsedValue;
bool quotedKey = false;
bool quotedValue = false;
if ( DeclEditorParsePropertyLine( lines[i], key, parsedValue, quotedKey, quotedValue ) ) {
if ( key.CompareNoCase( oldKey ? oldKey : newKey ) == 0 ) {
matchedLine = i;
preserveQuotedKey = quotedKey;
preserveQuotedValue = quotedValue;
break;
}
}
}
DeclEditorCountBraces( lines[i], depth );
}
CString replacement = DeclEditorFormatPropertyLine( decl ? decl->GetType() : DECL_ENTITYDEF, newKey, value, preserveQuotedKey, preserveQuotedValue );
if ( matchedLine >= 0 ) {
lines[matchedLine] = replacement;
} else {
if ( lines.GetSize() > 0 ) {
CString last = lines[lines.GetSize() - 1];
last.TrimLeft();
last.TrimRight();
if ( last.IsEmpty() ) {
lines[lines.GetSize() - 1] = replacement;
} else {
lines.Add( replacement );
}
} else {
lines.Add( replacement );
}
}
CString joinedBody = DeclEditorJoinLines( lines );
idStr newBody = (LPCTSTR)joinedBody;
SetBodyText( newBody );
SyncPropertiesFromBody();
SetModified( true );
return true;
}
bool DialogDeclEditor::RemovePropertyFromBody( const char *key ) {
if ( !key || !key[0] ) {
return false;
}
CString body;
if ( rawBodyEdit.GetSafeHwnd() ) {
rawBodyEdit.GetWindowText( body );
}
CStringArray lines;
DeclEditorSplitLines( body, lines );
int depth = 0;
int matchedLine = -1;
for( int i = 0; i < lines.GetSize(); i++ ) {
if ( depth == 0 ) {
CString parsedKey, parsedValue;
bool quotedKey = false;
bool quotedValue = false;
if ( DeclEditorParsePropertyLine( lines[i], parsedKey, parsedValue, quotedKey, quotedValue ) ) {
if ( parsedKey.CompareNoCase( key ) == 0 ) {
matchedLine = i;
break;
}
}
}
DeclEditorCountBraces( lines[i], depth );
}
if ( matchedLine < 0 ) {
return false;
}
lines.RemoveAt( matchedLine );
CString joinedBody = DeclEditorJoinLines( lines );
idStr newBody = (LPCTSTR)joinedBody;
SetBodyText( newBody );
SyncPropertiesFromBody();
SetModified( true );
return true;
}
bool DialogDeclEditor::TestDecl( const idStr &declText ) {
idLexer src( LEXFL_NOSTRINGCONCAT );
idToken token;
int indent;
src.LoadMemory( declText, declText.Length(), "decl text" );
indent = 0;
while( src.ReadToken( &token ) ) {
if ( token == "{" ) {
indent++;
} else if ( token == "}" ) {
indent--;
}
}
if ( indent < 0 ) {
MessageBox( "Missing opening brace!", decl ? va( "Error saving %s", decl->GetFileName() ) : "Error saving decl", MB_OK | MB_ICONERROR );
return false;
}
if ( indent > 0 ) {
MessageBox( "Missing closing brace!", decl ? va( "Error saving %s", decl->GetFileName() ) : "Error saving decl", MB_OK | MB_ICONERROR );
return false;
}
return true;
}
bool DialogDeclEditor::SaveDeclText( void ) {
if ( !decl ) {
return false;
}
idStr declText;
idStr editedName;
if ( !GetEditedDeclName( editedName ) ) {
MessageBox( "Enter a declaration name before saving.", "Error saving declaration", MB_OK | MB_ICONERROR );
return false;
}
BuildDeclText( declText );
if ( !TestDecl( declText ) ) {
return false;
}
const declType_t type = decl->GetType();
idStr oldName = decl->GetName();
idStr fileName = decl->GetFileName();
idStr targetName = editedName;
if ( decl->SourceFileChanged() ) {
if ( MessageBox( va( "Declaration file %s has been modified outside of the editor.\r\nReload declarations and save?", fileName.c_str() ),
va( "Warning saving: %s", fileName.c_str() ), MB_OKCANCEL | MB_ICONERROR ) != IDOK ) {
return false;
}
declManager->Reload( false );
DeclBrowserReloadDeclarations();
}
if ( oldName.Icmp( targetName ) != 0 ) {
if ( !declManager->RenameDecl( type, oldName, targetName ) ) {
MessageBox( va( "Couldn't rename %s to %s.", oldName.c_str(), targetName.c_str() ), "Error renaming declaration", MB_OK | MB_ICONERROR );
return false;
}
const idDecl *renamedDecl = declManager->FindType( type, targetName, false );
if ( renamedDecl ) {
decl = const_cast<idDecl *>( renamedDecl );
}
}
if ( !declManager->SetDeclText( type, targetName, declText, true, false ) ) {
MessageBox( va( "Couldn't update text for %s.", targetName.c_str() ), "Error saving declaration", MB_OK | MB_ICONERROR );
return false;
}
if ( !declManager->SaveDecl( type, targetName ) ) {
MessageBox( va( "Couldn't save: %s.\r\nMake sure the declaration file is not read-only.", fileName.c_str() ),
va( "Error saving: %s", fileName.c_str() ), MB_OK | MB_ICONERROR );
return false;
}
const idDecl *freshDecl = declManager->FindType( type, targetName, false );
if ( freshDecl ) {
decl = const_cast<idDecl *>( freshDecl );
}
if ( decl ) {
decl->Invalidate();
}
SetModified( false );
DeclBrowserReloadDeclarations();
common->Printf( "saved decl %s\n", targetName.c_str() );
return true;
}
void DialogDeclEditor::UpdateStatusBar( void ) {
if ( !statusText.GetSafeHwnd() ) {
return;
}
if ( !decl ) {
statusText.SetWindowText( "No declaration selected" );
return;
}
CString status;
status.Format( "%s/%s | %d properties | %s", declManager->GetDeclNameFromType( decl->GetType() ), decl->GetName(), properties.Num(), modified ? "modified" : "saved" );
statusText.SetWindowText( status );
}
void DialogDeclEditor::OnNameChange() {
if ( updatingControls ) {
return;
}
SetModified( true );
}
void DialogDeclEditor::OnRawBodyChange() {
if ( updatingControls ) {
return;
}
SetModified( true );
}
void DialogDeclEditor::OnPropertyItemChanged( NMHDR *pNMHDR, LRESULT *pResult ) {
NM_LISTVIEW *listView = (NM_LISTVIEW *)pNMHDR;
if ( ( listView->uChanged & LVIF_STATE ) && ( listView->uNewState & LVIS_SELECTED ) ) {
SelectProperty( listView->iItem );
}
*pResult = 0;
}
void DialogDeclEditor::OnPropertyCustomDraw( NMHDR *pNMHDR, LRESULT *pResult ) {
LPNMLVCUSTOMDRAW customDraw = (LPNMLVCUSTOMDRAW)pNMHDR;
switch( customDraw->nmcd.dwDrawStage ) {
case CDDS_PREPAINT:
*pResult = CDRF_NOTIFYITEMDRAW;
return;
case CDDS_ITEMPREPAINT:
customDraw->clrText = DECL_EDITOR_TEXT;
customDraw->clrTextBk = ( customDraw->nmcd.uItemState & CDIS_SELECTED ) ? DECL_EDITOR_SELECTION : DECL_EDITOR_FIELD_BG;
*pResult = CDRF_NOTIFYSUBITEMDRAW;
return;
case CDDS_SUBITEM | CDDS_ITEMPREPAINT:
customDraw->clrText = DECL_EDITOR_TEXT;
customDraw->clrTextBk = ( customDraw->nmcd.uItemState & CDIS_SELECTED ) ? DECL_EDITOR_SELECTION : DECL_EDITOR_FIELD_BG;
*pResult = CDRF_DODEFAULT;
return;
default:
break;
}
*pResult = CDRF_DODEFAULT;
}
void DialogDeclEditor::OnBnClickedAddProperty() {
CString key;
CString value;
keyEdit.GetWindowText( key );
valueEdit.GetWindowText( value );
key.TrimLeft();
key.TrimRight();
if ( key.IsEmpty() ) {
MessageBox( "Enter a property key first.", "Add declaration property", MB_OK | MB_ICONINFORMATION );
return;
}
CString oldKey = key;
int sel = propertyList.GetNextItem( -1, LVNI_SELECTED );
if ( sel >= 0 ) {
int propIndex = (int)propertyList.GetItemData( sel );
if ( propIndex >= 0 && propIndex < properties.Num() ) {
oldKey = properties[propIndex].key.c_str();
}
}
int duplicate = FindProperty( key );
if ( duplicate >= 0 && oldKey.CompareNoCase( key ) != 0 ) {
MessageBox( va( "Property '%s' already exists.", (LPCTSTR)key ), "Add declaration property", MB_OK | MB_ICONERROR );
return;
}
UpsertPropertyInBody( oldKey, key, value );
}
void DialogDeclEditor::OnBnClickedRemoveProperty() {
CString key;
int sel = propertyList.GetNextItem( -1, LVNI_SELECTED );
if ( sel >= 0 ) {
int propIndex = (int)propertyList.GetItemData( sel );
if ( propIndex >= 0 && propIndex < properties.Num() ) {
key = properties[propIndex].key.c_str();
}
}
if ( key.IsEmpty() && keyEdit.GetSafeHwnd() ) {
keyEdit.GetWindowText( key );
}
key.TrimLeft();
key.TrimRight();
if ( key.IsEmpty() ) {
MessageBox( "Select or enter a property key to remove.", "Remove declaration property", MB_OK | MB_ICONINFORMATION );
return;
}
if ( !RemovePropertyFromBody( key ) ) {
MessageBox( va( "Property '%s' was not found in the editable top-level body.", (LPCTSTR)key ), "Remove declaration property", MB_OK | MB_ICONINFORMATION );
}
}
void DialogDeclEditor::OnBnClickedParseRaw() {
SyncPropertiesFromBody();
}
void DialogDeclEditor::OnBnClickedDeleteDecl() {
if ( !decl ) {
return;
}
const declType_t type = decl->GetType();
idStr name = decl->GetName();
if ( MessageBox( va( "Delete declaration '%s/%s' from its source file?", declManager->GetDeclNameFromType( type ), name.c_str() ),
"Delete declaration", MB_YESNO | MB_ICONQUESTION ) != IDYES ) {
return;
}
if ( !declManager->DeleteDecl( type, name, true ) ) {
MessageBox( va( "Couldn't delete declaration '%s'.", name.c_str() ), "Delete declaration", MB_OK | MB_ICONERROR );
return;
}
ClearDecl();
DeclBrowserReloadDeclarations();
common->Printf( "deleted decl %s\n", name.c_str() );
}
void DialogDeclEditor::OnBnClickedTest() {
idStr declText;
if ( decl ) {
BuildDeclText( declText );
if ( !TestDecl( declText ) ) {
return;
}
char *oldDeclText = (char *)_alloca( ( decl->GetTextLength() + 1 ) * sizeof( char ) );
decl->GetText( oldDeclText );
decl->SetText( declText );
decl->Invalidate();
declManager->DeclByIndex( decl->GetType(), decl->Index(), true );
decl->SetText( oldDeclText );
decl->Invalidate();
common->Printf( "tested %s\n", decl->GetName() );
if ( testButton.GetSafeHwnd() ) {
testButton.EnableWindow( FALSE );
}
}
}
void DialogDeclEditor::OnBnClickedSave() {
SaveDeclText();
}
void DialogDeclEditor::OnBnClickedRevert() {
if ( !decl ) {
return;
}
if ( modified ) {
if ( MessageBox( "Revert changes to this declaration?", "Revert", MB_YESNO | MB_ICONQUESTION ) != IDYES ) {
return;
}
}
LoadDecl( decl );
}