/* =========================================================================== Doom 3 GPL Source Code Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code"). Doom 3 Source Code is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Doom 3 Source Code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Doom 3 Source Code. If not, see . In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. =========================================================================== */ #include "tools/edit_gui_common.h" #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 "DialogDeclEditor.h" #ifdef ID_DEBUG_MEMORY #undef new #undef DEBUG_NEW #define DEBUG_NEW new #endif // DialogDeclEditor dialog static UINT FindDialogMessage = ::RegisterWindowMessage( FINDMSGSTRING ); toolTip_t DialogDeclEditor::toolTips[] = { { IDC_DECLEDITOR_BUTTON_TEST, "test decl" }, { IDOK, "save decl" }, { IDCANCEL, "cancel" }, { 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 ================ */ DialogDeclEditor::~DialogDeclEditor() { } /* ================ 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 } /* ================ DialogDeclEditor::PreTranslateMessage ================ */ 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); } /* ================ DialogDeclEditor::TestDecl ================ */ bool DialogDeclEditor::TestDecl( const idStr &declText ) { idLexer src( LEXFL_NOSTRINGCONCAT ); idToken token; int indent; src.LoadMemory( declText, declText.Length(), "decl text" ); indent = 0; while( src.ReadToken( &token ) ) { if ( token == "{" ) { indent++; } else if ( token == "}" ) { indent--; } } if ( indent < 0 ) { MessageBox( "Missing opening brace!", va( "Error saving %s", decl->GetFileName() ), MB_OK | MB_ICONERROR ); return false; } if ( indent > 0 ) { MessageBox( "Missing closing brace!", va( "Error saving %s", decl->GetFileName() ), 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 ) ); } } /* ================ 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; } firstLine = decl->GetLineNum(); char *localDeclText = (char *)_alloca( ( decl->GetTextLength() + 1 ) * sizeof( char ) ); decl->GetText( localDeclText ); declText = localDeclText; // clean up new-line crapola declText.Replace( "\r", "" ); declText.Replace( "\n", "\r" ); declText.Replace( "\v", "\r" ); declText.StripLeading( '\r' ); declText.Append( "\r" ); 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++; } } SetWindowText( va( "Declaration Editor (%s, line %d)", decl->GetFileName(), decl->GetLineNum() ) ); float scaling_factor = Win_GetWindowScalingFactor(GetSafeHwnd()); rect.left = initialRect.left; rect.right = rect.left + (maxCharsPerLine * FONT_WIDTH + 32) *scaling_factor; rect.top = initialRect.top; rect.bottom = rect.top + (numLines * (FONT_HEIGHT+8) + 24 + 56)* scaling_factor; if ( rect.right < initialRect.right ) { rect.right = initialRect.right; } else if ( rect.right - rect.left > (1024 * scaling_factor) ) { rect.right = rect.left + (1024 * scaling_factor); } if ( rect.bottom < initialRect.bottom ) { rect.bottom = initialRect.bottom; } else if ( rect.bottom - rect.top > (768 * scaling_factor) ) { rect.bottom = rect.top + (768 * scaling_factor); } MoveWindow( rect ); testButton.EnableWindow( FALSE ); okButton.EnableWindow( FALSE ); UpdateStatusBar(); declEdit.SetFocus(); } /* ================ 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 ); float scaling_factor = Win_GetWindowScalingFactor(GetSafeHwnd()); float scaled_toolbar_height = (TOOLBAR_HEIGHT * scaling_factor); float scaled_button_space = (BUTTON_SPACE * scaling_factor); if ( declEdit.GetSafeHwnd() ) { rect.left = BORDER_SIZE; rect.top = BORDER_SIZE; rect.right = clientRect.Width() - BORDER_SIZE; rect.bottom = clientRect.Height() - (56 * scaling_factor); 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() - scaled_toolbar_height - height; rect.right = BORDER_SIZE + width; rect.bottom = clientRect.Height() - scaled_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 - scaled_button_space - 2 * width; rect.top = clientRect.Height() - scaled_toolbar_height - height; rect.right = clientRect.Width() - BORDER_SIZE - scaled_button_space - width; rect.bottom = clientRect.Height() - scaled_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() - scaled_toolbar_height - height; rect.right = clientRect.Width() - BORDER_SIZE; rect.bottom = clientRect.Height() - scaled_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 ) { return; } declEdit.GoToLine( goToLineDlg.GetLine() - firstLine ); } /* ================ 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 ); } } /* ================ 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 ); } } /* ================ DialogDeclEditor::OnEditReplace ================ */ void DialogDeclEditor::OnEditReplace() { CString selText = declEdit.GetSelText(); if ( selText.GetLength() ) { findStr = selText; } // 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::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" ); if ( !TestDecl( declText ) ) { return; } char *oldDeclText = (char *)_alloca( ( decl->GetTextLength() + 1 ) * sizeof( char ) ); decl->GetText( oldDeclText ); decl->SetText( declText ); decl->Invalidate(); declManager->DeclByIndex( decl->GetType(), decl->Index(), true ); decl->SetText( oldDeclText ); decl->Invalidate(); common->Printf( "tested %s\n", decl->GetName() ); 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 ); } /* ================ DialogDeclEditor::OnBnClickedCancel ================ */ void DialogDeclEditor::OnBnClickedCancel() { if ( okButton.IsWindowEnabled() ) { if ( MessageBox( "Cancel changes?", "Cancel", MB_YESNO | MB_ICONQUESTION ) != IDYES ) { return; } } OnCancel(); }