winamp/Src/external_dependencies/openmpt-trunk/mptrack/Childfrm.cpp
2024-09-24 14:54:57 +02:00

484 lines
13 KiB
C++

/*
* ChildFrm.cpp
* ------------
* Purpose: Implementation of the MDI document child windows.
* Notes : (currently none)
* Authors: OpenMPT Devs
* The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
*/
#include "stdafx.h"
#include <afxpriv.h>
#include "Mptrack.h"
#include "Mainfrm.h"
#include "Childfrm.h"
#include "Moddoc.h"
#include "Globals.h"
#include "View_gen.h"
#include "Ctrl_pat.h"
#include "View_pat.h"
#include "Ctrl_smp.h"
#include "View_smp.h"
#include "Ctrl_ins.h"
#include "View_ins.h"
#include "view_com.h"
#include "Childfrm.h"
#include "ChannelManagerDlg.h"
#include "mpt/io/io.hpp"
#include "mpt/io/io_stdstream.hpp"
#include "../common/FileReader.h"
#include <sstream>
OPENMPT_NAMESPACE_BEGIN
/////////////////////////////////////////////////////////////////////////////
// CChildFrame
IMPLEMENT_DYNCREATE(CChildFrame, CMDIChildWnd)
BEGIN_MESSAGE_MAP(CChildFrame, CMDIChildWnd)
//{{AFX_MSG_MAP(CChildFrame)
ON_WM_DESTROY()
ON_WM_NCACTIVATE()
ON_WM_MDIACTIVATE()
ON_MESSAGE(WM_MOD_CHANGEVIEWCLASS, &CChildFrame::OnChangeViewClass)
ON_MESSAGE(WM_MOD_INSTRSELECTED, &CChildFrame::OnInstrumentSelected)
// toolbar "tooltip" notification
ON_NOTIFY_EX_RANGE(TTN_NEEDTEXT, 0, 0xFFFF, &CChildFrame::OnToolTipText)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
CChildFrame *CChildFrame::m_lastActiveFrame = nullptr;
int CChildFrame::glMdiOpenCount = 0;
/////////////////////////////////////////////////////////////////////////////
// CChildFrame construction/destruction
CChildFrame::CChildFrame()
{
m_bInitialActivation=true; //rewbs.fix3185
m_szCurrentViewClassName[0] = 0;
m_hWndCtrl = m_hWndView = NULL;
m_bMaxWhenClosed = false;
glMdiOpenCount++;
}
CChildFrame::~CChildFrame()
{
if ((--glMdiOpenCount) == 0)
{
TrackerSettings::Instance().gbMdiMaximize = m_bMaxWhenClosed;
}
}
BOOL CChildFrame::OnCreateClient(LPCREATESTRUCT lpcs, CCreateContext* pContext)
{
// create a splitter with 2 rows, 1 column
if (!m_wndSplitter.CreateStatic(this, 2, 1)) return FALSE;
// add the first splitter pane - the default view in row 0
int cy = Util::ScalePixels(TrackerSettings::Instance().glGeneralWindowHeight, m_hWnd); //rewbs.varWindowSize - default to general tab.
if (cy <= 1) cy = (lpcs->cy*2) / 3;
if (!m_wndSplitter.CreateView(0, 0, pContext->m_pNewViewClass, CSize(0, cy), pContext)) return FALSE;
// Get 2nd window handle
CModControlView *pModView;
if ((pModView = GetModControlView()) != nullptr)
{
m_hWndCtrl = pModView->m_hWnd;
pModView->SetMDIParentFrame(m_hWnd);
}
const BOOL bStatus = ChangeViewClass(RUNTIME_CLASS(CViewGlobals), pContext);
// If it all worked, we now have a splitter window which contain two different views
return bStatus;
}
void CChildFrame::SetSplitterHeight(int cy)
{
if (cy <= 1) cy = 188; //default to 188? why not..
m_wndSplitter.SetRowInfo(0, Util::ScalePixels(cy, m_hWnd), 15);
}
BOOL CChildFrame::PreCreateWindow(CREATESTRUCT& cs)
{
return CMDIChildWnd::PreCreateWindow(cs);
}
BOOL CChildFrame::OnNcActivate(BOOL bActivate)
{
if(bActivate && m_hWndView)
{
// Need this in addition to OnMDIActivate when switching from a non-MDI window such as a plugin editor
CMainFrame::GetMainFrame()->SetMidiRecordWnd(m_hWndView);
}
if(m_hWndCtrl)
::SendMessage(m_hWndCtrl, bActivate ? WM_MOD_MDIACTIVATE : WM_MOD_MDIDEACTIVATE, 0, 0);
if(m_hWndView)
::SendMessage(m_hWndView, bActivate ? WM_MOD_MDIACTIVATE : WM_MOD_MDIDEACTIVATE, 0, 0);
return CMDIChildWnd::OnNcActivate(bActivate);
}
void CChildFrame::OnMDIActivate(BOOL bActivate, CWnd *pActivateWnd, CWnd *pDeactivateWnd)
{
CMDIChildWnd::OnMDIActivate(bActivate, pActivateWnd, pDeactivateWnd);
if(bActivate)
{
MPT_ASSERT(pActivateWnd == this);
CMainFrame::GetMainFrame()->UpdateEffectKeys(static_cast<CModDoc *>(GetActiveDocument()));
CMainFrame::GetMainFrame()->SetMidiRecordWnd(m_hWndView);
m_lastActiveFrame = this;
}
if(m_hWndCtrl)
::SendMessage(m_hWndCtrl, bActivate ? WM_MOD_MDIACTIVATE : WM_MOD_MDIDEACTIVATE, 0, 0);
if(m_hWndView)
::SendMessage(m_hWndView, bActivate ? WM_MOD_MDIACTIVATE : WM_MOD_MDIDEACTIVATE, 0, 0);
// Update channel manager according to active document
auto instance = CChannelManagerDlg::sharedInstance();
if(instance != nullptr)
{
if(!bActivate && pActivateWnd == nullptr)
instance->SetDocument(nullptr);
else if(bActivate)
instance->SetDocument(static_cast<CModDoc *>(GetActiveDocument()));
}
}
void CChildFrame::ActivateFrame(int nCmdShow)
{
if ((glMdiOpenCount == 1) && (TrackerSettings::Instance().gbMdiMaximize) && (nCmdShow == -1))
{
nCmdShow = SW_SHOWMAXIMIZED;
}
CMDIChildWnd::ActivateFrame(nCmdShow);
// When song first loads, initialise patternViewState to point to start of song.
CView *pView = GetActiveView();
CModDoc *pModDoc = nullptr;
if (pView) pModDoc = (CModDoc *)pView->GetDocument();
if ((m_hWndCtrl) && (pModDoc))
{
if (m_bInitialActivation && m_ViewPatterns.nPattern == 0)
{
if(!pModDoc->GetSoundFile().Order().empty())
m_ViewPatterns.nPattern = pModDoc->GetSoundFile().Order()[0];
m_bInitialActivation = false;
}
}
}
void CChildFrame::OnUpdateFrameTitle(BOOL bAddToTitle)
{
// update our parent window first
GetMDIFrame()->OnUpdateFrameTitle(bAddToTitle);
if ((GetStyle() & FWS_ADDTOTITLE) == 0) return; // leave child window alone!
CDocument* pDocument = GetActiveDocument();
if (bAddToTitle)
{
CString szText;
if (pDocument == nullptr)
{
szText.Preallocate(m_strTitle.GetLength() + 10);
szText = m_strTitle;
} else
{
szText.Preallocate(pDocument->GetTitle().GetLength() + 10);
szText = pDocument->GetTitle();
if (pDocument->IsModified()) szText += _T("*");
}
if (m_nWindow > 0)
szText.AppendFormat(_T(":%d"), m_nWindow);
// set title if changed, but don't remove completely
AfxSetWindowText(m_hWnd, szText);
}
}
BOOL CChildFrame::ChangeViewClass(CRuntimeClass* pViewClass, CCreateContext* pContext)
{
CMainFrame *pMainFrm = CMainFrame::GetMainFrame();
CWnd *pWnd;
if (!strcmp(pViewClass->m_lpszClassName, m_szCurrentViewClassName)) return TRUE;
if (m_szCurrentViewClassName[0])
{
m_szCurrentViewClassName[0] = 0;
m_wndSplitter.DeleteView(1, 0);
}
if ((m_hWndView) && (pMainFrm))
{
if (pMainFrm->GetMidiRecordWnd() == m_hWndView)
{
pMainFrm->SetMidiRecordWnd(NULL);
}
}
m_hWndView = NULL;
if (!m_wndSplitter.CreateView(1, 0, pViewClass, CSize(0, 0), pContext)) return FALSE;
// Get 2nd window handle
if ((pWnd = m_wndSplitter.GetPane(1, 0)) != NULL) m_hWndView = pWnd->m_hWnd;
strcpy(m_szCurrentViewClassName, pViewClass->m_lpszClassName);
m_wndSplitter.RecalcLayout();
if ((m_hWndView) && (m_hWndCtrl))
{
::PostMessage(m_hWndView, WM_MOD_VIEWMSG, VIEWMSG_SETCTRLWND, (LPARAM)m_hWndCtrl);
::PostMessage(m_hWndCtrl, WM_MOD_CTRLMSG, CTRLMSG_SETVIEWWND, (LPARAM)m_hWndView);
pMainFrm->SetMidiRecordWnd(m_hWndView);
}
return TRUE;
}
void CChildFrame::ForceRefresh()
{
CModControlView *pModView;
if ((pModView = GetModControlView()) != nullptr)
{
pModView->ForceRefresh();
}
return;
}
void CChildFrame::SavePosition(BOOL bForce)
{
if (m_hWnd)
{
m_bMaxWhenClosed = IsZoomed() != FALSE;
if (bForce) TrackerSettings::Instance().gbMdiMaximize = m_bMaxWhenClosed;
if (!IsIconic())
{
CWnd *pWnd = m_wndSplitter.GetPane(0, 0);
if (pWnd)
{
CRect rect(0, 0, 0, 0);
pWnd->GetWindowRect(&rect);
if(rect.Width() == 0)
return;
int l = Util::ScalePixelsInv(rect.Height(), m_hWnd);
//rewbs.varWindowSize - not the nicest piece of code, but we need to distinguish between the views:
if (strcmp(CViewGlobals::classCViewGlobals.m_lpszClassName, m_szCurrentViewClassName) == 0)
TrackerSettings::Instance().glGeneralWindowHeight = l;
else if (strcmp(CViewPattern::classCViewPattern.m_lpszClassName, m_szCurrentViewClassName) == 0)
TrackerSettings::Instance().glPatternWindowHeight = l;
else if (strcmp(CViewSample::classCViewSample.m_lpszClassName, m_szCurrentViewClassName) == 0)
TrackerSettings::Instance().glSampleWindowHeight = l;
else if (strcmp(CViewInstrument::classCViewInstrument.m_lpszClassName, m_szCurrentViewClassName) == 0)
TrackerSettings::Instance().glInstrumentWindowHeight = l;
else if (strcmp(CViewComments::classCViewComments.m_lpszClassName, m_szCurrentViewClassName) == 0)
TrackerSettings::Instance().glCommentsWindowHeight = l;
}
}
}
}
int CChildFrame::GetSplitterHeight()
{
if (m_hWnd)
{
CRect rect;
CWnd *pWnd = m_wndSplitter.GetPane(0, 0);
if (pWnd)
{
pWnd->GetWindowRect(&rect);
return Util::ScalePixelsInv(rect.Height(), m_hWnd);
}
}
return 15; // tidy default
};
LRESULT CChildFrame::SendCtrlMessage(UINT uMsg, LPARAM lParam) const
{
if(m_hWndCtrl)
return ::SendMessage(m_hWndCtrl, WM_MOD_CTRLMSG, uMsg, lParam);
return 0;
}
LRESULT CChildFrame::SendViewMessage(UINT uMsg, LPARAM lParam) const
{
if(m_hWndView)
return ::SendMessage(m_hWndView, WM_MOD_VIEWMSG, uMsg, lParam);
return 0;
}
LRESULT CChildFrame::OnInstrumentSelected(WPARAM wParam, LPARAM lParam)
{
CView *pView = GetActiveView();
CModDoc *pModDoc = NULL;
if (pView) pModDoc = (CModDoc *)pView->GetDocument();
if ((m_hWndCtrl) && (pModDoc))
{
auto nIns = lParam;
if ((!wParam) && (pModDoc->GetNumInstruments() > 0))
{
nIns = pModDoc->FindSampleParent(static_cast<SAMPLEINDEX>(nIns));
if(nIns == INSTRUMENTINDEX_INVALID)
{
nIns = 0;
}
}
::SendMessage(m_hWndCtrl, WM_MOD_CTRLMSG, CTRLMSG_PAT_SETINSTRUMENT, nIns);
}
return 0;
}
/////////////////////////////////////////////////////////////////////////////
// CChildFrame message handlers
void CChildFrame::OnDestroy()
{
SavePosition();
if(m_lastActiveFrame == this)
m_lastActiveFrame = nullptr;
CMDIChildWnd::OnDestroy();
}
BOOL CChildFrame::OnToolTipText(UINT, NMHDR* pNMHDR, LRESULT* pResult)
{
auto pTTT = reinterpret_cast<TOOLTIPTEXT *>(pNMHDR);
TCHAR szFullText[256] = _T("");
CString strTipText;
UINT_PTR nID = pNMHDR->idFrom;
if (pTTT->uFlags & TTF_IDISHWND)
{
// idFrom is actually the HWND of the tool
nID = static_cast<UINT_PTR>(::GetDlgCtrlID(reinterpret_cast<HWND>(nID)));
}
if ((nID >= 1000) && (nID < 65536) && (m_hWndCtrl) && (::SendMessage(m_hWndCtrl, WM_MOD_GETTOOLTIPTEXT, nID, (LPARAM)szFullText)))
{
strTipText = szFullText;
} else
{
// allow top level routing frame to handle the message
if (GetRoutingFrame() != NULL) return FALSE;
if (nID != 0) // will be zero on a separator
{
AfxLoadString((UINT)nID, szFullText);
// this is the command id, not the button index
AfxExtractSubString(strTipText, szFullText, 1, _T('\n'));
}
}
mpt::String::WriteCStringBuf(pTTT->szText) = strTipText;
*pResult = 0;
// bring the tooltip window above other popup windows
::SetWindowPos(pNMHDR->hwndFrom, HWND_TOP, 0, 0, 0, 0,
SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOOWNERZORDER);
return TRUE; // message was handled
}
LRESULT CChildFrame::OnChangeViewClass(WPARAM wParam, LPARAM lParam)
{
CModControlDlg *pDlg = (CModControlDlg *)lParam;
if (pDlg)
{
CRuntimeClass *pNewViewClass = pDlg->GetAssociatedViewClass();
if (pNewViewClass) ChangeViewClass(pNewViewClass);
::PostMessage(m_hWndCtrl, WM_MOD_CTRLMSG, CTRLMSG_ACTIVATEPAGE, (LPARAM)wParam);
}
return 0;
}
const char *CChildFrame::GetCurrentViewClassName() const
{
return m_szCurrentViewClassName;
}
std::string CChildFrame::SerializeView() const
{
std::ostringstream f(std::ios::out | std::ios::binary);
// Version
mpt::IO::WriteVarInt(f, 0u);
// Current page
mpt::IO::WriteVarInt(f, static_cast<uint8>(GetModControlView()->GetActivePage()));
CModControlView *view = GetModControlView();
if (strcmp(CViewPattern::classCViewPattern.m_lpszClassName, m_szCurrentViewClassName) == 0)
{
mpt::IO::WriteVarInt(f, (uint32)view->SendMessage(WM_MOD_CTRLMSG, CTRLMSG_GETCURRENTORDER)); // Order number
} else if (strcmp(CViewSample::classCViewSample.m_lpszClassName, m_szCurrentViewClassName) == 0)
{
mpt::IO::WriteVarInt(f, (uint32)view->SendMessage(WM_MOD_CTRLMSG, CTRLMSG_GETCURRENTINSTRUMENT)); // Sample number
} else if (strcmp(CViewInstrument::classCViewInstrument.m_lpszClassName, m_szCurrentViewClassName) == 0)
{
mpt::IO::WriteVarInt(f, (uint32)view->SendMessage(WM_MOD_CTRLMSG, CTRLMSG_GETCURRENTINSTRUMENT)); // Instrument number
}
return f.str();
}
void CChildFrame::DeserializeView(FileReader &file)
{
uint32 version, page;
if(file.ReadVarInt(version) && version == 0 &&
file.ReadVarInt(page) && page >= 0 && page < CModControlView::MAX_PAGES)
{
UINT pageDlg = 0;
switch(page)
{
case CModControlView::VIEW_GLOBALS:
pageDlg = IDD_CONTROL_GLOBALS;
break;
case CModControlView::VIEW_PATTERNS:
pageDlg = IDD_CONTROL_PATTERNS;
file.ReadVarInt(m_ViewPatterns.initialOrder);
break;
case CModControlView::VIEW_SAMPLES:
pageDlg = IDD_CONTROL_SAMPLES;
file.ReadVarInt(m_ViewSamples.initialSample);
break;
case CModControlView::VIEW_INSTRUMENTS:
pageDlg = IDD_CONTROL_INSTRUMENTS;
file.ReadVarInt(m_ViewInstruments.initialInstrument);
break;
case CModControlView::VIEW_COMMENTS:
pageDlg = IDD_CONTROL_COMMENTS;
break;
}
GetModControlView()->PostMessage(WM_MOD_ACTIVATEVIEW, pageDlg, (LPARAM)-1);
}
}
void CChildFrame::ToggleViews()
{
auto focus = ::GetFocus();
if(focus == GetHwndView() || ::IsChild(GetHwndView(), focus))
SendCtrlMessage(CTRLMSG_SETFOCUS);
else if(focus == GetHwndCtrl() || ::IsChild(GetHwndCtrl(), focus))
SendViewMessage(VIEWMSG_SETFOCUS);
}
OPENMPT_NAMESPACE_END