/* =========================================================================== IceTech GPL Source Code Copyright (C) 2026 Justin Marshall This file is part of the IceTech GPL Source Code (?IceTech Source Code?). IceTech Source Code is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. IceTech Source Code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with IceTech Source Code. If not, see . In addition, the IceTech Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the IceTech Source Code. If not, please request a copy in writing from id Software at the address below. If you have questions concerning this license or the applicable additional terms, you may contact in writing Justin Marshall, justinmarshall20@gmail.com =========================================================================== */ #include "precompiled.h" #pragma hdrstop #include "MaterialEditor.h" #include "MEMainFrame.h" #include "MaterialDef.h" #ifdef _DEBUG #define new DEBUG_NEW #endif #define TAB_CONTROL 0x1006 extern bool MaterialEditorIsEmbedded(void); extern void MaterialEditorDark_ApplyToWindow(HWND hWnd); extern COLORREF MaterialEditorDark_BackgroundColor(void); extern COLORREF MaterialEditorDark_PanelColor(void); extern COLORREF MaterialEditorDark_TextColor(void); extern COLORREF MaterialEditorDark_AccentColor(void); extern COLORREF MaterialEditorDark_BorderColor(void); IMPLEMENT_DYNAMIC(MEMainFrame, CFrameWnd) BEGIN_MESSAGE_MAP(MEMainFrame, CFrameWnd) ON_WM_CREATE() ON_WM_SETFOCUS() ON_WM_DESTROY() ON_WM_SIZE() ON_NOTIFY(TCN_SELCHANGE, TAB_CONTROL, OnTcnSelChange) ON_COMMAND(ID_ME_FILE_EXIT, OnFileExit) ON_COMMAND(ID_ME_FILE_SAVEMATERIAL, OnFileSaveMaterial) ON_COMMAND(ID_ME_FILE_SAVEFILE, OnFileSaveFile) ON_COMMAND(ID_ME_FILE_SAVE, OnFileSaveAll) ON_UPDATE_COMMAND_UI(ID_ME_FILE_SAVEMATERIAL, OnFileSaveMaterialUpdate ) ON_UPDATE_COMMAND_UI(ID_ME_FILE_SAVEFILE, OnFileSaveFileUpdate ) ON_UPDATE_COMMAND_UI(ID_ME_FILE_SAVE, OnFileSaveAllUpdate ) ON_COMMAND(ID_ME_PREVIEW_APPLYCHANGES, OnApplyMaterial) ON_COMMAND(ID_ME_PREVIEW_APPLYFILE, OnApplyFile) ON_COMMAND(ID_ME_PREVIEW_APPLYALL, OnApplyAll) ON_UPDATE_COMMAND_UI(ID_ME_PREVIEW_APPLYCHANGES, OnApplyMaterialUpdate ) ON_UPDATE_COMMAND_UI(ID_ME_PREVIEW_APPLYFILE, OnApplyFileUpdate ) ON_UPDATE_COMMAND_UI(ID_ME_PREVIEW_APPLYALL, OnApplyAllUpdate ) ON_COMMAND(ID_ME_EDIT_CUT, OnEditCut) ON_COMMAND(ID_ME_EDIT_COPY, OnEditCopy) ON_COMMAND(ID_ME_EDIT_PASTE, OnEditPaste) ON_COMMAND(ID_ME_EDIT_DELETE, OnEditDelete) ON_COMMAND(ID_ME_EDIT_RENAME, OnEditRename) ON_UPDATE_COMMAND_UI(ID_ME_EDIT_CUT, OnEditCutUpdate) ON_UPDATE_COMMAND_UI(ID_ME_EDIT_COPY, OnEditCopyUpdate) ON_UPDATE_COMMAND_UI(ID_ME_EDIT_PASTE, OnEditPasteUpdate) ON_UPDATE_COMMAND_UI(ID_ME_EDIT_DELETE, OnEditDeleteUpdate) ON_UPDATE_COMMAND_UI(ID_ME_EDIT_RENAME, OnEditRenameUpdate) ON_COMMAND(ID_ME_EDIT_FIND, OnEditFind) ON_COMMAND(ID_ME_EDIT_FIND_NEXT, OnEditFindNext) ON_COMMAND(ID_ME_EDIT_UNDO, OnEditUndo) ON_COMMAND(ID_ME_EDIT_REDO, OnEditRedo) ON_UPDATE_COMMAND_UI(ID_ME_EDIT_UNDO, OnEditUndoUpdate ) ON_UPDATE_COMMAND_UI(ID_ME_EDIT_REDO, OnEditRedoUpdate ) ON_COMMAND(ID_VIEW_INCLUDEFILENAME, OnViewIncludeFile) ON_COMMAND(ID_PREVIEW_RELOADARBPROGRAMS, OnReloadArbPrograms) ON_COMMAND(ID_PREVIEW_RELOADIMAGES, OnReloadImages ) END_MESSAGE_MAP() static UINT indicators[] = { ID_SEPARATOR, // status line indicator ID_INDICATOR_CAPS, ID_INDICATOR_NUM, ID_INDICATOR_SCRL, }; #define ME_EMBEDDED_MENUBAR_ID 0x6E10 static HWND s_meEmbeddedMenuBar = NULL; static HMENU s_meEmbeddedMenu = NULL; static CRect s_meEmbeddedMenuRects[64]; static int s_meEmbeddedMenuRectCount = 0; static int s_meEmbeddedMenuHotItem = -1; static int MEEmbeddedMenuBar_PreferredHeight() { int h = ::GetSystemMetrics(SM_CYMENU) + 4; return (h < 22) ? 22 : h; } static COLORREF MEEmbeddedMenuBar_MenuColor() { return RGB(28, 31, 36); } static COLORREF MEEmbeddedMenuBar_HotColor() { return RGB(45, 50, 58); } static void MEEmbeddedMenuBar_FillRect(HDC hDC, const RECT& rc, COLORREF color) { HBRUSH brush = ::CreateSolidBrush(color); ::FillRect(hDC, &rc, brush); ::DeleteObject(brush); } static void MEEmbeddedMenuBar_RebuildRects(HWND hWnd, HDC hDC) { s_meEmbeddedMenuRectCount = 0; if (!s_meEmbeddedMenu) { return; } int x = 4; const int y = 1; const int h = MEEmbeddedMenuBar_PreferredHeight() - 2; const int count = ::GetMenuItemCount(s_meEmbeddedMenu); for (int i = 0; i < count && s_meEmbeddedMenuRectCount < 64; i++) { char text[128]; text[0] = '\0'; ::GetMenuStringA(s_meEmbeddedMenu, i, text, sizeof(text), MF_BYPOSITION); SIZE size; size.cx = 0; size.cy = 0; ::GetTextExtentPoint32A(hDC, text, strlen(text), &size); CRect rect(x, y, x + size.cx + 22, y + h); s_meEmbeddedMenuRects[s_meEmbeddedMenuRectCount++] = rect; x = rect.right + 1; } } static int MEEmbeddedMenuBar_HitTest(const CPoint& point) { for (int i = 0; i < s_meEmbeddedMenuRectCount; i++) { if (s_meEmbeddedMenuRects[i].PtInRect(point)) { return i; } } return -1; } static void MEEmbeddedMenuBar_Draw(HWND hWnd, HDC hDC) { RECT client; ::GetClientRect(hWnd, &client); MEEmbeddedMenuBar_FillRect(hDC, client, MEEmbeddedMenuBar_MenuColor()); HFONT font = (HFONT)::SendMessage(hWnd, WM_GETFONT, 0, 0); HFONT oldFont = font ? (HFONT)::SelectObject(hDC, font) : NULL; ::SetBkMode(hDC, TRANSPARENT); MEEmbeddedMenuBar_RebuildRects(hWnd, hDC); if (s_meEmbeddedMenu) { const int count = ::GetMenuItemCount(s_meEmbeddedMenu); for (int i = 0; i < count && i < s_meEmbeddedMenuRectCount; i++) { char text[128]; text[0] = '\0'; ::GetMenuStringA(s_meEmbeddedMenu, i, text, sizeof(text), MF_BYPOSITION); CRect itemRect = s_meEmbeddedMenuRects[i]; if (i == s_meEmbeddedMenuHotItem) { MEEmbeddedMenuBar_FillRect(hDC, itemRect, MEEmbeddedMenuBar_HotColor()); HPEN pen = ::CreatePen(PS_SOLID, 1, MaterialEditorDark_AccentColor()); HPEN oldPen = (HPEN)::SelectObject(hDC, pen); HBRUSH oldBrush = (HBRUSH)::SelectObject(hDC, GetStockObject(NULL_BRUSH)); ::Rectangle(hDC, itemRect.left, itemRect.top, itemRect.right, itemRect.bottom); ::SelectObject(hDC, oldBrush); ::SelectObject(hDC, oldPen); ::DeleteObject(pen); } ::SetTextColor(hDC, MaterialEditorDark_TextColor()); ::DrawTextA(hDC, text, -1, &itemRect, DT_SINGLELINE | DT_VCENTER | DT_CENTER); } } HPEN linePen = ::CreatePen(PS_SOLID, 1, MaterialEditorDark_BorderColor()); HPEN oldPen = (HPEN)::SelectObject(hDC, linePen); ::MoveToEx(hDC, client.left, client.bottom - 1, NULL); ::LineTo(hDC, client.right, client.bottom - 1); ::SelectObject(hDC, oldPen); ::DeleteObject(linePen); if (oldFont) { ::SelectObject(hDC, oldFont); } } static CWnd *MEEmbeddedMenuBar_GetCommandTarget(HWND hWnd) { HWND hFrame = ::GetParent(hWnd); if (!hFrame) { return NULL; } CWnd *target = CWnd::FromHandlePermanent(hFrame); if (!target) { target = CWnd::FromHandle(hFrame); } return target; } static void MEEmbeddedMenuBar_Track(HWND hWnd, int menuIndex) { if (!s_meEmbeddedMenu || menuIndex < 0 || menuIndex >= ::GetMenuItemCount(s_meEmbeddedMenu)) { return; } HMENU hPopup = ::GetSubMenu(s_meEmbeddedMenu, menuIndex); if (!hPopup) { return; } CRect itemRect; if (menuIndex < s_meEmbeddedMenuRectCount) { itemRect = s_meEmbeddedMenuRects[menuIndex]; } else { ::GetClientRect(hWnd, &itemRect); } POINT pt; pt.x = itemRect.left; pt.y = itemRect.bottom; ::ClientToScreen(hWnd, &pt); s_meEmbeddedMenuHotItem = menuIndex; ::InvalidateRect(hWnd, NULL, FALSE); ::UpdateWindow(hWnd); CWnd *target = MEEmbeddedMenuBar_GetCommandTarget(hWnd); HWND hTarget = target ? target->GetSafeHwnd() : ::GetParent(hWnd); if (hTarget) { ::SetForegroundWindow(hTarget); ::SendMessage(hTarget, WM_INITMENUPOPUP, (WPARAM)hPopup, MAKELPARAM(menuIndex, FALSE)); } UINT command = ::TrackPopupMenu( hPopup, TPM_LEFTALIGN | TPM_TOPALIGN | TPM_LEFTBUTTON | TPM_RIGHTBUTTON | TPM_RETURNCMD, pt.x, pt.y, 0, hTarget ? hTarget : hWnd, NULL ); if (command != 0 && hTarget) { ::SendMessage(hTarget, WM_COMMAND, MAKEWPARAM(command, 0), 0); } s_meEmbeddedMenuHotItem = -1; ::InvalidateRect(hWnd, NULL, FALSE); if (hTarget) { ::PostMessage(hTarget, WM_NULL, 0, 0); } } static LRESULT CALLBACK MEEmbeddedMenuBar_WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { case WM_ERASEBKGND: return 1; case WM_PAINT: { PAINTSTRUCT ps; HDC hDC = ::BeginPaint(hWnd, &ps); MEEmbeddedMenuBar_Draw(hWnd, hDC); ::EndPaint(hWnd, &ps); return 0; } case WM_LBUTTONDOWN: { HDC hDC = ::GetDC(hWnd); MEEmbeddedMenuBar_RebuildRects(hWnd, hDC); ::ReleaseDC(hWnd, hDC); CPoint pt((short)LOWORD(lParam), (short)HIWORD(lParam)); MEEmbeddedMenuBar_Track(hWnd, MEEmbeddedMenuBar_HitTest(pt)); return 0; } case WM_MOUSEMOVE: { HDC hDC = ::GetDC(hWnd); MEEmbeddedMenuBar_RebuildRects(hWnd, hDC); ::ReleaseDC(hWnd, hDC); CPoint pt((short)LOWORD(lParam), (short)HIWORD(lParam)); int hot = MEEmbeddedMenuBar_HitTest(pt); if (hot != s_meEmbeddedMenuHotItem) { s_meEmbeddedMenuHotItem = hot; ::InvalidateRect(hWnd, NULL, FALSE); } break; } case WM_SETFONT: return ::DefWindowProc(hWnd, uMsg, wParam, lParam); case WM_NCDESTROY: if (hWnd == s_meEmbeddedMenuBar) { s_meEmbeddedMenuBar = NULL; s_meEmbeddedMenuHotItem = -1; s_meEmbeddedMenuRectCount = 0; } break; } return ::DefWindowProc(hWnd, uMsg, wParam, lParam); } static BOOL MEEmbeddedMenuBar_Create(CWnd *parent) { if (!parent || !parent->GetSafeHwnd()) { return FALSE; } if (s_meEmbeddedMenuBar && ::IsWindow(s_meEmbeddedMenuBar)) { ::SetParent(s_meEmbeddedMenuBar, parent->GetSafeHwnd()); ::ShowWindow(s_meEmbeddedMenuBar, SW_SHOW); return TRUE; } if (!s_meEmbeddedMenu) { s_meEmbeddedMenu = ::LoadMenu(AfxGetInstanceHandle(), MAKEINTRESOURCE(IDR_ME_MAINFRAME)); } CString className = AfxRegisterWndClass( CS_HREDRAW | CS_VREDRAW, ::LoadCursor(NULL, IDC_ARROW), (HBRUSH)::GetStockObject(BLACK_BRUSH), NULL ); s_meEmbeddedMenuBar = ::CreateWindowEx( 0, className, _T("MaterialEditorEmbeddedMenuBar"), WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS, 0, 0, 0, MEEmbeddedMenuBar_PreferredHeight(), parent->GetSafeHwnd(), (HMENU)ME_EMBEDDED_MENUBAR_ID, AfxGetInstanceHandle(), NULL ); if (!s_meEmbeddedMenuBar) { return FALSE; } ::SetWindowLongPtr(s_meEmbeddedMenuBar, GWLP_WNDPROC, (LONG_PTR)MEEmbeddedMenuBar_WndProc); if (materialEditorFont && materialEditorFont->GetSafeHandle()) { ::SendMessage(s_meEmbeddedMenuBar, WM_SETFONT, (WPARAM)materialEditorFont->GetSafeHandle(), TRUE); } return TRUE; } static void MEEmbeddedMenuBar_DestroyForParent(HWND hParent) { if (s_meEmbeddedMenuBar && ::IsWindow(s_meEmbeddedMenuBar) && ::GetParent(s_meEmbeddedMenuBar) == hParent) { ::DestroyWindow(s_meEmbeddedMenuBar); s_meEmbeddedMenuBar = NULL; } if (s_meEmbeddedMenu) { ::DestroyMenu(s_meEmbeddedMenu); s_meEmbeddedMenu = NULL; } } static HWND MEEmbeddedMenuBar_GetSafeHwnd() { return (s_meEmbeddedMenuBar && ::IsWindow(s_meEmbeddedMenuBar)) ? s_meEmbeddedMenuBar : NULL; } static CMenu *MEEmbeddedMenuBar_GetMenu() { return s_meEmbeddedMenu ? CMenu::FromHandle(s_meEmbeddedMenu) : NULL; } /** * Constructor for MEMainFrame. Initialize some member data and load the options. */ MEMainFrame::MEMainFrame() { currentDoc = NULL; m_find = NULL; m_consoleView = NULL; m_materialTreeView = NULL; m_previewPropertyView = NULL; m_materialPreviewView = NULL; m_materialEditView = NULL; m_stageView = NULL; m_materialPropertyView = NULL; m_materialEditSplitter = NULL; searchData.searched = false; options.Load(); } /** * Destructor for MEMainFrame. */ MEMainFrame::~MEMainFrame() { } /** * Called to add console text to the console view. * @param msg The text that is to be added to the console. */ void MEMainFrame::PrintConsoleMessage(const char *msg) { if (m_consoleView && m_consoleView->GetSafeHwnd()) { m_consoleView->AddText(msg); } } /** * Sets a few window styles for the main window during the creation process. */ BOOL MEMainFrame::PreCreateWindow(CREATESTRUCT& cs) { if( !CFrameWnd::PreCreateWindow(cs) ) return FALSE; cs.dwExStyle &= ~WS_EX_CLIENTEDGE; cs.lpszClass = AfxRegisterWndClass(0, ::LoadCursor(NULL, IDC_ARROW), (HBRUSH)::GetStockObject(BLACK_BRUSH), NULL); if (MaterialEditorIsEmbedded()) { cs.style &= ~(WS_POPUP | WS_CAPTION | WS_THICKFRAME | WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX); cs.style |= WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS; cs.dwExStyle &= ~(WS_EX_APPWINDOW | WS_EX_TOOLWINDOW | WS_EX_WINDOWEDGE | WS_EX_DLGMODALFRAME | WS_EX_CLIENTEDGE); cs.dwExStyle |= WS_EX_CONTROLPARENT; } return TRUE; } /** * Called by the MFC framework to allow the window to create any client windows. This method * creates all of the spliter windows and registers all of the views with the document manager. */ BOOL MEMainFrame::OnCreateClient(LPCREATESTRUCT lpcs, CCreateContext* pContext) { // The Material Editor now lives inside the Radiant tab host, so the old // internal Editor/Console tab strip is gone. Console output remains routed // through Doom's main console instead of a second nested console tab. m_splitterWnd.CreateStatic(this, 2, 1); m_editSplitter.CreateStatic(&m_splitterWnd, 1, 2, WS_CHILD | WS_VISIBLE | WS_BORDER, m_splitterWnd.IdFromRowCol(0, 0)); if(!m_editSplitter.CreateView(0, 0, RUNTIME_CLASS(MaterialTreeView), CSize(300, 200), pContext)) { TRACE0("Failed to create material list pane\n"); return FALSE; } if(!m_editSplitter.CreateView(0, 1, RUNTIME_CLASS(MaterialEditView), CSize(200, 200), pContext)) { TRACE0("Failed to create stage property pane\n"); return FALSE; } m_previewSplitter.CreateStatic(&m_splitterWnd, 1, 2, WS_CHILD | WS_VISIBLE | WS_BORDER, m_splitterWnd.IdFromRowCol(1, 0)); if(!m_previewSplitter.CreateView(0, 0, RUNTIME_CLASS(MaterialPreviewPropView), CSize(300, 200), pContext)) { TRACE0("Failed to create preview property pane\n"); return FALSE; } if(!m_previewSplitter.CreateView(0, 1, RUNTIME_CLASS(MaterialPreviewView), CSize(100, 200), pContext)) { TRACE0("Failed to create preview pane\n"); return FALSE; } //Get references to all of the views m_materialTreeView = (MaterialTreeView*)m_editSplitter.GetPane(0, 0); m_previewPropertyView = (MaterialPreviewPropView*)m_previewSplitter.GetPane(0, 0); m_materialPreviewView = (MaterialPreviewView*)m_previewSplitter.GetPane(0, 1); m_materialEditView = (MaterialEditView*)m_editSplitter.GetPane(0, 1); m_stageView = m_materialEditView->m_stageView; m_materialPropertyView = m_materialEditView->m_materialPropertyView; m_materialEditSplitter = &m_materialEditView->m_editSplitter; //Load the splitter positions from the registry int val = options.GetMaterialEditHeight(); if(val <= 0) val = 300; m_splitterWnd.SetRowInfo(0, val, 0); val = options.GetMaterialTreeWidth(); if(val <= 0) val = 300; m_editSplitter.SetColumnInfo(0, val, 0); val = options.GetStageWidth(); if(val <= 0) val = 200; m_materialEditSplitter->SetColumnInfo(0, val, 0); val = options.GetPreviewPropertiesWidth(); if(val <= 0) val = 300; m_previewSplitter.SetColumnInfo(0, val, 0); //Register the views with the document manager materialDocManager.RegisterMaterialView(this); materialDocManager.RegisterMaterialView(m_materialTreeView); materialDocManager.RegisterMaterialView(m_stageView); materialDocManager.RegisterMaterialView(m_materialPropertyView); materialDocManager.RegisterMaterialView(m_materialPreviewView); materialDocManager.RegisterMaterialView(m_materialEditView); //Let the stage window know about the prop window m_stageView->SetMaterialPropertyView(m_materialPropertyView); //Let the preview props now about the preview window m_previewPropertyView->RegisterPreviewView(m_materialPreviewView); m_previewPropertyView->InitializePropTree(); m_previewPropertyView->GetPropertyTreeCtrl().SetColumn(120); MaterialDefManager::InitializeMaterialDefLists(); //Some prop tree initialization //m_materialPropertyView->InitializePropTreeDefs(); val = options.GetMaterialPropHeadingWidth(); if(val <= 0) val = 200; m_materialPropertyView->GetPropertyTreeCtrl().SetColumn(val); m_materialPropertyView->LoadSettings(); val = options.GetPreviewPropHeadingWidth(); if(val <= 0) val = 120; m_previewPropertyView->GetPropertyTreeCtrl().SetColumn(val); //Build the material list m_materialTreeView->InitializeMaterialList(true); SetActiveView(m_materialTreeView); MaterialEditorDark_ApplyToWindow(GetSafeHwnd()); return CFrameWnd::OnCreateClient(lpcs, pContext); } /** * Called by the framework while the window is being created. This methods * creates the tool bars and status bars * /todo Bmatt Nerve: Need to get the toolbars to work correctly. */ int MEMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct) { if (CFrameWnd::OnCreate(lpCreateStruct) == -1) return -1; // Toolbars and status bars belong to the old standalone Material Editor. // Keep only a compact menu strip when this frame is embedded in Radiant. if (MaterialEditorIsEmbedded()) { MEEmbeddedMenuBar_Create(this); } MaterialEditorDark_ApplyToWindow(GetSafeHwnd()); //Load the window placement from the options only for the legacy standalone frame. if (!MaterialEditorIsEmbedded()) { options.GetWindowPlacement ( "mainframe", m_hWnd ); } return 0; } /** * Called by the MFC framework while the window is being destroyed. This method * saves the splitter and window positions. */ void MEMainFrame::OnDestroy() { CFrameWnd::OnDestroy(); int cur; int min; if (m_splitterWnd.GetSafeHwnd()) { m_splitterWnd.GetRowInfo(0, cur, min); options.SetMaterialEditHeight(cur); } if (m_editSplitter.GetSafeHwnd()) { m_editSplitter.GetColumnInfo(0, cur, min); options.SetMaterialTreeWidth(cur); } if (m_materialEditSplitter && m_materialEditSplitter->GetSafeHwnd()) { m_materialEditSplitter->GetColumnInfo(0, cur, min); options.SetStageWidth(cur); } if (m_previewSplitter.GetSafeHwnd()) { m_previewSplitter.GetColumnInfo(0, cur, min); options.SetPreviewPropertiesWidth(cur); } if (m_materialPropertyView && m_materialPropertyView->GetSafeHwnd()) { cur = m_materialPropertyView->GetPropertyTreeCtrl().GetColumn(); options.SetMaterialPropHeadingWidth(cur); m_materialPropertyView->SaveSettings(); } if (m_previewPropertyView && m_previewPropertyView->GetSafeHwnd()) { cur = m_previewPropertyView->GetPropertyTreeCtrl().GetColumn(); options.SetPreviewPropHeadingWidth(cur); } if (!MaterialEditorIsEmbedded()) { options.SetWindowPlacement ( "mainframe", m_hWnd ); } options.Save(); MEEmbeddedMenuBar_DestroyForParent(GetSafeHwnd()); MaterialDefManager::DestroyMaterialDefLists(); if (!MaterialEditorIsEmbedded()) { AfxGetApp()->ExitInstance(); } } /** * Called by the MFC framework when the window size is changed. This method adjusts the console view * so that it is always at the bottom of the window and resizes the splitter window to fit * the remaining space. */ void MEMainFrame::OnSize(UINT nType, int cx, int cy) { CFrameWnd::OnSize(nType, cx, cy); if (!m_splitterWnd.GetSafeHwnd()) { return; } int menuHeight = 0; HWND hMenuBar = MEEmbeddedMenuBar_GetSafeHwnd(); if (MaterialEditorIsEmbedded() && hMenuBar) { menuHeight = MEEmbeddedMenuBar_PreferredHeight(); ::MoveWindow(hMenuBar, 0, 0, cx, menuHeight, TRUE); } int contentHeight = cy - menuHeight; if (contentHeight < 0) { contentHeight = 0; } m_splitterWnd.MoveWindow(0, menuHeight, cx, contentHeight); } /** * Called when the user changes the editor/console tab selection. This methods shows and hides * the appropriate windows. */ void MEMainFrame::OnTcnSelChange(NMHDR *pNMHDR, LRESULT *pResult) { // The old nested Editor/Console tabs were removed for the integrated layout. if (pResult) { *pResult = 0; } } /** * Shuts down the material editor. * /todo BMatt Nerve: Need to warn the user if a file is modified. */ void MEMainFrame::OnFileExit() { if (MaterialEditorIsEmbedded()) { ShowWindow(SW_HIDE); return; } PostMessage(WM_DESTROY, 0, 0); } /** * Saves the selected material. */ void MEMainFrame::OnFileSaveMaterial() { MaterialDoc* material = materialDocManager.GetCurrentMaterialDoc(); if(material) { materialDocManager.SaveMaterial(material); } } /** * Saves the selected file. */ void MEMainFrame::OnFileSaveFile() { idStr filename = m_materialTreeView->GetSaveFilename(); if(filename.Length() > 0) { materialDocManager.SaveFile(filename); } } /** * Saves all modified materials. */ void MEMainFrame::OnFileSaveAll() { materialDocManager.SaveAllMaterials(); } /** * Enables the save material menu item if a material is selected and has been modified. */ void MEMainFrame::OnFileSaveMaterialUpdate(CCmdUI *pCmdUI) { MaterialDoc* pDoc = materialDocManager.GetCurrentMaterialDoc(); if(pCmdUI->m_pMenu == NULL) { pCmdUI->Enable(TRUE); return; } if(pDoc && pDoc->modified) { pCmdUI->Enable(TRUE); } else { pCmdUI->Enable(FALSE); } } /** * Enables the Save File menu item if the current file contains a modified material. */ void MEMainFrame::OnFileSaveFileUpdate(CCmdUI *pCmdUI) { if(pCmdUI->m_pMenu == NULL) { pCmdUI->Enable(TRUE); return; } if(m_materialTreeView->CanSaveFile()) { pCmdUI->Enable(TRUE); } else { pCmdUI->Enable(FALSE); } } /** * Enables the Save All menu item if there are any materials that have been modified. */ void MEMainFrame::OnFileSaveAllUpdate(CCmdUI *pCmdUI) { if(pCmdUI->m_pMenu == NULL) { pCmdUI->Enable(TRUE); return; } if(materialDocManager.IsAnyModified()) { pCmdUI->Enable(TRUE); } else { pCmdUI->Enable(FALSE); } } /** * Apply the selected material. */ void MEMainFrame::OnApplyMaterial() { MaterialDoc* material = materialDocManager.GetCurrentMaterialDoc(); if(material) { materialDocManager.ApplyMaterial(material); } } /** * Applies all modified materials in the selected file. */ void MEMainFrame::OnApplyFile() { idStr filename = m_materialTreeView->GetSaveFilename(); if(filename.Length() > 0) { materialDocManager.ApplyFile(filename); } } /** * Applies all modified materials. */ void MEMainFrame::OnApplyAll() { materialDocManager.ApplyAll(); } /** * Enables the Apply Material menu item if the current material has an apply waiting. */ void MEMainFrame::OnApplyMaterialUpdate(CCmdUI *pCmdUI) { MaterialDoc* pDoc = materialDocManager.GetCurrentMaterialDoc(); if(pCmdUI->m_pMenu == NULL) { pCmdUI->Enable(TRUE); return; } if(pDoc && pDoc->applyWaiting) { pCmdUI->Enable(TRUE); } else { pCmdUI->Enable(FALSE); } } /** * Enables the apply file menu item if the current file contains any materials * that need to be applied. */ void MEMainFrame::OnApplyFileUpdate(CCmdUI *pCmdUI) { if(pCmdUI->m_pMenu == NULL) { pCmdUI->Enable(TRUE); return; } MaterialDoc* pDoc = materialDocManager.GetCurrentMaterialDoc(); if(pDoc && materialDocManager.DoesFileNeedApply(pDoc->renderMaterial->GetFileName())) { pCmdUI->Enable(TRUE); } else { pCmdUI->Enable(FALSE); } } /** * Enables the apply all menu item if there are any materials that need * to be applied. */ void MEMainFrame::OnApplyAllUpdate(CCmdUI *pCmdUI) { if(pCmdUI->m_pMenu == NULL) { pCmdUI->Enable(TRUE); return; } if(materialDocManager.DoesAnyNeedApply()) { pCmdUI->Enable(TRUE); } else { pCmdUI->Enable(FALSE); } } /** * Performs a cut operation on the selected material. */ void MEMainFrame::OnEditCut() { CWnd* focus = GetFocus(); if(focus) { if (focus->IsKindOf(RUNTIME_CLASS(MaterialTreeView))) { m_materialTreeView->OnCut(); } } } /** * Performs a copy operation on the selected material or stage. */ void MEMainFrame::OnEditCopy() { CWnd* focus = GetFocus(); if(focus) { if (focus->IsKindOf(RUNTIME_CLASS(StageView))) { m_stageView->OnCopy(); } else if (focus->IsKindOf(RUNTIME_CLASS(MaterialTreeView))) { m_materialTreeView->OnCopy(); } } } /** * Performs a paste operation on the selected material or stage. */ void MEMainFrame::OnEditPaste() { CWnd* focus = GetFocus(); if(focus) { if (focus->IsKindOf(RUNTIME_CLASS(StageView))) { m_stageView->OnPaste(); } else if (focus->IsKindOf(RUNTIME_CLASS(MaterialTreeView))) { m_materialTreeView->OnPaste(); } } } /** * Performs a delete operation on the selected material or stage. */ void MEMainFrame::OnEditDelete() { CWnd* focus = GetFocus(); if(focus) { if (focus->IsKindOf(RUNTIME_CLASS(StageView))) { m_stageView->OnDeleteStage(); } else if (focus->IsKindOf(RUNTIME_CLASS(MaterialTreeView))) { m_materialTreeView->OnDeleteMaterial(); } } } /** * Performs a rename operation on the selected material or stage. */ void MEMainFrame::OnEditRename() { CWnd* focus = GetFocus(); if(focus) { if (focus->IsKindOf(RUNTIME_CLASS(StageView))) { m_stageView->OnRenameStage(); } else if (focus->IsKindOf(RUNTIME_CLASS(MaterialTreeView))) { m_materialTreeView->OnRenameMaterial(); } } } /** * Enable the cut menu item if a material is selected. */ void MEMainFrame::OnEditCutUpdate(CCmdUI *pCmdUI) { if(pCmdUI->m_pMenu == NULL) { pCmdUI->Enable(TRUE); return; } BOOL enable = FALSE; CWnd* focus = GetFocus(); if(focus) { if (focus->IsKindOf(RUNTIME_CLASS(StageView))) { if(m_stageView->CanCut()) { enable = TRUE; } } else if (focus->IsKindOf(RUNTIME_CLASS(MaterialTreeView))) { if(m_materialTreeView->CanCut()) { enable = TRUE; } } else if (focus->IsKindOf(RUNTIME_CLASS(CRichEditCtrl))) { enable = TRUE; } } pCmdUI->Enable(enable); } /** * Enables the copy menu item if a material or stage is selected. */ void MEMainFrame::OnEditCopyUpdate(CCmdUI *pCmdUI) { if(pCmdUI->m_pMenu == NULL) { pCmdUI->Enable(TRUE); return; } BOOL enable = FALSE; CWnd* focus = GetFocus(); if(focus) { if (focus->IsKindOf(RUNTIME_CLASS(StageView))) { if(m_stageView->CanCopy()) { enable = TRUE; } } else if (focus->IsKindOf(RUNTIME_CLASS(MaterialTreeView))) { if(m_materialTreeView->CanCopy()) { enable = TRUE; } } else if (focus->IsKindOf(RUNTIME_CLASS(CRichEditCtrl))) { enable = TRUE; } } pCmdUI->Enable(enable); } /** * Enables a paste operation when a material or stage has been copied. */ void MEMainFrame::OnEditPasteUpdate(CCmdUI *pCmdUI) { if(pCmdUI->m_pMenu == NULL) { pCmdUI->Enable(TRUE); return; } BOOL enable = FALSE; CWnd* focus = GetFocus(); if(focus) { if (focus->IsKindOf(RUNTIME_CLASS(StageView))) { if(m_stageView->CanPaste()) { enable = TRUE; } } else if (focus->IsKindOf(RUNTIME_CLASS(MaterialTreeView))) { if(m_materialTreeView->CanPaste()) { enable = TRUE; } } else if (focus->IsKindOf(RUNTIME_CLASS(CRichEditCtrl))) { enable = TRUE; } } pCmdUI->Enable(enable); } /** * Enables a delete operation when a material or stage is selected. */ void MEMainFrame::OnEditDeleteUpdate(CCmdUI *pCmdUI) { if(pCmdUI->m_pMenu == NULL) { pCmdUI->Enable(TRUE); return; } BOOL enable = FALSE; CWnd* focus = GetFocus(); if(focus) { if (focus->IsKindOf(RUNTIME_CLASS(StageView))) { if(m_stageView->CanDelete()) { enable = TRUE; } } else if (focus->IsKindOf(RUNTIME_CLASS(MaterialTreeView))) { if(m_materialTreeView->CanDelete()) { enable = TRUE; } } else if (focus->IsKindOf(RUNTIME_CLASS(CRichEditCtrl))) { enable = TRUE; } } pCmdUI->Enable(enable); } /** * Enables a rename operation when a material, folder or stage is selected. */ void MEMainFrame::OnEditRenameUpdate(CCmdUI *pCmdUI) { if(pCmdUI->m_pMenu == NULL) { pCmdUI->Enable(TRUE); return; } BOOL enable = FALSE; CWnd* focus = GetFocus(); if(focus) { if (focus->IsKindOf(RUNTIME_CLASS(StageView))) { if(m_stageView->CanRename()) { enable = TRUE; } } else if (focus->IsKindOf(RUNTIME_CLASS(MaterialTreeView))) { if(m_materialTreeView->CanRename()) { enable = TRUE; } } } pCmdUI->Enable(enable); } /** * Opens the find dialog. */ void MEMainFrame::OnEditFind() { if (m_find== NULL) { m_find = new FindDialog(this); m_find->Create(); m_find->ShowWindow(SW_SHOW); } else m_find->SetActiveWindow(); } /** * Performs a search with the previously selected search parameters. */ void MEMainFrame::OnEditFindNext() { FindNext(NULL); } /** * Performs an undo operation. */ void MEMainFrame::OnEditUndo() { //Check for undo operation on special windows CWnd* focus = GetFocus(); if(focus) { if (focus->IsKindOf(RUNTIME_CLASS(CRichEditCtrl))) { m_materialEditView->m_textView.Undo(); return; } } materialDocManager.Undo(); } /** * Performs a redo operation. */ void MEMainFrame::OnEditRedo() { //Check for redo operation on special windows CWnd* focus = GetFocus(); if(focus) { if (focus->IsKindOf(RUNTIME_CLASS(CRichEditCtrl))) { m_materialEditView->m_textView.Redo(); return; } } materialDocManager.Redo(); } /** * Enables the undo menu item if an undo is available. */ void MEMainFrame::OnEditUndoUpdate(CCmdUI *pCmdUI) { if(pCmdUI->m_pMenu == NULL) { pCmdUI->Enable(TRUE); return; } CWnd* focus = GetFocus(); if(focus) { if (focus->IsKindOf(RUNTIME_CLASS(CRichEditCtrl))) { pCmdUI->Enable(m_materialEditView->m_textView.CanUndo()); return; } } pCmdUI->Enable(materialDocManager.IsUndoAvailable()); } /** * Enables the redo menu item if a redo is available. */ void MEMainFrame::OnEditRedoUpdate(CCmdUI *pCmdUI) { if(pCmdUI->m_pMenu == NULL) { pCmdUI->Enable(TRUE); return; } CWnd* focus = GetFocus(); if(focus) { if (focus->IsKindOf(RUNTIME_CLASS(CRichEditCtrl))) { pCmdUI->Enable(m_materialEditView->m_textView.CanRedo()); return; } } pCmdUI->Enable(materialDocManager.IsRedoAvailable()); } /** * Toggles between including the file into the material list and not. */ void MEMainFrame::OnViewIncludeFile() { CMenu* mmenu = MaterialEditorIsEmbedded() ? MEEmbeddedMenuBar_GetMenu() : GetMenu(); if (!mmenu) { m_materialTreeView->InitializeMaterialList(true); return; } UINT state = mmenu->GetMenuState(ID_VIEW_INCLUDEFILENAME, MF_BYCOMMAND); ASSERT(state != 0xFFFFFFFF); if (state & MF_CHECKED) { mmenu->CheckMenuItem(ID_VIEW_INCLUDEFILENAME, MF_UNCHECKED | MF_BYCOMMAND); m_materialTreeView->InitializeMaterialList(false); } else { mmenu->CheckMenuItem(ID_VIEW_INCLUDEFILENAME, MF_CHECKED | MF_BYCOMMAND); m_materialTreeView->InitializeMaterialList(true); } } /** * Executes the reloadARBPrograms console command for convinience. */ void MEMainFrame::OnReloadArbPrograms() { cmdSystem->BufferCommandText(CMD_EXEC_NOW, "reloadARBprograms"); } /** * Executes the reloadImages command to reload images that have been changed outside * of the editor. */ void MEMainFrame::OnReloadImages() { cmdSystem->BufferCommandText(CMD_EXEC_NOW, "reloadImages"); } /** * Called by the find dialog when it is closing. */ void MEMainFrame::CloseFind() { m_find = NULL; } /** * Begins a search based on the provided parameters or the previously used * parameters. */ void MEMainFrame::FindNext(MaterialSearchData_t* search) { if(search) { searchData = *search; } else { if(!searchData.searched) { return; } } //The material tree controls the searching if(!m_materialTreeView->FindNextMaterial(&searchData)) { MessageBox(va("Unable to find '%s'.", searchData.searchText.c_str()), "Find"); } searchData.searched = true; } /** * Called when the selected material has changed. * @param pMaterial The newly selected material. */ void MEMainFrame::MV_OnMaterialSelectionChange(MaterialDoc* pMaterial) { }