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.
1041 lines
24 KiB
1041 lines
24 KiB
/* server.c - This module contains the OLE server worker/public routines.
|
|
*
|
|
* Created by Microsoft Corporation.
|
|
*/
|
|
|
|
#include "packager.h"
|
|
|
|
|
|
#define CBLINKMAX 260
|
|
|
|
|
|
/*
|
|
* This server only supports one document per instance. The items are
|
|
* just rectangles over the document, possibly overlapping.
|
|
*/
|
|
|
|
static LHCLIENTDOC lhClipDoc = 0;
|
|
static OLESERVERDOCVTBL vdocvtbl; // Document virtual table
|
|
static OLEOBJECTVTBL vitemvtbl; // Item virtual table
|
|
static OLESERVERVTBL vsrvrvtbl; // Server virtual table
|
|
static LPSAMPITEM vlpitem[CITEMSMAX]; // Pointers to active OLE items
|
|
static INT cItems = 0; // Number of active OLE items
|
|
static CHAR szClip[] = "Clipboard";
|
|
|
|
|
|
static VOID DeleteDoc(LPSAMPDOC lpdoc);
|
|
static BOOL SendItemChangeMsg(LPSAMPITEM lpitem, UINT options);
|
|
static INT FindItem(LPSAMPITEM lpitem);
|
|
|
|
|
|
|
|
/************ Server initialization and termination routines **********/
|
|
/* InitServer() - Initializes the OLE server
|
|
*/
|
|
BOOL
|
|
InitServer(
|
|
VOID
|
|
)
|
|
{
|
|
// Allocate the server block
|
|
if (!(ghServer = LocalAlloc(LMEM_MOVEABLE | LMEM_ZEROINIT, sizeof(PBSRVR)))
|
|
|| !(glpsrvr = (LPSAMPSRVR)LocalLock(ghServer)))
|
|
goto errRtn;
|
|
|
|
// Initialize the server, document, and item virtual tables
|
|
vsrvrvtbl.Open = SrvrOpen;
|
|
vsrvrvtbl.Create = SrvrCreate;
|
|
vsrvrvtbl.CreateFromTemplate = SrvrCreateFromTemplate;
|
|
vsrvrvtbl.Edit = SrvrEdit;
|
|
vsrvrvtbl.Exit = SrvrExit;
|
|
vsrvrvtbl.Release = SrvrRelease;
|
|
vsrvrvtbl.Execute = SrvrExecute;
|
|
|
|
vdocvtbl.Save = DocSave;
|
|
vdocvtbl.Close = DocClose;
|
|
vdocvtbl.SetHostNames = DocSetHostNames;
|
|
vdocvtbl.SetDocDimensions = DocSetDocDimensions;
|
|
vdocvtbl.GetObject = DocGetObject;
|
|
vdocvtbl.Release = DocRelease;
|
|
vdocvtbl.SetColorScheme = DocSetColorScheme;
|
|
vdocvtbl.Execute = DocExecute;
|
|
|
|
vitemvtbl.QueryProtocol = ItemQueryProtocol;
|
|
vitemvtbl.Release = ItemDelete;
|
|
vitemvtbl.Show = ItemShow;
|
|
vitemvtbl.DoVerb = ItemDoVerb;
|
|
vitemvtbl.GetData = ItemGetData;
|
|
vitemvtbl.SetData = ItemSetData;
|
|
vitemvtbl.SetTargetDevice = ItemSetTargetDevice;
|
|
vitemvtbl.SetBounds = ItemSetBounds;
|
|
vitemvtbl.EnumFormats = ItemEnumFormats;
|
|
vitemvtbl.SetColorScheme = ItemSetColorScheme;
|
|
|
|
|
|
// Try to register the server
|
|
glpsrvr->olesrvr.lpvtbl = &vsrvrvtbl;
|
|
if (Error(OleRegisterServer(gszAppClassName, (LPOLESERVER)glpsrvr,
|
|
(LONG_PTR * )&glpsrvr->lhsrvr, ghInst, OLE_SERVER_MULTI)))
|
|
goto errRtn;
|
|
|
|
// Initialize the client name
|
|
lstrcpy(gszClientName, "");
|
|
|
|
return TRUE;
|
|
|
|
errRtn:
|
|
ErrorMessage(E_FAILED_TO_REGISTER_SERVER);
|
|
|
|
// If we failed, clean up
|
|
if (glpsrvr)
|
|
{
|
|
LocalUnlock(ghServer);
|
|
glpsrvr = NULL;
|
|
}
|
|
|
|
if (ghServer)
|
|
LocalFree(ghServer);
|
|
|
|
ghServer = NULL;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
|
|
/* DeleteServer() - Revokes the OLE server.
|
|
*/
|
|
VOID
|
|
DeleteServer(
|
|
LPSAMPSRVR lpsrvr
|
|
)
|
|
{
|
|
if (gfServer)
|
|
{
|
|
gfServer = FALSE;
|
|
OleRevokeServer(lpsrvr->lhsrvr);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/* DestroyServer() - Deallocates the OLE server.
|
|
*/
|
|
VOID
|
|
DestroyServer(
|
|
VOID
|
|
)
|
|
{
|
|
if (ghServer)
|
|
{
|
|
// Release the server virtual table and info
|
|
LocalUnlock(ghServer);
|
|
LocalFree(ghServer);
|
|
ghServer = NULL;
|
|
|
|
// Destroy the window only when we're all through
|
|
DestroyWindow(ghwndFrame);
|
|
gfServer = FALSE;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/********************* Document support functions ********************/
|
|
/* InitDoc() - Initialize and register the document with the OLE library.
|
|
*/
|
|
LPSAMPDOC
|
|
InitDoc(
|
|
LPSAMPSRVR lpsrvr,
|
|
LHSERVERDOC lhdoc,
|
|
LPSTR lptitle
|
|
)
|
|
{
|
|
HANDLE hdoc = NULL;
|
|
LPSAMPDOC lpdoc = NULL;
|
|
|
|
if (!(hdoc = LocalAlloc(LMEM_MOVEABLE | LMEM_ZEROINIT, sizeof(PBDOC)))
|
|
|| !(lpdoc = (LPSAMPDOC)LocalLock(hdoc)))
|
|
goto errRtn;
|
|
|
|
lpdoc->hdoc = hdoc;
|
|
lpdoc->aName = GlobalAddAtom(lptitle);
|
|
lpdoc->oledoc.lpvtbl = &vdocvtbl;
|
|
|
|
if (!lhdoc)
|
|
{
|
|
if (Error(OleRegisterServerDoc(lpsrvr->lhsrvr, lptitle,
|
|
(LPOLESERVERDOC)lpdoc, (LHSERVERDOC * ) & lpdoc->lhdoc)))
|
|
goto errRtn;
|
|
}
|
|
else
|
|
{
|
|
lpdoc->lhdoc = lhdoc;
|
|
}
|
|
|
|
gfDocExists = TRUE;
|
|
gfDocCleared = FALSE;
|
|
return lpdoc;
|
|
|
|
errRtn:
|
|
ErrorMessage(E_FAILED_TO_REGISTER_DOCUMENT);
|
|
|
|
// Clean up
|
|
if (lpdoc)
|
|
LocalUnlock(hdoc);
|
|
|
|
if (hdoc)
|
|
LocalFree(hdoc);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
|
|
/* DeleteDoc() - Notify the OLE library that the document is to be deleted.
|
|
*/
|
|
static VOID
|
|
DeleteDoc(
|
|
LPSAMPDOC lpdoc
|
|
)
|
|
{
|
|
if (gfOleClosed)
|
|
SendDocChangeMsg(lpdoc, OLE_CLOSED);
|
|
|
|
OleRevokeServerDoc(lpdoc->lhdoc);
|
|
}
|
|
|
|
|
|
|
|
/* ChangeDocName() - Notify the OLE library that the document name is changing.
|
|
*/
|
|
VOID
|
|
ChangeDocName(
|
|
LPSAMPDOC *lplpdoc,
|
|
LPSTR lpname
|
|
)
|
|
{
|
|
// If the document exists, delete and re-register.
|
|
if (*lplpdoc)
|
|
{
|
|
GlobalDeleteAtom((*lplpdoc)->aName);
|
|
(*lplpdoc)->aName = GlobalAddAtom(lpname);
|
|
|
|
//
|
|
// If the document contains items, just notify the children.
|
|
// If we aren't embedded, always delete and re-register.
|
|
//
|
|
OleRenameServerDoc((*lplpdoc)->lhdoc, lpname);
|
|
if (gfEmbedded && cItems)
|
|
return;
|
|
|
|
DeleteDoc(*lplpdoc);
|
|
}
|
|
|
|
*lplpdoc = InitDoc(glpsrvr, 0, lpname);
|
|
}
|
|
|
|
|
|
|
|
/* SendDocChangeMsg() - Notify the client that the document has changed.
|
|
*/
|
|
BOOL
|
|
SendDocChangeMsg(
|
|
LPSAMPDOC lpdoc,
|
|
UINT options
|
|
)
|
|
{
|
|
BOOL fSuccess = FALSE;
|
|
INT i;
|
|
|
|
for (i = 0; i < cItems; i++)
|
|
{
|
|
if (SendItemChangeMsg(vlpitem[i], options))
|
|
fSuccess = TRUE;
|
|
}
|
|
|
|
return fSuccess;
|
|
}
|
|
|
|
|
|
|
|
/* CreateNewDoc() - Called when a document is newly created.
|
|
*
|
|
* Returns: hDocument if document successfully created, NULL otherwise.
|
|
* Note: This function is only called when the document is being
|
|
* created through OLE actions.
|
|
*/
|
|
LPSAMPDOC
|
|
CreateNewDoc(
|
|
LPSAMPSRVR lpsrvr,
|
|
LHSERVERDOC lhdoc,
|
|
LPSTR lpstr
|
|
)
|
|
{
|
|
glpdoc = InitDoc(lpsrvr, lhdoc, lpstr);
|
|
StringCchCopy(szUntitled, ARRAYSIZE(szUntitled), lpstr);
|
|
SetTitle(TRUE);
|
|
|
|
return glpdoc;
|
|
}
|
|
|
|
|
|
|
|
/* CreateDocFromFile() - Called when a document is to be created from a file.
|
|
*
|
|
* Returns: hDocument if document successfully created, NULL otherwise.
|
|
* Note: This function is only called when the document is being
|
|
* created through OLE actions. The file name is temporarily
|
|
* set to load the file, then it is reset to "". This is so
|
|
* that we won't save back to the template if we exit.
|
|
*/
|
|
LPSAMPDOC
|
|
CreateDocFromFile(
|
|
LPSAMPSRVR lpsrvr,
|
|
LHSERVERDOC lhdoc,
|
|
LPSTR lpstr
|
|
)
|
|
{
|
|
// Initialize document
|
|
if (!(glpdoc = InitDoc(lpsrvr, lhdoc, lpstr)) || !(*lpstr))
|
|
return NULL;
|
|
|
|
lstrcpy(szUntitled, lpstr); // This could overrun, but I don't see how I can check the length of the lpstr coming in
|
|
SetTitle(TRUE);
|
|
|
|
return glpdoc;
|
|
}
|
|
|
|
|
|
|
|
/********************** Item support functions ************************/
|
|
/* CopyObjects() - Copies selection to the clipboard.
|
|
*/
|
|
BOOL
|
|
CopyObjects(
|
|
VOID
|
|
)
|
|
{
|
|
HANDLE hdata;
|
|
|
|
// If we can't open the clipboard, fail
|
|
if (!OpenClipboard(ghwndFrame))
|
|
return FALSE;
|
|
|
|
Hourglass(TRUE);
|
|
|
|
// Empty the clipboard
|
|
EmptyClipboard();
|
|
|
|
//
|
|
// Copy the clipboard contents.
|
|
//
|
|
// Start with Native Data - which will just contain all the objects
|
|
// which intersect with the selection rectangle.
|
|
//
|
|
if (hdata = GetNative(TRUE))
|
|
{
|
|
SetClipboardData(gcfNative, hdata);
|
|
OleSavedClientDoc(lhClipDoc);
|
|
}
|
|
|
|
if (lhClipDoc)
|
|
{
|
|
OleRevokeClientDoc(lhClipDoc);
|
|
lhClipDoc = 0;
|
|
}
|
|
|
|
if (hdata = GetLink())
|
|
SetClipboardData(gcfOwnerLink, hdata);
|
|
|
|
//
|
|
// Metafile data: Re-invert the image before putting
|
|
// it onto the clipboard.
|
|
//
|
|
if (hdata = GetMF())
|
|
SetClipboardData(CF_METAFILEPICT, hdata);
|
|
|
|
CloseClipboard();
|
|
Hourglass(FALSE);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|
|
/* CreateNewItem() - Allocate a new item.
|
|
*
|
|
* Note: lpitem->rc will be filled out by the caller.
|
|
*/
|
|
LPSAMPITEM
|
|
CreateNewItem(
|
|
LPSAMPDOC lpdoc
|
|
)
|
|
{
|
|
HANDLE hitem = NULL;
|
|
LPSAMPITEM lpitem = NULL;
|
|
|
|
// Now create the item
|
|
if (!(hitem = GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, sizeof(ITEM)))
|
|
|| !(lpitem = (LPSAMPITEM)GlobalLock(hitem)))
|
|
goto errRtn;
|
|
|
|
lpitem->hitem = hitem;
|
|
lpitem->oleobject.lpvtbl = &vitemvtbl;
|
|
return lpitem;
|
|
|
|
errRtn:
|
|
if (lpitem)
|
|
GlobalUnlock(hitem);
|
|
|
|
if (hitem)
|
|
GlobalFree(hitem);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
|
|
/* SendItemChangeMsg() - Notify the client that the item has changed.
|
|
*/
|
|
static BOOL
|
|
SendItemChangeMsg(
|
|
LPSAMPITEM lpitem,
|
|
UINT options
|
|
)
|
|
{
|
|
if (lpitem->lpoleclient)
|
|
{
|
|
(*lpitem->lpoleclient->lpvtbl->CallBack)
|
|
(lpitem->lpoleclient, options, (LPOLEOBJECT)lpitem);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
|
|
/******************** Data reading/writing functions *********************/
|
|
/* GetNative(fClip) - Write the item native format to a memory block.
|
|
*
|
|
* This function will just write the objects which intersect
|
|
* with the selection rectangle into a memory block. If we
|
|
* are running as an embedded instance, return ALL items, even
|
|
* those which are not in the selection area. Then we will
|
|
* never lose objects that we move out of the selection area
|
|
* when editing an embedded object.
|
|
*
|
|
* Args: fClip - TRUE means native data is for copying to clipboard
|
|
*
|
|
* Returns: A handle containing the native format, or NULL.
|
|
*/
|
|
HANDLE
|
|
GetNative(
|
|
BOOL fClip
|
|
)
|
|
{
|
|
BOOL fSuccess = FALSE;
|
|
DWORD cBytes = 0L;
|
|
HANDLE hdata = NULL;
|
|
LPSTR lpdata = NULL;
|
|
DWORD cEmbWrite;
|
|
LPOLEOBJECT lpobjapp = NULL;
|
|
LPOLEOBJECT lpobjcon = NULL;
|
|
LPPICT lpcPict;
|
|
LPPICT lpaPict;
|
|
WORD w;
|
|
|
|
// Compute the size of the appearance
|
|
lpaPict = glpobj[APPEARANCE];
|
|
lpcPict = glpobj[CONTENT];
|
|
|
|
switch (gpty[APPEARANCE])
|
|
{
|
|
case ICON:
|
|
cBytes += IconWriteToNative(glpobj[APPEARANCE], NULL);
|
|
break;
|
|
|
|
case PICTURE:
|
|
if (fClip)
|
|
{
|
|
if (Error(OleRegisterClientDoc(
|
|
gszAppClassName, szClip, 0L, &lhClipDoc)))
|
|
goto Error;
|
|
|
|
if (Error(OleClone(
|
|
lpaPict->lpObject, glpclient, lhClipDoc,
|
|
szAppearance, &lpobjapp)))
|
|
goto Error;
|
|
|
|
cBytes += PicWriteToNative(lpaPict, lpobjapp, NULL);
|
|
}
|
|
else
|
|
{
|
|
cBytes += PicWriteToNative(lpaPict, lpaPict->lpObject, NULL);
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
// Compute the content size
|
|
switch (gpty[CONTENT])
|
|
{
|
|
case CMDLINK:
|
|
cBytes += CmlWriteToNative(glpobj[CONTENT], NULL);
|
|
break;
|
|
|
|
case PEMBED: /* EmbWrite returns -1L if the user cancels */
|
|
cEmbWrite = EmbWriteToNative(glpobj[CONTENT], NULL);
|
|
|
|
if (cEmbWrite == (DWORD) - 1L)
|
|
return FALSE;
|
|
|
|
cBytes += cEmbWrite;
|
|
break;
|
|
|
|
case PICTURE:
|
|
if (fClip)
|
|
{
|
|
if (!lhClipDoc && (Error(OleRegisterClientDoc(
|
|
gszAppClassName, szClip, 0L, &lhClipDoc))))
|
|
goto Error;
|
|
|
|
if (Error(OleClone(lpcPict->lpObject, glpclient,
|
|
lhClipDoc, szContent, &lpobjcon)))
|
|
goto Error;
|
|
|
|
cBytes += PicWriteToNative(lpcPict, lpobjcon, NULL);
|
|
}
|
|
else
|
|
{
|
|
cBytes += PicWriteToNative(lpcPict, lpcPict->lpObject, NULL);
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (cBytes == 0L) // then no data
|
|
goto Error;
|
|
|
|
cBytes += (DWORD)(2 * sizeof(WORD));
|
|
|
|
// Allocate a memory block for the data
|
|
if (!(hdata = GlobalAlloc(GMEM_ZEROINIT, cBytes)) ||
|
|
!(lpdata = (LPSTR)GlobalLock(hdata)))
|
|
goto Error;
|
|
|
|
// Write out the appearance
|
|
w = (WORD)gpty[APPEARANCE];
|
|
MemWrite(&lpdata, (LPSTR)&w, sizeof(WORD));
|
|
switch (gpty[APPEARANCE])
|
|
{
|
|
case ICON:
|
|
IconWriteToNative(glpobj[APPEARANCE], &lpdata);
|
|
break;
|
|
|
|
case PICTURE:
|
|
if (fClip)
|
|
PicWriteToNative(lpaPict, lpobjapp, &lpdata);
|
|
else
|
|
PicWriteToNative(lpaPict, lpaPict->lpObject, &lpdata);
|
|
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
// Write out the content
|
|
w = (WORD)gpty[CONTENT];
|
|
MemWrite(&lpdata, (LPSTR)&w, sizeof(WORD));
|
|
|
|
switch (gpty[CONTENT])
|
|
{
|
|
case CMDLINK:
|
|
CmlWriteToNative(glpobj[CONTENT], &lpdata);
|
|
break;
|
|
|
|
case PEMBED:
|
|
EmbWriteToNative(glpobj[CONTENT], &lpdata);
|
|
break;
|
|
|
|
case PICTURE:
|
|
if (fClip)
|
|
PicWriteToNative(lpcPict, lpobjcon, &lpdata);
|
|
else
|
|
PicWriteToNative(lpcPict, lpcPict->lpObject, &lpdata);
|
|
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
fSuccess = TRUE;
|
|
|
|
Error:
|
|
if (lpobjcon)
|
|
OleRelease (lpobjcon);
|
|
|
|
if (lpobjapp)
|
|
OleRelease (lpobjapp);
|
|
|
|
if (lpdata)
|
|
GlobalUnlock(hdata);
|
|
|
|
if (!fSuccess && hdata)
|
|
{
|
|
GlobalFree(hdata);
|
|
hdata = NULL;
|
|
}
|
|
|
|
return hdata;
|
|
}
|
|
|
|
|
|
|
|
/* PutNative() - Read the item native data from a selector.
|
|
*
|
|
* Reads as many objects as it can, in upwards order (better error recovery).
|
|
* Note: It may be worthwhile to scale the object(s) to the window here.
|
|
*
|
|
* Returns: TRUE iff successful.
|
|
*/
|
|
BOOL
|
|
PutNative(
|
|
HANDLE hdata
|
|
)
|
|
{
|
|
BOOL fSuccess = FALSE;
|
|
LPSTR lpdata;
|
|
WORD w;
|
|
|
|
if (!(lpdata = (LPSTR)GlobalLock(hdata)))
|
|
goto Error;
|
|
|
|
// Delete any previous panes
|
|
DeletePane(APPEARANCE, TRUE);
|
|
DeletePane(CONTENT, TRUE);
|
|
|
|
// Read in the appearance
|
|
MemRead(&lpdata, (LPSTR)&w, sizeof(WORD));
|
|
gpty[APPEARANCE] = w;
|
|
switch (gpty[APPEARANCE])
|
|
{
|
|
case ICON:
|
|
if (!(glpobj[APPEARANCE] = IconReadFromNative(&lpdata)))
|
|
gpty[APPEARANCE] = NOTHING;
|
|
|
|
break;
|
|
|
|
case PICTURE:
|
|
if (glpobj[APPEARANCE] =
|
|
PicReadFromNative(&lpdata, gszCaption[APPEARANCE]))
|
|
{
|
|
SendMessage(ghwndPane[APPEARANCE], WM_FIXSCROLL, 0, 0L);
|
|
break;
|
|
}
|
|
|
|
default:
|
|
gpty[APPEARANCE] = NOTHING;
|
|
break;
|
|
}
|
|
|
|
// Read the content
|
|
MemRead(&lpdata, (LPSTR)&w, sizeof(WORD));
|
|
gpty[CONTENT] = w;
|
|
switch (gpty[CONTENT])
|
|
{
|
|
case CMDLINK:
|
|
if (!(glpobj[CONTENT] = CmlReadFromNative(&lpdata)))
|
|
gpty[CONTENT] = NOTHING;
|
|
|
|
break;
|
|
|
|
case PEMBED:
|
|
if (!(glpobj[CONTENT] = (LPVOID)EmbReadFromNative(&lpdata)))
|
|
gpty[CONTENT] = NOTHING;
|
|
|
|
break;
|
|
|
|
case PICTURE:
|
|
if (glpobj[CONTENT] =
|
|
(LPVOID)PicReadFromNative(&lpdata, gszCaption[CONTENT]))
|
|
{
|
|
SendMessage(ghwndPane[CONTENT], WM_FIXSCROLL, 0, 0L);
|
|
EnableWindow(ghwndPict, TRUE);
|
|
break;
|
|
}
|
|
|
|
default:
|
|
gpty[CONTENT] = NOTHING;
|
|
break;
|
|
}
|
|
|
|
fSuccess = TRUE;
|
|
InvalidateRect(ghwndFrame, NULL, TRUE);
|
|
|
|
Error:
|
|
if (lpdata)
|
|
GlobalUnlock(hdata);
|
|
|
|
return fSuccess;
|
|
}
|
|
|
|
|
|
|
|
/* GetLink() - Retrieves ObjectLink/OwnerLink information.
|
|
*
|
|
* This function returns a string describing the selected area.
|
|
*/
|
|
HANDLE
|
|
GetLink(
|
|
VOID
|
|
)
|
|
{
|
|
CHAR pchlink[CBLINKMAX];
|
|
INT cblink;
|
|
HANDLE hlink;
|
|
LPSTR lplink;
|
|
|
|
// Link data - <App name>\0<Doc name>\0<Item name>\0\0
|
|
StringCchCopy((LPSTR)pchlink, ARRAYSIZE(pchlink), gszAppClassName); // ok const
|
|
cblink = lstrlen((LPSTR)pchlink) + 1;
|
|
|
|
// Copy the file name
|
|
StringCchCopy((LPSTR)(pchlink + cblink), ARRAYSIZE(pchlink) - cblink, szDummy); // szDummy size = 20
|
|
cblink += lstrlen((LPSTR)(pchlink + cblink)) + 1;
|
|
|
|
// Copy the item name
|
|
StringCchCopy((LPSTR)(pchlink + cblink), ARRAYSIZE(pchlink) - cblink, szDummy);
|
|
cblink += lstrlen((LPSTR)(pchlink + cblink)) + 1;
|
|
pchlink[cblink++] = 0; /* throw in another NULL at the end */
|
|
|
|
// Allocate a memory block for the data
|
|
if (!(hlink = GlobalAlloc(GMEM_ZEROINIT, cblink)) ||
|
|
!(lplink = (LPSTR)GlobalLock(hlink)))
|
|
goto Error;
|
|
|
|
// Copy the data, then return the memory block
|
|
MemWrite(&lplink, (LPSTR)pchlink, cblink);
|
|
GlobalUnlock(hlink);
|
|
return hlink;
|
|
|
|
Error:
|
|
if (hlink)
|
|
GlobalFree(hlink);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
|
|
/* GetMF() - Retrieve a metafile of the selected area.
|
|
*
|
|
* Note: Originally, tried to Blt directly from the Window DC. This
|
|
* doesn't work very well because when the window is obscured,
|
|
* the obscured portion shows up when the link is updated.
|
|
*/
|
|
HANDLE
|
|
GetMF(
|
|
VOID
|
|
)
|
|
{
|
|
BOOL fError = TRUE;
|
|
HANDLE hdata = NULL;
|
|
HDC hdcMF = NULL;
|
|
HDC hdcWnd = NULL;
|
|
HFONT hfont;
|
|
HANDLE hmfpict;
|
|
LPMETAFILEPICT lpmfpict;
|
|
LPIC lpic;
|
|
LPPICT lppict;
|
|
RECT rcTemp;
|
|
RECT rcText;
|
|
INT cxImage;
|
|
INT cyImage;
|
|
|
|
hmfpict = GlobalAlloc(GMEM_ZEROINIT, sizeof(METAFILEPICT));
|
|
if (!hmfpict)
|
|
goto Error;
|
|
|
|
lpmfpict = (LPMETAFILEPICT)GlobalLock(hmfpict);
|
|
|
|
// If the picture has a metafile, use it!
|
|
if (gpty[APPEARANCE] == PICTURE)
|
|
{
|
|
LPMETAFILEPICT lpmfpictOrg = NULL;
|
|
|
|
if (Error(OleGetData(
|
|
((LPPICT)glpobj[APPEARANCE])->lpObject, CF_METAFILEPICT, &hdata))
|
|
|| !hdata
|
|
|| !(lpmfpictOrg = (LPMETAFILEPICT)GlobalLock(hdata)))
|
|
goto NoPicture;
|
|
|
|
// Copy the metafile
|
|
lpmfpict->hMF = CopyMetaFile(lpmfpictOrg->hMF, NULL);
|
|
GlobalUnlock(hdata);
|
|
|
|
// If we failed, just draw it
|
|
if (!lpmfpict->hMF)
|
|
goto NoPicture;
|
|
|
|
// Finish filling in the metafile header
|
|
lpmfpict->mm = lpmfpictOrg->mm;
|
|
lpmfpict->xExt = lpmfpictOrg->xExt;
|
|
lpmfpict->yExt = lpmfpictOrg->yExt;
|
|
|
|
GlobalUnlock(hmfpict);
|
|
return hmfpict;
|
|
}
|
|
|
|
NoPicture:
|
|
// Get the window DC, and make a DC compatible to it.
|
|
if (!(hdcWnd = GetDC(NULL)))
|
|
goto Error;
|
|
|
|
switch (gpty[APPEARANCE])
|
|
{
|
|
case ICON:
|
|
lpic = (LPIC)glpobj[APPEARANCE];
|
|
|
|
// Set the icon text rectangle, and the icon font
|
|
SetRect(&rcText, 0, 0, gcxArrange, gcyArrange);
|
|
hfont = SelectObject(hdcWnd, ghfontTitle);
|
|
|
|
// Figure out how large the text region will be
|
|
// since this is going in a metafile we will not wrap
|
|
// the icon text
|
|
|
|
DrawText(hdcWnd, lpic->szIconText, -1, &rcText,
|
|
DT_CALCRECT | DT_WORDBREAK | DT_NOPREFIX | DT_SINGLELINE);
|
|
|
|
if (hfont)
|
|
SelectObject(hdcWnd, hfont);
|
|
|
|
// Compute the image size
|
|
rcText.right++;
|
|
cxImage = (rcText.right > gcxIcon) ? rcText.right : gcxIcon;
|
|
cyImage = gcyIcon + rcText.bottom + 1;
|
|
break;
|
|
|
|
case PICTURE:
|
|
lppict = (LPPICT)glpobj[APPEARANCE];
|
|
cxImage = lppict->rc.right - lppict->rc.left + 1;
|
|
cyImage = lppict->rc.bottom - lppict->rc.top + 1;
|
|
break;
|
|
|
|
default:
|
|
cxImage = GetSystemMetrics(SM_CXICON);
|
|
cyImage = GetSystemMetrics(SM_CYICON);
|
|
break;
|
|
}
|
|
|
|
cxImage += cxImage / 4; // grow the image a bit
|
|
|
|
cyImage += cyImage / 8;
|
|
|
|
// Create the metafile
|
|
if (!(hdcMF = CreateMetaFile(NULL)))
|
|
goto Error;
|
|
|
|
// Initialize the metafile
|
|
SetWindowOrgEx(hdcMF, 0, 0, NULL);
|
|
SetWindowExtEx(hdcMF, cxImage - 1, cyImage - 1, NULL);
|
|
|
|
//
|
|
// Fill in the background
|
|
//
|
|
// We displace back to (0, 0) because that's where the BITMAP resides.
|
|
//
|
|
SetRect(&rcTemp, 0, 0, cxImage, cyImage);
|
|
switch (gpty[APPEARANCE])
|
|
{
|
|
case ICON:
|
|
IconDraw(glpobj[APPEARANCE], hdcMF, &rcTemp, FALSE, cxImage, cyImage);
|
|
break;
|
|
|
|
case PICTURE:
|
|
PicDraw(glpobj[APPEARANCE], hdcMF, &rcTemp, 0, 0, TRUE, FALSE);
|
|
break;
|
|
|
|
default:
|
|
DrawIcon(hdcMF, 0, 0, LoadIcon(ghInst, MAKEINTRESOURCE(ID_APPLICATION)));
|
|
break;
|
|
}
|
|
|
|
// Map to device independent coordinates
|
|
rcTemp.right =
|
|
MulDiv((rcTemp.right - rcTemp.left), HIMETRIC_PER_INCH, giXppli);
|
|
rcTemp.bottom =
|
|
MulDiv((rcTemp.bottom - rcTemp.top), HIMETRIC_PER_INCH, giYppli);
|
|
|
|
// Finish filling in the metafile header
|
|
lpmfpict->mm = MM_ANISOTROPIC;
|
|
lpmfpict->xExt = rcTemp.right;
|
|
lpmfpict->yExt = rcTemp.bottom;
|
|
lpmfpict->hMF = CloseMetaFile(hdcMF);
|
|
|
|
fError = FALSE;
|
|
|
|
Error:
|
|
if (hdcWnd)
|
|
ReleaseDC(NULL, hdcWnd);
|
|
|
|
// If we had an error, return NULL
|
|
if (fError && hmfpict)
|
|
{
|
|
GlobalUnlock(hmfpict);
|
|
GlobalFree(hmfpict);
|
|
hmfpict = NULL;
|
|
}
|
|
|
|
return hmfpict;
|
|
}
|
|
|
|
|
|
|
|
/* InitEmbedded() - Perform operations specific to editing embedded objects.
|
|
*
|
|
* This routine changes the menu items as appropriate.
|
|
*/
|
|
VOID
|
|
InitEmbedded(
|
|
BOOL fCreate
|
|
)
|
|
{
|
|
HMENU hmenu;
|
|
|
|
if (hmenu = GetMenu(ghwndFrame))
|
|
EnableMenuItem(hmenu, IDM_UPDATE, fCreate ? MF_GRAYED : MF_ENABLED);
|
|
|
|
gfEmbedded = TRUE;
|
|
}
|
|
|
|
|
|
|
|
/***************** Item circular queue/utility functions *****************/
|
|
/* AddItem() - Add an item to the global item list.
|
|
*/
|
|
LPSAMPITEM
|
|
AddItem(
|
|
LPSAMPITEM lpitem
|
|
)
|
|
{
|
|
INT i;
|
|
HANDLE hitem;
|
|
|
|
i = FindItem((LPSAMPITEM)lpitem);
|
|
if (i < cItems)
|
|
{
|
|
vlpitem[i]->ref++;
|
|
|
|
// Free the duplicate item
|
|
GlobalUnlock(hitem = lpitem->hitem);
|
|
GlobalFree(hitem);
|
|
}
|
|
else
|
|
{
|
|
if (i < CITEMSMAX)
|
|
{
|
|
vlpitem[cItems] = (LPSAMPITEM)lpitem;
|
|
vlpitem[cItems++]->ref = 1;
|
|
}
|
|
else
|
|
{
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
return vlpitem[i];
|
|
}
|
|
|
|
|
|
|
|
/* DeleteItem() - Delete an item from the global item list.
|
|
*
|
|
* Returns: TRUE iff successful.
|
|
*/
|
|
BOOL
|
|
DeleteItem(
|
|
LPSAMPITEM lpitem
|
|
)
|
|
{
|
|
BOOL fFound;
|
|
HANDLE hitem;
|
|
INT i;
|
|
|
|
i = FindItem(lpitem);
|
|
|
|
if ((fFound = (i < cItems && vlpitem[i]->ref))
|
|
&& !(--vlpitem[i]->ref))
|
|
{
|
|
// Free the item
|
|
GlobalUnlock(hitem = vlpitem[i]->hitem);
|
|
GlobalFree(hitem);
|
|
|
|
// Shift everything else down
|
|
cItems--;
|
|
for ( ; i < cItems; i++)
|
|
vlpitem[i] = vlpitem[i + 1];
|
|
}
|
|
|
|
return fFound;
|
|
}
|
|
|
|
|
|
|
|
/* FindItem() - Locate an item in the global item list.
|
|
*/
|
|
static INT
|
|
FindItem(
|
|
LPSAMPITEM lpitem
|
|
)
|
|
{
|
|
BOOL fFound = FALSE;
|
|
INT i;
|
|
|
|
for (i = 0; i < cItems && !fFound;)
|
|
{
|
|
if (lpitem->aName == vlpitem[i]->aName)
|
|
{
|
|
fFound = TRUE;
|
|
}
|
|
else
|
|
{
|
|
i++;
|
|
}
|
|
}
|
|
|
|
return i;
|
|
}
|
|
|
|
|
|
|
|
/* EndEmbedding() - Return to normal editing.
|
|
*
|
|
* This routine changes the menu items as appropriate.
|
|
*/
|
|
VOID
|
|
EndEmbedding(
|
|
VOID
|
|
)
|
|
{
|
|
HMENU hmenu;
|
|
|
|
// Fix the "Untitled" string
|
|
LoadString(ghInst, IDS_UNTITLED, szUntitled, CBMESSAGEMAX);
|
|
|
|
if (hmenu = GetMenu(ghwndFrame))
|
|
EnableMenuItem(hmenu, IDM_UPDATE, MF_GRAYED);
|
|
|
|
gfEmbedded = FALSE;
|
|
}
|