1
0

Childfrm.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483
  1. /*
  2. * ChildFrm.cpp
  3. * ------------
  4. * Purpose: Implementation of the MDI document child windows.
  5. * Notes : (currently none)
  6. * Authors: OpenMPT Devs
  7. * The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
  8. */
  9. #include "stdafx.h"
  10. #include <afxpriv.h>
  11. #include "Mptrack.h"
  12. #include "Mainfrm.h"
  13. #include "Childfrm.h"
  14. #include "Moddoc.h"
  15. #include "Globals.h"
  16. #include "View_gen.h"
  17. #include "Ctrl_pat.h"
  18. #include "View_pat.h"
  19. #include "Ctrl_smp.h"
  20. #include "View_smp.h"
  21. #include "Ctrl_ins.h"
  22. #include "View_ins.h"
  23. #include "view_com.h"
  24. #include "Childfrm.h"
  25. #include "ChannelManagerDlg.h"
  26. #include "mpt/io/io.hpp"
  27. #include "mpt/io/io_stdstream.hpp"
  28. #include "../common/FileReader.h"
  29. #include <sstream>
  30. OPENMPT_NAMESPACE_BEGIN
  31. /////////////////////////////////////////////////////////////////////////////
  32. // CChildFrame
  33. IMPLEMENT_DYNCREATE(CChildFrame, CMDIChildWnd)
  34. BEGIN_MESSAGE_MAP(CChildFrame, CMDIChildWnd)
  35. //{{AFX_MSG_MAP(CChildFrame)
  36. ON_WM_DESTROY()
  37. ON_WM_NCACTIVATE()
  38. ON_WM_MDIACTIVATE()
  39. ON_MESSAGE(WM_MOD_CHANGEVIEWCLASS, &CChildFrame::OnChangeViewClass)
  40. ON_MESSAGE(WM_MOD_INSTRSELECTED, &CChildFrame::OnInstrumentSelected)
  41. // toolbar "tooltip" notification
  42. ON_NOTIFY_EX_RANGE(TTN_NEEDTEXT, 0, 0xFFFF, &CChildFrame::OnToolTipText)
  43. //}}AFX_MSG_MAP
  44. END_MESSAGE_MAP()
  45. CChildFrame *CChildFrame::m_lastActiveFrame = nullptr;
  46. int CChildFrame::glMdiOpenCount = 0;
  47. /////////////////////////////////////////////////////////////////////////////
  48. // CChildFrame construction/destruction
  49. CChildFrame::CChildFrame()
  50. {
  51. m_bInitialActivation=true; //rewbs.fix3185
  52. m_szCurrentViewClassName[0] = 0;
  53. m_hWndCtrl = m_hWndView = NULL;
  54. m_bMaxWhenClosed = false;
  55. glMdiOpenCount++;
  56. }
  57. CChildFrame::~CChildFrame()
  58. {
  59. if ((--glMdiOpenCount) == 0)
  60. {
  61. TrackerSettings::Instance().gbMdiMaximize = m_bMaxWhenClosed;
  62. }
  63. }
  64. BOOL CChildFrame::OnCreateClient(LPCREATESTRUCT lpcs, CCreateContext* pContext)
  65. {
  66. // create a splitter with 2 rows, 1 column
  67. if (!m_wndSplitter.CreateStatic(this, 2, 1)) return FALSE;
  68. // add the first splitter pane - the default view in row 0
  69. int cy = Util::ScalePixels(TrackerSettings::Instance().glGeneralWindowHeight, m_hWnd); //rewbs.varWindowSize - default to general tab.
  70. if (cy <= 1) cy = (lpcs->cy*2) / 3;
  71. if (!m_wndSplitter.CreateView(0, 0, pContext->m_pNewViewClass, CSize(0, cy), pContext)) return FALSE;
  72. // Get 2nd window handle
  73. CModControlView *pModView;
  74. if ((pModView = GetModControlView()) != nullptr)
  75. {
  76. m_hWndCtrl = pModView->m_hWnd;
  77. pModView->SetMDIParentFrame(m_hWnd);
  78. }
  79. const BOOL bStatus = ChangeViewClass(RUNTIME_CLASS(CViewGlobals), pContext);
  80. // If it all worked, we now have a splitter window which contain two different views
  81. return bStatus;
  82. }
  83. void CChildFrame::SetSplitterHeight(int cy)
  84. {
  85. if (cy <= 1) cy = 188; //default to 188? why not..
  86. m_wndSplitter.SetRowInfo(0, Util::ScalePixels(cy, m_hWnd), 15);
  87. }
  88. BOOL CChildFrame::PreCreateWindow(CREATESTRUCT& cs)
  89. {
  90. return CMDIChildWnd::PreCreateWindow(cs);
  91. }
  92. BOOL CChildFrame::OnNcActivate(BOOL bActivate)
  93. {
  94. if(bActivate && m_hWndView)
  95. {
  96. // Need this in addition to OnMDIActivate when switching from a non-MDI window such as a plugin editor
  97. CMainFrame::GetMainFrame()->SetMidiRecordWnd(m_hWndView);
  98. }
  99. if(m_hWndCtrl)
  100. ::SendMessage(m_hWndCtrl, bActivate ? WM_MOD_MDIACTIVATE : WM_MOD_MDIDEACTIVATE, 0, 0);
  101. if(m_hWndView)
  102. ::SendMessage(m_hWndView, bActivate ? WM_MOD_MDIACTIVATE : WM_MOD_MDIDEACTIVATE, 0, 0);
  103. return CMDIChildWnd::OnNcActivate(bActivate);
  104. }
  105. void CChildFrame::OnMDIActivate(BOOL bActivate, CWnd *pActivateWnd, CWnd *pDeactivateWnd)
  106. {
  107. CMDIChildWnd::OnMDIActivate(bActivate, pActivateWnd, pDeactivateWnd);
  108. if(bActivate)
  109. {
  110. MPT_ASSERT(pActivateWnd == this);
  111. CMainFrame::GetMainFrame()->UpdateEffectKeys(static_cast<CModDoc *>(GetActiveDocument()));
  112. CMainFrame::GetMainFrame()->SetMidiRecordWnd(m_hWndView);
  113. m_lastActiveFrame = this;
  114. }
  115. if(m_hWndCtrl)
  116. ::SendMessage(m_hWndCtrl, bActivate ? WM_MOD_MDIACTIVATE : WM_MOD_MDIDEACTIVATE, 0, 0);
  117. if(m_hWndView)
  118. ::SendMessage(m_hWndView, bActivate ? WM_MOD_MDIACTIVATE : WM_MOD_MDIDEACTIVATE, 0, 0);
  119. // Update channel manager according to active document
  120. auto instance = CChannelManagerDlg::sharedInstance();
  121. if(instance != nullptr)
  122. {
  123. if(!bActivate && pActivateWnd == nullptr)
  124. instance->SetDocument(nullptr);
  125. else if(bActivate)
  126. instance->SetDocument(static_cast<CModDoc *>(GetActiveDocument()));
  127. }
  128. }
  129. void CChildFrame::ActivateFrame(int nCmdShow)
  130. {
  131. if ((glMdiOpenCount == 1) && (TrackerSettings::Instance().gbMdiMaximize) && (nCmdShow == -1))
  132. {
  133. nCmdShow = SW_SHOWMAXIMIZED;
  134. }
  135. CMDIChildWnd::ActivateFrame(nCmdShow);
  136. // When song first loads, initialise patternViewState to point to start of song.
  137. CView *pView = GetActiveView();
  138. CModDoc *pModDoc = nullptr;
  139. if (pView) pModDoc = (CModDoc *)pView->GetDocument();
  140. if ((m_hWndCtrl) && (pModDoc))
  141. {
  142. if (m_bInitialActivation && m_ViewPatterns.nPattern == 0)
  143. {
  144. if(!pModDoc->GetSoundFile().Order().empty())
  145. m_ViewPatterns.nPattern = pModDoc->GetSoundFile().Order()[0];
  146. m_bInitialActivation = false;
  147. }
  148. }
  149. }
  150. void CChildFrame::OnUpdateFrameTitle(BOOL bAddToTitle)
  151. {
  152. // update our parent window first
  153. GetMDIFrame()->OnUpdateFrameTitle(bAddToTitle);
  154. if ((GetStyle() & FWS_ADDTOTITLE) == 0) return; // leave child window alone!
  155. CDocument* pDocument = GetActiveDocument();
  156. if (bAddToTitle)
  157. {
  158. CString szText;
  159. if (pDocument == nullptr)
  160. {
  161. szText.Preallocate(m_strTitle.GetLength() + 10);
  162. szText = m_strTitle;
  163. } else
  164. {
  165. szText.Preallocate(pDocument->GetTitle().GetLength() + 10);
  166. szText = pDocument->GetTitle();
  167. if (pDocument->IsModified()) szText += _T("*");
  168. }
  169. if (m_nWindow > 0)
  170. szText.AppendFormat(_T(":%d"), m_nWindow);
  171. // set title if changed, but don't remove completely
  172. AfxSetWindowText(m_hWnd, szText);
  173. }
  174. }
  175. BOOL CChildFrame::ChangeViewClass(CRuntimeClass* pViewClass, CCreateContext* pContext)
  176. {
  177. CMainFrame *pMainFrm = CMainFrame::GetMainFrame();
  178. CWnd *pWnd;
  179. if (!strcmp(pViewClass->m_lpszClassName, m_szCurrentViewClassName)) return TRUE;
  180. if (m_szCurrentViewClassName[0])
  181. {
  182. m_szCurrentViewClassName[0] = 0;
  183. m_wndSplitter.DeleteView(1, 0);
  184. }
  185. if ((m_hWndView) && (pMainFrm))
  186. {
  187. if (pMainFrm->GetMidiRecordWnd() == m_hWndView)
  188. {
  189. pMainFrm->SetMidiRecordWnd(NULL);
  190. }
  191. }
  192. m_hWndView = NULL;
  193. if (!m_wndSplitter.CreateView(1, 0, pViewClass, CSize(0, 0), pContext)) return FALSE;
  194. // Get 2nd window handle
  195. if ((pWnd = m_wndSplitter.GetPane(1, 0)) != NULL) m_hWndView = pWnd->m_hWnd;
  196. strcpy(m_szCurrentViewClassName, pViewClass->m_lpszClassName);
  197. m_wndSplitter.RecalcLayout();
  198. if ((m_hWndView) && (m_hWndCtrl))
  199. {
  200. ::PostMessage(m_hWndView, WM_MOD_VIEWMSG, VIEWMSG_SETCTRLWND, (LPARAM)m_hWndCtrl);
  201. ::PostMessage(m_hWndCtrl, WM_MOD_CTRLMSG, CTRLMSG_SETVIEWWND, (LPARAM)m_hWndView);
  202. pMainFrm->SetMidiRecordWnd(m_hWndView);
  203. }
  204. return TRUE;
  205. }
  206. void CChildFrame::ForceRefresh()
  207. {
  208. CModControlView *pModView;
  209. if ((pModView = GetModControlView()) != nullptr)
  210. {
  211. pModView->ForceRefresh();
  212. }
  213. return;
  214. }
  215. void CChildFrame::SavePosition(BOOL bForce)
  216. {
  217. if (m_hWnd)
  218. {
  219. m_bMaxWhenClosed = IsZoomed() != FALSE;
  220. if (bForce) TrackerSettings::Instance().gbMdiMaximize = m_bMaxWhenClosed;
  221. if (!IsIconic())
  222. {
  223. CWnd *pWnd = m_wndSplitter.GetPane(0, 0);
  224. if (pWnd)
  225. {
  226. CRect rect(0, 0, 0, 0);
  227. pWnd->GetWindowRect(&rect);
  228. if(rect.Width() == 0)
  229. return;
  230. int l = Util::ScalePixelsInv(rect.Height(), m_hWnd);
  231. //rewbs.varWindowSize - not the nicest piece of code, but we need to distinguish between the views:
  232. if (strcmp(CViewGlobals::classCViewGlobals.m_lpszClassName, m_szCurrentViewClassName) == 0)
  233. TrackerSettings::Instance().glGeneralWindowHeight = l;
  234. else if (strcmp(CViewPattern::classCViewPattern.m_lpszClassName, m_szCurrentViewClassName) == 0)
  235. TrackerSettings::Instance().glPatternWindowHeight = l;
  236. else if (strcmp(CViewSample::classCViewSample.m_lpszClassName, m_szCurrentViewClassName) == 0)
  237. TrackerSettings::Instance().glSampleWindowHeight = l;
  238. else if (strcmp(CViewInstrument::classCViewInstrument.m_lpszClassName, m_szCurrentViewClassName) == 0)
  239. TrackerSettings::Instance().glInstrumentWindowHeight = l;
  240. else if (strcmp(CViewComments::classCViewComments.m_lpszClassName, m_szCurrentViewClassName) == 0)
  241. TrackerSettings::Instance().glCommentsWindowHeight = l;
  242. }
  243. }
  244. }
  245. }
  246. int CChildFrame::GetSplitterHeight()
  247. {
  248. if (m_hWnd)
  249. {
  250. CRect rect;
  251. CWnd *pWnd = m_wndSplitter.GetPane(0, 0);
  252. if (pWnd)
  253. {
  254. pWnd->GetWindowRect(&rect);
  255. return Util::ScalePixelsInv(rect.Height(), m_hWnd);
  256. }
  257. }
  258. return 15; // tidy default
  259. };
  260. LRESULT CChildFrame::SendCtrlMessage(UINT uMsg, LPARAM lParam) const
  261. {
  262. if(m_hWndCtrl)
  263. return ::SendMessage(m_hWndCtrl, WM_MOD_CTRLMSG, uMsg, lParam);
  264. return 0;
  265. }
  266. LRESULT CChildFrame::SendViewMessage(UINT uMsg, LPARAM lParam) const
  267. {
  268. if(m_hWndView)
  269. return ::SendMessage(m_hWndView, WM_MOD_VIEWMSG, uMsg, lParam);
  270. return 0;
  271. }
  272. LRESULT CChildFrame::OnInstrumentSelected(WPARAM wParam, LPARAM lParam)
  273. {
  274. CView *pView = GetActiveView();
  275. CModDoc *pModDoc = NULL;
  276. if (pView) pModDoc = (CModDoc *)pView->GetDocument();
  277. if ((m_hWndCtrl) && (pModDoc))
  278. {
  279. auto nIns = lParam;
  280. if ((!wParam) && (pModDoc->GetNumInstruments() > 0))
  281. {
  282. nIns = pModDoc->FindSampleParent(static_cast<SAMPLEINDEX>(nIns));
  283. if(nIns == INSTRUMENTINDEX_INVALID)
  284. {
  285. nIns = 0;
  286. }
  287. }
  288. ::SendMessage(m_hWndCtrl, WM_MOD_CTRLMSG, CTRLMSG_PAT_SETINSTRUMENT, nIns);
  289. }
  290. return 0;
  291. }
  292. /////////////////////////////////////////////////////////////////////////////
  293. // CChildFrame message handlers
  294. void CChildFrame::OnDestroy()
  295. {
  296. SavePosition();
  297. if(m_lastActiveFrame == this)
  298. m_lastActiveFrame = nullptr;
  299. CMDIChildWnd::OnDestroy();
  300. }
  301. BOOL CChildFrame::OnToolTipText(UINT, NMHDR* pNMHDR, LRESULT* pResult)
  302. {
  303. auto pTTT = reinterpret_cast<TOOLTIPTEXT *>(pNMHDR);
  304. TCHAR szFullText[256] = _T("");
  305. CString strTipText;
  306. UINT_PTR nID = pNMHDR->idFrom;
  307. if (pTTT->uFlags & TTF_IDISHWND)
  308. {
  309. // idFrom is actually the HWND of the tool
  310. nID = static_cast<UINT_PTR>(::GetDlgCtrlID(reinterpret_cast<HWND>(nID)));
  311. }
  312. if ((nID >= 1000) && (nID < 65536) && (m_hWndCtrl) && (::SendMessage(m_hWndCtrl, WM_MOD_GETTOOLTIPTEXT, nID, (LPARAM)szFullText)))
  313. {
  314. strTipText = szFullText;
  315. } else
  316. {
  317. // allow top level routing frame to handle the message
  318. if (GetRoutingFrame() != NULL) return FALSE;
  319. if (nID != 0) // will be zero on a separator
  320. {
  321. AfxLoadString((UINT)nID, szFullText);
  322. // this is the command id, not the button index
  323. AfxExtractSubString(strTipText, szFullText, 1, _T('\n'));
  324. }
  325. }
  326. mpt::String::WriteCStringBuf(pTTT->szText) = strTipText;
  327. *pResult = 0;
  328. // bring the tooltip window above other popup windows
  329. ::SetWindowPos(pNMHDR->hwndFrom, HWND_TOP, 0, 0, 0, 0,
  330. SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOOWNERZORDER);
  331. return TRUE; // message was handled
  332. }
  333. LRESULT CChildFrame::OnChangeViewClass(WPARAM wParam, LPARAM lParam)
  334. {
  335. CModControlDlg *pDlg = (CModControlDlg *)lParam;
  336. if (pDlg)
  337. {
  338. CRuntimeClass *pNewViewClass = pDlg->GetAssociatedViewClass();
  339. if (pNewViewClass) ChangeViewClass(pNewViewClass);
  340. ::PostMessage(m_hWndCtrl, WM_MOD_CTRLMSG, CTRLMSG_ACTIVATEPAGE, (LPARAM)wParam);
  341. }
  342. return 0;
  343. }
  344. const char *CChildFrame::GetCurrentViewClassName() const
  345. {
  346. return m_szCurrentViewClassName;
  347. }
  348. std::string CChildFrame::SerializeView() const
  349. {
  350. std::ostringstream f(std::ios::out | std::ios::binary);
  351. // Version
  352. mpt::IO::WriteVarInt(f, 0u);
  353. // Current page
  354. mpt::IO::WriteVarInt(f, static_cast<uint8>(GetModControlView()->GetActivePage()));
  355. CModControlView *view = GetModControlView();
  356. if (strcmp(CViewPattern::classCViewPattern.m_lpszClassName, m_szCurrentViewClassName) == 0)
  357. {
  358. mpt::IO::WriteVarInt(f, (uint32)view->SendMessage(WM_MOD_CTRLMSG, CTRLMSG_GETCURRENTORDER)); // Order number
  359. } else if (strcmp(CViewSample::classCViewSample.m_lpszClassName, m_szCurrentViewClassName) == 0)
  360. {
  361. mpt::IO::WriteVarInt(f, (uint32)view->SendMessage(WM_MOD_CTRLMSG, CTRLMSG_GETCURRENTINSTRUMENT)); // Sample number
  362. } else if (strcmp(CViewInstrument::classCViewInstrument.m_lpszClassName, m_szCurrentViewClassName) == 0)
  363. {
  364. mpt::IO::WriteVarInt(f, (uint32)view->SendMessage(WM_MOD_CTRLMSG, CTRLMSG_GETCURRENTINSTRUMENT)); // Instrument number
  365. }
  366. return f.str();
  367. }
  368. void CChildFrame::DeserializeView(FileReader &file)
  369. {
  370. uint32 version, page;
  371. if(file.ReadVarInt(version) && version == 0 &&
  372. file.ReadVarInt(page) && page >= 0 && page < CModControlView::MAX_PAGES)
  373. {
  374. UINT pageDlg = 0;
  375. switch(page)
  376. {
  377. case CModControlView::VIEW_GLOBALS:
  378. pageDlg = IDD_CONTROL_GLOBALS;
  379. break;
  380. case CModControlView::VIEW_PATTERNS:
  381. pageDlg = IDD_CONTROL_PATTERNS;
  382. file.ReadVarInt(m_ViewPatterns.initialOrder);
  383. break;
  384. case CModControlView::VIEW_SAMPLES:
  385. pageDlg = IDD_CONTROL_SAMPLES;
  386. file.ReadVarInt(m_ViewSamples.initialSample);
  387. break;
  388. case CModControlView::VIEW_INSTRUMENTS:
  389. pageDlg = IDD_CONTROL_INSTRUMENTS;
  390. file.ReadVarInt(m_ViewInstruments.initialInstrument);
  391. break;
  392. case CModControlView::VIEW_COMMENTS:
  393. pageDlg = IDD_CONTROL_COMMENTS;
  394. break;
  395. }
  396. GetModControlView()->PostMessage(WM_MOD_ACTIVATEVIEW, pageDlg, (LPARAM)-1);
  397. }
  398. }
  399. void CChildFrame::ToggleViews()
  400. {
  401. auto focus = ::GetFocus();
  402. if(focus == GetHwndView() || ::IsChild(GetHwndView(), focus))
  403. SendCtrlMessage(CTRLMSG_SETFOCUS);
  404. else if(focus == GetHwndCtrl() || ::IsChild(GetHwndCtrl(), focus))
  405. SendViewMessage(VIEWMSG_SETFOCUS);
  406. }
  407. OPENMPT_NAMESPACE_END