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.
552 lines
14 KiB
552 lines
14 KiB
//------------------------------------------------------------------------------
|
|
// undo.cpp
|
|
// Copyright (c)1997-1999 Microsoft Corporation, All Rights Reserved
|
|
//
|
|
// Undo support routines for TriEdit
|
|
//
|
|
//------------------------------------------------------------------------------
|
|
|
|
#include "stdafx.h"
|
|
|
|
#include <ocidl.h>
|
|
|
|
#include "undo.h"
|
|
#include "triedit.h"
|
|
#include "document.h"
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// AddUndoUnit
|
|
//
|
|
// Add the given undo unit to the given Trident instance. Return S_OK
|
|
// or a Trident error code.
|
|
//
|
|
|
|
HRESULT AddUndoUnit(IUnknown* punkTrident, IOleUndoUnit* pioleUndoUnit)
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
IServiceProvider* piservProv;
|
|
IOleUndoManager* pioleUndoManager;
|
|
|
|
if (punkTrident && pioleUndoUnit)
|
|
{
|
|
hr = punkTrident->QueryInterface(IID_IServiceProvider, (LPVOID*)&piservProv);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
_ASSERTE(piservProv);
|
|
hr = piservProv->QueryService(IID_IOleUndoManager,
|
|
IID_IOleUndoManager, (LPVOID*)&pioleUndoManager);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
_ASSERTE(pioleUndoManager);
|
|
hr = pioleUndoManager->Add(pioleUndoUnit);
|
|
_ASSERTE(SUCCEEDED(hr));
|
|
pioleUndoManager->Release();
|
|
}
|
|
piservProv->Release();
|
|
}
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// EmptyUndoRedoStack
|
|
//
|
|
// If fUndo is TRUE, discard all undo items from the given undo manager.
|
|
// If fUndo is FALSE, discard all redo items from the given undo manager
|
|
// Return S_OK if all goes well, or a Trident error code otherwise.
|
|
//
|
|
|
|
HRESULT EmptyUndoRedoStack(BOOL fUndo, IOleUndoManager *pUndoManager)
|
|
{
|
|
CComPtr<IEnumOleUndoUnits> srpEnum;
|
|
CComPtr<IOleUndoUnit> srpcd;
|
|
ULONG cFetched=0, cTotal=0;
|
|
HRESULT hr = E_FAIL;
|
|
|
|
if (fUndo)
|
|
{
|
|
if (FAILED(hr = pUndoManager->EnumUndoable(&srpEnum)))
|
|
goto Fail;
|
|
}
|
|
else
|
|
{
|
|
if (FAILED(hr = pUndoManager->EnumRedoable(&srpEnum)))
|
|
goto Fail;
|
|
}
|
|
|
|
|
|
while (SUCCEEDED(srpEnum->Next(1, &srpcd, &cFetched)))
|
|
{
|
|
_ASSERTE(cFetched <=1);
|
|
if (srpcd == NULL)
|
|
break;
|
|
|
|
cTotal++;
|
|
srpcd.Release();
|
|
}
|
|
|
|
// get the one on top of the stack and discard from that
|
|
if (cTotal > 0)
|
|
{
|
|
if (FAILED(hr = srpEnum->Reset()))
|
|
goto Fail;
|
|
if (FAILED(hr = srpEnum->Skip(cTotal-1)))
|
|
goto Fail;
|
|
|
|
srpcd.Release();
|
|
if (FAILED(hr = srpEnum->Next(1, &srpcd, &cFetched)))
|
|
goto Fail;
|
|
|
|
_ASSERTE(cFetched ==1);
|
|
|
|
if (FAILED(hr = pUndoManager->DiscardFrom(srpcd)))
|
|
goto Fail;
|
|
}
|
|
|
|
Fail:
|
|
return hr;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// GetUndoManager
|
|
//
|
|
// Obtain and return (under *ppOleUndoManager) the IOleUndoManager
|
|
// associated with the given Trident instance. Return S_OK if a
|
|
// manager was returned; E_FAIL otherwise.
|
|
//
|
|
|
|
HRESULT GetUndoManager(IUnknown* punkTrident, IOleUndoManager **ppOleUndoManager)
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
CComPtr<IServiceProvider> srpiservProv;
|
|
CComPtr<IOleUndoManager> srpioleUndoManager;
|
|
|
|
_ASSERTE(ppOleUndoManager != NULL);
|
|
_ASSERTE(punkTrident != NULL);
|
|
if (punkTrident)
|
|
{
|
|
hr = punkTrident->QueryInterface(IID_IServiceProvider, (LPVOID*)&srpiservProv);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
_ASSERTE(srpiservProv);
|
|
if (SUCCEEDED(hr = srpiservProv->QueryService(IID_IOleUndoManager,
|
|
IID_IOleUndoManager, (LPVOID*)&srpioleUndoManager)))
|
|
{
|
|
*ppOleUndoManager = srpioleUndoManager;
|
|
(*ppOleUndoManager)->AddRef();
|
|
}
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// CUndo::CUndo
|
|
// CUndo::~Undo
|
|
//
|
|
// Simple constructor and destructor for the CUndo class.
|
|
//
|
|
|
|
CUndo::CUndo()
|
|
{
|
|
m_cRef = 1;
|
|
m_fUndo = TRUE;
|
|
|
|
}
|
|
|
|
CUndo::~CUndo()
|
|
{
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// CUndo::QueryInterface (IUnknown method)
|
|
// CUndo::AddRef (IUnknown method)
|
|
// CUndo::Release (IUnknown method)
|
|
//
|
|
// Implementations of the three IUnknown methods.
|
|
//
|
|
|
|
STDMETHODIMP CUndo::QueryInterface(REFIID riid, LPVOID* ppvObject)
|
|
{
|
|
if (!ppvObject)
|
|
return E_POINTER;
|
|
if (IsEqualGUID(riid, IID_IUnknown))
|
|
*ppvObject = (IUnknown*)this;
|
|
else
|
|
if (IsEqualGUID(riid, IID_IOleUndoUnit))
|
|
*ppvObject = (IOleUndoUnit*)this;
|
|
else
|
|
return E_NOINTERFACE;
|
|
AddRef();
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP_(ULONG) CUndo::AddRef(void)
|
|
{
|
|
return InterlockedIncrement((LONG*)&m_cRef);
|
|
}
|
|
|
|
STDMETHODIMP_(ULONG) CUndo::Release(void)
|
|
{
|
|
ULONG cRef = InterlockedDecrement((LONG*)&m_cRef);
|
|
if (!cRef)
|
|
delete this;
|
|
return cRef;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// CUndoDrag::CUndoDrag
|
|
// CUndoDrag::~CUndoDrag
|
|
//
|
|
// Constructor for an object which can undo the drag of an HTML element.
|
|
//
|
|
|
|
CUndoDrag::CUndoDrag(IHTMLStyle* pihtmlStyle, POINT ptOrig, POINT ptMove)
|
|
{
|
|
m_pihtmlStyle = pihtmlStyle;
|
|
if (m_pihtmlStyle)
|
|
m_pihtmlStyle->AddRef();
|
|
m_ptOrig = ptOrig;
|
|
m_ptMove = ptMove;
|
|
}
|
|
|
|
CUndoDrag::~CUndoDrag()
|
|
{
|
|
SAFERELEASE(m_pihtmlStyle);
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// CUndoDrag::Do (IOleUndoUnit method)
|
|
//
|
|
// Do or undo dragging of an HTML element from place to place. Set or
|
|
// restore the item's position. Return S_OK.
|
|
//
|
|
|
|
STDMETHODIMP CUndoDrag::Do(IOleUndoManager *pUndoManager)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
if (pUndoManager)
|
|
{
|
|
hr = pUndoManager->Add(this);
|
|
}
|
|
if (m_pihtmlStyle)
|
|
{
|
|
// We do a put_pixelLeft(-1) and put_pixelTop(-1) below in order
|
|
// to work around a Trident problem. Sometimes they don't think
|
|
// that anything has changed - these calls below fool them into
|
|
// thinking that the values have changed.
|
|
if (m_fUndo)
|
|
{
|
|
m_pihtmlStyle->put_pixelLeft(-1);
|
|
m_pihtmlStyle->put_pixelLeft(m_ptOrig.x);
|
|
m_pihtmlStyle->put_pixelTop(-1);
|
|
m_pihtmlStyle->put_pixelTop(m_ptOrig.y);
|
|
}
|
|
else
|
|
{
|
|
m_pihtmlStyle->put_pixelLeft(-1);
|
|
m_pihtmlStyle->put_pixelLeft(m_ptMove.x);
|
|
m_pihtmlStyle->put_pixelTop(-1);
|
|
m_pihtmlStyle->put_pixelTop(m_ptMove.y);
|
|
}
|
|
m_fUndo = !m_fUndo;
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// CUndoDrag::GetDescription (IOleUndoUnit method)
|
|
//
|
|
// Return the description of the undo item. Note that this function
|
|
// returns an empty string since this is the only would-be localizable
|
|
// content in TriEdit.
|
|
//
|
|
|
|
STDMETHODIMP CUndoDrag::GetDescription(BSTR *pBstr)
|
|
{
|
|
if (pBstr)
|
|
{
|
|
*pBstr = SysAllocString(_T(" "));
|
|
return S_OK;
|
|
}
|
|
return E_FAIL;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// CUndoDrag::GetUnitType (IOleUndoUnit method)
|
|
//
|
|
// Return the CLSID and an identifier for the undo item.
|
|
//
|
|
|
|
STDMETHODIMP CUndoDrag::GetUnitType(CLSID *pClsid, LONG *plID)
|
|
{
|
|
if (pClsid)
|
|
*pClsid = UID_TRIEDIT_UNDO;
|
|
if (plID)
|
|
*plID = TRIEDIT_UNDO_DRAG;
|
|
return S_OK;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// CUndoDrag::OnNextAdd (IOleUndoUnit method)
|
|
//
|
|
// Do nothing, but do it extremely well.
|
|
//
|
|
|
|
STDMETHODIMP CUndoDrag::OnNextAdd(void)
|
|
{
|
|
return S_OK;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// CUndoPackManager::~CUndoPackManager
|
|
//
|
|
// Destructor for a CUndoPackManager object. If currently packing undo
|
|
// items, end the packing before destroying the object.
|
|
//
|
|
|
|
CUndoPackManager::~CUndoPackManager(void)
|
|
{
|
|
if (m_fPacking)
|
|
End();
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// CUndoPackManager::Start
|
|
//
|
|
// Called to tell the pack manager to begin accumulating subsequent
|
|
// undo units into a unit that can be undone in one fell swoop. Turn
|
|
// on the packing flag. Return S_OK if all goes well or E_FAIL if
|
|
// something goes wrong.
|
|
//
|
|
|
|
HRESULT CUndoPackManager::Start(void)
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
CComPtr<IOleUndoManager> srpioleUndoManager;
|
|
CComPtr<IEnumOleUndoUnits> srpEnum;
|
|
CComPtr<IOleUndoUnit> srpcd;
|
|
ULONG cFetched=0;
|
|
|
|
_ASSERTE(m_indexStartPacking==0);
|
|
|
|
if (FAILED(hr = GetUndoManager(m_srpUnkTrident, &srpioleUndoManager)))
|
|
goto Fail;
|
|
|
|
if (FAILED(hr = srpioleUndoManager->EnumUndoable(&srpEnum)))
|
|
goto Fail;
|
|
|
|
while(SUCCEEDED(srpEnum->Next(1, &srpcd, &cFetched)))
|
|
{
|
|
_ASSERTE(cFetched <=1);
|
|
if (srpcd == NULL)
|
|
break;
|
|
|
|
m_indexStartPacking++;
|
|
srpcd.Release();
|
|
}
|
|
|
|
m_fPacking = TRUE;
|
|
|
|
Fail:
|
|
if (!m_fPacking)
|
|
m_indexStartPacking=0;
|
|
|
|
return hr;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// CUndoPackManager::End
|
|
//
|
|
// Called to tell the pack manager to stop accumulating undo units. Pack
|
|
// the accumulated undo units in to the parent undo unit and turn off
|
|
// the packing flag. Return S_OK if all goes well or E_FAIL if something
|
|
// goes wrong.
|
|
//
|
|
|
|
HRESULT CUndoPackManager::End(void)
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
CUndoPackUnit *pUndoParentUnit;
|
|
|
|
_ASSERTE(m_srpUnkTrident != NULL);
|
|
pUndoParentUnit = new CUndoPackUnit();
|
|
_ASSERTE(pUndoParentUnit != NULL);
|
|
|
|
if (FAILED(hr = pUndoParentUnit->PackUndo(m_indexStartPacking, m_srpUnkTrident)))
|
|
goto Fail;
|
|
|
|
m_fPacking = FALSE;
|
|
Fail:
|
|
pUndoParentUnit->Release();
|
|
return hr;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// CUndoPackUnit::Do (IOleUndoUnit method)
|
|
//
|
|
// Invoke the Do method of each undo unit referenced by the object. Return
|
|
// S_OK.
|
|
//
|
|
|
|
STDMETHODIMP CUndoPackUnit::Do(IOleUndoManager *pUndoManager)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
for (INT i=sizeof(m_rgUndo)/sizeof(IOleUndoUnit*)-1; i >= 0; i--)
|
|
{
|
|
if (m_rgUndo[i] == NULL)
|
|
continue;
|
|
|
|
if (FAILED(hr = m_rgUndo[i]->Do(pUndoManager)))
|
|
goto Fail;
|
|
}
|
|
|
|
::EmptyUndoRedoStack(FALSE, pUndoManager);
|
|
|
|
Fail:
|
|
return hr;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// CUndoPackUnit::GetDescription (IOleUndoUnit method)
|
|
//
|
|
// Return the description of the undo item. Note that this function
|
|
// returns an empty string since this string would be one of only
|
|
// two localizable strings in TriEdit.
|
|
//
|
|
|
|
STDMETHODIMP CUndoPackUnit::GetDescription(BSTR *pBstr)
|
|
{
|
|
if (pBstr)
|
|
{
|
|
// In order to save localization work for the two TriEdit strings,
|
|
// it was decided that we would return a blank string here
|
|
*pBstr = SysAllocString(_T(" "));
|
|
return S_OK;
|
|
}
|
|
return E_FAIL;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// CUndoPackUnit::GetUnitType (IOleUndoUnit method)
|
|
//
|
|
// Return the CLSID and an identifier for the undo item.
|
|
//
|
|
|
|
STDMETHODIMP CUndoPackUnit::GetUnitType(CLSID *pClsid, LONG *plID)
|
|
{
|
|
if (pClsid)
|
|
*pClsid = UID_TRIEDIT_UNDO;
|
|
if (plID)
|
|
*plID = TRIEDIT_UNDO_PACK;
|
|
return S_OK;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// CUndoPackUnit::OnNextAdd (IOleUndoUnit method)
|
|
//
|
|
// Do nothing, but do it extremely well.
|
|
//
|
|
|
|
STDMETHODIMP CUndoPackUnit::OnNextAdd(void)
|
|
{
|
|
return S_OK;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// CUndoPackUnit::PackUndo
|
|
//
|
|
// Pack all of the undo units starting at the given index in to
|
|
// the parent undo manager. Return S_OK if all goes well, or
|
|
// E_FAIL if something goes wrong.
|
|
//
|
|
|
|
HRESULT CUndoPackUnit::PackUndo(ULONG indexStartPacking, IUnknown *pUnkTrident)
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
CComPtr<IOleUndoManager> srpioleUndoManager;
|
|
CComPtr<IEnumOleUndoUnits> srpEnumUndo;
|
|
CComPtr<IOleUndoUnit> rgUndo[cUndoPackMax]; // CONSIDER: allocate dynamically
|
|
CComPtr<IOleUndoUnit> srpcd;
|
|
ULONG cFetched=0, cUndo=0, i=0;
|
|
|
|
if (FAILED(hr = GetUndoManager(pUnkTrident, &srpioleUndoManager)))
|
|
goto Fail;
|
|
|
|
if (FAILED(hr = srpioleUndoManager->EnumUndoable(&srpEnumUndo)))
|
|
goto Fail;
|
|
|
|
_ASSERTE(srpEnumUndo != NULL);
|
|
while(SUCCEEDED(srpEnumUndo->Next(1, &srpcd, &cFetched)))
|
|
{
|
|
_ASSERTE(cFetched <= 1);
|
|
if (srpcd == NULL)
|
|
break;
|
|
|
|
cUndo++;
|
|
srpcd.Release();
|
|
}
|
|
|
|
// if there's nothing to pack
|
|
if ((cUndo-indexStartPacking) == 0)
|
|
return S_OK;
|
|
|
|
if ((cUndo-indexStartPacking) > cUndoPackMax)
|
|
return E_OUTOFMEMORY;
|
|
|
|
// get the undo units that we want to pack
|
|
if (FAILED(hr = srpEnumUndo->Reset()))
|
|
goto Fail;
|
|
if (FAILED(hr =srpEnumUndo->Skip(indexStartPacking)))
|
|
goto Fail;
|
|
if (FAILED(hr = srpEnumUndo->Next(cUndo-indexStartPacking, (IOleUndoUnit **) &m_rgUndo, &cFetched)))
|
|
goto Fail;
|
|
_ASSERTE(cFetched == (cUndo-indexStartPacking));
|
|
|
|
// now clear the undo/redo stack and then adds back the undo unit except that one that we just packed
|
|
if (FAILED(hr = srpEnumUndo->Reset()))
|
|
goto Fail;
|
|
|
|
if (FAILED(hr = srpEnumUndo->Next(cUndo, (IOleUndoUnit **) &rgUndo, &cFetched)))
|
|
goto Fail;
|
|
|
|
_ASSERTE(cFetched == cUndo);
|
|
|
|
if (FAILED(hr = srpioleUndoManager->DiscardFrom(NULL)))
|
|
goto Fail;
|
|
|
|
for (i=0; i < indexStartPacking; i++)
|
|
{
|
|
if (FAILED(hr = srpioleUndoManager->Add(rgUndo[i])))
|
|
goto Fail;
|
|
}
|
|
|
|
if (FAILED(hr = ::AddUndoUnit(pUnkTrident, this)))
|
|
goto Fail;
|
|
Fail:
|
|
return hr;
|
|
}
|
|
|