diff --git a/neo/engine/tools/decl/DialogDeclBrowser.cpp b/neo/engine/tools/decl/DialogDeclBrowser.cpp index 26b3e5c5..ab054a4e 100644 --- a/neo/engine/tools/decl/DialogDeclBrowser.cpp +++ b/neo/engine/tools/decl/DialogDeclBrowser.cpp @@ -29,14 +29,9 @@ If you have questions concerning this license or the applicable additional terms #include "precompiled.h" #pragma hdrstop -#include "../../sys/win32/rc/DeclEditor_resource.h" -#include "../../sys/win32/rc/ScriptEditor_resource.h" - -#include "../comafx/CPathTreeCtrl.h" -#include "../script/DialogScriptEditor.h" +#include "../radiant/qe3.h" #include "DialogDeclBrowser.h" -#include "DialogDeclEditor.h" -#include "DialogDeclNew.h" +#include "../radiant/InspectorDialog.h" #ifdef ID_DEBUG_MEMORY #undef new @@ -44,87 +39,558 @@ If you have questions concerning this license or the applicable additional terms #define DEBUG_NEW new #endif -const int DECLTYPE_SHIFT = 24; -const int DECLINDEX_MASK = ( 1 << DECLTYPE_SHIFT ) - 1; -const int DECLTYPE_SCRIPT = 126; -const int DECLTYPE_GUI = 127; +const int DECLTYPE_SHIFT = 24; +const int DECLINDEX_MASK = ( 1 << DECLTYPE_SHIFT ) - 1; #define GetIdFromTypeAndIndex( type, index ) ( ( (int)type << DECLTYPE_SHIFT ) | index ) #define GetTypeFromId( id ) ( (declType_t) ( (int)id >> DECLTYPE_SHIFT ) ) #define GetIndexFromId( id ) ( (int)id & DECLINDEX_MASK ) +static const COLORREF DECL_BROWSER_BG = RGB( 17, 19, 23 ); +static const COLORREF DECL_BROWSER_PANEL_BG = RGB( 23, 26, 31 ); +static const COLORREF DECL_BROWSER_FIELD_BG = RGB( 14, 16, 20 ); +static const COLORREF DECL_BROWSER_TEXT = RGB( 226, 232, 240 ); +static const COLORREF DECL_BROWSER_MUTED_TEXT = RGB( 151, 163, 184 ); +static const COLORREF DECL_BROWSER_BORDER = RGB( 58, 64, 72 ); +static const COLORREF DECL_BROWSER_ACCENT = RGB( 87, 166, 255 ); + +static const int DECL_BROWSER_MARGIN = 8; +static const int DECL_BROWSER_ROW_HEIGHT = 24; +static const int DECL_BROWSER_BUTTON_HEIGHT = 24; +static const int DECL_BROWSER_STATUS_HEIGHT = 20; +static const int DECL_BROWSER_LEFT_MIN_WIDTH = 260; +static const int DECL_BROWSER_LEFT_MAX_WIDTH = 420; + +static const UINT IDC_DECLBROWSER_CHILD_TREE = 0x5C00; +static const UINT IDC_DECLBROWSER_CHILD_BASE_TREE = 0x5C01; +static const UINT IDC_DECLBROWSER_CHILD_FIND_NAME = 0x5C02; +static const UINT IDC_DECLBROWSER_CHILD_FIND_TEXT = 0x5C03; +static const UINT IDC_DECLBROWSER_CHILD_FIND = 0x5C04; +static const UINT IDC_DECLBROWSER_CHILD_LOAD = 0x5C05; +static const UINT IDC_DECLBROWSER_CHILD_RELOAD = 0x5C06; +static const UINT IDC_DECLBROWSER_CHILD_NEW_TYPE = 0x5C07; +static const UINT IDC_DECLBROWSER_CHILD_NEW_NAME = 0x5C08; +static const UINT IDC_DECLBROWSER_CHILD_NEW_FILE = 0x5C09; +static const UINT IDC_DECLBROWSER_CHILD_CREATE = 0x5C0A; +static const UINT IDC_DECLBROWSER_CHILD_EDITOR = 0x5C0B; + +static const char *DECL_BROWSER_DARK_FIELD_OLDPROC = "IceTech.DeclBrowser.DarkFieldOldProc"; + toolTip_t DialogDeclBrowser::toolTips[] = { - { IDC_DECLBROWSER_TREE, "decl browser" }, - { IDC_DECLBROWSER_EDIT_SEARCH_NAMES, "search for declarations with matching name, use meta characters: *, ? and [abc...]" }, - { IDC_DECLBROWSER_EDIT_SEARCH_TEXT, "search for declarations containing text" }, - { IDC_DECLBROWSER_BUTTON_FIND, "find declarations matching the search strings" }, - { IDC_DECLBROWSER_BUTTON_EDIT, "edit selected declaration" }, - { IDC_DECLBROWSER_BUTTON_NEW, "create new declaration" }, - { IDC_DECLBROWSER_BUTTON_RELOAD, "reload declarations" }, - { IDOK, "ok" }, - { IDCANCEL, "cancel" }, + { IDC_DECLBROWSER_CHILD_TREE, "declaration browser" }, + { IDC_DECLBROWSER_CHILD_FIND_NAME, "search for declarations with matching name, supports *, ? and [abc...]" }, + { IDC_DECLBROWSER_CHILD_FIND_TEXT, "search for declarations containing text" }, + { IDC_DECLBROWSER_CHILD_FIND, "find declarations matching the search strings" }, + { IDC_DECLBROWSER_CHILD_LOAD, "load the selected declaration into the editor" }, + { IDC_DECLBROWSER_CHILD_RELOAD, "reload declarations" }, + { IDC_DECLBROWSER_CHILD_NEW_TYPE, "type for a new declaration" }, + { IDC_DECLBROWSER_CHILD_NEW_NAME, "name for a new declaration" }, + { IDC_DECLBROWSER_CHILD_NEW_FILE, "source file for a new declaration" }, + { IDC_DECLBROWSER_CHILD_CREATE, "create a new declaration through the declaration manager" }, { 0, NULL } }; - -static DialogDeclBrowser *g_DeclDialog = NULL; - - -IMPLEMENT_DYNAMIC(DialogDeclBrowser, CDialog) - -/* -================ -DialogDeclBrowser::DialogDeclBrowser -================ -*/ -DialogDeclBrowser::DialogDeclBrowser( CWnd* pParent /*=NULL*/ ) - : CDialog(DialogDeclBrowser::IDD, pParent) - , m_pchTip(NULL) - , m_pwchTip(NULL) -{ +static void DeclBrowserFillRect( HDC hDC, const RECT &rc, COLORREF color ) { + HBRUSH brush = ::CreateSolidBrush( color ); + ::FillRect( hDC, &rc, brush ); + ::DeleteObject( brush ); } -/* -================ -DialogDeclBrowser::~DialogDeclBrowser -================ -*/ -DialogDeclBrowser::~DialogDeclBrowser() { - delete m_pwchTip; - delete m_pchTip; +static void DeclBrowserFrameRect( HDC hDC, const RECT &rc, COLORREF color ) { + HBRUSH brush = ::CreateSolidBrush( color ); + ::FrameRect( hDC, &rc, brush ); + ::DeleteObject( brush ); } -/* -================ -DialogDeclBrowser::DoDataExchange -================ -*/ -void DialogDeclBrowser::DoDataExchange(CDataExchange* pDX) { - CDialog::DoDataExchange(pDX); - //{{AFX_DATA_MAP(DialogDeclBrowser) - DDX_Control(pDX, IDC_DECLBROWSER_TREE, declTree); - DDX_Control(pDX, IDC_DECLBROWSER_STATIC_SEARCH_NAMES, findNameStatic); - DDX_Control(pDX, IDC_DECLBROWSER_STATIC_SEARCH_TEXT, findTextStatic); - DDX_Control(pDX, IDC_DECLBROWSER_EDIT_SEARCH_NAMES, findNameEdit); - DDX_Control(pDX, IDC_DECLBROWSER_EDIT_SEARCH_TEXT, findTextEdit); - DDX_Control(pDX, IDC_DECLBROWSER_BUTTON_FIND, findButton); - DDX_Control(pDX, IDC_DECLBROWSER_BUTTON_EDIT, editButton); - DDX_Control(pDX, IDC_DECLBROWSER_BUTTON_NEW, newButton); - DDX_Control(pDX, IDC_DECLBROWSER_BUTTON_RELOAD, reloadButton); - DDX_Control(pDX, IDCANCEL, cancelButton); - //}}AFX_DATA_MAP +static void DeclBrowserApplyNativeDarkTheme( 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 DeclBrowserIsClass( HWND hWnd, const char *className ) { + char actual[128]; + actual[0] = '\0'; + ::GetClassNameA( hWnd, actual, sizeof( actual ) ); + return ::lstrcmpiA( actual, className ) == 0; +} + +static void DeclBrowserRemoveClientEdge( 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 DeclBrowserDrawWindowFrame( HWND hWnd ) { + HDC hDC = ::GetWindowDC( hWnd ); + if ( !hDC ) { + return; + } + RECT rc; + ::GetWindowRect( hWnd, &rc ); + ::OffsetRect( &rc, -rc.left, -rc.top ); + DeclBrowserFrameRect( hDC, rc, DECL_BROWSER_BORDER ); + ::ReleaseDC( hWnd, hDC ); +} + +static LRESULT CALLBACK DeclBrowserDarkFieldProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam ) { + WNDPROC oldProc = (WNDPROC)::GetPropA( hWnd, DECL_BROWSER_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 ); + DeclBrowserDrawWindowFrame( 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_BROWSER_DARK_FIELD_OLDPROC ); + return ::CallWindowProc( oldProc, hWnd, uMsg, wParam, lParam ); + } + } + + return ::CallWindowProc( oldProc, hWnd, uMsg, wParam, lParam ); +} + +static void DeclBrowserSubclassField( HWND hWnd ) { + if ( !hWnd || ::GetPropA( hWnd, DECL_BROWSER_DARK_FIELD_OLDPROC ) ) { + return; + } + WNDPROC oldProc = (WNDPROC)::GetWindowLongPtr( hWnd, GWLP_WNDPROC ); + ::SetPropA( hWnd, DECL_BROWSER_DARK_FIELD_OLDPROC, (HANDLE)oldProc ); + ::SetWindowLongPtr( hWnd, GWLP_WNDPROC, (LONG_PTR)DeclBrowserDarkFieldProc ); } -/* -================ -DialogDeclBrowser::AddDeclTypeToTree -================ -*/ template< class type > int idListDeclSortCompare( const type *a, const type *b ) { return idStr::IcmpPath( (*a)->GetName(), (*b)->GetName() ); } +IMPLEMENT_DYNAMIC(DialogDeclBrowser, CWnd) + +DialogDeclBrowser::DialogDeclBrowser() + : CWnd() + , numListedDecls( 0 ) + , controlsCreated( false ) + , reloadingTree( false ) + , m_pchTip( NULL ) + , m_pwchTip( NULL ) { +} + +DialogDeclBrowser::~DialogDeclBrowser() { + delete m_pwchTip; + delete m_pchTip; + if ( modernFont.GetSafeHandle() ) { + modernFont.DeleteObject(); + } + if ( bgBrush.GetSafeHandle() ) { + bgBrush.DeleteObject(); + } + if ( panelBrush.GetSafeHandle() ) { + panelBrush.DeleteObject(); + } + if ( editBrush.GetSafeHandle() ) { + editBrush.DeleteObject(); + } + if ( staticBrush.GetSafeHandle() ) { + staticBrush.DeleteObject(); + } +} + +BOOL DialogDeclBrowser::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, "DeclBrowserChild", WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN, rect, parent, id ); +} + +BEGIN_MESSAGE_MAP(DialogDeclBrowser, 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_NOTIFY(TVN_SELCHANGED, IDC_DECLBROWSER_CHILD_TREE, OnTreeSelChanged) + ON_NOTIFY(NM_DBLCLK, IDC_DECLBROWSER_CHILD_TREE, OnTreeDblclk) + ON_BN_CLICKED(IDC_DECLBROWSER_CHILD_FIND, OnBnClickedFind) + ON_BN_CLICKED(IDC_DECLBROWSER_CHILD_LOAD, OnBnClickedLoad) + ON_BN_CLICKED(IDC_DECLBROWSER_CHILD_CREATE, OnBnClickedCreate) + ON_BN_CLICKED(IDC_DECLBROWSER_CHILD_RELOAD, OnBnClickedReload) + ON_CBN_SELCHANGE(IDC_DECLBROWSER_CHILD_NEW_TYPE, OnTypeChanged) +END_MESSAGE_MAP() + +int DialogDeclBrowser::OnCreate( LPCREATESTRUCT lpCreateStruct ) { + if ( CWnd::OnCreate( lpCreateStruct ) == -1 ) { + return -1; + } + + CreateChildControls(); + EnableToolTips( TRUE ); + ApplyDarkTheme(); + PopulateTypeCombo(); + InitBaseDeclTree(); + findNameString = "*"; + findTextString = ""; + findNameEdit.SetWindowText( findNameString.c_str() ); + findTextEdit.SetWindowText( findTextString.c_str() ); + OnBnClickedFind(); + SetBrowserStatus( va( "%d decls listed", numListedDecls ) ); + return 0; +} + +void DialogDeclBrowser::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; + + declTree.Create( WS_CHILD | WS_VISIBLE | WS_TABSTOP | WS_BORDER | TVS_HASLINES | TVS_LINESATROOT | TVS_HASBUTTONS | TVS_SHOWSELALWAYS, r, this, IDC_DECLBROWSER_CHILD_TREE ); + baseDeclTree.Create( WS_CHILD | TVS_HASLINES | TVS_LINESATROOT | TVS_HASBUTTONS, r, this, IDC_DECLBROWSER_CHILD_BASE_TREE ); + + findNameStatic.Create( "Name", staticStyle, r, this ); + findTextStatic.Create( "Text", staticStyle, r, this ); + findNameEdit.CreateEx( WS_EX_CLIENTEDGE, "EDIT", "", editStyle, r, this, IDC_DECLBROWSER_CHILD_FIND_NAME ); + findTextEdit.CreateEx( WS_EX_CLIENTEDGE, "EDIT", "", editStyle, r, this, IDC_DECLBROWSER_CHILD_FIND_TEXT ); + findButton.Create( "Find", buttonStyle, r, this, IDC_DECLBROWSER_CHILD_FIND ); + loadButton.Create( "Load", buttonStyle, r, this, IDC_DECLBROWSER_CHILD_LOAD ); + reloadButton.Create( "Reload", buttonStyle, r, this, IDC_DECLBROWSER_CHILD_RELOAD ); + + newTypeStatic.Create( "Type", staticStyle, r, this ); + newNameStatic.Create( "New", staticStyle, r, this ); + newFileStatic.Create( "File", staticStyle, r, this ); + newTypeCombo.Create( WS_CHILD | WS_VISIBLE | WS_TABSTOP | CBS_DROPDOWNLIST | WS_VSCROLL, r, this, IDC_DECLBROWSER_CHILD_NEW_TYPE ); + newNameEdit.CreateEx( WS_EX_CLIENTEDGE, "EDIT", "", editStyle, r, this, IDC_DECLBROWSER_CHILD_NEW_NAME ); + newFileEdit.CreateEx( WS_EX_CLIENTEDGE, "EDIT", "", editStyle, r, this, IDC_DECLBROWSER_CHILD_NEW_FILE ); + createButton.Create( "Create", buttonStyle, r, this, IDC_DECLBROWSER_CHILD_CREATE ); + statusText.Create( "No declaration selected", WS_CHILD | WS_VISIBLE | SS_LEFT | SS_NOPREFIX, r, this ); + + declEditor.Create( this, IDC_DECLBROWSER_CHILD_EDITOR ); + + controlsCreated = true; +} + +void DialogDeclBrowser::PopulateTypeCombo( void ) { + if ( !newTypeCombo.GetSafeHwnd() ) { + return; + } + newTypeCombo.ResetContent(); + for( int i = 0; i < declManager->GetNumDeclTypes(); i++ ) { + newTypeCombo.AddString( declManager->GetDeclNameFromType( (declType_t)i ) ); + } + if ( newTypeCombo.GetCount() > 0 ) { + newTypeCombo.SetCurSel( 0 ); + OnTypeChanged(); + } +} + +void DialogDeclBrowser::ApplyModernFontRecursive( 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(); + } +} + +void DialogDeclBrowser::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 ); + } + + if ( !bgBrush.GetSafeHandle() ) { + bgBrush.CreateSolidBrush( DECL_BROWSER_BG ); + } + if ( !panelBrush.GetSafeHandle() ) { + panelBrush.CreateSolidBrush( DECL_BROWSER_PANEL_BG ); + } + if ( !editBrush.GetSafeHandle() ) { + editBrush.CreateSolidBrush( DECL_BROWSER_FIELD_BG ); + } + if ( !staticBrush.GetSafeHandle() ) { + staticBrush.CreateSolidBrush( DECL_BROWSER_BG ); + } + + SetFont( &modernFont, FALSE ); + ApplyModernFontRecursive( this ); + DeclBrowserApplyNativeDarkTheme( GetSafeHwnd() ); + ModifyStyleEx( WS_EX_CLIENTEDGE, 0, SWP_FRAMECHANGED ); + + for( HWND child = ::GetWindow( GetSafeHwnd(), GW_CHILD ); child != NULL; child = ::GetWindow( child, GW_HWNDNEXT ) ) { + DeclBrowserApplyNativeDarkTheme( child ); + if ( DeclBrowserIsClass( child, "Edit" ) || DeclBrowserIsClass( child, "SysTreeView32" ) || DeclBrowserIsClass( child, "ComboBox" ) ) { + DeclBrowserRemoveClientEdge( child ); + DeclBrowserSubclassField( child ); + } + } + + if ( declTree.GetSafeHwnd() ) { + ::SendMessage( declTree.GetSafeHwnd(), TVM_SETBKCOLOR, 0, (LPARAM)DECL_BROWSER_FIELD_BG ); + ::SendMessage( declTree.GetSafeHwnd(), TVM_SETTEXTCOLOR, 0, (LPARAM)DECL_BROWSER_TEXT ); + } + Invalidate( TRUE ); +} + +BOOL DialogDeclBrowser::OnToolTipNotify( UINT id, NMHDR *pNMHDR, LRESULT *pResult ) { + if ( pNMHDR->hwndFrom == declTree.GetSafeHwnd() ) { + CString toolTip; + const idDecl *decl = GetDeclFromTreeItem( (HTREEITEM)pNMHDR->idFrom ); + if ( !decl ) { + return FALSE; + } + toolTip = va( "%s, line: %d", decl->GetFileName(), decl->GetLineNum() ); + + TOOLTIPTEXTA* pTTTA = (TOOLTIPTEXTA*)pNMHDR; + TOOLTIPTEXTW* pTTTW = (TOOLTIPTEXTW*)pNMHDR; +#ifndef _UNICODE + if( pNMHDR->code == TTN_NEEDTEXTA ) { + delete m_pchTip; + m_pchTip = new char[toolTip.GetLength() + 2]; + lstrcpyn( m_pchTip, toolTip, toolTip.GetLength() + 1 ); + pTTTA->lpszText = (LPSTR)m_pchTip; + } else { + delete m_pwchTip; + m_pwchTip = new WCHAR[toolTip.GetLength() + 2]; + _mbstowcsz( m_pwchTip, toolTip, toolTip.GetLength() + 1 ); + pTTTW->lpszText = (WCHAR*)m_pwchTip; + } +#else + if( pNMHDR->code == TTN_NEEDTEXTA ) { + delete m_pchTip; + m_pchTip = new char[toolTip.GetLength() + 2]; + _wcstombsz( m_pchTip, toolTip, toolTip.GetLength() + 1 ); + pTTTA->lpszText = (LPSTR)m_pchTip; + } else { + delete m_pwchTip; + m_pwchTip = new WCHAR[toolTip.GetLength() + 2]; + lstrcpyn( m_pwchTip, toolTip, toolTip.GetLength() + 1 ); + pTTTW->lpszText = (WCHAR*)m_pwchTip; + } +#endif + return TRUE; + } + + return DefaultOnToolTipNotify( toolTips, id, pNMHDR, pResult ); +} + +HBRUSH DialogDeclBrowser::OnCtlColor( CDC* pDC, CWnd* pWnd, UINT nCtlColor ) { + HBRUSH hbr = CWnd::OnCtlColor( pDC, pWnd, nCtlColor ); + if ( !pDC ) { + return hbr; + } + + pDC->SetTextColor( DECL_BROWSER_TEXT ); + switch( nCtlColor ) { + case CTLCOLOR_DLG: + pDC->SetBkColor( DECL_BROWSER_BG ); + return (HBRUSH)bgBrush.GetSafeHandle(); + case CTLCOLOR_STATIC: + pDC->SetBkColor( DECL_BROWSER_BG ); + pDC->SetTextColor( DECL_BROWSER_MUTED_TEXT ); + return (HBRUSH)staticBrush.GetSafeHandle(); + case CTLCOLOR_EDIT: + case CTLCOLOR_LISTBOX: + pDC->SetBkColor( DECL_BROWSER_FIELD_BG ); + pDC->SetTextColor( DECL_BROWSER_TEXT ); + return (HBRUSH)editBrush.GetSafeHandle(); + case CTLCOLOR_BTN: + pDC->SetBkColor( DECL_BROWSER_BG ); + pDC->SetTextColor( DECL_BROWSER_TEXT ); + return (HBRUSH)bgBrush.GetSafeHandle(); + default: + break; + } + return hbr; +} + +BOOL DialogDeclBrowser::OnEraseBkgnd( CDC* pDC ) { + CRect rc; + GetClientRect( &rc ); + pDC->FillSolidRect( &rc, DECL_BROWSER_BG ); + return TRUE; +} + +void DialogDeclBrowser::OnPaint() { + CPaintDC dc( this ); + CRect rc; + GetClientRect( &rc ); + dc.FillSolidRect( &rc, DECL_BROWSER_BG ); +} + +void DialogDeclBrowser::OnSetFocus( CWnd *pOldWnd ) { + CWnd::OnSetFocus( pOldWnd ); + if ( declTree.GetSafeHwnd() ) { + declTree.SetFocus(); + } +} + +void DialogDeclBrowser::OnDestroy() { + CWnd::OnDestroy(); +} + +void DialogDeclBrowser::OnSize( UINT nType, int cx, int cy ) { + CWnd::OnSize( nType, cx, cy ); + LayoutBrowser( cx, cy ); +} + +void DialogDeclBrowser::LayoutBrowser( int cx, int cy ) { + if ( !controlsCreated || cx <= 0 || cy <= 0 ) { + return; + } + + const int margin = DECL_BROWSER_MARGIN; + const int row = DECL_BROWSER_ROW_HEIGHT; + const int buttonHeight = DECL_BROWSER_BUTTON_HEIGHT; + const int statusHeight = DECL_BROWSER_STATUS_HEIGHT; + int leftWidth = cx / 3; + if ( leftWidth < DECL_BROWSER_LEFT_MIN_WIDTH ) { + leftWidth = DECL_BROWSER_LEFT_MIN_WIDTH; + } + if ( leftWidth > DECL_BROWSER_LEFT_MAX_WIDTH ) { + leftWidth = DECL_BROWSER_LEFT_MAX_WIDTH; + } + if ( leftWidth > cx - 240 ) { + leftWidth = cx - 240; + } + if ( leftWidth < 180 ) { + leftWidth = 180; + } + + const int left = margin; + const int top = margin; + const int divider = left + leftWidth + margin; + const int editorLeft = divider + margin; + const int statusTop = cy - statusHeight; + const int createHeight = row * 4 + margin * 2; + const int searchHeight = row * 3 + margin; + const int treeTop = top + searchHeight; + const int createTop = statusTop - createHeight - margin; + const int treeBottom = createTop - margin; + + LockWindowUpdate(); + + if ( findNameStatic.GetSafeHwnd() ) { + findNameStatic.MoveWindow( left, top + 3, 42, row ); + } + if ( findNameEdit.GetSafeHwnd() ) { + findNameEdit.MoveWindow( left + 46, top, leftWidth - 46, row ); + } + if ( findTextStatic.GetSafeHwnd() ) { + findTextStatic.MoveWindow( left, top + row + 3, 42, row ); + } + if ( findTextEdit.GetSafeHwnd() ) { + findTextEdit.MoveWindow( left + 46, top + row, leftWidth - 46, row ); + } + if ( findButton.GetSafeHwnd() ) { + findButton.MoveWindow( left, top + row * 2 + margin / 2, 64, buttonHeight ); + } + if ( loadButton.GetSafeHwnd() ) { + loadButton.MoveWindow( left + 68, top + row * 2 + margin / 2, 64, buttonHeight ); + } + if ( reloadButton.GetSafeHwnd() ) { + reloadButton.MoveWindow( left + 136, top + row * 2 + margin / 2, 72, buttonHeight ); + } + if ( declTree.GetSafeHwnd() ) { + declTree.MoveWindow( left, treeTop, leftWidth, treeBottom - treeTop ); + } + if ( baseDeclTree.GetSafeHwnd() ) { + baseDeclTree.MoveWindow( 0, 0, 0, 0 ); + } + + if ( newTypeStatic.GetSafeHwnd() ) { + newTypeStatic.MoveWindow( left, createTop + 3, 40, row ); + } + if ( newTypeCombo.GetSafeHwnd() ) { + newTypeCombo.MoveWindow( left + 46, createTop, leftWidth - 46, 300 ); + } + if ( newNameStatic.GetSafeHwnd() ) { + newNameStatic.MoveWindow( left, createTop + row + 3, 40, row ); + } + if ( newNameEdit.GetSafeHwnd() ) { + newNameEdit.MoveWindow( left + 46, createTop + row, leftWidth - 46, row ); + } + if ( newFileStatic.GetSafeHwnd() ) { + newFileStatic.MoveWindow( left, createTop + row * 2 + 3, 40, row ); + } + if ( newFileEdit.GetSafeHwnd() ) { + newFileEdit.MoveWindow( left + 46, createTop + row * 2, leftWidth - 46, row ); + } + if ( createButton.GetSafeHwnd() ) { + createButton.MoveWindow( left, createTop + row * 3 + margin / 2, 82, buttonHeight ); + } + + if ( declEditor.GetSafeHwnd() ) { + declEditor.MoveWindow( editorLeft, top, cx - editorLeft - margin, statusTop - top - margin ); + } + if ( statusText.GetSafeHwnd() ) { + statusText.MoveWindow( margin, statusTop + 2, cx - margin * 2, statusHeight - 2 ); + } + + UnlockWindowUpdate(); +} + void DialogDeclBrowser::AddDeclTypeToTree( declType_t type, const char *root, CPathTreeCtrl &tree ) { int i; idList decls; @@ -142,99 +608,38 @@ void DialogDeclBrowser::AddDeclTypeToTree( declType_t type, const char *root, CP stack.PushRoot( NULL ); - for ( i = 0; i < decls.Num(); i++) { + for ( i = 0; i < decls.Num(); i++ ) { + if ( !decls[i] ) { + continue; + } declName = rootStr + decls[i]->GetName(); - declName.BackSlashesToSlashes(); - declName.Strip(' '); - + declName.Strip( ' ' ); tree.AddPathToTree( declName, GetIdFromTypeAndIndex( type, decls[i]->Index() ), stack ); } } -/* -================ -DialogDeclBrowser::AddScriptsToTree -================ -*/ -void DialogDeclBrowser::AddScriptsToTree( CPathTreeCtrl &tree ) { - int i; - idPathTreeStack stack; - idStr scriptName; - idFileList *files; - - files = fileSystem->ListFilesTree( "script", ".script", true ); - - stack.PushRoot( NULL ); - - for ( i = 0; i < files->GetNumFiles(); i++) { - scriptName = files->GetFile( i ); - - scriptName.BackSlashesToSlashes(); - scriptName.StripFileExtension(); - - tree.AddPathToTree( scriptName, GetIdFromTypeAndIndex( DECLTYPE_SCRIPT, i ), stack ); - } - - fileSystem->FreeFileList( files ); -} - -/* -================ -DialogDeclBrowser::AddGUIsToTree -================ -*/ -void DialogDeclBrowser::AddGUIsToTree( CPathTreeCtrl &tree ) { - int i; - idPathTreeStack stack; - idStr scriptName; - idFileList *files; - - files = fileSystem->ListFilesTree( "guis", ".gui", true ); - - stack.PushRoot( NULL ); - - for ( i = 0; i < files->GetNumFiles(); i++) { - scriptName = files->GetFile( i ); - - scriptName.BackSlashesToSlashes(); - scriptName.StripFileExtension(); - - tree.AddPathToTree( scriptName, GetIdFromTypeAndIndex( DECLTYPE_GUI, i ), stack ); - } - - fileSystem->FreeFileList( files ); -} - -/* -================ -DialogDeclBrowser::InitBaseDeclTree -================ -*/ void DialogDeclBrowser::InitBaseDeclTree( void ) { - int i; - + if ( !baseDeclTree.GetSafeHwnd() ) { + return; + } numListedDecls = 0; baseDeclTree.DeleteAllItems(); - for ( i = 0; i < declManager->GetNumDeclTypes(); i++ ) { + for ( int i = 0; i < declManager->GetNumDeclTypes(); i++ ) { AddDeclTypeToTree( (declType_t)i, declManager->GetDeclNameFromType( (declType_t)i ), baseDeclTree ); } - - AddScriptsToTree( baseDeclTree ); - AddGUIsToTree( baseDeclTree ); } -/* -================ -DialogDeclBrowser::GetDeclName -================ -*/ void DialogDeclBrowser::GetDeclName( HTREEITEM item, idStr &typeName, idStr &declName ) const { HTREEITEM parent; idStr itemName; declName.Clear(); + if ( !item ) { + typeName.Clear(); + return; + } for( parent = declTree.GetParentItem( item ); parent; parent = declTree.GetParentItem( parent ) ) { itemName = declTree.GetItemText( item ); declName = itemName + "/" + declName; @@ -244,17 +649,11 @@ void DialogDeclBrowser::GetDeclName( HTREEITEM item, idStr &typeName, idStr &dec typeName = declTree.GetItemText( item ); } -/* -================ -DialogDeclBrowser::GetDeclFromTreeItem -================ -*/ const idDecl *DialogDeclBrowser::GetDeclFromTreeItem( HTREEITEM item ) const { int id, index; declType_t type; - const idDecl *decl; - if ( declTree.GetChildItem( item ) ) { + if ( !item || declTree.GetChildItem( item ) ) { return NULL; } @@ -266,100 +665,13 @@ const idDecl *DialogDeclBrowser::GetDeclFromTreeItem( HTREEITEM item ) const { return NULL; } - decl = declManager->DeclByIndex( type, index, false ); - - return decl; + return declManager->DeclByIndex( type, index, false ); } -/* -================ -DialogDeclBrowser::GetSelectedDecl -================ -*/ const idDecl *DialogDeclBrowser::GetSelectedDecl( void ) const { return GetDeclFromTreeItem( declTree.GetSelectedItem() ); } -/* -================ -DialogDeclBrowser::EditSelected -================ -*/ -void DialogDeclBrowser::EditSelected( void ) const { - int id, index; - idDict spawnArgs; - const idDecl *decl; - declType_t type; - HTREEITEM item; - - item = declTree.GetSelectedItem(); - - if ( declTree.GetChildItem( item ) ) { - return; - } - - id = declTree.GetItemData( item ); - type = GetTypeFromId( id ); - index = GetIndexFromId( id ); - - switch( type ) { - case DECL_AF: { - decl = declManager->DeclByIndex( type, index, false ); - spawnArgs.Set( "articulatedFigure", decl->GetName() ); - AFEditorInit( &spawnArgs ); - break; - } - case DECL_PARTICLE: { - decl = declManager->DeclByIndex( type, index, false ); - spawnArgs.Set( "model", decl->GetName() ); - ParticleEditorInit( &spawnArgs ); - break; - } - case DECL_PDA: { - decl = declManager->DeclByIndex( type, index, false ); - spawnArgs.Set( "pda", decl->GetName() ); - PDAEditorInit( &spawnArgs ); - break; - } - case DECLTYPE_SCRIPT: - case DECLTYPE_GUI: { - idStr typeName, declName; - GetDeclName( item, typeName, declName ); - DialogScriptEditor *scriptEditor; - //scriptEditor = new DialogScriptEditor; - //scriptEditor->Create( IDD_DIALOG_SCRIPTEDITOR, GetParent() ); - //scriptEditor->OpenFile( typeName + "/" + declName + ( ( type == DECLTYPE_SCRIPT ) ? ".script" : ".gui" ) ); - //scriptEditor->ShowWindow( SW_SHOW ); - scriptEditor->SetFocus(); - break; - } - default: { - decl = declManager->DeclByIndex( type, index, false ); - DialogDeclEditor *declEditor; - declEditor = new DialogDeclEditor; - declEditor->Create( IDD_DIALOG_DECLEDITOR, GetParent() ); - declEditor->LoadDecl( const_cast( decl ) ); - declEditor->ShowWindow( SW_SHOW ); - declEditor->SetFocus(); - break; - } - } -} - -/* -================ -DeclBrowserCompareDecl -================ -*/ -bool DeclBrowserCompareDecl( void *data, HTREEITEM item, const char *name ) { - return reinterpret_cast(data)->CompareDecl( item, name ); -} - -/* -================ -DialogDeclBrowser::CompareDecl -================ -*/ bool DialogDeclBrowser::CompareDecl( HTREEITEM item, const char *name ) const { if ( findNameString.Length() ) { if ( !idStr::Filter( findNameString, name, false ) ) { @@ -375,573 +687,291 @@ bool DialogDeclBrowser::CompareDecl( HTREEITEM item, const char *name ) const { type = GetTypeFromId( id ); index = GetIndexFromId( id ); - if ( type == DECLTYPE_SCRIPT || type == DECLTYPE_GUI ) { - // search for the text in the script or gui - idStr text; - void *buffer; - if ( fileSystem->ReadFile( idStr( name ) + ( ( type == DECLTYPE_SCRIPT ) ? ".script" : ".gui" ), &buffer ) == -1 ) { - return false; - } - text = (char *) buffer; - fileSystem->FreeFile( buffer ); - if ( text.Find( findTextString, false ) == -1 ) { - return false; - } - } else { - // search for the text in the decl - const idDecl *decl = declManager->DeclByIndex( type, index, false ); - char *declText = (char *)_alloca( ( decl->GetTextLength() + 1 ) * sizeof( char ) ); - decl->GetText( declText ); - if ( idStr::FindText( declText, findTextString, false ) == -1 ) { - return false; - } + if ( type < 0 || type >= declManager->GetNumDeclTypes() ) { + return false; + } + + const idDecl *decl = declManager->DeclByIndex( type, index, false ); + if ( !decl ) { + return false; + } + char *declText = (char *)_alloca( ( decl->GetTextLength() + 1 ) * sizeof( char ) ); + decl->GetText( declText ); + if ( idStr::FindText( declText, findTextString, false ) == -1 ) { + return false; } } return true; } -/* -================ -DialogDeclBrowser::OnInitDialog -================ -*/ -BOOL DialogDeclBrowser::OnInitDialog() { - - com_editors |= EDITOR_DECL; - - CDialog::OnInitDialog(); - - GetClientRect( initialRect ); - - statusBar.CreateEx( SBARS_SIZEGRIP, WS_CHILD | WS_VISIBLE | CBRS_BOTTOM, initialRect, this, AFX_IDW_STATUS_BAR ); - - baseDeclTree.Create( 0, initialRect, this, IDC_DECLBROWSER_BASE_TREE ); - - InitBaseDeclTree(); - - findNameString = "*"; - findNameEdit.SetWindowText( findNameString ); - - findTextString = ""; - findTextEdit.SetWindowText( findTextString ); - - numListedDecls = baseDeclTree.SearchTree( DeclBrowserCompareDecl, this, declTree ); - - statusBar.SetWindowText( va( "%d decls listed", numListedDecls ) ); - - EnableToolTips( TRUE ); - - return TRUE; // return TRUE unless you set the focus to a control - // EXCEPTION: OCX Property Pages should return FALSE +bool DeclBrowserCompareDecl( void *data, HTREEITEM item, const char *name ) { + return reinterpret_cast( data )->CompareDecl( item, name ); } -/* -================ -DeclBrowserInit -================ -*/ -void DeclBrowserInit( const idDict *spawnArgs ) { - - if ( renderSystem->IsFullScreen() ) { - common->Printf( "Cannot run the declaration editor in fullscreen mode.\n" - "Set r_fullscreen to 0 and vid_restart.\n" ); - return; - } - - if ( g_DeclDialog == NULL ) { - InitAfx(); - g_DeclDialog = new DialogDeclBrowser(); - } - - if ( g_DeclDialog->GetSafeHwnd() == NULL) { - g_DeclDialog->Create( IDD_DIALOG_DECLBROWSER ); -/* - // FIXME: restore position - CRect rct; - g_DeclDialog->SetWindowPos( NULL, rct.left, rct.top, 0, 0, SWP_NOSIZE ); -*/ - } - - idKeyInput::ClearStates(); - - g_DeclDialog->ShowWindow( SW_SHOW ); - g_DeclDialog->SetFocus(); - - if ( spawnArgs ) { - } -} - -/* -================ -DeclBrowserRun -================ -*/ -void DeclBrowserRun( void ) { -#if _MSC_VER >= 1300 - MSG *msg = AfxGetCurrentMessage(); // TODO Robert fix me!! -#else - MSG *msg = &m_msgCur; -#endif - - while( ::PeekMessage(msg, NULL, NULL, NULL, PM_NOREMOVE) ) { - // pump message - if ( !AfxGetApp()->PumpMessage() ) { - } - } -} - -/* -================ -DeclBrowserShutdown -================ -*/ -void DeclBrowserShutdown( void ) { - delete g_DeclDialog; - g_DeclDialog = NULL; -} - -/* -================ -DeclBrowserReloadDeclarations -================ -*/ -void DeclBrowserReloadDeclarations( void ) { - if ( g_DeclDialog ) { - g_DeclDialog->ReloadDeclarations(); - } -} - -/* -================ -DialogDeclBrowser::ReloadDeclarations -================ -*/ -void DialogDeclBrowser::ReloadDeclarations( void ) { - InitBaseDeclTree(); - OnBnClickedFind(); -} - -BEGIN_MESSAGE_MAP(DialogDeclBrowser, CDialog) - ON_NOTIFY_EX_RANGE(TTN_NEEDTEXTW, 0, 0xFFFF, OnToolTipNotify) - ON_NOTIFY_EX_RANGE(TTN_NEEDTEXTA, 0, 0xFFFF, OnToolTipNotify) - ON_WM_DESTROY() - ON_WM_ACTIVATE() - ON_WM_MOVE() - ON_WM_SIZE() - ON_WM_SIZING() - ON_WM_SETFOCUS() - ON_NOTIFY(TVN_SELCHANGED, IDC_DECLBROWSER_TREE, OnTreeSelChanged) - ON_NOTIFY(NM_DBLCLK, IDC_DECLBROWSER_TREE, OnTreeDblclk) - ON_BN_CLICKED(IDC_DECLBROWSER_BUTTON_FIND, OnBnClickedFind) - ON_BN_CLICKED(IDC_DECLBROWSER_BUTTON_EDIT, OnBnClickedEdit) - ON_BN_CLICKED(IDC_DECLBROWSER_BUTTON_NEW, OnBnClickedNew) - ON_BN_CLICKED(IDC_DECLBROWSER_BUTTON_RELOAD, OnBnClickedReload) - ON_BN_CLICKED(IDOK, OnBnClickedOk) - ON_BN_CLICKED(IDCANCEL, OnBnClickedCancel) -END_MESSAGE_MAP() - -// DialogDeclBrowser message handlers - -/* -================ -DialogDeclBrowser::OnActivate -================ -*/ -void DialogDeclBrowser::OnActivate( UINT nState, CWnd *pWndOther, BOOL bMinimized ) { - CDialog::OnActivate( nState, pWndOther, bMinimized ); -} - -/* -================ -DialogDeclBrowser::OnToolTipNotify -================ -*/ -BOOL DialogDeclBrowser::OnToolTipNotify( UINT id, NMHDR *pNMHDR, LRESULT *pResult ) { - // need to handle both ANSI and UNICODE versions of the message - TOOLTIPTEXTA* pTTTA = (TOOLTIPTEXTA*)pNMHDR; - TOOLTIPTEXTW* pTTTW = (TOOLTIPTEXTW*)pNMHDR; - - if ( pNMHDR->hwndFrom == declTree.GetSafeHwnd() ) { - CString toolTip; - const idDecl *decl = GetDeclFromTreeItem( (HTREEITEM) pNMHDR->idFrom ); - - if ( !decl ) { - return FALSE; - } - - toolTip = va( "%s, line: %d", decl->GetFileName(), decl->GetLineNum() ); - -#ifndef _UNICODE - if( pNMHDR->code == TTN_NEEDTEXTA ) { - delete m_pchTip; - m_pchTip = new TCHAR[toolTip.GetLength() + 2]; - lstrcpyn( m_pchTip, toolTip, toolTip.GetLength() + 1 ); - pTTTW->lpszText = (WCHAR*)m_pchTip; - } else { - delete m_pwchTip; - m_pwchTip = new WCHAR[toolTip.GetLength() + 2]; - _mbstowcsz( m_pwchTip, toolTip, toolTip.GetLength() + 1 ); - pTTTW->lpszText = (WCHAR*)m_pwchTip; - } -#else - if( pNMHDR->code == TTN_NEEDTEXTA ) { - delete m_pchTip; - m_pchTip = new TCHAR[toolTip.GetLength() + 2]; - _wcstombsz( m_pchTip, toolTip, toolTip.GetLength() + 1 ); - pTTTA->lpszText = (LPTSTR)m_pchTip; - } else { - delete m_pwchTip; - m_pwchTip = new WCHAR[toolTip.GetLength() + 2]; - lstrcpyn( m_pwchTip, toolTip, toolTip.GetLength() + 1 ); - pTTTA->lpszText = (LPTSTR) m_pwchTip; - } -#endif - return TRUE; - } - - return DefaultOnToolTipNotify( toolTips, id, pNMHDR, pResult ); -} - -/* -================ -DialogDeclBrowser::OnSetFocus -================ -*/ -void DialogDeclBrowser::OnSetFocus( CWnd *pOldWnd ) { - CDialog::OnSetFocus( pOldWnd ); -} - -/* -================ -DialogDeclBrowser::OnDestroy -================ -*/ -void DialogDeclBrowser::OnDestroy() { - - com_editors &= ~EDITOR_DECL; - - return CDialog::OnDestroy(); -} - -/* -================ -DialogDeclBrowser::OnMove -================ -*/ -void DialogDeclBrowser::OnMove( int x, int y ) { - if ( GetSafeHwnd() ) { - CRect rct; - GetWindowRect( rct ); - // FIXME: save position - } - CDialog::OnMove( x, y ); -} - -/* -================ -DialogDeclBrowser::OnMove -================ -*/ -#define BORDER_SIZE 4 -#define BUTTON_SPACE 4 -#define TOOLBAR_HEIGHT 24 - -void DialogDeclBrowser::OnSize( UINT nType, int cx, int cy ) { - CRect clientRect, rect; - - LockWindowUpdate(); - - CDialog::OnSize( nType, cx, cy ); - - GetClientRect( clientRect ); - - if ( declTree.GetSafeHwnd() ) { - rect.left = BORDER_SIZE; - rect.top = BORDER_SIZE; - rect.right = clientRect.Width() - BORDER_SIZE; - rect.bottom = clientRect.Height() - 100; - declTree.MoveWindow( rect.left, rect.top, rect.Width(), rect.Height() ); - } - - if ( findNameStatic.GetSafeHwnd() ) { - rect.left = BORDER_SIZE + 2; - rect.top = clientRect.Height() - 100 + BUTTON_SPACE + 2; - rect.right = BORDER_SIZE + 80; - rect.bottom = clientRect.Height() - 76 + 2; - findNameStatic.MoveWindow( rect.left, rect.top, rect.Width(), rect.Height() ); - } - - if ( findTextStatic.GetSafeHwnd() ) { - rect.left = BORDER_SIZE + 2; - rect.top = clientRect.Height() - 78 + BUTTON_SPACE + 2; - rect.right = BORDER_SIZE + 80; - rect.bottom = clientRect.Height() - 54 + 2; - findTextStatic.MoveWindow( rect.left, rect.top, rect.Width(), rect.Height() ); - } - - if ( findNameEdit.GetSafeHwnd() ) { - rect.left = BORDER_SIZE + 80; - rect.top = clientRect.Height() - 100 + BUTTON_SPACE; - rect.right = clientRect.Width() - BORDER_SIZE; - rect.bottom = clientRect.Height() - 76; - findNameEdit.MoveWindow( rect.left, rect.top, rect.Width(), rect.Height() ); - } - - if ( findTextEdit.GetSafeHwnd() ) { - rect.left = BORDER_SIZE + 80; - rect.top = clientRect.Height() - 78 + BUTTON_SPACE; - rect.right = clientRect.Width() - BORDER_SIZE; - rect.bottom = clientRect.Height() - 54; - findTextEdit.MoveWindow( rect.left, rect.top, rect.Width(), rect.Height() ); - } - - if ( findButton.GetSafeHwnd() ) { - findButton.GetClientRect( rect ); - int width = rect.Width(); - int height = rect.Height(); - rect.left = BORDER_SIZE; - rect.top = clientRect.Height() - TOOLBAR_HEIGHT - height; - rect.right = BORDER_SIZE + width; - rect.bottom = clientRect.Height() - TOOLBAR_HEIGHT; - findButton.MoveWindow( rect.left, rect.top, rect.Width(), rect.Height() ); - } - - if ( editButton.GetSafeHwnd() ) { - editButton.GetClientRect( rect ); - int width = rect.Width(); - int height = rect.Height(); - rect.left = BORDER_SIZE + BUTTON_SPACE + width; - rect.top = clientRect.Height() - TOOLBAR_HEIGHT - height; - rect.right = BORDER_SIZE + BUTTON_SPACE + 2 * width; - rect.bottom = clientRect.Height() - TOOLBAR_HEIGHT; - editButton.MoveWindow( rect.left, rect.top, rect.Width(), rect.Height() ); - } - - if ( newButton.GetSafeHwnd() ) { - newButton.GetClientRect( rect ); - int width = rect.Width(); - int height = rect.Height(); - rect.left = BORDER_SIZE + 2 * BUTTON_SPACE + 2 * width; - rect.top = clientRect.Height() - TOOLBAR_HEIGHT - height; - rect.right = BORDER_SIZE + 2 * BUTTON_SPACE + 3 * width; - rect.bottom = clientRect.Height() - TOOLBAR_HEIGHT; - newButton.MoveWindow( rect.left, rect.top, rect.Width(), rect.Height() ); - } - - if ( reloadButton.GetSafeHwnd() ) { - reloadButton.GetClientRect( rect ); - int width = rect.Width(); - int height = rect.Height(); - rect.left = BORDER_SIZE + 3 * BUTTON_SPACE + 3 * width; - rect.top = clientRect.Height() - TOOLBAR_HEIGHT - height; - rect.right = BORDER_SIZE + 3 * BUTTON_SPACE + 4 * width; - rect.bottom = clientRect.Height() - TOOLBAR_HEIGHT; - reloadButton.MoveWindow( rect.left, rect.top, rect.Width(), rect.Height() ); - } - - if ( cancelButton.GetSafeHwnd() ) { - cancelButton.GetClientRect( rect ); - int width = rect.Width(); - int height = rect.Height(); - rect.left = clientRect.Width() - BORDER_SIZE - width; - rect.top = clientRect.Height() - TOOLBAR_HEIGHT - height; - rect.right = clientRect.Width() - BORDER_SIZE; - rect.bottom = clientRect.Height() - TOOLBAR_HEIGHT; - cancelButton.MoveWindow( rect.left, rect.top, rect.Width(), rect.Height() ); - } - - if ( statusBar.GetSafeHwnd() ) { - rect.left = clientRect.Width() - 2; - rect.top = clientRect.Height() - 2; - rect.right = clientRect.Width() - 2; - rect.bottom = clientRect.Height() - 2; - statusBar.MoveWindow( rect.left, rect.top, rect.Width(), rect.Height() ); - } - - UnlockWindowUpdate(); -} - -/* -================ -DialogDeclBrowser::OnSizing -================ -*/ -void DialogDeclBrowser::OnSizing( UINT nSide, LPRECT lpRect ) { - /* - 1 = left - 2 = right - 3 = top - 4 = left - top - 5 = right - top - 6 = bottom - 7 = left - bottom - 8 = right - bottom - */ - - CDialog::OnSizing( nSide, lpRect ); - - if ( ( nSide - 1 ) % 3 == 0 ) { - if ( lpRect->right - lpRect->left < initialRect.Width() ) { - lpRect->left = lpRect->right - initialRect.Width(); - } - } else if ( ( nSide - 2 ) % 3 == 0 ) { - if ( lpRect->right - lpRect->left < initialRect.Width() ) { - lpRect->right = lpRect->left + initialRect.Width(); - } - } - if ( nSide >= 3 && nSide <= 5 ) { - if ( lpRect->bottom - lpRect->top < initialRect.Height() ) { - lpRect->top = lpRect->bottom - initialRect.Height(); - } - } else if ( nSide >= 6 && nSide <= 9 ) { - if ( lpRect->bottom - lpRect->top < initialRect.Height() ) { - lpRect->bottom = lpRect->top + initialRect.Height(); - } - } -} - -/* -================ -DialogDeclBrowser::OnTreeSelChanged -================ -*/ -void DialogDeclBrowser::OnTreeSelChanged( NMHDR* pNMHDR, LRESULT* pResult ) { - LV_KEYDOWN* pLVKeyDow = (LV_KEYDOWN*)pNMHDR; - - const idDecl *decl = GetSelectedDecl(); - if ( decl ) { - statusBar.SetWindowText( va( "%d decls listed - %s, line: %d", numListedDecls, decl->GetFileName(), decl->GetLineNum() ) ); - findNameEdit.SetWindowText( va( "%s/%s", declManager->GetDeclNameFromType( decl->GetType() ), decl->GetName() ) ); - } else { - HTREEITEM item = declTree.GetSelectedItem(); - idStr typeName, declName; - GetDeclName( item, typeName, declName ); - findNameEdit.SetWindowText( va( "%s/%s*", typeName.c_str(), declName.c_str() ) ); - statusBar.SetWindowText( va( "%d decls listed", numListedDecls ) ); - } - - *pResult = 0; -} - -/* -================ -DialogDeclBrowser::OnTreeDblclk -================ -*/ -void DialogDeclBrowser::OnTreeDblclk( NMHDR *pNMHDR, LRESULT *pResult ) { - // post a message as if the edit button was clicked to make sure the editor gets focus - PostMessage( WM_COMMAND, ( BN_CLICKED << 16 ) | editButton.GetDlgCtrlID(), 0 ); - - *pResult = 1; -} - -/* -================ -DialogDeclBrowser::OnBnClickedFind -================ -*/ void DialogDeclBrowser::OnBnClickedFind() { CString windowText; findNameEdit.GetWindowText( windowText ); findNameString = windowText; findNameString.Strip( ' ' ); + if ( !findNameString.Length() ) { + findNameString = "*"; + } findTextEdit.GetWindowText( windowText ); findTextString = windowText; findTextString.Strip( ' ' ); - + + reloadingTree = true; numListedDecls = baseDeclTree.SearchTree( DeclBrowserCompareDecl, this, declTree ); - - statusBar.SetWindowText( va( "%d decls listed", numListedDecls ) ); + reloadingTree = false; + SetBrowserStatus( va( "%d decls listed", numListedDecls ) ); } -/* -================ -DialogDeclBrowser::OnBnClickedEdit -================ -*/ -void DialogDeclBrowser::OnBnClickedEdit() { - EditSelected(); +void DialogDeclBrowser::OnBnClickedLoad() { + LoadSelectedDecl(); } -/* -================ -DialogDeclBrowser::OnBnClickedNew -================ -*/ -void DialogDeclBrowser::OnBnClickedNew() { - HTREEITEM item; - idStr typeName, declName; - const idDecl *decl; - DialogDeclNew newDeclDlg; - - newDeclDlg.SetDeclTree( &baseDeclTree ); - - item = declTree.GetSelectedItem(); - if ( item ) { - GetDeclName( item, typeName, declName ); - newDeclDlg.SetDefaultType( typeName ); - newDeclDlg.SetDefaultName( declName ); - } - - decl = GetSelectedDecl(); - if ( decl ) { - newDeclDlg.SetDefaultFile( decl->GetFileName() ); - } - - if ( newDeclDlg.DoModal() != IDOK ) { +void DialogDeclBrowser::LoadSelectedDecl( void ) { + const idDecl *selected = GetSelectedDecl(); + if ( !selected ) { + declEditor.ClearDecl(); + SetBrowserStatus( va( "%d decls listed", numListedDecls ) ); return; } - decl = newDeclDlg.GetNewDecl(); - - if ( decl ) { - declName = declManager->GetDeclNameFromType( decl->GetType() ); - declName += "/"; - declName += decl->GetName(); - - int id = GetIdFromTypeAndIndex( decl->GetType(), decl->Index() ); - - baseDeclTree.InsertPathIntoTree( declName, id ); - item = declTree.InsertPathIntoTree( declName, id ); - declTree.SelectItem( item ); - - EditSelected(); - } + idDecl *editable = const_cast( selected ); + declEditor.LoadDecl( editable ); + SetCreateDefaultsFromSelection( selected ); + SetBrowserStatus( va( "%d decls listed - %s/%s, %s line %d", numListedDecls, declManager->GetDeclNameFromType( selected->GetType() ), selected->GetName(), selected->GetFileName(), selected->GetLineNum() ) ); +} + +void DialogDeclBrowser::OnTreeSelChanged( NMHDR* pNMHDR, LRESULT* pResult ) { + if ( !reloadingTree ) { + const idDecl *decl = GetSelectedDecl(); + if ( decl ) { + findNameEdit.SetWindowText( va( "%s/%s", declManager->GetDeclNameFromType( decl->GetType() ), decl->GetName() ) ); + LoadSelectedDecl(); + } else { + HTREEITEM item = declTree.GetSelectedItem(); + idStr typeName, declName; + GetDeclName( item, typeName, declName ); + if ( typeName.Length() ) { + findNameEdit.SetWindowText( va( "%s/%s*", typeName.c_str(), declName.c_str() ) ); + } + SetBrowserStatus( va( "%d decls listed", numListedDecls ) ); + } + } + *pResult = 0; +} + +void DialogDeclBrowser::OnTreeDblclk( NMHDR *pNMHDR, LRESULT *pResult ) { + LoadSelectedDecl(); + FocusDeclEditor(); + *pResult = 1; } -/* -================ -DialogDeclBrowser::OnBnClickedReload -================ -*/ void DialogDeclBrowser::OnBnClickedReload() { - declManager->Reload( false ); - ReloadDeclarations(); } -/* -================ -DialogDeclBrowser::OnBnClickedOk -================ -*/ -void DialogDeclBrowser::OnBnClickedOk() { - // with a modeless dialog once it is closed and re-activated windows seems - // to enjoy mapping ENTER back to the default button ( OK ) even if you have - // it NOT set as the default.. in this case use cancel button exit and ignore - // default IDOK handling. - // OnOK(); +void DialogDeclBrowser::ReloadDeclarations( void ) { + idStr selectedType; + idStr selectedName; + const idDecl *selected = GetSelectedDecl(); + if ( selected ) { + selectedType = declManager->GetDeclNameFromType( selected->GetType() ); + selectedName = selected->GetName(); + } + + InitBaseDeclTree(); + OnBnClickedFind(); + + if ( selectedType.Length() && selectedName.Length() ) { + declType_t type = declManager->GetDeclTypeFromName( selectedType ); + SelectDeclInTree( type, selectedName ); + } } -/* -================ -DialogDeclBrowser::OnBnClickedCancel -================ -*/ -void DialogDeclBrowser::OnBnClickedCancel() { - OnCancel(); +void DialogDeclBrowser::SelectDeclInTree( declType_t type, const char *name ) { + if ( !name || !name[0] || type < 0 || type >= declManager->GetNumDeclTypes() ) { + return; + } + idStr path = declManager->GetDeclNameFromType( type ); + path += "/"; + path += name; + HTREEITEM item = declTree.FindItem( path ); + if ( item ) { + declTree.SelectItem( item ); + declTree.EnsureVisible( item ); + LoadSelectedDecl(); + } +} + +void DialogDeclBrowser::FocusDeclEditor( void ) { + if ( declEditor.GetSafeHwnd() ) { + declEditor.SetFocus(); + } +} + +declType_t DialogDeclBrowser::GetSelectedNewType( void ) { + CString typeName; + int sel = newTypeCombo.GetCurSel(); + if ( sel != CB_ERR ) { + newTypeCombo.GetLBText( sel, typeName ); + } else { + newTypeCombo.GetWindowText( typeName ); + } + return declManager->GetDeclTypeFromName( typeName ); +} + +void DialogDeclBrowser::GetDefaultFileForType( declType_t type, idStr &fileName ) const { + switch( type ) { + case DECL_TABLE: + case DECL_MATERIAL: + fileName = "materials/generated.mtr"; + break; + case DECL_SKIN: + fileName = "skins/generated.skin"; + break; + case DECL_SOUND: + fileName = "sound/generated.sndshd"; + break; + case DECL_ENTITYDEF: + fileName = "def/generated.def"; + break; + case DECL_MODELDEF: + fileName = "def/generated.def"; + break; + case DECL_FX: + fileName = "fx/generated.fx"; + break; + case DECL_PARTICLE: + fileName = "particles/generated.prt"; + break; + case DECL_AF: + fileName = "af/generated.af"; + break; + default: + fileName = "def/generated.decl"; + break; + } +} + +void DialogDeclBrowser::SetCreateDefaultsFromSelection( const idDecl *decl ) { + if ( !decl ) { + return; + } + if ( newTypeCombo.GetSafeHwnd() ) { + CString typeName = declManager->GetDeclNameFromType( decl->GetType() ); + int index = newTypeCombo.FindStringExact( -1, typeName ); + if ( index != CB_ERR ) { + newTypeCombo.SetCurSel( index ); + } + } + if ( newFileEdit.GetSafeHwnd() ) { + newFileEdit.SetWindowText( decl->GetFileName() ); + } +} + +void DialogDeclBrowser::OnTypeChanged() { + declType_t type = GetSelectedNewType(); + idStr fileName; + GetDefaultFileForType( type, fileName ); + if ( newFileEdit.GetSafeHwnd() ) { + newFileEdit.SetWindowText( fileName.c_str() ); + } +} + +void DialogDeclBrowser::OnBnClickedCreate() { + CString nameText; + CString fileText; + newNameEdit.GetWindowText( nameText ); + newFileEdit.GetWindowText( fileText ); + nameText.TrimLeft(); + nameText.TrimRight(); + fileText.TrimLeft(); + fileText.TrimRight(); + + if ( nameText.IsEmpty() ) { + MessageBox( "Enter a declaration name.", "Create declaration", MB_OK | MB_ICONINFORMATION ); + return; + } + + declType_t type = GetSelectedNewType(); + if ( type < 0 || type >= declManager->GetNumDeclTypes() ) { + MessageBox( "Select a valid declaration type.", "Create declaration", MB_OK | MB_ICONERROR ); + return; + } + + idStr fileName; + if ( fileText.IsEmpty() ) { + GetDefaultFileForType( type, fileName ); + } else { + fileName = fileText; + } + + idStr path = declManager->GetDeclNameFromType( type ); + path += "/"; + path += (LPCTSTR)nameText; + if ( baseDeclTree.FindItem( path ) ) { + MessageBox( "Declaration already exists.", "Create declaration", MB_OK | MB_ICONERROR ); + return; + } + + idDecl *newDecl = declManager->CreateNewDecl( type, nameText, fileName, true ); + if ( !newDecl ) { + MessageBox( "The declaration manager could not create the declaration.", "Create declaration", MB_OK | MB_ICONERROR ); + return; + } + + findNameEdit.SetWindowText( va( "%s/%s", declManager->GetDeclNameFromType( type ), newDecl->GetName() ) ); + findTextEdit.SetWindowText( "" ); + InitBaseDeclTree(); + OnBnClickedFind(); + SelectDeclInTree( type, newDecl->GetName() ); + FocusDeclEditor(); +} + +void DialogDeclBrowser::SetBrowserStatus( const char *text ) { + if ( statusText.GetSafeHwnd() ) { + statusText.SetWindowText( text ? text : "" ); + } +} + +void DeclBrowserInit( const idDict *spawnArgs ) { + if ( renderSystem->IsFullScreen() ) { + common->Printf( "Cannot run the declaration editor in fullscreen mode.\n" + "Set r_fullscreen to 0 and vid_restart.\n" ); + return; + } + + InitAfx(); + idKeyInput::ClearStates(); + + if ( g_Inspectors && g_Inspectors->GetSafeHwnd() ) { + g_Inspectors->ShowWindow( SW_SHOW ); + g_Inspectors->SetMode( W_DECLS ); + g_Inspectors->GetDeclBrowser().SetFocus(); + return; + } + + common->Printf( "Declaration editor is hosted in the Inspector Decls tab. Open the Inspector window first.\n" ); +} + +void DeclBrowserRun( void ) { + // No private message pump is needed. The declaration browser/editor now + // lives inside the Inspector dialog and uses Radiant's normal UI pump. +} + +void DeclBrowserShutdown( void ) { + // Owned by CInspectorDialog. +} + +void DeclBrowserReloadDeclarations( void ) { + if ( g_Inspectors && g_Inspectors->GetSafeHwnd() ) { + g_Inspectors->GetDeclBrowser().ReloadDeclarations(); + } } diff --git a/neo/engine/tools/decl/DialogDeclBrowser.h b/neo/engine/tools/decl/DialogDeclBrowser.h index ec21ca8b..bf60b8d4 100644 --- a/neo/engine/tools/decl/DialogDeclBrowser.h +++ b/neo/engine/tools/decl/DialogDeclBrowser.h @@ -31,83 +31,104 @@ If you have questions concerning this license or the applicable additional terms #pragma once -// DialogDeclBrowser dialog +#include "../comafx/CPathTreeCtrl.h" +#include "DialogDeclEditor.h" -class DialogDeclBrowser : public CDialog { +// Legacy class name retained for source compatibility. This is now a dockable +// child window instead of a floating editor. + +class DialogDeclBrowser : public CWnd { DECLARE_DYNAMIC(DialogDeclBrowser) public: - DialogDeclBrowser( CWnd* pParent = NULL ); // standard constructor + DialogDeclBrowser(); virtual ~DialogDeclBrowser(); + BOOL Create( CWnd *parent, UINT id = 0 ); void ReloadDeclarations( void ); bool CompareDecl( HTREEITEM item, const char *name ) const; - - //{{AFX_VIRTUAL(DialogDeclBrowser) - virtual BOOL OnInitDialog(); - virtual void DoDataExchange( CDataExchange* pDX ); // DDX/DDV support - //}}AFX_VIRTUAL + const idDecl * GetSelectedDecl( void ) const; + DialogDeclEditor & GetEditor( void ) { return declEditor; } + void FocusDeclEditor( void ); protected: - //{{AFX_MSG(DialogDeclBrowser) + afx_msg int OnCreate( LPCREATESTRUCT lpCreateStruct ); afx_msg BOOL OnToolTipNotify( UINT id, NMHDR *pNMHDR, LRESULT *pResult ); + afx_msg HBRUSH OnCtlColor( CDC* pDC, CWnd* pWnd, UINT nCtlColor ); + afx_msg BOOL OnEraseBkgnd( CDC* pDC ); + afx_msg void OnPaint(); afx_msg void OnSetFocus( CWnd *pOldWnd ); afx_msg void OnDestroy(); - afx_msg void OnActivate(UINT nState, CWnd* pWndOther, BOOL bMinimized); - afx_msg void OnMove( int x, int y ); afx_msg void OnSize( UINT nType, int cx, int cy ); - afx_msg void OnSizing( UINT nSide, LPRECT lpRect ); afx_msg void OnTreeSelChanged( NMHDR* pNMHDR, LRESULT* pResult ); afx_msg void OnTreeDblclk( NMHDR *pNMHDR, LRESULT *pResult ); afx_msg void OnBnClickedFind(); - afx_msg void OnBnClickedEdit(); - afx_msg void OnBnClickedNew(); + afx_msg void OnBnClickedLoad(); + afx_msg void OnBnClickedCreate(); afx_msg void OnBnClickedReload(); - afx_msg void OnBnClickedOk(); - afx_msg void OnBnClickedCancel(); - //}}AFX_MSG + afx_msg void OnTypeChanged(); DECLARE_MESSAGE_MAP() private: - - //{{AFX_DATA(DialogDeclBrowser) - enum { IDD = IDD_DIALOG_DECLBROWSER }; - CStatusBarCtrl statusBar; CPathTreeCtrl declTree; + CPathTreeCtrl baseDeclTree; CStatic findNameStatic; CStatic findTextStatic; CEdit findNameEdit; CEdit findTextEdit; CButton findButton; - CButton editButton; - CButton newButton; + CButton loadButton; CButton reloadButton; - CButton cancelButton; - //}}AFX_DATA + CStatic newTypeStatic; + CStatic newNameStatic; + CStatic newFileStatic; + CComboBox newTypeCombo; + CEdit newNameEdit; + CEdit newFileEdit; + CButton createButton; + CStatic statusText; + DialogDeclEditor declEditor; static toolTip_t toolTips[]; - CRect initialRect; - CPathTreeCtrl baseDeclTree; int numListedDecls; idStr findNameString; idStr findTextString; + bool controlsCreated; + bool reloadingTree; - TCHAR * m_pchTip; + char * m_pchTip; WCHAR * m_pwchTip; -private: - void AddDeclTypeToTree( declType_t type, const char *root, CPathTreeCtrl &tree ); - void AddScriptsToTree( CPathTreeCtrl &tree ); - void AddGUIsToTree( CPathTreeCtrl &tree ); - void InitBaseDeclTree( void ); + CFont modernFont; + CBrush bgBrush; + CBrush panelBrush; + CBrush editBrush; + CBrush staticBrush; +private: + void CreateChildControls( void ); + void PopulateTypeCombo( void ); + void AddDeclTypeToTree( declType_t type, const char *root, CPathTreeCtrl &tree ); + void InitBaseDeclTree( void ); void GetDeclName( HTREEITEM item, idStr &typeName, idStr &declName ) const; const idDecl * GetDeclFromTreeItem( HTREEITEM item ) const; - const idDecl * GetSelectedDecl( void ) const; - void EditSelected( void ) const; + void LoadSelectedDecl( void ); + void SelectDeclInTree( declType_t type, const char *name ); + void ApplyDarkTheme( void ); + void ApplyModernFontRecursive( CWnd *wnd ); + void LayoutBrowser( int cx, int cy ); + void SetBrowserStatus( const char *text ); + declType_t GetSelectedNewType( void ); + void SetCreateDefaultsFromSelection( const idDecl *decl ); + void GetDefaultFileForType( declType_t type, idStr &fileName ) const; }; +void DeclBrowserInit( const idDict *spawnArgs ); +void DeclBrowserRun( void ); +void DeclBrowserShutdown( void ); +void DeclBrowserReloadDeclarations( void ); + #endif /* !__DIALOGDECLBROWSER_H__ */ diff --git a/neo/engine/tools/decl/DialogDeclEditor.cpp b/neo/engine/tools/decl/DialogDeclEditor.cpp index c095fff5..7f4c77cf 100644 --- a/neo/engine/tools/decl/DialogDeclEditor.cpp +++ b/neo/engine/tools/decl/DialogDeclEditor.cpp @@ -29,12 +29,7 @@ If you have questions concerning this license or the applicable additional terms #include "precompiled.h" #pragma hdrstop -#include "../../sys/win32/rc/Common_resource.h" -#include "../../sys/win32/rc/DeclEditor_resource.h" - -#include "../comafx/DialogGoToLine.h" -#include "../comafx/CPathTreeCtrl.h" -#include "DialogDeclBrowser.h" +#include "../radiant/qe3.h" #include "DialogDeclEditor.h" #ifdef ID_DEBUG_MEMORY @@ -43,78 +38,1188 @@ If you have questions concerning this license or the applicable additional terms #define DEBUG_NEW new #endif +void DeclBrowserReloadDeclarations( void ); -// DialogDeclEditor dialog +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 UINT FindDialogMessage = ::RegisterWindowMessage( FINDMSGSTRING ); +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_BUTTON_TEST, "test decl" }, - { IDOK, "save decl" }, - { IDCANCEL, "cancel" }, + { 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 } }; - -IMPLEMENT_DYNAMIC(DialogDeclEditor, CDialog) - -/* -================ -DialogDeclEditor::DialogDeclEditor -================ -*/ -DialogDeclEditor::DialogDeclEditor( CWnd* pParent /*=NULL*/ ) - : CDialog(DialogDeclEditor::IDD, pParent) - , findDlg(NULL) - , matchCase(false) - , matchWholeWords(false) - , decl(NULL) - , firstLine(0) -{ +DialogDeclEditor::DialogDeclEditor() + : CWnd() + , decl( NULL ) + , firstLine( 0 ) + , modified( false ) + , updatingControls( false ) + , controlsCreated( false ) { } -/* -================ -DialogDeclEditor::~DialogDeclEditor -================ -*/ 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(); + } } -/* -================ -DialogDeclEditor::DoDataExchange -================ -*/ -void DialogDeclEditor::DoDataExchange(CDataExchange* pDX) { - CDialog::DoDataExchange(pDX); - //{{AFX_DATA_MAP(DialogDeclEditor) - DDX_Control(pDX, IDC_DECLEDITOR_EDIT_TEXT, declEdit); - DDX_Control(pDX, IDC_DECLEDITOR_BUTTON_TEST, testButton); - DDX_Control(pDX, IDOK, okButton); - DDX_Control(pDX, IDCANCEL, cancelButton); - //}}AFX_DATA_MAP +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 ); } -/* -================ -DialogDeclEditor::PreTranslateMessage -================ -*/ +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 ) { - if ( WM_KEYFIRST <= pMsg->message && pMsg->message <= WM_KEYLAST ) { - if ( m_hAccel && ::TranslateAccelerator( m_hWnd, m_hAccel, pMsg ) ) { - return TRUE; + 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 ); } } - return CWnd::PreTranslateMessage(pMsg); + + 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; } -/* -================ -DialogDeclEditor::TestDecl -================ -*/ bool DialogDeclEditor::TestDecl( const idStr &declText ) { idLexer src( LEXFL_NOSTRINGCONCAT ); idToken token; @@ -132,511 +1237,227 @@ bool DialogDeclEditor::TestDecl( const idStr &declText ) { } if ( indent < 0 ) { - MessageBox( "Missing opening brace!", va( "Error saving %s", decl->GetFileName() ), MB_OK | MB_ICONERROR ); + 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!", va( "Error saving %s", decl->GetFileName() ), MB_OK | MB_ICONERROR ); + MessageBox( "Missing closing brace!", decl ? va( "Error saving %s", decl->GetFileName() ) : "Error saving decl", MB_OK | MB_ICONERROR ); return false; } return true; } -/* -================ -DialogDeclEditor::UpdateStatusBar -================ -*/ -void DialogDeclEditor::UpdateStatusBar( void ) { - int line, column, character; - - if ( decl ) { - declEdit.GetCursorPos( line, column, character ); - statusBar.SetWindowText( va( "Line: %d, Column: %d, Character: %d", decl->GetLineNum() + line, column, character ) ); +bool DialogDeclEditor::SaveDeclText( void ) { + if ( !decl ) { + return false; } -} -/* -================ -DialogDeclEditor::LoadDecl -================ -*/ -void DialogDeclEditor::LoadDecl( idDecl *decl ) { - int numLines = 0; - int numCharsPerLine = 0; - int maxCharsPerLine = 0; idStr declText; - CRect rect; - - this->decl = decl; - - switch( decl->GetType() ) { - case DECL_ENTITYDEF: - declEdit.SetStringColor( SRE_COLOR_BLUE, SRE_COLOR_DARK_CYAN ); - declEdit.LoadKeyWordsFromFile( "editors/entity.def" ); - break; - case DECL_MATERIAL: - declEdit.LoadKeyWordsFromFile( "editors/material.def" ); - break; - case DECL_SKIN: - declEdit.LoadKeyWordsFromFile( "editors/skin.def" ); - break; - case DECL_SOUND: - declEdit.LoadKeyWordsFromFile( "editors/sound.def" ); - break; - case DECL_FX: - declEdit.LoadKeyWordsFromFile( "editors/fx.def" ); - break; - case DECL_PARTICLE: - declEdit.LoadKeyWordsFromFile( "editors/particle.def" ); - break; - case DECL_AF: - declEdit.LoadKeyWordsFromFile( "editors/af.def" ); - break; - case DECL_TABLE: - declEdit.LoadKeyWordsFromFile( "editors/table.def" ); - break; - case DECL_MODELDEF: - declEdit.LoadKeyWordsFromFile( "editors/model.def" ); - break; - default: - declEdit.LoadKeyWordsFromFile( va( "editors/%s.def", declManager->GetDeclNameFromType( decl->GetType() ) ) ); - break; + idStr editedName; + if ( !GetEditedDeclName( editedName ) ) { + MessageBox( "Enter a declaration name before saving.", "Error saving declaration", MB_OK | MB_ICONERROR ); + return false; } - firstLine = decl->GetLineNum(); + BuildDeclText( declText ); + if ( !TestDecl( declText ) ) { + return false; + } - char *localDeclText = (char *)_alloca( ( decl->GetTextLength() + 1 ) * sizeof( char ) ); - decl->GetText( localDeclText ); - declText = localDeclText; + const declType_t type = decl->GetType(); + idStr oldName = decl->GetName(); + idStr fileName = decl->GetFileName(); + idStr targetName = editedName; - // clean up new-line crapola - declText.Replace( "\r", "" ); - declText.Replace( "\n", "\r" ); - declText.Replace( "\v", "\r" ); - declText.StripLeading( '\r' ); - declText.Append( "\r" ); + 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(); + } - declEdit.SetText( declText ); - - for( const char *ptr = declText.c_str(); *ptr; ptr++ ) { - if ( *ptr == '\r' ) { - if ( numCharsPerLine > maxCharsPerLine ) { - maxCharsPerLine = numCharsPerLine; - } - numCharsPerLine = 0; - numLines++; - } else if ( *ptr == '\t' ) { - numCharsPerLine += TAB_SIZE; - } else { - numCharsPerLine++; + 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 ); } } - SetWindowText( va( "Declaration Editor (%s, line %d)", decl->GetFileName(), decl->GetLineNum() ) ); - - rect.left = initialRect.left; - rect.right = rect.left + maxCharsPerLine * FONT_WIDTH + 32; - rect.top = initialRect.top; - rect.bottom = rect.top + numLines * (FONT_HEIGHT+8) + 24 + 56; - if ( rect.right < initialRect.right ) { - rect.right = initialRect.right; - } else if ( rect.right - rect.left > 1024 ) { - rect.right = rect.left + 1024; + 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 ( rect.bottom < initialRect.bottom ) { - rect.bottom = initialRect.bottom; - } else if ( rect.bottom - rect.top > 768 ) { - rect.bottom = rect.top + 768; + + 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; } - MoveWindow( rect ); - testButton.EnableWindow( FALSE ); - okButton.EnableWindow( FALSE ); + const idDecl *freshDecl = declManager->FindType( type, targetName, false ); + if ( freshDecl ) { + decl = const_cast( freshDecl ); + } + if ( decl ) { + decl->Invalidate(); + } - UpdateStatusBar(); - - declEdit.SetFocus(); + SetModified( false ); + DeclBrowserReloadDeclarations(); + common->Printf( "saved decl %s\n", targetName.c_str() ); + return true; } -/* -================ -DialogDeclEditor::OnInitDialog -================ -*/ -BOOL DialogDeclEditor::OnInitDialog() { - - com_editors |= EDITOR_DECL; - - CDialog::OnInitDialog(); - - // load accelerator table - m_hAccel = ::LoadAccelerators( AfxGetResourceHandle(), MAKEINTRESOURCE( IDR_ACCELERATOR_DECLEDITOR ) ); - - // create status bar - statusBar.CreateEx( SBARS_SIZEGRIP, WS_CHILD | WS_VISIBLE | CBRS_BOTTOM, initialRect, this, AFX_IDW_STATUS_BAR ); - - declEdit.Init(); - - GetClientRect( initialRect ); - - EnableToolTips( TRUE ); - - testButton.EnableWindow( FALSE ); - okButton.EnableWindow( FALSE ); - - UpdateStatusBar(); - - return FALSE; // return TRUE unless you set the focus to a control - // EXCEPTION: OCX Property Pages should return FALSE -} - - -BEGIN_MESSAGE_MAP(DialogDeclEditor, CDialog) - ON_NOTIFY_EX_RANGE(TTN_NEEDTEXTW, 0, 0xFFFF, OnToolTipNotify) - ON_NOTIFY_EX_RANGE(TTN_NEEDTEXTA, 0, 0xFFFF, OnToolTipNotify) - ON_WM_DESTROY() - ON_WM_ACTIVATE() - ON_WM_MOVE() - ON_WM_SIZE() - ON_WM_SIZING() - ON_WM_SETFOCUS() - ON_COMMAND(ID_EDIT_FIND, OnEditFind) - ON_COMMAND(ID_EDIT_REPLACE, OnEditReplace) - ON_COMMAND(ID_DECLEDITOR_FIND_NEXT, OnEditFindNext) - ON_COMMAND(ID_DECLEDITOR_GOTOLINE, OnEditGoToLine) - ON_REGISTERED_MESSAGE(FindDialogMessage, OnFindDialogMessage) - ON_NOTIFY(EN_CHANGE, IDC_DECLEDITOR_EDIT_TEXT, OnEnChangeEdit) - ON_NOTIFY(EN_MSGFILTER, IDC_DECLEDITOR_EDIT_TEXT, OnEnInputEdit) - ON_BN_CLICKED(IDC_DECLEDITOR_BUTTON_TEST, OnBnClickedTest) - ON_BN_CLICKED(IDOK, OnBnClickedOk) - ON_BN_CLICKED(IDCANCEL, OnBnClickedCancel) -END_MESSAGE_MAP() - - -// DialogDeclEditor message handlers - -/* -================ -DialogDeclEditor::OnActivate -================ -*/ -void DialogDeclEditor::OnActivate( UINT nState, CWnd *pWndOther, BOOL bMinimized ) { - CDialog::OnActivate( nState, pWndOther, bMinimized ); -} - -/* -================ -DialogDeclEditor::OnToolTipNotify -================ -*/ -BOOL DialogDeclEditor::OnToolTipNotify( UINT id, NMHDR *pNMHDR, LRESULT *pResult ) { - return DefaultOnToolTipNotify( toolTips, id, pNMHDR, pResult ); -} - -/* -================ -DialogDeclEditor::OnSetFocus -================ -*/ -void DialogDeclEditor::OnSetFocus( CWnd *pOldWnd ) { - CDialog::OnSetFocus( pOldWnd ); -} - -/* -================ -DialogDeclEditor::OnDestroy -================ -*/ -void DialogDeclEditor::OnDestroy() { - return CDialog::OnDestroy(); -} - -/* -================ -DialogDeclEditor::OnMove -================ -*/ -void DialogDeclEditor::OnMove( int x, int y ) { - if ( GetSafeHwnd() ) { - CRect rct; - GetWindowRect( rct ); - // FIXME: save position - } - CDialog::OnMove( x, y ); -} - -/* -================ -DialogDeclEditor::OnSize -================ -*/ -#define BORDER_SIZE 0 -#define BUTTON_SPACE 4 -#define TOOLBAR_HEIGHT 24 - -void DialogDeclEditor::OnSize( UINT nType, int cx, int cy ) { - CRect clientRect, rect; - - LockWindowUpdate(); - - CDialog::OnSize( nType, cx, cy ); - - GetClientRect( clientRect ); - - if ( declEdit.GetSafeHwnd() ) { - rect.left = BORDER_SIZE; - rect.top = BORDER_SIZE; - rect.right = clientRect.Width() - BORDER_SIZE; - rect.bottom = clientRect.Height() - 56; - declEdit.MoveWindow( rect.left, rect.top, rect.Width(), rect.Height() ); - } - - if ( testButton.GetSafeHwnd() ) { - testButton.GetClientRect( rect ); - int width = rect.Width(); - int height = rect.Height(); - rect.left = BORDER_SIZE; - rect.top = clientRect.Height() - TOOLBAR_HEIGHT - height; - rect.right = BORDER_SIZE + width; - rect.bottom = clientRect.Height() - TOOLBAR_HEIGHT; - testButton.MoveWindow( rect.left, rect.top, rect.Width(), rect.Height() ); - } - - if ( okButton.GetSafeHwnd() ) { - okButton.GetClientRect( rect ); - int width = rect.Width(); - int height = rect.Height(); - rect.left = clientRect.Width() - BORDER_SIZE - BUTTON_SPACE - 2 * width; - rect.top = clientRect.Height() - TOOLBAR_HEIGHT - height; - rect.right = clientRect.Width() - BORDER_SIZE - BUTTON_SPACE - width; - rect.bottom = clientRect.Height() - TOOLBAR_HEIGHT; - okButton.MoveWindow( rect.left, rect.top, rect.Width(), rect.Height() ); - } - - if ( cancelButton.GetSafeHwnd() ) { - cancelButton.GetClientRect( rect ); - int width = rect.Width(); - int height = rect.Height(); - rect.left = clientRect.Width() - BORDER_SIZE - width; - rect.top = clientRect.Height() - TOOLBAR_HEIGHT - height; - rect.right = clientRect.Width() - BORDER_SIZE; - rect.bottom = clientRect.Height() - TOOLBAR_HEIGHT; - cancelButton.MoveWindow( rect.left, rect.top, rect.Width(), rect.Height() ); - } - - if ( statusBar.GetSafeHwnd() ) { - rect.left = clientRect.Width() - 2; - rect.top = clientRect.Height() - 2; - rect.right = clientRect.Width() - 2; - rect.bottom = clientRect.Height() - 2; - statusBar.MoveWindow( rect.left, rect.top, rect.Width(), rect.Height() ); - } - - UnlockWindowUpdate(); -} - -/* -================ -DialogDeclEditor::OnSizing -================ -*/ -void DialogDeclEditor::OnSizing( UINT nSide, LPRECT lpRect ) { - /* - 1 = left - 2 = right - 3 = top - 4 = left - top - 5 = right - top - 6 = bottom - 7 = left - bottom - 8 = right - bottom - */ - - CDialog::OnSizing( nSide, lpRect ); - - if ( ( nSide - 1 ) % 3 == 0 ) { - if ( lpRect->right - lpRect->left < initialRect.Width() ) { - lpRect->left = lpRect->right - initialRect.Width(); - } - } else if ( ( nSide - 2 ) % 3 == 0 ) { - if ( lpRect->right - lpRect->left < initialRect.Width() ) { - lpRect->right = lpRect->left + initialRect.Width(); - } - } - if ( nSide >= 3 && nSide <= 5 ) { - if ( lpRect->bottom - lpRect->top < initialRect.Height() ) { - lpRect->top = lpRect->bottom - initialRect.Height(); - } - } else if ( nSide >= 6 && nSide <= 9 ) { - if ( lpRect->bottom - lpRect->top < initialRect.Height() ) { - lpRect->bottom = lpRect->top + initialRect.Height(); - } - } -} - -/* -================ -DialogDeclEditor::OnEditGoToLine -================ -*/ -void DialogDeclEditor::OnEditGoToLine() { - DialogGoToLine goToLineDlg; - - goToLineDlg.SetRange( firstLine, firstLine + declEdit.GetLineCount() - 1 ); - if ( goToLineDlg.DoModal() != IDOK ) { +void DialogDeclEditor::UpdateStatusBar( void ) { + if ( !statusText.GetSafeHwnd() ) { return; } - declEdit.GoToLine( goToLineDlg.GetLine() - firstLine ); + 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 ); } -/* -================ -DialogDeclEditor::OnEditFind -================ -*/ -void DialogDeclEditor::OnEditFind() { - - CString selText = declEdit.GetSelText(); - if ( selText.GetLength() ) { - findStr = selText; - } - - if ( !findDlg ) { - // create find/replace dialog - findDlg = new CFindReplaceDialog(); // Must be created on the heap - findDlg->Create( TRUE, findStr, "", FR_DOWN, this ); +void DialogDeclEditor::OnNameChange() { + if ( updatingControls ) { + return; } + SetModified( true ); } -/* -================ -DialogDeclEditor::OnEditFindNext -================ -*/ -void DialogDeclEditor::OnEditFindNext() { - if ( declEdit.FindNext( findStr, matchCase, matchWholeWords, searchForward ) ) { - declEdit.SetFocus(); - } else { - AfxMessageBox( "The specified text was not found.", MB_OK | MB_ICONINFORMATION, 0 ); +void DialogDeclEditor::OnRawBodyChange() { + if ( updatingControls ) { + return; } + SetModified( true ); } -/* -================ -DialogDeclEditor::OnEditReplace -================ -*/ -void DialogDeclEditor::OnEditReplace() { - - CString selText = declEdit.GetSelText(); - if ( selText.GetLength() ) { - findStr = selText; +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 ); } - - // create find/replace dialog - if ( !findDlg ) { - findDlg = new CFindReplaceDialog(); // Must be created on the heap - findDlg->Create( FALSE, findStr, "", FR_DOWN, this ); - } -} - -/* -================ -DialogDeclEditor::OnFindDialogMessage -================ -*/ -LRESULT DialogDeclEditor::OnFindDialogMessage( WPARAM wParam, LPARAM lParam ) { - if ( findDlg == NULL ) { - return 0; - } - - if ( findDlg->IsTerminating() ) { - findDlg = NULL; - return 0; - } - - if( findDlg->FindNext() ) { - findStr = findDlg->GetFindString(); - matchCase = findDlg->MatchCase() != FALSE; - matchWholeWords = findDlg->MatchWholeWord() != FALSE; - searchForward = findDlg->SearchDown() != FALSE; - - OnEditFindNext(); - } - - if ( findDlg->ReplaceCurrent() ) { - long selStart, selEnd; - - replaceStr = findDlg->GetReplaceString(); - - declEdit.GetSel( selStart, selEnd ); - if ( selEnd > selStart ) { - declEdit.ReplaceSel( replaceStr, TRUE ); - } - } - - if ( findDlg->ReplaceAll() ) { - replaceStr = findDlg->GetReplaceString(); - findStr = findDlg->GetFindString(); - matchCase = findDlg->MatchCase() != FALSE; - matchWholeWords = findDlg->MatchWholeWord() != FALSE; - - int numReplaces = declEdit.ReplaceAll( findStr, replaceStr, matchCase, matchWholeWords ); - if ( numReplaces == 0 ) { - AfxMessageBox( "The specified text was not found.", MB_OK | MB_ICONINFORMATION, 0 ); - } else { - AfxMessageBox( va( "Replaced %d occurances.", numReplaces ), MB_OK | MB_ICONINFORMATION, 0 ); - } - } - - return 0; -} - -/* -================ -DialogDeclEditor::OnEnChangeEdit -================ -*/ -void DialogDeclEditor::OnEnChangeEdit( NMHDR *pNMHDR, LRESULT *pResult ) { - testButton.EnableWindow( TRUE ); - okButton.EnableWindow( TRUE ); -} - -/* -================ -DialogDeclEditor::OnEnInputEdit -================ -*/ -void DialogDeclEditor::OnEnInputEdit( NMHDR *pNMHDR, LRESULT *pResult ) { - MSGFILTER *msgFilter = (MSGFILTER *)pNMHDR; - - if ( msgFilter->msg != 512 && msgFilter->msg != 33 ) { - UpdateStatusBar(); - } - *pResult = 0; } -/* -================ -DialogDeclEditor::OnBnClickedTest -================ -*/ +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 ) { - - declEdit.GetText( declText ); - - // clean up new-line crapola - declText.Replace( "\n", "" ); - declText.Replace( "\r", "\r\n" ); - declText.Replace( "\v", "\r\n" ); - declText.StripLeading( "\r\n" ); - declText.Insert( "\r\n\r\n", 0 ); - declText.StripTrailing( "\r\n" ); + BuildDeclText( declText ); if ( !TestDecl( declText ) ) { return; @@ -651,65 +1472,24 @@ void DialogDeclEditor::OnBnClickedTest() { decl->Invalidate(); common->Printf( "tested %s\n", decl->GetName() ); - testButton.EnableWindow( FALSE ); + if ( testButton.GetSafeHwnd() ) { + testButton.EnableWindow( FALSE ); + } } } -/* -================ -DialogDeclEditor::OnBnClickedOk -================ -*/ -void DialogDeclEditor::OnBnClickedOk() { - idStr declText; - - if ( decl ) { - - declEdit.GetText( declText ); - - // clean up new-line crapola - declText.Replace( "\n", "" ); - declText.Replace( "\r", "\r\n" ); - declText.Replace( "\v", "\r\n" ); - declText.StripLeading( "\r\n" ); - declText.Insert( "\r\n\r\n", 0 ); - declText.StripTrailing( "\r\n" ); - - if ( !TestDecl( declText ) ) { - return; - } - - if ( decl->SourceFileChanged() ) { - if ( MessageBox( va( "Declaration file %s has been modified outside of the editor.\r\nReload declarations and save?", decl->GetFileName() ), - va( "Warning saving: %s", decl->GetFileName() ), MB_OKCANCEL | MB_ICONERROR ) != IDOK ) { - return; - } - declManager->Reload( false ); - DeclBrowserReloadDeclarations(); - } - - decl->SetText( declText ); - if ( !decl->ReplaceSourceFileText() ) { - MessageBox( va( "Couldn't save: %s.\r\nMake sure the declaration file is not read-only.", decl->GetFileName() ), - va( "Error saving: %s", decl->GetFileName() ), MB_OK | MB_ICONERROR ); - return; - } - decl->Invalidate(); - } - - okButton.EnableWindow( FALSE ); +void DialogDeclEditor::OnBnClickedSave() { + SaveDeclText(); } -/* -================ -DialogDeclEditor::OnBnClickedCancel -================ -*/ -void DialogDeclEditor::OnBnClickedCancel() { - if ( okButton.IsWindowEnabled() ) { - if ( MessageBox( "Cancel changes?", "Cancel", MB_YESNO | MB_ICONQUESTION ) != IDYES ) { +void DialogDeclEditor::OnBnClickedRevert() { + if ( !decl ) { + return; + } + if ( modified ) { + if ( MessageBox( "Revert changes to this declaration?", "Revert", MB_YESNO | MB_ICONQUESTION ) != IDYES ) { return; } } - OnCancel(); + LoadDecl( decl ); } diff --git a/neo/engine/tools/decl/DialogDeclEditor.h b/neo/engine/tools/decl/DialogDeclEditor.h index a39461da..03dddbfa 100644 --- a/neo/engine/tools/decl/DialogDeclEditor.h +++ b/neo/engine/tools/decl/DialogDeclEditor.h @@ -31,75 +31,117 @@ If you have questions concerning this license or the applicable additional terms #pragma once -#include "../comafx/CSyntaxRichEditCtrl.h" +#include "afxcmn.h" -// DialogDeclEditor dialog +// Despite the legacy file/class name this is no longer a dialog. It is a +// dockable child-window editor used by the inspector declaration tab. It uses +// only stock MFC controls and no custom syntax edit widget. -class DialogDeclEditor : public CDialog { +class DialogDeclEditor : public CWnd { DECLARE_DYNAMIC(DialogDeclEditor) public: - DialogDeclEditor( CWnd* pParent = NULL ); // standard constructor + DialogDeclEditor(); virtual ~DialogDeclEditor(); + BOOL Create( CWnd *parent, UINT id = 0 ); void LoadDecl( idDecl *decl ); + void ClearDecl( void ); + bool IsModified( void ) const { return modified; } + idDecl * GetDecl( void ) const { return decl; } - //{{AFX_VIRTUAL(DialogDeclEditor) - virtual BOOL OnInitDialog(); - virtual void DoDataExchange( CDataExchange* pDX ); // DDX/DDV support virtual BOOL PreTranslateMessage( MSG* pMsg ); - //}}AFX_VIRTUAL protected: - //{{AFX_MSG(DialogDeclEditor) + afx_msg int OnCreate( LPCREATESTRUCT lpCreateStruct ); afx_msg BOOL OnToolTipNotify( UINT id, NMHDR *pNMHDR, LRESULT *pResult ); + afx_msg HBRUSH OnCtlColor( CDC* pDC, CWnd* pWnd, UINT nCtlColor ); + afx_msg BOOL OnEraseBkgnd( CDC* pDC ); + afx_msg void OnPaint(); afx_msg void OnSetFocus( CWnd *pOldWnd ); afx_msg void OnDestroy(); - afx_msg void OnActivate( UINT nState, CWnd* pWndOther, BOOL bMinimized ); - afx_msg void OnMove( int x, int y ); afx_msg void OnSize( UINT nType, int cx, int cy ); - afx_msg void OnSizing( UINT nSide, LPRECT lpRect ); - afx_msg void OnEditGoToLine(); - afx_msg void OnEditFind(); - afx_msg void OnEditFindNext(); - afx_msg void OnEditReplace(); - afx_msg LRESULT OnFindDialogMessage( WPARAM wParam, LPARAM lParam ); - afx_msg void OnEnChangeEdit( NMHDR *pNMHDR, LRESULT *pResult ); - afx_msg void OnEnInputEdit( NMHDR *pNMHDR, LRESULT *pResult ); + afx_msg void OnNameChange(); + afx_msg void OnRawBodyChange(); + afx_msg void OnPropertyItemChanged( NMHDR *pNMHDR, LRESULT *pResult ); + afx_msg void OnPropertyCustomDraw( NMHDR *pNMHDR, LRESULT *pResult ); + afx_msg void OnBnClickedAddProperty(); + afx_msg void OnBnClickedRemoveProperty(); + afx_msg void OnBnClickedParseRaw(); + afx_msg void OnBnClickedDeleteDecl(); afx_msg void OnBnClickedTest(); - afx_msg void OnBnClickedOk(); - afx_msg void OnBnClickedCancel(); - //}}AFX_MSG + afx_msg void OnBnClickedSave(); + afx_msg void OnBnClickedRevert(); DECLARE_MESSAGE_MAP() private: - - //{{AFX_DATA(DialogDeclEditor) - enum { IDD = IDD_DIALOG_DECLEDITOR }; - CStatusBarCtrl statusBar; - CSyntaxRichEditCtrl declEdit; + CStatic typeLabel; + CStatic fileLabel; + CStatic nameLabel; + CStatic propertiesLabel; + CStatic keyLabel; + CStatic valueLabel; + CStatic rawBodyLabel; + CStatic statusText; + CEdit nameEdit; + CEdit keyEdit; + CEdit valueEdit; + CEdit rawBodyEdit; + CListCtrl propertyList; + CButton addPropertyButton; + CButton removePropertyButton; + CButton parseRawButton; + CButton deleteDeclButton; CButton testButton; - CButton okButton; - CButton cancelButton; - //}}AFX_DATA + CButton saveButton; + CButton revertButton; + + struct declProperty_t { + idStr key; + idStr value; + int lineIndex; + bool quotedKey; + bool quotedValue; + }; static toolTip_t toolTips[]; - HACCEL m_hAccel; - CRect initialRect; - CFindReplaceDialog *findDlg; - CString findStr; - CString replaceStr; - bool matchCase; - bool matchWholeWords; - bool searchForward; idDecl * decl; int firstLine; + bool modified; + bool updatingControls; + bool controlsCreated; + + CFont modernFont; + CFont monoFont; + CBrush bgBrush; + CBrush panelBrush; + CBrush editBrush; + CBrush staticBrush; + + idList properties; private: + void CreateChildControls( void ); + void ApplyDarkTheme( void ); + void ApplyFontRecursive( CWnd *wnd ); + void SetControlsEnabled( bool enabled ); + void SetModified( bool isModified ); + void LoadBodyText( const char *declText ); + void GetBodyText( idStr &bodyText ) const; + void SetBodyText( const idStr &bodyText ); + void BuildDeclText( idStr &declText ) const; + void SyncPropertiesFromBody( void ); + void RefreshPropertyList( void ); + void SelectProperty( int listIndex ); + int FindProperty( const char *key ) const; + bool UpsertPropertyInBody( const char *oldKey, const char *newKey, const char *value ); + bool RemovePropertyFromBody( const char *key ); + bool GetEditedDeclName( idStr &name ) const; bool TestDecl( const idStr &declText ); + bool SaveDeclText( void ); void UpdateStatusBar( void ); }; diff --git a/neo/engine/tools/decl/DialogDeclNew.cpp b/neo/engine/tools/decl/DialogDeclNew.cpp index f7c829a1..16c9c82f 100644 --- a/neo/engine/tools/decl/DialogDeclNew.cpp +++ b/neo/engine/tools/decl/DialogDeclNew.cpp @@ -29,8 +29,9 @@ If you have questions concerning this license or the applicable additional terms #include "precompiled.h" #pragma hdrstop +#include "../radiant/qe3.h" #include "../../sys/win32/rc/DeclEditor_resource.h" - +#include "../radiant/qe3.h" #include "../comafx/CPathTreeCtrl.h" #include "DialogDeclBrowser.h" #include "DialogDeclNew.h" @@ -203,7 +204,7 @@ void DialogDeclNew::OnBnClickedFile() { case DECL_TABLE: folder = "materials"; ext = "(*.mtr)|*.mtr|(*.*)|*.*||"; break; case DECL_MATERIAL: folder = "materials"; ext = "(*.mtr)|*.mtr|(*.*)|*.*||"; break; case DECL_SKIN: folder = "skins"; ext = "(*.skin)|*.skin|(*.*)|*.*||"; break; - case DECL_SOUND: folder = "sound"; ext = "(*.sndshd|*.sndshd|(*.*)|*.*||"; break; + case DECL_SOUND: folder = "sound"; ext = "(*.sndshd)|*.sndshd|(*.*)|*.*||"; break; case DECL_ENTITYDEF: folder = "def"; ext = "(*.def)|*.def|(*.decl)|*.decl|(*.*)|*.*||"; break; case DECL_MODELDEF: folder = "def"; ext = "(*.def)|*.def|(*.*)|*.*||"; break; case DECL_FX: folder = "fx"; ext = "(*.fx)|*.fx|(*.*)|*.*||"; break; diff --git a/neo/engine/tools/decl/DialogEntityDefEditor.cpp b/neo/engine/tools/decl/DialogEntityDefEditor.cpp index aef4b7a2..2bef9181 100644 --- a/neo/engine/tools/decl/DialogEntityDefEditor.cpp +++ b/neo/engine/tools/decl/DialogEntityDefEditor.cpp @@ -63,6 +63,7 @@ DialogEntityDefEditor::DialogEntityDefEditor */ DialogEntityDefEditor::DialogEntityDefEditor( CWnd* pParent /*=NULL*/ ) : CDialog(DialogEntityDefEditor::IDD, pParent) + , m_hAccel(NULL) , decl(NULL) , firstLine(0) { @@ -212,8 +213,11 @@ void DialogEntityDefEditor::LoadDecl( idDeclEntityDef *decl ) { DialogEntityDefEditor::PopulateLists ================= */ -void DialogEntityDefEditor::PopulateLists( const char *declText, const int textLength ) +void DialogEntityDefEditor::PopulateLists( const char *declText, int textLength ) { + if ( textLength < 0 ) { + textLength = declText ? idStr::Length( declText ) : 0; + } idLexer src; idToken token, token2; @@ -798,7 +802,7 @@ void DialogEntityDefEditor::OnBnClickedAdd() { if (pItem) { // Items with a * in front are inherited and shouldn't be written out if (pItem->m_propName[0] == '*') { - if (newKey = pItem->m_propName.Mid(1)) { + if (newKey == pItem->m_propName.Mid(1)) { matchedInherit = i; } } @@ -839,7 +843,7 @@ void DialogEntityDefEditor::OnBnClickedDelete() { if (pItem) { // Items with a * in front are inherited and shouldn't be written out if (pItem->m_propName[0] == '*') { - if (delKey = pItem->m_propName.Mid(1)) { + if (delKey == pItem->m_propName.Mid(1)) { matchedInherit = i; } } @@ -854,7 +858,7 @@ void DialogEntityDefEditor::OnBnClickedDelete() { delete keyValsList.GetItemDataPtr(matchedKey); keyValsList.DeleteString(matchedKey); } - else if (matchedInherit) { + else if (matchedInherit >= 0) { MessageBox("Cannot delete an inherited value"); } } diff --git a/neo/engine/tools/decl/DialogEntityDefEditor.h b/neo/engine/tools/decl/DialogEntityDefEditor.h index af613608..e500d0bb 100644 --- a/neo/engine/tools/decl/DialogEntityDefEditor.h +++ b/neo/engine/tools/decl/DialogEntityDefEditor.h @@ -107,7 +107,7 @@ private: int firstLine; private: - void PopulateLists(idStr &declText); + void PopulateLists( const char *declText, int textLength = -1 ); void SetInherit(idStr &inherit); void BuildDeclText(idStr &declText); bool TestDecl( const idStr &declText ); diff --git a/neo/engine/tools/radiant/InspectorDialog.cpp b/neo/engine/tools/radiant/InspectorDialog.cpp index 9efbf970..8746e3d2 100644 --- a/neo/engine/tools/radiant/InspectorDialog.cpp +++ b/neo/engine/tools/radiant/InspectorDialog.cpp @@ -17,6 +17,7 @@ Copyright (C) 2026 Justin Marshall #include "InspectorDialog.h" #include "OutlinerDlg.h" #include "TabsDlg.h" +#include "../../sys/win32/rc/DeclEditor_resource.h" #include CInspectorDialog* g_Inspectors = NULL; @@ -379,7 +380,7 @@ void InspectorsDockingCallback(bool docked, int ID, CWnd* wnd) { CInspectorDialog::CInspectorDialog(CWnd* pParent /*=NULL*/) : CTabsDlg(CInspectorDialog::IDD, pParent) { initialized = false; - dockedTabs = W_CONSOLE | W_TEXTURE | W_MEDIA | W_OUTLINER; + dockedTabs = W_CONSOLE | W_TEXTURE | W_MEDIA | W_OUTLINER | W_DECLS; } CInspectorDialog::~CInspectorDialog() { @@ -421,14 +422,17 @@ BOOL CInspectorDialog::OnInitDialog() { mediaDlg.Create(IDD_DIALOG_TEXTURELIST, this); entityDlg.Create(IDD_DIALOG_ENTITY, this); outlinerDlg.Create(this); + declBrowserDlg.Create(this); - dockedTabs = GetCvarInt("radiant_InspectorDockedDialogs", W_CONSOLE | W_TEXTURE | W_MEDIA | W_OUTLINER); + dockedTabs = GetCvarInt("radiant_InspectorDockedDialogs", W_CONSOLE | W_TEXTURE | W_MEDIA | W_OUTLINER | W_DECLS); + dockedTabs |= W_DECLS; AddDockedWindow(&consoleWnd, W_CONSOLE, 1, "Console", (dockedTabs & W_CONSOLE) != 0, InspectorsDockingCallback); AddDockedWindow(&texWnd, W_TEXTURE, 2, "Textures", (dockedTabs & W_TEXTURE) != 0, InspectorsDockingCallback); AddDockedWindow(&mediaDlg, W_MEDIA, 3, "Media", (dockedTabs & W_MEDIA) != 0, InspectorsDockingCallback); AddDockedWindow(&entityDlg, W_ENTITY, 4, "Entity", (dockedTabs & W_ENTITY) != 0, InspectorsDockingCallback); AddDockedWindow(&outlinerDlg, W_OUTLINER, 5, "Outliner", (dockedTabs & W_OUTLINER) != 0, InspectorsDockingCallback); + AddDockedWindow(&declBrowserDlg, W_DECLS, 6, "Decls", (dockedTabs & W_DECLS) != 0, InspectorsDockingCallback); ApplyModernTheme(); @@ -485,11 +489,13 @@ void CInspectorDialog::ApplyModernTheme() { ApplyModernFontRecursive(&mediaDlg); ApplyModernFontRecursive(&entityDlg); ApplyModernFontRecursive(&outlinerDlg); + ApplyModernFontRecursive(&declBrowserDlg); InspectorDark_ApplyToDialog(&consoleWnd); InspectorDark_ApplyToDialog(&mediaDlg); InspectorDark_ApplyNativeTheme(entityDlg.GetSafeHwnd()); InspectorDark_ApplyToDialog(&outlinerDlg); + InspectorDark_ApplyToDialog(&declBrowserDlg); ModifyStyleEx(WS_EX_CLIENTEDGE, 0, SWP_FRAMECHANGED); @@ -694,9 +700,30 @@ void CInspectorDialog::OnClose() { CTabsDlg::OnClose(); } +static bool InspectorIsTextInputClass(HWND hWnd) { + if (!hWnd) { + return false; + } + char className[128]; + className[0] = '\0'; + ::GetClassNameA(hWnd, className, sizeof(className)); + return ::lstrcmpiA(className, "Edit") == 0 || + ::lstrcmpiA(className, "ComboBox") == 0 || + ::lstrcmpiA(className, "ComboLBox") == 0 || + ::lstrcmpiA(className, "SysTreeView32") == 0 || + ::lstrcmpiA(className, "SysListView32") == 0; +} + BOOL CInspectorDialog::PreTranslateMessage(MSG* pMsg) { if (pMsg->message == WM_KEYDOWN || pMsg->message == WM_KEYUP) { - g_pParentWnd->PostMessage(pMsg->message, pMsg->wParam, pMsg->lParam); + HWND focus = ::GetFocus(); + const bool inDecls = declBrowserDlg.GetSafeHwnd() && + (focus == declBrowserDlg.GetSafeHwnd() || ::IsChild(declBrowserDlg.GetSafeHwnd(), focus)); + const bool editingText = InspectorIsTextInputClass(focus); + + if (!inDecls && !editingText && g_pParentWnd) { + g_pParentWnd->PostMessage(pMsg->message, pMsg->wParam, pMsg->lParam); + } } return CTabsDlg::PreTranslateMessage(pMsg); diff --git a/neo/engine/tools/radiant/InspectorDialog.h b/neo/engine/tools/radiant/InspectorDialog.h index fa8765d9..4b7570f0 100644 --- a/neo/engine/tools/radiant/InspectorDialog.h +++ b/neo/engine/tools/radiant/InspectorDialog.h @@ -32,6 +32,11 @@ If you have questions concerning this license or the applicable additional terms #include "ConsoleDlg.h" #include "TabsDlg.h" #include "OutlinerDlg.h" +#include "../decl/DialogDeclBrowser.h" + +#ifndef W_DECLS +#define W_DECLS 0x0020 +#endif // CInspectorDialog dialog @@ -75,6 +80,8 @@ public: CDialogTextures mediaDlg; CEntityDlg entityDlg; COutlinerDlg outlinerDlg; + DialogDeclBrowser declBrowserDlg; + DialogDeclBrowser &GetDeclBrowser() { return declBrowserDlg; } void SetMode(int mode, bool updateTabs = true); void UpdateEntitySel(eclass_t *ent); void UpdateSelectedEntity(); diff --git a/neo/engine/tools/sound/DialogSound.cpp b/neo/engine/tools/sound/DialogSound.cpp index 8e56fca4..51ddc5a8 100644 --- a/neo/engine/tools/sound/DialogSound.cpp +++ b/neo/engine/tools/sound/DialogSound.cpp @@ -665,17 +665,17 @@ void CDialogSound::OnCheckPlay() void CDialogSound::OnBtnEdit() { - const idDecl *decl = declManager->FindDeclWithoutParsing( DECL_SOUND, strShader, false ); - - if ( decl ) { - DialogDeclEditor *declEditor; - - declEditor = new DialogDeclEditor; - declEditor->Create( IDD_DIALOG_DECLEDITOR, GetParent() ); - declEditor->LoadDecl( const_cast( decl ) ); - declEditor->ShowWindow( SW_SHOW ); - declEditor->SetFocus(); - } + //const idDecl *decl = declManager->FindDeclWithoutParsing( DECL_SOUND, strShader, false ); + // + //if ( decl ) { + // DialogDeclEditor *declEditor; + // + // declEditor = new DialogDeclEditor; + // declEditor->Create( IDD_DIALOG_DECLEDITOR, GetParent() ); + // declEditor->LoadDecl( const_cast( decl ) ); + // declEditor->ShowWindow( SW_SHOW ); + // declEditor->SetFocus(); + //} } void CDialogSound::OnBtnDrop()