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.
 
 
 
 
 
 

5983 lines
187 KiB

#include "precomp.h"
#include "PrevWnd.h"
#include "PrevCtrl.h"
#include "resource.h"
#include <shimgdata.h>
#include "shutil.h"
#include "tasks.h"
#include <shellp.h>
#include <ccstock2.h>
#include <htmlhelp.h>
#include "prwiziid.h"
#pragma hdrstop
#define GET_X_LPARAM(lp) ((int)(short)LOWORD(lp))
#define GET_Y_LPARAM(lp) ((int)(short)HIWORD(lp))
#define COPYDATATYPE_DATAOBJECT 1
#define COPYDATATYPE_FILENAME 2
int g_x = 0, g_y = 0; // mouse coordinates
BOOL g_bMirroredOS = FALSE;
#define HTMLHELP_FILENAME TEXT("ImgPrev.chm")
static COLORREF g_crCustomColors[] = {
RGB(255,255,255),
RGB(255,255,255),
RGB(255,255,255),
RGB(255,255,255),
RGB(255,255,255),
RGB(255,255,255),
RGB(255,255,255),
RGB(255,255,255),
RGB(255,255,255),
RGB(255,255,255),
RGB(255,255,255),
RGB(255,255,255),
RGB(255,255,255),
RGB(255,255,255),
RGB(255,255,255),
RGB(255,255,255)
};
static void _RotateRect(CRect &rect, CAnnotation* pAnnotation)
{
UINT uType = pAnnotation->GetType();
if (uType != MT_TYPEDTEXT && uType != MT_FILETEXT && uType != MT_STAMP && uType != MT_ATTACHANOTE)
return;
CTextAnnotation* pTextAnnotation = (CTextAnnotation*)pAnnotation;
int nOrientation = pTextAnnotation->GetOrientation();
if (nOrientation == 900 || nOrientation == 2700)
{
int nWidth = rect.Width();
int nHeight = rect.Height();
rect.right = rect.left + nHeight;
rect.bottom = rect.top + nWidth;
}
}
CPreviewWnd::CPreviewWnd() : m_ctlToolbar(NULL, this, 1), m_ctlPreview(this), m_ctlEdit(NULL, this, 2)
{
// we are often created on the stack, so we can't be sure that we're zero initialized
m_fCanCrop = FALSE;
m_fCropping = FALSE;
m_rectCropping.SetRectEmpty();
m_fBusy = FALSE;
m_hCurOld = NULL;
m_hCurrent = NULL;
m_fWarnQuietSave = TRUE;
m_fWarnNoSave = TRUE;
m_fPromptingUser = FALSE;
m_fCanAnnotate = FALSE;
m_fAnnotating = FALSE;
m_fEditingAnnotation = FALSE;
m_fDirty = FALSE;
m_pEvents = 0;
m_fPaused = FALSE;
m_fGoBack = FALSE;
m_fHidePrintBtn = FALSE;
m_fPrintable = FALSE;
m_fDisableEdit = FALSE;
m_fCanSave = TRUE;
m_fExitApp = FALSE;
m_fAllowContextMenu = TRUE; // full screen window always has a context menu
m_iCurSlide = -1;
m_iDecodingNextImage = -1;
m_pNextImageData = NULL;
m_fToolbarHidden = TRUE;
m_dwMultiPageMode = MPCMD_HIDDEN;
m_fIgnoreUITimers = FALSE;
DWORD cbSize = sizeof(m_uTimeout);
UINT uDefault = DEFAULT_SHIMGVW_TIMEOUT;
SHRegGetUSValue(REGSTR_SHIMGVW, REGSTR_TIMEOUT, NULL, (void *)&m_uTimeout, &cbSize, FALSE, (void *)&uDefault, sizeof(uDefault));
InitSelectionTracking();
g_bMirroredOS = IS_MIRRORING_ENABLED(); // global????
m_hdpaSelectedAnnotations = NULL;
m_haccel = NULL;
m_hpal = NULL;
m_dwMode = 0;
m_fShowToolbar = TRUE;
m_punkSite = NULL;
m_pImageFactory = NULL;
m_pcwndSlideShow = NULL;
m_hFont = NULL;
m_pImageData = NULL;
m_ppidls = NULL;
m_cItems = 0;
_pcm3 = NULL;
m_pTaskScheduler = NULL;
m_pici = NULL;
_pdtobj = NULL;
m_fFirstTime = TRUE;
m_fFirstItem = FALSE;
m_dwEffect = DROPEFFECT_NONE;
m_fIgnoreNextNotify = FALSE;
m_uRegister = 0;
m_fNoRestore = FALSE;
m_bRTLMirrored = FALSE;
m_cWalkDepth = 0;
m_fIgnoreAllNotifies = FALSE;
}
HRESULT CPreviewWnd::Initialize(CPreviewWnd* pother, DWORD dwMode, BOOL bExitApp)
{
HRESULT hr = E_OUTOFMEMORY;
m_hdpaSelectedAnnotations = DPA_Create(16);
if (m_hdpaSelectedAnnotations)
{
hr = S_OK;
m_dwMode = dwMode;
m_fExitApp = bExitApp;
// Set some defaults based on the mode
if (CONTROL_MODE == m_dwMode)
{
m_fHidePrintBtn = TRUE;
}
if (pother)
{
m_fHidePrintBtn = pother->m_fHidePrintBtn;
m_fPrintable = pother->m_fPrintable;
m_fDisableEdit = pother->m_fDisableEdit;
m_fCanSave = pother->m_fCanSave;
m_haccel = pother->m_haccel;
m_dwMultiPageMode = pother->m_dwMultiPageMode;
m_hpal = pother->m_hpal;
m_iCurSlide = pother->m_iCurSlide;
m_uTimeout = pother->m_uTimeout;
SetSite(pother->m_punkSite);
// we grab a reference to the controlling objects m_pImageFactory
// becaue it would be odd to create new ones.
m_pImageFactory = pother->m_pImageFactory;
if (m_pImageFactory)
{
m_pImageFactory->AddRef();
}
m_pTaskScheduler = pother->m_pTaskScheduler;
if (m_pTaskScheduler)
{
m_pTaskScheduler->AddRef();
}
// lets copy the DPA of items also, and the current index
if (pother->m_ppidls)
{
m_ppidls = (LPITEMIDLIST*)CoTaskMemAlloc(sizeof(LPITEMIDLIST)*pother->m_cItems);
if (m_ppidls)
{
for (int iItem = 0; iItem != pother->m_cItems; iItem++)
{
if (SUCCEEDED(pother->_GetItem(iItem, &m_ppidls[m_cItems])))
{
m_cItems++;
}
}
}
}
}
}
return hr;
}
int ClearCB(void *p, void *pData);
CPreviewWnd::~CPreviewWnd()
{
CleanupSelectionTracking();
if (m_hdpaSelectedAnnotations != NULL)
DPA_Destroy(m_hdpaSelectedAnnotations);
::DeleteObject(m_hFont);
ATOMICRELEASE(m_pImageData);
ATOMICRELEASE(m_pNextImageData);
ATOMICRELEASE(m_pImageFactory);
SetSite(NULL);
ATOMICRELEASE(_pdtobj);
ATOMICRELEASE(m_pTaskScheduler);
_ClearDPA();
if (m_pcwndSlideShow)
{
if (m_pcwndSlideShow->m_hWnd)
{
m_pcwndSlideShow->DestroyWindow();
}
delete m_pcwndSlideShow;
}
if (m_pici)
{
LocalFree(m_pici);
m_pici = NULL;
}
}
BOOL CPreviewWnd::CreateSlideshowWindow(UINT cWalkDepth)
{
RECT rc = { CW_USEDEFAULT, CW_USEDEFAULT, 0, 0 };
if (!Create(NULL, rc, NULL, WS_VISIBLE | WS_POPUP | WS_CLIPCHILDREN))
return FALSE;
WINDOWPLACEMENT wp = {0};
wp.length = sizeof(wp);
GetWindowPlacement(&wp);
wp.showCmd = SW_MAXIMIZE;
SetWindowPlacement(&wp);
SetWindowPos(NULL, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE|SWP_NOZORDER|SWP_SHOWWINDOW);
// when we get called from autoplay to do a slideshow,
// we need to walk deeply to find everything
// that the autoplay code may have found
m_cWalkDepth = cWalkDepth;
return TRUE;
}
BOOL CPreviewWnd::_CloseSlideshowWindow()
{
if (m_fExitApp)
PostQuitMessage(0);
else
PostMessage(WM_CLOSE, 0, 0);
return TRUE;
}
BOOL CPreviewWnd::CreateViewerWindow()
{
// create the window hidden, that way any sizing etc we perform doesn't reflect
// until its actually visible.
RECT rc = { CW_USEDEFAULT, CW_USEDEFAULT, 0, 0 };
BOOL bRet = (NULL != Create(NULL, rc, NULL, WS_OVERLAPPEDWINDOW));
m_haccel = LoadAccelerators(_Module.GetModuleInstance(), MAKEINTRESOURCE(IDA_PREVWND_SINGLEPAGE));
if (bRet)
{
// restore the window size based on the information we store in the registry.
HKEY hk;
if (ERROR_SUCCESS == RegOpenKey(HKEY_CURRENT_USER, REGSTR_SHIMGVW, &hk))
{
DWORD cbSize, dwType;
// set the window placement, passing the restore rectangle for the window.
WINDOWPLACEMENT wp = { 0 };
wp.length = sizeof(wp);
GetWindowPlacement(&wp);
cbSize = sizeof(wp.rcNormalPosition);
RegQueryValueEx(hk, REGSTR_BOUNDS, NULL, &dwType, (BYTE*)&wp.rcNormalPosition, &cbSize);
BOOL fMaximize = TRUE;
cbSize = sizeof(fMaximize);
RegQueryValueEx(hk, REGSTR_MAXIMIZED, NULL, &dwType, (BYTE*)&fMaximize, &cbSize);
if (fMaximize)
wp.showCmd = SW_MAXIMIZE;
SetWindowPlacement(&wp);
RegCloseKey(hk);
}
// now show the window having set its placement etc.
SetWindowPos(NULL, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE|SWP_NOZORDER|SWP_SHOWWINDOW);
}
return bRet;
}
void ReplaceWindowIcon(HWND hwnd, int id, HICON hicon)
{
HICON hiconOld = (HICON)SendMessage(hwnd, WM_SETICON, id, (LPARAM)hicon);
if (hiconOld)
DestroyIcon(hiconOld);
}
LRESULT CPreviewWnd::OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL&)
{
HRESULT hr = S_OK;
if (IS_BIDI_LOCALIZED_SYSTEM())
{
SHSetWindowBits(m_hWnd, GWL_EXSTYLE, WS_EX_LAYOUTRTL, WS_EX_LAYOUTRTL);
m_bRTLMirrored = TRUE;
}
if (!m_pImageFactory)
{
hr = CoCreateInstance(CLSID_ShellImageDataFactory, NULL, CLSCTX_INPROC,
IID_PPV_ARG(IShellImageDataFactory, &m_pImageFactory));
if (FAILED(hr))
return -1;
}
if (!m_pTaskScheduler)
{
hr = IUnknown_QueryService(m_punkSite, SID_ShellTaskScheduler, IID_PPV_ARG(IShellTaskScheduler, &m_pTaskScheduler));
if (FAILED(hr))
{
hr = CoCreateInstance(CLSID_ShellTaskScheduler, NULL, CLSCTX_INPROC,
IID_PPV_ARG(IShellTaskScheduler, &m_pTaskScheduler));
if (FAILED(hr))
return -1;
}
}
// figure out where to place the zoom window
RECT rcWnd;
GetClientRect(&rcWnd);
if (m_fShowToolbar)
{
// Create a toolbar control and then subclass it
if (!CreateToolbar())
return -1;
m_iSSToolbarSelect = 0;
}
HICON hicon = LoadIcon(_Module.GetModuleInstance(), MAKEINTRESOURCE(IDI_FULLSCREEN));
ReplaceWindowIcon(m_hWnd, ICON_SMALL, hicon);
ReplaceWindowIcon(m_hWnd, ICON_BIG, hicon);
// Create the preview window
DWORD dwExStyle = 0; // (WINDOW_MODE == m_dwMode) ? WS_EX_CLIENTEDGE : 0 ;
if (m_ctlPreview.Create(m_hWnd, rcWnd, NULL, WS_CHILD|WS_VISIBLE|WS_CLIPCHILDREN|WS_CLIPSIBLINGS, dwExStyle))
{
// When the window is created its default mode should be NOACTION. This call is needed
// because the object might have a longer life cycle than the window. If a new window
// is created for the same object we want to reset the state.
m_ctlPreview.SetMode(CZoomWnd::MODE_NOACTION);
m_ctlPreview.SetScheduler(m_pTaskScheduler);
}
RegisterDragDrop(m_hWnd, SAFECAST(this, IDropTarget *));
return 0;
}
HRESULT CPreviewWnd::_SaveIfDirty(BOOL fCanCancel)
{
// Callers assume that _SaveIfDirty will either return S_OK or S_FALSE
// since this function is designed to sit in a loop until the user gives
// up saving (cancels) or save successfully.
HRESULT hr = S_OK;
if (m_fDirty)
{
CComBSTR bstrMsg, bstrTitle;
if (bstrMsg.LoadString(IDS_SAVEWARNING_MSGBOX) && bstrTitle.LoadString(IDS_PROJNAME))
{
hr = E_FAIL;
while (FAILED(hr))
{
UINT uFlags;
if (fCanCancel)
uFlags = MB_YESNOCANCEL;
else
uFlags = MB_YESNO;
uFlags |= MB_ICONQUESTION | MB_DEFBUTTON1 | MB_APPLMODAL;
m_fPromptingUser = TRUE;
int iResult = MessageBox(bstrMsg, bstrTitle, uFlags);
m_fPromptingUser = FALSE;
if (iResult == IDYES)
{
// if this fails we keep looping.
// if this returns S_OK we succeed
// if this returns S_FALSE we cancel
hr = _SaveAsCmd();
}
else if (iResult == IDCANCEL)
{
hr = S_FALSE;
}
else
{
hr = S_OK;
}
}
}
if (S_OK == hr)
m_fDirty = FALSE;
}
return hr;
}
LRESULT CPreviewWnd::OnClose(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& fHandled)
{
// hack alert- if CONTROL is held down while pressing the close button, do the registry stuff.
// make sure the E accelerator isn't being used (edit verb)!
if (m_fPromptingUser)
{
SetForegroundWindow(m_hWnd);
fHandled = TRUE;
return 0;
}
if (!m_fNoRestore && GetKeyState(VK_CONTROL) & 0x8000)
{
CRegKey Key;
if (ERROR_SUCCESS == Key.Open(HKEY_CURRENT_USER, REGSTR_SHIMGVW))
{
Key.DeleteValue(REGSTR_LOSSYROTATE);
}
if (ERROR_SUCCESS == Key.Open(HKEY_CURRENT_USER, REGSTR_DONTSHOWME))
{
Key.DeleteValue(REGSTR_SAVELESS);
Key.DeleteValue(REGSTR_LOSSYROTATE);
}
CComBSTR bstrMsg, bstrTitle;
if (bstrMsg.LoadString(IDS_RESET_MSGBOX) && bstrTitle.LoadString(IDS_PROJNAME))
{
m_fPromptingUser = TRUE;
MessageBox(bstrMsg, bstrTitle, MB_OK | MB_APPLMODAL);
m_fPromptingUser = FALSE;
}
}
fHandled = FALSE; // let it close
HRESULT hr = _SaveIfDirty(TRUE);
if (hr == S_FALSE) // _SaveIfDirty can only return S_OK and S_FALSE
{
m_fNoRestore = FALSE;
fHandled = TRUE;
}
if (!fHandled)
{
m_fClosed = TRUE;
}
return 0;
}
// we only have to erase the portion not covered by the zoomwnd
// change this code if the toolbar is put back at the top of the window
LRESULT CPreviewWnd::OnEraseBkgnd(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& fHandled)
{
RECT rc;
RECT rcZoomwnd;
m_ctlPreview.GetClientRect(&rcZoomwnd);
GetClientRect(&rc);
rc.top = RECTHEIGHT(rcZoomwnd);
SetBkColor((HDC)wParam, m_ctlPreview.GetBackgroundColor());
ExtTextOut((HDC)wParam, 0, 0, ETO_OPAQUE, &rc, NULL, 0, NULL);
fHandled = TRUE;
return TRUE;
}
LRESULT CPreviewWnd::OnSize(UINT , WPARAM wParam, LPARAM lParam, BOOL&)
{
int x =0, y =0, cx =0, cy =0;
if (lParam == 0)
{
RECT rcClient;
GetClientRect(&rcClient);
cx = RECTWIDTH(rcClient);
cy = RECTHEIGHT(rcClient);
}
else
{
cx = GET_X_LPARAM(lParam);
cy = GET_Y_LPARAM(lParam);
}
if (m_fShowToolbar)
{
SIZE sizeToolbar;
m_ctlToolbar.SendMessage(TB_GETMAXSIZE, 0, (LPARAM)&sizeToolbar);
if (sizeToolbar.cx > cx)
sizeToolbar.cx = cx;
if (SLIDESHOW_MODE != m_dwMode)
{
// Center the toolbar horizontally
LONG cyBottomMargin = (CONTROL_MODE == m_dwMode) ? NEWTOOLBAR_BOTTOMMARGIN_CTRLMODE : NEWTOOLBAR_BOTTOMMARGIN;
LONG xNewToolbar = ( cx - sizeToolbar.cx ) / 2;
LONG yNewToolbar = cy - ( cyBottomMargin + sizeToolbar.cy );
::SetWindowPos(m_ctlToolbar.m_hWnd, NULL, xNewToolbar, yNewToolbar, sizeToolbar.cx, sizeToolbar.cy, SWP_NOZORDER);
// make the preview window shorter so the toolbar is below it
cy -= ( NEWTOOLBAR_TOPMARGIN + sizeToolbar.cy + cyBottomMargin);
}
else
{
// Pin the toolbar to the upper right corner
UINT uFlags = 0;
if (m_fToolbarHidden)
uFlags |= SWP_HIDEWINDOW;
else
uFlags |= SWP_SHOWWINDOW;
::SetWindowPos(m_ctlToolbar.m_hWnd, HWND_TOP, cx-sizeToolbar.cx, 0, sizeToolbar.cx, sizeToolbar.cy, uFlags);
}
}
::SetWindowPos(m_ctlPreview.m_hWnd, NULL, x, y, cx, cy, SWP_NOZORDER);
return 0;
}
BOOL CPreviewWnd::_VerbExists(LPCTSTR pszVerb)
{
// TODO: Create the context menu for the item and check it for the verb
return TRUE;
}
// given a verb lets invoke it for the current file
HRESULT CPreviewWnd::_InvokeVerb(LPCTSTR pszVerb, LPCTSTR szParameters)
{
SHELLEXECUTEINFO sei = {0};
sei.cbSize = sizeof(sei);
sei.fMask = SEE_MASK_INVOKEIDLIST | SEE_MASK_HMONITOR;
sei.hwnd = m_hWnd;
sei.lpVerb = pszVerb;
sei.nShow = SW_SHOW;
sei.lpParameters = szParameters;
sei.hMonitor = MonitorFromWindow(m_hWnd, MONITOR_DEFAULTTONEAREST);
LPITEMIDLIST pidl = NULL;
TCHAR szPath[MAX_PATH];
HRESULT hr = GetCurrentIDList(&pidl);
if (SUCCEEDED(hr))
{
sei.lpIDList = pidl;
}
else if (SUCCEEDED(hr = PathFromImageData(szPath, ARRAYSIZE(szPath))))
{
sei.lpFile = szPath;
}
if (SUCCEEDED(hr))
{
hr = S_OK;
if (!ShellExecuteEx(&sei))
hr = E_FAIL;
}
ILFree(pidl);
return hr;
}
// show the window and activate it
// if the window is minimized, restore it
void RestoreAndActivate(HWND hwnd)
{
if (IsIconic(hwnd))
{
ShowWindow(hwnd, SW_RESTORE);
}
SetForegroundWindow(hwnd);
}
// Handles WM_COMMAND messages sent from the toolbar control
LRESULT CPreviewWnd::OnToolbarCommand(WORD wNotifyCode, WORD wID, HWND hwnd, BOOL& fHandled)
{
// don't use early returns in this function
m_fIgnoreAllNotifies = TRUE;
switch (wID)
{
case ID_ZOOMINCMD:
ZoomIn();
break;
case ID_ZOOMOUTCMD:
ZoomOut();
break;
case ID_SELECTCMD:
if (m_fCanAnnotate)
{
_UpdateButtons(wID);
}
break;
case ID_CROPCMD:
if (m_fCanCrop)
{
m_rectCropping.SetRectEmpty();
_UpdateButtons(wID);
}
break;
case ID_ACTUALSIZECMD:
ActualSize();
break;
case ID_BESTFITCMD:
BestFit();
break;
case ID_PRINTCMD:
_RefreshSelection(FALSE);
_InvokePrintWizard();
break;
case ID_PREVPAGECMD:
case ID_NEXTPAGECMD:
_PrevNextPage(ID_NEXTPAGECMD==wID);
break;
case ID_NEXTIMGCMD:
case ID_PREVIMGCMD:
_RefreshSelection(FALSE);
if (WINDOW_MODE == m_dwMode)
{
HRESULT hr = _SaveIfDirty(TRUE);
if (hr != S_FALSE)
{
_ShowNextSlide(ID_PREVIMGCMD == wID);
}
}
else if (m_punkSite)
{
IFolderView* pfv;
if (SUCCEEDED(IUnknown_QueryService(m_punkSite, SID_DefView, IID_PPV_ARG(IFolderView, &pfv))))
{
int iCurrent, cItems;
if (SUCCEEDED(pfv->ItemCount(SVGIO_ALLVIEW, &cItems)) && (cItems > 1) &&
SUCCEEDED(pfv->GetFocusedItem(&iCurrent)))
{
int iToSelect = iCurrent + ((ID_PREVIMGCMD == wID) ? -1 : 1);
if (iToSelect < 0)
{
iToSelect = cItems-1;
}
else if (iToSelect >= cItems)
{
iToSelect = 0;
}
pfv->SelectItem(iToSelect, SVSI_SELECTIONMARK | SVSI_SELECT
| SVSI_ENSUREVISIBLE | SVSI_DESELECTOTHERS | SVSI_FOCUSED);
}
pfv->Release();
}
}
break;
case ID_FREEHANDCMD:
case ID_HIGHLIGHTCMD:
case ID_LINECMD:
case ID_FRAMECMD:
case ID_RECTCMD:
case ID_TEXTCMD:
case ID_NOTECMD:
if (m_fCanAnnotate)
{
_UpdateButtons(wID);
}
break;
case ID_PROPERTIESCMD:
_UpdateButtons(wID);
_PropertiesCmd();
break;
case ID_SAVEASCMD:
_UpdateButtons(wID);
_SaveAsCmd();
break;
case ID_EDITCMD:
_UpdateButtons(wID);
if (m_fCanAnnotate && m_fAnnotating)
{
_StartEditing();
}
break;
case ID_HELPCMD:
_UpdateButtons(wID);
HtmlHelp(::GetDesktopWindow(), HTMLHELP_FILENAME, HH_DISPLAY_TOPIC, NULL);
break;
case ID_OPENCMD:
_UpdateButtons(wID);
_OpenCmd();
break;
case ID_DELETECMD:
_UpdateButtons(wID);
if (m_fCanAnnotate && m_fAnnotating && DPA_GetPtrCount(m_hdpaSelectedAnnotations) > 0)
{
_RemoveAnnotatingSelection();
}
else
{
_DeleteCurrentSlide();
}
break;
case ID_SLIDESHOWCMD:
if (!m_fFirstTime) // don't try to do this while the namespace walk is ongoing
{
StartSlideShow(NULL);
}
break;
}
m_fIgnoreAllNotifies = FALSE;
return 0;
}
// OnEditCommand
//
// Handles picture editting(rotate/flip/save etc) WM_COMMAND messages
LRESULT CPreviewWnd::OnEditCommand(WORD , WORD wID, HWND , BOOL& )
{
m_fIgnoreAllNotifies = TRUE;
switch (wID)
{
case ID_ROTATE90CMD:
Rotate(90);
break;
case ID_ROTATE270CMD:
Rotate(270);
break;
}
m_fIgnoreAllNotifies = FALSE;
return 0;
}
LRESULT CPreviewWnd::OnPositionCommand(WORD wNotifyCode, WORD wID, HWND hwnd, BOOL& fHandled)
{
if (SLIDESHOW_MODE == m_dwMode)
{
switch (wID)
{
case ID_NUDGELEFTCMD:
OnSlideshowCommand(0, ID_PREVCMD, NULL, fHandled);
break;
case ID_NUDGERIGHTCMD:
OnSlideshowCommand(0, ID_NEXTCMD, NULL, fHandled);
break;
default:
break;
}
}
else
{
if (DPA_GetPtrCount(m_hdpaSelectedAnnotations) == 0)
{
BOOL bDummy;
switch (wID)
{
case ID_MOVELEFTCMD:
m_ctlPreview.OnScroll(WM_HSCROLL, SB_PAGEUP, 0, fHandled);
break;
case ID_MOVERIGHTCMD:
m_ctlPreview.OnScroll(WM_HSCROLL, SB_PAGEDOWN, 0, fHandled);
break;
case ID_MOVEUPCMD:
m_ctlPreview.OnScroll(WM_VSCROLL, SB_PAGEUP, 0, fHandled);
break;
case ID_MOVEDOWNCMD:
m_ctlPreview.OnScroll(WM_VSCROLL, SB_PAGEDOWN, 0, fHandled);
break;
case ID_NUDGELEFTCMD:
if (m_ctlPreview.ScrollBarsPresent())
{
m_ctlPreview.OnScroll(WM_HSCROLL, SB_LINEUP, 0, fHandled);
}
else
{
OnToolbarCommand(0, ID_PREVIMGCMD, m_hWnd, bDummy);
}
break;
case ID_NUDGERIGHTCMD:
if (m_ctlPreview.ScrollBarsPresent())
{
m_ctlPreview.OnScroll(WM_HSCROLL, SB_LINEDOWN, 0, fHandled);
}
else
{
OnToolbarCommand(0, ID_NEXTIMGCMD, m_hWnd, bDummy);
}
break;
case ID_NUDGEUPCMD:
if (m_ctlPreview.ScrollBarsPresent())
{
m_ctlPreview.OnScroll(WM_VSCROLL, SB_LINEUP, 0, fHandled);
}
else
{
OnToolbarCommand(0, ID_PREVIMGCMD, m_hWnd, bDummy);
}
break;
case ID_NUDGEDOWNCMD:
if (m_ctlPreview.ScrollBarsPresent())
{
m_ctlPreview.OnScroll(WM_VSCROLL, SB_LINEDOWN, 0, fHandled);
}
else
{
OnToolbarCommand(0, ID_NEXTIMGCMD, m_hWnd, bDummy);
}
break;
default:
break;
}
}
else
{
CRect rectImage;
m_ctlPreview.GetVisibleImageWindowRect(rectImage);
m_ctlPreview.GetImageFromWindow((LPPOINT)(LPRECT)rectImage, 2);
rectImage.DeflateRect(5, 5);
CSize size(0,0);
switch (wID)
{
case ID_MOVELEFTCMD:
size.cx = -25;
break;
case ID_MOVERIGHTCMD:
size.cx = 25;
break;
case ID_MOVEUPCMD:
size.cy = -25;
break;
case ID_MOVEDOWNCMD:
size.cy = 25;
break;
case ID_NUDGELEFTCMD:
size.cx = -1;
break;
case ID_NUDGERIGHTCMD:
size.cx = 1;
break;
case ID_NUDGEUPCMD:
size.cy = -1;
break;
case ID_NUDGEDOWNCMD:
size.cy = 1;
break;
default:
break;
}
if (size.cx == 0 && size.cy == 0)
return 0;
_UpdateAnnotatingSelection();
CRect rect;
CRect rectNewPos;
BOOL bValidMove = TRUE;
for (int i = 0; i < DPA_GetPtrCount(m_hdpaSelectedAnnotations); i++)
{
CAnnotation* pAnnotation = (CAnnotation*)DPA_GetPtr(m_hdpaSelectedAnnotations, i);
pAnnotation->GetRect(rect);
rect.NormalizeRect();
rect.OffsetRect(size);
if (!rectNewPos.IntersectRect(rectImage, rect))
bValidMove = FALSE;
}
if (!bValidMove)
return 0;
for (int i = 0; i < DPA_GetPtrCount(m_hdpaSelectedAnnotations); i++)
{
CAnnotation* pAnnotation = (CAnnotation*)DPA_GetPtr(m_hdpaSelectedAnnotations, i);
pAnnotation->Move(size);
}
m_fDirty = TRUE;
_UpdateAnnotatingSelection();
}
}
return 0;
}
// call SetNewImage when the entire image has changed, call UpdateImage if it's the same image
// but it needs to be updated
void CPreviewWnd::_SetNewImage(CDecodeTask * pData)
{
// store the new pointer
if (m_pImageData)
{
_SaveIfDirty();
// m_pImageData might be NULL after _SaveAsCmd
ATOMICRELEASE(m_pImageData);
}
m_pImageData = pData;
m_fWarnQuietSave = TRUE; // Reset for each image
m_fWarnNoSave = TRUE;
if (!m_pImageData)
{
StatusUpdate(IDS_LOADFAILED);
return;
}
m_pImageData->AddRef();
// Even if m_hpal is NULL it is still correct, so we always go ahead and set it.
m_ctlPreview.SetPalette(m_hpal);
if (SLIDESHOW_MODE != m_dwMode)
{
// update toolbar state
_SetMultipageCommands();
_SetMultiImagesCommands();
BOOL fCanAnnotate = _CanAnnotate(m_pImageData);
_SetAnnotatingCommands(fCanAnnotate);
BOOL fCanCrop = _CanCrop(m_pImageData);
_SetCroppingCommands(fCanCrop);
_RefreshSelection(TRUE);
_SetEditCommands();
m_fPrintable = _VerbExists(TEXT("print"));
m_ctlToolbar.SendMessage(TB_ENABLEBUTTON, ID_PRINTCMD, MAKELONG(m_fPrintable, 0));
// we need to watch non-TIFFs for changes so we can reload.
// TIFFs are problematic because we allow annotations, and reloading during annotation
// would suck
if (CONTROL_MODE != m_dwMode)
{
_RegisterForChangeNotify(TRUE);
}
}
// notify our child
m_ctlPreview.SetImageData(m_pImageData, TRUE);
m_ctlToolbar.SendMessage(TB_ENABLEBUTTON, ID_ACTUALSIZECMD, MAKELONG(TRUE, 0));
m_ctlToolbar.SendMessage(TB_ENABLEBUTTON, ID_BESTFITCMD, MAKELONG(FALSE, 0));
// update our toolbar
BOOL fHandled;
OnSize(0x0, 0, 0, fHandled);
}
// for refreshing the current m_pImageData because it has changed in some
// way, i.e. it has advanced to the next frame, the next page, or was edited.
void CPreviewWnd::_UpdateImage()
{
_RefreshSelection(TRUE);
m_ctlPreview.SetImageData(m_pImageData, FALSE);
}
// Handles slidwshow (pause/resume, next/previous etc) WM_COMMAND messages
void CPreviewWnd::TogglePlayState()
{
if (!m_fPaused)
{
KillTimer(TIMER_SLIDESHOW);
}
else
{
SetTimer(TIMER_SLIDESHOW, m_uTimeout);
}
m_fPaused = !m_fPaused;
WPARAM wpCheck, wpUncheck;
if (m_fPaused)
{
wpCheck = ID_PAUSECMD;
wpUncheck = ID_PLAYCMD;
}
else
{
wpCheck = ID_PLAYCMD;
wpUncheck = ID_PAUSECMD;
}
m_ctlToolbar.SendMessage(TB_SETSTATE, wpCheck, TBSTATE_ENABLED | TBSTATE_CHECKED);
m_ctlToolbar.SendMessage(TB_SETSTATE, wpUncheck, TBSTATE_ENABLED);
}
LRESULT CPreviewWnd::OnMenuMessage(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& fHandled)
{
if (_pcm3)
_pcm3->HandleMenuMsg(uMsg, wParam, lParam);
return 0;
}
LRESULT CPreviewWnd::OnAppCommand(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& fHandled)
{
UINT cmd = GET_APPCOMMAND_LPARAM(lParam);
DWORD dwKeys = GET_KEYSTATE_LPARAM(lParam);
switch (cmd)
{
case APPCOMMAND_BROWSER_FORWARD:
if (SLIDESHOW_MODE == m_dwMode)
OnSlideshowCommand(0, ID_NEXTCMD, NULL, fHandled);
else
{
if ((dwKeys & MK_CONTROL) || (m_pImageData && !m_pImageData->IsMultipage()))
OnToolbarCommand(0, ID_NEXTIMGCMD, NULL, fHandled);
else
NextPage();
}
break;
case APPCOMMAND_BROWSER_BACKWARD:
if (SLIDESHOW_MODE == m_dwMode)
OnSlideshowCommand(0, ID_PREVCMD, NULL, fHandled);
else
{
if ((dwKeys & MK_CONTROL) || (m_pImageData && !m_pImageData->IsMultipage()))
OnToolbarCommand(0, ID_PREVIMGCMD, NULL, fHandled);
else
PreviousPage();
}
break;
default:
fHandled = FALSE;
}
return 0;
}
LRESULT CPreviewWnd::OnSlideshowCommand(WORD wNotifyCode, WORD wID, HWND hwnd, BOOL& fHandled)
{
switch (wID)
{
case ID_PLAYCMD:
m_iSSToolbarSelect = 0;
if (m_fPaused)
{
m_fGoBack = FALSE;
TogglePlayState();
_ShowNextSlide(m_fGoBack);
}
fHandled = TRUE;
break;
case ID_PAUSECMD:
m_iSSToolbarSelect = 1;
if (!m_fPaused)
{
TogglePlayState();
}
fHandled = TRUE;
break;
case ID_NEXTCMD:
case ID_PREVCMD:
if (wID == ID_PREVCMD)
{
m_iSSToolbarSelect = 3;
m_fGoBack = TRUE;
}
else
{
m_iSSToolbarSelect = 4;
m_fGoBack = FALSE;
}
_ShowNextSlide(m_fGoBack);
fHandled = TRUE;
break;
case ID_CLOSECMD:
m_iSSToolbarSelect = 6;
_CloseSlideshowWindow();
break;
}
return 0;
}
BOOL CPreviewWnd::CreateToolbar()
{
// ensure that the common controls are initialized
INITCOMMONCONTROLSEX icc;
icc.dwSize = sizeof(icc);
icc.dwICC = ICC_BAR_CLASSES;
InitCommonControlsEx(&icc);
return (SLIDESHOW_MODE == m_dwMode) ? _CreateSlideshowToolbar() : _CreateViewerToolbar();
}
static const TBBUTTON c_tbSlideShow[] =
{
// override default toolbar width for separators; iBitmap member of
// TBBUTTON struct is a union of bitmap index & separator width
{ 0, ID_PLAYCMD, TBSTATE_ENABLED | TBSTATE_CHECKED, TBSTYLE_CHECKGROUP, {0,0}, 0, 0},
{ 1, ID_PAUSECMD, TBSTATE_ENABLED, TBSTYLE_CHECKGROUP, {0,0}, 0, 0},
{ 0, 0, TBSTATE_ENABLED, TBSTYLE_SEP, {0,0}, 0, 0},
{ 2, ID_PREVCMD, TBSTATE_ENABLED, TBSTYLE_BUTTON, {0,0}, 0, 0},
{ 3, ID_NEXTCMD, TBSTATE_ENABLED, TBSTYLE_BUTTON, {0,0}, 0, 0},
#if 0
{ 0, 0, TBSTATE_ENABLED, TBSTYLE_SEP, {0,0}, 0, 0},
{ 5, ID_DELETECMD, TBSTATE_ENABLED, TBSTYLE_BUTTON, {0,0}, 0, 0},
#endif
{ 0, 0, TBSTATE_ENABLED, TBSTYLE_SEP, {0,0}, 0, 0},
{ 4, ID_CLOSECMD, TBSTATE_ENABLED, TBSTYLE_BUTTON, {0,0}, 0, 0},
};
BOOL CPreviewWnd::_CreateSlideshowToolbar()
{
DWORD dwStyle = WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS |
CCS_NODIVIDER | CCS_NORESIZE |
TBSTYLE_LIST | TBSTYLE_FLAT | TBSTYLE_TOOLTIPS;
HWND hwndTB = CreateWindowEx(WS_EX_TOOLWINDOW, TOOLBARCLASSNAME, NULL, dwStyle, 0, 0, 0, 0,
m_hWnd, NULL, _Module.GetModuleInstance(), NULL);
_InitializeToolbar(hwndTB, IDB_SLIDESHOWTOOLBAR, IDB_SLIDESHOWTOOLBAR_HOT, IDB_SLIDESHOWTOOLBARHIGH, IDB_SLIDESHOWTOOLBARHIGH_HOT);
TBBUTTON tbSlideShow[ARRAYSIZE(c_tbSlideShow)];
memcpy(tbSlideShow, c_tbSlideShow, sizeof(c_tbSlideShow));
// Add the buttons, and then set the minimum and maximum button widths.
::SendMessage(hwndTB, TB_ADDBUTTONS, (UINT)ARRAYSIZE(c_tbSlideShow), (LPARAM)tbSlideShow);
LRESULT dwSize = ::SendMessage(hwndTB, TB_GETBUTTONSIZE, 0, 0);
SIZE size = {0, HIWORD(dwSize)};
::SendMessage(hwndTB, TB_GETIDEALSIZE, 0, (LPARAM)&size);
RECT rcClient;
RECT rcToolbar = {0, 0, size.cx, size.cy};
GetClientRect(&rcClient);
AdjustWindowRectEx(&rcToolbar, dwStyle, FALSE, WS_EX_TOOLWINDOW);
::SetWindowPos(hwndTB, HWND_TOP, RECTWIDTH(rcClient)-RECTWIDTH(rcToolbar), 0,
RECTWIDTH(rcToolbar), RECTHEIGHT(rcToolbar), 0);
//> REVIEW This is a feature that CyraR would like to get into Whistler, but it doesn't seem to work. I will investigate more after Beta1
// LONG lStyle = ::GetWindowLong(hwndTB, GWL_EXSTYLE);
// ::SetWindowLong(hwndTB, GWL_EXSTYLE, lStyle | WS_EX_LAYERED);
// if (::SetLayeredWindowAttributes(hwndTB, 0, 0, 0) == 0)
// {
// void *lpMsgBuf;
// ::FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
// NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &lpMsgBuf, 0, NULL);
//
// MessageBox((LPCTSTR)lpMsgBuf, L"Error", MB_OK | MB_ICONINFORMATION);
// ::LocalFree(lpMsgBuf);
// }
m_ctlToolbar.SubclassWindow(hwndTB);
ShowSSToolbar(FALSE, TRUE);
return (NULL != hwndTB);
}
enum EViewerToolbarButtons
{
PREVIMGPOS = 0,
NEXTIMGPOS,
VIEWSEPPOS, // seperator
BESTFITPOS,
ACTUALSIZEPOS,
SLIDESHOWPOS,
IMAGECMDSEPPOS, // seperator
ZOOMINPOS,
ZOOMEOUTPOS,
SELECTPOS,
CROPPOS,
ROTATESEPPOS, // seperator
ROTATE90POS,
ROTATE270POS,
// these are all TIFF related
PAGESEPPOS,
PREVPAGEPOS,
PAGELISTPOS,
NEXTPAGEPOS,
ANNOTATEPOS,
FREEHANDPOS,
HIGLIGHTPOS,
LINEPOS,
FRAMEPOS,
RECTPOS,
TEXTPOS,
NOTEPOS,
PRINTSEPPOS,
DELETEPOS,
PRINTPOS,
PROPERTIESPOS,
SAVEASPOS,
OPENPOS,
HELPSEPPOS,
HELPPOS,
MAXPOS,
};
static const TBBUTTON c_tbViewer[] =
{
// override default toolbar width for separators; iBitmap member of
// TBBUTTON struct is a union of bitmap index & separator width
{ 0, ID_PREVIMGCMD, TBSTATE_ENABLED, TBSTYLE_BUTTON, {0,0}, 0, -1},
{ 1, ID_NEXTIMGCMD, TBSTATE_ENABLED, TBSTYLE_BUTTON, {0,0}, 0, -1},
{ 0, ID_VIEWCMDSEP, TBSTATE_ENABLED, TBSTYLE_SEP, {0,0}, 0, -1},
{ 5, ID_BESTFITCMD, TBSTATE_ENABLED, TBSTYLE_BUTTON, {0,0}, 0, -1},
{ 6, ID_ACTUALSIZECMD, TBSTATE_ENABLED, TBSTYLE_BUTTON, {0,0}, 0, -1},
{ 8, ID_SLIDESHOWCMD, TBSTATE_ENABLED, TBSTYLE_BUTTON, {0,0}, 0, -1},
{ 0, 0, TBSTATE_ENABLED, TBSTYLE_SEP, {0,0}, 0, -1},
{ 2, ID_ZOOMINCMD, TBSTATE_ENABLED, TBSTYLE_BUTTON, {0,0}, 0, -1},
{ 3, ID_ZOOMOUTCMD, TBSTATE_ENABLED, TBSTYLE_BUTTON, {0,0}, 0, -1},
{ 4, ID_SELECTCMD, TBSTATE_HIDDEN, TBSTYLE_BUTTON, {0,0}, 0, -1},
{ 23, ID_CROPCMD, TBSTATE_HIDDEN, TBSTYLE_BUTTON, {0,0}, 0, -1},
{ 0, ID_ROTATESEP, TBSTATE_ENABLED, TBSTYLE_SEP, {0,0}, 0, -1},
{ 12, ID_ROTATE90CMD, TBSTATE_ENABLED, TBSTYLE_BUTTON, {0,0}, 0, -1},
{ 11, ID_ROTATE270CMD, TBSTATE_ENABLED, TBSTYLE_BUTTON, {0,0}, 0, -1},
{ 0, ID_PAGECMDSEP, TBSTATE_HIDDEN, TBSTYLE_SEP, {0,0}, 0, -1}, //tiff
{ 9, ID_PREVPAGECMD, TBSTATE_HIDDEN, TBSTYLE_BUTTON, {0,0}, 0, -1}, //tiff
{ I_IMAGENONE, ID_PAGELIST, TBSTATE_HIDDEN, BTNS_WHOLEDROPDOWN | BTNS_SHOWTEXT | BTNS_AUTOSIZE, {0,0}, 0, -1},//tiff
{ 10, ID_NEXTPAGECMD, TBSTATE_HIDDEN, TBSTYLE_BUTTON, {0,0}, 0, -1}, //tiff
{ 0, ID_ANNOTATESEP, TBSTATE_HIDDEN, TBSTYLE_SEP, {0,0}, 0, -1}, //tiff
{ 13, ID_FREEHANDCMD, TBSTATE_HIDDEN, TBSTYLE_BUTTON, {0,0}, 0, -1}, //tiff
{ 14, ID_HIGHLIGHTCMD, TBSTATE_HIDDEN, TBSTYLE_BUTTON, {0,0}, 0, -1}, //tiff
{ 15, ID_LINECMD, TBSTATE_HIDDEN, TBSTYLE_BUTTON, {0,0}, 0, -1}, //tiff
{ 16, ID_FRAMECMD, TBSTATE_HIDDEN, TBSTYLE_BUTTON, {0,0}, 0, -1}, //tiff
{ 17, ID_RECTCMD, TBSTATE_HIDDEN, TBSTYLE_BUTTON, {0,0}, 0, -1}, //tiff
{ 18, ID_TEXTCMD, TBSTATE_HIDDEN, TBSTYLE_BUTTON, {0,0}, 0, -1}, //tiff
{ 19, ID_NOTECMD, TBSTATE_HIDDEN, TBSTYLE_BUTTON, {0,0}, 0, -1}, //tiff
{ 0, 0, TBSTATE_ENABLED, TBSTYLE_SEP, {0,0}, 0, -1},
{ 25, ID_DELETECMD, TBSTATE_ENABLED, TBSTYLE_BUTTON, {0,0}, 0, -1},
{ 20, ID_PRINTCMD, TBSTATE_ENABLED, TBSTYLE_BUTTON, {0,0}, 0, -1},
{ 21, ID_PROPERTIESCMD, TBSTATE_HIDDEN, TBSTYLE_BUTTON, {0,0}, 0, -1},
{ 22, ID_SAVEASCMD, TBSTATE_ENABLED, TBSTYLE_BUTTON, {0,0}, 0, -1},
{ 26, ID_OPENCMD, TBSTATE_ENABLED, TBSTYLE_BUTTON, {0,0}, 0, -1},
{ 0, 0, TBSTATE_ENABLED, TBSTYLE_SEP, {0,0}, 0, -1},
{ 24, ID_HELPCMD, TBSTATE_ENABLED, TBSTYLE_BUTTON, {0,0}, 0, -1},
};
void CPreviewWnd::_InitializeViewerToolbarButtons(HWND hwndToolbar, const TBBUTTON c_tbbuttons[], size_t c_nButtons, TBBUTTON tbbuttons[], size_t nButtons)
{
ASSERT(c_nButtons == nButtons); // Sanity check.
ASSERT(c_nButtons <= 100); // Sanity check.
// Determine if running RTL mirrored and initialize toolbar accordingly.
if (!m_bRTLMirrored)
{
//
// Init LTR.
//
memcpy(tbbuttons, c_tbbuttons, c_nButtons * sizeof(TBBUTTON));
}
else
{
//
// Init RTL.
//
// Toolbar window inherits RTL style from parent hwnd, but we don't
// want full-blown RTL. We do want the icons to have their positions
// reversed in the toolbar, but we don't want the button bitmaps to
// get blitted backwards. So we turn of RTL for the toolbar hwnd
// and do a manual reorder of the buttons in RTL fashion.
// Remove RTL style from toolbar hwnd.
DWORD dwStyle = ::GetWindowLong(hwndToolbar, GWL_EXSTYLE);
DWORD dwNewStyle = (dwStyle & ~WS_EX_LAYOUTRTL);
ASSERT(dwStyle != dwNewStyle); // Sanity check.
::SetWindowLong(hwndToolbar, GWL_EXSTYLE, dwNewStyle);
// Reverse toolbar button order.
size_t iFrom = nButtons - 1;
size_t iTo = 0;
while (iTo < iFrom)
{
memcpy(&tbbuttons[iTo], &c_tbbuttons[iFrom], sizeof(TBBUTTON));
memcpy(&tbbuttons[iFrom], &c_tbbuttons[iTo], sizeof(TBBUTTON));
iFrom--;
iTo++;
}
if (iTo == iFrom)
{
memcpy(&tbbuttons[iTo], &c_tbbuttons[iFrom], sizeof(TBBUTTON));
}
}
}
inline UINT CPreviewWnd::_IndexOfViewerToolbarButton(EViewerToolbarButtons eButton)
{
ASSERT(eButton > 0);
if (!m_bRTLMirrored)
{
return eButton;
}
else
{
return MAXPOS - eButton - 1;
}
}
BOOL CPreviewWnd::_CreateViewerToolbar()
{
DWORD dwStyle = WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS |
CCS_NODIVIDER | CCS_NORESIZE |
TBSTYLE_LIST | TBSTYLE_FLAT | TBSTYLE_TOOLTIPS;
HWND hwndTB = CreateWindowEx(0, TOOLBARCLASSNAME, NULL, dwStyle, 0, 0, 0, 0,
m_hWnd, NULL, _Module.GetModuleInstance(), NULL);
_InitializeToolbar(hwndTB, IDB_TOOLBAR, IDB_TOOLBAR_HOT, IDB_TOOLBARHIGH, IDB_TOOLBARHIGH_HOT);
TBBUTTON tbbuttons[ARRAYSIZE(c_tbViewer)];
_InitializeViewerToolbarButtons(hwndTB, c_tbViewer, ARRAYSIZE(c_tbViewer), tbbuttons, ARRAYSIZE(tbbuttons));
if (CONTROL_MODE == m_dwMode)
{
ASSERT(ID_BESTFITCMD==tbbuttons[_IndexOfViewerToolbarButton(BESTFITPOS)].idCommand);
tbbuttons[_IndexOfViewerToolbarButton(BESTFITPOS)].fsState = TBSTATE_HIDDEN;
ASSERT(ID_ACTUALSIZECMD==tbbuttons[_IndexOfViewerToolbarButton(ACTUALSIZEPOS)].idCommand);
tbbuttons[_IndexOfViewerToolbarButton(ACTUALSIZEPOS)].fsState = TBSTATE_HIDDEN;
ASSERT(ID_SLIDESHOWCMD==tbbuttons[_IndexOfViewerToolbarButton(SLIDESHOWPOS)].idCommand);
tbbuttons[_IndexOfViewerToolbarButton(SLIDESHOWPOS)].fsState = TBSTATE_HIDDEN;
ASSERT(ID_ZOOMINCMD==tbbuttons[_IndexOfViewerToolbarButton(ZOOMINPOS)].idCommand);
tbbuttons[_IndexOfViewerToolbarButton(ZOOMINPOS)].fsState = TBSTATE_HIDDEN;
ASSERT(ID_ZOOMOUTCMD==tbbuttons[_IndexOfViewerToolbarButton(ZOOMEOUTPOS)].idCommand);
tbbuttons[_IndexOfViewerToolbarButton(ZOOMEOUTPOS)].fsState = TBSTATE_HIDDEN;
ASSERT(ID_SAVEASCMD==tbbuttons[_IndexOfViewerToolbarButton(SAVEASPOS)].idCommand);
tbbuttons[_IndexOfViewerToolbarButton(SAVEASPOS)].fsState = TBSTATE_HIDDEN;
ASSERT(ID_DELETECMD==tbbuttons[_IndexOfViewerToolbarButton(DELETEPOS)].idCommand);
tbbuttons[_IndexOfViewerToolbarButton(DELETEPOS)].fsState = TBSTATE_HIDDEN;
ASSERT(ID_OPENCMD==tbbuttons[_IndexOfViewerToolbarButton(OPENPOS)].idCommand);
tbbuttons[_IndexOfViewerToolbarButton(OPENPOS)].fsState = TBSTATE_HIDDEN;
ASSERT(ID_HELPCMD==tbbuttons[_IndexOfViewerToolbarButton(HELPPOS)].idCommand);
tbbuttons[_IndexOfViewerToolbarButton(HELPPOS)].fsState = TBSTATE_HIDDEN;
// remove a few seperators too:
tbbuttons[_IndexOfViewerToolbarButton(VIEWSEPPOS)].fsState = TBSTATE_HIDDEN;
tbbuttons[_IndexOfViewerToolbarButton(IMAGECMDSEPPOS)].fsState = TBSTATE_HIDDEN;
tbbuttons[_IndexOfViewerToolbarButton(PRINTSEPPOS)].fsState = TBSTATE_HIDDEN;
tbbuttons[_IndexOfViewerToolbarButton(HELPSEPPOS)].fsState = TBSTATE_HIDDEN;
}
if (m_fHidePrintBtn)
{
ASSERT(ID_PRINTCMD==tbbuttons[_IndexOfViewerToolbarButton(PRINTPOS)].idCommand);
tbbuttons[_IndexOfViewerToolbarButton(PRINTPOS)].fsState = TBSTATE_HIDDEN;
}
if (m_fDisableEdit)
{
ASSERT(ID_ROTATESEP == tbbuttons[_IndexOfViewerToolbarButton(ROTATESEPPOS)].idCommand);
tbbuttons[_IndexOfViewerToolbarButton(ROTATESEPPOS)].fsState = TBSTATE_HIDDEN;
ASSERT(ID_ROTATE90CMD == tbbuttons[_IndexOfViewerToolbarButton(ROTATE90POS)].idCommand);
tbbuttons[_IndexOfViewerToolbarButton(ROTATE90POS)].fsState = TBSTATE_HIDDEN;
ASSERT(ID_ROTATE270CMD == tbbuttons[_IndexOfViewerToolbarButton(ROTATE270POS)].idCommand);
tbbuttons[_IndexOfViewerToolbarButton(ROTATE270POS)].fsState = TBSTATE_HIDDEN;
}
if (m_bRTLMirrored)
{
UINT uTmp = tbbuttons[_IndexOfViewerToolbarButton(PREVIMGPOS)].iBitmap;
tbbuttons[_IndexOfViewerToolbarButton(PREVIMGPOS)].iBitmap = tbbuttons[_IndexOfViewerToolbarButton(NEXTIMGPOS)].iBitmap;
tbbuttons[_IndexOfViewerToolbarButton(NEXTIMGPOS)].iBitmap = uTmp;
uTmp = tbbuttons[_IndexOfViewerToolbarButton(PREVPAGEPOS)].iBitmap;
tbbuttons[_IndexOfViewerToolbarButton(PREVPAGEPOS)].iBitmap = tbbuttons[_IndexOfViewerToolbarButton(NEXTPAGEPOS)].iBitmap;
tbbuttons[_IndexOfViewerToolbarButton(NEXTPAGEPOS)].iBitmap = uTmp;
}
// Add the buttons, and then set the minimum and maximum button widths.
::SendMessage(hwndTB, TB_ADDBUTTONS, ARRAYSIZE(tbbuttons), (LPARAM)tbbuttons);
// we just created the toolbar so we are now in the hidden state of the multipage buttons.
m_dwMultiPageMode = MPCMD_HIDDEN;
m_fCanAnnotate = FALSE;
m_fCanCrop = FALSE;
m_ctlToolbar.SubclassWindow(hwndTB);
return (NULL != hwndTB);
}
void CPreviewWnd::_InitializeToolbar(HWND hwndTB, int idLow, int idLowHot, int idHigh, int idHighHot)
{
int cxBitmap = 16, cyBitmap = 16;
::SendMessage(hwndTB, CCM_SETVERSION, COMCTL32_VERSION, 0);
::SendMessage (hwndTB, TB_SETEXTENDEDSTYLE, 0, TBSTYLE_EX_HIDECLIPPEDBUTTONS | TBSTYLE_EX_MIXEDBUTTONS | TBSTYLE_EX_DOUBLEBUFFER);
// Sets the size of the TBBUTTON structure.
::SendMessage(hwndTB, TB_BUTTONSTRUCTSIZE, sizeof(TBBUTTON), 0);
// Set the maximum number of text rows and bitmap size.
::SendMessage(hwndTB, TB_SETMAXTEXTROWS, 1, 0);
int nDepth = SHGetCurColorRes();
HIMAGELIST himl = ImageList_LoadImage(_Module.GetModuleInstance(),
(nDepth > 8) ? MAKEINTRESOURCE(idHigh) : MAKEINTRESOURCE(idLow),
cxBitmap, 0, RGB(0, 255, 0), IMAGE_BITMAP,
(nDepth > 8) ? LR_CREATEDIBSECTION : LR_DEFAULTCOLOR);
::SendMessage(hwndTB, TB_SETIMAGELIST, 0, (LPARAM)himl);
HIMAGELIST himlHot = ImageList_LoadImage(_Module.GetModuleInstance(),
(nDepth > 8) ? MAKEINTRESOURCE(idHighHot) : MAKEINTRESOURCE(idLowHot),
cxBitmap, 0, RGB(0, 255, 0), IMAGE_BITMAP,
(nDepth > 8) ? LR_CREATEDIBSECTION : LR_DEFAULTCOLOR);
::SendMessage(hwndTB, TB_SETHOTIMAGELIST, 0, (LPARAM)himlHot);
}
LRESULT CPreviewWnd::OnPrintClient(UINT , WPARAM wParam, LPARAM lParam, BOOL&)
{
COLORREF bgClr = m_ctlPreview.GetBackgroundColor();
RECT rcFill;
GetClientRect(&rcFill);
SHFillRectClr((HDC)wParam, &rcFill, bgClr);
return TRUE;
}
LRESULT CPreviewWnd::OnNeedText(int , LPNMHDR pnmh, BOOL&)
{
TOOLTIPTEXT *pTTT = (TOOLTIPTEXT *)pnmh;
// tooltip text messages have the same string ID as the control ID
pTTT->lpszText = MAKEINTRESOURCE(pTTT->hdr.idFrom);
pTTT->hinst = _Module.GetModuleInstance();
// Except in Control Mode due to a DUI Web View bug that's too hard to fix for Whistler...
if (CONTROL_MODE == m_dwMode)
{
// keyboard accelerators are broken, so swap IDs around for the few that display them
static const struct {
UINT idCommand;
UINT idsNewName;
} map[] = {
{ ID_PRINTCMD, IDS_PRINTCMD },
{ ID_ROTATE90CMD, IDS_ROTATE90CMD },
{ ID_ROTATE270CMD, IDS_ROTATE270CMD }};
for (int i = 0 ; i < ARRAYSIZE(map) ; i++)
{
if (map[i].idCommand == pTTT->hdr.idFrom)
{
pTTT->lpszText = MAKEINTRESOURCE(map[i].idsNewName);
break;
}
}
}
return TRUE;
}
LRESULT CPreviewWnd::OnDropDown(int id, LPNMHDR pnmh, BOOL&)
{
LPNMTOOLBAR pnmTB = (LPNMTOOLBAR)pnmh;
switch (pnmTB->iItem)
{
case ID_PAGELIST:
_DropDownPageList (pnmTB);
break;
default:
return TRUE;
}
return FALSE;
}
void CPreviewWnd::_DropDownPageList(LPNMTOOLBAR pnmTB)
{
HMENU hmenuPopup = CreatePopupMenu();
if (hmenuPopup)
{
for (DWORD i = 1; i <= m_pImageData->_cImages; i++)
{
TCHAR szBuffer[10];
wnsprintf(szBuffer, ARRAYSIZE(szBuffer), TEXT("%d"), i);
MENUITEMINFO mii = {0};
mii.cbSize = sizeof(mii);
mii.fMask = MIIM_STRING | MIIM_ID;
mii.wID = i;
mii.dwTypeData = szBuffer;
InsertMenuItem (hmenuPopup, i-1, TRUE, &mii);
}
RECT rc;
::SendMessage(pnmTB->hdr.hwndFrom, TB_GETRECT, (WPARAM)pnmTB->iItem, (LPARAM)&rc);
::MapWindowPoints(pnmTB->hdr.hwndFrom, HWND_DESKTOP, (LPPOINT)&rc, 2);
TPMPARAMS tpm = { 0};
tpm.cbSize = sizeof(TPMPARAMS);
tpm.rcExclude = rc;
BOOL bRet = ::TrackPopupMenuEx(hmenuPopup,
TPM_RETURNCMD | TPM_LEFTALIGN | TPM_LEFTBUTTON | TPM_VERTICAL | TPM_NONOTIFY,
rc.left, rc.bottom,
m_hWnd, &tpm);
if (bRet)
{
if (m_fDirty)
{
m_ctlPreview.CommitAnnotations();
}
m_pImageData->SelectPage((LONG)bRet-1);
_UpdateImage();
_SetMultipageCommands();
}
DestroyMenu(hmenuPopup);
}
}
void CPreviewWnd::SetNotify(CEvents * pEvents)
{
m_pEvents = pEvents;
}
void CPreviewWnd::SetPalette(HPALETTE hpal)
{
m_hpal = hpal;
}
BOOL CPreviewWnd::GetPrintable()
{
return m_fPrintable;
}
LRESULT CPreviewWnd::OnWheelTurn(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL&)
{
// REVIEW: Shouldn't this just be translated into a command?
// this message is ALWAYS forwarded to the zoom window
m_ctlPreview.SendMessage(uMsg, wParam, lParam);
// Since we know that the mouse wheel will either ZoomIn or ZoomOut lets update the buttons if we are in Window Mode.
if (WINDOW_MODE == m_dwMode)
{
m_ctlToolbar.SendMessage(TB_ENABLEBUTTON, ID_ACTUALSIZECMD, MAKELONG(TRUE, 0));
m_ctlToolbar.SendMessage(TB_ENABLEBUTTON, ID_BESTFITCMD, MAKELONG(!m_ctlPreview.IsBestFit(), 0));
}
return 0;
}
BOOL CPreviewWnd::OnNonSlideShowTab()
{
BOOL fHandled = FALSE;
if ((SLIDESHOW_MODE != m_dwMode) && m_fShowToolbar)
{
if (GetFocus() != m_ctlToolbar.m_hWnd)
{
m_ctlToolbar.SetFocus();
m_ctlToolbar.SetActiveWindow();
m_ctlToolbar.SendMessage(TB_SETHOTITEM, 0, 0);
m_iSSToolbarSelect = 0;
fHandled = TRUE;
}
}
return fHandled;
}
// Forwards WM_KEYUP and WM_KEYDOWN events to the zoom window but only if they are keys
// that the zoom window cares about.
// Activates the slideshow toolbar if needed
LRESULT CPreviewWnd::OnKeyEvent(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& fHandled)
{
MSG msg;
msg.hwnd = m_hWnd;
msg.message = uMsg;
msg.wParam = wParam;
msg.lParam = lParam;
GetCursorPos (&msg.pt);
fHandled = FALSE;
if (SLIDESHOW_MODE == m_dwMode)
{
if (WM_KEYDOWN == uMsg)
{
switch (wParam)
{
case VK_TAB:
OnSlideshowCommand(0, ID_PAUSECMD, NULL, fHandled);
ShowSSToolbar(TRUE);
KillTimer(TIMER_TOOLBAR);
m_ctlToolbar.SetFocus();
m_ctlToolbar.SendMessage(TB_SETHOTITEM, 0, 0);
m_iSSToolbarSelect = 0;
fHandled = TRUE;
break;
case VK_SPACE:
ShowSSToolbar(!m_fPaused); // if we are unpausing, hide the toolbar if it's shown. if we are pausing, show the toolbar
OnSlideshowCommand(0, m_fPaused ? ID_PLAYCMD : ID_PAUSECMD, NULL, fHandled);
break;
case VK_PRIOR: // PAGEUP
case VK_UP:
case VK_LEFT:
case VK_BACK: // BACKSPACE
OnSlideshowCommand(0, ID_PREVCMD, NULL, fHandled);
break;
case VK_NEXT: // PAGEDOWN
case VK_RIGHT:
case VK_DOWN:
case VK_RETURN: // ENTER
OnSlideshowCommand(0, ID_NEXTCMD, NULL, fHandled);
break;
case VK_DELETE:
_DeleteCurrentSlide();
fHandled = TRUE;
break;
case 'K':
if (0x8000 & GetKeyState(VK_CONTROL))
{
OnSlideshowCommand(0, ID_PAUSECMD, NULL, fHandled);
Rotate(90);
fHandled = TRUE;
}
break;
case 'L':
if (0x8000 & GetKeyState(VK_CONTROL))
{
OnSlideshowCommand(0, ID_PAUSECMD, NULL, fHandled);
Rotate(270);
fHandled = TRUE;
}
break;
case VK_ESCAPE:
PostMessage(m_fExitApp ? WM_QUIT : WM_CLOSE, 0, 0);
fHandled = TRUE;
break;
}
}
}
else if (!TranslateAccelerator(&msg)) // Only translate accelerators in the non-slideshow case
// Slideshow keys are handled explicitly above
{
switch (wParam)
{
case VK_SHIFT:
case VK_CONTROL:
case VK_PRIOR:
case VK_NEXT:
case VK_HOME:
case VK_END:
// these are forwarded to the zoom window
m_ctlPreview.SendMessage(uMsg, wParam, lParam);
break;
case VK_TAB:
fHandled = OnNonSlideShowTab();
break;
case VK_ESCAPE:
m_ctlPreview.SetMode(CZoomWnd::MODE_NOACTION);
_UpdateButtons(NOBUTTON);
fHandled = TRUE;
break;
}
}
return 0;
}
// OnTBKeyEvent
//
LRESULT CPreviewWnd::OnTBKeyEvent(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& fHandled)
{
fHandled = FALSE;
if (SLIDESHOW_MODE == m_dwMode && m_fToolbarHidden)
{
ShowSSToolbar(TRUE);
m_ctlToolbar.SetFocus();
m_ctlToolbar.SendMessage(TB_SETHOTITEM, 0, 0);
m_iSSToolbarSelect = 0;
fHandled = TRUE;
}
else
{
switch (wParam)
{
case VK_ESCAPE:
if (WM_KEYDOWN == uMsg)
{
if (SLIDESHOW_MODE == m_dwMode)
{
PostMessage(m_fExitApp ? WM_QUIT : WM_CLOSE, 0, 0);
}
else
{
m_ctlPreview.SetMode(CZoomWnd::MODE_NOACTION);
_UpdateButtons(NOBUTTON);
SetFocus();
SetActiveWindow();
}
}
break;
case VK_SHIFT:
case VK_CONTROL:
case VK_PRIOR:
case VK_NEXT:
case VK_HOME:
case VK_END:
// these are forwarded to the zoom window
m_ctlPreview.SendMessage(uMsg, wParam, lParam);
break;
case VK_LEFT:
case VK_RIGHT:
if (WM_KEYDOWN == uMsg)
{
int iSel = (int)m_ctlToolbar.SendMessage(TB_GETHOTITEM, 0, 0);
int iSize = (int)m_ctlToolbar.SendMessage(TB_BUTTONCOUNT, 0, 0);
int iStepSize = (wParam == VK_RIGHT) ? 1 : iSize - 1; // ((pos + (size - 1)) % size) == left by 1
if (iSel != -1)
{
m_iSSToolbarSelect = iSel;
}
TBBUTTON tb = {0};
do
{
m_iSSToolbarSelect = (m_iSSToolbarSelect + iStepSize) % iSize;
m_ctlToolbar.SendMessage(TB_GETBUTTON, m_iSSToolbarSelect, (LPARAM)&tb);
}
while ((tb.fsStyle & TBSTYLE_SEP) || (tb.fsState & TBSTATE_HIDDEN) || !(tb.fsState & TBSTATE_ENABLED)); // don't stop on the separators
m_ctlToolbar.SendMessage(TB_SETHOTITEM, m_iSSToolbarSelect, 0);
fHandled = TRUE;
}
break;
case VK_RETURN:
case VK_SPACE:
if ((WM_KEYDOWN == uMsg) && (SLIDESHOW_MODE == m_dwMode))
{
// to "press" the button, get its command id and sendmessage on it
// TB_PRESSBUTTON doesn't work here, don't know why.
// m_ctlToolbar.SendMessage(TB_PRESSBUTTON, m_iSSToolbarSelect, MAKELONG(TRUE, 0));
TBBUTTON tbbutton;
if (m_ctlToolbar.SendMessage(TB_GETBUTTON, m_iSSToolbarSelect, (LPARAM)&tbbutton))
{
OnSlideshowCommand(0, (WORD)tbbutton.idCommand, NULL, fHandled);
}
fHandled = TRUE;
}
break;
case VK_TAB:
if ((WM_KEYDOWN == uMsg) && (CONTROL_MODE != m_dwMode))
{
// move focus back to the previewwnd
SetFocus();
fHandled = TRUE;
ShowSSToolbar(FALSE);
SetTimer(TIMER_TOOLBAR, m_uTimeout);
}
break;
default:
break;
}
}
return 0;
}
LRESULT CPreviewWnd::OnSysCommand(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& fHandled)
{
fHandled = FALSE;
if ((SLIDESHOW_MODE == m_dwMode) &&
((SC_MONITORPOWER == wParam) || (SC_SCREENSAVE == wParam)))
{
fHandled = TRUE;
}
return 0;
}
HRESULT CPreviewWnd::_PreviewFromStream(IStream *pStream, UINT iItem, BOOL fUpdateCaption)
{
IRunnableTask * pTask;
if (fUpdateCaption)
{
// set the caption here in case decode fails
STATSTG stat;
if (SUCCEEDED(pStream->Stat(&stat, 0)))
{
SetCaptionInfo(stat.pwcsName);
CoTaskMemFree(stat.pwcsName);
}
else
{
SetCaptionInfo(NULL);
}
}
HRESULT hr = CDecodeTask::Create(pStream, NULL, iItem, m_pImageFactory, m_hWnd, &pTask);
if (SUCCEEDED(hr))
{
TASKOWNERID toid;
GetTaskIDFromMode(GTIDFM_DECODE, m_dwMode, &toid);
hr = m_pTaskScheduler->AddTask(pTask, toid, 0, ITSAT_DEFAULT_PRIORITY);
pTask->Release();
}
return hr;
}
HRESULT CPreviewWnd::_PreviewFromFile(LPCTSTR pszFile, UINT iItem, BOOL fUpdateCaption)
{
IRunnableTask * pTask;
if (fUpdateCaption)
{
// set the caption here in case decode fails
SetCaptionInfo(pszFile);
}
HRESULT hr = CDecodeTask::Create(NULL, pszFile, iItem, m_pImageFactory, m_hWnd, &pTask);
if (SUCCEEDED(hr))
{
TASKOWNERID toid;
GetTaskIDFromMode(GTIDFM_DECODE, m_dwMode, &toid);
hr = m_pTaskScheduler->AddTask(pTask, toid, 0, ITSAT_DEFAULT_PRIORITY);
pTask->Release();
}
return hr;
}
#define SLIDESHOW_CURSOR_NOTBUSY 0x0
#define SLIDESHOW_CURSOR_BUSY 0x1
#define SLIDESHOW_CURSOR_HIDDEN 0x2
#define SLIDESHOW_CURSOR_NORMAL 0x3
#define SLIDESHOW_CURSOR_CURRENT 0x4
void CPreviewWnd::SetCursorState(DWORD dwType)
{
switch (dwType)
{
case SLIDESHOW_CURSOR_NOTBUSY:
KillTimer(TIMER_BUSYCURSOR);
if (m_fBusy) // ignore multiple NOTBUSY, which we receive for the precaching
{
m_hCurrent = m_hCurOld;
SetCursor(m_hCurrent);
m_fBusy = FALSE;
}
break;
case SLIDESHOW_CURSOR_BUSY:
if (!m_fBusy)
{
m_hCurrent = LoadCursor(NULL, IDC_APPSTARTING);
m_hCurOld = SetCursor(m_hCurrent);
m_fBusy = TRUE;
}
break;
case SLIDESHOW_CURSOR_HIDDEN:
m_hCurOld = NULL;
if (!m_fBusy)
{
m_hCurrent = m_hCurOld;
SetCursor(m_hCurrent);
}
break;
case SLIDESHOW_CURSOR_NORMAL:
m_hCurOld = LoadCursor(NULL, IDC_ARROW);
if (!m_fBusy)
{
m_hCurrent = m_hCurOld;
SetCursor(m_hCurrent);
}
break;
case SLIDESHOW_CURSOR_CURRENT:
SetCursor(m_hCurrent);
break;
}
}
LRESULT CPreviewWnd::OnTimer(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& fHandled)
{
if (TIMER_ANIMATION == wParam)
{
KillTimer(TIMER_ANIMATION);
if (m_pImageData && m_pImageData->IsAnimated() && _ShouldDisplayAnimations()) // might have switched pages between timer calls
{
if (m_pImageData->NextFrame())
{
SetTimer(TIMER_ANIMATION, m_pImageData->GetDelay());
// paint the new image
_UpdateImage();
}
}
}
else if (TIMER_DATAOBJECT == wParam)
{
KillTimer(TIMER_DATAOBJECT); // on shot timer
if (_pdtobj)
{
PreviewItemsFromUnk(_pdtobj);
ATOMICRELEASE(_pdtobj);
}
}
else if (TIMER_BUSYCURSOR == wParam)
{
SetCursorState(SLIDESHOW_CURSOR_BUSY);
}
else if (SLIDESHOW_MODE == m_dwMode)
{
if (TIMER_SLIDESHOW == wParam)
{
_ShowNextSlide(FALSE); // always go forward?
}
else if (TIMER_TOOLBAR == wParam && !m_fIgnoreUITimers)
{
KillTimer(TIMER_TOOLBAR);
ShowSSToolbar(FALSE);
}
}
return 0;
}
void CPreviewWnd::ShowSSToolbar(BOOL bShow, BOOL fForce /*=FALSE*/)
{
if (SLIDESHOW_MODE == m_dwMode)
{
if (fForce)
{
POINT pt;
RECT rc;
GetCursorPos(&pt);
GetWindowRect(&rc);
if (PtInRect(&rc, pt))
{
g_x = pt.x;
g_y = pt.y;
}
}
if (!bShow)
{
if (!m_fToolbarHidden || fForce)
{
//AnimateWindow(m_ctlToolbar.m_hWnd, 200, AW_VER_NEGATIVE | AW_SLIDE | AW_HIDE);
m_ctlToolbar.SetWindowPos(NULL, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_HIDEWINDOW);
m_fToolbarHidden = TRUE;
m_ctlToolbar.SendMessage(TB_SETHOTITEM, -1, 0);
SetCursorState(SLIDESHOW_CURSOR_HIDDEN);
}
}
else
{
KillTimer(TIMER_TOOLBAR);
if (m_fToolbarHidden || fForce)
{
//AnimateWindow(m_ctlToolbar.m_hWnd, 200, AW_VER_POSITIVE | AW_SLIDE | AW_ACTIVATE);
m_ctlToolbar.SetWindowPos(NULL, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
m_fToolbarHidden = FALSE;
SetCursorState(SLIDESHOW_CURSOR_NORMAL);
}
SetTimer(TIMER_TOOLBAR, m_uTimeout);
}
}
}
void CPreviewWnd::OnDraw(HDC hdc)
{
if (m_fCropping)
{
CSelectionTracker tracker;
_SetupCroppingTracker(tracker);
CRect rectImage(0, 0, m_ctlPreview.m_cxImage, m_ctlPreview.m_cyImage);
m_ctlPreview.GetWindowFromImage((LPPOINT)(LPRECT)rectImage, 2);
CRect rectCrop = m_rectCropping;
m_ctlPreview.GetWindowFromImage((LPPOINT)(LPRECT)rectCrop, 2);
HRGN hrgn = ::CreateRectRgn(0, 0, 0, 0);
if (hrgn != NULL)
{
HRGN hrgnImage = ::CreateRectRgnIndirect(rectImage);
if (hrgnImage != NULL)
{
HRGN hrgnCrop = ::CreateRectRgnIndirect(rectCrop);
if (hrgnCrop != NULL)
{
if (ERROR != ::CombineRgn(hrgn, hrgnImage, hrgnCrop, RGN_DIFF))
{
::InvertRgn(hdc, hrgn);
}
::DeleteObject(hrgnCrop);
}
::DeleteObject(hrgnImage);
}
::DeleteObject(hrgn);
}
tracker.Draw(hdc);
}
else
{
if (m_fAnnotating)
{
if (DPA_GetPtrCount(m_hdpaSelectedAnnotations) > 0)
{
CSelectionTracker tracker;
_SetupAnnotatingTracker(tracker);
tracker.Draw(hdc);
}
}
}
}
void CPreviewWnd::OnDrawComplete()
{
if (SLIDESHOW_MODE == m_dwMode)
{
if (!m_fPaused)
SetTimer(TIMER_SLIDESHOW, m_uTimeout);
SetCursorState(SLIDESHOW_CURSOR_NOTBUSY);
}
}
BOOL CPreviewWnd::OnMouseDown(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
if (SLIDESHOW_MODE == m_dwMode)
{
if (uMsg == WM_LBUTTONDOWN)
{
_ShowNextSlide(FALSE); // advance the slide (param is "go back?")
return TRUE;
}
}
else
{
if (m_fCropping)
return _OnMouseDownForCropping(uMsg, wParam, lParam);
else
return _OnMouseDownForAnnotating(uMsg, wParam, lParam);
}
return FALSE;
}
BOOL CPreviewWnd::OnMouseMove(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
if (SLIDESHOW_MODE == m_dwMode)
{
int xPos = GET_X_LPARAM(lParam);
int yPos = GET_Y_LPARAM(lParam);
int dx = xPos > g_x ? xPos - g_x : g_x - xPos;
int dy = yPos > g_y ? yPos - g_y : g_y - yPos;
if (dx > 10 || dy > 10)
{
ShowSSToolbar(TRUE);
}
g_x = xPos;
g_y = yPos;
}
return TRUE;
}
LRESULT CPreviewWnd::OnTBMouseLeave(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& fHandled)
{
if (SLIDESHOW_MODE == m_dwMode)
{
m_fTBTrack = FALSE;
ShowSSToolbar(TRUE);
}
fHandled = FALSE;
return 0;
}
LRESULT CPreviewWnd::OnTBMouseMove(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& fHandled)
{
if (SLIDESHOW_MODE == m_dwMode)
{
if (!m_fTBTrack)
{
TRACKMOUSEEVENT tme;
tme.cbSize = sizeof(tme);
tme.dwFlags = TME_LEAVE;
tme.hwndTrack = m_ctlToolbar.m_hWnd;
TrackMouseEvent(&tme);
ShowSSToolbar(TRUE);
KillTimer(TIMER_TOOLBAR); // we keep the toolbar down for as long as mouse is over it
m_fTBTrack = TRUE;
}
}
fHandled = FALSE;
return 0;
}
BOOL CPreviewWnd::OnSetCursor(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
if (SLIDESHOW_MODE == m_dwMode)
{
if (m_fToolbarHidden)
{
SetCursorState(SLIDESHOW_CURSOR_HIDDEN);
}
else
{
SetCursorState(SLIDESHOW_CURSOR_NORMAL);
}
return TRUE;
}
else if (m_fAnnotating)
{
if (DPA_GetPtrCount(m_hdpaSelectedAnnotations) > 0)
{
CSelectionTracker tracker;
_SetupAnnotatingTracker(tracker);
if (tracker.SetCursor(m_ctlPreview.m_hWnd, lParam))
return TRUE;
}
SetCursor(LoadCursor(NULL, IDC_ARROW));
return TRUE;
}
else if (m_fCropping)
{
CSelectionTracker tracker;
_SetupCroppingTracker(tracker);
if (tracker.SetCursor(m_ctlPreview.m_hWnd, lParam))
return TRUE;
SetCursor(LoadCursor(NULL, IDC_ARROW));
return TRUE;
}
return FALSE;
}
BOOL CPreviewWnd::GetColor(COLORREF * pref)
{
*pref = 0; // black
if (SLIDESHOW_MODE == m_dwMode)
{
return TRUE;
}
return FALSE;
}
BOOL CPreviewWnd::OnSetColor(HDC hdc)
{
if (SLIDESHOW_MODE == m_dwMode)
{
SetBkColor(hdc, 0); // black
SetTextColor(hdc, 0xffffff); // white
return TRUE;
}
return FALSE;
}
// IObjectWithSite
HRESULT CPreviewWnd::SetSite(IUnknown *punk)
{
IUnknown_Set(&m_punkSite, punk);
if (m_pcwndSlideShow)
{
m_pcwndSlideShow->SetSite(punk);
}
return S_OK;
}
// This function take the name of the file being previewed and converts it into a
// title for the Full Screen Preview window. In converting the title it take into
// account user preference settings for how to display the filename.
void CPreviewWnd::SetCaptionInfo(LPCTSTR pszPath)
{
TCHAR szTitle[MAX_PATH] = TEXT("");
TCHAR szDisplayName[MAX_PATH] = TEXT("");
SHFILEINFO sfi = {0};
//
// Default to pszPath for the caption
// pszPath is non-null before the decode is attempted
if (pszPath)
{
if (SHGetFileInfo(pszPath, 0, &sfi, sizeof(sfi), SHGFI_DISPLAYNAME | SHGFI_USEFILEATTRIBUTES))
{
StrCpyN(szTitle, sfi.szDisplayName, ARRAYSIZE(szTitle));
StrCatBuff(szTitle, TEXT(" - "), ARRAYSIZE(szTitle));
}
}
TCHAR szApp[64];
szApp[0] = 0;
LoadString(_Module.GetModuleInstance(), IDS_PROJNAME, szApp, ARRAYSIZE(szApp));
StrCatBuff(szTitle, szApp, ARRAYSIZE(szTitle));
SetWindowText(szTitle);
}
LRESULT CPreviewWnd::OnDestroy(UINT , WPARAM , LPARAM , BOOL& fHandled)
{
RevokeDragDrop(m_hWnd);
_RegisterForChangeNotify(FALSE);
FlushBitmapMessages();
fHandled = FALSE;
// Make sure we don't leak icons
ReplaceWindowIcon(m_hWnd, ICON_SMALL, NULL);
ReplaceWindowIcon(m_hWnd, ICON_BIG, NULL);
// release the image lists used by the toolbar.
HWND hwndTB = m_ctlToolbar.m_hWnd;
HIMAGELIST himl = (HIMAGELIST)::SendMessage(hwndTB, TB_GETHOTIMAGELIST, 0, 0);
::SendMessage(hwndTB, TB_SETHOTIMAGELIST, 0, NULL);
ImageList_Destroy(himl);
himl = (HIMAGELIST)::SendMessage(hwndTB, TB_GETIMAGELIST, 0, 0);
::SendMessage(hwndTB, TB_SETIMAGELIST, 0, NULL);
ImageList_Destroy(himl);
if (WINDOW_MODE == m_dwMode)
{
WINDOWPLACEMENT wp;
wp.length = sizeof(wp);
if (GetWindowPlacement(&wp))
{
HKEY hk;
if (ERROR_SUCCESS == RegCreateKeyEx(HKEY_CURRENT_USER, REGSTR_SHIMGVW, 0, NULL,
REG_OPTION_NON_VOLATILE, KEY_WRITE,
NULL, &hk, NULL))
{
RegSetValueEx(hk, REGSTR_BOUNDS, NULL, REG_BINARY,
(BYTE*)&wp.rcNormalPosition, sizeof(wp.rcNormalPosition));
BOOL fIsMaximized = (wp.showCmd == SW_SHOWMAXIMIZED);
RegSetValueEx(hk, REGSTR_MAXIMIZED, NULL, REG_BINARY,
(BYTE*)&fIsMaximized, sizeof(fIsMaximized));
RegCloseKey(hk);
}
}
PostQuitMessage(0);
}
return 0;
}
HRESULT CPreviewWnd::GetCurrentIDList(LPITEMIDLIST *ppidl)
{
HRESULT hr = _GetItem(m_iCurSlide, ppidl);
if (FAILED(hr))
{
TCHAR szPath[MAX_PATH];
hr = PathFromImageData(szPath, ARRAYSIZE(szPath));
if (SUCCEEDED(hr))
{
hr = SHILCreateFromPath(szPath, ppidl, NULL);
}
}
return hr;
}
void CPreviewWnd::MenuPoint(LPARAM lParam, int *px, int *py)
{
if (-1 == lParam)
{
// message is from the keyboard, figure out where to place the window
RECT rc;
::GetWindowRect(m_hWnd, &rc);
*px = ((rc.left + rc.right) / 2);
*py = ((rc.top + rc.bottom) / 2);
}
else
{
*px = GET_X_LPARAM(lParam);
*py = GET_Y_LPARAM(lParam);
}
}
#define ID_FIRST 1 // Context Menu ID's
#define ID_LAST 0x7fff
LRESULT CPreviewWnd::OnContextMenu(UINT , WPARAM wParam, LPARAM lParam, BOOL& fHandled)
{
if (!m_fAllowContextMenu)
return 0;
if (m_fCanAnnotate && m_fAnnotating && DPA_GetPtrCount(m_hdpaSelectedAnnotations) > 0)
{
HMENU hpopup = CreatePopupMenu();
if (hpopup)
{
CComBSTR bstrDelete;
CComBSTR bstrProperties;
if (bstrDelete.LoadString(IDS_DELETECMD) &&
bstrProperties.LoadString(IDS_PROPERTIESCMD))
{
if (AppendMenu(hpopup, MF_STRING, ID_DELETECMD, bstrDelete) &&
AppendMenu(hpopup, MF_STRING, ID_PROPERTIESCMD, bstrProperties))
{
int x, y;
MenuPoint(lParam, &x, &y);
TrackPopupMenu(hpopup, TPM_RIGHTBUTTON | TPM_LEFTALIGN, x, y, 0, m_hWnd, NULL);
}
}
DestroyMenu(hpopup);
}
return 0;
}
LPITEMIDLIST pidl;
HRESULT hr = GetCurrentIDList(&pidl); // gets the dynamically generated title for this window
if (SUCCEEDED(hr))
{
IContextMenu *pcm;
hr = SHGetUIObjectFromFullPIDL(pidl, NULL, IID_PPV_ARG(IContextMenu, &pcm));
if (SUCCEEDED(hr))
{
HMENU hpopup = CreatePopupMenu();
if (hpopup)
{
// SetSite required if you want in place navigation
IUnknown_SetSite(pcm, SAFECAST(this, IServiceProvider *));
hr = pcm->QueryContextMenu(hpopup, 0, ID_FIRST, ID_LAST, CMF_NORMAL);
if (SUCCEEDED(hr))
{
int x, y;
MenuPoint(lParam, &x, &y);
ASSERT(_pcm3 == NULL);
pcm->QueryInterface(IID_PPV_ARG(IContextMenu3, &_pcm3));
// if there's a separator after "copy", remove it
UINT uCopy = GetMenuIndexForCanonicalVerb(hpopup, pcm,ID_FIRST, L"copy");
if (uCopy != -1)
{
UINT uState = GetMenuState(hpopup, uCopy+1, MF_BYPOSITION);
if (-1 != uState && (uState & MF_SEPARATOR))
{
RemoveMenu(hpopup, uCopy+1, MF_BYPOSITION);
}
}
ContextMenu_DeleteCommandByName(pcm, hpopup, ID_FIRST, L"link");
ContextMenu_DeleteCommandByName(pcm, hpopup, ID_FIRST, L"cut");
ContextMenu_DeleteCommandByName(pcm, hpopup, ID_FIRST, L"copy");
// the shell may have added a static Preview verb
ContextMenu_DeleteCommandByName(pcm, hpopup, ID_FIRST, L"open");
if (!m_fPaused)
{
TogglePlayState();
}
if (SLIDESHOW_MODE == m_dwMode)
{
m_fIgnoreUITimers = TRUE;
}
int idCmd = TrackPopupMenu(hpopup, TPM_RETURNCMD | TPM_RIGHTBUTTON | TPM_LEFTALIGN,
x, y, 0, m_hWnd, NULL);
ATOMICRELEASE(_pcm3);
if (idCmd > 0)
{
CMINVOKECOMMANDINFO cmdInfo =
{
sizeof(cmdInfo),
0,
m_hWnd,
(LPSTR)MAKEINTRESOURCE(idCmd - ID_FIRST),
NULL,
NULL,
SW_NORMAL
};
TCHAR szCommandString[40] = TEXT("");
ContextMenu_GetCommandStringVerb(pcm, idCmd - ID_FIRST, szCommandString, ARRAYSIZE(szCommandString));
if (lstrcmpi(szCommandString, TEXT("edit")) == 0)
{
hr = _SaveIfDirty(TRUE);
if (S_OK != hr)
{
hr = E_ABORT;
}
}
if (SUCCEEDED(hr))
{
if (lstrcmpi(szCommandString, TEXT("print")) == 0)
{
_RefreshSelection(FALSE);
_InvokePrintWizard();
}
else
{
hr = pcm->InvokeCommand(&cmdInfo);
}
}
if (SUCCEEDED(hr))
{
if (lstrcmpi(szCommandString, TEXT("delete")) == 0)
{
_RemoveFromArray(m_iCurSlide);
_ShowNextSlide(FALSE);
}
else if (lstrcmpi(szCommandString, TEXT("edit")) == 0)
{
m_fDirty = FALSE;
m_fNoRestore = TRUE;
// RAID 414238: Image Preview Control
// Context menu "&Edit" causes image preview window
// to close, wrecking Explorer when in 'control mode'.
if (m_dwMode != CONTROL_MODE)
{
PostMessage(WM_CLOSE, 0, 0);
}
}
}
}
if (SLIDESHOW_MODE == m_dwMode)
{
SetTimer(TIMER_TOOLBAR, m_uTimeout);
m_fIgnoreUITimers = FALSE;
}
}
IUnknown_SetSite(pcm, NULL);
DestroyMenu(hpopup);
}
pcm->Release();
}
ILFree(pidl);
}
return 0;
}
int ClearCB(void *p, void *pData)
{
SHFree(p);
return 1;
}
void CPreviewWnd::_ClearDPA()
{
if (m_ppidls)
{
for (UINT i = 0; i < m_cItems; i++)
ILFree(m_ppidls[i]);
CoTaskMemFree(m_ppidls);
m_ppidls = NULL;
}
m_cItems = 0;
m_iCurSlide = 0;
}
HRESULT CPreviewWnd::SetWallpaper(BSTR pszPath)
{
return SUCCEEDED(SetWallpaperHelper(pszPath)) ? S_OK : S_FALSE;
}
HRESULT CPreviewWnd::StartSlideShow(IUnknown *punkToView)
{
HRESULT hr = E_FAIL;
if (NULL == punkToView)
punkToView = m_punkSite;
if (SLIDESHOW_MODE == m_dwMode)
{
// these are required for slideshow
KillTimer(TIMER_SLIDESHOW);
SetCursorState(SLIDESHOW_CURSOR_HIDDEN);
m_fGoBack = FALSE;
// if slide show was reopened cancel any previous tracking
TRACKMOUSEEVENT tme = {0};
tme.cbSize = sizeof(tme);
tme.dwFlags = TME_CANCEL | TME_LEAVE;
tme.hwndTrack = m_ctlToolbar.m_hWnd;
TrackMouseEvent(&tme);
m_fTBTrack = FALSE;
if (punkToView)
hr = PreviewItemsFromUnk(punkToView);
else
hr = _PreviewItem(m_iCurSlide);
if (SUCCEEDED(hr))
m_fPaused = FALSE;
}
else
{
//create the slide show window
// Full Screen
if (m_pcwndSlideShow && m_pcwndSlideShow->m_hWnd)
{
RestoreAndActivate(m_pcwndSlideShow->m_hWnd);
}
else
{
// create the window
if (!m_pcwndSlideShow)
{
m_pcwndSlideShow = new CPreviewWnd();
if (!m_pcwndSlideShow)
{
// out of memory
return E_OUTOFMEMORY;
}
else
{
if (FAILED(m_pcwndSlideShow->Initialize(this, SLIDESHOW_MODE, FALSE)))
{
return E_OUTOFMEMORY;
}
}
}
m_pcwndSlideShow->m_iCurSlide = m_iCurSlide; // so the slide show stays in sync
RECT rc = { 0,0,GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN)};
m_pcwndSlideShow->Create(NULL, rc, NULL, WS_VISIBLE | WS_POPUP);
}
hr = m_pcwndSlideShow->StartSlideShow(NULL);
}
return hr;
}
HRESULT CPreviewWnd::_GetItem(UINT iItem, LPITEMIDLIST *ppidl)
{
HRESULT hr = E_FAIL;
if (iItem < m_cItems)
{
hr = SHILClone(m_ppidls[iItem], ppidl);
}
return hr;
}
void CPreviewWnd::_RemoveFromArray(UINT iItem)
{
if (iItem < m_cItems)
{
ILFree(m_ppidls[iItem]); // this one is now gone
// slide all other pidls down in the array
for (UINT i = iItem + 1; i < m_cItems; i++)
{
m_ppidls[i - 1] = m_ppidls[i];
}
m_cItems--;
m_ppidls[m_cItems] = NULL; // make sure stale ptr is now NULL
// if we deleted an item before m_iCurSlide then we must adjust m_iCurSlide
if (iItem < m_iCurSlide)
{
m_iCurSlide--;
}
else if (m_iCurSlide == m_cItems)
{
m_iCurSlide = 0;
}
// Now prepare for "ShowNextSlide"
if (!m_iCurSlide)
{
m_iCurSlide = m_cItems ? m_cItems-1 : 0;
}
else
{
m_iCurSlide--;
}
// make sure the prefetch task has the right index
if (m_pNextImageData)
{
if (!(m_pNextImageData->_iItem) && iItem && m_cItems)
{
m_pNextImageData->_iItem = m_cItems-1;
}
else if (m_pNextImageData->_iItem > iItem)
{
m_pNextImageData->_iItem--;
}
else
{
FlushBitmapMessages();
ATOMICRELEASE(m_pNextImageData);
}
}
}
}
HRESULT CPreviewWnd::_DeleteCurrentSlide()
{
LPITEMIDLIST pidl;
HRESULT hr = _GetItem(m_iCurSlide, &pidl);
if (SUCCEEDED(hr))
{
TCHAR szPath[MAX_PATH + 1] = {0}; // +1 and zero init for dbl NULL terminate extra terminator
DWORD dwAttribs = SFGAO_FILESYSTEM | SFGAO_STREAM;
hr = SHGetNameAndFlags(pidl, SHGDN_FORPARSING, szPath, ARRAYSIZE(szPath)-1, &dwAttribs);
if (SUCCEEDED(hr))
{
BOOL fSuccess = TRUE;
if (dwAttribs & SFGAO_FILESYSTEM)
{
SHFILEOPSTRUCT fo = {0};
fo.hwnd = m_hWnd;
fo.wFunc = FO_DELETE;
fo.pFrom = szPath;
fo.fFlags = FOF_ALLOWUNDO;
fo.fAnyOperationsAborted = FALSE;
if (SHFileOperation(&fo) == ERROR_SUCCESS)
{
if (fo.fAnyOperationsAborted == TRUE)
fSuccess = FALSE;
}
}
else
{
_InvokeVerb(TEXT("delete"));
// We have to assume success since there is no way to know if the user
// cancelled the confirmation dialog without hitting the camera again.
}
if (fSuccess)
{
m_fDirty = FALSE;
_RemoveFromArray(m_iCurSlide);
_ShowNextSlide(FALSE);
}
}
ILFree(pidl);
}
return hr;
}
// index can be either current or next slide so that user can click multiple times on next/prev button
HRESULT CPreviewWnd::_ShowNextSlide(BOOL bGoBack)
{
HRESULT hr = E_FAIL;
if (m_cItems)
{
if (bGoBack)
{
if (m_iCurSlide)
m_iCurSlide--;
else
m_iCurSlide = m_cItems - 1;
}
else
{
m_iCurSlide++;
if (m_iCurSlide >= m_cItems)
m_iCurSlide = 0;
}
if (!m_fPaused)
{
SetTimer(TIMER_SLIDESHOW, m_uTimeout);
}
SetTimer(TIMER_BUSYCURSOR, 500);
LPITEMIDLIST pidl;
// set the caption in case the load fails
if (SUCCEEDED(_GetItem(m_iCurSlide, &pidl)))
{
TCHAR szPath[MAX_PATH];
if (SUCCEEDED(SHGetNameAndFlags(pidl, SHGDN_FORPARSING | SHGDN_INFOLDER, szPath, ARRAYSIZE(szPath)-1, NULL)))
{
SetCaptionInfo(szPath);
}
else
{
SetCaptionInfo(NULL);
}
ILFree(pidl);
}
hr = _PreviewItem(m_iCurSlide);
if (SUCCEEDED(hr))
{
_PreLoadItem((m_iCurSlide + 1) % m_cItems);
}
}
return hr;
}
HRESULT CPreviewWnd::_StartDecode(UINT iItem, BOOL fUpdateCaption)
{
LPITEMIDLIST pidl;
HRESULT hr = _GetItem(iItem, &pidl);
if (SUCCEEDED(hr))
{
TCHAR szPath[MAX_PATH];
DWORD dwAttribs = SFGAO_FILESYSTEM | SFGAO_STREAM;
hr = SHGetNameAndFlags(pidl, SHGDN_FORPARSING, szPath, ARRAYSIZE(szPath), &dwAttribs);
if (SUCCEEDED(hr) && (dwAttribs & SFGAO_FILESYSTEM))
{
hr = _PreviewFromFile(szPath, iItem, fUpdateCaption);
}
else if (dwAttribs & SFGAO_STREAM)
{
// this might not be a file system object, try to bind to it via IStream
IStream *pstrm;
hr = SHBindToObject(NULL, IID_X_PPV_ARG(IStream, pidl, &pstrm));
if (SUCCEEDED(hr))
{
hr = _PreviewFromStream(pstrm, iItem, fUpdateCaption);
pstrm->Release();
}
}
else
{
// funky attributes?
hr = S_FALSE;
}
ILFree(pidl);
}
return hr;
}
HRESULT CPreviewWnd::_PreLoadItem(UINT iItem)
{
HRESULT hr = _StartDecode(iItem, FALSE);
if (SUCCEEDED(hr))
{
m_iDecodingNextImage = iItem;
}
return hr;
}
HRESULT CPreviewWnd::_PreviewItem(UINT iItem)
{
HRESULT hr = S_OK;
if ((SLIDESHOW_MODE == m_dwMode) && (0 == m_cItems)) // if no more items, user just deleted the last one, so quit the slideshow
{
_CloseSlideshowWindow();
}
else
{
if (!_TrySetImage())
{
// If we are not currently already decoding this item, let's get cranking!
if (m_iDecodingNextImage != iItem)
{
hr = _StartDecode(iItem, TRUE);
}
StatusUpdate((S_OK == hr) ? IDS_LOADING : IDS_LOADFAILED);
}
}
return hr;
}
int CPreviewWnd::TranslateAccelerator(MSG *pmsg)
{
if (IsVK_TABCycler(pmsg))
{
if (OnNonSlideShowTab())
{
return TRUE;
}
}
else if (m_haccel)
{
ASSERT(m_hWnd);
return ::TranslateAccelerator(m_hWnd, m_haccel, pmsg);
}
return FALSE;
}
// Sent when the image generation status has changed, once when the image is first
// being created and again if there is an error of any kind. This should invalidate
// and free any left over bitmap and the cached copy of the previous m_ImgCtx
void CPreviewWnd::StatusUpdate(int iStatus)
{
if (m_pImageData)
{
m_pImageData->Release();
m_pImageData = NULL;
}
//
// the caption is set at the first attempt to load an image
m_ctlPreview.StatusUpdate(iStatus);
_SetMultipageCommands();
_SetMultiImagesCommands();
_SetAnnotatingCommands(FALSE);
_SetEditCommands();
m_fPrintable = FALSE;
m_ctlToolbar.SendMessage(TB_ENABLEBUTTON, ID_PRINTCMD, MAKELONG(m_fPrintable, 0));
// update our toolbar
BOOL fHandled;
OnSize(0x0, 0, 0, fHandled);
}
// Return:
// S_OK walk succeeded, image files found to preview: display new images in preview
// S_FALSE walk cancelled (by user): display existing image in preview (no change)
// E_XXXX walk failed: display no image in preview
//
HRESULT CPreviewWnd::WalkItemsToPreview(IUnknown* punk)
{
HRESULT hr = _SaveIfDirty(TRUE);
if (FAILED(hr) || hr == S_FALSE)
return hr;
m_fFirstItem = TRUE;
_ClearDPA(); // clean up old stuff
INamespaceWalk *pnsw;
hr = CoCreateInstance(CLSID_NamespaceWalker, NULL, CLSCTX_INPROC, IID_PPV_ARG(INamespaceWalk, &pnsw));
if (SUCCEEDED(hr))
{
// in control mode we only dislay one item at a time. lets setup our
// state so this can work just like the other modes
DWORD dwFlags = (CONTROL_MODE == m_dwMode) ? 0 : NSWF_ONE_IMPLIES_ALL | NSWF_NONE_IMPLIES_ALL;
m_fClosed = FALSE;
hr = pnsw->Walk(punk, dwFlags, m_cWalkDepth, SAFECAST(this, INamespaceWalkCB *));
// the window might have been closed during the namespace walk
if (WINDOW_MODE == m_dwMode && m_fClosed)
{
hr = E_FAIL;
}
if (SUCCEEDED(hr))
{
hr = pnsw->GetIDArrayResult(&m_cItems, &m_ppidls);
if (SUCCEEDED(hr) && (m_dwMode == WINDOW_MODE) && m_cItems && m_fFirstTime)
{
m_fFirstTime = FALSE;
SHAddToRecentDocs(SHARD_PIDL, m_ppidls[0]);
}
}
pnsw->Release();
}
// Clarification of INamespaceWalk return values:
// S_OK walk has succeeded, and image files found to preview
// S_FALSE walk has succeeded, but no image files found to preview
// **** convert to E_FAIL to keep in line with return of function
// E_XXXX walk has failed
//
return hr == S_FALSE ? E_FAIL : hr;
}
void CPreviewWnd::PreviewItems()
{
if (WINDOW_MODE == m_dwMode)
{
RestoreAndActivate(m_hWnd);
}
_PreviewItem(0);
if (SLIDESHOW_MODE == m_dwMode)
{
if (m_cItems > 1)
{
_PreLoadItem(1);
}
}
}
// build the _ppidl and m_cItems members and preview the first one
HRESULT CPreviewWnd::PreviewItemsFromUnk(IUnknown *punk)
{
HRESULT hr = WalkItemsToPreview(punk);
if (SUCCEEDED(hr))
{
if (S_FALSE != hr)
PreviewItems();
}
else
{
StatusUpdate(IDS_LOADFAILED);
}
return hr;
}
// If the "new" image is the same as the old image, and the old image was recently edited then we assume that
// the reason we are getting a new ShowFile request is due to an FSChangeNotify on the file. We also assume
// that we are the cause of this change notify. Further we assume that we already have the correctly decoded
// image still ready. These are assumptions which might not be TRUE 100%, in which case we will do really
// strange things, but they should be TRUE 99.9% of the time which is considered "good enough". The reason we
// make these dangerous assumptions is to prevent decoding the image again and thus flickering between the
// old image, the "generating preview..." message, and the new (identical) image.
BOOL CPreviewWnd::_ReShowingSameFile(LPCTSTR pszNewFile)
{
BOOL bIsSameFile = FALSE;
if (m_pImageData)
{
if (pszNewFile && m_fWasEdited)
{
m_fWasEdited = FALSE;
TCHAR szOldPath[MAX_PATH];
if ((S_OK == PathFromImageData(szOldPath, ARRAYSIZE(szOldPath))) &&
(0 == StrCmpI(szOldPath, pszNewFile)))
{
if (m_pEvents)
m_pEvents->OnPreviewReady();
bIsSameFile = TRUE;
}
}
if (!bIsSameFile)
{
m_pImageData->Release(); // need to start clean
m_pImageData = NULL;
}
}
return bIsSameFile;
}
// pszFile may be NULL. cItems expresses how many are selected so we can
// display "multiple items selected" and not display anything.
LRESULT CPreviewWnd::ShowFile(LPCTSTR pszFile, UINT cItems, BOOL fReshow)
{
if (!m_hWnd)
return S_FALSE;
HRESULT hr = S_FALSE;
TCHAR szLongName[MAX_PATH]; // short file names are UGLY
if (GetLongPathName(pszFile, szLongName, ARRAYSIZE(szLongName)))
{
pszFile = szLongName;
}
if (!fReshow && _ReShowingSameFile(pszFile))
return S_FALSE;
// It is possible that there is already a bitmap message in our queue from the previous rendering.
// If this is the case we should remove that message and release its bitmap before we continue.
// If we do not then that message will get processed and will send the OnPreviewReady event to the
// obejct container but this event might no longer be valid.
FlushBitmapMessages();
if (pszFile && *pszFile)
{
IDataObject *pdtobj;
hr = GetUIObjectFromPath(pszFile, IID_PPV_ARG(IDataObject, &pdtobj));
if (SUCCEEDED(hr))
{
hr = PreviewItemsFromUnk(pdtobj);
m_fPaused = TRUE;
pdtobj->Release();
}
}
else
{
int iRetCode = (cItems > 1) ? IDS_MULTISELECT : IDS_NOPREVIEW;
// Set the Return Code into all owned zoom windows. This instructs these windows to disregard
// their previous images and display the status message instead.
StatusUpdate(iRetCode);
}
return hr;
}
LRESULT CPreviewWnd::IV_OnIVScroll(UINT , WPARAM , LPARAM lParam, BOOL&)
{
DWORD nHCode = LOWORD(lParam);
DWORD nVCode = HIWORD(lParam);
if (nHCode)
{
m_ctlPreview.SendMessage(WM_HSCROLL, nHCode, NULL);
}
if (nVCode)
{
m_ctlPreview.SendMessage(WM_VSCROLL, nVCode, NULL);
}
return 0;
}
// IV_OnSetOptions
//
// This message is sent to turn on or off all the optional features of the image preview control.
// NOTE: When used as a control this function is called BEFORE the window is created. Don't do
// anything in this function that will fail without a window unless you check for this condition.
LRESULT CPreviewWnd::IV_OnSetOptions(UINT , WPARAM wParam, LPARAM lParam, BOOL&)
{
BOOL bResult = TRUE;
// Boolify lParam just to be safe.
lParam = lParam ? 1:0;
switch (wParam)
{
case IVO_TOOLBAR:
if ((BOOL)lParam != m_fShowToolbar)
{
m_fShowToolbar = (BOOL)lParam;
if (m_hWnd)
{
if (m_fShowToolbar)
{
if (!m_ctlToolbar)
{
bResult = CreateToolbar();
if (!bResult)
{
m_fShowToolbar = FALSE;
break;
}
}
}
else
{
if (m_ctlToolbar)
{
m_ctlToolbar.DestroyWindow();
}
}
}
}
break;
case IVO_PRINTBTN:
if ((BOOL)lParam != m_fHidePrintBtn)
{
m_fHidePrintBtn = (BOOL)lParam;
if (m_hWnd && m_ctlToolbar)
{
m_ctlToolbar.SendMessage(TB_HIDEBUTTON,ID_PRINTCMD,lParam);
}
}
break;
case IVO_CONTEXTMENU:
m_fAllowContextMenu = (BOOL)lParam;
break;
case IVO_PRINTABLE:
TraceMsg(TF_WARNING, "Obsolete IVO_PRINTABLE option received.");
break;
case IVO_DISABLEEDIT:
m_fDisableEdit = (BOOL)lParam;
break;
default:
break;
}
return bResult;
}
void CPreviewWnd::_SetEditCommands()
{
if (CONTROL_MODE != m_dwMode)
{
// We can save if we have a file; the save dialog will show available encoders
BOOL fCanSave = m_pImageData ? TRUE : FALSE;
m_ctlToolbar.SendMessage(TB_HIDEBUTTON, ID_SAVEASCMD, MAKELONG(!fCanSave, 0));
m_ctlToolbar.SendMessage(TB_ENABLEBUTTON, ID_SAVEASCMD, MAKELONG(fCanSave, 0));
}
BOOL fCanRotate = m_pImageData != NULL;
if (CONTROL_MODE != m_dwMode)
{
m_ctlToolbar.SendMessage(TB_HIDEBUTTON, ID_ROTATESEP, MAKELONG(!fCanRotate, 0));
m_ctlToolbar.SendMessage(TB_HIDEBUTTON, ID_ROTATE90CMD, MAKELONG(!fCanRotate, 0));
m_ctlToolbar.SendMessage(TB_HIDEBUTTON, ID_ROTATE270CMD, MAKELONG(!fCanRotate, 0));
}
else
{
// we don't rotate multi-page images in control mode
fCanRotate = fCanRotate && !m_pImageData->IsMultipage();
}
// No matter where we are GDIPlus can't rotate WMF or EMF files. Curiously,
// we will let you rotate ICO files, but because we don't have an encoder
// we won't save them :)
if (fCanRotate)
{
fCanRotate = !(IsEqualGUID(ImageFormatWMF, m_pImageData->_guidFormat) ||
IsEqualGUID(ImageFormatEMF, m_pImageData->_guidFormat) ||
m_pImageData->IsAnimated());
}
m_ctlToolbar.SendMessage(TB_ENABLEBUTTON, ID_ROTATESEP, MAKELONG(fCanRotate, 0));
m_ctlToolbar.SendMessage(TB_ENABLEBUTTON, ID_ROTATE90CMD, MAKELONG(fCanRotate, 0));
m_ctlToolbar.SendMessage(TB_ENABLEBUTTON, ID_ROTATE270CMD, MAKELONG(fCanRotate, 0));
TCHAR szFile[MAX_PATH];
BOOL fCanOpen = SUCCEEDED(PathFromImageData(szFile, ARRAYSIZE(szFile)));
m_ctlToolbar.SendMessage(TB_ENABLEBUTTON, ID_OPENCMD, MAKELONG(fCanOpen, 0));
}
void CPreviewWnd::_UpdatePageNumber()
{
TCHAR szText[20];
wnsprintf(szText, ARRAYSIZE(szText), TEXT("%d"), m_pImageData->_iCurrent+1);
TBBUTTONINFO bi = {0};
bi.cbSize = sizeof(bi);
bi.dwMask = TBIF_TEXT | TBIF_STATE;
bi.fsState = TBSTATE_ENABLED;
bi.pszText = szText;
m_ctlToolbar.SendMessage(TB_SETBUTTONINFO, ID_PAGELIST, (LPARAM)&bi);
}
void CPreviewWnd::_SetMultipageCommands()
{
DWORD dwMode;
// this code relies on the fact that TIFFs are the only multipage format we view
if (!m_pImageData || m_pImageData->_guidFormat != ImageFormatTIFF )
{
dwMode = MPCMD_HIDDEN;
}
else if (!m_pImageData->IsMultipage())
{
dwMode = MPCMD_DISABLED;
}
else if (m_pImageData->IsFirstPage())
{
dwMode = MPCMD_FIRSTPAGE;
}
else if (m_pImageData->IsLastPage())
{
dwMode = MPCMD_LASTPAGE;
}
else
{
dwMode = MPCMD_MIDDLEPAGE;
}
// remember which buttons are enabled/hidden so we can quickly create our context menu
if (dwMode != m_dwMultiPageMode)
{
m_dwMultiPageMode = dwMode;
if (CONTROL_MODE != m_dwMode)
{
// Switch accelerator tables so that Page Up and Page Down work
if (dwMode == MPCMD_HIDDEN || dwMode == MPCMD_DISABLED)
{
m_haccel = LoadAccelerators(_Module.GetModuleInstance(), MAKEINTRESOURCE(IDA_PREVWND_SINGLEPAGE));
}
else
{
m_haccel = LoadAccelerators(_Module.GetModuleInstance(), MAKEINTRESOURCE(IDA_PREVWND_MULTIPAGE));
}
m_ctlToolbar.SendMessage(TB_HIDEBUTTON, ID_PAGECMDSEP, MAKELONG((MPCMD_HIDDEN==dwMode),0));
m_ctlToolbar.SendMessage(TB_HIDEBUTTON, ID_PREVPAGECMD, MAKELONG((MPCMD_HIDDEN==dwMode),0));
m_ctlToolbar.SendMessage(TB_HIDEBUTTON, ID_PAGELIST, MAKELONG((MPCMD_HIDDEN==dwMode),0));
m_ctlToolbar.SendMessage(TB_HIDEBUTTON, ID_NEXTPAGECMD, MAKELONG((MPCMD_HIDDEN==dwMode),0));
if (MPCMD_HIDDEN != dwMode)
{
_UpdatePageNumber();
m_ctlToolbar.SendMessage(TB_ENABLEBUTTON, ID_PREVPAGECMD, MAKELONG((MPCMD_FIRSTPAGE!=dwMode && MPCMD_DISABLED!=dwMode),0));
m_ctlToolbar.SendMessage(TB_ENABLEBUTTON, ID_NEXTPAGECMD, MAKELONG((MPCMD_LASTPAGE !=dwMode && MPCMD_DISABLED!=dwMode),0));
}
}
}
else
{
if (CONTROL_MODE != m_dwMode)
{
if (dwMode == MPCMD_MIDDLEPAGE)
{
_UpdatePageNumber();
}
}
}
}
void CPreviewWnd::_SetMultiImagesCommands()
{
BOOL bHasFiles = m_cItems;
if (CONTROL_MODE != m_dwMode)
{
m_ctlToolbar.SendMessage(TB_HIDEBUTTON, ID_PREVIMGCMD, MAKELONG(!bHasFiles, 0));
m_ctlToolbar.SendMessage(TB_HIDEBUTTON, ID_NEXTIMGCMD, MAKELONG(!bHasFiles, 0));
m_ctlToolbar.SendMessage(TB_HIDEBUTTON, ID_VIEWCMDSEP, MAKELONG(!bHasFiles, 0));
m_ctlToolbar.SendMessage(TB_ENABLEBUTTON, ID_PREVIMGCMD, MAKELONG(bHasFiles, 0));
m_ctlToolbar.SendMessage(TB_ENABLEBUTTON, ID_NEXTIMGCMD, MAKELONG(bHasFiles, 0));
}
}
HRESULT CPreviewWnd::PathFromImageData(LPTSTR pszFile, UINT cch)
{
*pszFile = 0;
IShellImageData *pSID;
HRESULT hr = m_pImageData ? m_pImageData->Lock(&pSID) : E_FAIL;
if (SUCCEEDED(hr))
{
IPersistFile *ppf;
hr = pSID->QueryInterface(IID_PPV_ARG(IPersistFile, &ppf));
if (SUCCEEDED(hr))
{
WCHAR *psz;
hr = ppf->GetCurFile(&psz);
if (SUCCEEDED(hr))
{
lstrcpyn(pszFile, psz, cch);
CoTaskMemFree(psz);
}
ppf->Release();
}
m_pImageData->Unlock();
}
return hr;
}
HRESULT CPreviewWnd::ImageDataSave(LPCTSTR pszFile, BOOL bShowUI)
{
IShellImageData * pSID = NULL;
HRESULT hr = m_pImageData ? m_pImageData->Lock(&pSID) : E_FAIL;
Image *pimgRestore = NULL;
if (SUCCEEDED(hr))
{
GUID guidFmt = GUID_NULL;
BOOL bSave = TRUE;
BOOL bWarnBurn = FALSE;
BOOL bRestoreParams = FALSE;
pSID->GetRawDataFormat(&guidFmt);
// if saving to a jpeg, set the image quality to high
// if pszFile is NULL, we're saving the same file, so don't promote the image quality
if (pszFile)
{
m_pImageFactory->GetDataFormatFromPath(pszFile, &guidFmt);
if (guidFmt == ImageFormatJPEG )
{
IPropertyBag *ppb;
if (SUCCEEDED(SHCreatePropertyBagOnMemory(STGM_READWRITE,
IID_PPV_ARG(IPropertyBag, &ppb))))
{
// write the quality value for the recompression into the property bag
// we have to write the format too...CImageData relies on "all or nothing"
// from the encoder params property bag
VARIANT var;
hr = InitVariantFromGUID(&var, ImageFormatJPEG);
if (SUCCEEDED(hr))
{
ppb->Write(SHIMGKEY_RAWFORMAT, &var);
VariantClear(&var);
}
SHPropertyBag_WriteInt(ppb, SHIMGKEY_QUALITY, 100);
pSID->SetEncoderParams(ppb);
ppb->Release();
bRestoreParams = TRUE;
}
}
}
if (bShowUI && pszFile)
{
// warn the user if saving from TIFF to something that will lose annotations
BOOL bDestTiff = ImageFormatTIFF == guidFmt;
BOOL bAnnot = m_ctlPreview.GetAnnotations()->GetCount() > 0;
bWarnBurn = bAnnot && !bDestTiff;
#if 0
if (!bWarnBurn && S_OK == m_pImageData->IsMultipage() && !bDestTiff)
{
GUID guidFmt;
bWarnBurn = TRUE;
if (SUCCEEDED(m_pImageFactory->GetDataFormatFromPath(pszFile, &guidFmt)))
{
bWarn = !FmtSupportsMultiPage(pSID, &guidFmt);
}
}
#endif // 0 Put the multipage warning back in if needed, and change the wording of IDS_SAVE_WARN_TIFF
}
if (bWarnBurn)
{
m_fPromptingUser = TRUE;
bSave = (IDYES == ShellMessageBox(_Module.GetModuleInstance(), m_hWnd,
MAKEINTRESOURCE(IDS_SAVE_WARN_TIFF),
MAKEINTRESOURCE(IDS_PROJNAME),
MB_YESNO | MB_ICONINFORMATION));
m_fPromptingUser = FALSE;
if (bSave)
{
// Save the current image frame to restore after the save to a different file is complete
pimgRestore = _BurnAnnotations(pSID);
}
}
if (bSave)
{
IPersistFile *ppf;
hr = pSID->QueryInterface(IID_PPV_ARG(IPersistFile, &ppf));
if (SUCCEEDED(hr))
{
// if saving to the same file name, make sure
// the changenotify code ignores the notify this will generate
//
if (!pszFile)
{
m_fIgnoreNextNotify = TRUE;
}
hr = ppf->Save(pszFile, FALSE);
if (SUCCEEDED(hr))
{
m_fWasEdited = TRUE;
}
else if (!pszFile)
{
m_fIgnoreNextNotify = FALSE;
}
}
ppf->Release();
if (pimgRestore)
{
pSID->ReplaceFrame(pimgRestore);
}
}
else
{
hr = S_FALSE; // we did nothing
}
if (bRestoreParams)
{
pSID->SetEncoderParams(NULL);
}
m_pImageData->Unlock();
}
return hr;
}
HRESULT CPreviewWnd::_SaveAsCmd()
{
if (m_pImageData == NULL)
return S_OK;
OPENFILENAME ofn = {0};
TCHAR szOrgFile[MAX_PATH];
TCHAR szExt[MAX_PATH]={0};
PathFromImageData(szOrgFile, ARRAYSIZE(szOrgFile));
LPTSTR psz = PathFindExtension(szOrgFile);
StrCpyN(szExt, psz, ARRAYSIZE(szExt));
TCHAR szFile[MAX_PATH];
if (!m_fDisableEdit && m_fCanSave && m_pImageData->IsEditable())
{
// If we haven't explicitly been told not to and the file is writeable then
// suggest saving on top of the current image
PathFromImageData(szFile, ARRAYSIZE(szFile));
}
else
{
// Otherwise suggest New Image.jpg
LoadString(_Module.GetModuleInstance(), IDS_NEW_FILENAME, szFile, ARRAYSIZE(szFile));
}
CComBSTR bstrTitle;
bstrTitle.LoadString(IDS_SAVEAS_TITLE);
ofn.lStructSize = sizeof(ofn);
PathRemoveExtension(szFile);
TCHAR szFilter[MAX_PATH] = TEXT("\0");
ofn.nFilterIndex = _GetFilterStringForSave(szFilter, ARRAYSIZE(szFilter), szExt);
ofn.lpstrFilter = szFilter;
ofn.hwndOwner = m_hWnd;
ofn.lpstrFile = szFile;
ofn.lpstrTitle = bstrTitle;
ofn.nMaxFile = MAX_PATH - lstrlen(szExt);
ofn.Flags = OFN_EXPLORER | OFN_OVERWRITEPROMPT | OFN_PATHMUSTEXIST | OFN_ENABLESIZING;
ofn.lpstrDefExt = *szExt == TEXT('.') ? szExt + 1: szExt;
m_fPromptingUser = TRUE;
BOOL bResult = ::GetSaveFileName(&ofn);
m_fPromptingUser = FALSE;
if (bResult != 0)
{
m_ctlPreview.CommitAnnotations();
HRESULT hr = ImageDataSave(szFile, TRUE);
if (S_OK == hr)
{
if (lstrcmpi(szFile, szOrgFile) == 0)
{
_UpdateImage();
ShowFile(szFile, 1);
m_fDirty = FALSE;
}
}
else if (FAILED(hr))
{
// If we failed to save then we are corrupt and need to be reloaded
// If we were just copying then only show the message
if (lstrcmpi(szFile, szOrgFile) == 0)
{
_UpdateImage();
ShowFile(szOrgFile, 1, TRUE);
m_fDirty = FALSE;
}
else
{
// delete the failed copy
DeleteFile(szFile);
}
CComBSTR bstrMsg, bstrTitle;
if (bstrMsg.LoadString(IDS_SAVEFAILED_MSGBOX) && bstrTitle.LoadString(IDS_PROJNAME))
{
m_fPromptingUser = TRUE;
MessageBox(bstrMsg, bstrTitle, MB_OK | MB_ICONERROR | MB_APPLMODAL);
m_fPromptingUser = FALSE;
return E_FAIL;
}
}
}
else
{
DWORD dwResult = CommDlgExtendedError();
if (dwResult == FNERR_BUFFERTOOSMALL)
{
CComBSTR bstrMsg, bstrTitle;
if (bstrMsg.LoadString(IDS_NAMETOOLONG_MSGBOX) && bstrTitle.LoadString(IDS_PROJNAME))
{
m_fPromptingUser = TRUE;
MessageBox(bstrMsg, bstrTitle, MB_OK | MB_ICONERROR | MB_APPLMODAL);
m_fPromptingUser = FALSE;
}
}
return S_FALSE; // User probably cancelled
}
return S_OK;
}
void CPreviewWnd::_PropertiesCmd()
{
if (m_fAnnotating && DPA_GetPtrCount(m_hdpaSelectedAnnotations) == 1)
{
CAnnotation* pAnnotation = (CAnnotation*)DPA_GetPtr(m_hdpaSelectedAnnotations, 0);
if (!pAnnotation->HasWidth() && !pAnnotation->HasTransparent() && !pAnnotation->HasColor() && pAnnotation->HasFont())
{
CHOOSEFONT cf = {0};
LOGFONT lfFont;
pAnnotation->GetFont(lfFont);
COLORREF crFont = pAnnotation->GetFontColor();
cf.lStructSize = sizeof(cf);
cf.hwndOwner = m_hWnd;
cf.lpLogFont = &lfFont;
cf.Flags = CF_SCREENFONTS | CF_EFFECTS | CF_INITTOLOGFONTSTRUCT | CF_NOVERTFONTS | CF_NOSCRIPTSEL;
cf.rgbColors = crFont;
m_fPromptingUser = TRUE;
BOOL bResult = ::ChooseFont(&cf);
m_fPromptingUser = FALSE;
if (bResult)
{
crFont = cf.rgbColors;
lfFont.lfHeight = (lfFont.lfHeight > 0) ? lfFont.lfHeight : -lfFont.lfHeight;
pAnnotation->SetFont(lfFont);
pAnnotation->SetFontColor(crFont);
m_fDirty = TRUE;
CRegKey Key;
if (ERROR_SUCCESS != Key.Open(HKEY_CURRENT_USER, REGSTR_SHIMGVW))
{
Key.Create(HKEY_CURRENT_USER, REGSTR_SHIMGVW);
}
if (Key.m_hKey != NULL)
{
Key.SetValue(crFont, REGSTR_TEXTCOLOR);
::RegSetValueEx(Key, REGSTR_FONT, 0, REG_BINARY, (LPBYTE)&lfFont, sizeof(lfFont));
}
_RefreshSelection();
}
}
else
{
m_fPromptingUser = TRUE;
INT_PTR iResult = DialogBoxParam(_Module.GetModuleInstance(),
MAKEINTRESOURCE(IDD_ANNOPROPS),
m_hWnd, _AnnoPropsDlgProc, (LPARAM)this);
m_fPromptingUser = FALSE;
}
}
else
{
// Under these condition the has pressed Ctrl-I to get File Properties
// So serve them up.
CComBSTR bstrSummary;
bstrSummary.LoadString(IDS_SUMMARY);
_InvokeVerb(TEXT("properties"), bstrSummary);
}
}
HRESULT _VerbMatches(LPCWSTR pszFile, LPCWSTR pszVerb, LPCTSTR pszOurs)
{
TCHAR szTemp[MAX_PATH];
DWORD cch = ARRAYSIZE(szTemp);
HRESULT hr = AssocQueryString(ASSOCF_VERIFY, ASSOCSTR_COMMAND, pszFile, pszVerb, szTemp, &cch);
if (SUCCEEDED(hr))
{
hr = (StrStrI(szTemp, pszOurs)) ? S_OK : S_FALSE;
}
return hr;
}
void CPreviewWnd::_OpenCmd()
{
HRESULT hr = _SaveIfDirty(TRUE);
LPCTSTR pszVerb;
if (S_OK == hr)
{
TCHAR szFile[MAX_PATH];
hr = PathFromImageData(szFile, ARRAYSIZE(szFile));
if (SUCCEEDED(hr))
{
HRESULT hrOpen = _VerbMatches(szFile, L"open", TEXT("shimgvw.dll"));
HRESULT hrEdit = _VerbMatches(szFile, L"edit", TEXT("mspaint.exe"));
// if edit is empty, or if edit is mspaint and open is not shimgvw, use the open verb instead
if (SUCCEEDED(hrEdit))
{
if (S_OK == hrEdit && hrOpen == S_FALSE)
{
pszVerb = TEXT("open");
}
else
{
pszVerb = TEXT("edit");
}
}
else if (hrOpen == S_FALSE)
{
pszVerb = TEXT("open");
}
else
{
pszVerb = TEXT("openas");
}
hr = _InvokeVerb(pszVerb);
}
if (FAILED(hr))
return;
// set m_fNoRestore to avoid the rotation confirmation restoration popup-ation
m_fNoRestore = TRUE;
// The user had a chance to save but may have said no. Pretend we're not dirty
m_fDirty = FALSE;
PostMessage(WM_CLOSE, 0, 0);
}
}
BOOL CPreviewWnd::_CanAnnotate(CDecodeTask * pImageData)
{
// If we have an image and its encoder and we haven't been explicitly told not to allow editing
// and the image is writeable
if (m_pImageData && m_pImageData->IsEditable() && !m_fDisableEdit && m_fCanSave)
{
// then if its a TIFF we can annotate it
return IsEqualGUID(ImageFormatTIFF, pImageData->_guidFormat);
}
return FALSE;
}
BOOL CPreviewWnd::_CanCrop(CDecodeTask * pImageData)
{
if (m_pImageData != NULL)
{
// REVIEW I added this for CyraR as a proof of concept. If we decide to support it
// we still need to catch all the places where we should save the croppped image and
// call GDIplus to accomplish the crop.
#ifdef SUPPORT_CROPPING
if (S_OK != m_pImageData->IsEditable())
return FALSE;
LONG cPages;
if (S_OK == m_pImageData->GetPageCount(&cPages))
{
if (cPages > 1)
return FALSE;
}
return TRUE;
#endif
}
return FALSE;
}
// Called whenever the image changes to hide or show the annotation buttons.
void CPreviewWnd::_SetAnnotatingCommands(BOOL fEnableAnnotations)
{
if (CONTROL_MODE != m_dwMode)
{
if (fEnableAnnotations)
{
m_fCanAnnotate = TRUE;
m_fAnnotating = FALSE;
}
else
{
if (m_fAnnotating)
{
m_ctlPreview.SetMode(CZoomWnd::MODE_NOACTION);
m_ctlToolbar.SendMessage(TB_SETSTATE, ID_ZOOMOUTCMD, TBSTATE_ENABLED);
m_ctlToolbar.SendMessage(TB_SETSTATE, ID_ZOOMINCMD, TBSTATE_ENABLED);
}
m_fCanAnnotate = FALSE;
m_fAnnotating = FALSE;
}
m_ctlToolbar.SendMessage(TB_HIDEBUTTON, ID_SELECTCMD, MAKELONG(!m_fCanAnnotate, 0));
m_ctlToolbar.SendMessage(TB_HIDEBUTTON, ID_ANNOTATESEP, MAKELONG(!m_fCanAnnotate, 0));
m_ctlToolbar.SendMessage(TB_HIDEBUTTON, ID_FREEHANDCMD, MAKELONG(!m_fCanAnnotate, 0));
m_ctlToolbar.SendMessage(TB_HIDEBUTTON, ID_HIGHLIGHTCMD, MAKELONG(!m_fCanAnnotate, 0));
m_ctlToolbar.SendMessage(TB_HIDEBUTTON, ID_LINECMD, MAKELONG(!m_fCanAnnotate, 0));
m_ctlToolbar.SendMessage(TB_HIDEBUTTON, ID_FRAMECMD, MAKELONG(!m_fCanAnnotate, 0));
m_ctlToolbar.SendMessage(TB_HIDEBUTTON, ID_RECTCMD, MAKELONG(!m_fCanAnnotate, 0));
m_ctlToolbar.SendMessage(TB_HIDEBUTTON, ID_TEXTCMD, MAKELONG(!m_fCanAnnotate, 0));
m_ctlToolbar.SendMessage(TB_HIDEBUTTON, ID_NOTECMD, MAKELONG(!m_fCanAnnotate, 0));
m_ctlToolbar.SendMessage(TB_HIDEBUTTON, ID_PROPERTIESCMD, MAKELONG(!m_fCanAnnotate, 0));
m_ctlToolbar.SendMessage(TB_ENABLEBUTTON, ID_SELECTCMD, MAKELONG(m_fCanAnnotate, 0));
m_ctlToolbar.SendMessage(TB_ENABLEBUTTON, ID_ANNOTATESEP, MAKELONG(m_fCanAnnotate, 0));
m_ctlToolbar.SendMessage(TB_ENABLEBUTTON, ID_FREEHANDCMD, MAKELONG(m_fCanAnnotate, 0));
m_ctlToolbar.SendMessage(TB_ENABLEBUTTON, ID_HIGHLIGHTCMD, MAKELONG(m_fCanAnnotate, 0));
m_ctlToolbar.SendMessage(TB_ENABLEBUTTON, ID_LINECMD, MAKELONG(m_fCanAnnotate, 0));
m_ctlToolbar.SendMessage(TB_ENABLEBUTTON, ID_FRAMECMD, MAKELONG(m_fCanAnnotate, 0));
m_ctlToolbar.SendMessage(TB_ENABLEBUTTON, ID_RECTCMD, MAKELONG(m_fCanAnnotate, 0));
m_ctlToolbar.SendMessage(TB_ENABLEBUTTON, ID_TEXTCMD, MAKELONG(m_fCanAnnotate, 0));
m_ctlToolbar.SendMessage(TB_ENABLEBUTTON, ID_NOTECMD, MAKELONG(m_fCanAnnotate, 0));
m_ctlToolbar.SendMessage(TB_ENABLEBUTTON, ID_PROPERTIESCMD, MAKELONG(FALSE, 0));
}
}
void CPreviewWnd::_SetCroppingCommands(BOOL fEnableCropping)
{
if (CONTROL_MODE != m_dwMode)
{
if (fEnableCropping)
{
m_fCanCrop = TRUE;
m_fCropping = FALSE;
}
else
{
if (m_fCropping)
{
m_ctlPreview.SetMode(CZoomWnd::MODE_NOACTION);
m_ctlToolbar.SendMessage(TB_SETSTATE, ID_ZOOMOUTCMD, TBSTATE_ENABLED);
m_ctlToolbar.SendMessage(TB_SETSTATE, ID_ZOOMINCMD, TBSTATE_ENABLED);
}
m_fCanCrop = FALSE;
m_fCropping = FALSE;
}
m_ctlToolbar.SendMessage(TB_HIDEBUTTON, ID_CROPCMD, MAKELONG(!m_fCanCrop, 0));
m_ctlToolbar.SendMessage(TB_ENABLEBUTTON, ID_CROPCMD, MAKELONG(m_fCanCrop, 0));
}
}
// Called on Toolbar command to fix the state of the other buttons.
void CPreviewWnd::_UpdateButtons(WORD wID)
{
if (CONTROL_MODE != m_dwMode)
{
switch (wID)
{
case NOBUTTON:
case ID_ZOOMINCMD:
case ID_ZOOMOUTCMD:
case ID_SELECTCMD:
case ID_CROPCMD:
m_ctlToolbar.SendMessage(TB_SETSTATE, ID_ZOOMINCMD, TBSTATE_ENABLED);
m_ctlToolbar.SendMessage(TB_SETSTATE, ID_ZOOMOUTCMD, TBSTATE_ENABLED);
if (m_fCanAnnotate)
{
m_wNewAnnotation = 0;
m_ctlToolbar.SendMessage(TB_SETSTATE, ID_SELECTCMD, TBSTATE_ENABLED);
m_ctlToolbar.SendMessage(TB_SETSTATE, ID_FREEHANDCMD, TBSTATE_ENABLED);
m_ctlToolbar.SendMessage(TB_SETSTATE, ID_HIGHLIGHTCMD, TBSTATE_ENABLED);
m_ctlToolbar.SendMessage(TB_SETSTATE, ID_LINECMD, TBSTATE_ENABLED);
m_ctlToolbar.SendMessage(TB_SETSTATE, ID_FRAMECMD, TBSTATE_ENABLED);
m_ctlToolbar.SendMessage(TB_SETSTATE, ID_RECTCMD, TBSTATE_ENABLED);
m_ctlToolbar.SendMessage(TB_SETSTATE, ID_TEXTCMD, TBSTATE_ENABLED);
m_ctlToolbar.SendMessage(TB_SETSTATE, ID_NOTECMD, TBSTATE_ENABLED);
m_fAnnotating = (wID == ID_SELECTCMD);
}
if (m_fCanCrop)
{
m_fCropping = (wID == ID_CROPCMD);
m_ctlToolbar.SendMessage(TB_SETSTATE, ID_CROPCMD, TBSTATE_ENABLED);
}
_RefreshSelection(!m_fAnnotating);
m_ctlToolbar.SendMessage(TB_SETSTATE, wID, TBSTATE_ENABLED|TBSTATE_CHECKED);
break;
case ID_FREEHANDCMD:
case ID_LINECMD:
case ID_FRAMECMD:
case ID_RECTCMD:
case ID_TEXTCMD:
case ID_NOTECMD:
case ID_HIGHLIGHTCMD:
if (m_fCanAnnotate)
{
m_ctlToolbar.SendMessage(TB_SETSTATE, ID_ZOOMINCMD, TBSTATE_ENABLED);
m_ctlToolbar.SendMessage(TB_SETSTATE, ID_ZOOMOUTCMD, TBSTATE_ENABLED);
m_ctlToolbar.SendMessage(TB_SETSTATE, ID_FREEHANDCMD, TBSTATE_ENABLED);
m_ctlToolbar.SendMessage(TB_SETSTATE, ID_HIGHLIGHTCMD, TBSTATE_ENABLED);
m_ctlToolbar.SendMessage(TB_SETSTATE, ID_LINECMD, TBSTATE_ENABLED);
m_ctlToolbar.SendMessage(TB_SETSTATE, ID_FRAMECMD, TBSTATE_ENABLED);
m_ctlToolbar.SendMessage(TB_SETSTATE, ID_RECTCMD, TBSTATE_ENABLED);
m_ctlToolbar.SendMessage(TB_SETSTATE, ID_TEXTCMD, TBSTATE_ENABLED);
m_ctlToolbar.SendMessage(TB_SETSTATE, ID_NOTECMD, TBSTATE_ENABLED);
m_ctlToolbar.SendMessage(TB_SETSTATE, ID_SELECTCMD, TBSTATE_ENABLED|TBSTATE_CHECKED);
m_fAnnotating = TRUE;
_RefreshSelection(TRUE);
m_ctlToolbar.SendMessage(TB_SETSTATE, wID, TBSTATE_ENABLED|TBSTATE_CHECKED);
m_wNewAnnotation = wID;
}
break;
default:
if (m_fCanAnnotate)
{
m_wNewAnnotation = 0;
m_ctlToolbar.SendMessage(TB_SETSTATE, ID_SELECTCMD, TBSTATE_ENABLED);
m_ctlToolbar.SendMessage(TB_SETSTATE, ID_FREEHANDCMD, TBSTATE_ENABLED);
m_ctlToolbar.SendMessage(TB_SETSTATE, ID_HIGHLIGHTCMD, TBSTATE_ENABLED);
m_ctlToolbar.SendMessage(TB_SETSTATE, ID_LINECMD, TBSTATE_ENABLED);
m_ctlToolbar.SendMessage(TB_SETSTATE, ID_FRAMECMD, TBSTATE_ENABLED);
m_ctlToolbar.SendMessage(TB_SETSTATE, ID_RECTCMD, TBSTATE_ENABLED);
m_ctlToolbar.SendMessage(TB_SETSTATE, ID_TEXTCMD, TBSTATE_ENABLED);
m_ctlToolbar.SendMessage(TB_SETSTATE, ID_NOTECMD, TBSTATE_ENABLED);
}
break;
}
}
}
void CPreviewWnd::_RefreshSelection(BOOL fDeselect)
{
if (m_fCropping)
_UpdateCroppingSelection();
_UpdateAnnotatingSelection(fDeselect);
}
BOOL CPreviewWnd::_ShouldDisplayAnimations()
{
return !::GetSystemMetrics(SM_REMOTESESSION);
}
void CPreviewWnd::_UpdateAnnotatingSelection(BOOL fDeselect)
{
BOOL bEditing = FALSE;
if (m_ctlEdit.m_hWnd != NULL)
{
if (m_ctlEdit.IsWindowVisible())
{
_HideEditing();
bEditing = TRUE;
}
}
if (DPA_GetPtrCount(m_hdpaSelectedAnnotations) > 0)
{
CRect rectUpdate;
CSelectionTracker tracker;
_SetupAnnotatingTracker(tracker, bEditing);
tracker.GetTrueRect(rectUpdate);
// If we were editing or this was a straight line, we
// need to get the bounding rect as well
if (DPA_GetPtrCount(m_hdpaSelectedAnnotations) == 1)
{
CRect rect;
CAnnotation* pAnnotation = (CAnnotation*)DPA_GetPtr(m_hdpaSelectedAnnotations, 0);
pAnnotation->GetRect(rect);
m_ctlPreview.GetWindowFromImage((LPPOINT)(LPRECT)rect, 2);
rectUpdate.UnionRect(rectUpdate, rect);
}
m_ctlPreview.InvalidateRect(&rectUpdate);
if (m_fAnnotating && !fDeselect)
{
if (bEditing)
_StartEditing(FALSE);
}
else
{
_StopEditing();
DPA_DeleteAllPtrs(m_hdpaSelectedAnnotations);
}
}
// Disable the properties button if there are 0 or 2 or more annotations selected
m_ctlToolbar.SendMessage(TB_ENABLEBUTTON, ID_PROPERTIESCMD, MAKELONG(DPA_GetPtrCount(m_hdpaSelectedAnnotations) == 1, 0));
}
void CPreviewWnd::_UpdateCroppingSelection()
{
if (m_fCropping)
{
m_ctlPreview.InvalidateRect(NULL);
}
}
void CPreviewWnd::_RemoveAnnotatingSelection()
{
// Invalidate current selection and remove annotations
_UpdateAnnotatingSelection();
CAnnotationSet* pAnnotations = m_ctlPreview.GetAnnotations();
for (int i = 0; i < DPA_GetPtrCount(m_hdpaSelectedAnnotations); i++)
{
CAnnotation* pAnnotation = (CAnnotation*)DPA_GetPtr(m_hdpaSelectedAnnotations, i);
pAnnotations->RemoveAnnotation(pAnnotation);
delete pAnnotation;
m_fDirty = TRUE;
}
m_ctlToolbar.SendMessage(TB_ENABLEBUTTON, ID_PROPERTIESCMD, MAKELONG(FALSE, 0));
DPA_DeleteAllPtrs(m_hdpaSelectedAnnotations);
}
void CPreviewWnd::_SetupAnnotatingTracker(CSelectionTracker& tracker, BOOL bEditing)
{
CRect rect;
rect.SetRectEmpty();
if (!bEditing)
{
if (m_ctlEdit.m_hWnd != NULL)
bEditing = m_ctlEdit.IsWindowVisible();
}
if (DPA_GetPtrCount(m_hdpaSelectedAnnotations) > 0)
{
CAnnotation* pAnnotation;
if (DPA_GetPtrCount(m_hdpaSelectedAnnotations) == 1)
{
pAnnotation = (CAnnotation*)DPA_GetPtr(m_hdpaSelectedAnnotations, 0);
// If this is a straight-line annotation then we need to get
// to the actual points rather than the bounding rect
if (pAnnotation->GetType() == MT_STRAIGHTLINE)
{
CLineMark* pLine = (CLineMark*)pAnnotation;
pLine->GetPointsRect(rect);
}
else
{
pAnnotation->GetRect(rect);
}
if (bEditing)
_RotateRect(rect, pAnnotation);
}
else
{
for (int i = 0; i < DPA_GetPtrCount(m_hdpaSelectedAnnotations); i++)
{
CRect rectAnnotation;
pAnnotation = (CAnnotation*)DPA_GetPtr(m_hdpaSelectedAnnotations, i);
pAnnotation->GetRect(rectAnnotation);
rectAnnotation.NormalizeRect();
rect.UnionRect(rect, rectAnnotation);
}
}
m_ctlPreview.GetWindowFromImage((LPPOINT)(LPRECT)rect, 2);
}
tracker.m_rect = rect;
UINT uStyle = 0;
if (DPA_GetPtrCount(m_hdpaSelectedAnnotations) > 1)
{
uStyle = CSelectionTracker::hatchedBorder;
}
else if (DPA_GetPtrCount(m_hdpaSelectedAnnotations) == 1)
{
CAnnotation* pAnnotation = (CAnnotation*)DPA_GetPtr(m_hdpaSelectedAnnotations, 0);
if (pAnnotation->CanResize())
{
if (pAnnotation->GetType() == MT_STRAIGHTLINE)
uStyle = CSelectionTracker::resizeOutside | CSelectionTracker::lineSelection;
else
uStyle = CSelectionTracker::solidLine | CSelectionTracker::resizeOutside;
}
else
{
uStyle = CSelectionTracker::hatchedBorder;
}
}
tracker.m_uStyle = uStyle;
}
void CPreviewWnd::_SetupCroppingTracker(CSelectionTracker& tracker)
{
if (m_fCropping)
{
CRect rect(0, 0, m_ctlPreview.m_cxImage, m_ctlPreview.m_cyImage);
if (m_rectCropping.IsRectEmpty())
m_rectCropping = rect;
rect = m_rectCropping;
m_ctlPreview.GetWindowFromImage((LPPOINT)(LPRECT)rect, 2);
tracker.m_rect = rect;
tracker.m_uStyle = CSelectionTracker::solidLine | CSelectionTracker::resizeOutside;
}
}
BOOL CPreviewWnd::_OnMouseDownForCropping(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
if (!m_fCropping)
return FALSE;
if (uMsg != WM_LBUTTONDOWN)
return TRUE;
CSelectionTracker tracker;
CPoint point(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
_SetupCroppingTracker(tracker);
_RefreshSelection();
if (tracker.HitTest(point) == CSelectionTracker::hitNothing)
return TRUE;
if (tracker.Track(m_ctlPreview.m_hWnd, point))
{
CRect rectNewPos;
tracker.GetTrueRect(rectNewPos);
m_ctlPreview.GetImageFromWindow((LPPOINT)(LPRECT)rectNewPos, 2);
CRect rectImage(0, 0, m_ctlPreview.m_cxImage, m_ctlPreview.m_cyImage);
if (rectNewPos.left < rectImage.left)
m_rectCropping.left = rectImage.left;
else
m_rectCropping.left = rectNewPos.left;
if (rectNewPos.top < rectImage.top)
m_rectCropping.top = rectImage.top;
else
m_rectCropping.top = rectNewPos.top;
if (rectNewPos.right > rectImage.right)
m_rectCropping.right = rectImage.right;
else
m_rectCropping.right = rectNewPos.right;
if (rectNewPos.bottom > rectImage.bottom)
m_rectCropping.bottom = rectImage.bottom;
else
m_rectCropping.bottom = rectNewPos.bottom;
m_fDirty = TRUE;
}
_RefreshSelection();
return TRUE;
}
BOOL CPreviewWnd::_OnMouseDownForAnnotating(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
if (!m_fAnnotating)
return FALSE;
if (uMsg != WM_LBUTTONDOWN)
return TRUE;
CRect rect;
CRect rectImage;
CPoint point(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
CSelectionTracker tracker;
m_ctlPreview.GetVisibleImageWindowRect(rectImage);
if (DPA_GetPtrCount(m_hdpaSelectedAnnotations) == 0)
{
_OnMouseDownForAnnotatingHelper(point, rectImage);
return TRUE;
}
_SetupAnnotatingTracker(tracker);
tracker.GetTrueRect(rect);
if (tracker.HitTest(point) == CSelectionTracker::hitNothing)
{
_RefreshSelection(TRUE);
_OnMouseDownForAnnotatingHelper(point, rectImage);
return TRUE;
}
if (!tracker.Track(m_ctlPreview.m_hWnd, point))
{
_StartEditing();
return TRUE;
}
CRect rectNewPos;
tracker.GetTrueRect(rectNewPos);
rect.BottomRight() = rectNewPos.TopLeft();
m_ctlPreview.GetImageFromWindow((LPPOINT)(LPRECT)rect, 2);
CSize size = rect.BottomRight() - rect.TopLeft();
_RefreshSelection();
if (DPA_GetPtrCount(m_hdpaSelectedAnnotations) > 1)
{
if (size.cx == 0 && size.cy == 0)
return TRUE;
m_ctlPreview.GetImageFromWindow((LPPOINT)(LPRECT)rectImage, 2);
rectImage.DeflateRect(5, 5);
BOOL bValidMove = TRUE;
for (int i = 0; i < DPA_GetPtrCount(m_hdpaSelectedAnnotations); i++)
{
CAnnotation* pAnnotation = (CAnnotation*)DPA_GetPtr(m_hdpaSelectedAnnotations, i);
pAnnotation->GetRect(rect);
rect.NormalizeRect();
rect.OffsetRect(size);
if (!rectNewPos.IntersectRect(rectImage, rect))
bValidMove = FALSE;
}
if (!bValidMove)
return TRUE;
for (int i = 0; i < DPA_GetPtrCount(m_hdpaSelectedAnnotations); i++)
{
CAnnotation* pAnnotation = (CAnnotation*)DPA_GetPtr(m_hdpaSelectedAnnotations, i);
pAnnotation->Move(size);
}
}
else
{
CAnnotation* pAnnotation = (CAnnotation*)DPA_GetPtr(m_hdpaSelectedAnnotations, 0);
if (pAnnotation->CanResize())
{
CRect rectTest;
rect = tracker.m_rect;
m_ctlPreview.GetImageFromWindow((LPPOINT)(LPRECT)rect, 2);
rectTest = rect;
// If the annotation being manipulated is a straight line then the rectangle
// returned from the tracker could be empty (ie left=right or top=bottom)
// In this case the IntersectRect test below would fail because windows
// assumes empty rectangle don't intersect anything.
if (pAnnotation->GetType() == MT_STRAIGHTLINE)
{
if (rectTest.left == rectTest.right)
rectTest.right++;
if (rectTest.top == rectTest.bottom)
rectTest.bottom++;
}
rectTest.NormalizeRect();
m_ctlPreview.GetImageFromWindow((LPPOINT)(LPRECT)rectImage, 2);
rectImage.DeflateRect(5, 5);
if (!rectTest.IntersectRect(rectImage, rectTest))
return TRUE;
if (m_ctlEdit.m_hWnd != NULL)
{
if (m_ctlEdit.IsWindowVisible())
{
_RotateRect(rect, pAnnotation);
}
}
// If this is a line then the rect is assumed to be
// a non-normalized array of points.
pAnnotation->Resize(rect);
}
else
{
if (size.cx == 0 && size.cy == 0)
return TRUE;
pAnnotation->Move(size);
}
}
m_fDirty = TRUE;
_RefreshSelection();
return TRUE;
}
void CPreviewWnd::_OnMouseDownForAnnotatingHelper(CPoint ptMouse, CRect rectImage)
{
CRect rect;
CSelectionTracker tracker;
_SetupAnnotatingTracker(tracker);
if (m_wNewAnnotation == ID_FREEHANDCMD)
{
_CreateFreeHandAnnotation(ptMouse);
return;
}
// If we are creating a line then make sure the tracker has the lineSelection
// style so we get the appropriate visual feedback.
if (m_wNewAnnotation == ID_LINECMD)
{
tracker.m_uStyle = CSelectionTracker::resizeOutside | CSelectionTracker::lineSelection;
}
if (tracker.TrackRubberBand(m_ctlPreview.m_hWnd, ptMouse, TRUE))
{
rect = tracker.m_rect;
rect.NormalizeRect();
if ((rect.Width() > 10) || (rect.Height() > 10))
{
if (m_wNewAnnotation != 0)
{
_CreateAnnotation(tracker.m_rect);
}
else
{
CRect rectTest;
CRect rectAnnotation;
CAnnotationSet* pAnnotations = m_ctlPreview.GetAnnotations();
m_ctlPreview.GetImageFromWindow((LPPOINT)(LPRECT)rect, 2);
INT_PTR nCount = pAnnotations->GetCount();
for (INT_PTR i = 0; i < nCount; i++)
{
CAnnotation* pAnnotation = pAnnotations->GetAnnotation(i);
pAnnotation->GetRect(rectAnnotation);
rectAnnotation.NormalizeRect();
rectTest.UnionRect(rect, rectAnnotation);
if (rectTest == rect)
{
DPA_AppendPtr(m_hdpaSelectedAnnotations, pAnnotation);
}
}
_RefreshSelection(DPA_GetPtrCount(m_hdpaSelectedAnnotations) == 0);
}
}
}
else
{
if (m_wNewAnnotation == 0)
{
if (PtInRect(rectImage, ptMouse))
{
m_ctlPreview.GetImageFromWindow(&ptMouse, 1);
CAnnotationSet* pAnnotations = m_ctlPreview.GetAnnotations();
INT_PTR nCount = pAnnotations->GetCount();
// if the user is clicking a single point then
// we need to search the annotations in zorder
// from top to bottom
for (INT_PTR i = nCount - 1; i >= 0; i--)
{
CAnnotation* pAnnotation = pAnnotations->GetAnnotation(i);
pAnnotation->GetRect(rect);
rect.NormalizeRect();
if (PtInRect(rect, ptMouse))
{
DPA_AppendPtr(m_hdpaSelectedAnnotations, pAnnotation);
_RefreshSelection();
return;
}
}
_RefreshSelection(DPA_GetPtrCount(m_hdpaSelectedAnnotations) == 0);
}
}
else
{
_UpdateButtons(ID_SELECTCMD);
}
}
}
void CPreviewWnd::_CreateAnnotation(CRect rect)
{
if (m_wNewAnnotation == 0 || m_wNewAnnotation == ID_FREEHANDCMD)
return;
ULONG xDPI;
ULONG yDPI;
if (!(m_pImageData->GetResolution(&xDPI, &yDPI)))
return;
CAnnotation* pAnnotation = NULL;
switch(m_wNewAnnotation)
{
case ID_LINECMD:
pAnnotation = CAnnotation::CreateAnnotation(MT_STRAIGHTLINE, yDPI);
break;
case ID_FRAMECMD:
pAnnotation = CAnnotation::CreateAnnotation(MT_HOLLOWRECT, yDPI);
break;
case ID_RECTCMD:
pAnnotation = CAnnotation::CreateAnnotation(MT_FILLRECT, yDPI);
break;
case ID_TEXTCMD:
pAnnotation = CAnnotation::CreateAnnotation(MT_TYPEDTEXT, yDPI);
break;
case ID_NOTECMD:
pAnnotation = CAnnotation::CreateAnnotation(MT_ATTACHANOTE, yDPI);
break;
case ID_HIGHLIGHTCMD:
pAnnotation = CAnnotation::CreateAnnotation(MT_FILLRECT, yDPI);
if (pAnnotation != NULL)
pAnnotation->SetTransparent(TRUE);
break;
}
if (pAnnotation != NULL)
{
COLORREF crBackColor = RGB(255,255,0);
COLORREF crLineColor = RGB(255,0,0);
COLORREF crTextColor = RGB(0,0,0);
LOGFONT lfFont = {12, 0, 0, 0, 400, 0, 0, 0, 0, 0, 0, 0, 0, TEXT("Arial") };
DWORD dwWidth = 1;
CRegKey Key;
if (ERROR_SUCCESS == Key.Open(HKEY_CURRENT_USER, REGSTR_SHIMGVW))
{
Key.QueryValue(dwWidth, REGSTR_LINEWIDTH);
Key.QueryValue(crBackColor, REGSTR_BACKCOLOR);
Key.QueryValue(crLineColor, REGSTR_LINECOLOR);
Key.QueryValue(crTextColor, REGSTR_TEXTCOLOR);
DWORD dwType, cbSize;
cbSize = sizeof(lfFont);
::RegQueryValueEx(Key, REGSTR_FONT, NULL, &dwType, (LPBYTE)&lfFont, &cbSize);
}
if (m_wNewAnnotation != ID_LINECMD)
rect.NormalizeRect();
m_ctlPreview.GetImageFromWindow((LPPOINT)(LPRECT)rect, 2);
pAnnotation->Resize(rect);
if (pAnnotation->HasWidth())
pAnnotation->SetWidth(dwWidth);
if (pAnnotation->HasColor())
{
if (m_wNewAnnotation == ID_LINECMD || m_wNewAnnotation == ID_FRAMECMD)
pAnnotation->SetColor(crLineColor);
else
pAnnotation->SetColor(crBackColor);
}
if (pAnnotation->HasFont())
{
pAnnotation->SetFont(lfFont);
pAnnotation->SetFontColor(crTextColor);
}
DPA_DeleteAllPtrs(m_hdpaSelectedAnnotations);
DPA_AppendPtr(m_hdpaSelectedAnnotations, pAnnotation);
CAnnotationSet* pAnnotations = m_ctlPreview.GetAnnotations();
pAnnotations->AddAnnotation(pAnnotation);
m_fDirty = TRUE;
}
_UpdateButtons(ID_SELECTCMD);
}
void CPreviewWnd::_CreateFreeHandAnnotation(CPoint ptMouse)
{
if (m_wNewAnnotation != ID_FREEHANDCMD)
return;
// don't handle if capture already set
if (::GetCapture() != NULL)
return;
// set capture to the window which received this message
::SetCapture(m_ctlPreview.m_hWnd);
ASSERT(m_ctlPreview.m_hWnd == ::GetCapture());
::UpdateWindow(m_ctlPreview.m_hWnd);
ULONG xDPI;
ULONG yDPI;
if (!(m_pImageData->GetResolution(&xDPI, &yDPI)))
return;
CLineMark* pAnnotation = (CLineMark*)CAnnotation::CreateAnnotation(MT_FREEHANDLINE, yDPI);
if (pAnnotation == NULL)
return;
CDSA<POINT> Points;
Points.Create(256);
CPoint ptLast = ptMouse;
m_ctlPreview.GetImageFromWindow(&ptMouse, 1);
Points.AppendItem(&ptMouse);
// get DC for drawing
HDC hdcDraw;
// otherwise, just use normal DC
hdcDraw = ::GetDC(m_ctlPreview.m_hWnd);
ASSERT(hdcDraw != NULL);
COLORREF crLineColor = RGB(255,0,0);
DWORD dwWidth = 1;
CRegKey Key;
if (ERROR_SUCCESS == Key.Open(HKEY_CURRENT_USER, REGSTR_SHIMGVW))
{
Key.QueryValue(dwWidth, REGSTR_LINEWIDTH);
Key.QueryValue(crLineColor, REGSTR_LINECOLOR);
}
CRect rect(0,0,0,dwWidth);
m_ctlPreview.GetWindowFromImage((LPPOINT)(LPRECT)rect, 2);
DWORD dwRenderWidth = rect.Height();
HPEN hpen = ::CreatePen(PS_SOLID, dwRenderWidth, crLineColor);
HPEN hOld =(HPEN)::SelectObject(hdcDraw, hpen);
BOOL bCancel=FALSE;
// get messages until capture lost or cancelled/accepted
for (;;)
{
MSG msg;
if (!::GetMessage(&msg, NULL, 0, 0))
{
ASSERT(FALSE);
}
if (m_ctlPreview.m_hWnd != ::GetCapture())
{
bCancel = TRUE;
goto ExitLoop;
}
ptMouse.x = GET_X_LPARAM(msg.lParam);
ptMouse.y = GET_Y_LPARAM(msg.lParam);
switch (msg.message)
{
// handle movement/accept messages
case WM_LBUTTONUP:
case WM_MOUSEMOVE:
::MoveToEx(hdcDraw, ptLast.x, ptLast.y, NULL);
::LineTo(hdcDraw, ptMouse.x, ptMouse.y);
ptLast = ptMouse;
m_ctlPreview.GetImageFromWindow(&ptMouse, 1);
Points.AppendItem(&ptMouse);
if (msg.message == WM_LBUTTONUP)
goto ExitLoop;
break;
// handle cancel messages
case WM_KEYDOWN:
if (msg.wParam != VK_ESCAPE)
break;
// else fall through
case WM_RBUTTONDOWN:
bCancel = TRUE;
goto ExitLoop;
default:
::DispatchMessage(&msg);
break;
}
}
ExitLoop:
::SelectObject(hdcDraw, hOld);
::DeleteObject(hpen);
::ReleaseDC(m_ctlPreview.m_hWnd, hdcDraw);
::ReleaseCapture();
if (!bCancel)
{
int nAnnoPoints = Points.GetItemCount();
POINT* AnnoPoints = new POINT[nAnnoPoints];
if (AnnoPoints == NULL)
{
delete pAnnotation;
Points.Destroy();
_UpdateButtons(ID_SELECTCMD);
return;
}
for (int i = 0; i < nAnnoPoints; i++)
{
CPoint pt;
Points.GetItem(i, &pt);
AnnoPoints[i].x = pt.x;
AnnoPoints[i].y = pt.y;
}
Points.Destroy();
pAnnotation->SetPoints(AnnoPoints, nAnnoPoints);
pAnnotation->SetWidth(dwWidth);
pAnnotation->SetColor(crLineColor);
DPA_DeleteAllPtrs(m_hdpaSelectedAnnotations);
DPA_AppendPtr(m_hdpaSelectedAnnotations, pAnnotation);
CAnnotationSet* pAnnotations = m_ctlPreview.GetAnnotations();
pAnnotations->AddAnnotation(pAnnotation);
m_fDirty = TRUE;
}
_UpdateButtons(ID_SELECTCMD);
}
void CPreviewWnd::_StartEditing(BOOL bUpdateText)
{
if (DPA_GetPtrCount(m_hdpaSelectedAnnotations) != 1)
return;
CTextAnnotation* pAnnotation = (CTextAnnotation*)DPA_GetPtr(m_hdpaSelectedAnnotations, 0);
if (!pAnnotation)
{
return;
}
UINT uType = pAnnotation->GetType();
if (uType != MT_TYPEDTEXT && uType != MT_FILETEXT && uType != MT_STAMP && uType != MT_ATTACHANOTE)
return;
if (m_ctlEdit.m_hWnd == NULL)
{
HWND hwndEdit = ::CreateWindow(TEXT("EDIT"), NULL, ES_LEFT | ES_MULTILINE | ES_AUTOVSCROLL |
ES_WANTRETURN | WS_CHILD, 1, 1, 10, 10,
m_ctlPreview.m_hWnd, (HMENU)1496, NULL, NULL);
if (hwndEdit == NULL)
return;
m_ctlEdit.SubclassWindow(hwndEdit);
}
if (bUpdateText)
{
CComBSTR bstrText;
bstrText.Attach(pAnnotation->GetText());
if (bstrText.m_str != NULL)
m_ctlEdit.SetWindowText(bstrText);
else
m_ctlEdit.SetWindowText(TEXT(""));
}
m_ctlEdit.EnableWindow(TRUE);
LOGFONT lfFont;
pAnnotation->GetFont(lfFont);
HDC hdc = ::GetDC(NULL);
LONG lHeight = pAnnotation->GetFontHeight(hdc);
::ReleaseDC(NULL, hdc);
CRect rect(0,0,0,lHeight);
m_ctlPreview.GetWindowFromImage((LPPOINT)(LPRECT)rect, 2);
lfFont.lfHeight = -rect.Height();
HFONT hNewFont = ::CreateFontIndirect(&lfFont);
if (hNewFont)
{
::DeleteObject(m_hFont);
m_hFont = hNewFont;
m_ctlEdit.SendMessage(WM_SETFONT, (WPARAM)m_hFont, MAKELPARAM(TRUE,0));
}
pAnnotation->GetRect(rect);
_RotateRect(rect, pAnnotation);
m_ctlPreview.GetWindowFromImage((LPPOINT)(LPRECT)rect, 2);
m_ctlEdit.SetWindowPos(NULL, rect.left, rect.top, rect.Width(), rect.Height(), SWP_NOZORDER);
CSelectionTracker tracker;
_SetupAnnotatingTracker(tracker, FALSE);
CRect rectUpdate;
tracker.GetTrueRect(rectUpdate);
m_ctlPreview.InvalidateRect(rectUpdate);
_SetupAnnotatingTracker(tracker, TRUE);
tracker.GetTrueRect(rectUpdate);
m_ctlPreview.InvalidateRect(rectUpdate);
m_ctlEdit.ShowWindow(SW_SHOW);
m_ctlEdit.SetFocus();
m_fEditingAnnotation = TRUE;
}
void CPreviewWnd::_HideEditing()
{
if (m_ctlEdit.m_hWnd == NULL)
return;
if (!m_ctlEdit.IsWindowVisible())
return;
SetFocus();
m_ctlEdit.SetWindowPos(NULL, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_HIDEWINDOW);
m_ctlEdit.EnableWindow(FALSE);
}
void CPreviewWnd::_StopEditing()
{
if (m_ctlEdit.m_hWnd == NULL)
return;
_HideEditing();
if (!m_fEditingAnnotation)
return;
m_fEditingAnnotation = FALSE;
if (DPA_GetPtrCount(m_hdpaSelectedAnnotations) != 1)
return;
CTextAnnotation* pAnnotation = (CTextAnnotation*)DPA_GetPtr(m_hdpaSelectedAnnotations, 0);
UINT uType = pAnnotation->GetType();
if (uType != MT_TYPEDTEXT && uType != MT_FILETEXT && uType != MT_STAMP && uType != MT_ATTACHANOTE)
return;
// if the length greater than zero we save it
// otherwise be blow away the annotation.
int nLen = m_ctlEdit.GetWindowTextLength();
if (nLen > 0)
{
CComBSTR bstrText(nLen+1);
m_ctlEdit.GetWindowText(bstrText, nLen+1);
pAnnotation->SetText(bstrText);
m_fDirty = TRUE;
}
else
{
CSelectionTracker tracker;
_SetupAnnotatingTracker(tracker, TRUE);
CRect rectUpdate;
tracker.GetTrueRect(rectUpdate);
CRect rect;
pAnnotation->GetRect(rect);
rectUpdate.UnionRect(rectUpdate, rect);
DPA_DeleteAllPtrs(m_hdpaSelectedAnnotations);
CAnnotationSet* pAnnotations = m_ctlPreview.GetAnnotations();
pAnnotations->RemoveAnnotation(pAnnotation);
delete pAnnotation;
m_ctlPreview.InvalidateRect(rectUpdate);
m_fDirty = TRUE;
}
}
LRESULT CPreviewWnd::OnEditKeyEvent(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& fHandled)
{
switch (wParam)
{
case VK_ESCAPE:
{
CSelectionTracker tracker;
_SetupAnnotatingTracker(tracker);
CRect rectUpdate;
tracker.GetTrueRect(rectUpdate);
_HideEditing();
m_ctlPreview.InvalidateRect(rectUpdate);
_RefreshSelection();
fHandled = TRUE;
}
break;
default:
fHandled = FALSE;
break;
}
return 0;
}
BOOL_PTR CALLBACK CPreviewWnd::_AnnoPropsDlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
static LOGFONT lfFont;
static COLORREF crFont;
static COLORREF crColor;
CPreviewWnd* pThis;
switch (msg)
{
case WM_INITDIALOG:
{
HWND hwndCtl = NULL;
::SetWindowLongPtr(hwnd, DWLP_USER, lParam);
pThis = (CPreviewWnd*)lParam;
CAnnotation* pAnnotation = (CAnnotation*)DPA_GetPtr(pThis->m_hdpaSelectedAnnotations, 0);
hwndCtl = ::GetDlgItem(hwnd, IDC_WIDTHTEXT);
if (!pAnnotation->HasWidth())
{
::EnableWindow(hwndCtl, FALSE);
::ShowWindow(hwndCtl, SW_HIDE);
}
hwndCtl = ::GetDlgItem(hwnd, IDC_WIDTH);
if (pAnnotation->HasWidth())
{
UINT i = pAnnotation->GetWidth();
::SetDlgItemInt(hwnd, IDC_WIDTH, i, FALSE);
hwndCtl = ::GetDlgItem(hwnd, IDC_SPIN);
::SendMessage(hwndCtl, UDM_SETRANGE32, (WPARAM)1, (LPARAM)50);
::SendMessage(hwndCtl, UDM_SETPOS32, 0, (LPARAM)i);
}
else
{
::EnableWindow(hwndCtl, FALSE);
::ShowWindow(hwndCtl, SW_HIDE);
hwndCtl = ::GetDlgItem(hwnd, IDC_SPIN);
::EnableWindow(hwndCtl, FALSE);
::ShowWindow(hwndCtl, SW_HIDE);
}
hwndCtl = ::GetDlgItem(hwnd, IDC_TRANSPARENT);
if (pAnnotation->HasTransparent())
{
BOOL bTransparent = pAnnotation->GetTransparent();
::SendMessage(hwndCtl, BM_SETCHECK, (WPARAM)(bTransparent ? BST_CHECKED : BST_UNCHECKED), 0);
}
else
{
::EnableWindow(hwndCtl, FALSE);
::ShowWindow(hwndCtl, SW_HIDE);
}
if (pAnnotation->HasFont())
{
pAnnotation->GetFont(lfFont);
crFont = pAnnotation->GetFontColor();
}
else
{
hwndCtl = ::GetDlgItem(hwnd, IDC_FONT);
::EnableWindow(hwndCtl, FALSE);
::ShowWindow(hwndCtl, SW_HIDE);
}
if (pAnnotation->HasColor())
{
crColor = pAnnotation->GetColor();
}
else
{
hwndCtl = ::GetDlgItem(hwnd, IDC_COLOR);
::EnableWindow(hwndCtl, FALSE);
::ShowWindow(hwndCtl, SW_HIDE);
}
}
break;
case WM_COMMAND:
pThis = (CPreviewWnd*)::GetWindowLongPtr(hwnd, DWLP_USER);
switch (wParam)
{
case IDOK:
pThis->_RefreshSelection();
{
HWND hwndCtl = NULL;
CAnnotation* pAnnotation = (CAnnotation*)DPA_GetPtr(pThis->m_hdpaSelectedAnnotations, 0);
CRegKey Key;
if (ERROR_SUCCESS != Key.Open(HKEY_CURRENT_USER, REGSTR_SHIMGVW))
{
Key.Create(HKEY_CURRENT_USER, REGSTR_SHIMGVW);
}
if (pAnnotation->HasWidth())
{
UINT uWidth = ::GetDlgItemInt(hwnd, IDC_WIDTH, NULL, FALSE);
if (uWidth > 50 || uWidth < 1)
{
CComBSTR bstrMsg, bstrTitle;
if (bstrMsg.LoadString(IDS_WIDTHBAD_MSGBOX) && bstrTitle.LoadString(IDS_PROJNAME))
{
::MessageBox(hwnd, bstrMsg, bstrTitle, MB_OK | MB_ICONERROR | MB_APPLMODAL);
}
::SetDlgItemInt(hwnd, IDC_WIDTH, 50, FALSE);
return FALSE;
}
pAnnotation->SetWidth(uWidth);
if (Key.m_hKey != NULL)
{
Key.SetValue(uWidth, REGSTR_LINEWIDTH);
}
}
if (pAnnotation->HasTransparent())
{
hwndCtl = ::GetDlgItem(hwnd, IDC_TRANSPARENT);
BOOL bTransparent = FALSE;
if (::SendMessage(hwndCtl, BM_GETCHECK, 0, 0) == BST_CHECKED)
bTransparent = TRUE;
pAnnotation->SetTransparent(bTransparent);
}
if (pAnnotation->HasFont())
{
lfFont.lfHeight = (lfFont.lfHeight > 0) ? lfFont.lfHeight : -lfFont.lfHeight;
pAnnotation->SetFont(lfFont);
pAnnotation->SetFontColor(crFont);
if (Key.m_hKey != NULL)
{
Key.SetValue(crFont, REGSTR_TEXTCOLOR);
::RegSetValueEx(Key, REGSTR_FONT, 0, REG_BINARY, (LPBYTE)&lfFont, sizeof(lfFont));
}
}
if (pAnnotation->HasColor())
{
pAnnotation->SetColor(crColor);
UINT uType = pAnnotation->GetType();
if (Key.m_hKey != NULL)
{
if (uType == MT_STRAIGHTLINE || uType == MT_FREEHANDLINE || uType == MT_HOLLOWRECT)
Key.SetValue(crColor, REGSTR_LINECOLOR);
else
Key.SetValue(crColor, REGSTR_BACKCOLOR);
}
}
}
pThis->m_fDirty = TRUE;
pThis->_RefreshSelection();
EndDialog(hwnd, wParam);
return FALSE;
case IDCANCEL:
EndDialog(hwnd, wParam);
return FALSE;
case IDC_FONT:
{
CHOOSEFONT cf = {0};
LOGFONT lf;
lf = lfFont;
cf.lStructSize = sizeof(cf);
cf.hwndOwner = hwnd;
cf.lpLogFont = &lf;
cf.Flags = CF_SCREENFONTS | CF_EFFECTS | CF_INITTOLOGFONTSTRUCT | CF_NOVERTFONTS | CF_NOSCRIPTSEL;
cf.rgbColors = crFont;
if (::ChooseFont(&cf))
{
CopyMemory (&lfFont, &lf, sizeof(lfFont));
crFont = cf.rgbColors;
}
}
return FALSE;
case IDC_COLOR:
{
CHOOSECOLOR cc = {0};
cc.lStructSize = sizeof(cc);
cc.hwndOwner = hwnd;
cc.rgbResult = crColor;
cc.lpCustColors = g_crCustomColors;
cc.Flags = CC_RGBINIT | CC_SOLIDCOLOR;
if (::ChooseColor(&cc))
{
crColor = cc.rgbResult;
}
}
return FALSE;
default:
break;
}
break;
default:
return FALSE;
}
return TRUE;
}
BOOL CPreviewWnd::_TrySetImage()
{
BOOL fRet = FALSE;
if (m_pNextImageData && m_pNextImageData->_iItem == m_iCurSlide)
{
if (SUCCEEDED(m_pNextImageData->_hr))
{
m_fCanSave = !m_pNextImageData->_fIsReadOnly;
// update the toolbar state, our child windows, and our sibling windows
_SetNewImage(m_pNextImageData);
ATOMICRELEASE(m_pNextImageData);
if (m_pImageData->IsAnimated() && _ShouldDisplayAnimations())
{
// start the animation timer
SetTimer(TIMER_ANIMATION, m_pImageData->GetDelay());
}
// Notify anyone listening to our events that a preview has been completed
// we only fire this upon success
if (m_pEvents)
{
m_pEvents->OnPreviewReady();
}
fRet = TRUE;
}
else
{
// update the status to display an error message. This will also update the toolbar state.
StatusUpdate(IDS_LOADFAILED);
//
// We can't remove the item from the array because the user might try to delete it while
// the "load failed" string is still visible for it.
// even though the item failed to decode we must wait on the "Load Failed" state when we are in
// windowed mode, otherwise "open with..." is broken when you open a corrupted image or non-image.
// In slideshow mode we could simply skip to the next image.
if (m_pEvents)
m_pEvents->OnError();
}
}
return fRet;
}
LRESULT CPreviewWnd::IV_OnSetImageData(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& fHandled)
{
CDecodeTask * pData = (CDecodeTask *)wParam;
ATOMICRELEASE(m_pNextImageData);
m_pNextImageData = pData;
if (m_pNextImageData && m_iDecodingNextImage == m_pNextImageData->_iItem)
{
// We have finished decoding now, let's remember this.
m_iDecodingNextImage = -1;
// Let 's prepare the drawing now. This draws in the back buffer. Don't start this if we want to see
// the image now, as it would delay things.
if (SUCCEEDED(m_pNextImageData->_hr) && m_pNextImageData->_iItem != m_iCurSlide)
{
m_ctlPreview.PrepareImageData(m_pNextImageData);
}
}
_TrySetImage();
return TRUE;
}
// Creation of the image data is asynchronous. When our worker thread is done decoding
// an image it posts a IV_SETIMAGEDATA message with the image data object. As a result,
// we must flush these messages when the window is destroyed to prevent leaking any handles.
void CPreviewWnd::FlushBitmapMessages()
{
// Pass TRUE to wait for task to be removed before peeking out its messages
// Otherwise, if the task is in the middle of running, our PeekMessage won't
// see anything and we will return. Then the task will finish, post its message,
// and leak the data since we're not around to receive it.
TASKOWNERID toid;
GetTaskIDFromMode(GTIDFM_DECODE, m_dwMode, &toid);
if (m_pTaskScheduler)
{
m_pTaskScheduler->RemoveTasks(toid, ITSAT_DEFAULT_LPARAM, TRUE);
}
// if we were waiting for another image frame to be generated then cut it out, we don't care about that anymore
// if we have an animation timer running then kill it and remove any WM_TIMER messages
KillTimer(TIMER_ANIMATION);
KillTimer(TIMER_SLIDESHOW);
MSG msg;
while (PeekMessage(&msg, m_hWnd, WM_TIMER, WM_TIMER, PM_REMOVE))
{
// NTRAID#NTBUG9-359356-2001/04/05-seank
// If the queue is empty when PeekMessage is called and we have already
// Posted a quit message then PeekMessage will return a WM_QUIT message
// regardless of the filter min and max and subsequent calls to
// GetMessage will hang indefinitely see SEANK or JASONSCH for more
// info.
if (msg.message == WM_QUIT)
{
PostQuitMessage(0);
return;
}
}
// make sure any posted messages get flushed and we free the associated data
while (PeekMessage(&msg, m_hWnd, IV_SETIMAGEDATA, IV_SETIMAGEDATA, PM_REMOVE))
{
// NTRAID#NTBUG9-359356-2001/04/05-seank
// If the queue is empty when PeekMessage is called and we have already
// Posted a quit message then PeekMessage will return a WM_QUIT message
// regardless of the filter min and max and subsequent calls to
// GetMessage will hang indefinitely see SEANK or JASONSCH for more
// info.
if (msg.message == WM_QUIT)
{
PostQuitMessage(0);
return;
}
CDecodeTask * pData = (CDecodeTask *)msg.wParam;
ATOMICRELEASE(pData);
}
}
LRESULT CPreviewWnd::OnCopyData(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL&)
{
// We can get into a situation where we are still trying to preview
// the previous oncopydata because the previous window to that was a
// tiff that was being annotated and is prompting you to save. In this
// case throw away any future data
if (_pdtobj != NULL || m_fPromptingUser)
return TRUE;
COPYDATASTRUCT *pcds = (COPYDATASTRUCT*)lParam;
if (pcds)
{
HRESULT hr = E_FAIL;
switch (pcds->dwData)
{
case COPYDATATYPE_DATAOBJECT:
{
IStream *pstm;
if (SUCCEEDED(CreateStreamOnHGlobal(NULL, TRUE, &pstm)))
{
const LARGE_INTEGER li = {0, 0};
pstm->Write(pcds->lpData, pcds->cbData, NULL);
pstm->Seek(li, STREAM_SEEK_SET, NULL);
// unfortunaly we can not program the data object here as we are in a
// SendMessage() and any calls made on the data object will fail because
// of this. instead we grab a ref to the data object and set a timer
// so we can handle this once we have unwound from the send.
hr = CoUnmarshalInterface(pstm, IID_PPV_ARG(IDataObject, &_pdtobj));
pstm->Release();
}
}
break;
case COPYDATATYPE_FILENAME:
{
hr = GetUIObjectFromPath((LPCTSTR)pcds->lpData, IID_PPV_ARG(IDataObject, &_pdtobj));
}
break;
}
// unfortunaly we can not program the data object here as we are in a
// SendMessage() and any calls made on the data object will fail because
// of this. instead we grab a ref to the data object and set a timer
// so we can handle this once we have unwound from the send.
if (SUCCEEDED(hr))
{
SetTimer(TIMER_DATAOBJECT, 100); // do the real work here
}
}
return TRUE;
}
DWORD MakeFilterFromCodecs(LPTSTR szFilter, size_t cbFilter, UINT nCodecs, ImageCodecInfo *pCodecs, LPTSTR szExt, BOOL fExcludeTiff)
{
size_t nOffset = 0;
DWORD dwRet = 1;
for (UINT i = 0; i < nCodecs && nOffset < cbFilter - 1; i++)
{
if (fExcludeTiff && StrStrI(pCodecs->FilenameExtension, L"*.tif"))
{
continue;
}
// make sure there's space for nulls between strings and 2 at the end
if (4+lstrlen(pCodecs->FormatDescription) + lstrlen(pCodecs->FilenameExtension) + nOffset < cbFilter)
{
StrCpyN(szFilter+nOffset,pCodecs->FormatDescription, cbFilter -(nOffset + 1));
nOffset+=lstrlen(pCodecs->FormatDescription)+1;
StrCpyN(szFilter+nOffset,pCodecs->FilenameExtension, cbFilter -(nOffset + 1));
nOffset+=lstrlen(pCodecs->FilenameExtension)+1;
if (StrStrI(pCodecs->FilenameExtension, szExt))
{
dwRet = i + 1;
}
pCodecs++;
}
}
szFilter[nOffset] = 0;
return dwRet;
}
DWORD CPreviewWnd::_GetFilterStringForSave(LPTSTR szFilter, size_t cbFilter, LPTSTR szExt)
{
UINT nCodecs = 0;
UINT cbCodecs = 0;
BYTE *pData;
GetImageEncodersSize (&nCodecs, &cbCodecs);
DWORD dwRet = 1; // ofn.nFilterIndex is 1-based
if (cbCodecs)
{
pData = new BYTE[cbCodecs];
if (pData)
{
ImageCodecInfo *pCodecs = reinterpret_cast<ImageCodecInfo*>(pData);
if (Ok == GetImageEncoders (nCodecs, cbCodecs, pCodecs))
{
dwRet = MakeFilterFromCodecs(szFilter, cbFilter, nCodecs, pCodecs, szExt, m_pImageData->IsExtendedPixelFmt());
}
delete [] pData;
}
}
return dwRet;
}
HRESULT CPreviewWnd::SaveAs(BSTR bstrPath)
{
HRESULT hr = E_FAIL;
if (m_pImageData && m_pImageFactory)
{
IShellImageData * pSID;
hr = m_pImageData->Lock(&pSID);
if (SUCCEEDED(hr))
{
GUID guidFmt;
if (SUCCEEDED(m_pImageFactory->GetDataFormatFromPath(bstrPath, &guidFmt)))
{
IPropertyBag *pbagEnc;
hr = SHCreatePropertyBagOnMemory(STGM_READWRITE, IID_PPV_ARG(IPropertyBag, &pbagEnc));
if (SUCCEEDED(hr))
{
VARIANT var;
hr = InitVariantFromGUID(&var, guidFmt);
if (SUCCEEDED(hr))
{
hr = pbagEnc->Write(SHIMGKEY_RAWFORMAT, &var);
if (SUCCEEDED(hr))
{
hr = pSID->SetEncoderParams(pbagEnc);
}
VariantClear(&var);
}
pbagEnc->Release();
}
}
IPersistFile *ppf;
hr = pSID->QueryInterface(IID_PPV_ARG(IPersistFile, &ppf));
if (SUCCEEDED(hr))
{
hr = ppf->Save(bstrPath, TRUE);
ppf->Release();
}
m_pImageData->Unlock();
}
}
return hr;
}
BOOL CPreviewWnd::_IsImageFile(LPCTSTR pszFile)
{
BOOL bRet = FALSE;
if (m_pici || _BuildDecoderList())
{
bRet = (-1 != FindInDecoderList(m_pici, m_cDecoders, pszFile));
}
return bRet;
}
BOOL CPreviewWnd::_BuildDecoderList()
{
UINT cb;
BOOL bRet = FALSE;
if (Ok == GetImageDecodersSize(&m_cDecoders, &cb))
{
m_pici = (ImageCodecInfo*)LocalAlloc(LPTR, cb);
if (m_pici)
{
if (Ok != GetImageDecoders(m_cDecoders, cb, m_pici))
{
LocalFree(m_pici);
m_pici = NULL;
}
else
{
bRet = TRUE;
}
}
}
return bRet;
}
void CPreviewWnd::OpenFileList(HWND hwnd, IDataObject *pdtobj)
{
if (NULL == hwnd)
hwnd = m_hWnd;
IStream *pstm;
HRESULT hr = CreateStreamOnHGlobal(NULL, TRUE, &pstm);
if (SUCCEEDED(hr))
{
hr = CoMarshalInterface(pstm, IID_IDataObject, pdtobj, MSHCTX_NOSHAREDMEM, NULL, MSHLFLAGS_NORMAL);
if (SUCCEEDED(hr))
{
HGLOBAL hGlobal;
hr = GetHGlobalFromStream(pstm, &hGlobal);
if (SUCCEEDED(hr))
{
COPYDATASTRUCT cds = {0};
cds.dwData = COPYDATATYPE_DATAOBJECT;
cds.cbData = (DWORD)GlobalSize(hGlobal);
cds.lpData = GlobalLock(hGlobal);
SendMessage(hwnd, WM_COPYDATA, NULL, (LPARAM)&cds);
SetForegroundWindow(hwnd);
GlobalUnlock(hGlobal);
}
}
pstm->Release();
}
}
void CPreviewWnd::OpenFile(HWND hwnd, LPCTSTR pszFile)
{
if (NULL == hwnd)
hwnd = m_hWnd;
COPYDATASTRUCT cds = {0};
cds.dwData = COPYDATATYPE_FILENAME;
cds.cbData = (lstrlen(pszFile)+1)*sizeof(TCHAR);
cds.lpData = (void*)pszFile;
SendMessage(hwnd, WM_COPYDATA, NULL, (LPARAM)&cds);
SetForegroundWindow(hwnd);
}
// returns:
// TRUE window was re-used
BOOL CPreviewWnd::TryWindowReuse(IDataObject *pdtobj)
{
BOOL bRet = FALSE;
HWND hwnd = FindWindow(TEXT("ShImgVw:CPreviewWnd"), NULL);
if (hwnd)
{
// window reuse can't always work because shortcuts are launched on a thread that
// is too short lived to support the marshalled IDataObject given to us via WM_COPYDATA
// For now we'll try to close an existing window and open a new one.
::PostMessage(hwnd, WM_CLOSE, 0, 0);
}
return bRet;
}
// returns:
// TRUE window was re-used
BOOL CPreviewWnd::TryWindowReuse(LPCTSTR pszFileName)
{
BOOL bRet = FALSE;
HWND hwnd = FindWindow(TEXT("ShImgVw:CPreviewWnd"), NULL);
if (hwnd)
{
DWORD_PTR dwResult = FALSE;
SendMessageTimeout(hwnd, IV_ISAVAILABLE, 0, 0, SMTO_ABORTIFHUNG | SMTO_BLOCK, 1000, &dwResult);
if (dwResult)
{
OpenFile(hwnd, pszFileName);
bRet = TRUE;
}
}
return bRet;
}
STDMETHODIMP CPreviewWnd::QueryInterface(REFIID riid, void **ppv)
{
static const QITAB qit[] =
{
QITABENT(CPreviewWnd, IDropTarget),
QITABENT(CPreviewWnd, INamespaceWalkCB),
QITABENT(CPreviewWnd, IServiceProvider),
QITABENT(CPreviewWnd, IImgCmdTarget),
{ 0 },
};
return QISearch(this, qit, riid, ppv);
}
STDMETHODIMP_(ULONG) CPreviewWnd::AddRef()
{
return 3;
}
STDMETHODIMP_(ULONG) CPreviewWnd::Release()
{
return 2;
}
// INamespaceWalkCB
STDMETHODIMP CPreviewWnd::FoundItem(IShellFolder *psf, LPCITEMIDLIST pidl)
{
HRESULT hr = S_FALSE;
if (m_fFirstItem && (WINDOW_MODE == m_dwMode))
{
// REVIEW: Do this in other modes too?
StatusUpdate(IDS_LOADING);
m_fFirstItem = FALSE;
hr = S_OK;
}
else
{
TCHAR szName[MAX_PATH];
DisplayNameOf(psf, pidl, SHGDN_FORPARSING | SHGDN_INFOLDER, szName, ARRAYSIZE(szName));
if (_IsImageFile(szName))
{
hr = S_OK;
}
}
if (WINDOW_MODE == m_dwMode)
{
MSG msg;
while (PeekMessage(&msg, m_hWnd, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return hr;
}
STDMETHODIMP CPreviewWnd::EnterFolder(IShellFolder *psf, LPCITEMIDLIST pidl)
{
return S_OK;
}
STDMETHODIMP CPreviewWnd::LeaveFolder(IShellFolder *psf, LPCITEMIDLIST pidl)
{
return S_OK;
}
// IDropTarget
STDMETHODIMP CPreviewWnd::DragEnter(IDataObject *pdtobj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect)
{
m_dwEffect = DROPEFFECT_NONE;
//
// We only support CFSTR_SHELLIDLIST and CF_HDROP
//
static CLIPFORMAT cfidlist = 0;
if (!cfidlist)
{
cfidlist = (CLIPFORMAT)RegisterClipboardFormat(CFSTR_SHELLIDLIST);
}
FORMATETC fmt = {CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
if (SUCCEEDED(pdtobj->QueryGetData(&fmt)))
{
m_dwEffect = DROPEFFECT_COPY;
}
else
{
fmt.cfFormat = cfidlist;
if (SUCCEEDED(pdtobj->QueryGetData(&fmt)))
{
m_dwEffect = DROPEFFECT_COPY;
}
}
*pdwEffect &= m_dwEffect;
return S_OK;
}
STDMETHODIMP CPreviewWnd::DragOver(DWORD grfKeyState, POINTL pt, DWORD *pdwEffect)
{
*pdwEffect &= m_dwEffect;
return S_OK;
}
STDMETHODIMP CPreviewWnd::DragLeave()
{
m_dwEffect = DROPEFFECT_NONE;
return S_OK;
}
STDMETHODIMP CPreviewWnd::Drop(IDataObject *pdtobj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect)
{
if (m_dwEffect != DROPEFFECT_NONE)
{
PreviewItemsFromUnk(pdtobj);
}
*pdwEffect &= m_dwEffect;
return S_OK;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////
//
// IServiceProvider
//
//////////////////////////////////////////////////////////////////////////////////////////////////////
STDMETHODIMP CPreviewWnd::QueryService(REFGUID guidService, REFIID riid, void **ppv)
{
if (SID_SImageView == guidService)
{
return QueryInterface(riid, ppv);
}
else if (m_punkSite)
{
return IUnknown_QueryService(m_punkSite, guidService, riid, ppv);
}
return E_FAIL;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////
//
// IImgCmdTarget
//
//////////////////////////////////////////////////////////////////////////////////////////////////////
STDMETHODIMP CPreviewWnd::GetMode(DWORD * pdw)
{
*pdw = m_dwMode;
return S_OK;
}
STDMETHODIMP CPreviewWnd::GetPageFlags(DWORD * pdw)
{
*pdw = m_dwMultiPageMode;
return S_OK;
}
STDMETHODIMP CPreviewWnd::ZoomIn()
{
if (SLIDESHOW_MODE == m_dwMode)
{
m_ctlPreview.ZoomIn();
}
else
{
m_ctlPreview.SetMode(CZoomWnd::MODE_ZOOMIN);
m_ctlPreview.ZoomIn();
m_ctlToolbar.SendMessage(TB_ENABLEBUTTON, ID_ACTUALSIZECMD, MAKELONG(TRUE, 0));
m_ctlToolbar.SendMessage(TB_ENABLEBUTTON, ID_BESTFITCMD, MAKELONG(TRUE, 0));
_UpdateButtons(ID_ZOOMINCMD);
}
return S_OK;
}
STDMETHODIMP CPreviewWnd::ZoomOut()
{
if (SLIDESHOW_MODE == m_dwMode)
{
m_ctlPreview.ZoomOut();
}
else
{
m_ctlPreview.SetMode(CZoomWnd::MODE_ZOOMOUT);
m_ctlPreview.ZoomOut();
m_ctlToolbar.SendMessage(TB_ENABLEBUTTON, ID_ACTUALSIZECMD, MAKELONG(TRUE, 0));
m_ctlToolbar.SendMessage(TB_ENABLEBUTTON, ID_BESTFITCMD, MAKELONG(!m_ctlPreview.IsBestFit(), 0));
_UpdateButtons(ID_ZOOMOUTCMD);
}
return S_OK;
}
STDMETHODIMP CPreviewWnd::ActualSize()
{
_RefreshSelection(FALSE);
m_ctlToolbar.SendMessage(TB_ENABLEBUTTON, ID_ACTUALSIZECMD, MAKELONG(FALSE, 0));
m_ctlToolbar.SendMessage(TB_ENABLEBUTTON, ID_BESTFITCMD, MAKELONG(TRUE, 0));
m_ctlPreview.ActualSize();
if (m_pEvents)
{
m_pEvents->OnActualSizePress();
}
return S_OK;
}
STDMETHODIMP CPreviewWnd::BestFit()
{
_RefreshSelection(FALSE);
m_ctlToolbar.SendMessage(TB_ENABLEBUTTON, ID_ACTUALSIZECMD, MAKELONG(TRUE, 0));
m_ctlToolbar.SendMessage(TB_ENABLEBUTTON, ID_BESTFITCMD, MAKELONG(FALSE, 0));
m_ctlPreview.BestFit();
if (m_pEvents)
{
m_pEvents->OnBestFitPress();
}
return S_OK;
}
STDMETHODIMP CPreviewWnd::Rotate(DWORD dwAngle)
{
WORD wRotate;
switch (dwAngle)
{
case 90:
wRotate = ID_ROTATE90CMD;
break;
case 270:
wRotate = ID_ROTATE270CMD;
break;
default:
return E_INVALIDARG;
}
// If we don't have an image yet, there is nothing for us to do.
// Note: The keyboard accelerator will hit this path if no image is selected
if (!m_pImageData)
return E_FAIL;
// We quietly (the button is disabled but just in case you hit the
// accelerator key) don't rotate WMF or EMF.
if (IsEqualGUID(ImageFormatWMF, m_pImageData->_guidFormat) || IsEqualGUID(ImageFormatEMF, m_pImageData->_guidFormat))
return E_FAIL;
// Animated GIFs are not editable even though normal GIFs are. This can
// cause a lot of confusion, so provide some feedback if the user tries
// to rotate an animated image.
if (m_pImageData->IsAnimated())
{
TCHAR szPath[MAX_PATH];
PathFromImageData(szPath, ARRAYSIZE(szPath));
m_fPromptingUser = TRUE;
ShellMessageBox(_Module.GetModuleInstance(), m_hWnd, MAKEINTRESOURCE(IDS_ROTATE_MESSAGE), MAKEINTRESOURCE(IDS_PROJNAME), MB_OK | MB_ICONERROR, szPath);
m_fPromptingUser = FALSE;
return E_FAIL;
}
// From here on out you need to goto ErrorCleanup rather than return
_UpdateButtons(wRotate);
SetCursorState(SLIDESHOW_CURSOR_BUSY);
m_ctlToolbar.SendMessage(TB_ENABLEBUTTON, ID_ROTATE90CMD, MAKELONG(FALSE, 0));
m_ctlToolbar.SendMessage(TB_ENABLEBUTTON, ID_ROTATE270CMD, MAKELONG(FALSE, 0));
m_ctlToolbar.UpdateWindow();
if (m_pTaskScheduler)
{
TASKOWNERID toid;
GetTaskIDFromMode(GTIDFM_DRAW, m_dwMode, &toid);
m_pTaskScheduler->RemoveTasks(toid, ITSAT_DEFAULT_LPARAM, TRUE);
}
HRESULT hr = E_FAIL;
SIZE sz;
m_pImageData->GetSize(&sz);
// if we're thinking we can quietly save
if (m_pImageData->IsEditable() && !m_fDisableEdit && m_fCanSave)
{
// And the rotation might be lossy
if (::IsEqualGUID(ImageFormatJPEG, m_pImageData->_guidFormat) && ((sz.cx % 16 != 0) || (sz.cy % 16 != 0)))
{
int nResult = IDOK;
if (m_fWarnQuietSave)
{
CComBSTR bstrMsg, bstrTitle;
if (bstrMsg.LoadString(IDS_ROTATE_LOSS) && bstrTitle.LoadString(IDS_PROJNAME))
{
// Set default to return IDOK so we know if the user selected something or
// if the "don't show me this again" bit was respected
m_fPromptingUser = TRUE;
nResult = SHMessageBoxCheck(m_hWnd, bstrMsg, bstrTitle, MB_YESNO|MB_ICONWARNING, IDOK, REGSTR_LOSSYROTATE);
m_fPromptingUser = FALSE;
}
if (nResult != IDNO)
m_fWarnQuietSave = FALSE;
}
CRegKey Key;
if (ERROR_SUCCESS != Key.Open(HKEY_CURRENT_USER, REGSTR_SHIMGVW))
{
Key.Create(HKEY_CURRENT_USER, REGSTR_SHIMGVW);
}
if (Key.m_hKey != NULL)
{
if (nResult == IDOK) // If hidden, then load last result from registry
{
DWORD dwResult = 0;
Key.QueryValue(dwResult, REGSTR_LOSSYROTATE);
nResult = (int)dwResult;
}
else // Otherwise, write this as last result to registry
{
DWORD dwResult = (DWORD)nResult;
Key.SetValue(dwResult, REGSTR_LOSSYROTATE);
}
}
if (nResult == IDNO)
goto ErrorCleanup;
}
}
CAnnotationSet* pAnnotations = m_ctlPreview.GetAnnotations();
INT_PTR nCount = pAnnotations->GetCount();
for (INT_PTR i = 0; i < nCount; i++)
{
CAnnotation* pAnnotation = pAnnotations->GetAnnotation(i);
pAnnotation->Rotate(m_ctlPreview.m_cyImage, m_ctlPreview.m_cxImage, (ID_ROTATE90CMD == wRotate));
}
m_ctlPreview.CommitAnnotations();
hr = m_pImageData->Rotate(dwAngle);
if (FAILED(hr))
goto ErrorCleanup;
// Only if we have an encoder and we haven't been explicitly told not to edit and the source is writeable
if (m_pImageData->IsEditable() && !m_fDisableEdit && m_fCanSave)
{
// on successful edit we immediately save the result. If we want to do multiple edits
// before saving then you would simply need to wait and call Save later.
// NB: We currently only allow editing of items loaded from file system paths, no path means
// no edit. This is stupid, but that's how it is for now.
hr = ImageDataSave(NULL, FALSE);
if (SUCCEEDED(hr))
m_fDirty = FALSE;
else
{
// if we failed to save then go into can't save mode
if (WINDOW_MODE == m_dwMode)
m_fCanSave = FALSE;
}
}
_UpdateImage();
if ((!m_pImageData->IsEditable() || !m_fCanSave) && WINDOW_MODE == m_dwMode)
{
if (m_fWarnNoSave)
{
m_fWarnNoSave = FALSE;
CComBSTR bstrMsg, bstrTitle;
if (bstrMsg.LoadString(IDS_ROTATE_CANTSAVE) && bstrTitle.LoadString(IDS_PROJNAME))
{
m_fPromptingUser = TRUE;
SHMessageBoxCheck(m_hWnd, bstrMsg, bstrTitle, MB_OK|MB_ICONWARNING, IDOK, REGSTR_SAVELESS);
m_fPromptingUser = FALSE;
}
}
}
ErrorCleanup:
m_ctlToolbar.SendMessage(TB_ENABLEBUTTON, ID_ROTATE90CMD, MAKELONG(TRUE, 0));
m_ctlToolbar.SendMessage(TB_ENABLEBUTTON, ID_ROTATE270CMD, MAKELONG(TRUE, 0));
SetCursorState(SLIDESHOW_CURSOR_NOTBUSY);
return hr;
}
STDMETHODIMP CPreviewWnd::NextPage()
{
return _PrevNextPage(TRUE);
}
STDMETHODIMP CPreviewWnd::PreviousPage()
{
return _PrevNextPage(FALSE);
}
HRESULT CPreviewWnd::_PrevNextPage(BOOL fForward)
{
_RefreshSelection(FALSE);
if (m_pImageData && m_pImageData->IsMultipage())
{
if (m_fDirty)
{
m_ctlPreview.CommitAnnotations();
}
if (fForward)
{
m_pImageData->NextPage();
}
else
{
m_pImageData->PrevPage();
}
_UpdateImage();
_SetMultipageCommands();
}
return S_OK;
}
//
// When the user saves to a format other than TIFF and the current
// TIFF has annotations, we need to burn annotations
// into the current image frame before saving.
// If we ever support other multi-page format encoding besides TIFF, this
// code will get more complicated
// assumes the pSID is already locked
// note that the resulting image is always a color image. Eventually we should make
// the annotation rendering code respect the bit depth and palette of the
// current image.
Image *CPreviewWnd::_BurnAnnotations(IShellImageData *pSID)
{
Image *pimg = NULL;
if (SUCCEEDED(pSID->CloneFrame(&pimg)))
{
HDC hdc = ::GetDC(NULL);
if (hdc)
{
LPVOID pBits;
BITMAPINFO bi = {0};
bi.bmiHeader.biBitCount = 24;
bi.bmiHeader.biHeight = pimg->GetHeight();
bi.bmiHeader.biWidth = pimg->GetWidth();
bi.bmiHeader.biPlanes = 1;
bi.bmiHeader.biCompression = BI_RGB;
bi.bmiHeader.biSize = sizeof(bi.bmiHeader);
HBITMAP hbm = CreateDIBSection(hdc, &bi, DIB_RGB_COLORS, &pBits, NULL, 0);
if (hbm)
{
//
// For ROP codes to work we need to use pure GDI, then convert the new
// DIBSection back to an Image object
//
HDC hdcMem = ::CreateCompatibleDC(hdc);
Status s = GenericError;
if (hdcMem)
{
HBITMAP hbmOld = (HBITMAP)::SelectObject(hdcMem, hbm);
Graphics *g = Graphics::FromHDC(hdcMem);
if (g)
{
s = g->DrawImage(pimg, 0L, 0L, pimg->GetWidth(), pimg->GetHeight());
g->ReleaseHDC(hdcMem);
delete g;
// now draw the annotations
m_ctlPreview.GetAnnotations()->RenderAllMarks(hdcMem);
}
::SelectObject(hdcMem, hbmOld);
::DeleteDC(hdcMem);
}
if (Ok == s)
{
//
// Now create a new Bitmap from our DIBSection
Bitmap *pbmNew = Bitmap::FromHBITMAP(hbm, NULL);
if (pbmNew)
{
pSID->ReplaceFrame(pbmNew);
}
}
DeleteObject(hbm);
}
::ReleaseDC(NULL, hdc);
}
}
return pimg;
}
void CPreviewWnd::_InvokePrintWizard()
{
if (m_fPrintable)
{
HRESULT hr = S_OK;
if (m_fDirty)
{
m_ctlPreview.CommitAnnotations();
hr = ImageDataSave(NULL, FALSE);
}
if (SUCCEEDED(hr))
{
m_fPromptingUser = TRUE;
m_fDirty = FALSE;
IPrintPhotosWizardSetInfo *pwiz;
HRESULT hr = CoCreateInstance(CLSID_PrintPhotosWizard,
NULL, CLSCTX_INPROC_SERVER,
IID_PPV_ARG(IPrintPhotosWizardSetInfo, &pwiz));
if (SUCCEEDED(hr))
{
if (m_pImageData != NULL && m_pImageData->_guidFormat == ImageFormatTIFF && m_pImageData->IsMultipage())
hr = pwiz->SetFileListArray(&(m_ppidls[m_iCurSlide]), 1, 0);
else
hr = pwiz->SetFileListArray(m_ppidls, m_cItems, m_iCurSlide);
if (SUCCEEDED(hr))
{
hr = pwiz->RunWizard();
}
pwiz->Release();
}
// fall back to the shell if the wizard fails
if (FAILED(hr))
{
_InvokeVerb(TEXT("print"));
}
m_fPromptingUser = FALSE;
}
else
{
CComBSTR bstrMsg, bstrTitle;
if (bstrMsg.LoadString(IDS_SAVEFAILED_MSGBOX) && bstrTitle.LoadString(IDS_PROJNAME))
{
m_fPromptingUser = TRUE;
MessageBox(bstrMsg, bstrTitle, MB_OK | MB_ICONERROR | MB_APPLMODAL);
m_fPromptingUser = FALSE;
}
}
}
}
void GetTaskIDFromMode(DWORD dwTask, DWORD dwMode, TASKOWNERID *ptoid)
{
switch (dwTask)
{
case GTIDFM_DECODE:
*ptoid = (SLIDESHOW_MODE == dwMode) ? TOID_SlideshowDecode : TOID_PrimaryDecode;
break;
case GTIDFM_DRAW:
*ptoid = (SLIDESHOW_MODE == dwMode) ? TOID_DrawSlideshowFrame : TOID_DrawFrame;
break;
default:
ASSERTMSG(FALSE, "someone passed bad task to GetTaskIDFromMode");
break;
}
}
// Watch for changes in the file we are currently viewing. This ignores changes
// in the file being pre-fetched, but we'll live with that for now.
//
void CPreviewWnd::_RegisterForChangeNotify(BOOL fRegister)
{
// always deregister the current pidl first
if (m_uRegister)
{
SHChangeNotifyDeregister(m_uRegister);
m_uRegister = 0;
}
if (fRegister)
{
SHChangeNotifyEntry cne = {0};
if (SUCCEEDED(_GetItem(m_iCurSlide, (LPITEMIDLIST*)&cne.pidl)))
{
m_uRegister = SHChangeNotifyRegister(m_hWnd,
SHCNRF_ShellLevel | SHCNRF_InterruptLevel | SHCNRF_NewDelivery,
SHCNE_DISKEVENTS,
IV_ONCHANGENOTIFY,
1, &cne);
ILFree((LPITEMIDLIST)cne.pidl);
}
}
}
LRESULT CPreviewWnd::OnChangeNotify(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
// We can assume this notify is for the currently viewed PIDL and the event
// is one that would force us to reload
//
LONG lEvent;
LPSHChangeNotificationLock pshcnl = SHChangeNotification_Lock((HANDLE)wParam, (DWORD)lParam, NULL, &lEvent);
if (pshcnl)
{
// we can't render or manipulate deleted files so don't try
if (!m_fDirty || lEvent == SHCNE_DELETE || lEvent == SHCNE_RENAMEITEM)
{
if (!m_fIgnoreNextNotify)
{
if (!m_fIgnoreAllNotifies)
{
m_fDirty = FALSE;
_PreviewItem(m_iCurSlide);
}
}
else
{
m_fIgnoreNextNotify = FALSE;
}
bHandled = TRUE;
}
SHChangeNotification_Unlock(pshcnl);
}
return 0;
}
LRESULT CPreviewWnd::OnIsAvailable(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
bHandled = TRUE;
return !m_fPromptingUser;
}