]> icculus.org git repositories - icculus/iodoom3.git/blob - neo/tools/decl/DialogDeclEditor.cpp
hello world
[icculus/iodoom3.git] / neo / tools / decl / DialogDeclEditor.cpp
1 /*
2 ===========================================================================
3
4 Doom 3 GPL Source Code
5 Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. 
6
7 This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).  
8
9 Doom 3 Source Code is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
13
14 Doom 3 Source Code is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 GNU General Public License for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with Doom 3 Source Code.  If not, see <http://www.gnu.org/licenses/>.
21
22 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.
23
24 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.
25
26 ===========================================================================
27 */
28
29 #include "../../idlib/precompiled.h"
30 #pragma hdrstop
31
32 #include "../../sys/win32/rc/Common_resource.h"
33 #include "../../sys/win32/rc/DeclEditor_resource.h"
34
35 #include "../comafx/DialogGoToLine.h"
36 #include "../comafx/CPathTreeCtrl.h"
37 #include "DialogDeclBrowser.h"
38 #include "DialogDeclEditor.h"
39
40 #ifdef ID_DEBUG_MEMORY
41 #undef new
42 #undef DEBUG_NEW
43 #define DEBUG_NEW new
44 #endif
45
46
47 // DialogDeclEditor dialog
48
49 static UINT FindDialogMessage = ::RegisterWindowMessage( FINDMSGSTRING );
50
51 toolTip_t DialogDeclEditor::toolTips[] = {
52         { IDC_DECLEDITOR_BUTTON_TEST, "test decl" },
53         { IDOK, "save decl" },
54         { IDCANCEL, "cancel" },
55         { 0, NULL }
56 };
57
58
59 IMPLEMENT_DYNAMIC(DialogDeclEditor, CDialog)
60
61 /*
62 ================
63 DialogDeclEditor::DialogDeclEditor
64 ================
65 */
66 DialogDeclEditor::DialogDeclEditor( CWnd* pParent /*=NULL*/ )
67         : CDialog(DialogDeclEditor::IDD, pParent)
68         , findDlg(NULL)
69         , matchCase(false)
70         , matchWholeWords(false)
71         , decl(NULL)
72         , firstLine(0)
73 {
74 }
75
76 /*
77 ================
78 DialogDeclEditor::~DialogDeclEditor
79 ================
80 */
81 DialogDeclEditor::~DialogDeclEditor() {
82 }
83
84 /*
85 ================
86 DialogDeclEditor::DoDataExchange
87 ================
88 */
89 void DialogDeclEditor::DoDataExchange(CDataExchange* pDX) {
90         CDialog::DoDataExchange(pDX);
91         //{{AFX_DATA_MAP(DialogDeclEditor)
92         DDX_Control(pDX, IDC_DECLEDITOR_EDIT_TEXT, declEdit);
93         DDX_Control(pDX, IDC_DECLEDITOR_BUTTON_TEST, testButton);
94         DDX_Control(pDX, IDOK, okButton);
95         DDX_Control(pDX, IDCANCEL, cancelButton);
96         //}}AFX_DATA_MAP
97 }
98
99 /*
100 ================
101 DialogDeclEditor::PreTranslateMessage
102 ================
103 */
104 BOOL DialogDeclEditor::PreTranslateMessage( MSG* pMsg ) {
105         if ( WM_KEYFIRST <= pMsg->message && pMsg->message <= WM_KEYLAST ) {
106                 if ( m_hAccel && ::TranslateAccelerator( m_hWnd, m_hAccel, pMsg ) ) {
107                         return TRUE;
108                 }
109         }
110         return CWnd::PreTranslateMessage(pMsg);
111 }
112
113 /*
114 ================
115 DialogDeclEditor::TestDecl
116 ================
117 */
118 bool DialogDeclEditor::TestDecl( const idStr &declText ) {
119         idLexer src( LEXFL_NOSTRINGCONCAT );
120         idToken token;
121         int indent;
122
123         src.LoadMemory( declText, declText.Length(), "decl text" );
124
125         indent = 0;
126         while( src.ReadToken( &token ) ) {
127                 if ( token == "{" ) {
128                         indent++;
129                 } else if ( token == "}" ) {
130                         indent--;
131                 }
132         }
133
134         if ( indent < 0 ) {
135                 MessageBox( "Missing opening brace!", va( "Error saving %s", decl->GetFileName() ), MB_OK | MB_ICONERROR );
136                 return false;
137         }
138         if ( indent > 0 ) {
139                 MessageBox( "Missing closing brace!", va( "Error saving %s", decl->GetFileName() ), MB_OK | MB_ICONERROR );
140                 return false;
141         }
142         return true;
143 }
144
145 /*
146 ================
147 DialogDeclEditor::UpdateStatusBar
148 ================
149 */
150 void DialogDeclEditor::UpdateStatusBar( void ) {
151         int line, column, character;
152
153         if ( decl ) {
154                 declEdit.GetCursorPos( line, column, character );
155                 statusBar.SetWindowText( va( "Line: %d, Column: %d, Character: %d", decl->GetLineNum() + line, column, character ) );
156         }
157 }
158
159 /*
160 ================
161 DialogDeclEditor::LoadDecl
162 ================
163 */
164 void DialogDeclEditor::LoadDecl( idDecl *decl ) {
165         int numLines = 0;
166         int numCharsPerLine = 0;
167         int maxCharsPerLine = 0;
168         idStr declText;
169         CRect rect;
170
171         this->decl = decl;
172
173         switch( decl->GetType() ) {
174                 case DECL_ENTITYDEF:
175                         declEdit.SetStringColor( SRE_COLOR_BLUE, SRE_COLOR_DARK_CYAN );
176                         declEdit.LoadKeyWordsFromFile( "editors/entity.def" );
177                         break;
178                 case DECL_MATERIAL:
179                         declEdit.LoadKeyWordsFromFile( "editors/material.def" );
180                         break;
181                 case DECL_SKIN:
182                         declEdit.LoadKeyWordsFromFile( "editors/skin.def" );
183                         break;
184                 case DECL_SOUND:
185                         declEdit.LoadKeyWordsFromFile( "editors/sound.def" );
186                         break;
187                 case DECL_FX:
188                         declEdit.LoadKeyWordsFromFile( "editors/fx.def" );
189                         break;
190                 case DECL_PARTICLE:
191                         declEdit.LoadKeyWordsFromFile( "editors/particle.def" );
192                         break;
193                 case DECL_AF:
194                         declEdit.LoadKeyWordsFromFile( "editors/af.def" );
195                         break;
196                 case DECL_TABLE:
197                         declEdit.LoadKeyWordsFromFile( "editors/table.def" );
198                         break;
199                 case DECL_MODELDEF:
200                         declEdit.LoadKeyWordsFromFile( "editors/model.def" );
201                         break;
202                 default:
203                         declEdit.LoadKeyWordsFromFile( va( "editors/%s.def", declManager->GetDeclNameFromType( decl->GetType() ) ) );
204                         break;
205         }
206
207         firstLine = decl->GetLineNum();
208
209         char *localDeclText = (char *)_alloca( ( decl->GetTextLength() + 1 ) * sizeof( char ) );
210         decl->GetText( localDeclText );
211         declText = localDeclText;
212
213         // clean up new-line crapola
214         declText.Replace( "\r", "" );
215         declText.Replace( "\n", "\r" );
216         declText.Replace( "\v", "\r" );
217         declText.StripLeading( '\r' );
218         declText.Append( "\r" );
219
220         declEdit.SetText( declText );
221
222         for( const char *ptr = declText.c_str(); *ptr; ptr++ ) {
223                 if ( *ptr == '\r' ) {
224                         if ( numCharsPerLine > maxCharsPerLine ) {
225                                 maxCharsPerLine = numCharsPerLine;
226                         }
227                         numCharsPerLine = 0;
228                         numLines++;
229                 } else if ( *ptr == '\t' ) {
230                         numCharsPerLine += TAB_SIZE;
231                 } else {
232                         numCharsPerLine++;
233                 }
234         }
235
236         SetWindowText( va( "Declaration Editor (%s, line %d)", decl->GetFileName(), decl->GetLineNum() ) );
237
238         rect.left = initialRect.left;
239         rect.right = rect.left + maxCharsPerLine * FONT_WIDTH + 32;
240         rect.top = initialRect.top;
241         rect.bottom = rect.top + numLines * (FONT_HEIGHT+8) + 24 + 56;
242         if ( rect.right < initialRect.right ) {
243                 rect.right = initialRect.right;
244         } else if ( rect.right - rect.left > 1024 ) {
245                 rect.right = rect.left + 1024;
246         }
247         if ( rect.bottom < initialRect.bottom ) {
248                 rect.bottom = initialRect.bottom;
249         } else if ( rect.bottom - rect.top > 768 ) {
250                 rect.bottom = rect.top + 768;
251         }
252         MoveWindow( rect );
253
254         testButton.EnableWindow( FALSE );
255         okButton.EnableWindow( FALSE );
256
257         UpdateStatusBar();
258
259         declEdit.SetFocus();
260 }
261
262 /*
263 ================
264 DialogDeclEditor::OnInitDialog
265 ================
266 */
267 BOOL DialogDeclEditor::OnInitDialog()  {
268
269         com_editors |= EDITOR_DECL;
270
271         CDialog::OnInitDialog();
272
273         // load accelerator table
274         m_hAccel = ::LoadAccelerators( AfxGetResourceHandle(), MAKEINTRESOURCE( IDR_ACCELERATOR_DECLEDITOR ) );
275
276         // create status bar
277         statusBar.CreateEx( SBARS_SIZEGRIP, WS_CHILD | WS_VISIBLE | CBRS_BOTTOM, initialRect, this, AFX_IDW_STATUS_BAR );
278
279         declEdit.Init();
280
281         GetClientRect( initialRect );
282
283         EnableToolTips( TRUE );
284
285         testButton.EnableWindow( FALSE );
286         okButton.EnableWindow( FALSE );
287
288         UpdateStatusBar();
289
290         return FALSE; // return TRUE unless you set the focus to a control
291                       // EXCEPTION: OCX Property Pages should return FALSE
292 }
293
294
295 BEGIN_MESSAGE_MAP(DialogDeclEditor, CDialog)
296         ON_NOTIFY_EX_RANGE(TTN_NEEDTEXTW, 0, 0xFFFF, OnToolTipNotify)
297         ON_NOTIFY_EX_RANGE(TTN_NEEDTEXTA, 0, 0xFFFF, OnToolTipNotify)
298         ON_WM_DESTROY()
299         ON_WM_ACTIVATE()
300         ON_WM_MOVE()
301         ON_WM_SIZE()
302         ON_WM_SIZING()
303         ON_WM_SETFOCUS()
304         ON_COMMAND(ID_EDIT_FIND, OnEditFind)
305         ON_COMMAND(ID_EDIT_REPLACE, OnEditReplace)
306         ON_COMMAND(ID_DECLEDITOR_FIND_NEXT, OnEditFindNext)
307         ON_COMMAND(ID_DECLEDITOR_GOTOLINE, OnEditGoToLine)
308         ON_REGISTERED_MESSAGE(FindDialogMessage, OnFindDialogMessage)
309         ON_NOTIFY(EN_CHANGE, IDC_DECLEDITOR_EDIT_TEXT, OnEnChangeEdit)
310         ON_NOTIFY(EN_MSGFILTER, IDC_DECLEDITOR_EDIT_TEXT, OnEnInputEdit)
311         ON_BN_CLICKED(IDC_DECLEDITOR_BUTTON_TEST, OnBnClickedTest)
312         ON_BN_CLICKED(IDOK, OnBnClickedOk)
313         ON_BN_CLICKED(IDCANCEL, OnBnClickedCancel)
314 END_MESSAGE_MAP()
315
316
317 // DialogDeclEditor message handlers
318
319 /*
320 ================
321 DialogDeclEditor::OnActivate
322 ================
323 */
324 void DialogDeclEditor::OnActivate( UINT nState, CWnd *pWndOther, BOOL bMinimized ) {
325         CDialog::OnActivate( nState, pWndOther, bMinimized );
326 }
327
328 /*
329 ================
330 DialogDeclEditor::OnToolTipNotify
331 ================
332 */
333 BOOL DialogDeclEditor::OnToolTipNotify( UINT id, NMHDR *pNMHDR, LRESULT *pResult ) {
334         return DefaultOnToolTipNotify( toolTips, id, pNMHDR, pResult );
335 }
336
337 /*
338 ================
339 DialogDeclEditor::OnSetFocus
340 ================
341 */
342 void DialogDeclEditor::OnSetFocus( CWnd *pOldWnd ) {
343         CDialog::OnSetFocus( pOldWnd );
344 }
345
346 /*
347 ================
348 DialogDeclEditor::OnDestroy
349 ================
350 */
351 void DialogDeclEditor::OnDestroy() {
352         return CDialog::OnDestroy();
353 }
354
355 /*
356 ================
357 DialogDeclEditor::OnMove
358 ================
359 */
360 void DialogDeclEditor::OnMove( int x, int y ) {
361         if ( GetSafeHwnd() ) {
362                 CRect rct;
363                 GetWindowRect( rct );
364                 // FIXME: save position
365         }
366         CDialog::OnMove( x, y );
367 }
368
369 /*
370 ================
371 DialogDeclEditor::OnSize
372 ================
373 */
374 #define BORDER_SIZE                     0
375 #define BUTTON_SPACE            4
376 #define TOOLBAR_HEIGHT          24
377
378 void DialogDeclEditor::OnSize( UINT nType, int cx, int cy ) {
379         CRect clientRect, rect;
380
381         LockWindowUpdate();
382
383         CDialog::OnSize( nType, cx, cy );
384
385         GetClientRect( clientRect );
386
387         if ( declEdit.GetSafeHwnd() ) {
388                 rect.left = BORDER_SIZE;
389                 rect.top = BORDER_SIZE;
390                 rect.right = clientRect.Width() - BORDER_SIZE;
391                 rect.bottom = clientRect.Height() - 56;
392                 declEdit.MoveWindow( rect.left, rect.top, rect.Width(), rect.Height() );
393         }
394
395         if ( testButton.GetSafeHwnd() ) {
396                 testButton.GetClientRect( rect );
397                 int width = rect.Width();
398                 int height = rect.Height();
399                 rect.left = BORDER_SIZE;
400                 rect.top = clientRect.Height() - TOOLBAR_HEIGHT - height;
401                 rect.right = BORDER_SIZE + width;
402                 rect.bottom = clientRect.Height() - TOOLBAR_HEIGHT;
403                 testButton.MoveWindow( rect.left, rect.top, rect.Width(), rect.Height() );
404         }
405
406         if ( okButton.GetSafeHwnd() ) {
407                 okButton.GetClientRect( rect );
408                 int width = rect.Width();
409                 int height = rect.Height();
410                 rect.left = clientRect.Width() - BORDER_SIZE - BUTTON_SPACE - 2 * width;
411                 rect.top = clientRect.Height() - TOOLBAR_HEIGHT - height;
412                 rect.right = clientRect.Width() - BORDER_SIZE - BUTTON_SPACE - width;
413                 rect.bottom = clientRect.Height() - TOOLBAR_HEIGHT;
414                 okButton.MoveWindow( rect.left, rect.top, rect.Width(), rect.Height() );
415         }
416
417         if ( cancelButton.GetSafeHwnd() ) {
418                 cancelButton.GetClientRect( rect );
419                 int width = rect.Width();
420                 int height = rect.Height();
421                 rect.left = clientRect.Width() - BORDER_SIZE - width;
422                 rect.top = clientRect.Height() - TOOLBAR_HEIGHT - height;
423                 rect.right = clientRect.Width() - BORDER_SIZE;
424                 rect.bottom = clientRect.Height() - TOOLBAR_HEIGHT;
425                 cancelButton.MoveWindow( rect.left, rect.top, rect.Width(), rect.Height() );
426         }
427
428         if ( statusBar.GetSafeHwnd() ) {
429                 rect.left = clientRect.Width() - 2;
430                 rect.top = clientRect.Height() - 2;
431                 rect.right = clientRect.Width() - 2;
432                 rect.bottom = clientRect.Height() - 2;
433                 statusBar.MoveWindow( rect.left, rect.top, rect.Width(), rect.Height() );
434         }
435
436         UnlockWindowUpdate();
437 }
438
439 /*
440 ================
441 DialogDeclEditor::OnSizing
442 ================
443 */
444 void DialogDeclEditor::OnSizing( UINT nSide, LPRECT lpRect ) {
445         /*
446                 1 = left
447                 2 = right
448                 3 = top
449                 4 = left - top
450                 5 = right - top
451                 6 = bottom
452                 7 = left - bottom
453                 8 = right - bottom
454         */
455
456         CDialog::OnSizing( nSide, lpRect );
457
458         if ( ( nSide - 1 ) % 3 == 0 ) {
459                 if ( lpRect->right - lpRect->left < initialRect.Width() ) {
460                         lpRect->left = lpRect->right - initialRect.Width();
461                 }
462         } else if ( ( nSide - 2 ) % 3 == 0 ) {
463                 if ( lpRect->right - lpRect->left < initialRect.Width() ) {
464                         lpRect->right = lpRect->left + initialRect.Width();
465                 }
466         }
467         if ( nSide >= 3 && nSide <= 5 ) {
468                 if ( lpRect->bottom - lpRect->top < initialRect.Height() ) {
469                         lpRect->top = lpRect->bottom - initialRect.Height();
470                 }
471         } else if ( nSide >= 6 && nSide <= 9 ) {
472                 if ( lpRect->bottom - lpRect->top < initialRect.Height() ) {
473                         lpRect->bottom = lpRect->top + initialRect.Height();
474                 }
475         }
476 }
477
478 /*
479 ================
480 DialogDeclEditor::OnEditGoToLine
481 ================
482 */
483 void DialogDeclEditor::OnEditGoToLine() {
484         DialogGoToLine goToLineDlg;
485
486         goToLineDlg.SetRange( firstLine, firstLine + declEdit.GetLineCount() - 1 );
487         if ( goToLineDlg.DoModal() != IDOK ) {
488                 return;
489         }
490         declEdit.GoToLine( goToLineDlg.GetLine() - firstLine );
491 }
492
493 /*
494 ================
495 DialogDeclEditor::OnEditFind
496 ================
497 */
498 void DialogDeclEditor::OnEditFind() {
499
500         CString selText = declEdit.GetSelText();
501         if ( selText.GetLength() ) {
502                 findStr = selText;
503         }
504
505         if ( !findDlg ) {
506                 // create find/replace dialog
507                 findDlg = new CFindReplaceDialog();  // Must be created on the heap
508                 findDlg->Create( TRUE, findStr, "", FR_DOWN, this );
509         }
510 }
511
512 /*
513 ================
514 DialogDeclEditor::OnEditFindNext
515 ================
516 */
517 void DialogDeclEditor::OnEditFindNext() {
518         if ( declEdit.FindNext( findStr, matchCase, matchWholeWords, searchForward ) ) {
519                 declEdit.SetFocus();
520         } else {
521                 AfxMessageBox( "The specified text was not found.", MB_OK | MB_ICONINFORMATION, 0 );
522         }
523 }
524
525 /*
526 ================
527 DialogDeclEditor::OnEditReplace
528 ================
529 */
530 void DialogDeclEditor::OnEditReplace() {
531
532         CString selText = declEdit.GetSelText();
533         if ( selText.GetLength() ) {
534                 findStr = selText;
535         }
536
537         // create find/replace dialog
538         if ( !findDlg ) {
539                 findDlg = new CFindReplaceDialog();  // Must be created on the heap
540                 findDlg->Create( FALSE, findStr, "", FR_DOWN, this );
541         }
542 }
543
544 /*
545 ================
546 DialogDeclEditor::OnFindDialogMessage
547 ================
548 */
549 LRESULT DialogDeclEditor::OnFindDialogMessage( WPARAM wParam, LPARAM lParam ) {
550         if ( findDlg == NULL ) {
551                 return 0;
552         }
553
554         if ( findDlg->IsTerminating() ) {
555         findDlg = NULL;
556         return 0;
557     }
558
559         if( findDlg->FindNext() ) {
560                 findStr = findDlg->GetFindString();
561                 matchCase = findDlg->MatchCase() != FALSE;
562                 matchWholeWords = findDlg->MatchWholeWord() != FALSE;
563                 searchForward = findDlg->SearchDown() != FALSE;
564
565                 OnEditFindNext();
566     }
567
568         if ( findDlg->ReplaceCurrent() ) {
569                 long selStart, selEnd;
570
571                 replaceStr = findDlg->GetReplaceString();
572
573                 declEdit.GetSel( selStart, selEnd );
574                 if ( selEnd > selStart ) {
575                         declEdit.ReplaceSel( replaceStr, TRUE );
576                 }
577         }
578
579         if ( findDlg->ReplaceAll() ) {
580                 replaceStr = findDlg->GetReplaceString();
581                 findStr = findDlg->GetFindString();
582                 matchCase = findDlg->MatchCase() != FALSE;
583                 matchWholeWords = findDlg->MatchWholeWord() != FALSE;
584
585                 int numReplaces = declEdit.ReplaceAll( findStr, replaceStr, matchCase, matchWholeWords );
586                 if ( numReplaces == 0 ) {
587                         AfxMessageBox( "The specified text was not found.", MB_OK | MB_ICONINFORMATION, 0 );
588                 } else {
589                         AfxMessageBox( va( "Replaced %d occurances.", numReplaces ), MB_OK | MB_ICONINFORMATION, 0 );
590                 }
591         }
592
593         return 0;
594 }
595
596 /*
597 ================
598 DialogDeclEditor::OnEnChangeEdit
599 ================
600 */
601 void DialogDeclEditor::OnEnChangeEdit( NMHDR *pNMHDR, LRESULT *pResult ) {
602         testButton.EnableWindow( TRUE );
603         okButton.EnableWindow( TRUE );
604 }
605
606 /*
607 ================
608 DialogDeclEditor::OnEnInputEdit
609 ================
610 */
611 void DialogDeclEditor::OnEnInputEdit( NMHDR *pNMHDR, LRESULT *pResult ) {
612         MSGFILTER *msgFilter = (MSGFILTER *)pNMHDR;
613
614         if ( msgFilter->msg != 512 && msgFilter->msg != 33 ) {
615                 UpdateStatusBar();
616         }
617
618         *pResult = 0;
619 }
620
621 /*
622 ================
623 DialogDeclEditor::OnBnClickedTest
624 ================
625 */
626 void DialogDeclEditor::OnBnClickedTest() {
627         idStr declText;
628
629         if ( decl ) {
630
631                 declEdit.GetText( declText );
632
633                 // clean up new-line crapola
634                 declText.Replace( "\n", "" );
635                 declText.Replace( "\r", "\r\n" );
636                 declText.Replace( "\v", "\r\n" );
637                 declText.StripLeading( "\r\n" );
638                 declText.Insert( "\r\n\r\n", 0 );
639                 declText.StripTrailing( "\r\n" );
640
641                 if ( !TestDecl( declText ) ) {
642                         return;
643                 }
644
645                 char *oldDeclText = (char *)_alloca( ( decl->GetTextLength() + 1 ) * sizeof( char ) );
646                 decl->GetText( oldDeclText );
647                 decl->SetText( declText );
648                 decl->Invalidate();
649                 declManager->DeclByIndex( decl->GetType(), decl->Index(), true );
650                 decl->SetText( oldDeclText );
651                 decl->Invalidate();
652                 common->Printf( "tested %s\n", decl->GetName() );
653
654                 testButton.EnableWindow( FALSE );
655         }
656 }
657
658 /*
659 ================
660 DialogDeclEditor::OnBnClickedOk
661 ================
662 */
663 void DialogDeclEditor::OnBnClickedOk() {
664         idStr declText;
665
666         if ( decl ) {
667
668                 declEdit.GetText( declText );
669
670                 // clean up new-line crapola
671                 declText.Replace( "\n", "" );
672                 declText.Replace( "\r", "\r\n" );
673                 declText.Replace( "\v", "\r\n" );
674                 declText.StripLeading( "\r\n" );
675                 declText.Insert( "\r\n\r\n", 0 );
676                 declText.StripTrailing( "\r\n" );
677
678                 if ( !TestDecl( declText ) ) {
679                         return;
680                 }
681
682                 if ( decl->SourceFileChanged() ) {
683                         if ( MessageBox( va( "Declaration file %s has been modified outside of the editor.\r\nReload declarations and save?", decl->GetFileName() ),
684                                                         va( "Warning saving: %s", decl->GetFileName() ), MB_OKCANCEL | MB_ICONERROR ) != IDOK ) {
685                                 return;
686                         }
687                         declManager->Reload( false );
688                         DeclBrowserReloadDeclarations();
689                 }
690
691                 decl->SetText( declText );
692                 if ( !decl->ReplaceSourceFileText() ) {
693                         MessageBox( va( "Couldn't save: %s.\r\nMake sure the declaration file is not read-only.", decl->GetFileName() ),
694                                                 va( "Error saving: %s", decl->GetFileName() ), MB_OK | MB_ICONERROR );
695                         return;
696                 }
697                 decl->Invalidate();
698         }
699
700         okButton.EnableWindow( FALSE );
701 }
702
703 /*
704 ================
705 DialogDeclEditor::OnBnClickedCancel
706 ================
707 */
708 void DialogDeclEditor::OnBnClickedCancel() {
709         if ( okButton.IsWindowEnabled() ) {
710                 if ( MessageBox( "Cancel changes?", "Cancel", MB_YESNO | MB_ICONQUESTION ) != IDYES ) {
711                         return;
712                 }
713         }
714         OnCancel();
715 }