/* picture.c - This file contains OLE object handling routines. * * Created by Microsoft Corporation. */ #include "packager.h" #include "dialogs.h" static OLECLIENTVTBL clientTbl; static OLESTREAMVTBL streamTbl; static VOID PicGetBounds(LPOLEOBJECT lpObject, LPRECT lprc); /* InitClient() - Initialize the OLE client structures. */ BOOL InitClient( VOID ) { gcfFileName = (OLECLIPFORMAT)RegisterClipboardFormat("FileName"); gcfLink = (OLECLIPFORMAT)RegisterClipboardFormat("ObjectLink"); gcfNative = (OLECLIPFORMAT)RegisterClipboardFormat("Native"); gcfOwnerLink = (OLECLIPFORMAT)RegisterClipboardFormat("OwnerLink"); glpclient = PicCreateClient(&CallBack, (LPOLECLIENTVTBL)&clientTbl); if (!(glpStream = (LPAPPSTREAM)GlobalAlloc(GMEM_FIXED | GMEM_ZEROINIT, sizeof(APPSTREAM)))) goto Error; glpStream->lpstbl = (LPOLESTREAMVTBL)&streamTbl; streamTbl.Get = (DWORD (CALLBACK*)(LPOLESTREAM, void FAR*, DWORD))ReadStream; streamTbl.Put = (DWORD (CALLBACK*)(LPOLESTREAM, OLE_CONST void FAR*, DWORD))WriteStream; return TRUE; Error: if (glpStream) { GlobalFree(glpStream); glpStream = NULL; } if (glpclient) { GlobalFree(glpclient); glpclient = NULL; } return FALSE; } /* EndClient() - Clean up for termination. */ VOID EndClient( VOID ) { if (glpStream) { GlobalFree(glpStream); glpStream = NULL; } if (glpclient) { GlobalFree(glpclient); glpclient = NULL; } } /* PicCreate() - */ LPPICT PicCreate( LPOLEOBJECT lpObject, LPRECT lprcObject ) { HANDLE hpict = NULL; LPPICT lppict = NULL; RECT rc; if (lpObject) { if (!(hpict = GlobalAlloc(GMEM_MOVEABLE, sizeof(PICT))) || !(lppict = (LPPICT)GlobalLock(hpict))) goto errRtn; // // If size of window is specified, use it; otherwise, retrieve // the size of the item synchronously. // if (lprcObject) rc = *lprcObject; else { SetRectEmpty(&rc); PicGetBounds(lpObject, &rc); } // Store the data in the window itself lppict->hdata = hpict; lppict->lpObject = lpObject; lppict->rc = rc; lppict->fNotReady = FALSE; } return lppict; errRtn: ErrorMessage(E_FAILED_TO_CREATE_CHILD_WINDOW); if (lppict) GlobalUnlock(hpict); if (hpict) GlobalFree(hpict); return NULL; } /* PicDelete() - Deletes an object (called when the item window is destroyed). */ VOID PicDelete( LPPICT lppict ) { HANDLE hdata; LPOLEOBJECT lpObject; if (!lppict) return; if (lppict && lppict->lpObject) { lpObject = lppict->lpObject; lppict->lpObject = NULL; // Wait until the object isn't busy WaitForObject(lpObject); if (Error(OleDelete(lpObject))) ErrorMessage(E_FAILED_TO_DELETE_OBJECT); // Wait until the object deletion is complete WaitForObject(lpObject); } GlobalUnlock(hdata = lppict->hdata); GlobalFree(hdata); } /* PicDraw() - Draws the item associated with hwnd in the DC hDC. */ BOOL PicDraw( LPPICT lppict, HDC hDC, LPRECT lprc, INT xHSB, INT yVSB, BOOL fPicture, BOOL fFocus ) { BOOL fSuccess = FALSE; DWORD ot; HANDLE hdata; HFONT hfont; LPOLEOBJECT lpObjectUndo; LPSTR lpdata; RECT rc; RECT rcFocus; CHAR szDesc[CBMESSAGEMAX]; CHAR szFileName[CBPATHMAX]; CHAR szMessage[CBMESSAGEMAX + CBPATHMAX]; INT iDelta; INT iPane; iPane = (lppict == glpobj[CONTENT]); lpObjectUndo = (gptyUndo[iPane] == PICTURE) ? ((LPPICT)glpobjUndo[iPane])->lpObject : NULL; // If drawing the Picture, offset by scroll bars and draw if (fPicture) { if (IsRectEmpty(&(lppict->rc))) PicGetBounds(lppict->lpObject, &(lppict->rc)); rc = lppict->rc; // If image is smaller than pane, center horizontally if ((iDelta = lprc->right - lppict->rc.right) > 0) OffsetRect(&rc, iDelta >> 1, 0); else /* else, use the scroll bar value */ OffsetRect(&rc, -xHSB, 0); // If image is smaller than pane, center vertically if ((iDelta = lprc->bottom - lppict->rc.bottom) > 0) OffsetRect(&rc, 0, iDelta >> 1); else /* else, use the scroll bar value */ OffsetRect(&rc, 0, -yVSB); // If we have an object, call OleDraw() fSuccess = !Error(OleDraw(lppict->lpObject, hDC, &rc, NULL, NULL)); if (fFocus) DrawFocusRect(hDC, &rc); return fSuccess; } // Otherwise, draw the description string OleQueryType(lppict->lpObject, &ot); if ((ot == OT_LINK && Error(OleGetData(lppict->lpObject, gcfLink, &hdata))) || (ot == OT_EMBEDDED && Error(OleGetData(lppict->lpObject, gcfOwnerLink, &hdata))) || (ot == OT_STATIC && (!lpObjectUndo || Error(OleGetData(lpObjectUndo, gcfOwnerLink, &hdata))))) { LoadString(ghInst, IDS_OBJECT, szFileName, CBMESSAGEMAX); LoadString(ghInst, IDS_FROZEN, szDesc, CBMESSAGEMAX); goto DrawString; } if (hdata && (lpdata = GlobalLock(hdata))) { switch (ot) { case OT_LINK: while (*lpdata++) ; // return value ignored if(SUCCEEDED(StringCchCopy(szFileName, ARRAYSIZE(szFileName), lpdata))) { Normalize(szFileName); LoadString(ghInst, IDS_LINKTOFILE, szDesc, CBMESSAGEMAX); } break; case OT_EMBEDDED: RegGetClassId(szFileName, ARRAYSIZE(szFileName), lpdata); LoadString(ghInst, IDS_EMBEDFILE, szDesc, CBMESSAGEMAX); break; case OT_STATIC: RegGetClassId(szFileName, ARRAYSIZE(szFileName), lpdata); LoadString(ghInst, IDS_FROZEN, szDesc, CBMESSAGEMAX); break; } GlobalUnlock(hdata); DrawString: if(SUCCEEDED(StringCchPrintf(szMessage, ARRAYSIZE(szMessage), szDesc, szFileName))) // return value ignored { hfont = SelectObject(hDC, ghfontChild); DrawText(hDC, szMessage, -1, lprc, DT_NOPREFIX | DT_CENTER | DT_VCENTER | DT_SINGLELINE); if (fFocus) { rcFocus = *lprc; DrawText(hDC, szMessage, -1, &rcFocus, DT_CALCRECT | DT_NOPREFIX | DT_LEFT | DT_TOP | DT_SINGLELINE); OffsetRect(&rcFocus, (lprc->left + lprc->right - rcFocus.right) / 2, (lprc->top + lprc->bottom - rcFocus.bottom) / 2); DrawFocusRect(hDC, &rcFocus); } if (hfont) SelectObject(hDC, hfont); fSuccess = TRUE; } } return fSuccess; } /* PicPaste() - Retrieves an object from the clipboard. */ LPPICT PicPaste( BOOL fPaste, LPSTR lpstrName ) { LPOLEOBJECT lpObject; if (!OpenClipboard(ghwndFrame)) return NULL; /* Couldn't open the clipboard */ Hourglass(TRUE); // Don't replace the current object unless we're successful if (fPaste) { if (Error(OleCreateFromClip(gszProtocol, glpclient, glhcdoc, lpstrName, &lpObject, olerender_draw, 0))) { if (Error(OleCreateFromClip(gszSProtocol, glpclient, glhcdoc, lpstrName, &lpObject, olerender_draw, 0))) lpObject = NULL; } } else if (Error(OleCreateLinkFromClip( gszProtocol, glpclient, glhcdoc, lpstrName, &lpObject, olerender_draw, 0))) { lpObject = NULL; } CloseClipboard(); Hourglass(FALSE); if (!lpObject) return NULL; return PicCreate(lpObject, NULL); } /* Error() - check for OLE function error conditions * * This function increments gcOleWait as appropriate. * * Pre: Initialize ghwndError to where the focus should return. * * Returns: TRUE if an immediate error occurred. */ BOOL Error( OLESTATUS olestat ) { DWORD ot; INT iPane; switch (olestat) { case OLE_WAIT_FOR_RELEASE: gcOleWait++; return FALSE; case OLE_OK: return FALSE; case OLE_ERROR_STATIC: /* Only happens w/ dbl click */ ErrorMessage(W_STATIC_OBJECT); break; case OLE_ERROR_ADVISE_PICT: case OLE_ERROR_OPEN: /* Invalid link? */ case OLE_ERROR_NAME: iPane = (GetTopWindow(ghwndFrame) == ghwndPane[CONTENT]); if ((LPPICT)glpobj[iPane] == NULL) { ErrorMessage(E_FAILED_TO_CREATE_OBJECT); return FALSE; } else { OleQueryType(((LPPICT)glpobj[iPane])->lpObject, &ot); if (ot == OT_LINK) { if (ghwndError == ghwndFrame) { if (DialogBoxAfterBlock ( MAKEINTRESOURCE(DTINVALIDLINK), ghwndError, fnInvalidLink) == IDD_CHANGE) PostMessage(ghwndFrame, WM_COMMAND, IDM_LINKS, 0L); } else { // Failed, but already in Link Properties!! ErrorMessage(E_FAILED_TO_UPDATE_LINK); } return FALSE; } } break; default: break; } return TRUE; } /* CallBack() - Routine that OLE client DLL calls when events occur. * * This routine is called when the object has been updated and may * need to be redisplayed; if asynchronous operations have completed; * and if the application allows the user to cancel long operations * (like Painting, or other asynchronous operations). */ INT CALLBACK CallBack( LPOLECLIENT lpclient, OLE_NOTIFICATION flags, LPOLEOBJECT lpObject ) { INT iPane; switch (flags) { case OLE_CLOSED: if (gfInvisible) PostMessage(ghwndFrame, WM_SYSCOMMAND, SC_CLOSE, 0L); else SetFocus(ghwndError); break; case OLE_SAVED: case OLE_CHANGED: { // // The OLE libraries make sure that we only receive // update messages according to the Auto/Manual flags. // iPane = (gpty[CONTENT] == PICTURE && ((LPPICT)glpobj[CONTENT])->lpObject == lpObject); if (gpty[iPane] == PICTURE) { ((LPPICT)glpobj[iPane])->fNotReady = FALSE; InvalidateRect(ghwndPane[iPane], NULL, TRUE); SetRect(&(((LPPICT)glpobj[iPane])->rc), 0, 0, 0, 0); Dirty(); } break; } case OLE_RELEASE: { if (gcOleWait) gcOleWait--; else ErrorMessage(E_UNEXPECTED_RELEASE); switch (Error(OleQueryReleaseError(lpObject))) { case FALSE: switch (OleQueryReleaseMethod(lpObject)) { case OLE_SETUPDATEOPTIONS: iPane = (gpty[CONTENT] == PICTURE && ((LPPICT)glpobj[CONTENT])->lpObject == lpObject); PostMessage(ghwndPane[iPane], WM_COMMAND, IDM_LINKDONE, 0L); default: break; } break; case TRUE: switch (OleQueryReleaseMethod(lpObject)) { case OLE_DELETE: ErrorMessage(E_FAILED_TO_DELETE_OBJECT); break; case OLE_LOADFROMSTREAM: ErrorMessage(E_FAILED_TO_READ_OBJECT); break; case OLE_LNKPASTE: ErrorMessage(E_GET_FROM_CLIPBOARD_FAILED); break; case OLE_ACTIVATE: ErrorMessage(E_FAILED_TO_LAUNCH_SERVER); break; case OLE_UPDATE: ErrorMessage(E_FAILED_TO_UPDATE); break; case OLE_RECONNECT: ErrorMessage(E_FAILED_TO_RECONNECT_OBJECT); break; } break; } break; } case OLE_QUERY_RETRY: // if lpObject doesn't match any one of these 4 objects, it means // that PicDelete() has been called on lpObject, so there is no // point in continueing the RETRIES. // See PicDelete() code for more info. if ((glpobj[CONTENT] && lpObject == ((LPPICT)glpobj[CONTENT])->lpObject) || (glpobj[APPEARANCE] && lpObject == ((LPPICT) glpobj[APPEARANCE])->lpObject) || (glpobjUndo[CONTENT] && lpObject == ((LPPICT) glpobjUndo[CONTENT])->lpObject) || (glpobjUndo[APPEARANCE] && lpObject == ((LPPICT) glpobjUndo[APPEARANCE])->lpObject)) { return TRUE; } else { return FALSE; } case OLE_QUERY_PAINT: return TRUE; default: break; } return 0; } /* WaitForObject() - Waits, dispatching messages, until the object is free. * * If the object is busy, spin in a dispatch loop. */ VOID WaitForObject( LPOLEOBJECT lpObject ) { while (OleQueryReleaseStatus(lpObject) == OLE_BUSY) ProcessMessage(); } /* PicSetUpdateOptions() - Sets the update options of the object. * * Returns: TRUE if the command completed synchronously. */ BOOL PicSetUpdateOptions( LPPICT lppict, UINT idCmd ) { OLESTATUS olestat = OLE_ERROR_GENERIC; olestat = OleSetLinkUpdateOptions( lppict->lpObject, (idCmd == IDD_AUTO) ? oleupdate_always : oleupdate_oncall); if (Error(olestat)) ErrorMessage(E_FAILED_TO_UPDATE_LINK); return (olestat == OLE_OK); } /* PicReadFromNative() - Reads an object from the pointer lpstr. * * SIDE EFFECT: Advances the pointer past the object. */ LPPICT PicReadFromNative( LPSTR *lplpstr, LPSTR lpstrName ) { LPOLEOBJECT lpObject; LPSTR lpstrStart; RECT rcObject; WORD w; // Save current position of file pointer lpstrStart = *lplpstr; SetFile(SOP_MEMORY, 0, lplpstr); // Load the new object if (Error(OleLoadFromStream((LPOLESTREAM)glpStream, gszProtocol, glpclient, glhcdoc, lpstrName, &lpObject))) { // Reset file pointer, and try again *lplpstr = lpstrStart; SetFile(SOP_MEMORY, 0, lplpstr); // Read it with the "Static" protocol if (Error(OleLoadFromStream((LPOLESTREAM)glpStream, gszSProtocol, glpclient, glhcdoc, lpstrName, &lpObject))) return NULL; } MemRead(lplpstr, (LPSTR)&w, sizeof(WORD)); rcObject.left = (INT)w; MemRead(lplpstr, (LPSTR)&w, sizeof(WORD)); rcObject.top = (INT)w; MemRead(lplpstr, (LPSTR)&w, sizeof(WORD)); rcObject.right = (INT)w; MemRead(lplpstr, (LPSTR)&w, sizeof(WORD)); rcObject.bottom = (INT)w; // Create a window at the right place, and display the object return PicCreate(lpObject, &rcObject); } /* PicWriteToNative() - Writes an object to memory. * * SIDE EFFECT: Moves pointer to end of written object */ DWORD PicWriteToNative( LPPICT lppict, LPOLEOBJECT lpObject, LPSTR *lplpstr ) { DWORD cb = 0L; WORD w; // Save the object SetFile(SOP_MEMORY, 0, lplpstr); if (Error(OleSaveToStream(lpObject, (LPOLESTREAM)glpStream))) goto Done; cb += gcbObject; if (lplpstr) { w = (WORD)lppict->rc.left; MemWrite(lplpstr, (LPSTR)&w, sizeof(WORD)); w = (WORD)lppict->rc.top; MemWrite(lplpstr, (LPSTR)&w, sizeof(WORD)); w = (WORD)lppict->rc.right; MemWrite(lplpstr, (LPSTR)&w, sizeof(WORD)); w = (WORD)lppict->rc.bottom; MemWrite(lplpstr, (LPSTR)&w, sizeof(WORD)); } cb += (DWORD)(4 * sizeof(WORD)); Done: return cb; } /* Hourglass() - Puts up the hourglass as needed. */ VOID Hourglass( BOOL fOn ) { static HCURSOR hcurSaved = NULL; // Cursor saved when hourglass is up static UINT cWait = 0; // Number of "Hourglass"es up if (fOn) { if (!(cWait++)) hcurSaved = SetCursor(ghcurWait); } else { if (!(--cWait) && hcurSaved) { SetCursor(hcurSaved); hcurSaved = NULL; } } } VOID PicActivate( LPPICT lppict, UINT idCmd ) { DWORD ot; DWORD ot2; RECT rc; INT iPane; BOOL bAlreadyOpen = FALSE; iPane = (lppict == glpobj[CONTENT]); OleQueryType(lppict->lpObject, &ot); if (ot != OT_STATIC) { // Compute the window dimensions GetClientRect(ghwndPane[iPane], &rc); bAlreadyOpen = (OleQueryOpen(lppict->lpObject) == OLE_OK); // Open the object if (Error(OleActivate(lppict->lpObject, (idCmd == IDD_PLAY ? OLE_PLAY : OLE_EDIT), TRUE, TRUE, ghwndPane[iPane], &rc))) { ErrorMessage(E_FAILED_TO_LAUNCH_SERVER); goto errRtn; } else { WaitForObject(lppict->lpObject); if (!glpobj[iPane]) goto errRtn; OleQueryType(lppict->lpObject, &ot2); if (ot2 == OT_EMBEDDED) Error(OleSetHostNames(lppict->lpObject, gszAppClassName, (iPane == CONTENT) ? szContent : szAppearance)); } return; } else { ErrorMessage(W_STATIC_OBJECT); } errRtn: if (gfInvisible && !bAlreadyOpen) PostMessage(ghwndFrame, WM_SYSCOMMAND, SC_CLOSE, 0L); } VOID PicUpdate( LPPICT lppict ) { DWORD ot; OleQueryType(lppict->lpObject, &ot); if (ot == OT_LINK) { if (Error(OleUpdate(lppict->lpObject))) ErrorMessage(E_FAILED_TO_UPDATE); } } VOID PicFreeze( LPPICT lppict ) { DWORD ot; LPOLEOBJECT lpObject; INT iPane; iPane = (lppict == glpobj[CONTENT]); OleQueryType(lppict->lpObject, &ot); if (ot != OT_STATIC) { if (Error(OleObjectConvert(lppict->lpObject, gszSProtocol, glpclient, glhcdoc, gszCaption[iPane], &lpObject))) { ErrorMessage(E_FAILED_TO_FREEZE); return; } if (Error(OleDelete(lppict->lpObject))) ErrorMessage(E_FAILED_TO_DELETE_OBJECT); lppict->lpObject = lpObject; // Redraw the list box contents PostMessage(ghwndError, WM_REDRAW, 0, 0L); } } VOID PicChangeLink( LPPICT lppict ) { HANDLE hData; OLESTATUS olestat; // Change the link information olestat = OleGetData(lppict->lpObject, gcfLink, &hData); if (!Error(olestat) && hData) { hData = OfnGetNewLinkName(ghwndError, hData); if (hData && !Error(OleSetData(lppict->lpObject, gcfLink, hData))) PostMessage(ghwndError, WM_REDRAW, 0, 0L); } } /* PicCopy() - Puts an object onto the clipboard. * * Returns: TRUE iff successful. */ BOOL PicCopy( LPPICT lppict ) { BOOL fSuccess = FALSE; // If we can't open the clipboard, fail if (!lppict->lpObject || !OpenClipboard(ghwndFrame)) return FALSE; // Empty the clipboard EmptyClipboard(); // Successful if we managed to copy to the clipboard fSuccess = !Error(OleCopyToClipboard(lppict->lpObject)); CloseClipboard(); return fSuccess; } /* PicGetBounds() - */ static VOID PicGetBounds( LPOLEOBJECT lpObject, LPRECT lprc ) { if (IsRectEmpty(lprc)) { switch (OleQueryBounds(lpObject, lprc)) { case OLE_WAIT_FOR_RELEASE: Hourglass(TRUE); gcOleWait++; WaitForObject(lpObject); Hourglass(FALSE); case OLE_OK: // Map from HIMETRIC into screen coordinates lprc->right = MulDiv(giXppli, lprc->right - lprc->left, HIMETRIC_PER_INCH); lprc->bottom = MulDiv (giYppli, lprc->top - lprc->bottom, HIMETRIC_PER_INCH); lprc->left = 0; lprc->top = 0; default: break; } } } /* PicSaveUndo() - Saves a copy of the object for Undo. */ VOID PicSaveUndo( LPPICT lppict ) { INT iPane = (lppict == glpobj[CONTENT]); LPOLEOBJECT lpObject; // Clone the object if (Error(OleClone(lppict->lpObject, glpclient, glhcdoc, gszTemp, &lpObject)) || !lpObject) { ErrorMessage(W_FAILED_TO_CLONE_UNDO); } else { // Save the undo, delete the prior Undo DeletePane(iPane, FALSE); OleRename(lpObject, gszCaption[iPane]); glpobj[iPane] = PicCreate(lpObject, &(lppict->rc)); gpty[iPane] = PICTURE; if (iPane == CONTENT) EnableWindow(ghwndPict, TRUE); } } /* PicPaste() - Creates object from a file */ LPPICT PicFromFile( BOOL fEmbedded, LPSTR szFile ) { HRESULT hr; LPOLEOBJECT lpObject; Hourglass(TRUE); // Don't replace the current object unless we're successful if (fEmbedded) { hr = OleCreateFromFile(gszProtocol, glpclient, NULL, szFile, glhcdoc, gszCaption[CONTENT], &lpObject, olerender_draw, 0); } else { hr = OleCreateLinkFromFile(gszProtocol, glpclient, NULL, szFile, NULL, glhcdoc, gszCaption[CONTENT], &lpObject, olerender_draw, 0); } Hourglass(FALSE); if (FAILED(hr)) return NULL; WaitForObject(lpObject); return PicCreate(lpObject, NULL); } LPOLECLIENT PicCreateClient( PCALL_BACK fnCallBack, LPOLECLIENTVTBL lpclivtbl ) { LPOLECLIENT pclient; if (!(pclient = (LPOLECLIENT)GlobalAlloc(GMEM_FIXED | GMEM_ZEROINIT, sizeof(OLECLIENT)))) return NULL; pclient->lpvtbl = lpclivtbl; pclient->lpvtbl->CallBack = fnCallBack; return pclient; }