Files
DoomRTX/neo/engine/tools/materialeditor/MEMainFrame.cpp
T
2026-05-18 15:54:16 -07:00

1254 lines
32 KiB
C++

/*
===========================================================================
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 <http://www.gnu.org/licenses/>.
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) {
}