Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

3728 lines
116 KiB

/////////////////////////////////////////////////////////////////////////////
// Copyright (C) 1993-1998 Microsoft Corporation. All Rights Reserved.
//
// MODULE: msgview.cpp
//
// PURPOSE: Implements the Outlook Express view class that handles
// displaying the contents of folders with messages.
//
#include "pch.hxx"
#include "msgview.h"
#include "browser.h"
#include "thormsgs.h"
#include "msglist.h"
#include "msoedisp.h"
#include "statbar.h"
#include "ibodyobj.h"
#include "mehost.h"
#include "util.h"
#include "shlwapip.h"
#include "menuutil.h"
#include "storutil.h"
#include "ruleutil.h"
#include "note.h"
#include "newsutil.h"
#include "menures.h"
#include "ipab.h"
#include "order.h"
#include <inetcfg.h>
#include "instance.h"
/////////////////////////////////////////////////////////////////////////////
// Global Data
//
static const char s_szMessageViewWndClass[] = TEXT("Outlook Express Message View");
extern BOOL g_fBadShutdown;
/////////////////////////////////////////////////////////////////////////////
// Prototypes
//
/////////////////////////////////////////////////////////////////////////////
// Message Macros
//
// void OnPostCreate(HWND hwnd)
#define HANDLE_WM_POSTCREATE(hwnd, wParam, lParam, fn) \
((fn)(hwnd), 0L)
#define FORWARD_WM_POSTCREATE(hwnd, fn) \
(void)(fn)((hwnd), WM_POSTCREATE, 0L, 0L)
// LRESULT OnTestGetMsgId(HWND hwnd)
#define HANDLE_WM_TEST_GETMSGID(hwnd, wParam, lParam, fn) \
(LRESULT)((fn)(hwnd))
#define FORWARD_WM_TEST_GETMSGID(hwnd, fn) \
(LRESULT)(fn)((hwnd), WM_TEST_GETMSGID, 0L, 0L)
// LRESULT OnTestSaveMessage(HWND hwnd)
#define HANDLE_WM_TEST_SAVEMSG(hwnd, wParam, lParam, fn) \
(LRESULT)((fn)(hwnd))
#define FORWARD_WM_TEST_SAVEMSG(hwnd, fn) \
(LRESULT)(fn)((hwnd), WM_TEST_SAVEMSG, 0L, 0L)
/////////////////////////////////////////////////////////////////////////////
// Constructors, Destructors, and Initialization
//
CMessageView::CMessageView()
{
m_cRef = 1;
m_hwnd = NULL;
m_hwndParent = NULL;
m_pBrowser = NULL;
m_idFolder = FOLDERID_INVALID;
m_pDropTarget = NULL;
m_pMsgList = NULL;
m_pMsgListCT = NULL;
m_pMsgListAO = NULL;
m_dwCookie = 0;
m_pServer = NULL;
m_pPreview = NULL;
m_pPreviewCT = NULL;
m_fSplitHorz = TRUE;
SetRect(&m_rcSplit, 0, 0, 0, 0);
m_dwSplitVertPct = 50;
m_dwSplitHorzPct = 50;
m_fDragging = FALSE;
m_uUIState = SVUIA_DEACTIVATE;
m_cUnread = 0;
m_cItems = 0;
m_pGroups = NULL;
m_idMessageFocus = MESSAGEID_INVALID;
m_pProgress = NULL;
m_fNotDownloaded = FALSE;
m_cLastChar = GetTickCount();
m_pViewMenu = NULL;
}
CMessageView::~CMessageView()
{
SafeRelease(m_pViewMenu);
if (m_pGroups != NULL)
{
m_pGroups->Close();
m_pGroups->Release();
}
SafeRelease(m_pBrowser);
SafeRelease(m_pMsgList);
SafeRelease(m_pMsgListCT);
SafeRelease(m_pMsgListAO);
SafeRelease(m_pPreview);
SafeRelease(m_pPreviewCT);
SafeRelease(m_pProgress);
SafeRelease(m_pDropTarget);
Assert(NULL == m_pServer);
}
//
// FUNCTION: CMessageView::Initialize()
//
// PURPOSE: Get's called to initialize the object and tell it what folder
// it will be looking at.
//
// PARAMETERS:
// [in] pidl
// [in] *pFolder
//
// RETURN VALUE:
// HRESULT
//
HRESULT CMessageView::Initialize(FOLDERID idFolder)
{
TraceCall("CMessageView::Initialize");
// Copy the pidl, we'll use it later
m_idFolder = idFolder;
return (S_OK);
}
/////////////////////////////////////////////////////////////////////////////
// IUnknown
//
HRESULT CMessageView::QueryInterface(REFIID riid, LPVOID *ppvObj)
{
*ppvObj = NULL;
if (IsEqualIID(riid, IID_IUnknown))
*ppvObj = (LPVOID) (IUnknown *) (IViewWindow *) this;
else if (IsEqualIID(riid, IID_IOleWindow))
*ppvObj = (LPVOID) (IViewWindow *) this;
else if (IsEqualIID(riid, IID_IViewWindow))
*ppvObj = (LPVOID) (IViewWindow *) this;
else if (IsEqualIID(riid, IID_IMessageWindow))
*ppvObj = (LPVOID) (IMessageWindow *) this;
else if (IsEqualIID(riid, IID_IOleCommandTarget))
*ppvObj = (LPVOID) (IOleCommandTarget *) this;
else if (IsEqualIID(riid, IID_IBodyOptions))
*ppvObj = (LPVOID) (IBodyOptions *) this;
else if (IsEqualIID(riid, IID_IDispatch))
*ppvObj = (LPVOID) (IDispatch *) this;
else if (IsEqualIID(riid, DIID__MessageListEvents))
*ppvObj = (LPVOID) (IDispatch *) this;
else if (IsEqualIID(riid, IID_IServerInfo))
*ppvObj = (LPVOID) (IServerInfo *) this;
if (NULL == *ppvObj)
return (E_NOINTERFACE);
AddRef();
return S_OK;
}
ULONG CMessageView::AddRef(void)
{
return InterlockedIncrement((LONG *) &m_cRef);
}
ULONG CMessageView::Release(void)
{
InterlockedDecrement((LONG *) &m_cRef);
if (0 == m_cRef)
{
delete this;
return (0);
}
return (m_cRef);
}
/////////////////////////////////////////////////////////////////////////////
// IOleWindow
//
HRESULT CMessageView::GetWindow(HWND *pHwnd)
{
if (!pHwnd)
return (E_INVALIDARG);
if (m_hwnd)
{
*pHwnd = m_hwnd;
return (S_OK);
}
return (E_FAIL);
}
HRESULT CMessageView::ContextSensitiveHelp(BOOL fEnterMode)
{
return (E_NOTIMPL);
}
/////////////////////////////////////////////////////////////////////////////
// IViewWindow
//
//
// FUNCTION: CMessageView::TranslateAccelerator()
//
// PURPOSE: Called by the frame window to give us first crack at messages.
//
// PARAMETERS:
// [in] pMsg - The current message to be processed.
//
// RETURN VALUE:
// S_OK if the message was handled here and should not be processed further.
// S_FALSE if the message should continued to be processed elsewhere.
//
HRESULT CMessageView::TranslateAccelerator(LPMSG pMsg)
{
DWORD dwState = 0;
// See if the Preview Pane is interested
if (m_pPreview)
{
if (S_OK == m_pPreview->HrTranslateAccelerator(pMsg))
return (S_OK);
if (IsChild(m_hwnd, GetFocus()))
{
if (pMsg->message == WM_KEYDOWN && pMsg->wParam != VK_SPACE)
m_cLastChar = GetTickCount();
if (pMsg->message == WM_KEYDOWN &&
pMsg->wParam == VK_SPACE &&
GetTickCount() - m_cLastChar > 1000)
{
if (m_fNotDownloaded)
{
_UpdatePreviewPane(TRUE);
}
else if (SUCCEEDED(m_pMsgList->GetFocusedItemState(&dwState)) && dwState != 0)
{
if (m_pPreview->HrScrollPage()!=S_OK)
m_pMsgListCT->Exec(NULL, ID_SPACE_ACCEL, 0, NULL, NULL);
}
else
m_pMsgListCT->Exec(NULL, ID_SPACE_ACCEL, 0, NULL, NULL);
return S_OK;
}
}
}
// See if the message list is interested
if (m_pMsgListAO)
{
if (S_OK == m_pMsgListAO->TranslateAccelerator(pMsg))
return (S_OK);
}
return (S_FALSE);
}
//
// FUNCTION: CMessageView::UIActivate()
//
// PURPOSE: Called to notify the view when different activation and
// deactivation events occur.
//
// PARAMETERS:
// [in] uState - SVUIA_ACTIVATE_FOCUS, SVUIA_ACTIVATE_NOFOCUS, and
// SVUIA_DEACTIVATE.
//
// RETURN VALUE:
// Returns S_OK all the time.
//
HRESULT CMessageView::UIActivate(UINT uState)
{
if (uState != SVUIA_DEACTIVATE)
{
// If the focus stays within our frame, bug goes outside our view,
// i.e. the folder list get's focus, then we get an
// SVUIA_ACTIVATE_NOFOCUS. We need to UI Deactivate the preview
// pane when this happens.
if (uState == SVUIA_ACTIVATE_NOFOCUS && m_pPreview)
m_pPreview->HrUIActivate(FALSE);
if (m_uUIState != uState)
{
// Update our internal state
m_uUIState = uState;
// Update the toolbar state
m_pBrowser->UpdateToolbar();
}
}
else
{
// Only deactivate if we're not already deactivated
if (m_uUIState != SVUIA_DEACTIVATE)
{
// Update our internal state
m_uUIState = uState;
}
}
return (S_OK);
}
//
// FUNCTION: CMessageView::CreateViewWindow()
//
// PURPOSE: Called when it's time for the view to create it's window.
//
// PARAMETERS:
// [in] pPrevView - Pointer to the previous view if there was one
// [in] pBrowser - Pointer to the browser that hosts this view
// [in] prcView - Initial position and size of the view
// [out] pHwnd - Returns the HWND of the newly created view window
//
// RETURN VALUE:
// S_OK if the view window was created successfully.
// E_FAIL if the window couldn't be created for some reason or another.
//
HRESULT CMessageView::CreateViewWindow(IViewWindow *pPrevView, IAthenaBrowser *pBrowser,
RECT *prcView, HWND *pHwnd)
{
WNDCLASS wc;
// Without a browser pointer nothing will ever work.
if (!pBrowser)
return (E_INVALIDARG);
// Hang on to the browser pointer
m_pBrowser = pBrowser;
m_pBrowser->AddRef();
// Get the window handle of the browser
m_pBrowser->GetWindow(&m_hwndParent);
Assert(IsWindow(m_hwndParent));
// Load our persisted settings. If this fails will just run with defaults.
// _LoadSettings();
// Register our window class if we haven't already
if (!GetClassInfo(g_hInst, s_szMessageViewWndClass, &wc))
{
wc.style = 0;
wc.lpfnWndProc = CMessageView::ViewWndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = g_hInst;
wc.hIcon = NULL;
wc.hCursor = NULL;
wc.hbrBackground = (HBRUSH) (COLOR_3DFACE + 1);
wc.lpszMenuName = NULL;
wc.lpszClassName = s_szMessageViewWndClass;
if (!RegisterClass(&wc))
return (E_FAIL);
}
// Create the view window
m_hwnd = CreateWindowEx(WS_EX_CONTROLPARENT , s_szMessageViewWndClass, NULL,
WS_VISIBLE | WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS,
prcView->left, prcView->top, prcView->right - prcView->left,
prcView->bottom - prcView->top, m_hwndParent, NULL,
g_hInst, (LPVOID) this);
if (!m_hwnd)
return (E_FAIL);
*pHwnd = m_hwnd;
// Get the message folder object from the previous folder here.
_ReuseMessageFolder(pPrevView);
return (S_OK);
}
//
// FUNCTION: CMessageView::DestroyViewWindow()
//
// PURPOSE: Called by the browser to destroy the view window.
//
// RETURN VALUE:
// S_OK is returned always.
//
HRESULT CMessageView::DestroyViewWindow(void)
{
// This is of course only interesting if we actually _have_ a window to
// destroy.
if (m_hwnd)
{
// Tell the message list we're done with this folder
if (m_pMsgList)
{
m_pMsgList->SetFolder(FOLDERID_INVALID, NULL, 0, 0, 0);
}
// Unadvise our connection point
if (m_dwCookie)
{
AtlUnadvise(m_pMsgList, DIID__MessageListEvents, m_dwCookie);
m_dwCookie = 0;
}
// $REVIEW - PreDestroyViewWindow() used to be called here to tell the subclasses
// of the iminent destruction.
// Set our cached HWND to NULL before destroying prevents us from
// handling notifications after important stuff has been freed.
HWND hwndDest = m_hwnd;
m_hwnd = NULL;
DestroyWindow(hwndDest);
}
return (S_OK);
}
//
// FUNCTION: CMessageView::SaveViewState()
//
// PURPOSE: Called by the browser to give the view a chance to save it's
// settings before it is destroyed.
//
// RETURN VALUE:
// E_NOTIMPL
//
HRESULT CMessageView::SaveViewState(void)
{
FOLDERTYPE ft = GetFolderType(m_idFolder);
// Tell the message list to save it's state
if (m_pMsgList)
{
m_pMsgList->OnClose();
// We also need to save any settings that might have changed
FOLDER_OPTIONS fo = { 0 };
fo.cbSize = sizeof(FOLDER_OPTIONS);
fo.dwMask = FOM_THREAD | FOM_OFFLINEPROMPT | FOM_SHOWDELETED | FOM_SHOWREPLIES;
if (SUCCEEDED(m_pMsgList->GetViewOptions(&fo)))
{
switch (ft)
{
case FOLDER_NEWS:
SetDwOption(OPT_NEWS_THREAD, fo.fThread, 0, 0);
break;
case FOLDER_LOCAL:
case FOLDER_HTTPMAIL:
SetDwOption(OPT_MAIL_THREAD, fo.fThread, 0, 0);
break;
case FOLDER_IMAP:
SetDwOption(OPT_MAIL_THREAD, fo.fThread, 0, 0);
break;
}
SetDwOption(OPT_SHOW_DELETED, (DWORD) (fo.fDeleted), 0, 0);
SetDwOption(OPT_SHOW_REPLIES, (DWORD) (fo.fReplies), 0, 0);
}
}
// Reset the contents of the status bar
CStatusBar *pStatusBar;
m_pBrowser->GetStatusBar(&pStatusBar);
if (pStatusBar)
{
pStatusBar->SetStatusText("");
pStatusBar->Release();
}
return (S_OK);
}
//
// FUNCTION: CMessageView::OnPopupMenu()
//
// PURPOSE: Called whenever the frame receives a WM_INITMENUPOPUP
// notification. The view adds any menu items or sets any
// check marks that are appropriate.
//
// PARAMETERS:
// [in] hMenu - The handle of the root menu bar
// [in] hMenuPopup - The handle of the specific popup menu
// [in] uID - The ID of the popup menu
//
// RETURN VALUE:
// Unused
//
HRESULT CMessageView::OnPopupMenu(HMENU hMenu, HMENU hMenuPopup, UINT uID)
{
MENUITEMINFO mii;
UINT uItem;
HCHARSET hCharset;
// Handle our items
switch (uID)
{
case ID_POPUP_LANGUAGE:
{
mii.cbSize = sizeof(MENUITEMINFO);
mii.fMask = MIIM_SUBMENU;
UINT uiCodepage = 0;
HMENU hLangMenu = NULL;
m_pPreview->HrGetCharset(&hCharset);
uiCodepage = CustomGetCPFromCharset(hCharset, TRUE);
if(m_pBrowser->GetLanguageMenu(&hLangMenu, uiCodepage) == S_OK)
{
if(IsMenu(hMenuPopup))
DestroyMenu(hMenuPopup);
hMenuPopup = mii.hSubMenu = hLangMenu;
SetMenuItemInfo(hMenu, ID_POPUP_LANGUAGE, FALSE, &mii);
}
break;
}
case ID_POPUP_VIEW:
{
if (NULL == m_pViewMenu)
{
// Create the view menu
HrCreateViewMenu(0, &m_pViewMenu);
}
if (NULL != m_pViewMenu)
{
mii.cbSize = sizeof(MENUITEMINFO);
mii.fMask = MIIM_SUBMENU;
if (FALSE == GetMenuItemInfo(hMenuPopup, ID_POPUP_FILTER, FALSE, &mii))
{
break;
}
// Remove the old filter submenu
if(IsMenu(mii.hSubMenu))
DestroyMenu(mii.hSubMenu);
// Replace the view menu
if (FAILED(m_pViewMenu->HrReplaceMenu(0, hMenuPopup)))
{
break;
}
}
break;
}
case ID_POPUP_FILTER:
{
if (NULL != m_pViewMenu)
{
m_pViewMenu->UpdateViewMenu(0, hMenuPopup, m_pMsgList);
}
break;
}
}
// Let the message list update it's menus
if (m_pMsgList)
m_pMsgList->OnPopupMenu(hMenuPopup, uID);
// Let the preview pane update it's menus
if (m_pPreview)
m_pPreview->HrOnInitMenuPopup(hMenuPopup, uID);
return (S_OK);
}
HRESULT CMessageView::OnFrameWindowActivate(BOOL fActivate)
{
if (m_pPreview)
return m_pPreview->HrFrameActivate(fActivate);
return (S_OK);
}
HRESULT CMessageView::UpdateLayout(BOOL fVisible, BOOL fHeader, BOOL fVert,
BOOL fUpdate)
{
// If we haven't created the preview pane yet, and the call is telling
// us to make it visible, then we need to initialize it first.
if (!m_pPreview && fVisible)
{
if (!_InitPreviewPane())
return (E_UNEXPECTED);
}
// Header on / off
if (m_pPreview)
{
m_pPreview->HrSetStyle(fHeader ? MESTYLE_PREVIEW : MESTYLE_MINIHEADER);
}
// Split direction
if (m_pPreview)
{
RECT rcClient;
m_fSplitHorz = !fVert;
GetClientRect(m_hwnd, &rcClient);
OnSize(m_hwnd, SIZE_RESTORED, rcClient.right, rcClient.bottom);
}
//
// [PaulHi] 6/11/99 Raid 79491
// Backing out this fix that BrettM made for Raid 63739 because of problems
// with security message warnings.
//
#if 0
if (fVisible)
{
// if showing update the preview pane
_UpdatePreviewPane();
}
else
{
// if hiding, clear the contents
if (NULL != m_pPreview)
m_pPreview->HrUnloadAll(NULL, 0);
}
#endif
return (S_OK);
}
HRESULT CMessageView::GetMessageList(IMessageList ** ppMsgList)
{
HRESULT hr = S_OK;
// Check incoming params
if (NULL == ppMsgList)
{
hr = E_INVALIDARG;
goto exit;
}
// Initialize the outgoing param
*ppMsgList = NULL;
// Get the message list
if (NULL != m_pMsgList)
{
*ppMsgList = m_pMsgList;
(*ppMsgList)->AddRef();
}
// Set the return value
hr = (NULL == *ppMsgList) ? S_FALSE : S_OK;
exit:
return hr;
}
HRESULT CMessageView::GetCurCharSet(UINT *cp)
{
HCHARSET hCharset;
if(_IsPreview())
{
m_pPreview->HrGetCharset(&hCharset);
*cp = CustomGetCPFromCharset(hCharset, TRUE);
}
else
*cp = GetACP();
return S_OK;
}
//
// FUNCTION: CMessageView::QueryStatus()
//
// PURPOSE: Called by the browser to determine if a list of commands should
// should be enabled or disabled.
//
// PARAMETERS:
// [in] pguidCmdGroup - Group the commands are part of (unused)
// [in] cCmds - Number of commands to be evaluated
// [in] prgCmds - List of commands
// [out] pCmdText - Description text for a command
//
// RETURN VALUE:
// HRESULT
//
HRESULT CMessageView::QueryStatus(const GUID *pguidCmdGroup, ULONG cCmds, OLECMD prgCmds[],
OLECMDTEXT *pCmdText)
{
DWORD cSel;
HRESULT hr;
HWND hwndFocus = GetFocus();
BOOL fChildFocus = (hwndFocus != NULL && IsChild(m_hwnd, hwndFocus));
DWORD cFocus;
DWORD *rgSelected = 0;
FOLDERTYPE ftType;
// Let the sub objects look first
if (m_pMsgListCT)
{
hr = m_pMsgListCT->QueryStatus(pguidCmdGroup, cCmds, prgCmds, pCmdText);
}
if (_IsPreview() && m_pPreviewCT)
{
hr = m_pPreviewCT->QueryStatus(pguidCmdGroup, cCmds, prgCmds, pCmdText);
}
// Up front some work
m_pMsgList->GetSelected(&cFocus, &cSel, &rgSelected);
// Now loop through the commands in the prgCmds array looking for ones the
// sub objects didn't handle.
for (UINT i = 0; i < cCmds; i++)
{
if (prgCmds[i].cmdf == 0)
{
// If this command is from the language menu
if (prgCmds[i].cmdID >= ID_LANG_FIRST && prgCmds[i].cmdID <= ID_LANG_LAST)
{
HCHARSET hCharset;
m_pPreview->HrGetCharset(&hCharset);
// Enable only the supported languages
if (prgCmds[i].cmdID < (UINT) (ID_LANG_FIRST + GetIntlCharsetLanguageCount()))
{
#if 0
if(SetMimeLanguageCheckMark(CustomGetCPFromCharset(hCharset, TRUE), prgCmds[i].cmdID - ID_LANG_FIRST))
prgCmds[i].cmdf = OLECMDF_SUPPORTED | OLECMDF_ENABLED | OLECMDF_NINCHED;
else
prgCmds[i].cmdf = OLECMDF_SUPPORTED | OLECMDF_ENABLED;
#else
prgCmds[i].cmdf = OLECMDF_SUPPORTED | SetMimeLanguageCheckMark(CustomGetCPFromCharset(hCharset, TRUE), prgCmds[i].cmdID - ID_LANG_FIRST);
#endif
}
else
prgCmds[i].cmdf = OLECMDF_SUPPORTED;
continue;
}
// if the command id from the View.Current View menu
if ((ID_VIEW_FILTER_FIRST <= prgCmds[i].cmdID) && (ID_VIEW_FILTER_LAST >= prgCmds[i].cmdID))
{
if (NULL == m_pViewMenu)
{
// Create the view menu
HrCreateViewMenu(0, &m_pViewMenu);
}
if (NULL != m_pViewMenu)
{
m_pViewMenu->QueryStatus(m_pMsgList, &(prgCmds[i]));
}
continue;
}
// Look to see if it's a command we provide
switch (prgCmds[i].cmdID)
{
case ID_OPEN:
{
// Enabled only if the focus is in the ListView and there
// is at least one item selected.
m_pMsgList->GetSelectedCount(&cSel);
if (cSel)
prgCmds[i].cmdf = OLECMDF_SUPPORTED | OLECMDF_ENABLED;
else
prgCmds[i].cmdf = OLECMDF_SUPPORTED;
break;
}
case ID_REPLY:
case ID_REPLY_ALL:
{
// Enabled only if the focus is in the ListView and there
// is only one item selected
prgCmds[i].cmdf = OLECMDF_SUPPORTED;
if (cSel == 1)
{
// The message's body must also be downloaded
LPMESSAGEINFO pInfo;
if (SUCCEEDED(m_pMsgList->GetMessageInfo(rgSelected[0], &pInfo)))
{
if (pInfo->faStream != 0 && (0 == (pInfo->dwFlags & ARF_UNSENT)))
prgCmds[i].cmdf |= OLECMDF_ENABLED;
m_pMsgList->FreeMessageInfo(pInfo);
}
}
break;
}
case ID_SAVE_AS:
{
// Enabled only if the focus is in the ListView and there
// is one item selected
prgCmds[i].cmdf = OLECMDF_SUPPORTED;
if (_IsPreview() && (cSel == 1))
{
// The message's body must also be downloaded
LPMESSAGEINFO pInfo;
if (SUCCEEDED(m_pMsgList->GetMessageInfo(rgSelected[0], &pInfo)))
{
if (pInfo->faStream != 0)
prgCmds[i].cmdf |= OLECMDF_ENABLED;
m_pMsgList->FreeMessageInfo(pInfo);
}
}
break;
}
case ID_PRINT:
{
// Enabled only if the focus is in the ListView and there
// is more than one item selected
prgCmds[i].cmdf = OLECMDF_SUPPORTED;
if (_IsPreview() && cSel > 0)
{
// The message's body must also be downloaded
LPMESSAGEINFO pInfo;
if (SUCCEEDED(m_pMsgList->GetMessageInfo(rgSelected[0], &pInfo)))
{
if (pInfo->faStream != 0)
prgCmds[i].cmdf |= OLECMDF_ENABLED;
m_pMsgList->FreeMessageInfo(pInfo);
}
}
break;
}
case ID_FORWARD:
case ID_FORWARD_AS_ATTACH:
{
// Enabled only if the focus is in the ListView and there
// is only one item selected
prgCmds[i].cmdf = OLECMDF_SUPPORTED;
if (cSel > 0)
{
// The message's body must also be downloaded
LPMESSAGEINFO pInfo;
// Default to success
prgCmds[i].cmdf |= OLECMDF_ENABLED;
for (DWORD iItem = 0; iItem < cSel && (prgCmds[i].cmdf & OLECMDF_ENABLED); iItem++)
{
if (SUCCEEDED(m_pMsgList->GetMessageInfo(rgSelected[iItem], &pInfo)))
{
if (pInfo->faStream == 0 || (0 != (pInfo->dwFlags & ARF_UNSENT)))
{
prgCmds[i].cmdf &= ~OLECMDF_ENABLED;
}
m_pMsgList->FreeMessageInfo(pInfo);
}
}
}
break;
}
case ID_REPLY_GROUP:
{
// Enabled only if there is one news message selected
prgCmds[i].cmdf = OLECMDF_SUPPORTED;
if (cSel == 1)
{
// The message's body must also be downloaded
LPMESSAGEINFO pInfo;
if (SUCCEEDED(m_pMsgList->GetMessageInfo(rgSelected[0], &pInfo)))
{
if (pInfo->faStream != 0 && (pInfo->dwFlags & ARF_NEWSMSG) && (0 == (pInfo->dwFlags & ARF_UNSENT)))
prgCmds[i].cmdf |= OLECMDF_ENABLED;
m_pMsgList->FreeMessageInfo(pInfo);
}
}
break;
}
case ID_CANCEL_MESSAGE:
{
prgCmds[i].cmdf = OLECMDF_SUPPORTED;
if (DwGetOption(OPT_CANCEL_ALL_NEWS))
prgCmds[i].cmdf |= OLECMDF_ENABLED;
else
{
if (cSel == 1)
{
LPMESSAGEINFO pInfo;
if (SUCCEEDED(m_pMsgList->GetMessageInfo(rgSelected[0], &pInfo)))
{
if (NewsUtil_FCanCancel(m_idFolder, pInfo))
{
prgCmds[i].cmdf |= OLECMDF_ENABLED;
}
m_pMsgList->FreeMessageInfo(pInfo);
}
}
}
break;
}
case ID_POPUP_FILTER:
case ID_PREVIEW_PANE:
{
prgCmds[i].cmdf = OLECMDF_SUPPORTED | OLECMDF_ENABLED;
break;
}
case ID_POPUP_LANGUAGE_DEFERRED:
case ID_POPUP_LANGUAGE:
case ID_POPUP_LANGUAGE_MORE:
case ID_LANGUAGE:
{
// These are OK if the preview pane is visible and not empty
if (cSel > 0 && _IsPreview())
prgCmds[i].cmdf = OLECMDF_SUPPORTED | OLECMDF_ENABLED;
else
prgCmds[i].cmdf = OLECMDF_SUPPORTED;
break;
}
case ID_PREVIEW_SHOW:
case ID_PREVIEW_BELOW:
case ID_PREVIEW_BESIDE:
case ID_PREVIEW_HEADER:
{
FOLDERTYPE ftType;
DWORD dwOpt;
LAYOUTPOS pos;
BOOL fVisible;
DWORD dwFlags;
// Default return value
prgCmds[i].cmdf = OLECMDF_SUPPORTED;
// Get the folder type
m_pBrowser->GetFolderType(&ftType);
if (ftType == FOLDER_NEWS)
dwOpt = DISPID_MSGVIEW_PREVIEWPANE_NEWS;
else
dwOpt = DISPID_MSGVIEW_PREVIEWPANE_MAIL;
// Get the settings from the browser
m_pBrowser->GetViewLayout(dwOpt, &pos, &fVisible, &dwFlags, NULL);
switch (prgCmds[i].cmdID)
{
case ID_PREVIEW_SHOW:
{
// Always enabled, checked if already visible
if (fVisible)
prgCmds[i].cmdf |= (OLECMDF_ENABLED | OLECMDF_LATCHED);
else
prgCmds[i].cmdf |= OLECMDF_ENABLED;
break;
}
case ID_PREVIEW_BESIDE:
case ID_PREVIEW_BELOW:
{
// The command is enabled only if the preview pane
// is visible.
if (fVisible)
prgCmds[i].cmdf |= OLECMDF_ENABLED;
// If the preview pane is already beside, it should be latched etc.
if ((pos == LAYOUT_POS_LEFT && prgCmds[i].cmdID == ID_PREVIEW_BESIDE) ||
(pos == LAYOUT_POS_BOTTOM && prgCmds[i].cmdID == ID_PREVIEW_BELOW))
prgCmds[i].cmdf |= OLECMDF_NINCHED;
break;
}
case ID_PREVIEW_HEADER:
{
// Always enabled, checked if already visible
if (dwFlags)
prgCmds[i].cmdf |= (OLECMDF_ENABLED | OLECMDF_LATCHED);
else
prgCmds[i].cmdf |= OLECMDF_ENABLED;
break;
}
}
break;
}
case ID_REFRESH:
{
// Best I can tell, these are always enabled
prgCmds[i].cmdf |= OLECMDF_ENABLED;
break;
}
case ID_GET_HEADERS:
{
// Only in news
m_pBrowser->GetFolderType(&ftType);
if (ftType != FOLDER_LOCAL)
prgCmds[i].cmdf |= OLECMDF_ENABLED;
break;
}
case ID_ADD_SENDER:
case ID_BLOCK_SENDER:
{
prgCmds[i].cmdf = OLECMDF_SUPPORTED;
// Enabled only if there is only one item selected and
// we have access to the from address
// Not in IMAP or HTTPMAIL
m_pBrowser->GetFolderType(&ftType);
if (cSel == 1 &&
(prgCmds[i].cmdID == ID_ADD_SENDER || (FOLDER_HTTPMAIL != ftType && FOLDER_IMAP != ftType)))
{
// The message's body must also be downloaded
LPMESSAGEINFO pInfo;
if (SUCCEEDED(m_pMsgList->GetMessageInfo(rgSelected[0], &pInfo)))
{
if (((NULL != pInfo->pszEmailFrom) && ('\0' != pInfo->pszEmailFrom[0])) || (0 != pInfo->faStream))
prgCmds[i].cmdf |= OLECMDF_ENABLED;
m_pMsgList->FreeMessageInfo(pInfo);
}
}
break;
}
case ID_CREATE_RULE_FROM_MESSAGE:
{
prgCmds[i].cmdf = OLECMDF_SUPPORTED;
// Enabled only if there is only one item selected
// Not in IMAP or HTTPMAIL
m_pBrowser->GetFolderType(&ftType);
if ((cSel == 1) && (FOLDER_HTTPMAIL != ftType) && (FOLDER_IMAP != ftType))
{
LPMESSAGEINFO pInfo;
if (SUCCEEDED(m_pMsgList->GetMessageInfo(rgSelected[0], &pInfo)))
{
prgCmds[i].cmdf |= OLECMDF_ENABLED;
m_pMsgList->FreeMessageInfo(pInfo);
}
}
break;
}
case ID_COMBINE_AND_DECODE:
{
// Enabled only if the focus is in the ListView and there
// is at least one item selected.
m_pMsgList->GetSelectedCount(&cSel);
if (cSel > 1)
prgCmds[i].cmdf = OLECMDF_SUPPORTED | OLECMDF_ENABLED;
else
prgCmds[i].cmdf = OLECMDF_SUPPORTED;
break;
}
}
}
}
SafeMemFree(rgSelected);
return (S_OK);
}
HRESULT CMessageView::_StoreCharsetOntoRows(HCHARSET hCharset)
{
// Locals
HRESULT hr=S_OK;
INETCSETINFO CsetInfo;
IMessageTable *pTable=NULL;
DWORD *rgRows=NULL;
DWORD cRows=0;
HCURSOR hCursor=NULL;
// Trace
TraceCall("CMessageView::_StoreCharsetOntoRows");
// Invalid Args
if (NULL == m_pMsgList || NULL == hCharset)
return(TraceResult(E_INVALIDARG));
// Wait Cursor
hCursor = SetCursor(LoadCursor(NULL, IDC_WAIT));
// Get charset info
IF_FAILEXIT(hr = MimeOleGetCharsetInfo(hCharset, &CsetInfo));
// Get selected rows
IF_FAILEXIT(hr = m_pMsgList->GetSelected(NULL, &cRows, &rgRows));
// Get the message table
IF_FAILEXIT(hr = m_pMsgList->GetMessageTable(&pTable));
// Set the Language
SideAssert(SUCCEEDED(pTable->SetLanguage(cRows, rgRows, CsetInfo.cpiInternet)));
exit:
// Cleanup
SafeRelease(pTable);
SafeMemFree(rgRows);
// Reset Cursor
if (hCursor)
SetCursor(hCursor);
// Done
return(hr);
}
//
// FUNCTION: CMessageView::Exec()
//
// PURPOSE: Called to execute a verb that this view supports
//
// PARAMETERS:
// [in] pguidCmdGroup - unused
// [in] nCmdID - ID of the command to execute
// [in] nCmdExecOpt - Options that define how the command should execute
// [in] pvaIn - Any arguments for the command
// [out] pvaOut - Any return values for the command
//
// RETURN VALUE:
//
//
HRESULT CMessageView::Exec(const GUID *pguidCmdGroup, DWORD nCmdID, DWORD nCmdExecOpt,
VARIANTARG *pvaIn, VARIANTARG *pvaOut)
{
// See if our message list wants the command
if (m_pMsgListCT)
{
if (OLECMDERR_E_NOTSUPPORTED != m_pMsgListCT->Exec(pguidCmdGroup, nCmdID, nCmdExecOpt, pvaIn, pvaOut))
return (S_OK);
}
if (m_pPreviewCT)
{
if (OLECMDERR_E_NOTSUPPORTED != m_pPreviewCT->Exec(&CMDSETID_OutlookExpress, nCmdID, nCmdExecOpt, pvaIn, pvaOut))
return (S_OK);
}
// If the sub objects didn't support the command, then we should see if
// it's one of ours
// Language menu first
if (nCmdID >= ID_LANG_FIRST && nCmdID <= ID_LANG_LAST)
{
HCHARSET hCharset = NULL;
HCHARSET hOldCharset = NULL;
HRESULT hr = S_OK;
if(!m_pPreview)
return S_OK;
m_pPreview->HrGetCharset(&hOldCharset);
hCharset = GetMimeCharsetFromMenuID(nCmdID);
if(!hCharset || (hOldCharset == hCharset))
return(S_OK);
Assert (hCharset);
if(FAILED(hr = m_pPreview->HrSetCharset(hCharset)))
{
AthMessageBoxW( m_hwnd, MAKEINTRESOURCEW(idsAthena),
MAKEINTRESOURCEW((hr == hrIncomplete)?idsViewLangMimeDBBad:idsErrViewLanguage),
NULL, MB_OK|MB_ICONEXCLAMATION);
return E_FAIL;
}
// Set the charset onto the selected rows....
_StoreCharsetOntoRows(hCharset);
// SetDefaultCharset(hCharset);
// SwitchLanguage(nCmdID, TRUE);
return (S_OK);
}
// Handle the View.Current View menu
if ((ID_VIEW_FILTER_FIRST <= nCmdID) && (ID_VIEW_FILTER_LAST >= nCmdID))
{
if (NULL == m_pViewMenu)
{
// Create the view menu
HrCreateViewMenu(0, &m_pViewMenu);
}
if (NULL != m_pViewMenu)
{
// What we get from the browser is of type VT_I8, but rules only needs filter id which
// is a dword. So changing the type here is safe. Bug# 74275
pvaIn->vt = VT_I4;
if (SUCCEEDED(m_pViewMenu->Exec(m_hwnd, nCmdID, m_pMsgList, pvaIn, pvaOut)))
{
return (S_OK);
}
}
}
// Go through the rest of the commands
switch (nCmdID)
{
case ID_OPEN:
return CmdOpen(nCmdID, nCmdExecOpt, pvaIn, pvaOut);
case ID_REPLY:
case ID_REPLY_ALL:
case ID_FORWARD:
case ID_FORWARD_AS_ATTACH:
case ID_REPLY_GROUP:
return CmdReplyForward(nCmdID, nCmdExecOpt, pvaIn, pvaOut);
case ID_CANCEL_MESSAGE:
return CmdCancelMessage(nCmdID, nCmdExecOpt, pvaIn, pvaOut);
case ID_DOWNLOAD_MESSAGE:
return CmdFillPreview(nCmdID, nCmdExecOpt, pvaIn, pvaOut);
case ID_PREVIEW_PANE:
case ID_PREVIEW_SHOW:
case ID_PREVIEW_BELOW:
case ID_PREVIEW_BESIDE:
case ID_PREVIEW_HEADER:
return CmdShowPreview(nCmdID, nCmdExecOpt, pvaIn, pvaOut);
case ID_REFRESH:
return CmdRefresh(nCmdID, nCmdExecOpt, pvaIn, pvaOut);
case ID_BLOCK_SENDER:
return CmdBlockSender(nCmdID, nCmdExecOpt, pvaIn, pvaOut);
case ID_CREATE_RULE_FROM_MESSAGE:
return CmdCreateRule(nCmdID, nCmdExecOpt, pvaIn, pvaOut);
case ID_VIEW_SOURCE:
case ID_VIEW_MSG_SOURCE:
if (m_pPreview)
return m_pPreview->HrViewSource((ID_VIEW_SOURCE==nCmdID)?MECMD_VS_HTML:MECMD_VS_MESSAGE);
else
break;
case ID_ADD_SENDER:
return CmdAddToWab(nCmdID, nCmdExecOpt, pvaIn, pvaOut);
case ID_COMBINE_AND_DECODE:
return CmdCombineAndDecode(nCmdID, nCmdExecOpt, pvaIn, pvaOut);
}
return (E_FAIL);
}
//
// FUNCTION: CMessageView::Invoke()
//
// PURPOSE: This is where we receive notifications from the message list.
//
// PARAMETERS:
// <too many to list>
//
// RETURN VALUE:
// HRESULT
//
HRESULT CMessageView::Invoke(DISPID dispIdMember, REFIID riid, LCID lcid,
WORD wFlags, DISPPARAMS* pDispParams,
VARIANT* pVarResult, EXCEPINFO* pExcepInfo,
unsigned int* puArgErr)
{
switch (dispIdMember)
{
// Fired whenever the selection in the ListView changes
case DISPID_LISTEVENT_SELECTIONCHANGED:
{
// Need to load the preview pane with the new selected message
if (_IsPreview())
_UpdatePreviewPane();
// Tell the browser to update it's toolbar
if (m_pBrowser)
m_pBrowser->UpdateToolbar();
break;
}
// Fired whenever the ListView get's or loses focus.
case DISPID_LISTEVENT_FOCUSCHANGED:
{
// If the ListView is getting the focus, we need to UI deactivate
// the preview pane.
if (pDispParams->rgvarg[0].lVal)
{
if (m_pPreview)
{
m_pPreview->HrUIActivate(FALSE);
m_pBrowser->OnViewWindowActive(this);
}
}
break;
}
// Fired when the number of messages or unread messages changes
case DISPID_LISTEVENT_COUNTCHANGED:
{
// If we have a browser, update the status bar
if (m_pBrowser && !m_pProgress)
{
DWORD cTotal, cUnread, cOnServer;
// Readability forces me to do this
cTotal = pDispParams->rgvarg[0].lVal;
cUnread = pDispParams->rgvarg[1].lVal;
cOnServer = pDispParams->rgvarg[2].lVal;
// Got to update the status bar if there is one
CStatusBar *pStatusBar = NULL;
m_pBrowser->GetStatusBar(&pStatusBar);
if (pStatusBar)
{
TCHAR szStatus[CCHMAX_STRINGRES + 20];
TCHAR szFmt[CCHMAX_STRINGRES];
DWORD ids;
// If there are still messages on server load a different
// status string.
if (cOnServer)
{
AthLoadString(idsXMsgsYUnreadZonServ, szFmt, ARRAYSIZE(szFmt));
wnsprintf(szStatus, ARRAYSIZE(szStatus), szFmt, cTotal, cUnread, cOnServer);
}
else
{
AthLoadString(idsXMsgsYUnread, szFmt, ARRAYSIZE(szFmt));
wnsprintf(szStatus, ARRAYSIZE(szStatus), szFmt, cTotal, cUnread);
}
pStatusBar->SetStatusText(szStatus);
pStatusBar->Release();
}
// Also update the toolbar since commands like "Mark as Read" might
// change. However, we only do this if we go between zero and some or
// vice versa.
if ((m_cItems == 0 && cTotal) || (m_cItems != 0 && cTotal == 0) ||
(m_cUnread == 0 && cUnread) || (m_cUnread != 0 && cUnread == 0))
{
m_pBrowser->UpdateToolbar();
}
// Save this for next time.
m_cItems = cTotal;
m_cUnread = cUnread;
}
break;
}
// Fired when the message list want's to show status text
case DISPID_LISTEVENT_UPDATESTATUS:
{
_SetProgressStatusText(pDispParams->rgvarg->bstrVal);
break;
}
// Fired when progress happens
case DISPID_LISTEVENT_UPDATEPROGRESS:
{
CBands *pCoolbar = NULL;
// If this is a begin, then we start animating the logo
if (pDispParams->rgvarg[2].lVal == PROGRESS_STATE_BEGIN)
{
if (SUCCEEDED(m_pBrowser->GetCoolbar(&pCoolbar)))
{
pCoolbar->Invoke(idDownloadBegin, NULL);
pCoolbar->Release();
}
}
// If this is a continue, then we might get progress numbers
else if (pDispParams->rgvarg[2].lVal == PROGRESS_STATE_DEFAULT)
{
if (!m_pProgress)
{
if (m_pBrowser->GetStatusBar(&m_pProgress)==S_OK)
m_pProgress->ShowProgress(pDispParams->rgvarg[1].lVal);
}
if (m_pProgress)
m_pProgress->SetProgress(pDispParams->rgvarg[0].lVal);
}
// Or if this is an end, stop animating and clean up the status bar
else if (pDispParams->rgvarg[2].lVal == PROGRESS_STATE_END)
{
if (m_pProgress)
{
m_pProgress->HideProgress();
m_pProgress->Release();
m_pProgress = NULL;
}
if (SUCCEEDED(m_pBrowser->GetCoolbar(&pCoolbar)))
{
pCoolbar->Invoke(idDownloadEnd, NULL);
pCoolbar->Release();
}
// Reset the status bar back to it's default state
_SetDefaultStatusText();
}
break;
}
// Fired when the user double clicks an item in the ListView
case DISPID_LISTEVENT_ITEMACTIVATE:
{
CmdOpen(ID_OPEN, OLECMDEXECOPT_DONTPROMPTUSER, NULL, NULL);
break;
}
// Fired when we need to call update toolbar
case DISPID_LISTEVENT_UPDATECOMMANDSTATE:
{
PostMessage(m_hwndParent, CM_UPDATETOOLBAR, 0, 0L);
break;
}
// Fired when a message has been downloaded by the messagelist
case DISPID_LISTEVENT_ONMESSAGEAVAILABLE:
{
return _OnMessageAvailable((MESSAGEID)((LONG_PTR)pDispParams->rgvarg[0].lVal), (HRESULT)pDispParams->rgvarg[1].scode);
}
// Fired when the filter changes
case DISPID_LISTEVENT_FILTERCHANGED:
{
// If we have a browser, update the status bar
if (m_pBrowser && !m_pProgress)
{
// Got to update the status bar if there is one
CStatusBar *pStatusBar = NULL;
m_pBrowser->GetStatusBar(&pStatusBar);
if (pStatusBar)
{
pStatusBar->SetFilter((RULEID)((ULONG_PTR)pDispParams->rgvarg[0].ulVal));
pStatusBar->Release();
}
CBands* pBands;
if (m_pBrowser->GetCoolbar(&pBands) == S_OK)
{
pBands->Invoke(idNotifyFilterChange, &pDispParams->rgvarg[0].ulVal);
pBands->Release();
}
}
break;
}
case DISPID_LISTEVENT_ADURL_AVAILABLE:
{
if (m_pBrowser)
{
m_pBrowser->ShowAdBar(pDispParams->rgvarg[0].bstrVal);
}
break;
}
}
return (S_OK);
}
HRESULT CMessageView::GetMarkAsReadTime(LPDWORD pdwSecs)
{
if (!pdwSecs)
{
AssertSz(FALSE, "Null Pointer");
return (E_INVALIDARG);
}
*pdwSecs = DwGetOption(OPT_MARKASREAD);
return (S_OK);
}
HRESULT CMessageView::GetAccount(IImnAccount **ppAcct)
{
FOLDERINFO FolderInfo;
HRESULT hr = E_FAIL;
CHAR szAccountId[CCHMAX_ACCOUNT_NAME];
if (g_pStore && SUCCEEDED(g_pStore->GetFolderInfo(m_idFolder, &FolderInfo)))
{
if (SUCCEEDED(GetFolderAccountId(&FolderInfo, szAccountId, ARRAYSIZE(szAccountId)) && *szAccountId))
{
hr = g_pAcctMan->FindAccount(AP_ACCOUNT_ID, szAccountId, ppAcct);
// If local store then we can fail
if(FAILED(hr))
{
DWORD dwRow = 0;
DWORD cSel = 0;
if (SUCCEEDED(m_pMsgList->GetSelected(&dwRow, &cSel, NULL)))
{
LPMESSAGEINFO pMsgInfo;
if (SUCCEEDED(m_pMsgList->GetMessageInfo(dwRow, &pMsgInfo)))
{
hr = g_pAcctMan->FindAccount(AP_ACCOUNT_ID, pMsgInfo->pszAcctId, ppAcct);
m_pMsgList->FreeMessageInfo(pMsgInfo);
}
}
}
}
g_pStore->FreeRecord(&FolderInfo);
}
return(hr);
}
HRESULT CMessageView::GetFlags(LPDWORD pdwFlags)
{
FOLDERTYPE ftType;
if (!pdwFlags)
{
AssertSz(FALSE, "Null Pointer");
return (E_INVALIDARG);
}
*pdwFlags = BOPT_AUTOINLINE | BOPT_HTML | BOPT_INCLUDEMSG | BOPT_FROMSTORE;
if (m_pMsgList)
{
DWORD dwRow = 0;
DWORD cSel = 0;
if (SUCCEEDED(m_pMsgList->GetSelected(&dwRow, &cSel, NULL)))
{
LPMESSAGEINFO pMsgInfo;
if (cSel > 1)
*pdwFlags |= BOPT_MULTI_MSGS_SELECTED;
if (SUCCEEDED(m_pMsgList->GetMessageInfo(dwRow, &pMsgInfo)))
{
if (0 == (pMsgInfo->dwFlags & ARF_READ))
*pdwFlags |= BOPT_UNREAD;
if (0 == (pMsgInfo->dwFlags & ARF_NOSECUI))
*pdwFlags |= BOPT_SECURITYUIENABLED;
m_pMsgList->FreeMessageInfo(pMsgInfo);
}
}
}
m_pBrowser->GetFolderType(&ftType);
if (FOLDER_NEWS != ftType)
*pdwFlags |= BOPT_MAIL;
return (S_OK);
}
//
// FUNCTION: CMessageView::EventOccurred()
//
// PURPOSE: Get's hit whenever an interesting event happens in the preview
// pane.
//
// PARAMETERS:
// DWORD nCmdID
//
// RETURN VALUE:
// HRESULT
//
HRESULT CMessageView::EventOccurred(DWORD nCmdID, IMimeMessage *pMessage)
{
TraceCall("CMessageView::EventOccurred");
switch (nCmdID)
{
case MEHC_CMD_DOWNLOAD:
Assert(m_fNotDownloaded);
// If we're offline, we can make the reasonable assumption that
// the user wants to be online since they said they wanted to
// download this message.
if (g_pConMan && g_pConMan->IsGlobalOffline())
g_pConMan->SetGlobalOffline(FALSE);
_UpdatePreviewPane(TRUE);
break;
case MEHC_CMD_MARK_AS_READ:
if (m_pMsgList)
m_pMsgList->MarkRead(TRUE, 0);
break;
case MEHC_CMD_CONNECT:
if (g_pConMan)
g_pConMan->SetGlobalOffline(FALSE);
_UpdatePreviewPane();
break;
case MEHC_BTN_OPEN:
case MEHC_BTN_CONTINUE:
// Update the toolbar state
m_pBrowser->UpdateToolbar();
break;
case MEHC_UIACTIVATE:
m_pBrowser->OnViewWindowActive(this);
break;
case MEHC_CMD_PROCESS_RECEIPT:
if (m_pMsgList)
m_pMsgList->ProcessReceipt(pMessage);
break;
default:
/* AssertSz(FALSE, "CMessageView::EventOccured() - Unhandled Event."); */ // Valid situation - Warning message for S/MIME
break;
}
return (S_FALSE);
}
HRESULT CMessageView::GetFolderId(FOLDERID *pID)
{
if (pID)
{
*pID = m_idFolder;
return (S_OK);
}
return (E_INVALIDARG);
}
HRESULT CMessageView::GetMessageFolder(IMessageServer **ppServer)
{
if (m_pMsgList)
return (m_pMsgList->GetMessageServer(ppServer));
return (E_NOTIMPL);
}
/////////////////////////////////////////////////////////////////////////////
//
// Window Message Handling
//
//
// FUNCTION: CMessageView::ViewWndProc()
//
// PURPOSE: Callback handler for the view window. This function grabs the
// correct this pointer for the window and uses that to dispatch
// the message to the private message handler.
//
LRESULT CALLBACK CMessageView::ViewWndProc(HWND hwnd, UINT uMsg, WPARAM wParam,
LPARAM lParam)
{
LRESULT lResult;
CMessageView *pThis;
// WM_NCCREATE is the first message our window will receive. The lParam
// will have the pointer to the object that created this instance of the
// window.
if (uMsg == WM_NCCREATE)
{
// Save the object pointer in the window's extra bytes.
pThis = (CMessageView *) ((LPCREATESTRUCT) lParam)->lpCreateParams;
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LPARAM) pThis);
}
else
{
// If this is any other message, we need to get the object pointer
// from the window before dispatching the message.
pThis = (CMessageView *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
}
// If this ain't true, we're in trouble.
if (pThis)
{
return (pThis->_WndProc(hwnd, uMsg, wParam, lParam));
}
else
{
Assert(pThis);
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
}
//
// FUNCTION: CMessageView::_WndProc()
//
// PURPOSE: This private message handler dispatches messages to the
// appropriate handler.
//
LRESULT CALLBACK CMessageView::_WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
HANDLE_MSG(hwnd, WM_CREATE, OnCreate);
HANDLE_MSG(hwnd, WM_POSTCREATE, OnPostCreate);
HANDLE_MSG(hwnd, WM_SIZE, OnSize);
HANDLE_MSG(hwnd, WM_LBUTTONDOWN, OnLButtonDown);
HANDLE_MSG(hwnd, WM_MOUSEMOVE, OnMouseMove);
HANDLE_MSG(hwnd, WM_LBUTTONUP, OnLButtonUp);
HANDLE_MSG(hwnd, WM_NOTIFY, OnNotify);
HANDLE_MSG(hwnd, WM_DESTROY, OnDestroy);
HANDLE_MSG(hwnd, WM_SETFOCUS, OnSetFocus);
HANDLE_MSG(hwnd, WM_TEST_GETMSGID, OnTestGetMsgId);
HANDLE_MSG(hwnd, WM_TEST_SAVEMSG, OnTestSaveMessage);
case WM_FOLDER_LOADED:
OnFolderLoaded(hwnd, wParam, lParam);
break;
case WM_NEW_MAIL:
// Propagate up to browser
PostMessage(m_hwndParent, WM_NEW_MAIL, 0, 0);
break;
case NVM_GETNEWGROUPS:
if (m_pGroups != NULL)
{
m_pGroups->HandleGetNewGroups();
m_pGroups->Release();
m_pGroups = NULL;
}
return(0);
case WM_UPDATE_PREVIEW:
if (m_idMessageFocus == (MESSAGEID)wParam)
{
_UpdatePreviewPane();
}
break;
case CM_OPTIONADVISE:
_OptionUpdate((DWORD) wParam);
break;
case WM_MENUSELECT:
// HANDLE_WM_MENUSELECT() has a bug that prevents popups from displaying correctly.
OnMenuSelect(hwnd, wParam, lParam);
return (0);
case WM_SYSCOLORCHANGE:
case WM_WININICHANGE:
case WM_FONTCHANGE:
if (m_pMsgList)
{
IOleWindow *pWindow;
if (SUCCEEDED(m_pMsgList->QueryInterface(IID_IOleWindow, (LPVOID *) &pWindow)))
{
HWND hwndList;
pWindow->GetWindow(&hwndList);
SendMessage(hwndList, uMsg, wParam, lParam);
pWindow->Release();
}
}
return (0);
}
return (DefWindowProc(hwnd, uMsg, wParam, lParam));
}
// PURPOSE: WM_FOLDER_LOADED message is sent when messagelist is done loading the cached headers/messages etc
void CMessageView::OnFolderLoaded(HWND hwnd, WPARAM wParam, LPARAM lParam)
{
FOLDERINFO FolderInfo;
if (g_pStore && SUCCEEDED(g_pStore->GetFolderInfo(m_idFolder, &FolderInfo)))
{
CHAR szAccountId[CCHMAX_ACCOUNT_NAME];
if (SUCCEEDED(GetFolderAccountId(&FolderInfo, szAccountId, ARRAYSIZE(szAccountId))))
{
HRESULT hr;
if (g_pConMan)
{
hr = g_pConMan->CanConnect(szAccountId);
if ((hr != S_OK) && (hr != HR_E_DIALING_INPROGRESS) && (hr != HR_E_OFFLINE))
g_pConMan->Connect(szAccountId, hwnd, TRUE);
}
}
g_pStore->FreeRecord(&FolderInfo);
}
}
//
// FUNCTION: CMessageView::OnCreate()
//
// PURPOSE: Handler for the WM_CREATE message. In return we create our
// dependant objects and initialize them.
//
// PARAMETERS:
// [in] hwnd - Handle of the window being created
// [in] lpCreateStruct - Pointer to a structure with information about the
// creation.
//
// RETURN VALUE:
// Returns FALSE if something fails and the window should not be created,
// and returns TRUE if everything works fine.
//
BOOL CMessageView::OnCreate(HWND hwnd, LPCREATESTRUCT lpCreateStruct)
{
HRESULT hr;
HWND hwndList;
TraceCall("CMessageView::OnCreate");
// Save the window handle
m_hwnd = hwnd;
// Create the message list object
if (!_InitMessageList())
return (FALSE);
// Create the preview pane. If it fails that's OK, we'll just
// run without it.
_InitPreviewPane();
// Get updates when options change
OptionAdvise(m_hwnd);
// For later
PostMessage(m_hwnd, WM_POSTCREATE, 0, 0);
return (TRUE);
}
//
// FUNCTION: CMessageView::OnPostCreate()
//
// PURPOSE: Notifies when the view has finished being created. Any
// initialization that takes time can happen here, like loading
// the message table etc.
//
// PARAMETERS:
// [in] hwnd - Handle of the window
//
void CMessageView::OnPostCreate(HWND hwnd)
{
HRESULT hr;
FOLDERTYPE FolderType;
FOLDERINFO fiServerNode = {0};
HRESULT hrTemp;
TraceCall("CMessageView::OnPostCreate");
if (!g_pStore)
return;
FolderType = GetFolderType(m_idFolder);
ProcessICW(hwnd, FolderType);
// BETA-2: If this is IMAP folder, check if IMAP folderlist is dirty.
// If so, prompt user to refresh folderlist
hrTemp = GetFolderServer(m_idFolder, &fiServerNode);
TraceError(hrTemp);
if (SUCCEEDED(hrTemp))
{
if (FOLDER_IMAP == FolderType)
CheckIMAPDirty(fiServerNode.pszAccountId, hwnd, fiServerNode.idFolder, NOFLAGS);
}
// Tell the Message List control to load itself
if (m_pMsgList)
{
// Tell the message list to change folders
hr = m_pMsgList->SetFolder(m_idFolder, m_pServer, FALSE, NULL, NOSTORECALLBACK);
if (FAILED(hr) && hr != E_PENDING && m_pPreview)
{
m_pPreview->LoadHtmlErrorPage(c_szErrPage_FldrFail);
}
}
if (m_pServer)
{
m_pServer->ConnectionRelease();
m_pServer->Close(MSGSVRF_HANDS_OFF_SERVER);
m_pServer->Release();
m_pServer = NULL;
}
// Create a drop target
m_pDropTarget = new CDropTarget();
if (m_pDropTarget)
{
if (SUCCEEDED(m_pDropTarget->Initialize(m_hwnd, m_idFolder)))
{
RegisterDragDrop(m_hwnd, m_pDropTarget);
}
}
if (FolderType == FOLDER_NEWS)
NewsUtil_CheckForNewGroups(hwnd, m_idFolder, &m_pGroups);
// If its HTTP folder (Should have been hotmail folder), and if we are connected we ask for the ad url.
if ((FolderType == FOLDER_HTTPMAIL) &&
(g_pConMan && (S_OK == g_pConMan->CanConnect(fiServerNode.pszAccountId))))
{
m_pMsgList->GetAdBarUrl();
}
g_pStore->FreeRecord(&fiServerNode);
}
#define SPLIT_SIZE 3
void CMessageView::OnSize(HWND hwnd, UINT state, int cxClient, int cyClient)
{
RECT rc = {0, 0, cxClient, cyClient};
int split;
// If we are displaying the preview pane, we need to split the client area
// based on the position of the split bar.
if (_IsPreview())
{
// Line the windows up based on the split direction
if (m_fSplitHorz)
{
// Determine the split height
split = (cyClient * m_dwSplitHorzPct) / 100;
// Save the rect that the split bar occupies
SetRect(&m_rcSplit, 0, split, cxClient, split + SPLIT_SIZE);
// Set the position of the preview pane
rc.top = m_rcSplit.bottom;
rc.bottom = cyClient;
if (m_pPreview)
m_pPreview->HrSetSize(&rc);
// Set the position of the message list
SetRect(&rc, -1, 0, cxClient + 2, split);
m_pMsgList->SetRect(rc);
}
else
{
// Determine the split width
split = (cxClient * m_dwSplitVertPct) / 100;
// Save the rect that the split bar occupies
SetRect(&m_rcSplit, split, 0, split + SPLIT_SIZE, cyClient);
// Set the position of the message list
rc.right = split;
m_pMsgList->SetRect(rc);
// Set the position of the preview pane
rc.left = m_rcSplit.right;
rc.right = cxClient;
if (m_pPreview)
m_pPreview->HrSetSize(&rc);
}
}
else
{
SetRect(&rc, -1, 0, cxClient + 2, cyClient);
m_pMsgList->SetRect(rc);
}
return;
}
//
// FUNCTION: CMessageView::OnLButtonDown
//
// PURPOSE: We check to see if we're over the splitter bar and if so start
// a drag operation.
//
// PARAMETERS:
// hwnd - Handle to the view window.
// fDoubleClick - TRUE if this is a double click.
// x, y - Position of the mouse in client coordinates.
// keyFlags - State of the keyboard.
//
void CMessageView::OnLButtonDown(HWND hwnd, BOOL fDoubleClick, int x, int y, UINT keyFlags)
{
POINT pt = {x, y};
// Check to see if the mouse is over the split bar
if (_IsPreview() && PtInRect(&m_rcSplit, pt))
{
// Capture the mouse
SetCapture(m_hwnd);
// Start dragging
m_fDragging = TRUE;
}
}
//
// FUNCTION: CMessageView::OnMouseMove
//
// PURPOSE: We update any drag and drop information in response to mouse
// moves if a drag and drop is in progress.
//
// PARAMETERS:
// hwnd - Handle to the view window.
// x, y - Position of the mouse in client coordinates.
// keyFlags - State of the keyboard.
//
void CMessageView::OnMouseMove(HWND hwnd, int x, int y, UINT keyFlags)
{
HCURSOR hcur;
POINT pt = {x, y};
RECT rcClient;
// If we're dragging the split bar, update the window sizes
if (m_fDragging)
{
// Get the size of the window
GetClientRect(m_hwnd, &rcClient);
// Calculate the new split percentage
if (m_fSplitHorz)
{
// Make sure the user hasn't gone off the deep end
if (y > 32 && y < (rcClient.bottom - 32))
m_dwSplitHorzPct = (y * 100) / rcClient.bottom;
}
else
{
// Make sure the user hasn't gone off the deep end
if (x > 32 && x < (rcClient.right - 32))
m_dwSplitVertPct = (x * 100) / rcClient.right;
}
// Update the window sizes
OnSize(m_hwnd, SIZE_RESTORED, rcClient.right, rcClient.bottom);
}
else
{
// Just update the cursor
if (PtInRect(&m_rcSplit, pt))
{
if (m_fSplitHorz)
hcur = LoadCursor(NULL, MAKEINTRESOURCE(IDC_SIZENS));
else
hcur = LoadCursor(NULL, MAKEINTRESOURCE(IDC_SIZEWE));
}
else
hcur = LoadCursor(NULL, IDC_ARROW);
SetCursor(hcur);
}
}
//
// FUNCTION: CMessageView::OnLButtonUp
//
// PURPOSE: If a drag opteration is currently in progress (as determined
// by the g_fDragging variable) then this function handles
// ending the drag and updating the split position.
//
// PARAMETERS:
// hwnd - handle of the window receiving the message
// x - horizontal mouse position in client coordinates
// y - vertical mouse position in client coordinates
// keyFlags - Indicates whether various virtual keys are down
//
void CMessageView::OnLButtonUp(HWND hwnd, int x, int y, UINT keyFlags)
{
DWORD dwHeader;
DWORD dwSize;
BOOL fVisible;
DWORD dwOpt;
FOLDERTYPE ftType;
if (m_fDragging)
{
ReleaseCapture();
m_fDragging = FALSE;
// Get the old settings
m_pBrowser->GetFolderType(&ftType);
if (ftType == FOLDER_NEWS)
dwOpt = DISPID_MSGVIEW_PREVIEWPANE_NEWS;
else
dwOpt = DISPID_MSGVIEW_PREVIEWPANE_MAIL;
m_pBrowser->GetViewLayout(dwOpt, 0, &fVisible, &dwHeader, &dwSize);
// Update the new splits
if (m_fSplitHorz)
dwSize = MAKELONG(m_dwSplitHorzPct, 0);
else
dwSize = MAKELONG(0, m_dwSplitVertPct);
// Set the settings back to the browser
m_pBrowser->SetViewLayout(dwOpt, LAYOUT_POS_NA, fVisible, dwHeader, dwSize);
}
}
//
// FUNCTION: CMessageView::OnMenuSelect()
//
// PURPOSE: Put's helpful text on the status bar describing the selected
// menu item.
//
void CMessageView::OnMenuSelect(HWND hwnd, WPARAM wParam, LPARAM lParam)
{
// Let the preview pane have it first
if (m_pPreview)
{
if (S_OK == m_pPreview->HrWMMenuSelect(hwnd, wParam, lParam))
return;
}
// Handle it ourselves
CStatusBar *pStatusBar = NULL;
m_pBrowser->GetStatusBar(&pStatusBar);
HandleMenuSelect(pStatusBar, wParam, lParam);
pStatusBar->Release();
}
//
// FUNCTION: CMessageView::OnNotify
//
// PURPOSE: Processes the various notifications we receive from our child
// controls.
//
// PARAMETERS:
// hwnd - Handle of the view window.
// idCtl - identifies the control sending the notification
// pnmh - points to a NMHDR struct with more information regarding the
// notification
//
// RETURN VALUE:
// Dependant on the specific notification.
//
LRESULT CMessageView::OnNotify(HWND hwnd, int idFrom, LPNMHDR pnmhdr)
{
switch (pnmhdr->code)
{
case BDN_HEADERDBLCLK:
{
if (m_pPreview)
{
DWORD dw = 0;
BOOL f = 0;
FOLDERTYPE ftType;
DWORD dwOpt;
m_pBrowser->GetFolderType(&ftType);
if (ftType == FOLDER_NEWS)
dwOpt = DISPID_MSGVIEW_PREVIEWPANE_NEWS;
else
dwOpt = DISPID_MSGVIEW_PREVIEWPANE_MAIL;
m_pBrowser->GetViewLayout(dwOpt, 0, &f, &dw, 0);
m_pPreview->HrSetStyle(!dw ? MESTYLE_PREVIEW : MESTYLE_MINIHEADER);
m_pBrowser->SetViewLayout(dwOpt, LAYOUT_POS_NA, f, !dw, 0);
}
break;
}
case BDN_MARKASSECURE:
{
if (m_pMsgList)
{
DWORD dwRow = 0;
if (SUCCEEDED(m_pMsgList->GetSelected(&dwRow, NULL, NULL)))
m_pMsgList->MarkMessage(dwRow, MARK_MESSAGE_NOSECUI);
}
break;
}
}
return (0);
}
void CMessageView::OnDestroy(HWND hwnd)
{
if (m_pDropTarget)
{
RevokeDragDrop(hwnd);
m_pDropTarget->Release();
m_pDropTarget = 0;
}
// Stop advising for option changes
OptionUnadvise(m_hwnd);
// Release the preview pane
if (m_pPreview)
{
m_pPreview->HrUnloadAll(NULL, 0);
m_pPreview->HrClose();
}
}
void CMessageView::OnSetFocus(HWND hwnd, HWND hwndOldFocus)
{
IOleWindow *pWindow = 0;
HWND hwndList = 0;
if (m_pMsgList)
{
if (SUCCEEDED(m_pMsgList->QueryInterface(IID_IOleWindow, (LPVOID *) &pWindow)))
{
if (SUCCEEDED(pWindow->GetWindow(&hwndList)))
{
SetFocus(hwndList);
}
pWindow->Release();
}
}
}
//
// FUNCTION: CMessageView::OnTestGetMsgId()
//
// PURPOSE: This function is for the testing team. Please consult Racheli
// before modifying it in any way.
//
LRESULT CMessageView::OnTestGetMsgId(HWND hwnd)
{
DWORD cSel;
DWORD *rgSelected = NULL;
LRESULT lResult = -1;
LPMESSAGEINFO pInfo;
TraceCall("CMessageView::OnTestGetMsgId");
// Only handle this if we're in test mode
if (!DwGetOption(OPT_TEST_MODE))
return (-1);
// Get the range of selected messages
if (SUCCEEDED(m_pMsgList && m_pMsgList->GetSelected(NULL, &cSel, &rgSelected)))
{
// Get the message info for the selected row
if (cSel && SUCCEEDED(m_pMsgList->GetMessageInfo(*rgSelected, &pInfo)))
{
lResult = (LRESULT) pInfo->idMessage;
m_pMsgList->FreeMessageInfo(pInfo);
}
MemFree(&rgSelected);
}
return (lResult);
}
//
// FUNCTION: CMessageView::OnTestSaveMessage()
//
// PURPOSE: This method is for the testing team. Please consult Racheli
// before making any changes.
//
LRESULT CMessageView::OnTestSaveMessage(HWND hwnd)
{
DWORD cSel;
DWORD *rgSelected = NULL;
TCHAR szFile[MAX_PATH];
IUnknown *pUnkMessage;
IMimeMessage *pMessage = NULL;
LRESULT lResult = -1;
TraceCall("CMessageView::OnTestSaveMessage");
// Make sure we only do this in test mode
if (!DwGetOption(OPT_TEST_MODE))
return (-1);
// Get the dump file name
if (!GetOption(OPT_DUMP_FILE, szFile, ARRAYSIZE(szFile)))
return (-1);
// Get the selected range
if (SUCCEEDED(m_pMsgList->GetSelected(NULL, &cSel, &rgSelected)))
{
// Load the first selected message from the store
if (cSel && SUCCEEDED(m_pMsgList->GetMessage(*rgSelected, FALSE, FALSE, &pUnkMessage)))
{
// Get the IMimeMessage interface from the message
if (pUnkMessage && SUCCEEDED(pUnkMessage->QueryInterface(IID_IMimeMessage, (LPVOID *) &pMessage)))
{
// Save the message
HrSaveMsgToFile(pMessage, (LPTSTR) szFile);
pMessage->Release();
lResult = 0;
}
pUnkMessage->Release();
}
MemFree(rgSelected);
}
return (lResult);
}
//
// FUNCTION: CMessageView::CmdOpen()
//
// PURPOSE: Opens the selected messages.
//
// RETURN VALUE:
// HRESULT
//
HRESULT CMessageView::CmdOpen(DWORD nCmdID, DWORD nCmdExecOpt, VARIANTARG *pvaIn, VARIANTARG *pvaOut)
{
HRESULT hr;
TraceCall("CMessageView::CmdOpen");
// If more than 10 messages are selected, warn the user with a "Don't show
// me again" dialog that this could be bad.
DWORD dwSel = 0;
m_pMsgList->GetSelectedCount(&dwSel);
if (dwSel > 10)
{
TCHAR szBuffer[CCHMAX_STRINGRES];
LRESULT lResult;
AthLoadString(idsErrOpenManyMessages, szBuffer, ARRAYSIZE(szBuffer));
lResult = DoDontShowMeAgainDlg(m_hwnd, c_szRegManyMsgWarning,
MAKEINTRESOURCE(idsAthena), szBuffer,
MB_OKCANCEL);
if (IDCANCEL == lResult)
return (S_OK);
}
// Get the array of selected rows from the message list
DWORD *rgRows = NULL;
DWORD cRows = 0;
if (FAILED(hr = m_pMsgList->GetSelected(NULL, &cRows, &rgRows)))
return (hr);
// It's possible for the message list to go away while we're doing this.
// To keep us from crashing, make sure you verify it still exists during
// the loop.
LPMESSAGEINFO pInfo;
IMessageTable *pTable = NULL;
hr = m_pMsgList->GetMessageTable(&pTable);
if (SUCCEEDED(hr))
{
for (DWORD i = 0; (i < cRows && m_pMsgList != NULL); i++)
{
if (SUCCEEDED(hr = m_pMsgList->GetMessageInfo(rgRows[i], &pInfo)))
{
INIT_MSGSITE_STRUCT initStruct;
DWORD dwCreateFlags;
initStruct.initTable.pListSelect = NULL;
m_pMsgList->GetListSelector(&initStruct.initTable.pListSelect);
// Initialize note struct
initStruct.dwInitType = OEMSIT_MSG_TABLE;
initStruct.initTable.pMsgTable = pTable;
initStruct.folderID = m_idFolder;
initStruct.initTable.rowIndex = rgRows[i];
// Decide whether it is news or mail
if (pInfo->dwFlags & ARF_NEWSMSG)
dwCreateFlags = OENCF_NEWSFIRST;
else
dwCreateFlags = 0;
m_pMsgList->FreeMessageInfo(pInfo);
// Create and Open Note
hr = CreateAndShowNote(OENA_READ, dwCreateFlags, &initStruct, m_hwnd);
ReleaseObj(initStruct.initTable.pListSelect);
if (FAILED(hr))
break;
}
}
pTable->Release();
}
if (SUCCEEDED(hr) && g_pInstance)
{
FOLDERTYPE ft = GetFolderType(m_idFolder);
if (ft == FOLDER_IMAP || ft == FOLDER_LOCAL || ft == FOLDER_HTTPMAIL)
g_pInstance->UpdateTrayIcon(TRAYICONACTION_REMOVE);
}
SafeMemFree(rgRows);
return (S_OK);
}
//
// FUNCTION: CMessageView::CmdReply()
//
// PURPOSE: Replies or Reply-All's to the selected message.
//
// RETURN VALUE:
// HRESULT
//
HRESULT CMessageView::CmdReplyForward(DWORD nCmdID, DWORD nCmdExecOpt, VARIANTARG *pvaIn, VARIANTARG *pvaOut)
{
HRESULT hr;
DWORD dwFocused;
DWORD *rgRows = NULL;
DWORD cRows = 0;
OLECMD cmd;
IMessageTable *pTable = NULL;
PROPVARIANT var;
// We can hit this via accelerators. Since accelerators don't go through
// QueryStatus(), we need to make sure this should really be enabled.
cmd.cmdID = nCmdID;
cmd.cmdf = 0;
if (FAILED(QueryStatus(NULL, 1, &cmd, NULL)) || (0 == (cmd.cmdf & OLECMDF_ENABLED)))
return (S_OK);
if (m_pMsgList)
{
// Figure out which message is focused
if (SUCCEEDED(m_pMsgList->GetSelected(&dwFocused, &cRows, &rgRows)))
{
INIT_MSGSITE_STRUCT rInitSite;
DWORD dwCreateFlags;
DWORD dwAction = 0;
// Get the message table from the message list. The note will need
// this to deal with next / prev commands
hr = m_pMsgList->GetMessageTable(&pTable);
if (FAILED(hr))
goto exit;
if ((1 < cRows) && ((ID_FORWARD == nCmdID) || (ID_FORWARD_AS_ATTACH == nCmdID)))
{
IMimeMessage *pMsgFwd = NULL;
BOOL fErrorsOccured = FALSE,
fCreateNote = TRUE;
hr = HrCreateMessage(&pMsgFwd);
if (FAILED(hr))
goto exit;
// Raid 80277; Set default charset
if (NULL == g_hDefaultCharsetForMail)
ReadSendMailDefaultCharset();
pMsgFwd->SetCharset(g_hDefaultCharsetForMail, CSET_APPLY_ALL);
rInitSite.dwInitType = OEMSIT_MSG;
rInitSite.pMsg = pMsgFwd;
rInitSite.folderID = m_idFolder;
dwCreateFlags = 0;
dwAction = OENA_COMPOSE;
for (DWORD i = 0; i < cRows; i++)
{
DWORD iRow = rgRows[i];
IMimeMessage *pMsg = NULL;
// Since this command is
hr = pTable->OpenMessage(iRow, OPEN_MESSAGE_SECURE, &pMsg, NOSTORECALLBACK);
if (SUCCEEDED(hr))
{
// If this is the first message, get the account ID from it
if (i == 0)
{
var.vt = VT_LPSTR;
if (SUCCEEDED(pMsg->GetProp(PIDTOSTR(PID_ATT_ACCOUNTID), NOFLAGS, &var)))
{
pMsgFwd->SetProp(PIDTOSTR(PID_ATT_ACCOUNTID), NOFLAGS, &var);
}
}
if (FAILED(pMsgFwd->AttachObject(IID_IMimeMessage, (LPVOID)pMsg, NULL)))
fErrorsOccured = TRUE;
pMsg->Release();
}
else
fErrorsOccured = TRUE;
}
if (fErrorsOccured)
{
if(AthMessageBoxW(m_hwnd, MAKEINTRESOURCEW(idsAthenaMail),
MAKEINTRESOURCEW(idsErrorAttachingMsgsToNote), NULL, MB_OKCANCEL) == IDCANCEL)
fCreateNote = FALSE;
}
if (fCreateNote)
hr = CreateAndShowNote(dwAction, dwCreateFlags, &rInitSite, m_hwnd);
pMsgFwd->Release();
}
else
{
LPMESSAGEINFO pInfo;
// Get some information about the message
if (SUCCEEDED(hr = m_pMsgList->GetMessageInfo(dwFocused, &pInfo)))
{
// Determine if this is a news or mail message.
if (pInfo->dwFlags & ARF_NEWSMSG)
dwCreateFlags = OENCF_NEWSFIRST;
else
dwCreateFlags = 0;
// Reply or forward
if (nCmdID == ID_FORWARD)
dwAction = OENA_FORWARD;
else if (nCmdID == ID_FORWARD_AS_ATTACH)
dwAction = OENA_FORWARDBYATTACH;
else if (nCmdID == ID_REPLY)
dwAction = OENA_REPLYTOAUTHOR;
else if (nCmdID == ID_REPLY_ALL)
dwAction = OENA_REPLYALL;
else if (nCmdID == ID_REPLY_GROUP)
dwAction = OENA_REPLYTONEWSGROUP;
else
AssertSz(FALSE, "Didn't ask for a valid action");
// Fill out the initialization information
rInitSite.dwInitType = OEMSIT_MSG_TABLE;
rInitSite.initTable.pMsgTable = pTable;
rInitSite.initTable.pListSelect = NULL;
rInitSite.folderID = m_idFolder;
rInitSite.initTable.rowIndex = dwFocused;
m_pMsgList->FreeMessageInfo(pInfo);
// Create the note object
hr = CreateAndShowNote(dwAction, dwCreateFlags, &rInitSite, m_hwnd);
}
}
}
}
exit:
ReleaseObj(pTable);
SafeMemFree(rgRows);
return (S_OK);
}
HRESULT CMessageView::CmdCancelMessage(DWORD nCmdID, DWORD nCmdExecOpt, VARIANTARG *pvaIn, VARIANTARG *pvaOut)
{
HRESULT hr;
DWORD dwFocused;
DWORD *rgRows = NULL;
DWORD cRows = 0;
if (m_pMsgList)
{
// Figure out which message is focused
if (SUCCEEDED(m_pMsgList->GetSelected(&dwFocused, &cRows, &rgRows)))
{
IMessageTable *pTable = NULL;
LPMESSAGEINFO pInfo;
// Get the message table from the message list. The note will need
// this to deal with next / prev commands
hr = m_pMsgList->GetMessageTable(&pTable);
if (FAILED(hr))
goto exit;
// Get some information about the message
if (SUCCEEDED(hr = m_pMsgList->GetMessageInfo(dwFocused, &pInfo)))
{
hr = NewsUtil_HrCancelPost(m_hwnd, m_idFolder, pInfo);
m_pMsgList->FreeMessageInfo(pInfo);
}
pTable->Release();
}
}
exit:
SafeMemFree(rgRows);
return (S_OK);
}
//
// FUNCTION: CMessageView::CmdFillPreview()
//
// PURPOSE: Fills the preview pane with the selected & focused message.
//
// RETURN VALUE:
// HRESULT
//
HRESULT CMessageView::CmdFillPreview(DWORD nCmdID, DWORD nCmdExecOpt, VARIANTARG *pvaIn, VARIANTARG *pvaOut)
{
AssertSz(FALSE, "NYI");
return (E_NOTIMPL);
}
//
// FUNCTION: CMessageView::CmdShowPreview()
//
// PURPOSE: Handles updating the settings dealing with the preview pane.
//
// RETURN VALUE:
// HRESULT
//
HRESULT CMessageView::CmdShowPreview(DWORD nCmdID, DWORD nCmdExecOpt, VARIANTARG *pvaIn, VARIANTARG *pvaOut)
{
FOLDERTYPE ftType;
DWORD dwOpt;
LAYOUTPOS pos;
BOOL fVisible;
DWORD dwFlags;
// Get the folder type
m_pBrowser->GetFolderType(&ftType);
if (ftType == FOLDER_NEWS)
dwOpt = DISPID_MSGVIEW_PREVIEWPANE_NEWS;
else
dwOpt = DISPID_MSGVIEW_PREVIEWPANE_MAIL;
// Get the current settings from the browser
m_pBrowser->GetViewLayout(dwOpt, NULL, &fVisible, &dwFlags, NULL);
// Update the settings just based on the command
switch (nCmdID)
{
case ID_PREVIEW_PANE:
case ID_PREVIEW_SHOW:
{
// Set the complement of the visible bit
m_pBrowser->SetViewLayout(dwOpt, LAYOUT_POS_NA, !fVisible, dwFlags, NULL);
if (!fVisible)
{
// if showing update the preview pane
_UpdatePreviewPane();
}
else
{
// if hiding, clear the contents
m_pPreview->HrUnloadAll(NULL, 0);
}
break;
}
case ID_PREVIEW_BELOW:
{
// Update the position
m_pBrowser->SetViewLayout(dwOpt, LAYOUT_POS_BOTTOM, fVisible, dwFlags, NULL);
break;
}
case ID_PREVIEW_BESIDE:
{
// Update the position
m_pBrowser->SetViewLayout(dwOpt, LAYOUT_POS_LEFT, fVisible, dwFlags, NULL);
break;
}
case ID_PREVIEW_HEADER:
{
// Toggle the header flags
m_pBrowser->SetViewLayout(dwOpt, LAYOUT_POS_NA, fVisible, !dwFlags, NULL);
break;
}
default:
Assert(FALSE);
}
return (S_OK);
}
//
// FUNCTION: CMessageView::CmdRefresh()
//
// PURPOSE: Refreshes the contents of the message list.
//
// RETURN VALUE:
// HRESULT
//
HRESULT CMessageView::CmdRefresh(DWORD nCmdID, DWORD nCmdExecOpt, VARIANTARG* pvaIn, VARIANTARG *pvaOut)
{
HRESULT hr = E_FAIL;
FOLDERINFO FolderInfo;
TraceCall("CMessageView::CmdRefresh");
// Call into the message list now and let it refresh
if (m_pMsgListCT)
hr = m_pMsgListCT->Exec(NULL, ID_REFRESH_INNER, nCmdExecOpt, pvaIn, pvaOut);
// If we succeeded in refreshing the message list, also try to reload the
// preview pane.
_UpdatePreviewPane();
// If this is a local folder and this isn't newsonly mode, in the past we
// do a Send & Recieve.
if (FOLDER_LOCAL == GetFolderType(m_idFolder) && 0 == (g_dwAthenaMode & MODE_NEWSONLY))
PostMessage(m_hwndParent, WM_COMMAND, ID_SEND_RECEIVE, 0);
return (hr);
}
//
// FUNCTION: CMessageView::CmdBlockSender()
//
// PURPOSE: Add the sender of the selected messages to the block senders list
//
// RETURN VALUE:
// HRESULT
//
HRESULT CMessageView::CmdBlockSender(DWORD nCmdID, DWORD nCmdExecOpt, VARIANTARG *pvaIn, VARIANTARG *pvaOut)
{
HRESULT hr = S_OK;
DWORD * rgRows = NULL;
DWORD cRows = 0;
LPMESSAGEINFO pInfo = NULL;
IUnknown * pUnkMessage = NULL;
IMimeMessage * pMessage = 0;
LPSTR pszEmailFrom = NULL;
ADDRESSPROPS rSender = {0};
CHAR szRes[CCHMAX_STRINGRES];
LPSTR pszResult = NULL;
IOERule * pIRule = NULL;
BOOL fMsgInfoFreed = FALSE;
TraceCall("CMessageView::CmdBlockSender");
hr = m_pMsgList->GetSelected(NULL, &cRows, &rgRows);
if (FAILED(hr))
{
goto exit;
}
// It's possible for the message list to go away while we're doing this.
// To keep us from crashing, make sure you verify it still exists during
// the loop.
hr = m_pMsgList->GetMessageInfo(rgRows[0], &pInfo);
if (FAILED(hr))
{
goto exit;
}
// Do we already have the address?
if ((NULL != pInfo->pszEmailFrom) && ('\0' != pInfo->pszEmailFrom[0]))
{
pszEmailFrom = PszDupA(pInfo->pszEmailFrom);
if (NULL == pszEmailFrom)
{
hr = E_OUTOFMEMORY;
goto exit;
}
}
else
{
// Load that message from the store
hr = m_pMsgList->GetMessage(rgRows[0], FALSE, FALSE, &pUnkMessage);
if (FAILED(hr))
{
goto exit;
}
if (NULL == pUnkMessage)
{
hr = E_FAIL;
goto exit;
}
// Get the IMimeMessage interface from the message
hr = pUnkMessage->QueryInterface(IID_IMimeMessage, (LPVOID *) &pMessage);
if (FAILED(hr))
{
goto exit;
}
rSender.dwProps = IAP_EMAIL;
hr = pMessage->GetSender(&rSender);
if (FAILED(hr))
{
goto exit;
}
Assert(rSender.pszEmail && ISFLAGSET(rSender.dwProps, IAP_EMAIL));
if ((NULL == rSender.pszEmail) || ('\0' == rSender.pszEmail[0]))
{
hr = E_FAIL;
goto exit;
}
pszEmailFrom = PszDupA(rSender.pszEmail);
if (NULL == pszEmailFrom)
{
hr = E_OUTOFMEMORY;
goto exit;
}
// We don't need the message anymore
g_pMoleAlloc->FreeAddressProps(&rSender);
ZeroMemory(&rSender, sizeof(rSender));
SafeRelease(pMessage);
}
// Free up the info
m_pMsgList->FreeMessageInfo(pInfo);
fMsgInfoFreed = TRUE;
// Bring up the rule editor for this message
hr = RuleUtil_HrAddBlockSender((0 != (pInfo->dwFlags & ARF_NEWSMSG)) ? RULE_TYPE_NEWS : RULE_TYPE_MAIL, pszEmailFrom);
if (FAILED(hr))
{
goto exit;
}
// Load the template string
AthLoadString(idsSenderAddedPrompt, szRes, sizeof(szRes));
// Allocate the space to hold the final string
DWORD cchSize = (lstrlen(szRes) + lstrlen(pszEmailFrom) + 1);
hr = HrAlloc((VOID **) &pszResult, sizeof(*pszResult) * cchSize);
if (FAILED(hr))
{
goto exit;
}
// Build up the warning string
wnsprintf(pszResult, cchSize, szRes, pszEmailFrom);
// Show the success dialog
if (IDYES == AthMessageBox(m_hwnd, MAKEINTRESOURCE(idsAthena), pszResult, NULL, MB_YESNO | MB_ICONINFORMATION))
{
// Create a block sender rule
hr = HrBlockSendersFromFolder(m_hwnd, 0, m_idFolder, &pszEmailFrom, 1);
if (FAILED(hr))
{
goto exit;
}
}
hr = S_OK;
exit:
SafeRelease(pIRule);
SafeMemFree(pszResult);
g_pMoleAlloc->FreeAddressProps(&rSender);
SafeRelease(pMessage);
SafeRelease(pUnkMessage);
SafeMemFree(pszEmailFrom);
if (FALSE == fMsgInfoFreed)
{
m_pMsgList->FreeMessageInfo(pInfo);
}
SafeMemFree(rgRows);
if (FAILED(hr))
{
AthMessageBoxW(m_hwnd, MAKEINTRESOURCEW(idsAthena),
MAKEINTRESOURCEW(idsSenderError), NULL, MB_OK | MB_ICONERROR);
}
return (hr);
}
//
// FUNCTION: CMessageView::CmdCreateRule()
//
// PURPOSE: Add the sender of the selected messages to the block senders list
//
// RETURN VALUE:
// HRESULT
//
HRESULT CMessageView::CmdCreateRule(DWORD nCmdID, DWORD nCmdExecOpt, VARIANTARG *pvaIn, VARIANTARG *pvaOut)
{
HRESULT hr;
DWORD * rgRows = NULL;
DWORD cRows = 0;
LPMESSAGEINFO pInfo = NULL;
IUnknown * pUnkMessage = NULL;
IMimeMessage * pMessage = 0;
TraceCall("CMessageView::CmdCreateRule");
// Get the array of selected rows from the message list
if (FAILED(hr = m_pMsgList->GetSelected(NULL, &cRows, &rgRows)))
return (hr);
// It's possible for the message list to go away while we're doing this.
// To keep us from crashing, make sure you verify it still exists during
// the loop.
if (SUCCEEDED(hr = m_pMsgList->GetMessageInfo(rgRows[0], &pInfo)))
{
// Load that message from the store
if (S_OK == m_pMsgList->GetMessage(rgRows[0], FALSE, FALSE, &pUnkMessage))
{
// Get the IMimeMessage interface from the message
if (NULL != pUnkMessage)
{
pUnkMessage->QueryInterface(IID_IMimeMessage, (LPVOID *) &pMessage);
}
}
// Bring up the rule editor for this message
hr = HrCreateRuleFromMessage(m_hwnd, (0 != (pInfo->dwFlags & ARF_NEWSMSG)) ?
CRFMF_NEWS : CRFMF_MAIL, pInfo, pMessage);
}
SafeRelease(pMessage);
SafeRelease(pUnkMessage);
m_pMsgList->FreeMessageInfo(pInfo);
SafeMemFree(rgRows);
return (S_OK);
}
//
// FUNCTION: CMessageView::CmdAddToWab()
//
// PURPOSE: Add the sender of the selected messages to the WAB
//
// RETURN VALUE:
// HRESULT
//
HRESULT CMessageView::CmdAddToWab(DWORD nCmdID, DWORD nCmdExecOpt, VARIANTARG *pvaIn, VARIANTARG *pvaOut)
{
HRESULT hr = S_OK;
DWORD *rgRows = NULL;
DWORD cRows = 0;
LPMESSAGEINFO pInfo;
LPWAB pWAB = 0;
TraceCall("CMessageView::CmdAddToWab");
// Get the array of selected rows from the message list
if (FAILED(hr = m_pMsgList->GetSelected(NULL, &cRows, &rgRows)))
return (hr);
// Get the header info for the message
if (SUCCEEDED(hr = m_pMsgList->GetMessageInfo(rgRows[0], &pInfo)))
{
// Get a WAB object
if (SUCCEEDED(hr = HrCreateWabObject(&pWAB)))
{
// Add the sender to the WAB
if (FAILED(hr = pWAB->HrAddNewEntryA(pInfo->pszDisplayFrom, pInfo->pszEmailFrom)))
{
if (hr == MAPI_E_COLLISION)
AthMessageBoxW(m_hwnd, MAKEINTRESOURCEW(idsAthena), MAKEINTRESOURCEW(idsErrAddrDupe), 0, MB_OK | MB_ICONSTOP);
else
AthMessageBoxW(m_hwnd, MAKEINTRESOURCEW(idsAthena), MAKEINTRESOURCEW(idsErrAddToWabSender), 0, MB_OK | MB_ICONSTOP);
}
pWAB->Release();
}
m_pMsgList->FreeMessageInfo(pInfo);
}
SafeMemFree(rgRows);
return (S_OK);
}
//
// FUNCTION: CMessageView::CmdCombineAndDecode()
//
// PURPOSE: Combines the selected messages into a single message.
//
// RETURN VALUE:
// HRESULT
//
HRESULT CMessageView::CmdCombineAndDecode(DWORD nCmdID, DWORD nCmdExecOpt, VARIANTARG *pvaIn, VARIANTARG *pvaOut)
{
DWORD *rgRows = NULL;
DWORD cRows = 0;
CCombineAndDecode *pDecode = NULL;
HRESULT hr;
// Create the decoder object
pDecode = new CCombineAndDecode();
if (!pDecode)
return (S_OK);
// Get the array of selected rows from the message list
if (FAILED(hr = m_pMsgList->GetSelected(NULL, &cRows, &rgRows)))
return (hr);
// Get a pointer to the message table
IMessageTable *pTable = NULL;
if (SUCCEEDED(m_pMsgList->GetMessageTable(&pTable)))
{
// Initialize the decoder
pDecode->Start(m_hwnd, pTable, rgRows, cRows, m_idFolder);
}
SafeMemFree(rgRows);
pDecode->Release();
pTable->Release();
return (S_OK);
}
//
// FUNCTION: CMessageView::_SetListOptions()
//
// PURPOSE: Maps the folder that we're about to view to the correct column
// set and various options.
//
// RETURN VALUE:
// Returns S_OK if the column set was identified and set correctly. Returns
// a standard error HRESULT otherwise.
//
HRESULT CMessageView::_SetListOptions(void)
{
HRESULT hr;
BOOL fSelectFirst = FALSE;
FOLDERTYPE ft = GetFolderType(m_idFolder);
// Make sure this badboy exists
if (!m_pMsgList)
return (E_UNEXPECTED);
FOLDER_OPTIONS fo = {0};
fo.cbSize = sizeof(FOLDER_OPTIONS);
fo.dwMask = FOM_EXPANDTHREADS | FOM_SELECTFIRSTUNREAD | FOM_THREAD | FOM_MESSAGELISTTIPS | FOM_POLLTIME | FOM_COLORWATCHED | FOM_GETXHEADERS | FOM_SHOWDELETED | FOM_SHOWREPLIES;
fo.fExpandThreads = DwGetOption(OPT_AUTOEXPAND);
fo.fMessageListTips = DwGetOption(OPT_MESSAGE_LIST_TIPS);
fo.dwPollTime = DwGetOption(OPT_POLLFORMSGS);
fo.clrWatched = DwGetOption(OPT_WATCHED_COLOR);
fo.dwGetXHeaders = DwGetOption(OPT_DOWNLOADCHUNKS);
fo.fDeleted = DwGetOption(OPT_SHOW_DELETED);
fo.fReplies = DwGetOption(OPT_SHOW_REPLIES);
switch (ft)
{
case FOLDER_NEWS:
fo.fThread = DwGetOption(OPT_NEWS_THREAD);
fo.fSelectFirstUnread = TRUE;
break;
case FOLDER_LOCAL:
case FOLDER_HTTPMAIL:
fo.fThread = DwGetOption(OPT_MAIL_THREAD);
fo.fSelectFirstUnread = FALSE;
break;
case FOLDER_IMAP:
fo.fThread = DwGetOption(OPT_MAIL_THREAD);
fo.fSelectFirstUnread = FALSE;
break;
}
hr = m_pMsgList->SetViewOptions(&fo);
return (hr);
}
BOOL CMessageView::_IsPreview(void)
{
FOLDERTYPE ftType;
DWORD dwOpt;
// Get the folder type
m_pBrowser->GetFolderType(&ftType);
if (ftType == FOLDER_NEWS)
dwOpt = DISPID_MSGVIEW_PREVIEWPANE_NEWS;
else
dwOpt = DISPID_MSGVIEW_PREVIEWPANE_MAIL;
// Ask the browser if it should be on or off
BOOL f = FALSE;
if (m_pBrowser)
m_pBrowser->GetViewLayout(dwOpt, 0, &f, 0, 0);
return f;
}
BOOL CMessageView::_InitMessageList(void)
{
HWND hwndList;
// Create the message list object
if (FAILED(CreateMessageList(NULL, &m_pMsgList)))
return (FALSE);
// Initialize the message list
m_pMsgList->CreateList(m_hwnd, (IViewWindow *) this, &hwndList);
// Get the command target interface for the list
m_pMsgList->QueryInterface(IID_IOleCommandTarget, (LPVOID *) &m_pMsgListCT);
m_pMsgList->QueryInterface(IID_IOleInPlaceActiveObject, (LPVOID *) &m_pMsgListAO);
// Request Notifications
AtlAdvise(m_pMsgList, (IUnknown *)(IViewWindow *) this, DIID__MessageListEvents, &m_dwCookie);
// Set the column set for the message list
_SetListOptions();
return (TRUE);
}
//
// FUNCTION: CMessageView::_InitPreviewPane()
//
// PURPOSE: Creates the Preview Pane object and initializes it.
//
// RETURN VALUE:
// TRUE if the object was created and initialized, FALSE otherwise.
//
BOOL CMessageView::_InitPreviewPane(void)
{
CMimeEditDocHost *pDocHost = NULL;
CStatusBar *pStatusBar = NULL;
DWORD dwHeader;
LAYOUTPOS pos;
BOOL fVisible;
DWORD dwOpt;
HRESULT hr;
FOLDERTYPE ftType;
DWORD dwSize;
TraceCall("CMessageView::_InitPreviewPane");
// We only create the preview pane if it's supposed to be visible.
m_pBrowser->GetFolderType(&ftType);
if (ftType == FOLDER_NEWS)
dwOpt = DISPID_MSGVIEW_PREVIEWPANE_NEWS;
else
dwOpt = DISPID_MSGVIEW_PREVIEWPANE_MAIL;
// Get the settings from the browser
m_pBrowser->GetViewLayout(dwOpt, &pos, &fVisible, &dwHeader, &dwSize);
// Stash this info
m_dwSplitHorzPct = LOWORD(dwSize);
m_dwSplitVertPct = HIWORD(dwSize);
if (fVisible)
{
// Create the dochost
pDocHost = new CMimeEditDocHost(MEBF_OUTERCLIENTEDGE);
if (!pDocHost)
goto error;
// We want to get the IBodyObj2 interface from it.
pDocHost->QueryInterface(IID_IBodyObj2, (LPVOID *) &m_pPreview);
if (!m_pPreview)
goto error;
pDocHost->Release();
// Also get the IOleCommandTarget interface from it. If it fails, that's OK.
m_pPreview->QueryInterface(IID_IOleCommandTarget, (LPVOID *) &m_pPreviewCT);
if (m_pBrowser->GetStatusBar(&pStatusBar)==S_OK)
{
m_pPreview->HrSetStatusBar(pStatusBar);
pStatusBar->Release();
}
// Create the preview window
if (FAILED(m_pPreview->HrInit(m_hwnd, IBOF_DISPLAYTO|IBOF_TABLINKS, (IBodyOptions *) this)))
goto error;
hr = m_pPreview->HrShow(fVisible);
if (FAILED(hr))
goto error;
m_pPreview->HrSetText(MAKEINTRESOURCE(idsHTMLEmptyPreviewSel));
UpdateLayout(fVisible, dwHeader, pos == LAYOUT_POS_LEFT, FALSE);
// Give the preview pane our event sink interface
m_pPreview->SetEventSink((IMimeEditEventSink *) this);
return (TRUE);
}
error:
SafeRelease(pDocHost);
SafeRelease(m_pPreview);
return (FALSE);
}
void CMessageView::_UpdatePreviewPane(BOOL fForceDownload)
{
DWORD dwFocused;
DWORD cSelected;
DWORD *rgSelected = 0;
IUnknown *pUnkMessage = 0;
HRESULT hr;
if (m_pMsgList && m_pPreview)
{
m_idMessageFocus = MESSAGEID_INVALID;
m_fNotDownloaded = FALSE;
// Figure out which message is focused
if (SUCCEEDED(m_pMsgList->GetSelected(&dwFocused, &cSelected, &rgSelected)))
{
// If there is a focused item
if (-1 == dwFocused || 0 == cSelected)
{
m_pPreview->HrUnloadAll(idsHTMLEmptyPreviewSel, 0);
}
else
{
// Load that message from the store
hr = m_pMsgList->GetMessage(dwFocused, fForceDownload || DwGetOption(OPT_AUTOFILLPREVIEW), TRUE, &pUnkMessage);
switch (hr)
{
case MIME_E_SECURITY_CANTDECRYPT:
m_pPreview->LoadHtmlErrorPage(c_szErrPage_SMimeEncrypt);
break;
#ifdef SMIME_V3
case MIME_E_SECURITY_LABELACCESSDENIED:
case MIME_E_SECURITY_LABELACCESSCANCELLED:
case MIME_E_SECURITY_LABELCORRUPT:
m_pPreview->LoadHtmlErrorPage(c_szErrPage_SMimeLabel);
break;
#endif // SMIME_V3
case STORE_E_EXPIRED:
m_pPreview->LoadHtmlErrorPage(c_szErrPage_Expired);
break;
case STORE_E_NOBODY:
AssertSz(DwGetOption(OPT_AUTOFILLPREVIEW)==FALSE, "AutoPreview is on, download should have been started!");
if (g_pConMan->IsGlobalOffline())
m_pPreview->LoadHtmlErrorPage(c_szErrPage_Offline);
else
m_pPreview->LoadHtmlErrorPage(c_szErrPage_NotDownloaded);
m_fNotDownloaded = TRUE;
break;
case DB_E_DISKFULL:
m_pPreview->LoadHtmlErrorPage(c_szErrPage_DiskFull);
break;
case DB_S_NOTFOUND:
{
FOLDERINFO FolderInfo;
//I don't think we need this coz its being handled in callbackcanconnect
//If the message is not found in the store, we ask it to download.
if (g_pStore && SUCCEEDED(g_pStore->GetFolderInfo(m_idFolder, &FolderInfo)))
{
if(g_pConMan && !(g_pConMan->IsGlobalOffline()))
{
CHAR szAccountId[CCHMAX_ACCOUNT_NAME];
if (SUCCEEDED(GetFolderAccountId(&FolderInfo, szAccountId, ARRAYSIZE(szAccountId))))
{
if (g_pConMan->Connect(szAccountId, m_hwnd, TRUE)== S_OK)
hr = m_pMsgList->GetMessage(dwFocused, TRUE, TRUE, &pUnkMessage);
}
}
g_pStore->FreeRecord(&FolderInfo);
}
break;
}
case STORE_S_ALREADYPENDING:
case E_PENDING:
{
// if the message is being downloaded, let's store the message-id and wait for an update
LPMESSAGEINFO pInfo;
// clear the contents waiting for the new message to download
m_pPreview->HrUnloadAll(NULL, 0);
if (SUCCEEDED(m_pMsgList->GetMessageInfo(dwFocused, &pInfo)))
{
m_idMessageFocus = pInfo->idMessage;
m_pMsgList->FreeMessageInfo(pInfo);
}
break;
}
case E_NOT_ONLINE:
{
m_pPreview->LoadHtmlErrorPage(c_szErrPage_Offline);
break;
}
case S_OK:
{
// Get the IMimeMessage interface from the message
IMimeMessage *pMessage = 0;
if (pUnkMessage && SUCCEEDED(pUnkMessage->QueryInterface(IID_IMimeMessage, (LPVOID *) &pMessage)))
{
// bobn, brianv says we have to remove this...
/*if (g_dwBrowserFlags == 1)
{
LPSTR lpsz = NULL;
if (SUCCEEDED(MimeOleGetBodyPropA(pMessage, HBODY_ROOT, PIDTOSTR(PID_HDR_SUBJECT), NOFLAGS, &lpsz)))
{
if (0 == strcmp(lpsz, "Credits"))
g_dwBrowserFlags |= 2;
else
g_dwBrowserFlags = 0;
SafeMimeOleFree(lpsz);
}
}*/
if (_DoEmailBombCheck(pMessage)==S_OK)
{
// Get the load interface from the preview pane object
IPersistMime *pPersistMime = 0;
if (SUCCEEDED(m_pPreview->QueryInterface(IID_IPersistMime, (LPVOID *) &pPersistMime)))
{
DWORD dwHeader;
LAYOUTPOS pos;
BOOL fVisible;
DWORD dwOpt;
DWORD dwSize;
FOLDERTYPE ftType;
CStatusBar *pStatusBar = NULL;
// remember focus
BOOL fFocused = ((m_pPreview->HrHasFocus() == S_OK) ? TRUE : FALSE);
m_pBrowser->GetFolderType(&ftType);
if (ftType == FOLDER_NEWS)
dwOpt = DISPID_MSGVIEW_PREVIEWPANE_NEWS;
else
dwOpt = DISPID_MSGVIEW_PREVIEWPANE_MAIL;
// Get the settings from the browser
m_pBrowser->GetViewLayout(dwOpt, &pos, &fVisible, &dwHeader, &dwSize);
m_pPreview->HrResetDocument();
m_pPreview->HrSetStyle(dwHeader ? MESTYLE_PREVIEW : MESTYLE_MINIHEADER);
// Give the preview pane our event sink interface
m_pPreview->SetEventSink((IMimeEditEventSink *) this);
pPersistMime->Load(pMessage);
pPersistMime->Release();
// restore status bar
if (m_pBrowser->GetStatusBar(&pStatusBar)==S_OK)
{
m_pPreview->HrSetStatusBar(pStatusBar);
pStatusBar->Release();
}
// return focus
if(fFocused)
m_pPreview->HrSetUIActivate();
}
}
pMessage->Release();
}
pUnkMessage->Release();
break;
}
default:
m_pPreview->LoadHtmlErrorPage(c_szErrPage_GenFailure);
break;
}
}
if (rgSelected)
MemFree(rgSelected);
}
}
}
//
// FUNCTION: CMessageView::_SetProgressStatusText()
//
// PURPOSE: Takes the provided BSTR, converts it to ANSI, and smacks it
// on the status bar.
//
// PARAMETERS:
// [in] bstr - henious BSTR to put on the status bar.
//
void CMessageView::_SetProgressStatusText(BSTR bstr)
{
LPTSTR psz = NULL;
CStatusBar *pStatusBar = NULL;
m_pBrowser->GetStatusBar(&pStatusBar);
if (pStatusBar)
{
pStatusBar->SetStatusText((LPTSTR) bstr);
/*
CComBSTR cString(bstr);
// Allocate a string large enough
if (MemAlloc((LPVOID *) &psz, 2 * cString.Length()))
{
WideCharToMultiByte(CP_ACP, 0, cString, -1,
psz, 2 * cString.Length(), NULL, NULL);
pStatusBar->SetStatusText((LPTSTR) psz);
MemFree(psz);
}
*/
pStatusBar->Release();
}
}
//
// FUNCTION: CMessageView::_OnMessageAvailable()
//
// PURPOSE: Fired by the listview when a message has completed downloading
// if the message is the currently selected message in the preview
// then we update it. If it is not, we ignore the notification.
// We check for downloading errors and display and appropriate message
//
// PARAMETERS:
// [in] idMessage - message id of the message that was downloaded
// [in] hrCompletion - hresult indicating possible error failure
//
HRESULT CMessageView::_OnMessageAvailable(MESSAGEID idMessage, HRESULT hrCompletion)
{
if (m_idMessageFocus != idMessage)
return S_FALSE;
switch (hrCompletion)
{
// if we get a STORE_E_EXPIRED, then reload the preview pane to show error
case S_OK:
case STORE_E_EXPIRED:
// we post a message to ourselves to update the preview pane. We do this because
// any refcounts on the IStream into the store at this point have it locked for write
// if we post, then the stack is unwound after the notifications are fired and we're in a
// good state.
PostMessage(m_hwnd, WM_UPDATE_PREVIEW, (WPARAM)idMessage, 0);
break;
case S_FALSE:
case STORE_E_OPERATION_CANCELED:
case hrUserCancel:
case IXP_E_USER_CANCEL:
// S_FALSE means the operation was canceled
if (m_idMessageFocus != MESSAGEID_INVALID)
m_pPreview->LoadHtmlErrorPage(c_szErrPage_DownloadCanceled);
break;
case STG_E_MEDIUMFULL:
m_pPreview->LoadHtmlErrorPage(c_szErrPage_DiskFull);
break;
case HR_E_USER_CANCEL_CONNECT:
case HR_E_OFFLINE:
m_pPreview->LoadHtmlErrorPage(c_szErrPage_Offline);
break;
case MIME_E_SECURITY_CANTDECRYPT:
m_pPreview->LoadHtmlErrorPage(c_szErrPage_SMimeEncrypt);
break;
#ifdef SMIME_V3
case MIME_E_SECURITY_LABELACCESSDENIED:
case MIME_E_SECURITY_LABELACCESSCANCELLED:
case MIME_E_SECURITY_LABELCORRUPT:
m_pPreview->LoadHtmlErrorPage(c_szErrPage_SMimeLabel);
break;
#endif // SMIME_V3
default:
m_pPreview->LoadHtmlErrorPage(c_szErrPage_GenFailure);
break;
}
return S_OK;
}
//
// FUNCTION: CMessageView::_DoEmailBombCheck
//
// PURPOSE: Validates to ensure that the last time we closed OE we shutdown
// correctly. If we did not shutdown correctly, we look at the msgid stamp
// that we stored in the registry for the last selected preview message
// if it was the message we are about to preview, we do not show the
// message, to prevent jscript attacks etc.
//
// PARAMETERS:
// none
//
HRESULT CMessageView::_DoEmailBombCheck(LPMIMEMESSAGE pMsg)
{
FILETIME ft;
PROPVARIANT va;
DWORD dwType,
cb;
va.vt = VT_FILETIME;
if (pMsg && pMsg->GetProp(PIDTOSTR(STR_HDR_DATE), 0, &va)==S_OK)
{
if (g_fBadShutdown)
{
g_fBadShutdown=FALSE;
cb = sizeof(FILETIME);
if (AthUserGetValue(NULL, c_szLastMsg, &dwType, (LPBYTE)&ft, &cb)==S_OK &&
(ft.dwLowDateTime == va.filetime.dwLowDateTime &&
ft.dwHighDateTime == va.filetime.dwHighDateTime))
{
// possible the same dude
m_pPreview->LoadHtmlErrorPage(c_szErrPage_MailBomb);
return S_FALSE;
}
}
AthUserSetValue(NULL, c_szLastMsg, REG_BINARY, (LPBYTE)&va.filetime, sizeof(FILETIME));
}
return S_OK;
}
void CMessageView::_OptionUpdate(DWORD dwUpdate)
{
if (m_pMsgList &&
(dwUpdate == OPT_AUTOEXPAND ||
dwUpdate == OPT_MESSAGE_LIST_TIPS ||
dwUpdate == OPT_POLLFORMSGS ||
dwUpdate == OPT_WATCHED_COLOR ||
dwUpdate == OPT_DOWNLOADCHUNKS))
{
FOLDER_OPTIONS fo = {0};
fo.cbSize = sizeof(FOLDER_OPTIONS);
fo.dwMask = FOM_EXPANDTHREADS | FOM_MESSAGELISTTIPS | FOM_POLLTIME | FOM_COLORWATCHED | FOM_GETXHEADERS;
fo.fExpandThreads = DwGetOption(OPT_AUTOEXPAND);
fo.fMessageListTips = DwGetOption(OPT_MESSAGE_LIST_TIPS);
fo.dwPollTime = DwGetOption(OPT_POLLFORMSGS);
fo.clrWatched = DwGetOption(OPT_WATCHED_COLOR);
fo.dwGetXHeaders = DwGetOption(OPT_DOWNLOADCHUNKS);
m_pMsgList->SetViewOptions(&fo);
}
}
void CMessageView::_SetDefaultStatusText(void)
{
DWORD cTotal;
DWORD cUnread;
DWORD cOnServer;
CStatusBar *pStatusBar = NULL;
TCHAR szStatus[CCHMAX_STRINGRES + 20];
TCHAR szFmt[CCHMAX_STRINGRES];
DWORD ids;
// If we don't have a browser pointer, we can't get the status bar
if (!m_pBrowser || !m_pMsgList)
return;
// Get the status bar if there is one.
m_pBrowser->GetStatusBar(&pStatusBar);
if (pStatusBar)
{
// Get the counts from the table
if (SUCCEEDED(m_pMsgList->GetMessageCounts(&cTotal, &cUnread, &cOnServer)))
{
// If there are still messages on server load a different
// status string.
if (cOnServer)
{
AthLoadString(idsXMsgsYUnreadZonServ, szFmt, ARRAYSIZE(szFmt));
wnsprintf(szStatus, ARRAYSIZE(szStatus), szFmt, cTotal, cUnread, cOnServer);
}
else
{
AthLoadString(idsXMsgsYUnread, szFmt, ARRAYSIZE(szFmt));
wnsprintf(szStatus, ARRAYSIZE(szStatus), szFmt, cTotal, cUnread);
}
pStatusBar->SetStatusText(szStatus);
// Also update the toolbar since commands like "Mark as Read" might
// change. However, we only do this if we go between zero and some or
// vice versa.
if ((m_cItems == 0 && cTotal) || (m_cItems != 0 && cTotal == 0) ||
(m_cUnread == 0 && cUnread) || (m_cUnread != 0 && cUnread == 0))
{
m_pBrowser->UpdateToolbar();
}
// Save this for next time.
m_cItems = cTotal;
m_cUnread = cUnread;
}
pStatusBar->Release();
}
}
BOOL CMessageView::_ReuseMessageFolder(IViewWindow *pPrevView)
{
IServerInfo *pInfo = NULL;
FOLDERID idPrev = FOLDERID_INVALID;
FOLDERID idServerPrev = FOLDERID_INVALID;
FOLDERID idServerCur = FOLDERID_INVALID;
BOOL fReturn = FALSE;
if (pPrevView && SUCCEEDED(pPrevView->QueryInterface(IID_IServerInfo, (LPVOID *) &pInfo)))
{
if (SUCCEEDED(pInfo->GetFolderId(&idPrev)))
{
if (SUCCEEDED(GetFolderServerId(idPrev, &idServerPrev)))
{
if (SUCCEEDED(GetFolderServerId(m_idFolder, &idServerCur)))
{
if (idServerPrev == idServerCur)
{
if (S_OK == pInfo->GetMessageFolder(&m_pServer))
{
m_pServer->ConnectionAddRef();
fReturn = TRUE;
}
}
}
}
}
pInfo->Release();
}
return (fReturn);
}