#include "precomp.h" #define __MARS_INLINE_FAST_IS_EQUAL_GUID #include "mcinc.h" #include "marswin.h" #include #include "parser\marsload.h" #include "panel.h" #include "place.h" #include // CLASS_CMarsWindow = {172AF160-5CD4-11d3-97FA-00C04F45D0B3} const GUID CLASS_CMarsWindow = { 0x172af160, 0x5cd4, 0x11d3, { 0x97, 0xfa, 0x0, 0xc0, 0x4f, 0x45, 0xd0, 0xb3 } }; // CLASS_CMarsDocument = {E0C4E3A8-20D6-47d6-87FB-0A43452117BA} const GUID CLASS_CMarsDocument = { 0xe0c4e3a8, 0x20d6, 0x47d6, { 0x87, 0xfb, 0xa, 0x43, 0x45, 0x21, 0x17, 0xba } }; #define WZ_WINDOWPLACEMENT L"WindowPlacement\\%d_%d_%s" #define WZ_POSITIONMAX L"Maximized" #define WZ_POSITIONRECT L"Rect" static void combineMin( long& out, long in1, long in2 ) { if(in1 > 0) { out = (in2 > 0) ? in2 + in1 : in1; } else { out = in2; } } static void combineMax( long& out, long in1, long in2 ) { if(in1 < 0 || in2 < 0) { out = -1; // Don't care... } else { out = in2 + in1; } } static void selectMin( long& out, long in1, long in2 ) { if(in1 < 0) { out = in2; } else if(in2 < 0) { out = in1; } else { out = max( in1, in2 ); } } static void selectMax( long& out, long in1, long in2 ) { if(in1 < 0) { out = in2; } else if(in2 < 0) { out = in1; } else { out = min( in1, in2 ); } } static BOOL WriteWindowPosition(CRegistryKey ®key, RECT *prc, BOOL fMaximized) { return ERROR_SUCCESS == regkey.SetBoolValue(fMaximized, WZ_POSITIONMAX) && ERROR_SUCCESS == regkey.SetBinaryValue(prc, sizeof(*prc), WZ_POSITIONRECT); } //================================================================== // // CMarsDocument implementation // //================================================================== CMarsDocument::CMarsDocument() { } CMarsDocument::~CMarsDocument() { } HRESULT CMarsWindow::Passivate() { return CMarsComObject::Passivate(); } HRESULT CMarsDocument::DoPassivate() { m_spPanels.PassivateAndRelease(); m_spPlaces.PassivateAndRelease(); m_spMarsWindow.Release(); m_spHostPanel.Release(); m_cwndDocument.Detach(); return S_OK; } IMPLEMENT_ADDREF_RELEASE(CMarsDocument); STDMETHODIMP CMarsDocument::QueryInterface(REFIID iid, void **ppvObject) { HRESULT hr; ATLASSERT(ppvObject); if(iid == IID_IUnknown || iid == IID_IServiceProvider ) { *ppvObject = SAFECAST(this, IServiceProvider *); } else if(iid == CLASS_CMarsDocument) { *ppvObject = SAFECAST(this, CMarsDocument *); } else { *ppvObject = NULL; } if(*ppvObject) { AddRef(); hr = S_OK; } else { hr = E_NOINTERFACE; } return hr; } HRESULT CMarsDocument::Init(CMarsWindow *pMarsWindow, CMarsPanel *pHostPanel) { ATLASSERT(pMarsWindow); m_spMarsWindow = pMarsWindow; m_spHostPanel = pHostPanel; if(pHostPanel) { m_cwndDocument.Attach(m_spHostPanel->Window()->m_hWnd); } else { m_cwndDocument.Attach(m_spMarsWindow->m_hWnd); } m_spPlaces.Attach(new CPlaceCollection(this)); m_spPanels.Attach(new CPanelCollection(this)); return (m_spMarsWindow && m_spPanels && m_spPlaces) ? S_OK : E_FAIL; } // static HRESULT CMarsDocument::CreateInstance(CMarsWindow *pMarsWindow, CMarsPanel *pHostPanel, CMarsDocument **ppObj) { ATLASSERT(pMarsWindow && ppObj && (*ppObj==NULL)); *ppObj=NULL; if(pMarsWindow) { CMarsDocument *pDoc; pDoc = new CMarsDocument(); if(pDoc) { if(SUCCEEDED(pDoc->Init(pMarsWindow, pHostPanel))) { *ppObj = pDoc; } else { pDoc->Passivate(); pDoc->Release(); } } } return (*ppObj) ? S_OK : E_FAIL; } // IServiceProvider HRESULT CMarsDocument::QueryService(REFGUID guidService, REFIID riid, void **ppv) { HRESULT hr = E_FAIL; *ppv = NULL; if(!IsPassive()) { if(guidService == SID_SMarsDocument) { hr = QueryInterface(riid, ppv); } } return hr; } HRESULT CMarsDocument::GetPlaces(IMarsPlaceCollection **ppPlaces) { ATLASSERT(ppPlaces); ATLASSERT(!IsPassive()); if(m_spPlaces) { return m_spPlaces.QueryInterface(ppPlaces); } *ppPlaces = NULL; return E_FAIL; } HRESULT CMarsDocument::ReadPanelDefinition(LPCWSTR pwszUrl) { FAIL_AFTER_PASSIVATE(); HRESULT hr; GetPanels()->lockLayout(); if(pwszUrl) { hr = CMMFParser::MMFToMars(pwszUrl, this); } else { hr = E_FAIL; } GetPanels()->unlockLayout(); return hr; } void CMarsDocument::ForwardMessageToChildren(UINT uMsg, WPARAM wParam, LPARAM lParam) { CPanelCollection* panels = GetPanels(); if(panels) { for(int i=0; i < panels->GetSize(); i++) { CMarsPanel* pPanel = (*panels)[i]; if(pPanel) pPanel->ForwardMessage(uMsg, wParam, lParam); } } } //================================================================== // // CMarsWindow implementation // //================================================================== CMarsWindow::CMarsWindow() { m_fShowTitleBar = TRUE; m_fEnableModeless = TRUE; m_fLayoutLocked = FALSE; } CMarsWindow::~CMarsWindow() { if(m_hAccel) { DestroyAcceleratorTable(m_hAccel); } } HRESULT CMarsWindow::DoPassivate() { (void)NotifyHost(MARSHOST_ON_WIN_PASSIVATE, SAFECAST(this, IMarsWindowOM *), 0); CMarsDocument::DoPassivate(); if(IsWindow()) { DestroyWindow(); } m_spMarsHost.Release(); return S_OK; } IMPLEMENT_ADDREF_RELEASE(CMarsWindow); STDMETHODIMP CMarsWindow::QueryInterface(REFIID iid, void **ppvObject) { HRESULT hr; ATLASSERT(ppvObject); if(iid == IID_IMarsWindowOM || iid == IID_IDispatch || iid == IID_IUnknown ) { *ppvObject = SAFECAST(this, IMarsWindowOM *); } else if(iid == IID_IOleWindow || iid == IID_IOleInPlaceUIWindow || iid == IID_IOleInPlaceFrame ) { *ppvObject = SAFECAST(this, IOleInPlaceFrame *); } else if(iid == IID_IServiceProvider) { *ppvObject = SAFECAST(this, IServiceProvider *); } else if(iid == IID_IProfferService) { *ppvObject = SAFECAST(this, IProfferService *); } else if(iid == CLASS_CMarsWindow) { *ppvObject = SAFECAST(this, CMarsWindow *); } else { *ppvObject = NULL; } if(*ppvObject) { AddRef(); hr = S_OK; } else { hr = CMarsDocument::QueryInterface(iid, ppvObject); } return hr; } // // Static creation function // HRESULT CMarsWindow::CreateInstance(IMarsHost *pMarsHost, MARSTHREADPARAM *pThreadParam, CMarsWindow **ppObj) { ATLASSERT(pThreadParam && ppObj && (*ppObj==NULL)); *ppObj=NULL; if(pThreadParam) { CComClassPtr spWin; spWin.Attach(new CMarsWindow()); if(spWin) { if(pThreadParam->pwszFirstPlace) { spWin->m_bstrFirstPlace = pThreadParam->pwszFirstPlace; } if(SUCCEEDED(spWin->Init(pMarsHost, pThreadParam)) && SUCCEEDED(spWin->Startup() ) ) { *ppObj = spWin.Detach(); } else { spWin.PassivateAndRelease(); } } } return (*ppObj) ? S_OK : E_FAIL; } HRESULT CMarsWindow::Init(IMarsHost *pMarsHost, MARSTHREADPARAM *pThreadParam) { HRESULT hr; m_pThreadParam = pThreadParam; m_spMarsHost = pMarsHost; Create(NULL, rcDefault, GetThreadParam()->pwszTitle, WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN ); hr = CMarsDocument::Init(this, NULL); if(SUCCEEDED(hr)) { CGlobalSettingsRegKey regkey; WINDOWPLACEMENT wp; wp.length = sizeof(wp); BOOL fMaximized; GetWindowPlacement( &wp ); if(GetThreadParam()->dwFlags & MTF_MANAGE_WINDOW_SIZE) { if(InitWindowPosition( regkey, FALSE )) { LoadWindowPosition( regkey, TRUE, wp, fMaximized ); } } if(SUCCEEDED(NotifyHost( MARSHOST_ON_WIN_SETPOS, SAFECAST(this, IMarsWindowOM *), (LPARAM)&wp ))) { // Always make sure the window is fully on-screen BoundWindowRectToMonitor( m_hWnd, &wp.rcNormalPosition ); } if(wp.showCmd == SW_MAXIMIZE) { m_fStartMaximized = true; } wp.showCmd = SW_HIDE; //// if(GetThreadParam()->dwFlags & MTF_DONT_SHOW_WINDOW) //// { //// wp.showCmd = SW_HIDE; //// } if(GetThreadParam()->dwFlags & MTF_MANAGE_WINDOW_SIZE) { // Make the next Mars window try and appear at the current location. WriteWindowPosition( regkey, &wp.rcNormalPosition, fMaximized ); } SetWindowPlacement( &wp ); } if(SUCCEEDED(hr)) { hr = NotifyHost( MARSHOST_ON_WIN_INIT, SAFECAST(this, IMarsWindowOM *), (LPARAM)m_hWnd ); } if(SUCCEEDED(hr)) { hr = ReadPanelDefinition(GetThreadParam()->pwszPanelURL); } return hr; } HRESULT CMarsWindow::Startup() { HRESULT hr; if(SUCCEEDED(hr = NotifyHost( MARSHOST_ON_WIN_READY, SAFECAST(this, IMarsWindowOM *), 0 ))) { if(hr == S_FALSE) { ; // Host has taken care of the startup. } else { CComClassPtr spPlace; if(SUCCEEDED(hr = GetPlaces()->GetPlace( m_bstrFirstPlace, &spPlace ))) { hr = spPlace->transitionTo(); } } } return hr; } // IServiceProvider HRESULT CMarsWindow::QueryService(REFGUID guidService, REFIID riid, void **ppv) { HRESULT hr = E_FAIL; *ppv = NULL; if(!IsPassive()) { if(guidService == SID_SProfferService || guidService == SID_SMarsWindow || guidService == SID_STopWindow ) { hr = QueryInterface(riid, ppv); } else { hr = IProfferServiceImpl::QueryService(guidService, riid, ppv); if(FAILED(hr)) { hr = CMarsDocument::QueryService(guidService, riid, ppv); if(FAILED(hr)) { hr = IUnknown_QueryService(m_spMarsHost, guidService, riid, ppv); } } } } return hr; } // IMarsWindowOM STDMETHODIMP CMarsWindow::get_active(VARIANT_BOOL *pbActive) { HRESULT hr = E_INVALIDARG; if(API_IsValidWritePtr( pbActive )) { if(VerifyNotPassive( &hr )) { *pbActive = VARIANT_BOOLIFY(IsWindowActive()); hr = S_OK; } else { *pbActive = VARIANT_FALSE; } } return hr; } STDMETHODIMP CMarsWindow::get_minimized(VARIANT_BOOL *pbMinimized) { HRESULT hr = E_INVALIDARG; if(API_IsValidWritePtr( pbMinimized )) { if(VerifyNotPassive( &hr )) { *pbMinimized = IsIconic() ? VARIANT_TRUE : VARIANT_FALSE; hr = S_OK; } else { *pbMinimized = VARIANT_FALSE; } } return hr; } STDMETHODIMP CMarsWindow::put_minimized(VARIANT_BOOL bMinimized) { ATLASSERT(IsValidVariantBoolVal(bMinimized)); HRESULT hr = S_OK; if(VerifyNotPassive( &hr )) { if(!!IsIconic() != !!bMinimized) { SendMessage(WM_SYSCOMMAND, (bMinimized ? SC_MINIMIZE : SC_RESTORE), 0); } else { hr = S_FALSE; } } return hr; } STDMETHODIMP CMarsWindow::get_maximized(VARIANT_BOOL *pbMaximized) { HRESULT hr = E_INVALIDARG; if(API_IsValidWritePtr( pbMaximized )) { if(VerifyNotPassive( &hr )) { *pbMaximized = IsZoomed() ? VARIANT_TRUE : VARIANT_FALSE; hr = S_OK; } else { *pbMaximized = VARIANT_FALSE; } } return hr; } STDMETHODIMP CMarsWindow::put_maximized(VARIANT_BOOL bMaximized) { ATLASSERT(IsValidVariantBoolVal(bMaximized)); HRESULT hr = S_OK; if(VerifyNotPassive( &hr )) { DWORD dwStyle = ::GetWindowLong( m_hWnd, GWL_STYLE ); DWORD dwNewStyle = dwStyle | (WS_MAXIMIZEBOX | WS_MINIMIZEBOX | WS_SIZEBOX); if(dwStyle != dwNewStyle) { ::SetWindowLong( m_hWnd, GWL_STYLE, dwNewStyle ); } if(!!IsZoomed() != !!bMaximized) { SendMessage(WM_SYSCOMMAND, (bMaximized ? SC_MAXIMIZE : SC_RESTORE), 0); } else { hr = S_FALSE; } } return hr; } STDMETHODIMP CMarsWindow::get_title(BSTR *pbstrTitle) { HRESULT hr = E_INVALIDARG; if(API_IsValidWritePtr(pbstrTitle)) { if(VerifyNotPassive(&hr)) { int nLen = (int)SendMessage(WM_GETTEXTLENGTH, 0, 0); // SysAllocStringLen adds 1 for the NULL terminator *pbstrTitle = SysAllocStringLen(NULL, nLen); if(*pbstrTitle) { GetWindowText(*pbstrTitle, nLen + 1); hr = S_OK; } else { hr = E_OUTOFMEMORY; } } else { *pbstrTitle = NULL; } } return hr; } STDMETHODIMP CMarsWindow::put_title(BSTR bstrTitle) { HRESULT hr = E_INVALIDARG; if(API_IsValidBstr(bstrTitle)) { if(VerifyNotPassive(&hr)) { // TODO: If the text is not displayable with the current system font // we need to come up with something legible. SetWindowText(bstrTitle); hr = S_OK; } } return hr; } STDMETHODIMP CMarsWindow::get_height(long *plHeight) { HRESULT hr = E_INVALIDARG; if(API_IsValidWritePtr(plHeight)) { if(VerifyNotPassive(&hr)) { hr = SCRIPT_ERROR; RECT rc; if(GetWindowRect(&rc)) { *plHeight = RECTHEIGHT(rc); hr = S_OK; } } } return hr; } STDMETHODIMP CMarsWindow::put_height(long lHeight) { HRESULT hr = SCRIPT_ERROR; if(VerifyNotPassive(&hr)) { RECT rc; if(GetWindowRect( &rc ) && SetWindowPos( NULL, 0, 0, RECTWIDTH(rc), lHeight, SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOMOVE )) { hr = S_OK; } } return hr; } STDMETHODIMP CMarsWindow::get_width(long *plWidth) { HRESULT hr = E_INVALIDARG; if(API_IsValidWritePtr(plWidth)) { if(VerifyNotPassive(&hr)) { hr = SCRIPT_ERROR; RECT rc; if(GetWindowRect(&rc)) { *plWidth = RECTWIDTH(rc); hr = S_OK; } } } return hr; } STDMETHODIMP CMarsWindow::put_width(long lWidth) { HRESULT hr = SCRIPT_ERROR; if(VerifyNotPassive(&hr)) { RECT rc; if(GetWindowRect( &rc ) && SetWindowPos( NULL, 0, 0, lWidth, RECTHEIGHT(rc), SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOMOVE )) { hr = S_OK; } } return hr; } STDMETHODIMP CMarsWindow::get_x(long *plX) { HRESULT hr = E_INVALIDARG; if(API_IsValidWritePtr(plX)) { if(VerifyNotPassive(&hr)) { hr = SCRIPT_ERROR; RECT rc; if(GetWindowRect(&rc)) { *plX = rc.left; hr = S_OK; } } } return hr; } STDMETHODIMP CMarsWindow::put_x(long lX) { HRESULT hr = SCRIPT_ERROR; if(VerifyNotPassive( &hr )) { RECT rc; if(GetWindowRect( &rc ) && SetWindowPos( NULL, lX, rc.top, 0, 0, SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOSIZE )) { hr = S_OK; } } return hr; } STDMETHODIMP CMarsWindow::get_y(long *plY) { HRESULT hr = E_INVALIDARG; if(API_IsValidWritePtr(plY)) { if(VerifyNotPassive(&hr)) { hr = SCRIPT_ERROR; RECT rc; if(GetWindowRect(&rc)) { *plY = rc.top; hr = S_OK; } } } return hr; } STDMETHODIMP CMarsWindow::put_y(long lY) { HRESULT hr = SCRIPT_ERROR; if(VerifyNotPassive( &hr )) { RECT rc; if(GetWindowRect( &rc ) && SetWindowPos( NULL, rc.left, lY, 0, 0, SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOSIZE )) { hr = S_OK; } } return hr; } STDMETHODIMP CMarsWindow::get_visible(VARIANT_BOOL *pbVisible) { HRESULT hr = E_INVALIDARG; if(API_IsValidWritePtr(pbVisible)) { if(VerifyNotPassive(&hr)) { *pbVisible = IsWindowVisible() ? VARIANT_TRUE : VARIANT_FALSE; hr = S_OK; } } return hr; } STDMETHODIMP CMarsWindow::put_visible(VARIANT_BOOL bVisible) { HRESULT hr = SCRIPT_ERROR; if(VerifyNotPassive(&hr)) { if(bVisible) { if(m_fUIPanelsReady) { DoShowWindow(SW_SHOW); } else { // Our UI hasn't finished loading yet so showing the window // now is ugly. We'll remember this put_visible was done, and // show the window when the UI panels have fully loaded. m_fDeferMakeVisible = TRUE; } } else { DoShowWindow(SW_HIDE); } hr = S_OK; } return hr; } STDMETHODIMP CMarsWindow::get_panels(IMarsPanelCollection **ppPanels) { HRESULT hr = E_INVALIDARG; if(API_IsValidWritePtr(ppPanels)) { *ppPanels = NULL; if(VerifyNotPassive(&hr)) { hr = GetPanels()->QueryInterface(IID_IMarsPanelCollection, (void **)ppPanels); } } return hr; } STDMETHODIMP CMarsWindow::get_places(IMarsPlaceCollection **ppPlaces) { HRESULT hr = E_INVALIDARG; if(API_IsValidWritePtr(ppPlaces)) { *ppPlaces = NULL; if(VerifyNotPassive(&hr)) { hr = GetPlaces()->QueryInterface(IID_IMarsPlaceCollection, (void **)ppPlaces); } } return hr; } STDMETHODIMP CMarsWindow::setWindowDimensions( /*[in]*/ long lX, /*[in]*/ long lY, /*[in]*/ long lW, /*[in]*/ long lH ) { HRESULT hr = SCRIPT_ERROR; if(VerifyNotPassive( &hr )) { if(SetWindowPos(NULL, lX, lY, lW, lH, SWP_NOACTIVATE | SWP_NOZORDER)) { hr = S_OK; } } return hr; } STDMETHODIMP CMarsWindow::close() { HRESULT hr = S_OK; if(VerifyNotPassive(&hr)) { PostMessage(WM_CLOSE, 0, 0); } return hr; } STDMETHODIMP CMarsWindow::refreshLayout() { HRESULT hr = S_OK; if(VerifyNotPassive( &hr )) { CPanelCollection *panels = GetPanels(); if(panels) panels->Layout(); } return hr; } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ // IOleWindow STDMETHODIMP CMarsWindow::GetWindow(HWND *phwnd) { HRESULT hr = E_INVALIDARG; if(API_IsValidWritePtr(phwnd)) { if(IsWindow()) { *phwnd = m_hWnd; hr = S_OK; } else { *phwnd = NULL; hr = E_FAIL; } } return hr; } STDMETHODIMP CMarsWindow::ContextSensitiveHelp(BOOL fEnterMode) { return E_NOTIMPL; } // IOleInPlaceUIWindow STDMETHODIMP CMarsWindow::GetBorder(LPRECT lprectBorder) { ATLASSERT(lprectBorder); // We don't negotiate any toolbar space -- if they want screen real estate // they won't get it from us willingly. return INPLACE_E_NOTOOLSPACE; } STDMETHODIMP CMarsWindow::RequestBorderSpace(LPCBORDERWIDTHS pborderwidths) { ATLASSERT(pborderwidths); // Look buddy, we told you before -- we ain't giving you any of our pixels. return INPLACE_E_NOTOOLSPACE; } STDMETHODIMP CMarsWindow::SetBorderSpace(LPCBORDERWIDTHS pborderwidths) { ATLASSERT(pborderwidths); // Pushy OLE object wouldn't ya say? return E_UNEXPECTED; // return E_BITEME; } STDMETHODIMP CMarsWindow::SetActiveObject(IOleInPlaceActiveObject *pActiveObject, LPCOLESTR pszObjName) { // REVIEW: Maybe this is how a panel should let us know it's active. We currently track that in // the CPanelCollection via SetActivePanel(). return S_OK; } // IOleInPlaceFrame STDMETHODIMP CMarsWindow::InsertMenus(HMENU hmenuShared, LPOLEMENUGROUPWIDTHS lpMenuWidths) { // Menus? We don't need no steenkin' menus. ATLASSERT(hmenuShared && API_IsValidWritePtr(lpMenuWidths) && (0 == lpMenuWidths->width[0]) && (0 == lpMenuWidths->width[2]) && (0 == lpMenuWidths->width[4])); return E_NOTIMPL; } STDMETHODIMP CMarsWindow::SetMenu(HMENU hmenuShared, HOLEMENU holemenu, HWND hwndActiveObject) { return E_NOTIMPL; } STDMETHODIMP CMarsWindow::RemoveMenus(HMENU hmenuShared) { return E_NOTIMPL; } STDMETHODIMP CMarsWindow::SetStatusText(LPCOLESTR pszStatusText) { ATLASSERT((NULL == pszStatusText) || (API_IsValidString(pszStatusText))); return S_OK; } STDMETHODIMP CMarsWindow::EnableModeless(BOOL fEnable) { FAIL_AFTER_PASSIVATE(); m_fEnableModeless = BOOLIFY(fEnable); return GetPanels()->DoEnableModeless(fEnable); } STDMETHODIMP CMarsWindow::TranslateAccelerator(LPMSG lpmsg, WORD wID) { // REVIEW: Should we make keyboard routing go through here? return S_FALSE; } //================================================================== // Window message handlers //================================================================== LRESULT CMarsWindow::ForwardToMarsHost(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) { bHandled = FALSE; if(m_spMarsHost) { MSG msg; msg.hwnd = m_hWnd; msg.message = uMsg; msg.wParam = wParam; msg.lParam = lParam; if(SUCCEEDED(m_spMarsHost->PreTranslateMessage( &msg ))) { bHandled = TRUE; } } return 0; } LRESULT CMarsWindow::OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) { SetIcon(GetThreadParam()->hIcon); return TRUE; } LRESULT CMarsWindow::OnSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) { CPanelCollection *panels = GetPanels(); if(!panels) return 0; switch(wParam) { case SIZE_MINIMIZED: if(!m_fLayoutLocked) { panels->lockLayout(); m_fLayoutLocked = TRUE; } break; case SIZE_MAXIMIZED: case SIZE_RESTORED : if(m_fLayoutLocked) { panels->unlockLayout(); m_fLayoutLocked = FALSE; } // Fall through... default: panels->Layout(); break; } return 0; } LRESULT CMarsWindow::OnClose(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) { if(GetThreadParam()->dwFlags & MTF_MANAGE_WINDOW_SIZE) { CGlobalSettingsRegKey regkey; if(InitWindowPosition( regkey, TRUE )) { SaveWindowPosition( regkey ); } } Passivate(); return FALSE; } LRESULT CMarsWindow::OnNCActivate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) { LRESULT lResult; if(!m_fShowTitleBar && !IsIconic()) { lResult = TRUE; } else { lResult = DefWindowProc(uMsg, wParam, lParam); } return lResult; } LRESULT CMarsWindow::OnActivate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) { WORD wActive = LOWORD(wParam); if(wActive == WA_INACTIVE) { // // If we are the active window, remember the focus location, to restore it later. // if(m_fActiveWindow) { if(!IsPassive()) m_hwndFocus = GetFocus(); m_fActiveWindow = FALSE; } } else { m_fActiveWindow = TRUE; } bHandled = FALSE; return 0; } LRESULT CMarsWindow::OnGetMinMaxInfo(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) { MINMAXINFO *pInfo = (MINMAXINFO *)lParam; CPanelCollection *spPanels = GetPanels(); if(spPanels) { long lAdjustWidth; long lAdjustHeight; POINT ptMin; POINT ptMax; RECT rcClient; RECT rcWindow; GetMinMaxInfo( spPanels, 0, ptMin, ptMax ); GetClientRect( &rcClient ); GetWindowRect( &rcWindow ); lAdjustWidth = (rcWindow.right - rcWindow.left) - (rcClient.right - rcClient.left); lAdjustHeight = (rcWindow.bottom - rcWindow.top ) - (rcClient.bottom - rcClient.top ); if(ptMin.x >= 0) pInfo->ptMinTrackSize.x = ptMin.x + lAdjustWidth ; if(ptMin.y >= 0) pInfo->ptMinTrackSize.y = ptMin.y + lAdjustHeight; if(ptMax.x >= 0) pInfo->ptMaxTrackSize.x = ptMax.x + lAdjustWidth ; if(ptMax.y >= 0) pInfo->ptMaxTrackSize.y = ptMax.y + lAdjustHeight; } return 0; } void CMarsWindow::GetMinMaxInfo( CPanelCollection *spPanels, int index, POINT& ptMin, POINT& ptMax ) { ptMin.x = -1; ptMin.y = -1; ptMax.x = -1; ptMax.y = -1; if(spPanels && index < spPanels->GetSize()) { CMarsPanel* pPanel = (*spPanels)[index++]; if(pPanel) { PANEL_POSITION pos = pPanel->GetPosition(); if(pos != PANEL_POPUP) { if(pPanel->IsVisible()) { POINT ptOurMin; POINT ptOurMax; POINT ptSubMin; POINT ptSubMax; pPanel->GetMinMaxInfo( ptOurMin, ptOurMax ); GetMinMaxInfo( spPanels, index, ptSubMin, ptSubMax ); if(pos == PANEL_BOTTOM || pos == PANEL_TOP) { selectMin( ptMin.x, ptOurMin.x, ptSubMin.x ); selectMax( ptMax.x, ptOurMax.x, ptSubMax.x ); } else { combineMin( ptMin.x, ptOurMin.x, ptSubMin.x ); combineMax( ptMax.x, ptOurMax.x, ptSubMax.x ); } if(pos == PANEL_LEFT || pos == PANEL_RIGHT) { selectMin( ptMin.y, ptOurMin.y, ptSubMin.y ); selectMax( ptMax.y, ptOurMax.y, ptSubMax.y ); } else { combineMin( ptMin.y, ptOurMin.y, ptSubMin.y ); combineMax( ptMax.y, ptOurMax.y, ptSubMax.y ); } } else { GetMinMaxInfo( spPanels, index, ptMin, ptMax ); } } } } } bool CMarsWindow::CanLayout( /*[in/out]*/ RECT rcClient ) { CPanelCollection *spPanels = GetPanels(); if(spPanels) { for(int i=0; iGetSize(); i++) { CMarsPanel* pPanel = (*spPanels)[i]; POINT ptDiff; if(pPanel->CanLayout( rcClient, ptDiff ) == false) { return false; } } } return true; } void CMarsWindow::FixLayout( /*[in/out]*/ RECT rcClient ) { CPanelCollection *spPanels = GetPanels(); if(spPanels) { POINT ptDiff; FixLayout( spPanels, 0, rcClient, ptDiff ); } } void CMarsWindow::FixLayout( CPanelCollection *spPanels, int index, RECT rcClient, POINT& ptDiff ) { ptDiff.x = 0; ptDiff.y = 0; if(index < spPanels->GetSize()) { CMarsPanel* pPanel = (*spPanels)[index++]; PANEL_POSITION pos = pPanel->GetPosition(); RECT rcClient2 = rcClient; POINT ptSubDiff; // // First round, try to fix first ourselves and then lets the other fix themselves. // if(pPanel->CanLayout( rcClient2, ptDiff ) == false) { if(pos == PANEL_BOTTOM || pos == PANEL_TOP) { if(ptDiff.y) { pPanel->put_height( pPanel->GetHeight() - ptDiff.y ); } } if(pos == PANEL_LEFT || pos == PANEL_RIGHT) { if(ptDiff.x) { pPanel->put_width ( pPanel->GetWidth () - ptDiff.x ); } } rcClient2 = rcClient; pPanel->CanLayout( rcClient2, ptDiff ); } FixLayout( spPanels, index, rcClient2, ptSubDiff ); // // Second round, based on what the other panels need, we adjust. // if(pos == PANEL_BOTTOM || pos == PANEL_TOP) { if(ptSubDiff.y) { pPanel->put_height( pPanel->GetHeight() - ptSubDiff.y ); } } if(pos == PANEL_LEFT || pos == PANEL_RIGHT) { if(ptSubDiff.x) { pPanel->put_width ( pPanel->GetWidth () - ptSubDiff.x ); } } pPanel->CanLayout( rcClient, ptDiff ); FixLayout( spPanels, index, rcClient2, ptSubDiff ); ptDiff.x += ptSubDiff.x; ptDiff.y += ptSubDiff.y; } } void DrawFrame(HDC hdc, LPRECT prc, HBRUSH hbrColor, int cl) { HBRUSH hbr; int cx, cy; RECT rcT; ATLASSERT(NULL != prc); int cyBorder = GetSystemMetrics(SM_CYBORDER); int cxBorder = GetSystemMetrics(SM_CXBORDER); rcT = *prc; cx = cl * cxBorder; cy = cl * cyBorder; hbr = (HBRUSH)SelectObject(hdc, hbrColor); PatBlt(hdc, rcT.left, rcT.top, cx, rcT.bottom - rcT.top, PATCOPY); rcT.left += cx; PatBlt(hdc, rcT.left, rcT.top, rcT.right - rcT.left, cy, PATCOPY); rcT.top += cy; rcT.right -= cx; PatBlt(hdc, rcT.right, rcT.top, cx, rcT.bottom - rcT.top, PATCOPY); rcT.bottom -= cy; PatBlt(hdc, rcT.left, rcT.bottom, rcT.right - rcT.left, cy, PATCOPY); SelectObject(hdc, hbr); *prc = rcT; } // For now it looks like we can let windows handle this since we are adjusting // the client rect ourselves in OnNCCalcSize. LRESULT CMarsWindow::OnNCPaint(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) { LRESULT lResult; if(!m_fShowTitleBar) { HDC hdc = GetDCEx(wParam != 1 ? (HRGN)wParam : NULL, DCX_WINDOW | DCX_INTERSECTRGN); if(NULL == hdc) { hdc = GetWindowDC(); } RECT rcWindow; GetWindowRect(&rcWindow); OffsetRect(&rcWindow, -rcWindow.left, -rcWindow.top); HBRUSH hbrBorder = CreateSolidBrush(GetSysColor(COLOR_ACTIVEBORDER)); HBRUSH hbrFrame = CreateSolidBrush(GetSysColor(COLOR_3DFACE)); DrawEdge(hdc, &rcWindow, EDGE_RAISED, (BF_RECT | BF_ADJUST)); NONCLIENTMETRICSA ncm; ncm.cbSize = sizeof(ncm); SystemParametersInfoA(SPI_GETNONCLIENTMETRICS, sizeof(ncm), (void *)(LPNONCLIENTMETRICS)&ncm, 0); DrawFrame(hdc, &rcWindow, hbrBorder, ncm.iBorderWidth); DrawFrame(hdc, &rcWindow, hbrFrame, 1); DeleteObject(hbrBorder); DeleteObject(hbrFrame); ReleaseDC(hdc); lResult = 0; } else { lResult = DefWindowProc(uMsg, wParam, lParam); } return lResult; } LRESULT CMarsWindow::OnPaint(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) { PAINTSTRUCT ps; BeginPaint(&ps); EndPaint(&ps); return 0; } LRESULT CMarsWindow::OnPaletteChanged(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) { HWND hwndPaletteChange = (HWND)wParam; // Ignore if we changed the palette if(hwndPaletteChange == m_hWnd) return 0; // If we are the active window and one of our children set the forground palette // we want to avoid realizing our palette in the foreground or we get in a tug-of-war // with lots of flashing. if(IsChild(hwndPaletteChange) && (m_hWnd == GetForegroundWindow())) { // Our child caused a palette change so force a redraw to use the // new system palette. Children shouldn't do this, bad child! RedrawWindow(NULL, NULL, RDW_INVALIDATE | RDW_ERASE | RDW_ALLCHILDREN); } else { // Select our foreground palette OnQueryNewPalette(uMsg, wParam, lParam, bHandled); } return 0; } LRESULT CMarsWindow::OnQueryNewPalette(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) { LRESULT lResult = FALSE; // Realize our palette if(g_hpalHalftone) { HDC hDC = GetDC(); if(hDC) { if(GetDeviceCaps(hDC, RASTERCAPS) & RC_PALETTE) { HPALETTE hOldPal = SelectPalette(hDC, g_hpalHalftone, FALSE); UINT i = RealizePalette(hDC); // Did the realization change? (We need to always invalidate background windows // because when we have multiple windows up only the first top-level // window will actually realize any colors. Windows lower in the // z-order always get 0 returned from RealizePalette, but they // may need repainting! We could further optimize by having the top // html window invalidate all the rest when i is non-zero. -- StevePro) if(i || (m_hWnd != GetForegroundWindow())) { // Yes, so force a repaint. RedrawWindow(NULL, NULL, RDW_INVALIDATE | RDW_ERASE | RDW_ALLCHILDREN); } SelectPalette(hDC, hOldPal, TRUE); RealizePalette(hDC); // lResult = i; lResult = TRUE; } ReleaseDC(hDC); } } return lResult; } LRESULT CMarsWindow::OnSysColorChange(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) { // We need to update our palette because some of the "reserved" colors may have changed HPALETTE hpal = SHCreateShellPalette(NULL); hpal = (HPALETTE)InterlockedExchangePointer( (LPVOID*)&g_hpalHalftone, hpal); if(hpal) { DeleteObject(hpal); } PostMessage(WM_QUERYNEWPALETTE, 0, (LPARAM) -1); // Trident likes to know about these changes ForwardMessageToChildren(uMsg, wParam, lParam); bHandled = FALSE; return 0; } LRESULT CMarsWindow::OnDisplayChange(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) { return OnSysColorChange(uMsg, wParam, lParam, bHandled); } LRESULT CMarsWindow::OnSysCommand(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) { if(wParam == SC_MINIMIZE) { // // If we are the active window, remember the focus location, to restore it later. // if(m_fActiveWindow) { if(!IsPassive()) m_hwndFocus = GetFocus(); m_fActiveWindow = FALSE; } } bHandled = FALSE; return 0; } LRESULT CMarsWindow::OnSetFocus(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) { // // If we have a saved focus location, restore it. // if(m_hwndFocus && m_hwndFocus != m_hWnd) { ::SetFocus( m_hwndFocus ); } return 0; } LRESULT CMarsWindow::OnEraseBkgnd(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) { return TRUE; } LRESULT CMarsWindow::OnNCCalcSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) { LRESULT lResult; if(!m_fShowTitleBar && !IsIconic()) { RECT *prc = (RECT *)lParam; NONCLIENTMETRICSA ncm; ncm.cbSize = sizeof(ncm); SystemParametersInfoA(SPI_GETNONCLIENTMETRICS, sizeof(ncm), &ncm, FALSE); int xDelta = GetSystemMetrics(SM_CXEDGE) + ncm.iBorderWidth + 1; int yDelta = GetSystemMetrics(SM_CYEDGE) + ncm.iBorderWidth + 1; InflateRect(prc, -xDelta, -yDelta); lResult = 0; } else { lResult = DefWindowProc(uMsg, wParam, lParam); } return lResult; } LRESULT CMarsWindow::OnSetText(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) { // HACK 'O RAMA: Turn off one of the WS_CAPTION style bits (WS_CAPTION == WS_BORDER | WS_DLGFRAME) // so that USER32 doesn't try and draw the title bar for us. DWORD dwStyle = GetWindowLong(GWL_STYLE); SetWindowLong(GWL_STYLE, dwStyle & ~WS_DLGFRAME); LRESULT lResult = DefWindowProc(); SetWindowLong(GWL_STYLE, dwStyle); return lResult; } void CMarsWindow::OnFinalMessage(HWND hWnd) { PostQuitMessage(0); } //================================================================== // Panel/Place methods //================================================================== void CMarsWindow::DoShowWindow(int nCmdShow) { if(GetThreadParam()->dwFlags & MTF_DONT_SHOW_WINDOW) return; ShowWindow( nCmdShow ); // Win95 doesn't let the window from another thread appear in front of an // existing window, so we must grab the foreground if(IsWindowVisible()) { SetForegroundWindow(m_hWnd); } } void CMarsWindow::OnTransitionComplete() { if(!m_fUIPanelsReady && !IsWindowVisible()) { m_fUIPanelsReady = TRUE; // start with the Mars window host's requested show mode int nCmdShow = GetThreadParam()->nCmdShow; if((nCmdShow == SW_HIDE) && m_fDeferMakeVisible) { // nCmdShow is SW_HIDE to indicate that the window should be shown // via put_visible. In this case, someone did a pub_visible(TRUE) before // our UI panels were finished loading, so we'll honor the request now. nCmdShow = SW_SHOW; } // only promote to maximized state if we are going to become visible if((nCmdShow != SW_HIDE) && m_fStartMaximized) { nCmdShow = SW_MAXIMIZE; } DoShowWindow(nCmdShow); } } HRESULT CMarsWindow::ReleaseOwnedObjects(IUnknown *pUnknownOwner) { return S_OK; } void CMarsWindow::SetFirstPlace( LPCWSTR szPlace ) { if(!m_bstrFirstPlace) { m_bstrFirstPlace = szPlace; } } void CMarsWindow::ShowTitleBar(BOOL fShowTitleBar) { if(!!m_fShowTitleBar != !!fShowTitleBar) { m_fShowTitleBar = fShowTitleBar ? TRUE : FALSE; SetWindowPos(NULL, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_NOMOVE | SWP_NOACTIVATE | SWP_FRAMECHANGED); } } BOOL CMarsWindow::TranslateAccelerator(MSG &msg) { BOOL bProcessed = FALSE; // if(msg.message == WM_SYSKEYDOWN) // { // switch (msg.wParam) // { // case VK_LEFT: // case VK_RIGHT: // GetTravelLog()->travel((msg.wParam == VK_LEFT) ? -1 : 1); // bProcessed = TRUE; // break; // } // } return bProcessed; } BOOL CMarsWindow::PreTranslateMessage(MSG &msg) { // Set to TRUE if you don't want this message dispatched normally BOOL bProcessed = FALSE; switch (msg.message) { case WM_SETFOCUS: break; default: if((msg.message >= WM_KEYFIRST) && (msg.message <= WM_KEYLAST)) { // First we take a crack bProcessed = TranslateAccelerator(msg); // Now let the active place try if(!bProcessed) { CMarsPlace *pPlace = GetPlaces()->GetCurrentPlace(); if(NULL != pPlace) { bProcessed = (pPlace->TranslateAccelerator(&msg) == S_OK); } } } else if((msg.message >= WM_MOUSEFIRST) && (msg.message <= WM_MOUSELAST)) { if(m_bSingleButtonMouse) { switch (msg.message) { case WM_RBUTTONDOWN: case WM_MBUTTONDOWN: msg.message = WM_LBUTTONDOWN; break; case WM_RBUTTONUP: case WM_MBUTTONUP: msg.message = WM_LBUTTONUP; break; case WM_RBUTTONDBLCLK: case WM_MBUTTONDBLCLK: msg.message = WM_LBUTTONDBLCLK; break; } } } } return bProcessed; } bool CMarsWindow::InitWindowPosition( CGlobalSettingsRegKey& regkey, BOOL fWrite ) { WCHAR rgPath[MAX_PATH]; RECT rc; if(::GetClientRect( ::GetDesktopWindow(), &rc )) { LPCWSTR szTitle = GetThreadParam()->pwszTitle; if(!szTitle) szTitle = L""; LONG Screen_width = rc.right - rc.left; LONG Screen_height = rc.bottom - rc.top; StringCchPrintfW( rgPath, ARRAYSIZE(rgPath), WZ_WINDOWPLACEMENT, (int)Screen_width, (int)Screen_height, szTitle ); if(fWrite) { if(regkey.CreateGlobalSubkey( rgPath ) == ERROR_SUCCESS) return true; } else { if(regkey.OpenGlobalSubkey( rgPath ) == ERROR_SUCCESS) return true; } } return false; } void CMarsWindow::SaveWindowPosition( CGlobalSettingsRegKey& regkey ) { WINDOWPLACEMENT wp; wp.length = sizeof(wp); GetWindowPlacement( &wp ); WriteWindowPosition(regkey, &wp.rcNormalPosition, IsZoomed()); } void CMarsWindow::LoadWindowPosition( CGlobalSettingsRegKey& regkey, BOOL fAllowMaximized, WINDOWPLACEMENT& wp, BOOL& fMaximized ) { RECT rc; // Use default values if there is no valid registry data if(ERROR_SUCCESS != regkey.QueryBoolValue(fMaximized, WZ_POSITIONMAX)) { fMaximized = fAllowMaximized; } if(ERROR_SUCCESS != regkey.QueryBinaryValue(&rc, sizeof(rc), WZ_POSITIONRECT)) { rc = wp.rcNormalPosition; GetThreadParam()->dwFlags &= ~MTF_RESTORING_FROM_REGISTRY; } else { GetThreadParam()->dwFlags |= MTF_RESTORING_FROM_REGISTRY; } // If the window is about to open with the same top-left corner as another // Mars window, cascade it. if(IsWindowOverlayed( m_hWnd, rc.left, rc.top )) { CascadeWindowRectOnMonitor( m_hWnd, &rc ); } // Always make sure the window is fully on-screen BoundWindowRectToMonitor( m_hWnd, &rc ); // Don't use maximized setting if we're opened by script m_fStartMaximized = fMaximized && fAllowMaximized; // Now set the size of the window -- we should be hidden at this point wp.rcNormalPosition = rc; wp.showCmd = IsWindowVisible() ? (fMaximized ? SW_MAXIMIZE : SW_NORMAL) : SW_HIDE; } void CMarsWindow::SpinMessageLoop( BOOL fWait ) { MSG msg; while(fWait ? GetMessage( &msg, NULL, 0, 0 ) : PeekMessage( &msg, NULL, 0, 0, PM_REMOVE )) { if(m_spMarsHost && SUCCEEDED(m_spMarsHost->PreTranslateMessage( &msg ))) continue; if(!PreTranslateMessage( msg )) { TranslateMessage( &msg ); DispatchMessage ( &msg ); } if(IsPassive()) break; } } //================================================================== // Mars App //================================================================== HRESULT STDMETHODCALLTYPE MarsThreadProc(IMarsHost *pMarsHost, MARSTHREADPARAM *pThreadParam) { HRESULT hr; if (!CThreadData::HaveData() && (NULL != pThreadParam) && (pThreadParam->cbSize == sizeof(*pThreadParam))) { hr = E_OUTOFMEMORY; CThreadData *pThreadData = new CThreadData; if (pThreadData && CThreadData::TlsSetValue(pThreadData)) { hr = CoInitialize(NULL); if(SUCCEEDED(hr)) { MarsAxWinInit(); CComClassPtr spMarsWindow; CMarsWindow::CreateInstance(pMarsHost, pThreadParam, &spMarsWindow); if(spMarsWindow) { spMarsWindow->SpinMessageLoop( TRUE ); // Ensure that no matter what the window is passivated & then release it if (!spMarsWindow->IsPassive()) { spMarsWindow->Passivate(); } } CoUninitialize(); } CThreadData::TlsSetValue(NULL); // paranoia } delete pThreadData; } else { if(pThreadParam) { ATLASSERT(pThreadParam->cbSize == sizeof(*pThreadParam)); } // If we already have TLS data then we are being reentered -- this is not a good thing! hr = E_UNEXPECTED; } return hr; }