mirror of
https://github.com/jmarshall23/DoomRTX.git
synced 2026-08-12 16:21:04 +02:00
1496 lines
43 KiB
C++
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 "ed ) {
|
|
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 "edKey, bool "edValue ) {
|
|
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 );
|
|
}
|