//------------------------------------------------------------------------------ // icmdtgt.cpp // Copyright (c)1997-1999 Microsoft Corporation, All Rights Reserved // // Author // bash // // History // 7-19-97 created (bash) // // Implementation of IOleCommandTarget // //------------------------------------------------------------------------------ #include "stdafx.h" #include #include //#include "mfcincl.h" #include "triedit.h" #include "document.h" #include "triedcid.h" //TriEdit Command IDs here. #include "dispatch.h" #include "undo.h" #define CMDSTATE_NOTSUPPORTED 0 #define CMDSTATE_DISABLED OLECMDF_SUPPORTED #define CMDSTATE_UP (OLECMDF_SUPPORTED | OLECMDF_ENABLED) #define CMDSTATE_DOWN (OLECMDF_SUPPORTED | OLECMDF_ENABLED | OLECMDF_LATCHED) #define CMDSTATE_NINCHED (OLECMDF_SUPPORTED | OLECMDF_ENABLED | OLECMDF_NINCHED) // Mapping from TriEdit to Trident commands typedef struct { ULONG cmdTriEdit; ULONG cmdTrident; } CMDMAP; /////////////////////////////////////////////////////////////////////////////// // // CTriEditDocument::MapTriEditCommand // // Map the given TriEdit IDM to the equivalent Trident IDM. // // Return: // Mapped command under *pCmdTrident and S_OK for a valid command. // E_FAIL for an invalid command. // HRESULT CTriEditDocument::MapTriEditCommand(ULONG cmdTriEdit, ULONG *pCmdTrident) { static CMDMAP rgCmdMap[] = { { IDM_TRIED_ACTIVATEACTIVEXCONTROLS, IDM_NOACTIVATENORMALOLECONTROLS }, { IDM_TRIED_ACTIVATEAPPLETS, IDM_NOACTIVATEJAVAAPPLETS }, { IDM_TRIED_ACTIVATEDTCS, IDM_NOACTIVATEDESIGNTIMECONTROLS }, { IDM_TRIED_BACKCOLOR, IDM_BACKCOLOR }, { IDM_TRIED_BLOCKFMT, IDM_BLOCKFMT }, { IDM_TRIED_BOLD, IDM_BOLD }, { IDM_TRIED_BROWSEMODE, IDM_BROWSEMODE }, { IDM_TRIED_COPY, IDM_COPY }, { IDM_TRIED_CUT, IDM_CUT }, { IDM_TRIED_DELETE, IDM_DELETE }, { IDM_TRIED_EDITMODE, IDM_EDITMODE }, { IDM_TRIED_FIND, IDM_FIND }, { IDM_TRIED_FONT, IDM_FONT }, { IDM_TRIED_FONTNAME, IDM_FONTNAME }, { IDM_TRIED_FONTSIZE, IDM_FONTSIZE }, { IDM_TRIED_FORECOLOR, IDM_FORECOLOR }, { IDM_TRIED_GETBLOCKFMTS, IDM_GETBLOCKFMTS }, { IDM_TRIED_HYPERLINK, IDM_HYPERLINK }, { IDM_TRIED_IMAGE, IDM_IMAGE }, { IDM_TRIED_INDENT, IDM_INDENT }, { IDM_TRIED_ITALIC, IDM_ITALIC }, { IDM_TRIED_JUSTIFYCENTER, IDM_JUSTIFYCENTER }, { IDM_TRIED_JUSTIFYLEFT, IDM_JUSTIFYLEFT }, { IDM_TRIED_JUSTIFYRIGHT, IDM_JUSTIFYRIGHT }, { IDM_TRIED_ORDERLIST, IDM_ORDERLIST }, { IDM_TRIED_OUTDENT, IDM_OUTDENT }, { IDM_TRIED_PASTE, IDM_PASTE }, { IDM_TRIED_PRINT, IDM_PRINT }, { IDM_TRIED_REDO, IDM_REDO }, { IDM_TRIED_REMOVEFORMAT, IDM_REMOVEFORMAT }, { IDM_TRIED_SELECTALL, IDM_SELECTALL }, { IDM_TRIED_SHOWBORDERS, IDM_SHOWZEROBORDERATDESIGNTIME }, { IDM_TRIED_SHOWDETAILS, IDM_SHOWALLTAGS }, { IDM_TRIED_UNDERLINE, IDM_UNDERLINE }, { IDM_TRIED_UNDO, IDM_UNDO }, { IDM_TRIED_UNLINK, IDM_UNLINK }, { IDM_TRIED_UNORDERLIST, IDM_UNORDERLIST } }; if (NULL == pCmdTrident) return E_POINTER; for (int i=0; i < sizeof(rgCmdMap)/sizeof(CMDMAP); ++i) { if (cmdTriEdit == rgCmdMap[i].cmdTriEdit) { *pCmdTrident = rgCmdMap[i].cmdTrident; return S_OK; } } return E_FAIL; } /////////////////////////////////////////////////////////////////////////////// // // CTriEditDocument::SetUpDefaults // // Set Trident flags to the TriEdit default values: // // IDM_PRESERVEUNDOALWAYS On // IDM_NOFIXUPURLSONPASTE On // IDM_NOACTIVATEDESIGNTIMECONTROLS Off // IDM_NOACTIVATEJAVAAPPLETS On // IDM_NOACTIVATENORMALOLECONTROLS On // // // No return value. void CTriEditDocument::SetUpDefaults() { VARIANT var; // Turn on Trident's preserve undo flag for setting properties V_VT(&var) = VT_BOOL; V_BOOL(&var) = TRUE; m_pCmdTgtTrident->Exec(&CMDSETID_Forms3, 6049, // IDM_PRESERVEUNDOALWAYS OLECMDEXECOPT_DONTPROMPTUSER, &var, NULL); // Turn on Trident's url fixup flag for paste and drag-drop V_VT(&var) = VT_BOOL; V_BOOL(&var) = TRUE; m_pCmdTgtTrident->Exec(&CMDSETID_Forms3, 2335, // IDM_NOFIXUPURLSONPASTE OLECMDEXECOPT_DONTPROMPTUSER, &var, NULL); // Set up defaults for Activating DTCs but not Applets or other ActiveX Controls V_VT(&var) = VT_BOOL; V_BOOL(&var) = FALSE; m_pCmdTgtTrident->Exec(&CMDSETID_Forms3, IDM_NOACTIVATEDESIGNTIMECONTROLS, OLECMDEXECOPT_DONTPROMPTUSER, &var, NULL); V_VT(&var) = VT_BOOL; V_BOOL(&var) = TRUE; m_pCmdTgtTrident->Exec(&CMDSETID_Forms3, IDM_NOACTIVATEJAVAAPPLETS, OLECMDEXECOPT_DONTPROMPTUSER, &var, NULL); V_VT(&var) = VT_BOOL; V_BOOL(&var) = TRUE; m_pCmdTgtTrident->Exec(&CMDSETID_Forms3, IDM_NOACTIVATENORMALOLECONTROLS, OLECMDEXECOPT_DONTPROMPTUSER, &var, NULL); } /////////////////////////////////////////////////////////////////////////////// // // // CTriEditDocument::SetUpGlyphTable // // Load the glyphs from the DLL and install them in Trident's table. No return // value. // void CTriEditDocument::SetUpGlyphTable(BOOL fDetails) { VARIANT var; const int RuleMax = 100; // This needs to be updated if we ever have a long rule const int PathMax = 256; // For %program files%\common files\microsoft shared\triedit\triedit.dll int iGlyphTableStart = IDS_GLYPHTABLESTART; int iGlyphTableEnd = fDetails ? IDS_GLYPHTABLEEND : IDS_GLYPHTABLEFORMEND; TCHAR szPathName[PathMax]; TCHAR szRule[RuleMax + PathMax]; TCHAR szGlyphTable[(RuleMax + PathMax) * (IDS_GLYPHTABLEEND - IDS_GLYPHTABLESTART + 1)]; TCHAR *pchGlyphTable, *pchTemp; // Get full path name for triedit.dll ::GetModuleFileName(_Module.GetModuleInstance(), szPathName, sizeof(szPathName) ); // Load glyph table pchGlyphTable = szGlyphTable; for (int i = iGlyphTableStart; i <= iGlyphTableEnd; i++) { ::LoadString(_Module.GetModuleInstance(), i, szRule, RuleMax); pchTemp = wcsstr(szRule, _T("!")); if (pchTemp) // else bad rule, ignore { *pchTemp = 0; // Copy upto the "!" wcscpy(pchGlyphTable, szRule); pchGlyphTable += wcslen(szRule); // Append pathname wcscpy(pchGlyphTable, szPathName); pchGlyphTable += wcslen(szPathName); // Skip past "!" pchTemp = pchTemp + 1; // Copy remaining characters wcscpy(pchGlyphTable, pchTemp); pchGlyphTable += wcslen(pchTemp); } } // First empty the glyph table m_pCmdTgtTrident->Exec(&CMDSETID_Forms3, IDM_EMPTYGLYPHTABLE, OLECMDEXECOPT_DONTPROMPTUSER, NULL, NULL); V_VT(&var) = VT_BSTR; V_BSTR(&var) = SysAllocString(szGlyphTable); m_pCmdTgtTrident->Exec(&CMDSETID_Forms3, IDM_ADDTOGLYPHTABLE, OLECMDEXECOPT_DONTPROMPTUSER, &var, NULL); VariantInit(&var); } /////////////////////////////////////////////////////////////////////////////// // // CTriEditDocument::QueryStatus // // Report on the status of the given array of TriEdit and Trident commands. // Pass Trident commands on to Trident. Fix the Trident return value to // compensate for some inconsistencies. Return S_OK if all goes well, or // E_FAIL if not. // STDMETHODIMP CTriEditDocument::QueryStatus(const GUID *pguidCmdGroup, ULONG cCmds, OLECMD prgCmds[], OLECMDTEXT *pCmdText) { OLECMD *pCmd; INT c; HRESULT hr; if (pguidCmdGroup && IsEqualGUID((const GUID&)*pguidCmdGroup, GUID_TriEditCommandGroup)) { // Loop through each command in the ary, setting the status of each. for (pCmd = prgCmds, c = cCmds; --c >= 0; pCmd++) { // Assume this is a valid command and set default command status to DISABLED. // The state will get reset to UP, DOWN or NOTSUPPORTED in the switch statement below. pCmd->cmdf = CMDSTATE_DISABLED; switch(pCmd->cmdID) { case IDM_TRIED_IS_1D_ELEMENT: case IDM_TRIED_IS_2D_ELEMENT: { if (SUCCEEDED(GetElement()) && m_pihtmlElement) { pCmd->cmdf = CMDSTATE_UP; } break; } case IDM_TRIED_SET_ALIGNMENT: { pCmd->cmdf = CMDSTATE_UP; break; } case IDM_TRIED_LOCK_ELEMENT: { if (SUCCEEDED(GetElement()) && m_pihtmlElement) { BOOL f2d=FALSE; if (SUCCEEDED(Is2DElement(m_pihtmlElement, &f2d)) && f2d) { BOOL fLocked=FALSE; pCmd->cmdf = (SUCCEEDED(IsLocked(m_pihtmlElement, &fLocked)) && fLocked) ? CMDSTATE_DOWN : CMDSTATE_UP; } } break; } case IDM_TRIED_CONSTRAIN: { pCmd->cmdf = (m_fConstrain) ? CMDSTATE_DOWN : CMDSTATE_UP; break; } case IDM_TRIED_SEND_TO_BACK: case IDM_TRIED_SEND_TO_FRONT: case IDM_TRIED_SEND_BACKWARD: case IDM_TRIED_SEND_FORWARD: case IDM_TRIED_SEND_BEHIND_1D: case IDM_TRIED_SEND_FRONT_1D: { if (SUCCEEDED(GetElement()) && m_pihtmlElement) { BOOL f2d=FALSE; if (SUCCEEDED(Is2DElement(m_pihtmlElement, &f2d)) && f2d) { pCmd->cmdf = CMDSTATE_UP; } } break; } case IDM_TRIED_NUDGE_ELEMENT: { BOOL f2d = FALSE; if (SUCCEEDED(GetElement()) && m_pihtmlElement && SUCCEEDED(Is2DElement(m_pihtmlElement, &f2d)) && f2d) { BOOL fLock = FALSE; if (!(SUCCEEDED(IsLocked(m_pihtmlElement, &fLock)) && fLock)) pCmd->cmdf = CMDSTATE_UP; } break; } case IDM_TRIED_MAKE_ABSOLUTE: { if (SUCCEEDED(GetElement()) && m_pihtmlElement) { BOOL f2d = FALSE; if (SUCCEEDED(IsElementDTC(m_pihtmlElement))) break; if (SUCCEEDED(Is2DElement(m_pihtmlElement, &f2d))) { BOOL f2dCapable=FALSE; if ( f2d ) { pCmd->cmdf = CMDSTATE_DOWN; } else if (SUCCEEDED(Is2DCapable(m_pihtmlElement, &f2dCapable)) && f2dCapable) { pCmd->cmdf = CMDSTATE_UP; } } } break; } case IDM_TRIED_SET_2D_DROP_MODE: { pCmd->cmdf = (m_f2dDropMode) ? CMDSTATE_DOWN : CMDSTATE_UP; break; } case IDM_TRIED_INSERTROW: case IDM_TRIED_DELETEROWS: case IDM_TRIED_INSERTCELL: case IDM_TRIED_DELETECELLS: case IDM_TRIED_INSERTCOL: { pCmd->cmdf = (IsSelectionInTable() == S_OK && GetSelectionTypeInTable() != -1)? CMDSTATE_UP : CMDSTATE_DISABLED; break; } case IDM_TRIED_MERGECELLS: { ULONG grf = IsSelectionInTable() == S_OK ? GetSelectionTypeInTable() : 0; pCmd->cmdf = ( (grf != -1) && (!(grf & grfSelectOneCell) && (grf & (grfInSingleRow|grpSelectEntireRow)))) ? CMDSTATE_UP : CMDSTATE_DISABLED; break; } case IDM_TRIED_SPLITCELL: { ULONG grf = IsSelectionInTable() == S_OK ? GetSelectionTypeInTable() : 0; pCmd->cmdf = ((grf != -1) && (grf & grfSelectOneCell)) ? CMDSTATE_UP : CMDSTATE_DISABLED; break; } case IDM_TRIED_DELETECOLS: { ULONG grf = IsSelectionInTable() == S_OK ? GetSelectionTypeInTable() : 0; pCmd->cmdf = ((grf != -1) && (grf & grfInSingleRow)) ? CMDSTATE_UP : CMDSTATE_DISABLED; break; } case IDM_TRIED_INSERTTABLE: { pCmd->cmdf = FEnableInsertTable() ? CMDSTATE_UP : CMDSTATE_DISABLED; break; } case IDM_TRIED_DOVERB: { if (SUCCEEDED(GetElement()) && m_pihtmlElement && SUCCEEDED(DoVerb(NULL, TRUE))) pCmd->cmdf = CMDSTATE_UP; break; } case IDM_TRIED_ACTIVATEACTIVEXCONTROLS: case IDM_TRIED_ACTIVATEAPPLETS: case IDM_TRIED_ACTIVATEDTCS: case IDM_TRIED_BACKCOLOR: case IDM_TRIED_BLOCKFMT: case IDM_TRIED_BOLD: case IDM_TRIED_BROWSEMODE: case IDM_TRIED_COPY: case IDM_TRIED_CUT: case IDM_TRIED_DELETE: case IDM_TRIED_EDITMODE: case IDM_TRIED_FIND: case IDM_TRIED_FONT: case IDM_TRIED_FONTNAME: case IDM_TRIED_FONTSIZE: case IDM_TRIED_FORECOLOR: case IDM_TRIED_GETBLOCKFMTS: case IDM_TRIED_HYPERLINK: case IDM_TRIED_IMAGE: case IDM_TRIED_INDENT: case IDM_TRIED_ITALIC: case IDM_TRIED_JUSTIFYCENTER: case IDM_TRIED_JUSTIFYLEFT: case IDM_TRIED_JUSTIFYRIGHT: case IDM_TRIED_ORDERLIST: case IDM_TRIED_OUTDENT: case IDM_TRIED_PASTE: case IDM_TRIED_PRINT: case IDM_TRIED_REDO: case IDM_TRIED_REMOVEFORMAT: case IDM_TRIED_SELECTALL: case IDM_TRIED_SHOWBORDERS: case IDM_TRIED_SHOWDETAILS: case IDM_TRIED_UNDERLINE: case IDM_TRIED_UNDO: case IDM_TRIED_UNLINK: case IDM_TRIED_UNORDERLIST: { // We will return E_UNEXPECTED if Trident's command target is not available hr = E_UNEXPECTED; _ASSERTE(m_pCmdTgtTrident); if (m_pCmdTgtTrident) { OLECMD olecmd; olecmd.cmdf = pCmd->cmdf; if (SUCCEEDED(MapTriEditCommand(pCmd->cmdID, &olecmd.cmdID))) { hr = m_pCmdTgtTrident->QueryStatus(&CMDSETID_Forms3, 1, &olecmd, pCmdText); } pCmd->cmdf = olecmd.cmdf; } if (FAILED(hr)) return hr; // Trident returns NOTSUPPORTED sometimes when they really mean DISABLED, so we fix this up here. if (pCmd->cmdf == CMDSTATE_NOTSUPPORTED) pCmd->cmdf = CMDSTATE_DISABLED; // Trident returns CMDSTATE_DISABLED for IDM_TRIED_GETBLOCKFMTS but this command should never be disabled if (pCmd->cmdID == IDM_TRIED_GETBLOCKFMTS) pCmd->cmdf = CMDSTATE_UP; // Trident bug: Trident returns the wrong value for IDM_TRIED_SHOWBORDERS, // IDM_TRIED_SHOWDETAILS and the IDM_TRIED_ACTIVATE* commands, so we fix // them up here. We don't have code for IDM_TRIED_ACTIVATE* since the logic // of the Trident commands is actually reverse in these cases. if (pCmd->cmdID == IDM_TRIED_SHOWBORDERS || pCmd->cmdID == IDM_TRIED_SHOWDETAILS) { if (pCmd->cmdf == CMDSTATE_UP) pCmd->cmdf = CMDSTATE_DOWN; else if (pCmd->cmdf == CMDSTATE_DOWN) pCmd->cmdf = CMDSTATE_UP; } break; } default: { pCmd->cmdf = CMDSTATE_NOTSUPPORTED; break; } } // switch } // for return S_OK; } else if (m_pCmdTgtTrident) { hr = m_pCmdTgtTrident->QueryStatus(pguidCmdGroup, cCmds, prgCmds, pCmdText); if (hr != S_OK) return hr; // Loop through each command in the ary, fixing up the status of each. for (pCmd = prgCmds, c = cCmds; --c >= 0; pCmd++) { // Trident returns NOTSUPPORTED sometimes when they really mean DISABLED. if (pCmd->cmdf == CMDSTATE_NOTSUPPORTED) pCmd->cmdf = CMDSTATE_DISABLED; if (pguidCmdGroup && IsEqualGUID((const GUID&)*pguidCmdGroup, CMDSETID_Forms3)) { // Trident returns CMDSTATE_DISABLED for IDM_GETBLOCKFMTS but this command should never be disabled if (pCmd->cmdID == IDM_GETBLOCKFMTS) pCmd->cmdf = CMDSTATE_UP; // Trident bug: Trident returns the wrong value for IDM_SHOWZEROBORDER*, // IDM_SHOWALLTAGS and the IDM_NOACTIVATE* commands, so we fix // them up here. if (pCmd->cmdID == IDM_NOACTIVATENORMALOLECONTROLS || pCmd->cmdID == IDM_NOACTIVATEJAVAAPPLETS || pCmd->cmdID == IDM_NOACTIVATEDESIGNTIMECONTROLS || pCmd->cmdID == IDM_SHOWZEROBORDERATDESIGNTIME || pCmd->cmdID == IDM_SHOWALLTAGS) { if (pCmd->cmdf == CMDSTATE_UP) pCmd->cmdf = CMDSTATE_DOWN; else if (pCmd->cmdf == CMDSTATE_DOWN) pCmd->cmdf = CMDSTATE_UP; } } } return S_OK; } return E_UNEXPECTED; } /////////////////////////////////////////////////////////////////////////////// // // CTriEditDocument::Exec // // Perform the given TriEdit or Trident command. Pass Trident commands on to // Trident for execution. Return S_OK if all goes well or E_FAIL if not. // STDMETHODIMP CTriEditDocument::Exec(const GUID *pguidCmdGroup, DWORD nCmdID, DWORD nCmdExecOpt, VARIANTARG *pvaIn, VARIANTARG *pvaOut) { if (pguidCmdGroup && IsEqualGUID((const GUID&)*pguidCmdGroup, GUID_TriEditCommandGroup) && m_pUnkTrident) { HRESULT hr = GetElement(); switch(nCmdID) { case IDM_TRIED_IS_1D_ELEMENT: //[out,VT_BOOL] if (pvaOut && m_pihtmlElement && SUCCEEDED(VariantChangeType(pvaOut, pvaOut, 0, VT_BOOL))) { hr = Is2DElement(m_pihtmlElement, (BOOL*)&pvaOut->boolVal); _ASSERTE(SUCCEEDED(hr)); if (SUCCEEDED(hr)) { pvaOut->boolVal = !pvaOut->boolVal; } } break; case IDM_TRIED_IS_2D_ELEMENT: //[out,VT_BOOL] if (pvaOut && m_pihtmlElement && SUCCEEDED(VariantChangeType(pvaOut, pvaOut, 0, VT_BOOL))) { hr = Is2DElement(m_pihtmlElement, (BOOL*)&pvaOut->boolVal); _ASSERTE(SUCCEEDED(hr)); } break; case IDM_TRIED_NUDGE_ELEMENT: //[in,VT_BYREF (VARIANT.byref=LPPOINT)] { BOOL fLock = FALSE; IsLocked(m_pihtmlElement, &fLock); if (!pvaIn) hr = E_FAIL; else if (!fLock && VT_BYREF == pvaIn->vt && pvaIn->byref) { hr = NudgeElement(m_pihtmlElement, (LPPOINT)pvaIn->byref); _ASSERTE(SUCCEEDED(hr)); } } break; case IDM_TRIED_SET_ALIGNMENT: //[in,VT_BYREF (VARIANT.byref=LPPOINT)] if (!pvaIn) hr = E_FAIL; else if (VT_BYREF == pvaIn->vt && pvaIn->byref) { hr = SetAlignment((LPPOINT)pvaIn->byref); _ASSERTE(SUCCEEDED(hr)); } break; case IDM_TRIED_LOCK_ELEMENT: if (m_pihtmlElement) { BOOL f2d=FALSE; BOOL fLocked=TRUE; if (SUCCEEDED(Is2DElement(m_pihtmlElement, &f2d)) && f2d && SUCCEEDED(IsLocked(m_pihtmlElement, &fLocked))) { hr = LockElement(m_pihtmlElement, !fLocked); _ASSERTE(SUCCEEDED(hr)); } } break; case IDM_TRIED_SEND_TO_BACK: if (m_pihtmlElement) { hr = AssignZIndex(m_pihtmlElement, SEND_TO_BACK); _ASSERTE(SUCCEEDED(hr)); } break; case IDM_TRIED_SEND_TO_FRONT: if (m_pihtmlElement) { hr = AssignZIndex(m_pihtmlElement, SEND_TO_FRONT); _ASSERTE(SUCCEEDED(hr)); } break; case IDM_TRIED_SEND_BACKWARD: if (m_pihtmlElement) { hr = AssignZIndex(m_pihtmlElement, SEND_BACKWARD); _ASSERTE(SUCCEEDED(hr)); } break; case IDM_TRIED_SEND_FORWARD: if (m_pihtmlElement) { hr = AssignZIndex(m_pihtmlElement, SEND_FORWARD); _ASSERTE(SUCCEEDED(hr)); } break; case IDM_TRIED_SEND_BEHIND_1D: if (m_pihtmlElement) { hr = AssignZIndex(m_pihtmlElement, SEND_BEHIND_1D); _ASSERTE(SUCCEEDED(hr)); } break; case IDM_TRIED_SEND_FRONT_1D: if (m_pihtmlElement) { hr = AssignZIndex(m_pihtmlElement, SEND_FRONT_1D); _ASSERTE(SUCCEEDED(hr)); } break; case IDM_TRIED_CONSTRAIN: if (!pvaIn) hr = E_FAIL; else if (SUCCEEDED(hr = VariantChangeType(pvaIn, pvaIn, 0, VT_BOOL))) { hr = Constrain((BOOL)pvaIn->boolVal); } break; case IDM_TRIED_SET_2D_DROP_MODE: if (!pvaIn) hr = E_FAIL; else if (SUCCEEDED(hr = VariantChangeType(pvaIn, pvaIn, 0, VT_BOOL))) { m_f2dDropMode = pvaIn->boolVal; } break; case IDM_TRIED_INSERTROW: hr = InsertTableRow(); break; case IDM_TRIED_INSERTCOL: hr = InsertTableCol(); break; case IDM_TRIED_INSERTCELL: hr = InsertTableCell(); break; case IDM_TRIED_DELETEROWS: hr = DeleteTableRows(); break; case IDM_TRIED_DELETECOLS: hr = DeleteTableCols(); break; case IDM_TRIED_DELETECELLS: hr = DeleteTableCells(); break; case IDM_TRIED_MERGECELLS: hr = MergeTableCells(); break; case IDM_TRIED_SPLITCELL: hr = SplitTableCell(); break; case IDM_TRIED_INSERTTABLE: hr = InsertTable(pvaIn); break; case IDM_TRIED_DOVERB: if (m_pihtmlElement) hr = DoVerb(pvaIn, FALSE); else hr = E_FAIL; break; case IDM_TRIED_MAKE_ABSOLUTE: if (m_pihtmlElement) { BOOL f2d = FALSE; hr = Is2DElement(m_pihtmlElement, &f2d); if (SUCCEEDED(hr)) { BOOL f2dCapable=FALSE; if ( f2d ) { hr = Make1DElement(m_pihtmlElement); _ASSERTE(SUCCEEDED(hr)); } else if (SUCCEEDED(Is2DCapable(m_pihtmlElement, &f2dCapable)) && f2dCapable) { hr = Make2DElement(m_pihtmlElement); _ASSERTE(SUCCEEDED(hr)); } } } break; case IDM_TRIED_ACTIVATEACTIVEXCONTROLS: case IDM_TRIED_ACTIVATEAPPLETS: case IDM_TRIED_ACTIVATEDTCS: case IDM_TRIED_BACKCOLOR: case IDM_TRIED_BLOCKFMT: case IDM_TRIED_BOLD: case IDM_TRIED_BROWSEMODE: case IDM_TRIED_COPY: case IDM_TRIED_CUT: case IDM_TRIED_DELETE: case IDM_TRIED_EDITMODE: case IDM_TRIED_FIND: case IDM_TRIED_FONT: case IDM_TRIED_FONTNAME: case IDM_TRIED_FONTSIZE: case IDM_TRIED_FORECOLOR: case IDM_TRIED_GETBLOCKFMTS: case IDM_TRIED_HYPERLINK: case IDM_TRIED_IMAGE: case IDM_TRIED_INDENT: case IDM_TRIED_ITALIC: case IDM_TRIED_JUSTIFYCENTER: case IDM_TRIED_JUSTIFYLEFT: case IDM_TRIED_JUSTIFYRIGHT: case IDM_TRIED_ORDERLIST: case IDM_TRIED_OUTDENT: case IDM_TRIED_PASTE: case IDM_TRIED_PRINT: case IDM_TRIED_REDO: case IDM_TRIED_REMOVEFORMAT: case IDM_TRIED_SELECTALL: case IDM_TRIED_SHOWBORDERS: case IDM_TRIED_SHOWDETAILS: case IDM_TRIED_UNDERLINE: case IDM_TRIED_UNDO: case IDM_TRIED_UNLINK: case IDM_TRIED_UNORDERLIST: { ULONG cmdTrident; VARIANT varColor; // We will return E_FAIL if Trident's command target is not available hr = E_FAIL; _ASSERTE(m_pCmdTgtTrident); if (m_pCmdTgtTrident && (SUCCEEDED(MapTriEditCommand(nCmdID, &cmdTrident)))) { if (nCmdID == IDM_TRIED_ACTIVATEACTIVEXCONTROLS || nCmdID == IDM_TRIED_ACTIVATEAPPLETS || nCmdID == IDM_TRIED_ACTIVATEDTCS) { if (pvaIn && pvaIn->vt == VT_BOOL) pvaIn->boolVal = !pvaIn->boolVal; } // Trident bug: When you exec the forecolor, fontname or fontsize command, they also change the backcolor, // so we apply a workaround here. The workaround is to save the old backcolor and exec it later. if (pvaIn && (nCmdID == IDM_TRIED_FORECOLOR || nCmdID == IDM_TRIED_FONTNAME || nCmdID == IDM_TRIED_FONTSIZE)) { HRESULT hrT; VariantInit(&varColor); V_VT(&varColor) = VT_I4; hrT = m_pCmdTgtTrident->Exec(&CMDSETID_Forms3, IDM_BACKCOLOR, OLECMDEXECOPT_DONTPROMPTUSER, NULL, &varColor); _ASSERTE(SUCCEEDED(hrT)); } // Trident bug: When you exec the block format command with "Normal", they don't remove OL and UL tags if (pvaIn && nCmdID == IDM_TRIED_BLOCKFMT && pvaIn->vt == VT_BSTR && (_wcsicmp(pvaIn->bstrVal, L"Normal") == 0)) { OLECMD olecmd; olecmd.cmdID = IDM_ORDERLIST; olecmd.cmdf = CMDSTATE_NOTSUPPORTED; if (S_OK == m_pCmdTgtTrident->QueryStatus(&CMDSETID_Forms3, 1, &olecmd, NULL) && olecmd.cmdf == CMDSTATE_DOWN) m_pCmdTgtTrident->Exec(&CMDSETID_Forms3, IDM_ORDERLIST, OLECMDEXECOPT_DONTPROMPTUSER, NULL, NULL); olecmd.cmdID = IDM_UNORDERLIST; olecmd.cmdf = CMDSTATE_NOTSUPPORTED; if (S_OK == m_pCmdTgtTrident->QueryStatus(&CMDSETID_Forms3, 1, &olecmd, NULL) && olecmd.cmdf == CMDSTATE_DOWN) m_pCmdTgtTrident->Exec(&CMDSETID_Forms3, IDM_UNORDERLIST, OLECMDEXECOPT_DONTPROMPTUSER, NULL, NULL); } hr = m_pCmdTgtTrident->Exec(&CMDSETID_Forms3, cmdTrident, nCmdExecOpt, pvaIn, pvaOut); if (pvaIn && (nCmdID == IDM_TRIED_FORECOLOR || nCmdID == IDM_TRIED_FONTNAME || nCmdID == IDM_TRIED_FONTSIZE)) { HRESULT hrT; hrT = m_pCmdTgtTrident->Exec(&CMDSETID_Forms3, IDM_BACKCOLOR, OLECMDEXECOPT_DONTPROMPTUSER, &varColor, NULL); _ASSERTE(SUCCEEDED(hrT)); } else if (nCmdID == IDM_TRIED_SHOWDETAILS && pvaIn && pvaIn->vt == VT_BOOL) { SetUpGlyphTable(pvaIn->boolVal); } // Trident bug: They enable the justify commands but not actually support them. // We workaround this by returning S_OK for these no matter what Trident returns. if (nCmdID == IDM_TRIED_JUSTIFYLEFT || nCmdID == IDM_TRIED_JUSTIFYCENTER || nCmdID == IDM_TRIED_JUSTIFYRIGHT) hr = S_OK; } break; } default: hr = E_FAIL; break; } if (pvaIn) VariantClear(pvaIn); // We shouldn't return any unexpected error codes here, so return E_FAIL if (FAILED(hr)) hr = E_FAIL; return hr; } else if (m_pCmdTgtTrident) { HRESULT hr; BOOL fTridentCmdSet; VARIANT varColor; fTridentCmdSet = pguidCmdGroup && IsEqualGUID((const GUID&)*pguidCmdGroup, CMDSETID_Forms3); #ifdef NEEDED if (fTridentCmdSet) { if (nCmdID == IDM_PARSECOMPLETE) OnObjectModelComplete(); return S_OK; } #endif //NEEDED // Trident bug: When you exec the forecolor, fontname or fontsize command, they also change the backcolor, // so we apply a workaround here. The workaround is to save the old backcolor and exec it later. if (pvaIn && fTridentCmdSet && (nCmdID == IDM_FORECOLOR || nCmdID == IDM_FONTNAME || nCmdID == IDM_FONTSIZE)) { HRESULT hrT; VariantInit(&varColor); V_VT(&varColor) = VT_I4; hrT = m_pCmdTgtTrident->Exec(pguidCmdGroup, IDM_BACKCOLOR, OLECMDEXECOPT_DONTPROMPTUSER, NULL, &varColor); _ASSERTE(SUCCEEDED(hrT)); } // Trident bug: When you exec the block format command with "Normal", they don't remove OL and UL tags if (pvaIn && fTridentCmdSet && nCmdID == IDM_BLOCKFMT && pvaIn->vt == VT_BSTR && (_wcsicmp(pvaIn->bstrVal, L"Normal") == 0)) { OLECMD olecmd; olecmd.cmdID = IDM_ORDERLIST; olecmd.cmdf = CMDSTATE_NOTSUPPORTED; if (S_OK == m_pCmdTgtTrident->QueryStatus(&CMDSETID_Forms3, 1, &olecmd, NULL) && olecmd.cmdf == CMDSTATE_DOWN) m_pCmdTgtTrident->Exec(&CMDSETID_Forms3, IDM_ORDERLIST, OLECMDEXECOPT_DONTPROMPTUSER, NULL, NULL); olecmd.cmdID = IDM_UNORDERLIST; olecmd.cmdf = CMDSTATE_NOTSUPPORTED; if (S_OK == m_pCmdTgtTrident->QueryStatus(&CMDSETID_Forms3, 1, &olecmd, NULL) && olecmd.cmdf == CMDSTATE_DOWN) m_pCmdTgtTrident->Exec(&CMDSETID_Forms3, IDM_UNORDERLIST, OLECMDEXECOPT_DONTPROMPTUSER, NULL, NULL); } hr = m_pCmdTgtTrident->Exec(pguidCmdGroup, nCmdID, nCmdExecOpt, pvaIn, pvaOut); if (pvaIn && fTridentCmdSet && (nCmdID == IDM_FORECOLOR || nCmdID == IDM_FONTNAME || nCmdID == IDM_FONTSIZE)) { HRESULT hrT; hrT = m_pCmdTgtTrident->Exec(pguidCmdGroup, IDM_BACKCOLOR, OLECMDEXECOPT_DONTPROMPTUSER, &varColor, NULL); _ASSERTE(SUCCEEDED(hrT)); } else if ((nCmdID == IDM_SHOWALLTAGS || nCmdID == IDM_SHOWMISCTAGS) && pvaIn && pvaIn->vt == VT_BOOL) { SetUpGlyphTable(pvaIn->boolVal); } // Trident bug: They enable the justify commands but not actually support them. // We workaround this by returning S_OK for these no matter what Trident returns. if (fTridentCmdSet && (nCmdID == IDM_JUSTIFYLEFT || nCmdID == IDM_JUSTIFYCENTER || nCmdID == IDM_JUSTIFYRIGHT)) hr = S_OK; return hr; } return E_UNEXPECTED; } /////////////////////////////////////////////////////////////////////////////// // // CTriEditDocument::Is2DElement // // Test the given HTML element to ascertain if it is 2D positioned or not. // Return: // S_OK and *pf2D = TRUE if the element is 2D positioned. // S_OK and *pf2D = FALSE if the element is not 2D positioned. // HRESULT CTriEditDocument::Is2DElement(IHTMLElement* pihtmlElement, BOOL* pf2D) { IHTMLStyle* pihtmlStyle = NULL; BSTR bstrPosition = NULL; BOOL f2DCapable; _ASSERTE(pihtmlElement); _ASSERTE(pf2D); *pf2D = FALSE; if (SUCCEEDED(Is2DCapable(pihtmlElement, &f2DCapable))) { if (f2DCapable && SUCCEEDED(pihtmlElement->get_style(&pihtmlStyle))) { _ASSERTE(pihtmlStyle); if (SUCCEEDED(pihtmlStyle->get_position(&bstrPosition))) { if (bstrPosition) { *pf2D = (_wcsicmp(bstrPosition, L"absolute") == 0); SysFreeString(bstrPosition); } SAFERELEASE(pihtmlStyle); } } } return S_OK; } /////////////////////////////////////////////////////////////////////////////// // // CTriEDitDocument::NudgeElement // // Move the given HTML element (which must be 2D positioned) as indicated // by pptNudge, further adjusted by the grid spacing in m_ptAlign. Returns // S_OK if all goes well; E_UNEXPECTED otherwise. // HRESULT CTriEditDocument::NudgeElement(IHTMLElement* pihtmlElement, LPPOINT pptNudge) { HRESULT hr = E_UNEXPECTED; IHTMLStyle* pihtmlStyle = NULL; long x, y; _ASSERTE(pihtmlElement); _ASSERTE(pptNudge); if (pihtmlElement) { if (SUCCEEDED(pihtmlElement->get_style(&pihtmlStyle)) && pihtmlStyle && SUCCEEDED(pihtmlStyle->get_pixelTop(&y)) && SUCCEEDED(pihtmlStyle->get_pixelLeft(&x))) { if (x == 0 || y == 0) { IHTMLElement *pihtmlElementParent = NULL; RECT rcElement, rcParent; if (SUCCEEDED(pihtmlElement->get_offsetParent(&pihtmlElementParent)) && pihtmlElementParent) { if (SUCCEEDED(GetElementPosition(pihtmlElement, &rcElement))) { ::SetRect(&rcParent, 0, 0, 0, 0); if (SUCCEEDED(GetElementPosition(pihtmlElementParent, &rcParent))) { x = rcElement.left - rcParent.left; y = rcElement.top - rcParent.top; } } pihtmlElementParent->Release(); } } x += pptNudge->x; y += pptNudge->y; if (pptNudge->x != 0) { if (x >= 0) x -= (x % m_ptAlign.x); else x -= (((x % m_ptAlign.x) ? m_ptAlign.x : 0) + (x % m_ptAlign.x)); } if (pptNudge->y != 0) { if (y >= 0) y -= (y % m_ptAlign.y); else y -= (((y % m_ptAlign.y) ? m_ptAlign.y : 0) + (y % m_ptAlign.y)); } pihtmlStyle->put_pixelTop(y); pihtmlStyle->put_pixelLeft(x); return S_OK; } } SAFERELEASE(pihtmlStyle); return hr; } /////////////////////////////////////////////////////////////////////////////// // // CTriEditDocument::SetAlignment // // Set the TriEdit alignment values as indicated. Return S_OK if all goes // well; or E_POINTER if a bad pointer is supplied. // HRESULT CTriEditDocument::SetAlignment(LPPOINT pptAlign) { _ASSERTE(pptAlign); if (pptAlign) { m_ptAlign.x = max(pptAlign->x, 1); m_ptAlign.y = max(pptAlign->y, 1); return S_OK; } return E_POINTER; } /////////////////////////////////////////////////////////////////////////////// // // CTriEditDocument::LockElement // // Set or clear the TriEdit design-time locking flag (an expando attribute) as // indicated by fLock. Return S_OK if all goes well; E_FAIL if not. Note that // setting the locking flag also sets the top and left attributes if they // were not already set. // HRESULT CTriEditDocument::LockElement(IHTMLElement* pihtmlElement, BOOL fLock) { IHTMLStyle* pihtmlStyle=NULL; HRESULT hr = E_FAIL; VARIANT var; VARIANT_BOOL fSuccess = FALSE; if (pihtmlElement) { hr = pihtmlElement->get_style(&pihtmlStyle); _ASSERTE(SUCCEEDED(hr)); if (SUCCEEDED(hr)) { _ASSERTE(pihtmlStyle); if (pihtmlStyle) { if(!fLock) { hr = pihtmlStyle->removeAttribute(DESIGN_TIME_LOCK, 0, &fSuccess); _ASSERTE(fSuccess); } else { // Trident doesn't persist the Design_Time_Lock attribute // if left, top, width and height properties are not present as part of // the elements style attribute. Hence as a part of locking the element // we also assign the top and left styles only if they don't exist. LONG lTop, lLeft; pihtmlStyle->get_pixelTop(&lTop); pihtmlStyle->get_pixelLeft(&lLeft); if (lTop == 0 || lLeft == 0) { IHTMLElement *pihtmlElementParent = NULL; if (SUCCEEDED(pihtmlElement->get_offsetParent(&pihtmlElementParent)) && pihtmlElementParent) { if (SUCCEEDED(GetElementPosition(pihtmlElement, &m_rcElement))) { RECT rcParent; ::SetRect(&rcParent, 0, 0, 0, 0); if (SUCCEEDED(GetElementPosition(pihtmlElementParent, &rcParent))) { m_rcElement.left = m_rcElement.left - rcParent.left; m_rcElement.top = m_rcElement.top - rcParent.top; pihtmlStyle->put_pixelTop(m_rcElement.top); pihtmlStyle->put_pixelLeft(m_rcElement.left); } } pihtmlElementParent->Release(); } } VariantInit(&var); var.vt = VT_BSTR; var.bstrVal = SysAllocString(L"True"); hr = pihtmlStyle->setAttribute(DESIGN_TIME_LOCK, var, 0); hr = SUCCEEDED(hr) ? S_OK:E_FAIL; } pihtmlStyle->Release(); } } if (SUCCEEDED(hr)) { RECT rcElement; hr = GetElementPosition(pihtmlElement, &rcElement); _ASSERTE(SUCCEEDED(hr)); if (SUCCEEDED(hr)) { InflateRect(&rcElement, ELEMENT_GRAB_SIZE, ELEMENT_GRAB_SIZE); if( SUCCEEDED(hr = GetTridentWindow())) { _ASSERTE(m_hwndTrident); InvalidateRect(m_hwndTrident,&rcElement, FALSE); } } // Trident doesn't set itself to be dirty, so force the dirty state. VariantInit(&var); var.vt = VT_BOOL; var.boolVal = TRUE; if (m_pCmdTgtTrident) m_pCmdTgtTrident->Exec(&CMDSETID_Forms3, IDM_SETDIRTY, 0, &var, NULL); } } return hr; } /////////////////////////////////////////////////////////////////////////////// // // CTriEditDocument::IsLocked // // Test the given HTML element to ascertain if it is design-time locked or not. // Return: // S_OK and *pfLocked = TRUE if the element is design-time locked. // S_OK and *pfLocked = FALSE if the element is not design-time locked. // HRESULT CTriEditDocument::IsLocked(IHTMLElement* pihtmlElement, BOOL* pfLocked) { IHTMLStyle* pihtmlStyle=NULL; BSTR bstrAttributeName = NULL; HRESULT hr = E_FAIL; VARIANT var; VariantInit(&var); var.vt = VT_BSTR; var.bstrVal = NULL; if (pihtmlElement) { hr = pihtmlElement->get_style(&pihtmlStyle); _ASSERTE(SUCCEEDED(hr)); if (SUCCEEDED(hr)) { _ASSERTE(pihtmlStyle); if (pihtmlStyle) { bstrAttributeName = SysAllocString(DESIGN_TIME_LOCK); if (bstrAttributeName) { hr = pihtmlStyle->getAttribute(bstrAttributeName, 0, &var); _ASSERTE(SUCCEEDED(hr)); if (var.bstrVal == NULL) *pfLocked = FALSE; else *pfLocked = TRUE; SysFreeString(bstrAttributeName); } pihtmlStyle->Release(); } } } return hr; } /////////////////////////////////////////////////////////////////////////////// // // // CTriEditDocument::Make1DElement // // Set the given HTML element to layout in the flow. As a side effect this // also removes any design-time lock on the element. Return S_OK if all goes // well; E_UNEXPECTED otherwise. // HRESULT CTriEditDocument::Make1DElement(IHTMLElement* pihtmlElement) { IHTMLStyle* pihtmlStyle=NULL; VARIANT_BOOL fSuccess = FALSE; VARIANT var; HRESULT hr; if (pihtmlElement) { pihtmlElement->get_style(&pihtmlStyle); _ASSERTE(pihtmlStyle); if (pihtmlStyle) { VariantInit(&var); var.vt = VT_I4; var.lVal = 0; hr = pihtmlStyle->put_zIndex(var); _ASSERTE(SUCCEEDED(hr)); pihtmlStyle->removeAttribute(DESIGN_TIME_LOCK, 0, &fSuccess); pihtmlStyle->removeAttribute(L"position", 0, &fSuccess); pihtmlStyle->Release(); } } return (fSuccess? S_OK: E_UNEXPECTED); } /////////////////////////////////////////////////////////////////////////////// // // // CTriEditDocument::Make2DElement // // Set the given HTML element to be positioned. Return S_OK if all goes // well; E_FAIL otherwise. // HRESULT CTriEditDocument::Make2DElement(IHTMLElement* pihtmlElement, POINT *ppt) { IHTMLElement* pihtmlElementParent = NULL; IHTMLElementCollection* pihtmlCollection = NULL; IHTMLElement* pihtmlElementNew = NULL; IHTMLStyle* pihtmlElementStyle = NULL; VARIANT var; LONG lSourceIndex; HRESULT hr = E_FAIL; BSTR bstrOuterHtml = NULL; _ASSERTE(pihtmlElement); if(!pihtmlElement) { return E_FAIL; } hr = pihtmlElement->get_style(&pihtmlElementStyle); _ASSERTE(SUCCEEDED(hr) && pihtmlElementStyle); if (FAILED(hr) || !pihtmlElementStyle) { return E_FAIL; } // The reason to save the source index here is that once we call put_outerHTML // the element is lost, we later use the source index to get back the element from the collection. // Note that the source index remains the same after put_outerHTML. hr = pihtmlElement->get_sourceIndex(&lSourceIndex); _ASSERTE(SUCCEEDED(hr) && (lSourceIndex != -1)); if (lSourceIndex == -1 || FAILED(hr)) { return E_FAIL; } hr = pihtmlElement->get_offsetParent(&pihtmlElementParent); _ASSERTE(SUCCEEDED(hr) && pihtmlElementParent); if (SUCCEEDED(hr) && pihtmlElementParent) { VariantInit(&var); var.vt = VT_BSTR; var.bstrVal = SysAllocString(L"absolute"); hr = pihtmlElementStyle->setAttribute(L"position", var, 1); if (var.bstrVal) SysFreeString(var.bstrVal); _ASSERTE(SUCCEEDED(hr)); if (SUCCEEDED(hr)) { if (SUCCEEDED(hr = GetElementPosition(pihtmlElement, &m_rcElement))) { IHTMLTable* pihtmlTable = NULL; IHTMLElement* pihtmlElementTemp = NULL, *pihtmlElementPrev = NULL; RECT rcParent; BOOL f2d = FALSE; BOOL fIsIE5AndBeyond = IsIE5OrBetterInstalled(); ::SetRect(&rcParent, 0, 0, 0, 0); pihtmlElementTemp = pihtmlElementParent; pihtmlElementTemp->AddRef(); // Handle tables specially since the offset parent may have been the TD or the TR while (pihtmlElementTemp) { if (SUCCEEDED(pihtmlElementTemp->QueryInterface(IID_IHTMLTable, (void **)&pihtmlTable)) && pihtmlTable) break; pihtmlElementPrev = pihtmlElementTemp; pihtmlElementPrev->get_offsetParent(&pihtmlElementTemp); SAFERELEASE(pihtmlElementPrev); } // If parent is a 2d element, we need to offset its top and left if (pihtmlElementTemp && SUCCEEDED(Is2DElement(pihtmlElementTemp, &f2d)) && f2d) { GetElementPosition(pihtmlElementTemp, &rcParent); } else if (SUCCEEDED(Is2DElement(pihtmlElementParent, &f2d)) && f2d) { GetElementPosition(pihtmlElementParent, &rcParent); } SAFERELEASE(pihtmlTable); SAFERELEASE(pihtmlElementTemp); SAFERELEASE(pihtmlElementPrev); m_rcElement.left = (ppt ? ppt->x : m_rcElement.left) - rcParent.left; m_rcElement.top = (ppt ? ppt->y : m_rcElement.top) - rcParent.top; // We need to call get_outerHTML and put_outerHTML to work around a Trident bug // We should not really have to call these here, but the element doesn't get // updated unless we do this. if (fIsIE5AndBeyond || SUCCEEDED(hr = pihtmlElement->get_outerHTML(&bstrOuterHtml))) { if (fIsIE5AndBeyond || SUCCEEDED(hr = pihtmlElement->put_outerHTML(bstrOuterHtml))) { hr = GetAllCollection(&pihtmlCollection); _ASSERTE(SUCCEEDED(hr)); _ASSERTE(pihtmlCollection); if (SUCCEEDED(hr) && pihtmlCollection) { hr = GetCollectionElement(pihtmlCollection, lSourceIndex, &pihtmlElementNew); _ASSERTE(SUCCEEDED(hr)); _ASSERTE(pihtmlElementNew); if (SUCCEEDED(hr) && pihtmlElementNew) { hr = SelectElement(pihtmlElementNew, pihtmlElementParent); GetElement(); // to update m_pihtmlElement and friends after the above SelectElement if (SUCCEEDED(hr)) { hr = AssignZIndex(pihtmlElementNew, MADE_ABSOLUTE); _ASSERTE(SUCCEEDED(hr)); if (SUCCEEDED(hr)) { SAFERELEASE(pihtmlElementStyle); if (SUCCEEDED(hr = pihtmlElementNew->get_style(&pihtmlElementStyle))) { pihtmlElementStyle->put_pixelLeft(m_rcElement.left); pihtmlElementStyle->put_pixelTop(m_rcElement.top); VariantInit(&var); var.vt = VT_BOOL; var.boolVal = FALSE; pihtmlElementNew->scrollIntoView(var); } } } } } } } } } } if (bstrOuterHtml) SysFreeString(bstrOuterHtml); SAFERELEASE(pihtmlElementParent); SAFERELEASE(pihtmlElementStyle); SAFERELEASE(pihtmlElementNew); SAFERELEASE(pihtmlCollection); return hr; } /////////////////////////////////////////////////////////////////////////////// // // CTriEditDocument::Constrain // // Set the TriEdit constraint flag as indicated by fConstrain. Also, reset // the constraint direction to CONSTRAIN_NONE. Return S_OK. HRESULT CTriEditDocument::Constrain(BOOL fConstrain) { m_fConstrain = (fConstrain) ? TRUE:FALSE; m_eDirection = CONSTRAIN_NONE; return S_OK; } typedef struct SELCELLINFO { LONG cCellIndex; // cell index in a row LONG cRowIndex; // which row is this cell in CComPtr srpCell; // cell element CComPtr srpRow; // row element CComPtr srpTable; } SELCELLINFO; /////////////////////////////////////////////////////////////////////////////// // // CTriEditDocument::GetTableRowElementAndTableFromCell // // Given the IDispatch pointer to an element within a table, return the // row index in *pindexRow (if pindexRow is not NULL) and/or the // actual row element in *psrpRow (if psrpRow is not NULL) of the // element within the table. If psrpTable is not NULL, return the // table containing the element therein. Return S_OK if all goes well, // or E_FAIL if something goes wrong. // HRESULT CTriEditDocument::GetTableRowElementAndTableFromCell(IDispatch *srpCell, LONG *pindexRow , IDispatch **psrpRow, IDispatch **psrpTable) { CComPtr srpParent,srpElement; HRESULT hr = E_FAIL; CComBSTR bstrTag; _ASSERTE(srpCell != NULL); if (pindexRow == NULL && psrpRow == NULL) goto Fail; srpParent = srpCell; while (srpParent != NULL) { srpElement.Release(); if (FAILED(hr = GetDispatchProperty(srpParent, L"parentElement", VT_DISPATCH, (void**)&srpElement))) goto Fail; if (srpElement == NULL) { hr = E_FAIL; goto Fail; } bstrTag.Empty(); if (FAILED(hr = GetDispatchProperty(srpElement, L"tagName", VT_BSTR, &bstrTag))) goto Fail; if (lstrcmpi(_T("TR"), OLE2T(bstrTag)) == 0) { if (psrpRow != NULL) { *psrpRow = srpElement; (*psrpRow)->AddRef(); } if (pindexRow != NULL) { if (FAILED(hr = GetDispatchProperty(srpElement, L"rowIndex", VT_I4, pindexRow))) goto Fail; } break; } srpParent = srpElement; } if (psrpTable != NULL) { srpParent = srpElement; while (srpParent != NULL) { srpElement.Release(); if (FAILED(hr = GetDispatchProperty(srpParent, L"parentElement", VT_DISPATCH, (void**)&srpElement))) goto Fail; if (srpElement == NULL) { hr = E_FAIL; goto Fail; } bstrTag.Empty(); if (FAILED(hr = GetDispatchProperty(srpElement, L"tagName", VT_BSTR, &bstrTag))) goto Fail; if (lstrcmpi(_T("TABLE"), OLE2T(bstrTag)) == 0) { if (psrpTable != NULL) { *psrpTable = srpElement; (*psrpTable)->AddRef(); } break; } srpParent = srpElement; } } Fail: return hr; } /////////////////////////////////////////////////////////////////////////////// // // CTriEditDocument::FEnableInsertTable // // Return TRUE if the Trident selection is within a table and if the selection // type and location will allow elements to be inserted within the table. // Return FALSE otherwise. // BOOL CTriEditDocument::FEnableInsertTable(void) { BOOL fRet = FALSE; CComPtr srpRange,srpParent,srpElement; CComPtr srpSel; CComPtr srpiHTMLDoc; CComBSTR bstr; CComBSTR bstrTag; if (FAILED(m_pUnkTrident->QueryInterface(IID_IHTMLDocument2, (void**)&srpiHTMLDoc))) goto Fail; if (FAILED(srpiHTMLDoc->get_selection(&srpSel))) goto Fail; if (FAILED(GetDispatchProperty(srpSel, L"type", VT_BSTR, &bstr))) goto Fail; if (lstrcmpi(_T("CONTROL"), OLE2T(bstr)) == 0) { return FALSE; } if (FAILED(CallDispatchMethod(srpSel, L"createRange", VTS_DISPATCH_RETURN, (void**)&srpRange))) goto Fail; if (srpRange == NULL) goto Fail; srpParent = srpRange; while (srpParent != NULL) { srpElement.Release(); if (FAILED(GetDispatchProperty(srpParent, L"parentElement", VT_DISPATCH, (void**)&srpElement))) goto Fail; if (srpElement == NULL) break; bstrTag.Empty(); if (FAILED(GetDispatchProperty(srpElement, L"tagName", VT_BSTR, &bstrTag))) goto Fail; if (lstrcmpi(_T("INPUT"), OLE2T(bstrTag)) == 0) { return FALSE; } srpParent = srpElement; } // if the selection is inside a table, make sure only one cell is selected if (IsSelectionInTable() == S_OK) { UINT grf = GetSelectionTypeInTable(); if (grf != -1 && !(grf & grfSelectOneCell)) return FALSE; } fRet = TRUE; Fail: return fRet; } /////////////////////////////////////////////////////////////////////////////// // // CTriEditDocument::IsSelectionInTable // // Return S_OK if the Trident selection is within a table. Return // E_FAIL otherwise. // HRESULT CTriEditDocument::IsSelectionInTable(IDispatch **ppTable) { HRESULT hr=0; CComPtr srpSel; CComPtr srpRange,srpParent,srpElement; CComPtr srpiHTMLDoc; CComBSTR bstrTag; BOOL fTable= FALSE; if (FAILED(hr = m_pUnkTrident->QueryInterface(IID_IHTMLDocument2, (void**)&srpiHTMLDoc))) goto Fail; if (FAILED(hr = srpiHTMLDoc->get_selection(&srpSel))) goto Fail; if (FAILED(hr = CallDispatchMethod(srpSel, L"createRange", VTS_DISPATCH_RETURN, (void**)&srpRange))) goto Fail; srpParent = srpRange; while (srpParent != NULL) { srpElement.Release(); if (FAILED(hr = GetDispatchProperty(srpParent, L"parentElement", VT_DISPATCH, (void**)&srpElement))) goto Fail; if (srpElement == NULL) break; bstrTag.Empty(); if (FAILED(hr = GetDispatchProperty(srpElement, L"tagName", VT_BSTR, &bstrTag))) goto Fail; if (lstrcmpi(_T("TABLE"), OLE2T(bstrTag)) == 0) { if (ppTable != NULL) { *ppTable = srpElement; (*ppTable)->AddRef(); } fTable = TRUE; break; } else if (lstrcmpi(_T("CAPTION"), OLE2T(bstrTag)) == 0) { fTable = FALSE; break; } srpParent = srpElement; } Fail: return fTable ? S_OK : E_FAIL; } /////////////////////////////////////////////////////////////////////////////// // // CTriEditDocument::FillInSelectionCellsInfo // // Fill *pSelStart with the information concerning the table cell containing // the beginning of the Trident selection and *pSelSle with the information // on the table cell at the end of the selection. Return S_OK if all goes well, // or E_FAIL otherwise. HRESULT CTriEditDocument::FillInSelectionCellsInfo(struct SELCELLINFO * pselStart, struct SELCELLINFO *pselEnd) { CComPtr srpiHTMLDoc; CComPtr srpSel; CComPtr srpRange[2]; CComPtr srpParent; CComBSTR bstrText, bstrTag;; LONG cReturn=0; HRESULT i=0, hr=0; LONG cCharSelected=0; WCHAR *pData = NULL; BOOL fContain = FALSE; if (FAILED(hr = IsSelectionInTable())) goto Fail; if (FAILED(hr = m_pUnkTrident->QueryInterface(IID_IHTMLDocument2, (void**)&srpiHTMLDoc))) goto Fail; if (FAILED(hr = srpiHTMLDoc->get_selection(&srpSel))) goto Fail; for (i=0; i<2 ; i++) { // BUG 568250. We HAD treated the dispatch like a text range, this now crashes. CComPtr srpDisp; if (FAILED(hr = CallDispatchMethod(srpSel, L"createRange", VTS_DISPATCH_RETURN, (void**)&srpDisp))) { goto Fail; } else { if (FAILED(hr = srpDisp->QueryInterface(&srpRange[i]))) goto Fail; } } bstrText.Empty(); hr = srpRange[0]->get_text(&bstrText); if (FAILED(hr)) goto Fail; cCharSelected = bstrText ? ocslen(bstrText) : 0; pData = (WCHAR *) bstrText; // VID98 bug 3117: trident use '0x0D' to mark column/row and this char is ignored when // move range so we need to deduct these characters while (pData != NULL && *pData !='\0') { if (*pData == 0x0D) cCharSelected--; pData++; } if (pselStart != NULL) { hr = srpRange[0]->collapse(TRUE); if (FAILED(hr)) goto Fail; srpParent = srpRange[0]; while (srpParent != NULL) { pselStart->srpCell.Release(); if (FAILED(hr = GetDispatchProperty(srpParent, L"parentElement", VT_DISPATCH, (void**)&pselStart->srpCell))) goto Fail; if (pselStart->srpCell == NULL) { hr = E_FAIL; goto Fail; } bstrTag.Empty(); if (FAILED(hr = GetDispatchProperty(pselStart->srpCell, L"tagName", VT_BSTR, &bstrTag))) goto Fail; if (lstrcmpi(_T("TD"), OLE2T(bstrTag)) == 0 || lstrcmpi(_T("TH"), OLE2T(bstrTag)) == 0) { break; } srpParent = pselStart->srpCell; } _ASSERTE(pselStart->srpCell != NULL); if (FAILED(hr = GetDispatchProperty(pselStart->srpCell, L"cellIndex", VT_I4, &pselStart->cCellIndex))) goto Fail; pselStart->srpRow.Release(); if (FAILED(hr = GetTableRowElementAndTableFromCell(pselStart->srpCell, &pselStart->cRowIndex, &pselStart->srpRow, &pselStart->srpTable))) goto Fail; } if (pselEnd != NULL) { hr = srpRange[1]->collapse(FALSE); if (FAILED(hr)) goto Fail; if (cCharSelected != 0) { hr = srpRange[1]->moveStart(L"Character", -1, &cReturn); if (FAILED(hr)) goto Fail; hr = srpRange[1]->moveEnd(L"Character", -1, &cReturn); if (FAILED(hr)) goto Fail; } srpParent = srpRange[1]; while (srpParent != NULL) { pselEnd->srpCell.Release(); if (FAILED(hr = GetDispatchProperty(srpParent, L"parentElement", VT_DISPATCH, (void**)&pselEnd->srpCell))) goto Fail; if (pselEnd->srpCell == NULL) { hr = E_FAIL; goto Fail; } bstrTag.Empty(); if (FAILED(hr = GetDispatchProperty(pselEnd->srpCell, L"tagName", VT_BSTR, &bstrTag))) goto Fail; if (lstrcmpi(_T("TD"), OLE2T(bstrTag)) == 0 || lstrcmpi(_T("TH"), OLE2T(bstrTag)) == 0) { break; } srpParent = pselEnd->srpCell; } _ASSERTE(pselEnd->srpCell != NULL); if (FAILED(hr = GetDispatchProperty(pselEnd->srpCell, L"cellIndex", VT_I4, &pselEnd->cCellIndex))) goto Fail; pselEnd->srpRow.Release(); if (FAILED(hr = GetTableRowElementAndTableFromCell(pselEnd->srpCell, &pselEnd->cRowIndex, &pselEnd->srpRow, &pselEnd->srpTable))) goto Fail; } if (pselEnd != NULL && pselStart != NULL) { // VID 98 bug 3116: we need to check if first cell and last cell are in the same table. If they are not // the row index and cell index we just got do not make sense if (FAILED(hr = CallDispatchMethod(pselEnd->srpTable, L"contains", VTS_DISPATCH VTS_BOOL_RETURN, pselStart->srpRow, &fContain))) goto Fail; if (!fContain) return E_FAIL; fContain = FALSE; if (FAILED(hr = CallDispatchMethod(pselStart->srpTable, L"contains", VTS_DISPATCH VTS_BOOL_RETURN, pselEnd->srpRow, &fContain))) goto Fail; if (!fContain) return E_FAIL; } Fail: return hr; } /////////////////////////////////////////////////////////////////////////////// // // CTriEditDocument::GetSelectionTypeInTable // // Return a set of flags that characterize the current selection. Return // -1 if something goes wrong. The flags are as follows: // // grfInSingleRow Selection is comprised of one or more cells // within a single row. // // grfSelectOneCell Selection is comprised of a single cell. // // grpSelectEntireRow Selection is comprised of one or more // complete rows. ULONG CTriEditDocument::GetSelectionTypeInTable(void) { CComPtr srpCells; struct SELCELLINFO selinfo[2]; // 0 is start cell, 1 is end cell LONG cCells=0; HRESULT hr=0; ULONG grf=0; if (FAILED(hr = FillInSelectionCellsInfo(&selinfo[0], &selinfo[1]))) goto Fail; if (selinfo[0].cRowIndex == selinfo[1].cRowIndex) { grf |= grfInSingleRow; if (selinfo[0].cCellIndex == selinfo[1].cCellIndex) grf |= grfSelectOneCell; } else { grf &= ~grfInSingleRow; } if (selinfo[0].cCellIndex != 0) grf &= ~grpSelectEntireRow; else { srpCells.Release(); if (FAILED(hr = GetDispatchProperty(selinfo[1].srpRow, L"cells", VT_DISPATCH, (void**)&srpCells))) goto Fail; if (FAILED(hr = GetDispatchProperty(srpCells, L"length", VT_I4, &cCells))) goto Fail; if (selinfo[1].cCellIndex != cCells-1) grf &= ~grpSelectEntireRow; else grf |= grpSelectEntireRow; } Fail: return FAILED(hr) ? -1 : grf; } /////////////////////////////////////////////////////////////////////////////// // // CTriEditDocument::CopyProperty // // Copy properties from the pFrom element on to the pTo element. Return S_OK. // HRESULT CTriEditDocument::CopyProperty(IDispatch *pFrom, IDispatch *pTo) { CComVariant varProp; CComBSTR bstrProp; VARIANT_BOOL bProp; bstrProp.Empty(); if (SUCCEEDED(GetDispatchProperty(pFrom, L"align", VT_BSTR, (void **)&bstrProp))) { if (lstrcmpW(bstrProp, L"")) PutDispatchProperty(pTo, L"align", VT_BSTR, bstrProp); } bstrProp.Empty(); if (SUCCEEDED(GetDispatchProperty(pFrom, L"vAlign", VT_BSTR, (void **)&bstrProp))) { if (lstrcmpW(bstrProp, L"")) PutDispatchProperty(pTo, L"vAlign", VT_BSTR, bstrProp); } bstrProp.Empty(); if (SUCCEEDED(GetDispatchProperty(pFrom, L"background", VT_BSTR, (void **)&bstrProp))) { if (lstrcmpW(bstrProp, L"")) PutDispatchProperty(pTo, L"background", VT_BSTR, bstrProp); } bstrProp.Empty(); if (SUCCEEDED(GetDispatchProperty(pFrom, L"lang", VT_BSTR, (void **)&bstrProp))) { if (lstrcmpW(bstrProp, L"")) PutDispatchProperty(pTo, L"lang", VT_BSTR, bstrProp); } bstrProp.Empty(); if (SUCCEEDED(GetDispatchProperty(pFrom, L"className", VT_BSTR, (void **)&bstrProp))) { if (lstrcmpW(bstrProp, L"")) PutDispatchProperty(pTo, L"className", VT_BSTR, bstrProp); } varProp.Clear(); if (SUCCEEDED(GetDispatchProperty(pFrom, L"bgColor", VT_VARIANT, (void **)&varProp))) PutDispatchProperty(pTo, L"bgColor", VT_VARIANT, varProp); varProp.Clear(); if (SUCCEEDED(GetDispatchProperty(pFrom, L"borderColor", VT_VARIANT, (void **)&varProp))) PutDispatchProperty(pTo, L"borderColor", VT_VARIANT, varProp); varProp.Clear(); if (SUCCEEDED(GetDispatchProperty(pFrom, L"borderColorLight", VT_VARIANT, (void **)&varProp))) PutDispatchProperty(pTo, L"borderColorLight", VT_VARIANT, varProp); varProp.Clear(); if (SUCCEEDED(GetDispatchProperty(pFrom, L"borderColorDark", VT_VARIANT, (void **)&varProp))) PutDispatchProperty(pTo, L"borderColorDark", VT_VARIANT, varProp); varProp.Clear(); if (SUCCEEDED(GetDispatchProperty(pFrom, L"height", VT_VARIANT, (void **)&varProp))) PutDispatchProperty(pTo, L"height", VT_VARIANT, varProp); varProp.Clear(); if (SUCCEEDED(GetDispatchProperty(pFrom, L"width", VT_VARIANT, (void **)&varProp))) PutDispatchProperty(pTo, L"width", VT_VARIANT, varProp); if (SUCCEEDED(GetDispatchProperty(pFrom, L"noWrap", VT_BOOL, (void **)&bProp))) { #pragma warning(disable: 4310) // cast truncates constant value if (bProp == VARIANT_TRUE) #pragma warning(default: 4310) // cast truncates constant value PutDispatchProperty(pTo, L"noWrap", VT_BOOL, bProp); } return S_OK; } /////////////////////////////////////////////////////////////////////////////// // // CTriEditDocument::CopyStyle // // Copy style properties from style element pFrom on to style element pTo. // Return S_OK. // HRESULT CTriEditDocument::CopyStyle(IDispatch *pFrom, IDispatch *pTo) { CComPtr srpStyleTo, srpStyleFrom; if (SUCCEEDED(GetDispatchProperty(pFrom, L"style", VT_DISPATCH, (void **)&srpStyleFrom))) { if (SUCCEEDED(GetDispatchProperty(pTo, L"style", VT_DISPATCH, (void **)&srpStyleTo))) { CComVariant varProp; CComBSTR bstrProp; bstrProp.Empty(); if (SUCCEEDED(GetDispatchProperty(srpStyleFrom, L"backgroundAttachment", VT_BSTR, (void **)&bstrProp))) { if (lstrcmpW(bstrProp, L"")) PutDispatchProperty(srpStyleTo, L"backgroundAttachment", VT_BSTR, bstrProp); } bstrProp.Empty(); if (SUCCEEDED(GetDispatchProperty(srpStyleFrom, L"backgroundImage", VT_BSTR, (void **)&bstrProp))) { if (lstrcmpW(bstrProp, L"")) PutDispatchProperty(srpStyleTo, L"backgroundImage", VT_BSTR, bstrProp); } bstrProp.Empty(); if (SUCCEEDED(GetDispatchProperty(srpStyleFrom, L"backgroundRepeat", VT_BSTR, (void **)&bstrProp))) { if (lstrcmpW(bstrProp, L"")) PutDispatchProperty(srpStyleTo, L"backgroundRepeat", VT_BSTR, bstrProp); } bstrProp.Empty(); if (SUCCEEDED(GetDispatchProperty(srpStyleFrom, L"borderBottom", VT_BSTR, (void **)&bstrProp))) { if (lstrcmpW(bstrProp, L"")) PutDispatchProperty(srpStyleTo, L"borderBottom", VT_BSTR, bstrProp); } bstrProp.Empty(); if (SUCCEEDED(GetDispatchProperty(srpStyleFrom, L"borderLeft", VT_BSTR, (void **)&bstrProp))) { if (lstrcmpW(bstrProp, L"")) PutDispatchProperty(srpStyleTo, L"borderLeft", VT_BSTR, bstrProp); } bstrProp.Empty(); if (SUCCEEDED(GetDispatchProperty(srpStyleFrom, L"borderTop", VT_BSTR, (void **)&bstrProp))) { if (lstrcmpW(bstrProp, L"")) PutDispatchProperty(srpStyleTo, L"borderTop", VT_BSTR, bstrProp); } bstrProp.Empty(); if (SUCCEEDED(GetDispatchProperty(srpStyleFrom, L"borderRight", VT_BSTR, (void **)&bstrProp))) { if (lstrcmpW(bstrProp, L"")) PutDispatchProperty(srpStyleTo, L"borderRight", VT_BSTR, bstrProp); } bstrProp.Empty(); if (SUCCEEDED(GetDispatchProperty(srpStyleFrom, L"fontFamily", VT_BSTR, (void **)&bstrProp))) { if (lstrcmpW(bstrProp, L"")) PutDispatchProperty(srpStyleTo, L"fontFamily", VT_BSTR, bstrProp); } bstrProp.Empty(); if (SUCCEEDED(GetDispatchProperty(srpStyleFrom, L"fontStyle", VT_BSTR, (void **)&bstrProp))) { if (lstrcmpW(bstrProp, L"")) PutDispatchProperty(srpStyleTo, L"fontStyle", VT_BSTR, bstrProp); } bstrProp.Empty(); if (SUCCEEDED(GetDispatchProperty(srpStyleFrom, L"fontVariant", VT_BSTR, (void **)&bstrProp))) { if (lstrcmpW(bstrProp, L"")) PutDispatchProperty(srpStyleTo, L"fontVariant", VT_BSTR, bstrProp); } bstrProp.Empty(); if (SUCCEEDED(GetDispatchProperty(srpStyleFrom, L"fontWeight", VT_BSTR, (void **)&bstrProp))) { if (lstrcmpW(bstrProp, L"")) PutDispatchProperty(srpStyleTo, L"fontWeight", VT_BSTR, bstrProp); } bstrProp.Empty(); if (SUCCEEDED(GetDispatchProperty(srpStyleFrom, L"textAlign", VT_BSTR, (void **)&bstrProp))) { if (lstrcmpW(bstrProp, L"")) PutDispatchProperty(srpStyleTo, L"textAlign", VT_BSTR, bstrProp); } bstrProp.Empty(); if (SUCCEEDED(GetDispatchProperty(srpStyleFrom, L"textTransform", VT_BSTR, (void **)&bstrProp))) { if (lstrcmpW(bstrProp, L"")) PutDispatchProperty(srpStyleTo, L"textTransform", VT_BSTR, bstrProp); } bstrProp.Empty(); if (SUCCEEDED(GetDispatchProperty(srpStyleFrom, L"textDecoration", VT_BSTR, (void **)&bstrProp))) { if (lstrcmpW(bstrProp, L"")) PutDispatchProperty(srpStyleTo, L"textDecoration", VT_BSTR, bstrProp); } varProp.Clear(); if (SUCCEEDED(GetDispatchProperty(srpStyleFrom, L"backgroundcolor", VT_VARIANT, (void **)&varProp))) PutDispatchProperty(srpStyleTo, L"backgroundcolor", VT_VARIANT, varProp); varProp.Clear(); if (SUCCEEDED(GetDispatchProperty(srpStyleFrom, L"color", VT_VARIANT, (void **)&varProp))) PutDispatchProperty(srpStyleTo, L"color", VT_VARIANT, varProp); varProp.Clear(); if (SUCCEEDED(GetDispatchProperty(srpStyleFrom, L"fontSize", VT_VARIANT, (void **)&varProp))) PutDispatchProperty(srpStyleTo, L"fontSize", VT_VARIANT, varProp); varProp.Clear(); if (SUCCEEDED(GetDispatchProperty(srpStyleFrom, L"height", VT_VARIANT, (void **)&varProp))) PutDispatchProperty(srpStyleTo, L"height", VT_VARIANT, varProp); varProp.Clear(); if (SUCCEEDED(GetDispatchProperty(srpStyleFrom, L"letterSpacing", VT_VARIANT, (void **)&varProp))) PutDispatchProperty(srpStyleTo, L"letterSpacing", VT_VARIANT, varProp); varProp.Clear(); if (SUCCEEDED(GetDispatchProperty(srpStyleFrom, L"lineHeight", VT_VARIANT, (void **)&varProp))) PutDispatchProperty(srpStyleTo, L"lineHeight", VT_VARIANT, varProp); varProp.Clear(); if (SUCCEEDED(GetDispatchProperty(srpStyleFrom, L"paddingRight", VT_VARIANT, (void **)&varProp))) PutDispatchProperty(srpStyleTo, L"paddingRight", VT_VARIANT, varProp); varProp.Clear(); if (SUCCEEDED(GetDispatchProperty(srpStyleFrom, L"paddingBottom", VT_VARIANT, (void **)&varProp))) PutDispatchProperty(srpStyleTo, L"paddingBottom", VT_VARIANT, varProp); varProp.Clear(); if (SUCCEEDED(GetDispatchProperty(srpStyleFrom, L"paddingLeft", VT_VARIANT, (void **)&varProp))) PutDispatchProperty(srpStyleTo, L"paddingLeft", VT_VARIANT, varProp); varProp.Clear(); if (SUCCEEDED(GetDispatchProperty(srpStyleFrom, L"paddingTop", VT_VARIANT, (void **)&varProp))) PutDispatchProperty(srpStyleTo, L"paddingTop", VT_VARIANT, varProp); } } return S_OK; } /////////////////////////////////////////////////////////////////////////////// // // CTriEditDocument::DeleteTableRows // // Delete the table row(s) contained within the Trident selection. The // entire operation is a single undo unit. Return S_OK or a Trident error. // HRESULT CTriEditDocument::DeleteTableRows(void) { HRESULT hr = S_OK; CComPtr srpTable; struct SELCELLINFO selinfo[2]; // 0 is start cell, 1 is end cell INT i=0; CUndoPackManager undoPackMgr(m_pUnkTrident); if (FAILED(hr = IsSelectionInTable((IDispatch**)&srpTable))) goto Fail; if (FAILED(hr = FillInSelectionCellsInfo(&selinfo[0], &selinfo[1]))) goto Fail; undoPackMgr.Start(); for(i= selinfo[0].cRowIndex; i <= selinfo[1].cRowIndex; i++) { if (FAILED(hr = DeleteRowEx(srpTable, selinfo[0].cRowIndex))) goto Fail; } Fail: return hr; } /////////////////////////////////////////////////////////////////////////////// // // CTriEditDocument::DeleteRowEx // // Delete the indicated table row. If the row is the only row in the table, // delete the whole table. Return S_OK or a Trident error. // inline HRESULT CTriEditDocument::DeleteRowEx(IHTMLElement *pTable, LONG index) { HRESULT hr = S_OK; CComPtr srpRows; INT cRows = 0; if (FAILED(hr = GetDispatchProperty(pTable, L"rows", VT_DISPATCH, (void**)&srpRows))) goto Fail; if (FAILED(hr = GetDispatchProperty(srpRows, L"length", VT_I4, &cRows))) goto Fail; // if this is the only row in the table, delete the whole table if (cRows == 1) { _ASSERT(index == 0); hr = DeleteTable(pTable); } else { if (FAILED(hr = CallDispatchMethod(pTable, L"deleteRow", VTS_I4, index))) goto Fail; } Fail: return hr; } /////////////////////////////////////////////////////////////////////////////// // // CTriEditDocument::DeleteCellEx // // Delete the indicated cell from the indicated row of the given table. If // the cell is the only row in the table, delete the whole table. Return // S_OK or a Trident error. // inline HRESULT CTriEditDocument::DeleteCellEx(IHTMLElement *pTable, IDispatch *pRow, LONG indexRow, LONG indexCell) { HRESULT hr = S_OK; CComPtr srpCells; INT cCells = 0; if (FAILED(hr = GetDispatchProperty(pRow, L"cells", VT_DISPATCH, (void**)&srpCells))) goto Fail; if (FAILED(hr = GetDispatchProperty(srpCells, L"length", VT_I4, &cCells))) goto Fail; // if this is the only cell in the table, delete the whole row if (cCells == 1) { _ASSERT(indexCell == 0); hr = DeleteRowEx(pTable, indexRow); } else { if (FAILED(hr = CallDispatchMethod(pRow, L"deleteCell", VTS_I4, indexCell))) goto Fail; } Fail: return hr; } /////////////////////////////////////////////////////////////////////////////// // // CTriEditDocument::DeleteTable // // Delete the given table. Return S_OK if all goes well; E_FAIL if something // goes wrong. // HRESULT CTriEditDocument::DeleteTable(IHTMLElement *pTable) { CComPtr srpParent; HRESULT hr = E_FAIL; _ASSERTE(pTable != NULL); if (pTable == NULL) goto Fail; if (FAILED(hr=pTable->get_offsetParent(&srpParent))) goto Fail; _ASSERTE(srpParent != NULL); if (FAILED(hr = SelectElement(pTable, srpParent))) goto Fail; hr = Exec(&CMDSETID_Forms3, IDM_DELETE, OLECMDEXECOPT_DONTPROMPTUSER, NULL, NULL); Fail: return hr; } /////////////////////////////////////////////////////////////////////////////// // // CTriEditDocument::InsertTableRow // // Insert a new table row in to the table which contains the Trident selection, // in the row preceding the selection. The new row will have the same number of // cells as the row containing the selection. The colSpan of each new cell // will be copied from the row containing the selection. The entire operation // is a single undo unit. Returns S_OK or a Trident error. // HRESULT CTriEditDocument::InsertTableRow(void) { HRESULT hr = S_OK; CComPtr srpCell,srpCellNew, srpTable,srpCells,srpRows,srpNewRow,srpCellsNew; LONG ccolSpan=0; LONG cCells=0,i=0; struct SELCELLINFO selinfo; CUndoPackManager undoPackMgr(m_pUnkTrident); undoPackMgr.Start(); if (FAILED(hr = IsSelectionInTable(&srpTable))) goto Fail; if (FAILED(hr = FillInSelectionCellsInfo(&selinfo, NULL))) goto Fail; if (FAILED(hr = CallDispatchMethod(srpTable, L"insertRow", VTS_I4, selinfo.cRowIndex))) goto Fail; if (FAILED(hr = GetDispatchProperty(srpTable, L"rows", VT_DISPATCH, (void**)&srpRows))) goto Fail; if (FAILED(hr = CallDispatchMethod(srpRows, L"Item", VTS_I4 VTS_DISPATCH_RETURN, selinfo.cRowIndex, &srpNewRow))) goto Fail; CopyStyle(selinfo.srpRow, srpNewRow); // get the number of cells contains in the selected row if (FAILED(hr = GetDispatchProperty(selinfo.srpRow, L"cells", VT_DISPATCH, (void**)&srpCells))) goto Fail; if (FAILED(hr = GetDispatchProperty(srpCells, L"length", VT_I4, &cCells))) goto Fail; // now insert cells for (i=cCells-1; i >=0; i--) { if (FAILED(hr = CallDispatchMethod(srpNewRow, L"insertCell", VTS_I4, 0))) goto Fail; srpCell.Release(); if (FAILED(hr = CallDispatchMethod(srpCells, L"Item", VTS_I4 VTS_DISPATCH_RETURN, i, &srpCell))) goto Fail; srpCellsNew.Release(); if (FAILED(hr = GetDispatchProperty(srpNewRow, L"cells", VT_DISPATCH, (void**)&srpCellsNew))) goto Fail; srpCellNew.Release(); if (FAILED(hr = CallDispatchMethod(srpCellsNew, L"Item", VTS_I4 VTS_DISPATCH_RETURN, 0, &srpCellNew))) goto Fail; CopyStyle(srpCell, srpCellNew); CopyProperty(srpCell, srpCellNew); { VARIANT width; VariantInit(&width); if (SUCCEEDED(hr = GetDispatchProperty(srpCell, L"width", VT_VARIANT, &width))) PutDispatchProperty(srpCellNew, L"width", VT_VARIANT, width); } if (SUCCEEDED(hr = GetDispatchProperty(srpCell, L"colSpan", VT_I4, &ccolSpan))) PutDispatchProperty(srpCellNew, L"colSpan", VT_I4, ccolSpan); } Fail: return hr; } /////////////////////////////////////////////////////////////////////////////// // // CTriEditDocument::MapCellToFirstRowCell // // Given a table cell in pselInfo, return (by modifying pselInfo) the cell in // the first row with the same column position, accounting for colSpans. Return // S_OK or a Trident error. // HRESULT CTriEditDocument::MapCellToFirstRowCell(IDispatch *srpTable, struct SELCELLINFO *pselinfo) { HRESULT hr = 0; CComPtr srpCell, srpCells,srpRow,srpRows; INT i=0,iCellIndex=0,iColSpanCurRow=0,cSpan=0,iColSpanFirstRow=0,crowSpan=0; _ASSERTE(pselinfo != NULL); // if current selection is not first row, find the corresponding first row cell index if (pselinfo->cRowIndex == 0) return S_OK; srpCells.Release(); _ASSERTE(pselinfo->srpRow != NULL); if (FAILED(hr = GetDispatchProperty(pselinfo->srpRow, L"cells", VT_DISPATCH, (void**)&srpCells))) goto Fail; for (i=0; i < pselinfo->cCellIndex ; i++) { srpCell.Release(); _ASSERTE(srpCells != NULL); if (FAILED(hr = CallDispatchMethod(srpCells, L"Item", VTS_I4 VTS_DISPATCH_RETURN, i, &srpCell))) goto Fail; _ASSERTE(srpCell != NULL); if (FAILED(hr = GetDispatchProperty(srpCell, L"colSpan", VT_I4, &cSpan))) goto Fail; iColSpanCurRow += cSpan; } srpRows.Release(); _ASSERTE(srpTable != NULL); if (FAILED(hr = GetDispatchProperty(srpTable, L"rows", VT_DISPATCH, (void**)&srpRows))) goto Fail; _ASSERTE(srpRows != NULL); srpRow.Release(); if (FAILED(hr = CallDispatchMethod(srpRows, L"Item",VTS_I4 VTS_DISPATCH_RETURN, 0, &srpRow))) goto Fail; srpCells.Release(); if (FAILED(hr = GetDispatchProperty(srpRow, L"cells", VT_DISPATCH, (void**)&srpCells))) goto Fail; iCellIndex=-1; while(iColSpanCurRow >= iColSpanFirstRow) { iCellIndex++; srpCell.Release(); _ASSERTE(srpCells != NULL); if (FAILED(hr = CallDispatchMethod(srpCells, L"Item", VTS_I4 VTS_DISPATCH_RETURN, iCellIndex, &srpCell))) goto Fail; // we might hit the end. If so, first row is shorter than curret row and there's no mapping first row, bail out... if (srpCell == NULL) { hr = E_FAIL; goto Fail; } _ASSERTE(srpCell != NULL); if (FAILED(hr = GetDispatchProperty(srpCell, L"colSpan", VT_I4, &cSpan))) goto Fail; iColSpanFirstRow += cSpan; if (FAILED(hr = GetDispatchProperty(srpCell, L"rowSpan", VT_I4, &crowSpan))) goto Fail; if (crowSpan > pselinfo->cRowIndex) { iColSpanCurRow += cSpan; } } pselinfo->srpCell = srpCell; pselinfo->srpRow.Release(); if (FAILED(hr = GetTableRowElementAndTableFromCell(pselinfo->srpCell, NULL, &pselinfo->srpRow))) goto Fail; pselinfo->cRowIndex = 0; _ASSERTE(iCellIndex >= 0); pselinfo->cCellIndex = iCellIndex; Fail: return hr; } /////////////////////////////////////////////////////////////////////////////// // // CTriEditDocument::InsertTableCol // // Insert a new column in to the table containing the selection, at the column // of the selection. The entire operation is a single undo unit. Return S_OK // or a Trident error. // HRESULT CTriEditDocument::InsertTableCol(void) { HRESULT hr = S_OK; CComPtr srpCellNew, srpTable,srpRows,srpRow,srpCells,srpCell; LONG cRows=0,i=0, j=0, iColSpanInsert=0, iColSpanCur=0, cSpan=0,crowSpan = 0, cCells=0; struct SELCELLINFO selinfo; INT *pccolFix = NULL; CUndoPackManager undoPackMgr(m_pUnkTrident); undoPackMgr.Start(); if (FAILED(hr = IsSelectionInTable(&srpTable))) goto Fail; if (FAILED(hr = FillInSelectionCellsInfo(&selinfo, NULL))) goto Fail; MapCellToFirstRowCell(srpTable, &selinfo); srpCells.Release(); _ASSERTE(selinfo.srpRow != NULL); if (FAILED(hr = GetDispatchProperty(selinfo.srpRow, L"cells", VT_DISPATCH, (void**)&srpCells))) goto Fail; _ASSERTE(srpTable != NULL); if (FAILED(hr = GetDispatchProperty(srpTable, L"rows", VT_DISPATCH, (void**)&srpRows))) goto Fail; _ASSERTE(srpRows != NULL); if (FAILED(hr = GetDispatchProperty(srpRows, L"length", VT_I4, &cRows))) goto Fail; pccolFix = new INT[cRows]; _ASSERTE(pccolFix != NULL); for (i=0; i< cRows; i++) *(pccolFix+i) = 0; for (i=0; i < selinfo.cCellIndex; i++) { srpCell.Release(); _ASSERTE(srpCells != NULL); if (FAILED(hr = CallDispatchMethod(srpCells, L"Item", VTS_I4 VTS_DISPATCH_RETURN, i, &srpCell))) goto Fail; _ASSERTE(srpCell != NULL); if (FAILED(hr = GetDispatchProperty(srpCell, L"colSpan", VT_I4, &cSpan))) goto Fail; iColSpanInsert += cSpan; if (FAILED(hr = GetDispatchProperty(srpCell, L"rowSpan", VT_I4, &crowSpan))) goto Fail; // if someone before the current cell has row span, this needs to propogate to // the next spanned rows if (crowSpan > 1) { for (j= selinfo.cRowIndex+1; j < (selinfo.cRowIndex+crowSpan); j++) *(pccolFix+j) += cSpan; } } for (i=0; i < cRows;) { srpRow.Release(); _ASSERTE(srpRows != NULL); if (FAILED(hr = CallDispatchMethod(srpRows, L"Item", VTS_I4 VTS_DISPATCH_RETURN, i, &srpRow))) goto Fail; srpCells.Release(); _ASSERTE(srpRow != NULL); if (FAILED(hr = GetDispatchProperty(srpRow, L"cells", VT_DISPATCH, (void**)&srpCells))) goto Fail; _ASSERTE(srpCells != NULL); if (FAILED(hr = GetDispatchProperty(srpCells, L"length", VT_I4, &cCells))) goto Fail; iColSpanCur = *(pccolFix+i); for (j=0; j < cCells; j++) { srpCell.Release(); if (FAILED(hr = CallDispatchMethod(srpCells, L"Item", VTS_I4 VTS_DISPATCH_RETURN, j, &srpCell))) goto Fail; _ASSERTE(srpCell != NULL); if (FAILED(hr = GetDispatchProperty(srpCell, L"colSpan", VT_I4, &cSpan))) goto Fail; if (iColSpanCur >= iColSpanInsert) break; iColSpanCur += cSpan; } _ASSERTE(srpRow != NULL); if (FAILED(hr = CallDispatchMethod(srpRow, L"insertCell", VTS_I4, j))) goto Fail; srpCells.Release(); if (FAILED(hr = GetDispatchProperty(srpRow, L"cells", VT_DISPATCH, (void**)&srpCells))) goto Fail; srpCellNew.Release(); if (FAILED(hr = CallDispatchMethod(srpCells, L"Item", VTS_I4 VTS_DISPATCH_RETURN, j, &srpCellNew))) goto Fail; if (!(!srpCell)) { CopyStyle(srpCell, srpCellNew); CopyProperty(srpCell, srpCellNew); { VARIANT height; VariantInit(&height); if (SUCCEEDED(hr = GetDispatchProperty(srpCell, L"height", VT_VARIANT, &height))) PutDispatchProperty(srpCellNew, L"height", VT_VARIANT, height); } if (SUCCEEDED(GetDispatchProperty(srpCell, L"rowSpan", VT_I4, &cSpan))) PutDispatchProperty(srpCellNew, L"rowSpan", VT_I4, cSpan); } // cSpan might be 0 if we are inserting a cell into an empty row i += max(1, cSpan); } Fail: if (pccolFix != NULL) delete [] pccolFix; return hr; } /////////////////////////////////////////////////////////////////////////////// // // CTriEditDocument::DeleteTableCols // // Delete the table columns that are contained within the Trident selection. // The entire operation is a single undo unit. Return S_OK or a Trident error. // HRESULT CTriEditDocument::DeleteTableCols(void) { CComPtr srpRows,srpRow,srpCells,srpCell; CComPtr srpTable; struct SELCELLINFO selinfo[2]; // 0 is start cell, 1 is end cell LONG cRows=0, i=0, j=0, k=0, cCells=0; HRESULT hr=0; LONG iColSpanStart=0, iColSpanEnd=0,cColSpan=0,iColSpanCur=0, crowSpan=0; INT * pccolFixStart=NULL, *pccolFixEnd = NULL; CUndoPackManager undoPackMgr(m_pUnkTrident); undoPackMgr.Start(); if (FAILED(hr = IsSelectionInTable((IDispatch**)&srpTable))) goto Fail; if (FAILED(hr = FillInSelectionCellsInfo(&selinfo[0], &selinfo[1]))) goto Fail; if (!FAILED(MapCellToFirstRowCell(srpTable, &selinfo[1]))) MapCellToFirstRowCell(srpTable, &selinfo[0]); _ASSERTE(selinfo[0].srpRow != NULL); if (FAILED(hr = GetDispatchProperty(selinfo[0].srpRow, L"cells", VT_DISPATCH, (void**)&srpCells))) goto Fail; _ASSERTE(srpCells != NULL); if (FAILED(hr = GetDispatchProperty(srpCells, L"length", VT_I4, &cCells))) goto Fail; _ASSERTE(selinfo[1].cRowIndex == selinfo[0].cRowIndex); _ASSERTE(selinfo[1].cCellIndex >= selinfo[0].cCellIndex); srpRows.Release(); if (FAILED(hr = GetDispatchProperty(srpTable, L"rows", VT_DISPATCH, (void**)&srpRows))) goto Fail; _ASSERTE(srpRows != NULL); if (FAILED(hr = GetDispatchProperty(srpRows, L"length", VT_I4, &cRows))) goto Fail; pccolFixEnd = new INT[cRows]; pccolFixStart = new INT[cRows]; for (i=0; i< cRows; i++) { *(pccolFixStart+i) = 0; *(pccolFixEnd+i) = 0; } for (i=0; i<= selinfo[1].cCellIndex; i++) { srpCell.Release(); if (FAILED(hr = CallDispatchMethod(srpCells, L"Item", VTS_I4 VTS_DISPATCH_RETURN, i, &srpCell))) goto Fail; if (FAILED(hr = GetDispatchProperty(srpCell, L"colSpan", VT_I4, &cColSpan))) goto Fail; if (i < selinfo[0].cCellIndex) iColSpanStart += cColSpan; if (i <= selinfo[1].cCellIndex) iColSpanEnd += cColSpan; if (FAILED(hr = GetDispatchProperty(srpCell, L"rowSpan", VT_I4, &crowSpan))) goto Fail; if (crowSpan > 1) { if (i < selinfo[0].cCellIndex) { for (j= selinfo[0].cRowIndex+1; j < selinfo[0].cRowIndex+crowSpan; j++) *(pccolFixStart+j) += cColSpan; } if (i <= selinfo[1].cCellIndex) { for (j= selinfo[0].cRowIndex+1; j < selinfo[0].cRowIndex+crowSpan; j++) *(pccolFixEnd+j) += cColSpan; } } } for (j=cRows-1; j >= 0; j--) { srpRow.Release(); if (FAILED(hr = CallDispatchMethod(srpRows, L"Item", VTS_I4 VTS_DISPATCH_RETURN, j, &srpRow))) goto Fail; srpCells.Release(); if (FAILED(hr = GetDispatchProperty(srpRow, L"cells", VT_DISPATCH, (void**)&srpCells))) goto Fail; if (FAILED(hr = GetDispatchProperty(srpCells, L"length", VT_I4, &cCells))) goto Fail; iColSpanCur = 0; _ASSERTE(iColSpanEnd-*(pccolFixEnd+j) >= 0); _ASSERTE(iColSpanStart-*(pccolFixStart+j) >= 0); for (i=0, k=0; iColSpanCur <= (iColSpanEnd-*(pccolFixEnd+j)) && k < cCells ; i++, k++) { srpCell.Release(); if (FAILED(hr = CallDispatchMethod(srpCells, L"Item", VTS_I4 VTS_DISPATCH_RETURN, i, &srpCell))) goto Fail; if (FAILED(hr = GetDispatchProperty(srpCell, L"colSpan", VT_I4, &cColSpan))) goto Fail; if (iColSpanCur >= (iColSpanStart-*(pccolFixStart+j)) && iColSpanCur < (iColSpanEnd-*(pccolFixEnd+j))) { if (FAILED(hr = DeleteCellEx(srpTable, srpRow, j, i))) goto Fail; i--; // we've deleted one cell, need to decrement cell index } iColSpanCur += cColSpan; } } Fail: if (pccolFixStart != NULL) { delete [] pccolFixStart; } if (pccolFixEnd != NULL) { delete [] pccolFixEnd; } return hr; } /////////////////////////////////////////////////////////////////////////////// // // CTriEditDocument::InsertTableCell // // Insert a table cell before the cell containing the Trident selection; copy // the properties and style of the cell containing the selection to the new // cell. The entire operation is a single undo unit. Returns S_OK or a Trident // error. // HRESULT CTriEditDocument::InsertTableCell(void) { HRESULT hr = S_OK; struct SELCELLINFO selinfo; CComPtr srpCellNew, srpCells; CUndoPackManager undoPackMgr(m_pUnkTrident); undoPackMgr.Start(); if (FAILED(hr = FillInSelectionCellsInfo(&selinfo, NULL))) goto Fail; if (FAILED(hr = CallDispatchMethod(selinfo.srpRow, L"insertCell", VTS_I4, selinfo.cCellIndex))) goto Fail; srpCells.Release(); if (FAILED(hr = GetDispatchProperty(selinfo.srpRow, L"cells", VT_DISPATCH, (void**)&srpCells))) goto Fail; srpCellNew.Release(); if (FAILED(hr = CallDispatchMethod(srpCells, L"Item", VTS_I4 VTS_DISPATCH_RETURN, selinfo.cCellIndex, &srpCellNew))) goto Fail; CopyStyle(selinfo.srpCell, srpCellNew); CopyProperty(selinfo.srpCell, srpCellNew); Fail: return hr; } /////////////////////////////////////////////////////////////////////////////// // // CTriEditDocument::DeleteTableCells // // Delete the table cells contained within the Trident selection. Delete entire // rows as indicated. The entire operation is a single undo unit. Return // S_OK or a Trident error. // HRESULT CTriEditDocument::DeleteTableCells(void) { CComPtr srpTable,srpCells; struct SELCELLINFO selinfo[2]; // 0 is start cell, 1 is end cell LONG i=0, cCells=0; HRESULT hr=0; CUndoPackManager undoPackMgr(m_pUnkTrident); undoPackMgr.Start(); if (FAILED(hr = IsSelectionInTable((IDispatch**)&srpTable))) goto Fail; if (FAILED(hr = FillInSelectionCellsInfo(&selinfo[0], &selinfo[1]))) goto Fail; if (selinfo[0].cRowIndex == selinfo[1].cRowIndex) // same row { srpCells.Release(); if (FAILED(hr = GetDispatchProperty(selinfo[0].srpRow, L"cells", VT_DISPATCH, (void**)&srpCells))) goto Fail; if (FAILED(hr = GetDispatchProperty(srpCells, L"length", VT_I4, &cCells))) goto Fail; // if the selection is select all the cells in this row, delete the whole row instead if ( cCells == selinfo[1].cCellIndex+1 && selinfo[0].cCellIndex == 0) { if (FAILED(hr = DeleteRowEx(srpTable, selinfo[0].cRowIndex))) goto Fail; } else // delete cell by cell { for (i = selinfo[1].cCellIndex; i >= selinfo[0].cCellIndex; i--) { if (FAILED(hr = DeleteCellEx(srpTable, selinfo[0].srpRow, selinfo[0].cRowIndex, i))) goto Fail; } } } else { srpCells.Release(); if (FAILED(hr = GetDispatchProperty(selinfo[1].srpRow, L"cells", VT_DISPATCH, (void**)&srpCells))) goto Fail; if (FAILED(hr = GetDispatchProperty(srpCells, L"length", VT_I4, &cCells))) goto Fail; // if the selection ends at the last cell of the row, delete the whole row instead if ( cCells == selinfo[1].cCellIndex+1) { if (FAILED(hr = DeleteRowEx(srpTable, selinfo[1].cRowIndex))) goto Fail; } else // delete cell by cell { for (i = selinfo[1].cCellIndex; i >= 0; i--) { if (FAILED(hr = DeleteCellEx(srpTable, selinfo[1].srpRow, selinfo[1].cRowIndex, i))) goto Fail; } } for (i = selinfo[1].cRowIndex-1; i > selinfo[0].cRowIndex; i--) { if (FAILED(hr = DeleteRowEx(srpTable, i))) goto Fail; } if (selinfo[0].cCellIndex == 0) // if the selection is from first cell of a row across other rows, delete the whole row instead { if (FAILED(hr = DeleteRowEx(srpTable, selinfo[0].cRowIndex))) goto Fail; } else // delete cell by cell { srpCells.Release(); if (FAILED(hr = GetDispatchProperty(selinfo[0].srpRow, L"cells", VT_DISPATCH, (void**)&srpCells))) goto Fail; if (FAILED(hr = GetDispatchProperty(srpCells, L"length", VT_I4, &cCells))) goto Fail; for (i = cCells-1; i >= selinfo[0].cCellIndex; i--) { if (FAILED(hr = DeleteCellEx(srpTable, selinfo[0].srpRow, selinfo[0].cRowIndex, i))) goto Fail; } } } Fail: return hr; } /////////////////////////////////////////////////////////////////////////////// // // CTriEditDocument::MergeTableCells // // Merge the indicated cells in to a single cell, and adjust its colSpan. // The cells must be within a single table row. The innerHTML of all merged cells // is concatenated and placed in the remaining cell. Return S_OK or a Trident error. // HRESULT CTriEditDocument::MergeTableCells(IDispatch* srpTable, INT iRow, INT iIndexStart, INT iIndexEnd) { CComPtr srpCells,srpRows,srpCurRow,srpCell; INT ccolSpanTotal=0, i=0, ccolSpan=0; HRESULT hr=0; CComBSTR bstrText; CComBSTR bstrMergedText; if (FAILED(hr = GetDispatchProperty(srpTable, L"rows", VT_DISPATCH, (void**)&srpRows))) goto Fail; if (FAILED(hr = CallDispatchMethod(srpRows, L"Item", VTS_I4 VTS_DISPATCH_RETURN, iRow, &srpCurRow))) goto Fail; srpCells.Release(); if (FAILED(hr = GetDispatchProperty(srpCurRow, L"cells", VT_DISPATCH, (void**)&srpCells))) goto Fail; bstrMergedText.Empty(); ccolSpanTotal = 0; for (i = iIndexEnd; i >= iIndexStart; i--) { srpCell.Release(); if (FAILED(hr = CallDispatchMethod(srpCells, L"Item", VTS_I4 VTS_DISPATCH_RETURN, i, &srpCell))) goto Fail; bstrText.Empty(); if (FAILED(hr = GetDispatchProperty(srpCell, L"innerHTML", VT_BSTR, &bstrText))) goto Fail; bstrText += bstrMergedText; bstrMergedText = bstrText; if (FAILED(hr = GetDispatchProperty(srpCell, L"colSpan", VT_I4, &ccolSpan))) goto Fail; ccolSpanTotal += ccolSpan; if (i != iIndexStart) { if (FAILED(hr = DeleteCellEx((IHTMLElement*)srpTable, srpCurRow, iRow, i))) goto Fail; } else { if (FAILED(hr = PutDispatchProperty(srpCell, L"colSpan", VT_I4, ccolSpanTotal))) goto Fail; if (FAILED(hr = PutDispatchProperty(srpCell, L"innerHTML", VT_BSTR, bstrMergedText))) goto Fail; } } Fail: return hr; } /////////////////////////////////////////////////////////////////////////////// // // CTriEditDocument::MergeTableCells // // Merge the cells in the Trident selection in to a single cell, and adjust that // cell's colSpan. The cells must be within a single table row. The innerHTML of // all merged cells is concatenated and placed in the remaining cell. Return S_OK // or a Trident error. // HRESULT CTriEditDocument::MergeTableCells(void) { CComPtr srpCell, srpCells,srpElement,srpRows,srpRow; CComPtr srpTable; struct SELCELLINFO selinfo[2]; // 0 is start cell, 1 is end cell LONG i=0, cCells=0; HRESULT hr=0; CComBSTR bstrText; CComBSTR bstrMergedText; CUndoPackManager undoPackMgr(m_pUnkTrident); undoPackMgr.Start(); if (FAILED(hr = IsSelectionInTable((IDispatch**)&srpTable))) goto Fail; if (FAILED(hr = FillInSelectionCellsInfo(&selinfo[0], &selinfo[1]))) goto Fail; if (selinfo[0].cRowIndex == selinfo[1].cRowIndex) { if (selinfo[1].cCellIndex == selinfo[0].cCellIndex) { hr = S_OK; goto Fail; } if (FAILED(hr = MergeTableCells(srpTable, selinfo[0].cRowIndex, selinfo[0].cCellIndex, selinfo[1].cCellIndex))) goto Fail; } else { srpCells.Release(); if (FAILED(hr = GetDispatchProperty(selinfo[1].srpRow, L"cells", VT_DISPATCH, (void**)&srpCells))) goto Fail; if (FAILED(hr = MergeTableCells(srpTable, selinfo[1].cRowIndex, 0, selinfo[1].cCellIndex))) goto Fail; if (FAILED(hr = GetDispatchProperty(srpTable, L"rows", VT_DISPATCH, (void**)&srpRows))) goto Fail; for (i = selinfo[1].cRowIndex-1; i > selinfo[0].cRowIndex; i--) { srpElement.Release(); if (FAILED(hr = CallDispatchMethod(srpRows, L"Item", VTS_I4 VTS_DISPATCH_RETURN, i, &srpElement))) goto Fail; srpCells.Release(); if (FAILED(hr = GetDispatchProperty(srpElement, L"cells", VT_DISPATCH, (void**)&srpCells))) goto Fail; if (FAILED(hr = GetDispatchProperty(srpCells, L"length", VT_I4, &cCells))) goto Fail; if (FAILED(hr = MergeTableCells(srpTable, i, 0, cCells-1))) goto Fail; } srpCells.Release(); if (FAILED(hr = GetDispatchProperty(selinfo[0].srpRow, L"cells", VT_DISPATCH, (void**)&srpCells))) goto Fail; if (FAILED(hr = GetDispatchProperty(srpCells, L"length", VT_I4, &cCells))) goto Fail; if (FAILED(hr = MergeTableCells(srpTable, selinfo[0].cRowIndex, selinfo[0].cCellIndex, cCells-1))) goto Fail; bstrMergedText.Empty(); for (i = selinfo[0].cRowIndex; i <= selinfo[1].cRowIndex; i++) { srpRows.Release(); if (FAILED(hr = GetDispatchProperty(srpTable, L"rows", VT_DISPATCH, (void**)&srpRows))) goto Fail; srpRow.Release(); if (FAILED(hr = CallDispatchMethod(srpRows, L"Item", VTS_I4 VTS_DISPATCH_RETURN, selinfo[0].cRowIndex, &srpRow))) goto Fail; srpCells.Release(); if (FAILED(hr = GetDispatchProperty(srpRow, L"cells", VT_DISPATCH, (void**)&srpCells))) goto Fail; srpCell.Release(); if (FAILED(hr = CallDispatchMethod(srpCells, L"Item", VTS_I4 VTS_DISPATCH_RETURN, 0, &srpCell))) goto Fail; bstrText.Empty(); if (FAILED(hr = GetDispatchProperty(srpCell, L"innerHTML", VT_BSTR, &bstrText))) goto Fail; bstrMergedText += L"

"; bstrMergedText += bstrText; bstrMergedText += L"

"; if (i != selinfo[1].cRowIndex) { if (FAILED(hr = DeleteRowEx(srpTable, selinfo[0].cRowIndex))) goto Fail; } } if (FAILED(hr = PutDispatchProperty(srpCell, L"innerHTML", VT_BSTR, bstrMergedText))) goto Fail; } Fail: return hr; } /////////////////////////////////////////////////////////////////////////////// // // CTriEditDocument::SplitTableCell // // Split the indicated table cell in to two cells and adjust the colSpan // of the relevant cells in the other rows as needed. The entire operation is // a single undo unit. Return S_OK or a Trident error. // HRESULT CTriEditDocument::SplitTableCell(IDispatch *srpTable, INT iRow, INT index) { CComPtr srpCellSplit, srpCells,srpCell,srpElement,srpRows,srpRow,srpCurRow,srpCellNew; INT cRows=0,i=0,j=0,ccolSpan=0,ccolSpanCur=0,crowSpan=0, cCells=0; HRESULT hr=0; CComBSTR bstrText; INT *pccolFix = NULL; CUndoPackManager undoPackMgr(m_pUnkTrident); undoPackMgr.Start(); if (FAILED(hr = GetDispatchProperty(srpTable, L"rows", VT_DISPATCH, (void**)&srpRows))) goto Fail; if (FAILED(hr = GetDispatchProperty(srpRows, L"length", VT_I4, &cRows))) goto Fail; if (FAILED(hr = CallDispatchMethod(srpRows, L"Item", VTS_I4 VTS_DISPATCH_RETURN, iRow, &srpCurRow))) goto Fail; if (FAILED(hr = CallDispatchMethod(srpCurRow, L"insertCell", VTS_I4, index+1))) goto Fail; srpCells.Release(); if (FAILED(hr = GetDispatchProperty(srpCurRow, L"cells", VT_DISPATCH, (void**)&srpCells))) goto Fail; srpCellNew.Release(); if (FAILED(hr = CallDispatchMethod(srpCells, L"Item", VTS_I4 VTS_DISPATCH_RETURN, index+1, &srpCellNew))) goto Fail; srpCellSplit.Release(); if (FAILED(hr = CallDispatchMethod(srpCells, L"Item", VTS_I4 VTS_DISPATCH_RETURN, index, (void**)&srpCellSplit))) goto Fail; ccolSpan=0; if (FAILED(hr = GetDispatchProperty(srpCellSplit, L"colSpan", VT_I4, &ccolSpan))) goto Fail; CopyStyle(srpCellSplit, srpCellNew); CopyProperty(srpCellSplit, srpCellNew); if (ccolSpan == 1) { INT ccolSpanStart = 0,ccolSpanEnd=0; INT ccolSpanTmp = 0, cRowSpan = 0; pccolFix = new INT[cRows]; for (j=0; j < cRows; j++) *(pccolFix+j) = 0; for (j=0; j 1) for (i = index+1; i < index+cRowSpan; i++) *(pccolFix+i) += ccolSpanTmp; } ccolSpanEnd = ccolSpanStart + ccolSpan; for (j=0; j < cRows; j++) { if (j == iRow) continue; srpRow.Release(); if (FAILED(hr = CallDispatchMethod(srpRows, L"Item", VTS_I4 VTS_DISPATCH_RETURN, j, &srpRow))) goto Fail; srpCells.Release(); if (FAILED(hr = GetDispatchProperty(srpRow, L"cells", VT_DISPATCH, (void**)&srpCells))) goto Fail; if (FAILED(hr = GetDispatchProperty(srpCells, L"length", VT_I4, &cCells))) goto Fail; ccolSpanCur = *(pccolFix+j); for(i=0 ; i < cCells; i++) { srpCell.Release(); if (FAILED(hr = CallDispatchMethod(srpCells, L"Item", VTS_I4 VTS_DISPATCH_RETURN, i, &srpCell))) goto Fail; ccolSpan=0; if (FAILED(hr = GetDispatchProperty(srpCell, L"colSpan", VT_I4, &ccolSpan))) goto Fail; if (ccolSpanStart <= ccolSpanCur && ccolSpanCur < ccolSpanEnd) { if (FAILED(hr = PutDispatchProperty(srpCell, L"colSpan", VT_I4, ccolSpan+1))) goto Fail; } if (ccolSpanCur >= ccolSpanEnd) break; ccolSpanCur += ccolSpan; } } } else { if (FAILED(hr = PutDispatchProperty(srpCellNew, L"colSpan", VT_I4, ccolSpan/2))) goto Fail; if (FAILED(hr = PutDispatchProperty(srpCellSplit, L"colSpan", VT_I4, ccolSpan-ccolSpan/2))) goto Fail; } // now copy row span if (FAILED(hr = GetDispatchProperty(srpCellSplit, L"rowSpan", VT_I4, &crowSpan))) goto Fail; if (FAILED(hr = PutDispatchProperty(srpCellNew, L"rowSpan", VT_I4, crowSpan))) goto Fail; Fail: if (pccolFix != NULL) { delete [] pccolFix; } return hr; } /////////////////////////////////////////////////////////////////////////////// // // CTriEditDocument::SplitTableCell // // Split the table cell in the Trident selection in to two cells and adjust the // colSpan of the relevant cells in the other rows as needed. The entire operation // is a single undo unit. Return S_OK or a Trident error. // HRESULT CTriEditDocument::SplitTableCell(void) { CComPtr srpCell, srpTable,srpCells,srpElement,srpRows,srpRow; struct SELCELLINFO selinfo[2]; // 0 is start cell, 1 is end cell LONG i=0, j=0,cCells=0; HRESULT hr=0; CUndoPackManager undoPackMgr(m_pUnkTrident); undoPackMgr.Start(); if (FAILED(hr = IsSelectionInTable(&srpTable))) goto Fail; if (FAILED(hr = FillInSelectionCellsInfo(&selinfo[0], &selinfo[1]))) goto Fail; if (FAILED(hr = GetDispatchProperty(srpTable, L"rows", VT_DISPATCH, (void**)&srpRows))) goto Fail; if (selinfo[0].cRowIndex == selinfo[1].cRowIndex) { for (i = selinfo[1].cCellIndex; i >= selinfo[0].cCellIndex; i--) { if (FAILED(hr = SplitTableCell(srpTable, selinfo[0].cRowIndex, i))) goto Fail; } } else { for (i = selinfo[1].cCellIndex; i >= 0; i--) { if (FAILED(hr = SplitTableCell(srpTable, selinfo[1].cRowIndex, i))) goto Fail; } for (i = selinfo[1].cRowIndex-1; i > selinfo[0].cRowIndex; i--) { srpElement.Release(); if (FAILED(hr = CallDispatchMethod(srpRows, L"Item", VTS_I4 VTS_DISPATCH_RETURN, i, &srpElement))) goto Fail; srpCells.Release(); if (FAILED(hr = GetDispatchProperty(srpElement, L"cells", VT_DISPATCH, (void**)&srpCells))) goto Fail; if (FAILED(hr = GetDispatchProperty(srpCells, L"length", VT_I4, &cCells))) goto Fail; for (j = cCells-1; j >= 0; j--) { if (FAILED(hr = SplitTableCell(srpTable, i, j))) goto Fail; } } srpCells.Release(); if (FAILED(hr = GetDispatchProperty(selinfo[0].srpRow, L"cells", VT_DISPATCH, (void**)&srpCells))) goto Fail; if (FAILED(hr = GetDispatchProperty(srpCells, L"length", VT_I4, &cCells))) goto Fail; for (i = cCells-1; i >= selinfo[0].cCellIndex; i--) { if (FAILED(hr = SplitTableCell(srpTable, selinfo[0].cRowIndex, i))) goto Fail; } } Fail: return hr; } /////////////////////////////////////////////////////////////////////////////// // // CTriEditDocument::InsertTable // // Insert a table in to the document at the selection point. All parameters // are optional and taken from members of pvarargIn as follows: // // pvarargIn[0] I4 - Number of rows; default 0. // pvarargIn[1] I4 - Number of columns; default 0. // pvarargIn[2] BSTR - Table tag attributes; default "". // pvarargIn[3] BSTR - Table cell attributes; default "". // pvarargIn[4] BSTR - Table caption; default "". // // pvarArgIn must be sipplied even if the default values are to be used for // all parameters. The entire operation is a single undo unit. The wait cursor // is displayed since this can be a fairly time-consuming operation. Returns S_OK // or a Trident error. // HRESULT CTriEditDocument::InsertTable(VARIANTARG *pvarargIn) { HRESULT hr=0; CComPtr srpSel; CComPtr srpRange; CComPtr srpCell; CComPtr srpiHTMLDoc; CComBSTR bstrHtml; CComBSTR bstrTblAttr; CComBSTR bstrTCellAttr; CComBSTR bstrCaption; int i=0, j=0, iRow=0, iCol=0; VARIANT rgvar[5]; HCURSOR hOldCursor; if (pvarargIn == NULL) return E_FAIL; hOldCursor = ::SetCursor(::LoadCursor(NULL, IDC_WAIT)); for(i = 0; i < sizeof(rgvar)/sizeof(VARIANT); i++) VariantInit(&rgvar[i]); // default iRow=1; iCol=1; bstrTCellAttr.Empty(); bstrTblAttr.Empty(); if (pvarargIn != NULL) { LONG lLBound=0, lUBound=0,lIndex=0; SAFEARRAY *psa; LONG cParam; // number of parameters host passes in psa = V_ARRAY(pvarargIn); SafeArrayGetLBound(psa, 1, &lLBound); SafeArrayGetUBound(psa, 1, &lUBound); cParam = 0; _ASSERTE(lLBound == 0); _ASSERTE(lUBound - lLBound < 5); for (lIndex = lLBound; lIndex <= lUBound && cParam < sizeof(rgvar)/sizeof(VARIANT); lIndex++) { SafeArrayGetElement(psa, &lIndex, &rgvar[cParam++]); } // first element: number of rows if (cParam >= 1) iRow = V_I4(&rgvar[0]); // 2'rd element: number of columns if (cParam >= 2) iCol = V_I4(&rgvar[1]); // 3'rd element: table tag attributes if (cParam >= 3) bstrTblAttr = V_BSTR(&rgvar[2]); // 4'th element: table cell tag attributes if (cParam >= 4) bstrTCellAttr = V_BSTR(&rgvar[3]); if (cParam >= 5) bstrCaption = V_BSTR(&rgvar[4]); } if (iRow < 0 || iCol < 0) goto Fail; bstrHtml.Empty(); bstrHtml += "QueryInterface(IID_IHTMLDocument2, (void**)&srpiHTMLDoc))) goto Fail; if (FAILED(hr = srpiHTMLDoc->get_selection(&srpSel))) goto Fail; if (FAILED(hr = CallDispatchMethod(srpSel, L"createRange", VTS_DISPATCH_RETURN, (void**)&srpRange))) goto Fail; if (FAILED(hr = CallDispatchMethod(srpRange, L"pasteHTML", VTS_BSTR, bstrHtml))) goto Fail; Fail: for(i = 0; i < sizeof(rgvar)/sizeof(VARIANT); i++) VariantClear(&rgvar[i]); ::SetCursor(hOldCursor); return hr; } /////////////////////////////////////////////////////////////////////////////// // // CTriEditDocument::DoVerb // // Execute the verb in pvarargIn (or OLEIVERB_PRIMARY if pvarargIn is NULL) // on the current object (which must QI for IHTMLObjectElement). Return E_FAIL // or the code returned as a result of executing the verb, // HRESULT CTriEditDocument::DoVerb(VARIANTARG *pvarargIn, BOOL fQueryStatus) { LONG iVerb; IHTMLObjectElement *piHTMLObjectElement = NULL; IDispatch *pDisp = NULL; IOleObject *pOleObj = NULL; HRESULT hr = E_FAIL; _ASSERTE(m_pihtmlElement != NULL); if (SUCCEEDED(m_pihtmlElement->QueryInterface(IID_IHTMLObjectElement, (void **)&piHTMLObjectElement)) && piHTMLObjectElement) { if (SUCCEEDED(piHTMLObjectElement->get_object(&pDisp)) && pDisp) { if (SUCCEEDED(pDisp->QueryInterface(IID_IOleObject, (void **)&pOleObj)) && pOleObj) { if (fQueryStatus) // In the query status case, we're done hr = S_OK; else { if (pvarargIn == NULL) iVerb = OLEIVERB_PRIMARY; else if (pvarargIn->vt == VT_I4) iVerb = V_I4(pvarargIn); else { hr = E_INVALIDARG; goto LSkipDoVerb; } GetTridentWindow(); _ASSERTE(m_hwndTrident != NULL); hr = pOleObj->DoVerb(iVerb, NULL, NULL, 0, m_hwndTrident, NULL); } LSkipDoVerb: pOleObj->Release(); } pDisp->Release(); } piHTMLObjectElement->Release(); } return hr; } /////////////////////////////////////////////////////////////////////////////// // // CTriEditDocument::GetDocument // // Return the IHTMLDocument pointer (under *ppihtmlDocument) and S_OK, or // E_FAIL/E_POINTER. // STDMETHODIMP CTriEditDocument::GetDocument(IHTMLDocument2** ppihtmlDocument) { _ASSERTE(ppihtmlDocument); if (ppihtmlDocument) { if (m_pUnkTrident) { return m_pUnkTrident->QueryInterface(IID_IHTMLDocument2, (LPVOID*)ppihtmlDocument); } return E_FAIL; } return E_POINTER; } /////////////////////////////////////////////////////////////////////////////// // // CTriEditDocument::GetAllColllection // // Return the all collection of the HTML document (under *ppihtmlCollection), // or E_FAIL. // STDMETHODIMP CTriEditDocument::GetAllCollection(IHTMLElementCollection** ppihtmlCollection) { IHTMLDocument2* pihtmlDoc2; HRESULT hr=E_FAIL; _ASSERTE(ppihtmlCollection); if (ppihtmlCollection && SUCCEEDED(GetDocument(&pihtmlDoc2))) { _ASSERTE(pihtmlDoc2); hr = pihtmlDoc2->get_all(ppihtmlCollection); pihtmlDoc2->Release(); } return hr; } /////////////////////////////////////////////////////////////////////////////// // // CTriEditDocument::GetCollectionElement // // Return the indicated element from the given collection under *ppihtmlElement. // Return S_OK if all goes well,or E_FAIL or a Triedent error on error. // STDMETHODIMP CTriEditDocument::GetCollectionElement( IHTMLElementCollection* pihtmlCollection, LONG iElem, IHTMLElement** ppihtmlElement) { VARIANT var; VARIANT varEmpty; IDispatch* pidispElement=NULL; HRESULT hr = E_FAIL; _ASSERTE(pihtmlCollection && iElem >= 0 && ppihtmlElement); if (!pihtmlCollection || iElem < 0 || !ppihtmlElement) return E_POINTER; *ppihtmlElement = NULL; //initialize [out] parameter VariantInit(&var); var.vt = VT_I4; var.lVal = iElem; VariantInit(&varEmpty); varEmpty.vt = VT_EMPTY; hr = pihtmlCollection->item(var, varEmpty, &pidispElement); if (SUCCEEDED(hr)) { if (pidispElement) { hr = pidispElement->QueryInterface(IID_IHTMLElement, (LPVOID*)ppihtmlElement); pidispElement->Release(); } else { hr = E_FAIL; } } return hr; } /////////////////////////////////////////////////////////////////////////////// // // // CTriEditDocument::Is2DCapable // // Return (under *pfBool) TRUE if the given HTML element can be positioned // out of the flow as a 2D element, or FALSE if not. Return S_OK in either // case. Return E_FAIL or a Trident error if something goes wrong. // STDMETHODIMP CTriEditDocument::Is2DCapable(IHTMLElement* pihtmlElement, BOOL* pfBool) { HRESULT hr= E_FAIL; CComBSTR bstrTag; _ASSERTE(pihtmlElement); if (!pihtmlElement || !pfBool) return E_POINTER; *pfBool = FALSE; bstrTag.Empty(); if (FAILED(hr = GetDispatchProperty(pihtmlElement, L"tagName", VT_BSTR, &bstrTag))) return E_FAIL; if (lstrcmpi(_T("APPLET"), OLE2T(bstrTag)) == 0 || lstrcmpi(_T("BUTTON"), OLE2T(bstrTag)) == 0 || lstrcmpi(_T("DIV"), OLE2T(bstrTag)) == 0 || lstrcmpi(_T("EMBED"), OLE2T(bstrTag)) == 0 || lstrcmpi(_T("FIELDSET"), OLE2T(bstrTag)) == 0 || lstrcmpi(_T("HR"), OLE2T(bstrTag)) == 0 || lstrcmpi(_T("IFRAME"), OLE2T(bstrTag)) == 0 || lstrcmpi(_T("IMG"), OLE2T(bstrTag)) == 0 || lstrcmpi(_T("INPUT"), OLE2T(bstrTag)) == 0 || lstrcmpi(_T("MARQUEE"), OLE2T(bstrTag)) == 0 || lstrcmpi(_T("OBJECT"), OLE2T(bstrTag)) == 0 || lstrcmpi(_T("SELECT"), OLE2T(bstrTag)) == 0 || lstrcmpi(_T("SPAN"), OLE2T(bstrTag)) == 0 || lstrcmpi(_T("TABLE"), OLE2T(bstrTag)) == 0 || lstrcmpi(_T("TEXTAREA"), OLE2T(bstrTag)) == 0 ) { *pfBool = TRUE; return S_OK; } return S_OK; } /////////////////////////////////////////////////////////////////////////////// // // CTriEditDocument::SelectElement // // Select the given element within Trident as a site selection. Return S_OK or // a Trident error. // STDMETHODIMP CTriEditDocument::SelectElement(IHTMLElement* pihtmlElement, IHTMLElement* pihtmlElementParent) { IHTMLControlElement* picont=NULL; IHTMLElement* piParent=NULL; IDispatch* pidisp=NULL; IHTMLTextContainer* pitext=NULL; IHTMLControlRange* pirange=NULL; HRESULT hr; CComBSTR bstrTag; if ( !pihtmlElement || !pihtmlElementParent ) return E_FAIL; hr = pihtmlElement->QueryInterface(IID_IHTMLControlElement, (LPVOID*)&picont); if ( FAILED(hr) ) goto CleanUp; _ASSERTE(picont); hr = pihtmlElementParent->QueryInterface(IID_IHTMLTextContainer, (LPVOID*)&pitext); if ( FAILED(hr) ) goto CleanUp; _ASSERTE(pitext); hr = pitext->createControlRange(&pidisp); if ( FAILED(hr) ) goto CleanUp; _ASSERTE(pitext); hr = pidisp->QueryInterface(IID_IHTMLControlRange, (LPVOID*)&pirange); if ( FAILED(hr) ) goto CleanUp; _ASSERTE(pirange); hr = pirange->add(picont); if ( FAILED(hr) ) goto CleanUp; hr = pirange->select(); CleanUp: SAFERELEASE(picont); SAFERELEASE(piParent); SAFERELEASE(pidisp); SAFERELEASE(pitext); SAFERELEASE(pirange); return hr; } /////////////////////////////////////////////////////////////////////////////// // // CTriEditDocument::IsElementDTC // // Return S_OK if the given element is a DTC (Design-Time Control) or E_FAIL // if not. // HRESULT CTriEditDocument::IsElementDTC(IHTMLElement *pihtmlElement) { IHTMLObjectElement *piHTMLObjectElement = NULL; IDispatch *pDisp = NULL; IActiveDesigner *piActiveDesigner = NULL; IUnknown *piUnk = NULL; if (SUCCEEDED(pihtmlElement->QueryInterface(IID_IHTMLObjectElement, (void **)&piHTMLObjectElement)) && piHTMLObjectElement) { if (SUCCEEDED(piHTMLObjectElement->get_object(&pDisp)) && pDisp) { if (SUCCEEDED(pDisp->QueryInterface(IID_IUnknown, (void **)&piUnk)) && piUnk) { if (SUCCEEDED(piUnk->QueryInterface(IID_IActiveDesigner, (void **)&piActiveDesigner)) && piActiveDesigner) { piHTMLObjectElement->Release(); pDisp->Release(); piUnk->Release(); piActiveDesigner->Release(); return S_OK; } piUnk->Release(); } pDisp->Release(); } piHTMLObjectElement->Release(); } return E_FAIL; }