///////////////////////////////////////////////////////////////////////////// // Copyright (C) 1993-1998 Microsoft Corporation. All Rights Reserved. // // MODULE: navpane.cpp // // PURPOSE: // #include "pch.hxx" #include "navpane.h" #include "treeview.h" #include "baui.h" #include "browser.h" #include "menuutil.h" #include "inpobj.h" ///////////////////////////////////////////////////////////////////////////// // Local Stuff // const TCHAR c_szNavPaneClass[] = _T("Outlook Express Navigation Pane"); const TCHAR c_szPaneFrameClass[] = _T("Outlook Express Pane Frame"); // Sizing consts const int c_cxBorder = 1; const int c_cyBorder = 1; const int c_cxTextBorder = 4; const int c_cyTextBorder = 2; const int c_cyClose = 3; const int c_cySplit = 4; const int c_cxSplit = 3; #define ID_PANE_CLOSE 2000 #define ID_PANE_PIN 2001 #define ID_PANE_TITLE 2002 #define IDT_PANETIMER 100 #define ELAPSE_MOUSEOVERCHECK 250 ///////////////////////////////////////////////////////////////////////////// // CNavPane Implementation // CNavPane::CNavPane() { m_cRef = 1; m_fShow = FALSE; m_fTreeVisible = FALSE; m_fContactsVisible = FALSE; m_hwnd = 0; m_hwndParent = 0; m_hwndTree = 0; m_hwndContacts = 0; m_pSite = NULL; m_pTreeView = NULL; m_pContacts = NULL; m_pContactsFrame = NULL; m_pContactsTarget = NULL; m_cxWidth = 200; m_fResizing = FALSE; m_fSplitting = FALSE; m_cySplitPct = 50; ZeroMemory(&m_rcSplit, sizeof(RECT)); ZeroMemory(&m_rcSizeBorder, sizeof(RECT)); m_cyTitleBar = 32; } CNavPane::~CNavPane() { SafeRelease(m_pContactsFrame); } HRESULT CNavPane::Initialize(CTreeView *pTreeView) { // We've got to have this if (!pTreeView) return (E_INVALIDARG); // Keep it m_pTreeView = pTreeView; m_pTreeView->AddRef(); // Load some settings m_cxWidth = DwGetOption(OPT_NAVPANEWIDTH); if (m_cxWidth < 0) m_cxWidth = 200; m_cySplitPct = DwGetOption(OPT_NAVPANESPLIT); // Do some parameter checking if (m_cySplitPct > 100 || m_cySplitPct < 2) m_cySplitPct = 66; return (S_OK); } // // FUNCTION: CNavPane::QueryInterface() // // PURPOSE: Allows caller to retrieve the various interfaces supported by // this class. // HRESULT CNavPane::QueryInterface(REFIID riid, LPVOID *ppvObj) { TraceCall("CNavPane::QueryInterface"); *ppvObj = NULL; if (IsEqualIID(riid, IID_IUnknown)) *ppvObj = (LPVOID) (IDockingWindow *) this; else if (IsEqualIID(riid, IID_IDockingWindow)) *ppvObj = (LPVOID) (IDockingWindow *) this; else if (IsEqualIID(riid, IID_IObjectWithSite)) *ppvObj = (LPVOID) (IObjectWithSite *) this; else if (IsEqualIID(riid, IID_IOleCommandTarget)) *ppvObj = (LPVOID) (IOleCommandTarget *) this; else if (IsEqualIID(riid, IID_IInputObjectSite)) *ppvObj = (LPVOID) (IInputObjectSite *) this; else if (IsEqualIID(riid, IID_IInputObject)) *ppvObj = (LPVOID) (IInputObject *) this; if (*ppvObj) { AddRef(); return (S_OK); } return (E_NOINTERFACE); } // // FUNCTION: CNavPane::AddRef() // // PURPOSE: Adds a reference count to this object. // ULONG CNavPane::AddRef(void) { TraceCall("CNavPane::AddRef"); return ((ULONG) InterlockedIncrement((LONG *) &m_cRef)); } // // FUNCTION: CNavPane::Release() // // PURPOSE: Releases a reference on this object. // ULONG CNavPane::Release(void) { TraceCall("CNavPane::Release"); if (0 == InterlockedDecrement((LONG *) &m_cRef)) { delete this; return 0; } return (m_cRef); } // // FUNCTION: CNavPane::GetWindow() // // PURPOSE: Returns the handle of our outer window // // PARAMETERS: // [out] pHwnd - return value // // RETURN VALUE: // HRESULT // HRESULT CNavPane::GetWindow(HWND *pHwnd) { TraceCall("CNavPane::GetWindow"); if (!pHwnd) return (E_INVALIDARG); if (IsWindow(m_hwnd)) { *pHwnd = m_hwnd; return (S_OK); } return (E_FAIL); } // // FUNCTION: CNavPane::ContextSensitiveHelp() // // PURPOSE: Does anyone _ever_ implement this? // HRESULT CNavPane::ContextSensitiveHelp(BOOL fEnterMode) { TraceCall("CNavPane::ContextSensitiveHelp"); return (E_NOTIMPL); } // // FUNCTION: CNavPane::ShowDW() // // PURPOSE: Show's or hides the Nav pane. If the pane has not yet been // created it does that too. // // PARAMETERS: // [in] fShow - TRUE to show, FALSE to hide // // RETURN VALUE: // HRESULT // HRESULT CNavPane::ShowDW(BOOL fShow) { HRESULT hr; WNDCLASSEX wc; TraceCall("CNavPane::ShowDW"); // Nothing works without a site pointer if (!m_pSite) return (E_UNEXPECTED); // Check to see if we've been created yet if (!m_hwnd) { // Register the window class if necessary wc.cbSize = sizeof(WNDCLASSEX); if (!GetClassInfoEx(g_hInst, c_szNavPaneClass, &wc)) { wc.style = 0; wc.lpfnWndProc = _WndProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = g_hInst; wc.hCursor = LoadCursor(0, IDC_SIZEWE); wc.hbrBackground = (HBRUSH) (COLOR_3DFACE + 1); wc.lpszMenuName = NULL; wc.lpszClassName = c_szNavPaneClass; wc.hIcon = NULL; wc.hIconSm = NULL; RegisterClassEx(&wc); } // Get the parent window before we create ours if (FAILED(m_pSite->GetWindow(&m_hwndParent))) { AssertSz(FALSE, "CNavPane::ShowDW() - Failed to get a parent window handle."); } // Create the window m_hwnd = CreateWindowEx(WS_EX_CONTROLPARENT, c_szNavPaneClass, NULL, WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS, 0, 0, 10, 10, m_hwndParent, (HMENU) 0, g_hInst, this); if (!m_hwnd) { AssertSz(FALSE, "CNavPane::ShowDW() - Failed to create main window."); return (E_OUTOFMEMORY); } // Create any children if (FAILED(hr = _CreateChildWindows())) { AssertSz(FALSE, "CNavPane::ShowDW() - Failed to create child windows."); DestroyWindow(m_hwnd); return (hr); } } // Show or hide the window appropriately m_fShow = (fShow && (m_fTreeVisible || m_fContactsVisible)); ResizeBorderDW(0, 0, FALSE); ShowWindow(m_hwnd, fShow ? SW_SHOW : SW_HIDE); return (S_OK); } // // FUNCTION: CNavPane::ResizeBorderDW() // // PURPOSE: Called when it's time for us to re-request space from our // parent. // // PARAMETERS: // [in] prcBorder - a RECT containing the outer rectangle the object can request space in // [in] punkSite - pointer to the site that changed // [in] fReserved - unused. // // RETURN VALUE: // HRESULT // HRESULT CNavPane::ResizeBorderDW(LPCRECT prcBorder, IUnknown *punkSite, BOOL fReserved) { const DWORD c_cxResizeBorder = 3; HRESULT hr = S_OK; RECT rcRequest = { 0 }; RECT rcBorder; TraceCall("CNavPane::ResizeBorderDW"); // If we don't have a site pointer, this ain't gonna work if (!m_pSite) return (E_UNEXPECTED); // If we visible, then calculate our border requirements. If we're not // visible, the our requirements are zero and we can use the default // values in rcRequest. Assert(IsWindow(m_hwnd)); // If the caller didn't provide us with a rect, get one ourselves if (!prcBorder) { m_pSite->GetBorderDW((IDockingWindow *) this, &rcBorder); prcBorder = &rcBorder; } // The space we need is the min of either what we want to be or the // width of the parent minus some if (m_fShow) { rcRequest.left = min(prcBorder->right - prcBorder->left - 32, m_cxWidth); } // Ask for the space we need if (SUCCEEDED(m_pSite->RequestBorderSpaceDW((IDockingWindow *) this, &rcRequest))) { // Tell the site how be we're going to be if (SUCCEEDED(m_pSite->SetBorderSpaceDW((IDockingWindow *) this, &rcRequest))) { // Now once that's all done, resize ourselves if we're visible if (m_fShow) { SetWindowPos(m_hwnd, 0, prcBorder->left, prcBorder->top, rcRequest.left, prcBorder->bottom - prcBorder->top, SWP_NOZORDER | SWP_NOACTIVATE); } } } return (S_OK); } // // FUNCTION: CNavPane::CloseDW() // // PURPOSE: Called when the parent want's to destroy this window // // PARAMETERS: // [in] dwReserved - unused // // RETURN VALUE: // HRESULT // HRESULT CNavPane::CloseDW(DWORD dwReserved) { TraceCall("CNavPane::CloseDW"); // Save our settings SetDwOption(OPT_NAVPANEWIDTH, m_cxWidth, NULL, 0); SetDwOption(OPT_NAVPANESPLIT, m_cySplitPct, NULL, 0); if (m_pTreeView) m_pTreeView->DeInit(); if (m_hwnd) { DestroyWindow(m_hwnd); m_hwnd = NULL; } // Destroy our children here SafeRelease(m_pTreeView); SafeRelease(m_pContactsTarget); SafeRelease(m_pContacts); return (S_OK); } // // FUNCTION: CNavPane::GetSite() // // PURPOSE: Called to request an interface to our site // // PARAMETERS: // [in] riid - Requested interface // [out] ppvSite - Returned interface if available // // RETURN VALUE: // HRESULT // HRESULT CNavPane::GetSite(REFIID riid, LPVOID *ppvSite) { HRESULT hr; TraceCall("CNavPane::GetSite"); if (m_pSite) { // Ask our site for the requested interface hr = m_pSite->QueryInterface(riid, ppvSite); return (hr); } return (E_FAIL); } // // FUNCTION: CNavPane::SetSite() // // PURPOSE: Called to tell us who our site will be. // // PARAMETERS: // [in] pUnkSite - Pointer to the new site // // RETURN VALUE: // HRESULT // HRESULT CNavPane::SetSite(IUnknown *pUnkSite) { HRESULT hr = S_OK; TraceCall("CNavPane::SetSite"); // If we already have a site, release it if (m_pSite) { m_pSite->Release(); m_pSite = 0; } // If we were given a new site, keep it if (pUnkSite) { hr = pUnkSite->QueryInterface(IID_IDockingWindowSite, (LPVOID *) &m_pSite); return (hr); } return (hr); } // // FUNCTION: CNavPane::_WndProc() // // PURPOSE: External callback. // LRESULT CALLBACK CNavPane::_WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { CNavPane *pThis; if (uMsg == WM_NCCREATE) { pThis = (CNavPane *) ((LPCREATESTRUCT) lParam)->lpCreateParams; SetWindowLongPtr(hwnd, GWLP_USERDATA, (LPARAM) pThis); } else pThis = (CNavPane *) GetWindowLongPtr(hwnd, GWLP_USERDATA); if (pThis) return (pThis->_NavWndProc(hwnd, uMsg, wParam, lParam)); return (FALSE); } // // FUNCTION: CNavPane::_NavWndProc() // // PURPOSE: Left as an exercise for the reader // LRESULT CALLBACK CNavPane::_NavWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { HANDLE_MSG(hwnd, WM_SETCURSOR, _OnSetCursor); HANDLE_MSG(hwnd, WM_SIZE, _OnSize); HANDLE_MSG(hwnd, WM_MOUSEMOVE, _OnMouseMove); HANDLE_MSG(hwnd, WM_LBUTTONDOWN, _OnLButtonDown); HANDLE_MSG(hwnd, WM_LBUTTONUP, _OnLButtonUp); case WM_SYSCOLORCHANGE: case WM_WININICHANGE: { // Forward these to all our children if (IsWindow(m_hwndTree)) SendMessage(m_hwndTree, uMsg, wParam, lParam); if (IsWindow(m_hwndContacts)) SendMessage(m_hwndContacts, uMsg, wParam, lParam); // Update any of our own sizes m_cyTitleBar =(UINT) SendMessage(m_hwndTree, WM_GET_TITLE_BAR_HEIGHT, 0, 0); return (0); } } return (DefWindowProc(hwnd, uMsg, wParam, lParam)); } // // FUNCTION: CNavPane::_OnSize() // // PURPOSE: When our window get's resized, we need to resize our child // windows too. // void CNavPane::_OnSize(HWND hwnd, UINT state, int cx, int cy) { RECT rc; DWORD cyTree; DWORD cySplit = c_cySplit; TraceCall("CNavPane::_OnSize"); // If only the tree is visible if (m_fTreeVisible && !m_fContactsVisible) cyTree = cy; else if (m_fTreeVisible && m_fContactsVisible) cyTree = (cy * m_cySplitPct) / 100; else if (!m_fTreeVisible && m_fContactsVisible) { cyTree = 0; cySplit = 0; } // Resize the TreeView to fit inside our window if (m_hwndTree) SetWindowPos(m_hwndTree, 0, 0, 0, cx - c_cxSplit, cyTree, SWP_NOZORDER | SWP_NOACTIVATE); if (m_hwndContacts) SetWindowPos(m_hwndContacts, 0, 0, cyTree + cySplit, cx - 3, cy - cyTree - cySplit, SWP_NOZORDER | SWP_NOACTIVATE); // Figure out where a few things are, starting with the split bar SetRect(&rc, c_cxBorder, cyTree, cx - c_cxSplit - c_cxBorder, cyTree + cySplit); m_rcSplit = rc; // Figure out where the right side is SetRect(&rc, cx - c_cxSplit, 0, cx, cy); m_rcSizeBorder = rc; } // // FUNCTION: CNavPane::_OnLButtonDown() // // PURPOSE: When the user clicks down and we get this notification, it // must be because they want to resize. // void CNavPane::_OnLButtonDown(HWND hwnd, BOOL fDoubleClick, int x, int y, UINT keyFlags) { TraceCall("CNavPane::_OnLButtonDown"); if (!m_fResizing) { SetCapture(hwnd); m_fResizing = TRUE; POINT pt = {x, y}; if (PtInRect(&m_rcSplit, pt)) { m_fSplitting = TRUE; } } } // // FUNCTION: CNavPane::_OnMouseMove() // // PURPOSE: If we're resizing, update our position etc. // void CNavPane::_OnMouseMove(HWND hwnd, int x, int y, UINT keyFlags) { POINT pt = {x, y}; RECT rcClient; TraceCall("CNavPane::_OnMouseMove"); if (m_fResizing) { if (m_fSplitting) { GetClientRect(m_hwnd, &rcClient); m_cySplitPct = (int)(((float) pt.y / (float) rcClient.bottom) * 100); // Make sure we have the min's and max's right int cy = (rcClient.bottom * m_cySplitPct) / 100; if (cy < m_cyTitleBar) { m_cySplitPct = (int)(((float) m_cyTitleBar / (float) rcClient.bottom) * 100); } else if (rcClient.bottom - cy < m_cyTitleBar) { m_cySplitPct = (int)(((float) (rcClient.bottom - m_cyTitleBar) / (float) rcClient.bottom) * 100); } _OnSize(hwnd, 0, rcClient.right, rcClient.bottom); } else { if (pt.x > 32) { GetClientRect(m_hwndParent, &rcClient); m_cxWidth = max(0, min(pt.x, rcClient.right - 32)); ResizeBorderDW(0, 0, FALSE); } } } } // // FUNCTION: CNavPane::_OnLButtonUp() // // PURPOSE: If the user was resizing, then they're done now and we can // clean up. // void CNavPane::_OnLButtonUp(HWND hwnd, int x, int y, UINT keyFlags) { TraceCall("CNavPane::_OnLButtonUp"); if (m_fResizing) { ReleaseCapture(); m_fResizing = FALSE; m_fSplitting = FALSE; } } // // FUNCTION: CNavPane::_OnSetCursor() // // PURPOSE: Do some jimmying with the cursor // BOOL CNavPane::_OnSetCursor(HWND hwnd, HWND hwndCursor, UINT codeHitTest, UINT msg) { POINT pt; TraceCall("_OnSetCursor"); // Get the cursor position GetCursorPos(&pt); ScreenToClient(m_hwnd, &pt); // If the cursor is within the split bar, update the cursor if (PtInRect(&m_rcSplit, pt)) { SetCursor(LoadCursor(NULL, IDC_SIZENS)); return (TRUE); } if (PtInRect(&m_rcSizeBorder, pt)) { SetCursor(LoadCursor(NULL, IDC_SIZEWE)); return (TRUE); } return (FALSE); } // // FUNCTION: CNavPane::_OnNCHitTest() // // PURPOSE: We monkey around with the non client area to get the correct // cursors // // PARAMETERS: // [in] hwnd - Window handle the mouse is in // [in] x, y - Position of the mouse in screen coordinates // // RETURN VALUE: // Our personal opinion of where the mouse is. // UINT CNavPane::_OnNCHitTest(HWND hwnd, int x, int y) { POINT pt = {x, y}; // If the cursor is in the split bar if (PtInRect(&m_rcSplit, pt)) return (HTTOP); if (PtInRect(&m_rcSizeBorder, pt)) return (HTRIGHT); return (HTCLIENT); } // // FUNCTION: CNavPane::_CreateChildWindows() // // PURPOSE: Creates the child windows that will be displayed. // // RETURN VALUE: // HRESULT // HRESULT CNavPane::_CreateChildWindows(void) { IOleWindow *pWindow = NULL; IInputObject *pInputObj = NULL; HRESULT hr; TraceCall("CNavPane::_CreateChildWindows"); // The treeview is always created by the browser. All we have to do // is tell it to create it's UI. m_hwndTree = m_pTreeView->Create(m_hwnd, (IInputObjectSite *) this, TRUE); Assert(m_hwndTree); // If the tree is supposed to be visible, show it if (DwGetOption(OPT_SHOWTREE)) { ShowWindow(m_hwndTree, SW_SHOW); m_fTreeVisible = TRUE; m_cyTitleBar = (UINT) SendMessage(m_hwndTree, WM_GET_TITLE_BAR_HEIGHT, 0, 0); } // If we're showing contacts, create it if (DwGetOption(OPT_SHOWCONTACTS) && (!(g_dwAthenaMode & MODE_OUTLOOKNEWS))) { ShowContacts(TRUE); } return (S_OK); } // // FUNCTION: CNavPane::ShowFolderList() // // PURPOSE: Shows and hides the folder list doodad // // PARAMETERS: // BOOL fShow // BOOL CNavPane::ShowFolderList(BOOL fShow) { TraceCall("CNavPane::ShowFolderList"); // The folder list _always_ exists. We just toggle the state ShowWindow(m_hwndTree, fShow ? SW_SHOW : SW_HIDE); m_fTreeVisible = fShow; _UpdateVisibleState(); RECT rc; GetClientRect(m_hwnd, &rc); _OnSize(m_hwnd, 0, rc.right, rc.bottom); return (TRUE); } // // FUNCTION: CNavPane::ShowContacts() // // PURPOSE: // // PARAMETERS: // BOOL fShow // // RETURN VALUE: // BOOL // BOOL CNavPane::ShowContacts(BOOL fShow) { CMsgrAb *pMsgrAb; HWND hwnd; IAthenaBrowser *pBrowser; HRESULT hr; RECT rc = {0}; if (!m_pContacts) { hr = CreateMsgrAbCtrl(&m_pContacts); if (SUCCEEDED(hr)) { // Initialize the control m_pContactsFrame = new CPaneFrame(); if (!m_pContactsFrame) return (0); m_hwndContacts = m_pContactsFrame->Initialize(m_hwnd, this, idsABBandTitle, IDR_BA_TITLE_POPUP); pMsgrAb = (CMsgrAb *) m_pContacts; hwnd = pMsgrAb->CreateControlWindow(m_hwndContacts, rc); if (hwnd) { if (SUCCEEDED(m_pSite->QueryInterface(IID_IAthenaBrowser, (LPVOID *) &pBrowser))) { m_pContactsFrame->SetChild(hwnd, DISPID_MSGVIEW_CONTACTS, pBrowser, pMsgrAb, pMsgrAb); pBrowser->Release(); } } // Get the command target m_pContacts->QueryInterface(IID_IOleCommandTarget, (LPVOID *) &m_pContactsTarget); } } SetWindowPos(m_hwndContacts, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE); ShowWindow(m_hwndContacts, fShow ? SW_SHOW : SW_HIDE); m_fContactsVisible = fShow; _UpdateVisibleState(); GetClientRect(m_hwnd, &rc); _OnSize(m_hwnd, 0, rc.right, rc.bottom); return (TRUE); } // // FUNCTION: CNavPane::_UpdateVisibleState() // // PURPOSE: Checks to see if we need to show our hide ourselves // void CNavPane::_UpdateVisibleState(void) { // If this leaves us with nothing visible, then we hide ourselves if (!m_fTreeVisible && !m_fContactsVisible) { ShowWindow(m_hwnd, SW_HIDE); m_fShow = FALSE; ResizeBorderDW(0, 0, 0); } else if (m_fShow == FALSE && (m_fTreeVisible || m_fContactsVisible)) { // Show ourselves m_fShow = TRUE; ShowWindow(m_hwnd, SW_SHOW); ResizeBorderDW(0, 0, 0); } } HRESULT CNavPane::QueryStatus(const GUID *pguidCmdGroup, ULONG cCmds, OLECMD prgCmds[], OLECMDTEXT *pCmdText) { if (m_pContactsTarget) { for (UINT i = 0; i < cCmds; i++) { if (prgCmds[i].cmdf == 0 && prgCmds[i].cmdID == ID_CONTACTS_MNEMONIC) { prgCmds->cmdf = OLECMDF_SUPPORTED | OLECMDF_ENABLED; } } } if (m_pContactsTarget) return (m_pContactsTarget->QueryStatus(pguidCmdGroup, cCmds, prgCmds, pCmdText)); return (S_OK); } HRESULT CNavPane::Exec(const GUID *pguidCmdGroup, DWORD nCmdID, DWORD nCmdExecOpt, VARIANTARG *pvaIn, VARIANTARG *pvaOut) { if (m_pContactsTarget && nCmdID == ID_CONTACTS_MNEMONIC) { m_pContactsFrame->ShowMenu(); return (S_OK); } if (m_pContactsTarget) return (m_pContactsTarget->Exec(pguidCmdGroup, nCmdID, nCmdExecOpt, pvaIn, pvaOut)); return (OLECMDERR_E_NOTSUPPORTED); } BOOL CNavPane::IsContactsFocus(void) { IInputObject *pInputObject = 0; HRESULT hr = S_FALSE; if (m_pContacts) { if (SUCCEEDED(m_pContacts->QueryInterface(IID_IInputObject, (LPVOID *) &pInputObject))) { hr = pInputObject->HasFocusIO(); pInputObject->Release(); return (S_OK == hr); } } return (S_OK == hr); } HRESULT CNavPane::OnFocusChangeIS(IUnknown *punkSrc, BOOL fSetFocus) { // Simply call through to our host UnkOnFocusChangeIS(m_pSite, (IInputObject*) this, fSetFocus); return (S_OK); } HRESULT CNavPane::UIActivateIO(BOOL fActivate, LPMSG lpMsg) { if (fActivate) { UnkOnFocusChangeIS(m_pSite, (IInputObject *) this, TRUE); SetFocus(m_hwnd); } return (S_OK); } HRESULT CNavPane::HasFocusIO(void) { if (m_hwnd == 0) return (S_FALSE); HWND hwndFocus = GetFocus(); return (hwndFocus == m_hwnd || IsChild(m_hwnd, hwndFocus)) ? S_OK : S_FALSE; } HRESULT CNavPane::TranslateAcceleratorIO(LPMSG pMsg) { if (m_pTreeView && (m_pTreeView->HasFocusIO() == S_OK)) return m_pTreeView->TranslateAcceleratorIO(pMsg); if (m_pContacts && (UnkHasFocusIO(m_pContacts) == S_OK)) return UnkTranslateAcceleratorIO(m_pContacts, pMsg); return (S_FALSE); } ///////////////////////////////////////////////////////////////////////////// // CPaneFrame // CPaneFrame::CPaneFrame() { m_cRef = 1; m_hwnd = 0; m_hwndChild = 0; m_hwndParent = 0; m_szTitle[0] = 0; m_hFont = 0; m_hbr3DFace = 0; m_cyTitleBar = 0; m_fHighlightIndicator = FALSE; m_fHighlightPressed = FALSE; ZeroMemory(&m_rcTitleButton, sizeof(RECT)); m_hwndClose = 0; m_cButtons = 1; m_pBrowser = NULL; m_dwDispId = 0; m_pTarget = 0; m_idMenu = 0; m_fPin = FALSE; } CPaneFrame::~CPaneFrame() { if (m_hFont != 0) DeleteObject(m_hFont); if (m_hbr3DFace != 0) DeleteObject(m_hbr3DFace); } // // FUNCTION: CPaneFrame::Initialize() // // PURPOSE: Initializes the frame by telling the pane what it's title // should be. // // PARAMETERS: // [in] hwndParent // [in] idsTitle // // RETURN VALUE: // HWND // HWND CPaneFrame::Initialize(HWND hwndParent, IInputObjectSite *pSite, int idsTitle, int idMenu) { WNDCLASSEX wc; TraceCall("CPaneFrame::Initialize"); // This should be NULL Assert(NULL == m_hwnd); // Save this for later m_hwndParent = hwndParent; m_idMenu = idMenu; m_pSite = pSite; // Load the title AthLoadString(idsTitle, m_szTitle, ARRAYSIZE(m_szTitle)); // Register the window class if necessary wc.cbSize = sizeof(WNDCLASSEX); if (!GetClassInfoEx(g_hInst, c_szPaneFrameClass, &wc)) { wc.style = 0; wc.lpfnWndProc = _WndProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = g_hInst; wc.hCursor = LoadCursor(0, IDC_ARROW); wc.hbrBackground = (HBRUSH) (COLOR_3DFACE + 1); wc.lpszMenuName = NULL; wc.lpszClassName = c_szPaneFrameClass; wc.hIcon = NULL; wc.hIconSm = NULL; RegisterClassEx(&wc); } // Create the window m_hwnd = CreateWindowEx(WS_EX_CONTROLPARENT, c_szPaneFrameClass, m_szTitle, WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS, 0, 0, 0, 0, hwndParent, 0, g_hInst, this); if (!m_hwnd) { AssertSz(m_hwnd, "CPaneFrame::Initialize() - Failed to create a frame"); return (0); } return (m_hwnd); } // // FUNCTION: CPaneFrame::SetChild() // // PURPOSE: Allows the owner to tell us what the child window handle is. // BOOL CPaneFrame::SetChild(HWND hwndChild, DWORD dwDispId, IAthenaBrowser *pBrowser, IObjectWithSite *pObject, IOleCommandTarget *pTarget) { TraceCall("CPaneFrame::SetChild"); if (IsWindow(hwndChild)) { m_hwndChild = hwndChild; if (pBrowser) { m_pBrowser = pBrowser; m_dwDispId = dwDispId; } if (pObject) { pObject->SetSite((IInputObjectSite *) this); } if (pTarget) { m_pTarget = pTarget; } return (TRUE); } return (FALSE); } void CPaneFrame::ShowMenu(void) { if (m_idMenu) { _OnLButtonDown(m_hwnd, 0, m_rcTitleButton.left, m_rcTitleButton.top, 0); } } // // FUNCTION: CPaneFrame::QueryInterface() // // PURPOSE: Allows caller to retrieve the various interfaces supported by // this class. // HRESULT CPaneFrame::QueryInterface(REFIID riid, LPVOID *ppvObj) { TraceCall("CPaneFrame::QueryInterface"); *ppvObj = NULL; if (IsEqualIID(riid, IID_IUnknown)) *ppvObj = (LPVOID) (IInputObjectSite *) this; else if (IsEqualIID(riid, IID_IInputObjectSite)) *ppvObj = (LPVOID) (IInputObjectSite *) this; if (*ppvObj) { AddRef(); return (S_OK); } return (E_NOINTERFACE); } // // FUNCTION: CPaneFrame::AddRef() // // PURPOSE: Adds a reference count to this object. // ULONG CPaneFrame::AddRef(void) { TraceCall("CPaneFrame::AddRef"); return ((ULONG) InterlockedIncrement((LONG *) &m_cRef)); } // // FUNCTION: CPaneFrame::Release() // // PURPOSE: Releases a reference on this object. // ULONG CPaneFrame::Release(void) { TraceCall("CPaneFrame::Release"); if (0 == InterlockedDecrement((LONG *) &m_cRef)) { delete this; return 0; } return (m_cRef); } HRESULT CPaneFrame::OnFocusChangeIS(IUnknown *punkSrc, BOOL fSetFocus) { // Simply call through to our host UnkOnFocusChangeIS(m_pSite, (IInputObject*) this, fSetFocus); return (S_OK); } // // FUNCTION: CPaneFrame::_WndProc() // // PURPOSE: External callback. // LRESULT CALLBACK CPaneFrame::_WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { CPaneFrame *pThis; if (uMsg == WM_NCCREATE) { pThis = (CPaneFrame *) ((LPCREATESTRUCT) lParam)->lpCreateParams; SetWindowLongPtr(hwnd, GWLP_USERDATA, (LPARAM) pThis); } else pThis = (CPaneFrame *) GetWindowLongPtr(hwnd, GWLP_USERDATA); if (pThis) return (pThis->_FrameWndProc(hwnd, uMsg, wParam, lParam)); return (FALSE); } // // FUNCTION: CPaneFrame::_FrameWndProc() // // PURPOSE: Left as an exercise for the reader // LRESULT CALLBACK CPaneFrame::_FrameWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { HANDLE_MSG(hwnd, WM_CREATE, _OnCreate); HANDLE_MSG(hwnd, WM_SIZE, _OnSize); HANDLE_MSG(hwnd, WM_PAINT, _OnPaint); HANDLE_MSG(hwnd, WM_COMMAND, _OnCommand); HANDLE_MSG(hwnd, WM_MOUSEMOVE, _OnMouseMove); HANDLE_MSG(hwnd, WM_LBUTTONDOWN, _OnLButtonDown); HANDLE_MSG(hwnd, WM_TIMER, _OnTimer); case WM_TOGGLE_CLOSE_PIN: _OnToggleClosePin(hwnd, (BOOL) lParam); return (0); case WM_GET_TITLE_BAR_HEIGHT: return (m_cyTitleBar + (c_cyBorder * 2) + 1); case WM_SYSCOLORCHANGE: case WM_WININICHANGE: { // Forward these to all our children if (IsWindow(m_hwndChild)) SendMessage(m_hwndChild, uMsg, wParam, lParam); _UpdateDrawingInfo(); break; } case WM_SETFOCUS: { if (m_hwndChild && ((HWND)wParam) != m_hwndChild) SetFocus(m_hwndChild); break; } } return (DefWindowProc(hwnd, uMsg, wParam, lParam)); } // // FUNCTION: CPaneFrame::_OnCreate() // // PURPOSE: Loads some info that will be handy later // BOOL CPaneFrame::_OnCreate(HWND hwnd, LPCREATESTRUCT lpCreateStruct) { TraceCall("CPaneFrame::_OnCreate"); m_hwnd = hwnd; _UpdateDrawingInfo(); _CreateCloseToolbar(); return (TRUE); } // // FUNCTION: CPaneFrame::_OnSize() // // PURPOSE: Resizes our child to fit in the right place // void CPaneFrame::_OnSize(HWND hwnd, UINT state, int cx, int cy) { TraceCall("CPaneFrame::_OnSize"); m_rcChild.left = c_cyBorder; m_rcChild.top = m_cyTitleBar; m_rcChild.right = cx - (2 * c_cyBorder); m_rcChild.bottom = cy - m_cyTitleBar - c_cyBorder; if (m_hwndChild) SetWindowPos(m_hwndChild, 0, m_rcChild.left, m_rcChild.top, m_rcChild.right, m_rcChild.bottom, SWP_NOZORDER | SWP_NOACTIVATE); POINT pt = {cx, cy}; _PositionToolbar(&pt); // Invalidate the title area RECT rc = m_rcChild; rc.top = 0; rc.bottom = m_rcChild.top; InvalidateRect(m_hwnd, &rc, FALSE); rc.left = 0; rc.right = c_cyBorder; rc.bottom = cy; InvalidateRect(m_hwnd, &rc, FALSE); rc.left = cx - c_cyBorder; rc.right = cx; InvalidateRect(m_hwnd, &rc, FALSE); } // // FUNCTION: CPaneFrame::_OnPaint() // // PURPOSE: Called when it's time to paint our borders and title area. // void CPaneFrame::_OnPaint(HWND hwnd) { HDC hdc; PAINTSTRUCT ps; RECT rc; RECT rcClient; POINT pt[3]; HBRUSH hBrush, hBrushOld; HPEN hPen, hPenOld; // Get our window size GetClientRect(m_hwnd, &rcClient); rc = rcClient; // Start painting hdc = BeginPaint(hwnd, &ps); // Draw a simple edge around or window DrawEdge(hdc, &rc, BDR_SUNKENOUTER, BF_TOPRIGHT | BF_BOTTOMLEFT); // Now draw a raised edge around our title bar area InflateRect(&rc, -1, -1); rc.bottom = m_cyTitleBar; DrawEdge(hdc, &rc, BDR_RAISEDINNER, BF_TOPRIGHT | BF_BOTTOMLEFT); // Paint the background InflateRect(&rc, -c_cxBorder, -c_cyBorder); FillRect(hdc, &rc, m_hbr3DFace); // Now draw some groovy text SelectFont(hdc, m_hFont); SetBkColor(hdc, GetSysColor(COLOR_3DFACE)); SetTextColor(hdc, GetSysColor(COLOR_BTNTEXT)); // Draw the text InflateRect(&rc, -c_cxTextBorder, -c_cyTextBorder); if (!m_fPin) { DrawText(hdc, m_szTitle, -1, &rc, DT_CALCRECT | DT_VCENTER | DT_LEFT); DrawText(hdc, m_szTitle, -1, &rc, DT_VCENTER | DT_LEFT); } else { TCHAR sz[CCHMAX_STRINGRES]; AthLoadString(idsPushPinInfo, sz, ARRAYSIZE(sz)); IDrawText(hdc, sz, &rc, DT_VCENTER | DT_END_ELLIPSIS | DT_LEFT, rc.bottom - rc.top); DrawText(hdc, sz, -1, &rc, DT_CALCRECT | DT_VCENTER | DT_END_ELLIPSIS | DT_LEFT); } // Drop-down indicator if (m_idMenu) { COLORREF crFG = GetSysColor(COLOR_WINDOWTEXT); pt[0].x = rc.right + 6; pt[0].y = (m_cyTitleBar - 6) / 2 + 2; pt[1].x = pt[0].x + 6; pt[1].y = pt[0].y; pt[2].x = pt[0].x + 3; pt[2].y = pt[0].y + 3; hPen = CreatePen(PS_SOLID, 1, crFG); hBrush = CreateSolidBrush(crFG); hPenOld = SelectPen(hdc, hPen); hBrushOld = SelectBrush(hdc, hBrush); Polygon(hdc, pt, 3); SelectPen(hdc, hPenOld); SelectBrush(hdc, hBrushOld); DeleteObject(hPen); DeleteObject(hBrush); if (m_fHighlightIndicator) { rc = m_rcTitleButton; DrawEdge(hdc, &rc, m_fHighlightPressed ? BDR_SUNKENOUTER : BDR_RAISEDINNER, BF_TOPRIGHT | BF_BOTTOMLEFT); } } EndPaint(hwnd, &ps); } // // FUNCTION: _OnCommand() // // PURPOSE: We get the occasional command now and again // void CPaneFrame::_OnCommand(HWND hwnd, int id, HWND hwndCtl, UINT codeNotify) { switch (id) { case ID_PANE_CLOSE: { if (m_pBrowser) m_pBrowser->SetViewLayout(m_dwDispId, LAYOUT_POS_NA, FALSE, 0, 0); return; } case ID_PANE_PIN: { SendMessage(m_hwndChild, WMR_CLICKOUTSIDE, CLK_OUT_DEACTIVATE, 0); if (m_pBrowser) m_pBrowser->SetViewLayout(m_dwDispId, LAYOUT_POS_NA, TRUE, 0, 0); return; } } return; } // // FUNCTION: CPaneFrame::_OnToggleClosePin() // // PURPOSE: Sent to the frame when we should change the close button // to a pin button. // // PARAMETERS: // [in] fPin - TRUE to turn the Pin on, FALSE to turn it off. // void CPaneFrame::_OnToggleClosePin(HWND hwnd, BOOL fPin) { TraceCall("CPaneFrame::_OnToggleClosePin"); if (fPin) { static const TBBUTTON tb[] = { { 2, ID_PANE_PIN, TBSTATE_ENABLED, TBSTYLE_BUTTON, {0, 0}, 0, 0} }; SendMessage(m_hwndClose, TB_DELETEBUTTON, 0, 0); SendMessage(m_hwndClose, TB_ADDBUTTONS, ARRAYSIZE(tb), (LPARAM) tb); SendMessage(m_hwndClose, TB_SETHOTITEM, (WPARAM) -1, 0); m_fPin = TRUE; } else { static const TBBUTTON tb[] = { { 1, ID_PANE_CLOSE, TBSTATE_ENABLED, TBSTYLE_BUTTON, {0, 0}, 0, 0} }; SendMessage(m_hwndClose, TB_DELETEBUTTON, 0, 0); SendMessage(m_hwndClose, TB_ADDBUTTONS, ARRAYSIZE(tb), (LPARAM) tb); SendMessage(m_hwndClose, TB_SETHOTITEM, (WPARAM) -1, 0); m_fPin = FALSE; } } // // FUNCTION: CPaneFrame::_UpdateDrawingInfo() // // PURPOSE: When we get created or when the user changes their settings, // we need to reload our fonts, colors, and sizes. // void CPaneFrame::_UpdateDrawingInfo(void) { LOGFONT lf; TEXTMETRIC tm; HDC hdc; TraceCall("CPaneFrame::_UpdateDrawingInfo"); if (m_hFont) DeleteObject(m_hFont); // Figure out which font to use SystemParametersInfo(SPI_GETICONTITLELOGFONT, sizeof(LOGFONT), &lf, FALSE); // Create the font m_hFont = CreateFontIndirect(&lf); // Get the metrics of this font hdc = GetDC(m_hwnd); SelectFont(hdc, m_hFont); GetTextMetrics(hdc, &tm); // Calculate the height m_cyTitleBar = tm.tmHeight + (2 * c_cyBorder) + (2 * c_cyTextBorder); RECT rc = {2 * c_cxBorder, 2 * c_cyBorder, 0, m_cyTitleBar - c_cyBorder}; SIZE s; GetTextExtentPoint32(hdc, m_szTitle, lstrlen(m_szTitle), &s); m_rcTitleButton = rc; m_rcTitleButton.right = 14 + (2 * c_cxTextBorder) + s.cx + (2 * c_cxBorder); ReleaseDC(m_hwnd, hdc); // Get the brush we need if (m_hbr3DFace) DeleteObject(m_hbr3DFace); m_hbr3DFace = CreateSolidBrush(GetSysColor(COLOR_3DFACE)); } // // FUNCTION: CPaneFrame::_CreateCloseToolbar() // // PURPOSE: Creates the toolbar that has our close button // void CPaneFrame::_CreateCloseToolbar() { CHAR szTitle[255]; TraceCall("CPaneFrame::_CreateCloseToolbar"); AthLoadString(idsHideFolders, szTitle, ARRAYSIZE(szTitle)); m_hwndClose = CreateWindowEx(0, TOOLBARCLASSNAME, szTitle, WS_VISIBLE | WS_CHILD | TBSTYLE_FLAT | TBSTYLE_CUSTOMERASE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | CCS_NODIVIDER | CCS_NOMOVEY | CCS_NOPARENTALIGN | CCS_NORESIZE, 0, c_cyClose, 30, 15, m_hwnd, 0, g_hInst, NULL); if (m_hwndClose) { static const TBBUTTON tb[] = { { 1, ID_PANE_CLOSE, TBSTATE_ENABLED, TBSTYLE_BUTTON, {0, 0}, 0, 0} }; SendMessage(m_hwndClose, TB_BUTTONSTRUCTSIZE, sizeof(TBBUTTON), 0); SendMessage(m_hwndClose, TB_SETBITMAPSIZE, 0, (LPARAM) MAKELONG(11, 9)); TBADDBITMAP tbab = { g_hLocRes, idbClosePin }; SendMessage(m_hwndClose, TB_ADDBITMAP, 4, (LPARAM) &tbab); SendMessage(m_hwndClose, TB_ADDBUTTONS, ARRAYSIZE(tb), (LPARAM) tb); SendMessage(m_hwndClose, TB_SETINDENT, 0, 0); _SizeCloseToolbar(); } } // // FUNCTION: CPaneFrame::_SizeCloseToolbar() // // PURPOSE: Set's the size of the toolbar appropriately. // void CPaneFrame::_SizeCloseToolbar(void) { TraceCall("CPaneFrame::_SizeCloseToolbar"); RECT rc; LONG lButtonSize; GetWindowRect(m_hwndClose, &rc); lButtonSize = (LONG) SendMessage(m_hwndClose, TB_GETBUTTONSIZE, 0, 0L); SetWindowPos(m_hwndClose, NULL, 0, 0, LOWORD(lButtonSize) * m_cButtons, rc.bottom - rc.top, SWP_NOMOVE | SWP_NOACTIVATE); _PositionToolbar(NULL); } // // FUNCTION: CPaneFrame::_PositionToolbar() // // PURPOSE: Does the work of correctly positioning the close button // toolbar. // // PARAMETERS: // LPPOINT ppt // void CPaneFrame::_PositionToolbar(LPPOINT ppt) { TraceCall("CPaneFrame::_PositionToolbar"); if (m_hwndClose) { RECT rc; GetClientRect(m_hwnd, &rc); if (ppt) { rc.left = 0; rc.right = ppt->x; } RECT rcTB; GetWindowRect(m_hwndClose, &rcTB); rc.left = rc.right - (rcTB.right - rcTB.left) - 3; DWORD top = max((int) ((m_cyTitleBar - (rcTB.bottom - rcTB.top)) / 2) + 1, 0); SetWindowPos(m_hwndClose, HWND_TOP, rc.left, top, 0, 0, SWP_NOSIZE | SWP_NOACTIVATE); } } void CPaneFrame::_OnLButtonDown(HWND hwnd, BOOL fDoubleClick, int x, int y, UINT keyFlags) { POINT pt = {x, y}; UINT id; if (m_idMenu && PtInRect(&m_rcTitleButton, pt)) { m_fHighlightPressed = TRUE; InvalidateRect(m_hwnd, &m_rcTitleButton, TRUE); UpdateWindow(m_hwnd); HMENU hMenu = LoadPopupMenu(m_idMenu); MenuUtil_EnablePopupMenu(hMenu, m_pTarget); if (m_idMenu == IDR_BA_TITLE_POPUP && ((g_dwHideMessenger == BL_HIDE) || (g_dwHideMessenger == BL_DISABLE))) { DeleteMenu(hMenu, ID_NEW_ONLINE_CONTACT, MF_BYCOMMAND); DeleteMenu(hMenu, ID_SET_ONLINE_CONTACT, MF_BYCOMMAND); DeleteMenu(hMenu, SEP_MESSENGER, MF_BYCOMMAND); DeleteMenu(hMenu, ID_SORT_BY_NAME, MF_BYCOMMAND); DeleteMenu(hMenu, ID_SORT_BY_STATUS, MF_BYCOMMAND); } pt.x = m_rcTitleButton.left; pt.y = m_rcTitleButton.bottom; ClientToScreen(m_hwnd, &pt); id = TrackPopupMenuEx(hMenu, TPM_RETURNCMD | TPM_LEFTALIGN | TPM_LEFTBUTTON | TPM_RIGHTBUTTON, pt.x, pt.y, m_hwnd, NULL); if (id) { m_pTarget->Exec(NULL, id, OLECMDEXECOPT_DODEFAULT, NULL, NULL); } m_fHighlightPressed = m_fHighlightIndicator = FALSE; KillTimer(m_hwnd, IDT_PANETIMER); InvalidateRect(m_hwnd, &m_rcTitleButton, TRUE); UpdateWindow(m_hwnd); if(hMenu) { //Bug #101329 - (erici) Destroy leaked MENU. BOOL bMenuDestroyed = DestroyMenu(hMenu); Assert(bMenuDestroyed); } } } void CPaneFrame::_OnMouseMove(HWND hwnd, int x, int y, UINT keyFlags) { POINT pt = {x, y}; if (m_idMenu && (m_fHighlightIndicator != PtInRect(&m_rcTitleButton, pt))) { m_fHighlightIndicator = !m_fHighlightIndicator; InvalidateRect(m_hwnd, &m_rcTitleButton, TRUE); if (m_fHighlightIndicator) SetTimer(m_hwnd, IDT_PANETIMER, ELAPSE_MOUSEOVERCHECK, NULL); else KillTimer(m_hwnd, IDT_PANETIMER); } } void CPaneFrame::_OnTimer(HWND hwnd, UINT id) { RECT rcClient; POINT pt; DWORD dw; dw = GetMessagePos(); pt.x = LOWORD(dw); pt.y = HIWORD(dw); ScreenToClient(m_hwnd, &pt); if (id == IDT_PANETIMER) { GetClientRect(m_hwnd, &rcClient); // No need to handle mouse in client area, OnMouseMove will catch this. We // only need to catch the mouse moving out of the client area. if (!PtInRect(&rcClient, pt) && !m_fHighlightPressed) { KillTimer(m_hwnd, IDT_PANETIMER); m_fHighlightIndicator = FALSE; InvalidateRect(m_hwnd, &m_rcTitleButton, TRUE); } } }