/* =========================================================================== 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 . 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( 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( 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 ); }