diff --git a/neo/engine/tools/radiant/ConsoleDlg.cpp b/neo/engine/tools/radiant/ConsoleDlg.cpp
index 10c743cb..86b14bd0 100644
--- a/neo/engine/tools/radiant/ConsoleDlg.cpp
+++ b/neo/engine/tools/radiant/ConsoleDlg.cpp
@@ -2,26 +2,9 @@
===========================================================================
IceTech GPL Source Code
-Copyright (C) 2026 Justin Marshall
+Copyright (C) 2026 Justin Marshall
-This file is part of the IceTech GPL Source Code (?IceTech Source Code?).
-
-IceTech Source Code is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
-
-IceTech Source Code is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with IceTech Source Code. If not, see .
-
-In addition, the IceTech Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the IceTech Source Code. If not, please request a copy in writing from id Software at the address below.
-
-If you have questions concerning this license or the applicable additional terms, you may contact in writing Justin Marshall, justinmarshall20@gmail.com
+This file is part of the IceTech GPL Source Code (?IceTech Source Code?).
===========================================================================
*/
@@ -37,52 +20,441 @@ If you have questions concerning this license or the applicable additional terms
// CConsoleDlg dialog
IMPLEMENT_DYNCREATE(CConsoleDlg, CDialog)
+
CConsoleDlg::CConsoleDlg(CWnd* pParent /*=NULL*/)
- : CDialog(CConsoleDlg::IDD)
+ : CDialog(CConsoleDlg::IDD, pParent)
{
- currentHistoryPosition = -1;
- currentCommand = "";
+ currentHistoryPosition = -1;
+ currentCommand = "";
saveCurrentCommand = true;
}
CConsoleDlg::~CConsoleDlg()
{
+ if (blackBrush.GetSafeHandle() != NULL) {
+ blackBrush.DeleteObject();
+ }
}
void CConsoleDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
- DDX_Control(pDX, IDC_EDIT_CONSOLE, editConsole);
+
+ // Do not DDX IDC_EDIT_CONSOLE here.
+ // The resource control can stay as a normal EDIT placeholder.
+ // OnInitDialog destroys it and creates the RichEdit in its place.
DDX_Control(pDX, IDC_EDIT_INPUT, editInput);
}
-void CConsoleDlg::AddText( const char *msg ) {
- idStr work;
- CString work2;
-
- work = msg;
- work.RemoveColors();
- work = CEntityDlg::TranslateString( work.c_str() );
- editConsole.GetWindowText( work2 );
- int len = work2.GetLength();
- if ( len + work.Length() > (int)editConsole.GetLimitText() ) {
- work2 = work2.Right( editConsole.GetLimitText() * 0.75 );
- len = work2.GetLength();
- editConsole.SetWindowText(work2);
- }
- editConsole.SetSel( len, len );
- editConsole.ReplaceSel( work );
-}
-
-
BEGIN_MESSAGE_MAP(CConsoleDlg, CDialog)
ON_WM_SIZE()
ON_WM_SETFOCUS()
ON_WM_ACTIVATE()
+ ON_WM_CTLCOLOR()
END_MESSAGE_MAP()
-// CConsoleDlg message handlers
+BOOL CConsoleDlg::OnInitDialog()
+{
+ CDialog::OnInitDialog();
+
+ // Needed before creating CRichEditCtrl.
+ // Safe if already initialized elsewhere.
+ AfxInitRichEdit2();
+
+ if (blackBrush.GetSafeHandle() == NULL) {
+ blackBrush.CreateSolidBrush(RGB(0, 0, 0));
+ }
+
+ CreateRichConsoleFromPlaceholder();
+ SetupRichConsoleDefaults();
+
+ if (editInput.GetSafeHwnd() != NULL) {
+ editInput.SetLimitText(1024);
+ editInput.SetWindowText("");
+ }
+
+ return TRUE;
+}
+
+void CConsoleDlg::CreateRichConsoleFromPlaceholder()
+{
+ CWnd* oldConsole = GetDlgItem(IDC_EDIT_CONSOLE);
+ if (oldConsole == NULL || oldConsole->GetSafeHwnd() == NULL) {
+ return;
+ }
+
+ CRect rc;
+ oldConsole->GetWindowRect(&rc);
+ ScreenToClient(&rc);
+
+ oldConsole->DestroyWindow();
+
+ DWORD style =
+ WS_CHILD |
+ WS_VISIBLE |
+ WS_TABSTOP |
+ WS_VSCROLL |
+ ES_MULTILINE |
+ ES_AUTOVSCROLL |
+ ES_READONLY |
+ ES_NOHIDESEL |
+ ES_SAVESEL;
+
+ // Older MFC CRichEditCtrl::Create only takes 4 args.
+ // Do not use CreateEx here.
+ editConsole.Create(
+ style,
+ rc,
+ this,
+ IDC_EDIT_CONSOLE
+ );
+}
+
+void CConsoleDlg::SetupRichConsoleDefaults()
+{
+ if (editConsole.GetSafeHwnd() == NULL) {
+ return;
+ }
+
+ editConsole.SetEventMask(0);
+ editConsole.SetOptions(ECOOP_OR, ECO_AUTOVSCROLL);
+ editConsole.SetReadOnly(TRUE);
+
+ // CRichEditCtrl uses LimitText(), not SetLimitText().
+ editConsole.LimitText(1024 * 1024);
+
+ editConsole.SetBackgroundColor(FALSE, RGB(0, 0, 0));
+
+ CHARFORMAT cf;
+ memset(&cf, 0, sizeof(cf));
+ cf.cbSize = sizeof(cf);
+ cf.dwMask = CFM_COLOR | CFM_FACE | CFM_SIZE;
+ cf.crTextColor = ICE_CONSOLE_DEFAULT_COLOR;
+ cf.yHeight = 180; // 9pt
+
+ lstrcpyn(cf.szFaceName, "Consolas", LF_FACESIZE);
+
+ editConsole.SetDefaultCharFormat(cf);
+
+ editConsole.SetSel(0, -1);
+ editConsole.SetSelectionCharFormat(cf);
+ editConsole.SetSel(-1, -1);
+}
+
+CString CConsoleDlg::NormalizeNewlines(const CString& in) const
+{
+ CString out;
+ out.Preallocate(in.GetLength() + 16);
+
+ for (int i = 0; i < in.GetLength(); i++) {
+ TCHAR c = in[i];
+
+ if (c == '\r') {
+ if (i + 1 < in.GetLength() && in[i + 1] == '\n') {
+ out += "\r\n";
+ i++;
+ }
+ else {
+ out += "\r\n";
+ }
+ }
+ else if (c == '\n') {
+ out += "\r\n";
+ }
+ else {
+ out += c;
+ }
+ }
+
+ return out;
+}
+
+COLORREF CConsoleDlg::DoomColorForIndex(char c) const
+{
+ switch (c) {
+ case '0':
+ return RGB(0, 0, 0); // black
+
+ case '1':
+ return RGB(255, 64, 64); // red
+
+ case '2':
+ return RGB(64, 255, 64); // green
+
+ case '3':
+ return RGB(255, 255, 64); // yellow
+
+ case '4':
+ return RGB(80, 140, 255); // blue
+
+ case '5':
+ return RGB(64, 255, 255); // cyan
+
+ case '6':
+ return RGB(255, 64, 255); // magenta
+
+ case '7':
+ return RGB(230, 230, 230); // white
+
+ case '8':
+ return RGB(160, 160, 160); // gray
+
+ case '9':
+ return RGB(255, 150, 32); // orange
+
+ case 'r':
+ case 'R':
+ return RGB(255, 64, 64);
+
+ case 'g':
+ case 'G':
+ return RGB(64, 255, 64);
+
+ case 'y':
+ case 'Y':
+ return RGB(255, 255, 64);
+
+ case 'b':
+ case 'B':
+ return RGB(80, 140, 255);
+
+ case 'c':
+ case 'C':
+ return RGB(64, 255, 255);
+
+ case 'm':
+ case 'M':
+ return RGB(255, 64, 255);
+
+ case 'w':
+ case 'W':
+ return RGB(230, 230, 230);
+
+ default:
+ return ICE_CONSOLE_DEFAULT_COLOR;
+ }
+}
+
+static bool ConsoleStartsWithNoCase(const char* s, const char* prefix)
+{
+ if (s == NULL || prefix == NULL) {
+ return false;
+ }
+
+ while (*prefix != '\0') {
+ char a = *s;
+ char b = *prefix;
+
+ if (a >= 'A' && a <= 'Z') {
+ a = a - 'A' + 'a';
+ }
+
+ if (b >= 'A' && b <= 'Z') {
+ b = b - 'A' + 'a';
+ }
+
+ if (a != b) {
+ return false;
+ }
+
+ s++;
+ prefix++;
+ }
+
+ return true;
+}
+
+COLORREF CConsoleDlg::DoomColorForName(const char* name, int& consumed) const
+{
+ consumed = 0;
+
+ struct colorName_t {
+ const char* name;
+ COLORREF color;
+ };
+
+ static const colorName_t names[] = {
+ { "S_COLOR_BLACK", RGB(0, 0, 0) },
+ { "S_COLOR_RED", RGB(255, 64, 64) },
+ { "S_COLOR_GREEN", RGB(64, 255, 64) },
+ { "S_COLOR_YELLOW", RGB(255, 255, 64) },
+ { "S_COLOR_BLUE", RGB(80, 140, 255) },
+ { "S_COLOR_CYAN", RGB(64, 255, 255) },
+ { "S_COLOR_MAGENTA", RGB(255, 64, 255) },
+ { "S_COLOR_WHITE", RGB(230, 230, 230) },
+ { "S_COLOR_GRAY", RGB(160, 160, 160) },
+ { "S_COLOR_GREY", RGB(160, 160, 160) },
+ { "S_COLOR_ORANGE", RGB(255, 150, 32) },
+
+ { "COLOR_BLACK", RGB(0, 0, 0) },
+ { "COLOR_RED", RGB(255, 64, 64) },
+ { "COLOR_GREEN", RGB(64, 255, 64) },
+ { "COLOR_YELLOW", RGB(255, 255, 64) },
+ { "COLOR_BLUE", RGB(80, 140, 255) },
+ { "COLOR_CYAN", RGB(64, 255, 255) },
+ { "COLOR_MAGENTA", RGB(255, 64, 255) },
+ { "COLOR_WHITE", RGB(230, 230, 230) },
+ { "COLOR_GRAY", RGB(160, 160, 160) },
+ { "COLOR_GREY", RGB(160, 160, 160) },
+ { "COLOR_ORANGE", RGB(255, 150, 32) }
+ };
+
+ for (int i = 0; i < sizeof(names) / sizeof(names[0]); i++) {
+ if (ConsoleStartsWithNoCase(name, names[i].name)) {
+ consumed = (int)strlen(names[i].name);
+ return names[i].color;
+ }
+ }
+
+ return ICE_CONSOLE_DEFAULT_COLOR;
+}
+
+bool CConsoleDlg::TryConsumeColorToken(const char* s, int& consumed, COLORREF& color) const
+{
+ consumed = 0;
+ color = ICE_CONSOLE_DEFAULT_COLOR;
+
+ if (s == NULL || s[0] == '\0') {
+ return false;
+ }
+
+ // Doom/idTech style color escape: ^1, ^2, ^7, etc.
+ if (s[0] == '^' && s[1] != '\0') {
+ // ^^ means literal caret, not a color.
+ if (s[1] == '^') {
+ return false;
+ }
+
+ color = DoomColorForIndex(s[1]);
+ consumed = 2;
+ return true;
+ }
+
+ // Optional support if some code emits literal token names.
+ int namedConsumed = 0;
+ COLORREF namedColor = DoomColorForName(s, namedConsumed);
+ if (namedConsumed > 0) {
+ color = namedColor;
+ consumed = namedConsumed;
+ return true;
+ }
+
+ return false;
+}
+
+void CConsoleDlg::AppendConsoleRun(const CString& text, COLORREF color)
+{
+ if (text.IsEmpty() || editConsole.GetSafeHwnd() == NULL) {
+ return;
+ }
+
+ CString normalized = NormalizeNewlines(text);
+
+ editConsole.SetReadOnly(FALSE);
+
+ int len = editConsole.GetTextLength();
+ editConsole.SetSel(len, len);
+
+ CHARFORMAT cf;
+ memset(&cf, 0, sizeof(cf));
+ cf.cbSize = sizeof(cf);
+ cf.dwMask = CFM_COLOR | CFM_FACE | CFM_SIZE;
+ cf.crTextColor = color;
+ cf.yHeight = 180;
+ lstrcpyn(cf.szFaceName, "Consolas", LF_FACESIZE);
+
+ editConsole.SetSelectionCharFormat(cf);
+ editConsole.ReplaceSel(normalized, FALSE);
+
+ editConsole.SetReadOnly(TRUE);
+}
+
+void CConsoleDlg::TrimConsoleIfNeeded()
+{
+ if (editConsole.GetSafeHwnd() == NULL) {
+ return;
+ }
+
+ const int maxChars = 512 * 1024;
+ const int trimToChars = 384 * 1024;
+
+ int len = editConsole.GetTextLength();
+ if (len <= maxChars) {
+ return;
+ }
+
+ int removeCount = len - trimToChars;
+
+ editConsole.SetReadOnly(FALSE);
+ editConsole.SetSel(0, removeCount);
+ editConsole.ReplaceSel("", FALSE);
+ editConsole.SetReadOnly(TRUE);
+}
+
+void CConsoleDlg::AppendConsoleTextColored(const char* text)
+{
+ if (text == NULL || editConsole.GetSafeHwnd() == NULL) {
+ return;
+ }
+
+ COLORREF currentColor = ICE_CONSOLE_DEFAULT_COLOR;
+ CString run;
+
+ const char* p = text;
+
+ while (*p != '\0') {
+ int consumed = 0;
+ COLORREF newColor = ICE_CONSOLE_DEFAULT_COLOR;
+
+ if (TryConsumeColorToken(p, consumed, newColor)) {
+ if (!run.IsEmpty()) {
+ AppendConsoleRun(run, currentColor);
+ run.Empty();
+ }
+
+ currentColor = newColor;
+ p += consumed;
+ continue;
+ }
+
+ if (p[0] == '^' && p[1] == '^') {
+ run += '^';
+ p += 2;
+ continue;
+ }
+
+ run += *p;
+ p++;
+ }
+
+ if (!run.IsEmpty()) {
+ AppendConsoleRun(run, currentColor);
+ }
+
+ TrimConsoleIfNeeded();
+
+ int len = editConsole.GetTextLength();
+ editConsole.SetSel(len, len);
+ editConsole.SendMessage(EM_SCROLLCARET, 0, 0);
+}
+
+void CConsoleDlg::AddText(const char* msg)
+{
+ if (msg == NULL || editConsole.GetSafeHwnd() == NULL) {
+ return;
+ }
+
+ idStr work;
+
+ work = msg;
+
+ // DO NOT call RemoveColors() here.
+ // The RichEdit append path consumes Doom color codes and applies colors.
+ work = CEntityDlg::TranslateString(work.c_str());
+
+ AppendConsoleTextColored(work.c_str());
+}
void CConsoleDlg::OnSize(UINT nType, int cx, int cy)
{
@@ -92,158 +464,212 @@ void CConsoleDlg::OnSize(UINT nType, int cx, int cy)
return;
}
- CRect rect, crect;
- GetWindowRect(rect);
- editInput.GetWindowRect(crect);
+ CRect clientRect;
+ GetClientRect(&clientRect);
- editInput.SetWindowPos(NULL, 4, rect.Height() - 4 - crect.Height(), rect.Width() - 8, crect.Height(), SWP_SHOWWINDOW);
- editConsole.SetWindowPos(NULL, 4, 4, rect.Width() - 8, rect.Height() - crect.Height() - 8, SWP_SHOWWINDOW);
+ CRect inputRect;
+ editInput.GetWindowRect(&inputRect);
+
+ const int margin = 4;
+ const int inputHeight = inputRect.Height();
+
+ editInput.SetWindowPos(
+ NULL,
+ margin,
+ clientRect.Height() - margin - inputHeight,
+ clientRect.Width() - margin * 2,
+ inputHeight,
+ SWP_SHOWWINDOW
+ );
+
+ if (editConsole.GetSafeHwnd() != NULL) {
+ editConsole.SetWindowPos(
+ NULL,
+ margin,
+ margin,
+ clientRect.Width() - margin * 2,
+ clientRect.Height() - inputHeight - margin * 3,
+ SWP_SHOWWINDOW
+ );
+ }
}
BOOL CConsoleDlg::PreTranslateMessage(MSG* pMsg)
{
-
if (pMsg->hwnd == editInput.GetSafeHwnd()) {
- if (pMsg->message == WM_KEYDOWN && pMsg->wParam == VK_ESCAPE ) {
+ if (pMsg->message == WM_KEYDOWN && pMsg->wParam == VK_ESCAPE) {
Select_Deselect();
- g_pParentWnd->SetFocus ();
+ g_pParentWnd->SetFocus();
return TRUE;
}
- if (pMsg->message == WM_KEYDOWN && pMsg->wParam == VK_RETURN ) {
+ if (pMsg->message == WM_KEYDOWN && pMsg->wParam == VK_RETURN) {
ExecuteCommand();
return TRUE;
}
- if (pMsg->message == WM_KEYDOWN && pMsg->wParam == VK_ESCAPE ) {
- if (pMsg->wParam == VK_ESCAPE) {
- g_pParentWnd->GetCamera()->SetFocus();
- Select_Deselect();
- }
-
- return TRUE;
- }
-
- if (pMsg->message == WM_KEYDOWN && pMsg->wParam == VK_UP ) {
- //save off the current in-progress command so we can get back to it
- if ( saveCurrentCommand == true ) {
- CString str;
- editInput.GetWindowText ( str );
- currentCommand = str.GetBuffer ( 0 );
+ if (pMsg->message == WM_KEYDOWN && pMsg->wParam == VK_UP) {
+ // Save off the current in-progress command so we can get back to it.
+ if (saveCurrentCommand == true) {
+ CString str;
+ editInput.GetWindowText(str);
+ currentCommand = str.GetBuffer(0);
saveCurrentCommand = false;
- }
-
- if ( consoleHistory.Num () > 0 ) {
- editInput.SetWindowText ( consoleHistory[currentHistoryPosition] );
-
- int selLocation = consoleHistory[currentHistoryPosition].Length ();
- editInput.SetSel ( selLocation , selLocation + 1);
-}
-
- if ( currentHistoryPosition > 0) {
- --currentHistoryPosition;
- }
-
- return TRUE;
- }
-
- if (pMsg->message == WM_KEYDOWN && pMsg->wParam == VK_DOWN ) {
- int selLocation = 0;
- if ( currentHistoryPosition < consoleHistory.Num () - 1 ) {
- ++currentHistoryPosition;
- editInput.SetWindowText ( consoleHistory[currentHistoryPosition] );
- selLocation = consoleHistory[currentHistoryPosition].Length ();
- }
- else {
- editInput.SetWindowText ( currentCommand );
- selLocation = currentCommand.Length ();
- currentCommand.Clear ();
- saveCurrentCommand = true;
- }
-
- editInput.SetSel ( selLocation , selLocation + 1);
-
- return TRUE;
- }
- if (pMsg->message == WM_KEYDOWN && pMsg->wParam == VK_TAB ) {
- common->Printf ( "Command History\n----------------\n" );
- for ( int i = 0 ; i < consoleHistory.Num ();i++ )
-{
- common->Printf ( "[cmd %d]: %s\n" , i , consoleHistory[i].c_str() );
}
- common->Printf ( "----------------\n" );
- }
- if (pMsg->message == WM_KEYDOWN && pMsg->wParam == VK_NEXT) {
- editConsole.LineScroll ( 10 );
+
+ if (consoleHistory.Num() > 0) {
+ if (currentHistoryPosition < 0) {
+ currentHistoryPosition = consoleHistory.Num() - 1;
+ }
+ if (currentHistoryPosition >= consoleHistory.Num()) {
+ currentHistoryPosition = consoleHistory.Num() - 1;
+ }
+
+ editInput.SetWindowText(consoleHistory[currentHistoryPosition]);
+
+ int selLocation = consoleHistory[currentHistoryPosition].Length();
+ editInput.SetSel(selLocation, selLocation);
+
+ if (currentHistoryPosition > 0) {
+ --currentHistoryPosition;
+ }
+ }
+
+ return TRUE;
}
- if (pMsg->message == WM_KEYDOWN && pMsg->wParam == VK_PRIOR ) {
- editConsole.LineScroll ( -10 );
+ if (pMsg->message == WM_KEYDOWN && pMsg->wParam == VK_DOWN) {
+ int selLocation = 0;
+
+ if (currentHistoryPosition < consoleHistory.Num() - 1) {
+ ++currentHistoryPosition;
+ editInput.SetWindowText(consoleHistory[currentHistoryPosition]);
+ selLocation = consoleHistory[currentHistoryPosition].Length();
+ }
+ else {
+ editInput.SetWindowText(currentCommand);
+ selLocation = currentCommand.Length();
+ currentCommand.Clear();
+ saveCurrentCommand = true;
+ }
+
+ editInput.SetSel(selLocation, selLocation);
+ return TRUE;
}
- if (pMsg->message == WM_KEYDOWN && pMsg->wParam == VK_HOME ) {
- editConsole.LineScroll ( -editConsole.GetLineCount() );
+ if (pMsg->message == WM_KEYDOWN && pMsg->wParam == VK_TAB) {
+ common->Printf("Command History\n----------------\n");
+
+ for (int i = 0; i < consoleHistory.Num(); i++) {
+ common->Printf("[cmd %d]: %s\n", i, consoleHistory[i].c_str());
+ }
+
+ common->Printf("----------------\n");
+ return TRUE;
}
- if (pMsg->message == WM_KEYDOWN && pMsg->wParam == VK_END ) {
- editConsole.LineScroll ( editConsole.GetLineCount() );
+ if (pMsg->message == WM_KEYDOWN && pMsg->wParam == VK_NEXT) {
+ if (editConsole.GetSafeHwnd() != NULL) {
+ editConsole.LineScroll(10);
+ }
+ return TRUE;
+ }
+
+ if (pMsg->message == WM_KEYDOWN && pMsg->wParam == VK_PRIOR) {
+ if (editConsole.GetSafeHwnd() != NULL) {
+ editConsole.LineScroll(-10);
+ }
+ return TRUE;
+ }
+
+ if (pMsg->message == WM_KEYDOWN && pMsg->wParam == VK_HOME) {
+ if (editConsole.GetSafeHwnd() != NULL) {
+ editConsole.LineScroll(-editConsole.GetLineCount());
+ }
+ return TRUE;
+ }
+
+ if (pMsg->message == WM_KEYDOWN && pMsg->wParam == VK_END) {
+ if (editConsole.GetSafeHwnd() != NULL) {
+ editConsole.LineScroll(editConsole.GetLineCount());
+ }
+ return TRUE;
}
}
return CDialog::PreTranslateMessage(pMsg);
}
-void CConsoleDlg::OnSetFocus(CWnd* pOldWnd) {
+void CConsoleDlg::OnSetFocus(CWnd* pOldWnd)
+{
CDialog::OnSetFocus(pOldWnd);
- editInput.SetFocus();
+
+ if (editInput.GetSafeHwnd() != NULL) {
+ editInput.SetFocus();
+ }
}
-void CConsoleDlg::SetConsoleText ( const idStr& text ) {
- editInput.Clear ();
- editInput.SetWindowText ( text.c_str() );
+void CConsoleDlg::SetConsoleText(const idStr& text)
+{
+ if (editInput.GetSafeHwnd() != NULL) {
+ editInput.Clear();
+ editInput.SetWindowText(text.c_str());
+ }
}
-void CConsoleDlg::ExecuteCommand ( const idStr& cmd ) {
+void CConsoleDlg::ExecuteCommand(const idStr& cmd)
+{
CString str;
- if ( cmd.Length() > 0 ) {
+
+ if (cmd.Length() > 0) {
str = cmd;
}
else {
editInput.GetWindowText(str);
}
- if ( str != "" ) {
+ if (str != "") {
editInput.SetWindowText("");
- common->Printf("%s\n", str.GetBuffer(0));
- //avoid adding multiple identical commands in a row
- int index = consoleHistory.Num ();
+ common->Printf("%s\n", str.GetBuffer(0));
- if ( index == 0 || str.GetBuffer(0) != consoleHistory[index-1]) {
- //keep the history to 16 commands, removing the oldest command
- if ( consoleHistory.Num () > 16 ) {
- consoleHistory.RemoveIndex ( 0 );
+ // Avoid adding multiple identical commands in a row.
+ int index = consoleHistory.Num();
+
+ if (index == 0 || str.GetBuffer(0) != consoleHistory[index - 1]) {
+ // Keep the history to 16 commands, removing the oldest command.
+ if (consoleHistory.Num() > 16) {
+ consoleHistory.RemoveIndex(0);
}
- currentHistoryPosition = consoleHistory.Append ( str.GetBuffer (0) );
+
+ currentHistoryPosition = consoleHistory.Append(str.GetBuffer(0));
}
else {
- currentHistoryPosition = consoleHistory.Num () - 1;
+ currentHistoryPosition = consoleHistory.Num() - 1;
}
- currentCommand.Clear ();
+ currentCommand.Clear();
+ saveCurrentCommand = true;
bool propogateCommand = true;
- //process some of our own special commands
- if ( str.CompareNoCase ( "clear" ) == 0) {
- editConsole.SetSel ( 0 , -1 );
- editConsole.Clear ();
- }
- else if ( str.CompareNoCase ( "edit" ) == 0) {
+ // Process some of our own special commands.
+ if (str.CompareNoCase("clear") == 0) {
+ if (editConsole.GetSafeHwnd() != NULL) {
+ editConsole.SetReadOnly(FALSE);
+ editConsole.SetSel(0, -1);
+ editConsole.ReplaceSel("", FALSE);
+ editConsole.SetReadOnly(TRUE);
+ }
+
propogateCommand = false;
}
- if ( propogateCommand ) {
- cmdSystem->BufferCommandText( CMD_EXEC_NOW, str );
+ else if (str.CompareNoCase("edit") == 0) {
+ propogateCommand = false;
+ }
+
+ if (propogateCommand) {
+ cmdSystem->BufferCommandText(CMD_EXEC_NOW, str);
}
Sys_UpdateWindows(W_ALL);
@@ -254,8 +680,22 @@ void CConsoleDlg::OnActivate(UINT nState, CWnd* pWndOther, BOOL bMinimized)
{
CDialog::OnActivate(nState, pWndOther, bMinimized);
- if ( nState == WA_ACTIVE || nState == WA_CLICKACTIVE )
- {
- editInput.SetFocus();
- }
+ if (nState == WA_ACTIVE || nState == WA_CLICKACTIVE) {
+ if (editInput.GetSafeHwnd() != NULL) {
+ editInput.SetFocus();
+ }
+ }
}
+
+HBRUSH CConsoleDlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
+{
+ HBRUSH hbr = CDialog::OnCtlColor(pDC, pWnd, nCtlColor);
+
+ if (pWnd != NULL && pWnd->GetSafeHwnd() == editInput.GetSafeHwnd()) {
+ pDC->SetBkColor(RGB(0, 0, 0));
+ pDC->SetTextColor(ICE_CONSOLE_DEFAULT_COLOR);
+ return (HBRUSH)blackBrush.GetSafeHandle();
+ }
+
+ return hbr;
+}
\ No newline at end of file
diff --git a/neo/engine/tools/radiant/ConsoleDlg.h b/neo/engine/tools/radiant/ConsoleDlg.h
index 370acd4c..225c1a2d 100644
--- a/neo/engine/tools/radiant/ConsoleDlg.h
+++ b/neo/engine/tools/radiant/ConsoleDlg.h
@@ -2,9 +2,9 @@
===========================================================================
IceTech GPL Source Code
-Copyright (C) 2026 Justin Marshall
+Copyright (C) 2026 Justin Marshall
-This file is part of the IceTech GPL Source Code (?IceTech Source Code?).
+This file is part of the IceTech GPL Source Code (?IceTech Source Code?).
IceTech Source Code is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -16,17 +16,15 @@ but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
-You should have received a copy of the GNU General Public License
-along with IceTech Source Code. If not, see .
-
-In addition, the IceTech Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the IceTech Source Code. If not, please request a copy in writing from id Software at the address below.
-
-If you have questions concerning this license or the applicable additional terms, you may contact in writing Justin Marshall, justinmarshall20@gmail.com
-
===========================================================================
*/
#pragma once
+
#include "afxwin.h"
+#include "afxrich.h"
+
+
+#define ICE_CONSOLE_DEFAULT_COLOR RGB(64, 255, 255)
// CConsoleDlg dialog
@@ -36,31 +34,50 @@ class CConsoleDlg : public CDialog
DECLARE_DYNCREATE(CConsoleDlg)
public:
- CConsoleDlg(CWnd* pParent = NULL); // standard constructor
+ CConsoleDlg(CWnd* pParent = NULL);
virtual ~CConsoleDlg();
-// Dialog Data
enum { IDD = IDD_DIALOG_CONSOLE };
protected:
- virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
+ virtual void DoDataExchange(CDataExchange* pDX);
+ virtual BOOL OnInitDialog();
DECLARE_MESSAGE_MAP()
+
public:
- CEdit editConsole;
+ CRichEditCtrl editConsole;
CEdit editInput;
- void AddText(const char *msg);
- void SetConsoleText ( const idStr& text );
- void ExecuteCommand ( const idStr& cmd = "" );
-
+
+ void AddText(const char* msg);
+ void SetConsoleText(const idStr& text);
+ void ExecuteCommand(const idStr& cmd = "");
+
idStr consoleStr;
- idStrList consoleHistory;
- idStr currentCommand;
- int currentHistoryPosition;
+ idStrList consoleHistory;
+ idStr currentCommand;
+ int currentHistoryPosition;
bool saveCurrentCommand;
afx_msg void OnSize(UINT nType, int cx, int cy);
virtual BOOL PreTranslateMessage(MSG* pMsg);
afx_msg void OnSetFocus(CWnd* pOldWnd);
afx_msg void OnActivate(UINT nState, CWnd* pWndOther, BOOL bMinimized);
-};
+ afx_msg HBRUSH OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor);
+
+private:
+ CBrush blackBrush;
+
+ void CreateRichConsoleFromPlaceholder();
+ void SetupRichConsoleDefaults();
+
+ void AppendConsoleTextColored(const char* text);
+ void AppendConsoleRun(const CString& text, COLORREF color);
+ void TrimConsoleIfNeeded();
+
+ bool TryConsumeColorToken(const char* s, int& consumed, COLORREF& color) const;
+ COLORREF DoomColorForIndex(char c) const;
+ COLORREF DoomColorForName(const char* name, int& consumed) const;
+
+ CString NormalizeNewlines(const CString& in) const;
+};
\ No newline at end of file