diff options
Diffstat (limited to '')
-rw-r--r-- | embedserv/source/embed/docholder.cxx | 1368 | ||||
-rw-r--r-- | embedserv/source/embed/ed_idataobj.cxx | 293 | ||||
-rw-r--r-- | embedserv/source/embed/ed_iinplace.cxx | 79 | ||||
-rw-r--r-- | embedserv/source/embed/ed_ioleobject.cxx | 469 | ||||
-rw-r--r-- | embedserv/source/embed/ed_ipersiststr.cxx | 967 | ||||
-rw-r--r-- | embedserv/source/embed/esdll.cxx | 63 | ||||
-rw-r--r-- | embedserv/source/embed/guid.cxx | 131 | ||||
-rw-r--r-- | embedserv/source/embed/guid.hxx | 39 | ||||
-rw-r--r-- | embedserv/source/embed/iipaobj.cxx | 116 | ||||
-rw-r--r-- | embedserv/source/embed/intercept.cxx | 470 | ||||
-rw-r--r-- | embedserv/source/embed/servprov.cxx | 194 | ||||
-rw-r--r-- | embedserv/source/embed/syswinwrapper.cxx | 435 | ||||
-rw-r--r-- | embedserv/source/embed/tracker.cxx | 841 |
13 files changed, 5465 insertions, 0 deletions
diff --git a/embedserv/source/embed/docholder.cxx b/embedserv/source/embed/docholder.cxx new file mode 100644 index 000000000..12300b856 --- /dev/null +++ b/embedserv/source/embed/docholder.cxx @@ -0,0 +1,1368 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <docholder.hxx> +#include <embeddoc.hxx> +#include <intercept.hxx> +#include <syswinwrapper.hxx> +#include <iipaobj.hxx> +#include <common.h> + +#if !defined WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +#endif +#include <windows.h> + +#include <com/sun/star/awt/XTopWindow.hpp> +#include <com/sun/star/awt/PosSize.hpp> +#include <com/sun/star/awt/XView.hpp> +#include <com/sun/star/awt/Toolkit.hpp> +#include <com/sun/star/awt/XSystemChildFactory.hpp> +#include <com/sun/star/awt/XSystemDependentWindowPeer.hpp> +#include <com/sun/star/awt/XSystemDependentMenuPeer.hpp> +#include <com/sun/star/awt/WindowAttribute.hpp> +#include <com/sun/star/awt/XWindow.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/bridge/XBridgeSupplier2.hpp> +#include <com/sun/star/bridge/ModelDependent.hpp> +#include <com/sun/star/container/XNameAccess.hpp> +#include <com/sun/star/document/MacroExecMode.hpp> +#include <com/sun/star/embed/EmbedMapUnits.hpp> +#include <com/sun/star/embed/XVisualObject.hpp> +#include <com/sun/star/frame/TerminationVetoException.hpp> +#include <com/sun/star/frame/XComponentLoader.hpp> +#include <com/sun/star/frame/Frame.hpp> +#include <com/sun/star/frame/XModel.hpp> +#include <com/sun/star/frame/Desktop.hpp> +#include <com/sun/star/frame/XStatusListener.hpp> +#include <com/sun/star/lang/SystemDependent.hpp> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include <com/sun/star/task/InteractionHandler.hpp> +#include <com/sun/star/ui/XUIElement.hpp> +#include <com/sun/star/util/CloseVetoException.hpp> +#include <com/sun/star/util/XCloseBroadcaster.hpp> +#include <com/sun/star/util/XCloseable.hpp> +#include <com/sun/star/util/XModifyBroadcaster.hpp> +#include <comphelper/processfactory.hxx> +#include <comphelper/propertyvalue.hxx> +#include <o3tl/any.hxx> +#include <o3tl/unit_conversion.hxx> +#include <osl/diagnose.h> +#include <rtl/process.h> +#include <rtl/ref.hxx> + +using namespace ::com::sun::star; + +// add mutex locking ??? + +DocumentHolder::DocumentHolder( + const uno::Reference<lang::XMultiServiceFactory >& xFactory, + const ::rtl::Reference< EmbeddedDocumentInstanceAccess_Impl >& xOleAccess ) + : + m_bAllowInPlace(true), + m_pIOleIPSite(nullptr), + m_pIOleIPFrame(nullptr), + m_pIOleIPUIWindow(nullptr), + m_pCHatchWin(nullptr), + m_xOleAccess( xOleAccess ), + m_xFactory( xFactory ), + m_bOnDeactivate(false), + m_hWndxWinParent(nullptr), + m_hWndxWinCont(nullptr), + m_nMenuHandle(nullptr), + m_nMenuShared(nullptr), + m_nOLEMenu(nullptr), + m_nMacroExecMode( document::MacroExecMode::USE_CONFIG ), + m_bLink( false ) +{ + uno::Reference< frame::XDesktop2 > xDesktop = frame::Desktop::create(comphelper::getComponentContext(m_xFactory)); + xDesktop->addTerminateListener( static_cast<frame::XTerminateListener*>(this) ); +} + + +DocumentHolder::~DocumentHolder() +{ + delete m_pCHatchWin; + + ClearInterceptorInternally(); +} + + +void DocumentHolder::LoadDocInFrame( bool bPluginMode ) +{ + uno::Reference<frame::XComponentLoader> xComponentLoader( + m_xFrame,uno::UNO_QUERY); + if( xComponentLoader.is() && m_xDocument.is() ) + { + uno::Reference< task::XInteractionHandler2 > xHandler( + task::InteractionHandler::createWithParent(comphelper::getComponentContext(m_xFactory), nullptr) ); + + sal_Int32 nLen = bPluginMode ? 6 : 5; + uno::Sequence<beans::PropertyValue> aSeq( nLen ); + auto pSeq = aSeq.getArray(); + pSeq[0] = beans::PropertyValue( + "Model", + -1, + uno::Any(uno::Reference<uno::XInterface>(m_xDocument, uno::UNO_QUERY)), + beans::PropertyState_DIRECT_VALUE); + + pSeq[1] = beans::PropertyValue( + "ReadOnly", + -1, + uno::Any(false), + beans::PropertyState_DIRECT_VALUE); + + pSeq[2] = beans::PropertyValue( + "NoAutoSave", + -1, + uno::Any(true), + beans::PropertyState_DIRECT_VALUE); + + if ( bPluginMode ) + { + pSeq[3] = beans::PropertyValue( + "PluginMode", + -1, + uno::Any(sal_Int16(3)), + beans::PropertyState_DIRECT_VALUE); + } + + pSeq[nLen-2] = beans::PropertyValue( + "InteractionHandler", + -1, + uno::Any(xHandler), + beans::PropertyState_DIRECT_VALUE); + + pSeq[nLen-1] = beans::PropertyValue( + "MacroExecutionMode", + -1, + uno::Any(m_nMacroExecMode), + beans::PropertyState_DIRECT_VALUE); + + xComponentLoader->loadComponentFromURL( + "private:object", + "_self", + 0, + aSeq); + + const uno::Sequence< beans::PropertyValue > aResArgs = m_xDocument->getArgs(); + for ( beans::PropertyValue const & prop : aResArgs ) + if ( prop.Name == "MacroExecutionMode" ) + { + prop.Value >>= m_nMacroExecMode; + break; + } + } +} + +void DocumentHolder::OnPosRectChanged(LPRECT lpRect) const +{ + lpRect->left += m_aBorder.left; + lpRect->right -= m_aBorder.right; + lpRect->top += m_aBorder.top; + lpRect->bottom -= m_aBorder.bottom; + if(m_pIOleIPSite) + m_pIOleIPSite->OnPosRectChange(lpRect); +} + + +void DocumentHolder::DisableInplaceActivation(BOOL b) +{ + m_bAllowInPlace = ! b; +} + +BOOL DocumentHolder::isActive() const +{ + return m_pIOleIPSite != nullptr; +} + +HRESULT DocumentHolder::InPlaceActivate( + LPOLECLIENTSITE pActiveSite, + BOOL fIncludeUI) +{ + m_bOnDeactivate = false; + + if(!m_bAllowInPlace) + return ERROR; + + HRESULT hr; + HWND hWndSite; + RECT rcPos; + RECT rcClip; + OLEINPLACEFRAMEINFO frameInfo; + + if (nullptr==pActiveSite) + return ResultFromScode(E_INVALIDARG); + + if (nullptr!=m_pIOleIPSite) + { + if (fIncludeUI) + UIActivate(); + + return NOERROR; + } + + if ( !m_xDocument.is() ) + return ERROR; + + //1. Initialization, obtaining interfaces, OnInPlaceActivate. + hr=pActiveSite->QueryInterface( + IID_IOleInPlaceSite, + reinterpret_cast<void**>(&m_pIOleIPSite)); + + if (FAILED(hr)) + return hr; + + hr=m_pIOleIPSite->CanInPlaceActivate(); + + if (NOERROR!=hr) + { + m_pIOleIPSite->Release(); + m_pIOleIPSite=nullptr; + return ResultFromScode(E_FAIL); + } + + m_pIOleIPSite->OnInPlaceActivate(); + + //2. Get the site window + //3. and determine container frame and + // document window for tools and menus, as well + // as frameInfo for accelerators + m_pIOleIPSite->GetWindow(&hWndSite); + + frameInfo.cb=sizeof(OLEINPLACEFRAMEINFO); + m_pIOleIPSite->GetWindowContext( + &m_pIOleIPFrame,&m_pIOleIPUIWindow,&rcPos,&rcClip,&frameInfo); + + // initialize the office as, with hwnd as parentwindow + uno::Any aAny; + uno::Sequence<sal_Int8> aProcessIdent(16); + rtl_getGlobalProcessId(reinterpret_cast<sal_uInt8*>(aProcessIdent.getArray())); + + try + { + if(!m_xEditWindow.is()) + { // determine XWindow and window handle of parent + HWND hWndxWinParent(nullptr); + uno::Reference<awt::XWindow> xWin; + + uno::Reference<awt::XToolkit2> xToolkit = + awt::Toolkit::create(comphelper::getComponentContext(m_xFactory)); + + // create system window wrapper for hwnd + if( !m_pCHatchWin ) + m_pCHatchWin = new winwrap::CHatchWin( + m_hInstance,this); + + if(m_pCHatchWin->Init(hWndSite,/*ID_HATCHWINDOW*/2000, nullptr)) { + m_pCHatchWin->RectsSet(&rcPos,&rcClip); //set visible area + hWndxWinParent = m_pCHatchWin->Window(); + ShowWindow(hWndxWinParent,SW_SHOW); //Make visible. + } + else { + // no success initializing hatch window + delete m_pCHatchWin; + m_pCHatchWin = nullptr; + hWndxWinParent = hWndSite; + } + + xWin.set( + xToolkit->createSystemChild( + uno::Any(reinterpret_cast<sal_Int64>(hWndxWinParent)), + aProcessIdent, + lang::SystemDependent::SYSTEM_WIN32), + uno::UNO_QUERY); + + if(xWin.is()) { + xWin->setPosSize( + m_pCHatchWin ? HATCHWIN_BORDERWIDTHDEFAULT : 0, + m_pCHatchWin ? HATCHWIN_BORDERWIDTHDEFAULT : 0, + rcPos.right-rcPos.left, + rcPos.bottom - rcPos.top, + awt::PosSize::POSSIZE); + xWin->setVisible(true); + + m_xEditWindow = xWin; + m_hWndxWinParent = hWndxWinParent; + } + else + return ERROR; + } + else { + if(m_hWndxWinParent) { + SetParent(m_hWndxWinParent,hWndSite); + ShowWindow(m_hWndxWinParent,SW_SHOW); //Make visible. + } + + if ( !m_xFrame.is() ) + // initially set size to "empty", this guarantees that the final resize + // is always executed (will be done by "SetObjectRects" after getting internal border) + m_xEditWindow->setPosSize( + 0, + 0, + 0, + 0, + awt::PosSize::POSSIZE); + m_xEditWindow->setVisible(true); + } + + if(m_xContainerWindow.is()) { + if(m_hWndxWinCont) { + if(m_pIOleIPFrame) { + HWND hWndCont; + m_pIOleIPFrame->GetWindow(&hWndCont); + SetParent(m_hWndxWinCont,hWndCont); + ShowWindow(m_hWndxWinCont,SW_SHOW); + } + } + m_xContainerWindow->setVisible(true); + } + + if(m_xFrame.is()) + m_xFrame->activate(); + else { + // create frame and initialize it with the created window + m_xFrame = frame::Frame::create( comphelper::getComponentContext(m_xFactory) ); + m_xFrame->initialize(m_xEditWindow); + + m_xFrame->registerDispatchProviderInterceptor( CreateNewInterceptor() ); + + m_xLayoutManager.set( m_xFrame->getLayoutManager(), uno::UNO_QUERY ); + + if(m_xLayoutManager.is()) + m_xLayoutManager->setDockingAreaAcceptor(this); + + // load the model into the frame + LoadDocInFrame( true ); + + uno::Reference< frame::XDesktop2 > xDesktop = frame::Desktop::create(comphelper::getComponentContext(m_xFactory)); + xDesktop->getFrames()->append(m_xFrame); + + // determine the menuhandle to get menuitems. + if(m_xLayoutManager.is()) { + uno::Reference< css::ui::XUIElement > xUIEl( + m_xLayoutManager->getElement( + "private:resource/menubar/menubar")); + OSL_ENSURE(xUIEl.is(),"no menubar"); + uno::Reference<awt::XSystemDependentMenuPeer> xSDMP( + xUIEl->getRealInterface(), + uno::UNO_QUERY); + aAny = xSDMP->getMenuHandle( + aProcessIdent,lang::SystemDependent::SYSTEM_WIN32); + sal_Int64 tmp; + if( aAny >>= tmp ) + m_nMenuHandle = reinterpret_cast<HMENU>(tmp); + m_xLayoutManager->hideElement( + "private:resource/menubar/menubar" ); + } + } + + // TODO/cd: Workaround for status indicator bug. It always makes the + // document window visible, when someone tries to use the status + // indicator. As we save our document when we get the deactivation + // from OLE this conflict to hide floating windows. + if(m_xLayoutManager.is()) + m_xLayoutManager->setVisible(true); + + // get document border and resize rects according to border + GetDocumentBorder( &m_aBorder ); + SetObjectRects( &rcPos, &rcClip ); + + if ( m_xOleAccess.is() ) + { + LockedEmbedDocument_Impl aDocLock = m_xOleAccess->GetEmbedDocument(); + if ( aDocLock.GetEmbedDocument() ) + aDocLock.GetEmbedDocument()->ShowObject(); + } + + // setTitle(m_aDocumentNamePart); + if (fIncludeUI) + hr=UIActivate(); + + m_pIOleIPSite->DiscardUndoState(); + } + catch( const uno::Exception& ) + { + hr = ERROR; + } + + return hr; +} + + +void DocumentHolder::InPlaceDeactivate() +{ + m_bOnDeactivate = true; + + UIDeactivate(); + if(m_xFrame.is()) m_xFrame->deactivate(); + + if(m_xEditWindow.is()) { + m_xEditWindow->setVisible(false); + ShowWindow(m_hWndxWinParent,SW_HIDE); + SetParent(m_hWndxWinParent,nullptr); + } + + if(m_xContainerWindow.is()) { + m_xContainerWindow->setVisible(false); + ShowWindow(m_hWndxWinCont,SW_HIDE); + SetParent(m_hWndxWinCont,nullptr); + } + + // TODO/cd: Workaround for status indicator bug. It always makes the + // document window visible, when someone tries to use the status + // indicator. As we save our document when we get the deactivation + // from OLE this conflict to hide floating windows. + if (m_xLayoutManager.is()) + m_xLayoutManager->setVisible(false); + + if (nullptr!=m_pIOleIPSite) + m_pIOleIPSite->OnInPlaceDeactivate(); + + if(m_pIOleIPFrame) m_pIOleIPFrame->Release(); m_pIOleIPFrame = nullptr; + if(m_pIOleIPUIWindow) m_pIOleIPUIWindow->Release(); m_pIOleIPUIWindow = nullptr; + if(m_pIOleIPSite) m_pIOleIPSite->Release(); m_pIOleIPSite = nullptr; + + if ( m_xOleAccess.is() ) + { + LockedEmbedDocument_Impl aDocLock = m_xOleAccess->GetEmbedDocument(); + if ( aDocLock.GetEmbedDocument() ) + { + aDocLock.GetEmbedDocument()->SaveObject(); + } + } + + return; +} + + +HRESULT DocumentHolder::UIActivate() +{ + // 1. Call IOleInPlaceSite::UIActivate + if (nullptr!=m_pIOleIPSite) + m_pIOleIPSite->OnUIActivate(); + + //2. Critical for accelerators to work initially. + SetFocus(m_pCHatchWin->Window()); + // if(m_xEditWindow.is()) m_xEditWindow->setFocus(); + + //3. Set the active object + + OLECHAR starOffice[] = {'S','t','a','r','O','f','f','i','c','e',0}; + CComPtr< IOleInPlaceActiveObject > pObj = new CIIAObj( this ); + + if (nullptr!=m_pIOleIPFrame) + m_pIOleIPFrame->SetActiveObject( + pObj, starOffice ); + + if (nullptr!=m_pIOleIPUIWindow) + m_pIOleIPUIWindow->SetActiveObject( + pObj, starOffice ); + + //4. Create the shared menu. + InPlaceMenuCreate(); + + return NOERROR; +} + +void DocumentHolder::UIDeactivate() +{ + //1. Remove the shared menu. + InPlaceMenuDestroy(); + + if (nullptr!=m_pIOleIPFrame) + m_pIOleIPFrame->SetActiveObject(nullptr, nullptr); + + if (nullptr!=m_pIOleIPUIWindow) + m_pIOleIPUIWindow->SetActiveObject(nullptr, nullptr); + + //3. Call IOleInPlaceSite::OnUIDeactivate + if (nullptr!=m_pIOleIPSite) + m_pIOleIPSite->OnUIDeactivate(FALSE); + + return; +} + +static void CopyToOLEMenu(HMENU hOrig,WORD origPos,HMENU hDest,WORD destPos) +{ + HMENU subMenu(nullptr); + wchar_t buffer[256]; + + subMenu = GetSubMenu(hOrig,origPos); + GetMenuStringW(hOrig,origPos,buffer,256,MF_BYPOSITION); + InsertMenuW(hDest,destPos,MF_BYPOSITION | MF_POPUP, + reinterpret_cast<UINT_PTR>(subMenu),buffer); + + MENUITEMINFOW mi = {}; + mi.cbSize = sizeof(mi); + mi.fMask = MIIM_DATA; + if(GetMenuItemInfoW(hOrig,origPos,TRUE,&mi)) + SetMenuItemInfoW(hDest,destPos,TRUE,&mi); +} + +BOOL DocumentHolder::InPlaceMenuCreate() +{ + HMENU hMenu; + OLEMENUGROUPWIDTHS mgw; + + for (UINT i=0; i<6; i++) + mgw.width[i]=0; + + //We already have popup menu handles in m_pFR->m_phMenu[] + + //Create the new shared menu and let container do its thing + hMenu=CreateMenu(); + m_pIOleIPFrame->InsertMenus(hMenu,&mgw); + + int count = GetMenuItemCount(m_nMenuHandle); + int help = count-1; + + // start with 1, because we don't include "File" + WORD pos = static_cast<WORD>(mgw.width[0]); + CopyToOLEMenu(m_nMenuHandle,1,hMenu,pos); + mgw.width[1] = 1; + + // insert object menu here + pos = static_cast<WORD>(mgw.width[0] + mgw.width[1] + mgw.width[2]); + for(WORD i = 2; i < help-1; ++i,++pos) + CopyToOLEMenu(m_nMenuHandle,i,hMenu,pos); + mgw.width[3] = help - 3; + + // insert help menu + pos = static_cast<WORD>(mgw.width[0] + mgw.width[1] + mgw.width[2] + + mgw.width[3] + mgw.width[4]); + CopyToOLEMenu(m_nMenuHandle,WORD(help),hMenu,pos); + mgw.width[5] = 1; + + m_nMenuShared = hMenu; + m_nOLEMenu = OleCreateMenuDescriptor(m_nMenuShared,&mgw); + + uno::Reference<awt::XSystemDependentWindowPeer> xSysDepWin(m_xContainerWindow,uno::UNO_QUERY); + if(xSysDepWin.is()) { + uno::Sequence<sal_Int8> aProcessIdent(16); + rtl_getGlobalProcessId(reinterpret_cast<sal_uInt8*>(aProcessIdent.getArray())); + uno::Any aAny = xSysDepWin->getWindowHandle(aProcessIdent,lang::SystemDependent::SYSTEM_WIN32); + sal_Int64 tmp; + aAny >>= tmp; + HWND aHwnd = reinterpret_cast<HWND>(tmp); + m_pIOleIPFrame->SetMenu( + m_nMenuShared,m_nOLEMenu,aHwnd); + } + else + m_pIOleIPFrame->SetMenu( + m_nMenuShared,m_nOLEMenu,::GetWindow(m_hWndxWinParent,GW_CHILD)); + return TRUE; +} + +BOOL DocumentHolder::InPlaceMenuDestroy() +{ + if( nullptr == m_nMenuShared ) + return TRUE; + + m_pIOleIPFrame->SetMenu(nullptr,nullptr,nullptr); + + OleDestroyMenuDescriptor(m_nOLEMenu); + m_nOLEMenu = nullptr; + return TRUE; +} + +void DocumentHolder::OpenIntoWindow() +{ + // not implemented +} + +BOOL DocumentHolder::Undo() +{ + // not implemented + return false; +} + + +void DocumentHolder::FreeOffice() +{ + uno::Reference< frame::XDesktop2 > xDesktop = frame::Desktop::create(comphelper::getComponentContext(m_xFactory)); + xDesktop->removeTerminateListener( + static_cast<frame::XTerminateListener*>(this) ); +} + +void DocumentHolder::DisconnectFrameDocument( bool bComplete ) +{ + try + { + uno::Reference< util::XModifyBroadcaster > xModifiable( m_xDocument, uno::UNO_QUERY_THROW ); + xModifiable->removeModifyListener( static_cast<util::XModifyListener*>(this) ); + } + catch( const uno::Exception& ) + {} + + try + { + uno::Reference< util::XCloseBroadcaster > xBroadcaster( + m_xDocument, uno::UNO_QUERY_THROW ); + xBroadcaster->removeCloseListener( static_cast<util::XCloseListener*>(this) ); + } + catch( const uno::Exception& ) + {} + + try + { + uno::Reference< util::XCloseBroadcaster > xBroadcaster( + m_xFrame, uno::UNO_QUERY_THROW ); + xBroadcaster->removeCloseListener( static_cast<util::XCloseListener*>(this) ); + } + catch( const uno::Exception& ) + {} + + if ( bComplete ) + { + m_xFrame.clear(); + m_pIDispatch = nullptr; + m_xDocument.clear(); + } +} + +void DocumentHolder::CloseDocument() +{ + DisconnectFrameDocument(); + + uno::Reference< util::XCloseable > xCloseable( + m_xDocument, uno::UNO_QUERY ); + + if ( xCloseable.is() ) + { + try + { + xCloseable->close( true ); + } + catch( const uno::Exception& ) + {} + } + + m_pIDispatch = nullptr; + m_xDocument.clear(); +} + + +void DocumentHolder::CloseFrame() +{ + try + { + uno::Reference< util::XCloseBroadcaster > xBroadcaster( + m_xFrame, uno::UNO_QUERY_THROW ); + xBroadcaster->removeCloseListener( static_cast<util::XCloseListener*>(this) ); + } + catch( const uno::Exception& ) + {} + + uno::Reference<util::XCloseable> xCloseable( + m_xFrame,uno::UNO_QUERY); + if(xCloseable.is()) + try { + xCloseable->close(true); + } + catch( const uno::Exception& ) { + } + else if (m_xFrame.is()) + m_xFrame->dispose(); + + m_xFrame.clear(); +} + +void DocumentHolder::SetDocument( const uno::Reference< frame::XModel >& xDoc, bool bLink ) +{ + if ( m_xDocument.is() ) + CloseDocument(); + + m_xDocument = xDoc; + m_bLink = bLink; + + uno::Reference< util::XCloseBroadcaster > xBroadcaster( + m_xDocument, uno::UNO_QUERY ); + + if ( xBroadcaster.is() ) + xBroadcaster->addCloseListener( static_cast<util::XCloseListener*>(this) ); + + if ( m_xDocument.is() && !m_bLink ) + { + // set the document mode to embedded + uno::Sequence< beans::PropertyValue > aSeq{ comphelper::makePropertyValue("SetEmbedded", + true) }; + m_xDocument->attachResource(OUString(),aSeq); + } +} + +bool DocumentHolder::ExecuteSuspendCloseFrame() +{ + if ( m_xFrame.is() && m_xFactory.is() ) + { + try + { + uno::Reference< frame::XController > xController = m_xFrame->getController(); + if ( xController.is() ) + { + if ( !xController->suspend( true ) ) + return false; + + FreeOffice(); + try + { + uno::Reference<util::XCloseable> xCloseable( m_xFrame, uno::UNO_QUERY ); + if ( xCloseable.is() ) + xCloseable->close(true); + else + { + m_xFrame->dispose(); + } + } + catch( const util::CloseVetoException& ) + { + // should be called if the frame could not be closed + xController->suspend( false ); + } + } + } + catch( uno::Exception& ) + { + } + + m_xFrame.clear(); + } + + return true; +} + +uno::Reference< frame::XFrame2 > DocumentHolder::DocumentFrame() +{ + if(! m_xFrame.is() ) + { + uno::Reference<frame::XDesktop2> xDesktop = frame::Desktop::create(comphelper::getComponentContext(m_xFactory)); + + // the frame will be registered on desktop here, later when the document + // is loaded into the frame in ::show() method the terminate listener will be removed + // this is so only for outplace activation + m_xFrame.set( xDesktop->findFrame( "_blank", 0 ), uno::UNO_QUERY ); + + uno::Reference< util::XCloseBroadcaster > xBroadcaster( + m_xFrame, uno::UNO_QUERY ); + + if ( xBroadcaster.is() ) + { + xBroadcaster->addCloseListener( static_cast<util::XCloseListener*>(this) ); + FreeOffice(); // the frame is part of the desktop + } + } + + if( m_xFrame.is() ) + { + // intercept + m_xFrame->registerDispatchProviderInterceptor( CreateNewInterceptor() ); + } + + return m_xFrame; +} + + +uno::Reference< frame::XDispatchProviderInterceptor > DocumentHolder::CreateNewInterceptor() +{ + ::osl::MutexGuard aGuard( m_aMutex ); + + ClearInterceptorInternally(); + + uno::Reference< frame::XDispatchProviderInterceptor > xInterceptor( m_pInterceptor = new Interceptor( m_xOleAccess, this, m_bLink ) ); + m_xInterceptorLocker = xInterceptor; + return xInterceptor; +} + +void DocumentHolder::ClearInterceptorInternally() +{ + ::osl::MutexGuard aGuard( m_aMutex ); + uno::Reference< frame::XDispatchProviderInterceptor > xInterceptor( m_xInterceptorLocker ); + if ( xInterceptor.is() && m_pInterceptor ) + m_pInterceptor->DisconnectDocHolder(); + + m_xInterceptorLocker.clear(); + m_pInterceptor.clear(); +} + +void DocumentHolder::ClearInterceptor() +{ + ::osl::MutexGuard aGuard( m_aMutex ); + m_xInterceptorLocker.clear(); + m_pInterceptor.clear(); +} + + +void DocumentHolder::show() +{ + try + { + if(m_xFrame.is()) + { + m_xFrame->activate(); + uno::Reference<awt::XTopWindow> xTopWindow( + m_xFrame->getContainerWindow(),uno::UNO_QUERY); + if(xTopWindow.is()) + xTopWindow->toFront(); + } + else if( DocumentFrame().is() ) + { + LoadDocInFrame( false ); + + // get rid of second closer if it is there + uno::Reference< beans::XPropertySet > xLMProps( m_xFrame->getLayoutManager(), uno::UNO_QUERY ); + if ( xLMProps.is() ) + { + xLMProps->setPropertyValue("MenuBarCloser", + uno::Any( uno::Reference< frame::XStatusListener >() ) ); + } + + if ( !m_bLink ) + { + try + { + uno::Reference< util::XModifyBroadcaster > xModifiable( m_xDocument, uno::UNO_QUERY_THROW ); + xModifiable->addModifyListener( static_cast<util::XModifyListener*>(this) ); + } + catch( const uno::Exception& ) + {} + } + + if ( !m_bLink ) + setTitle(m_aDocumentNamePart); + } + } + catch( const uno::Exception& ) + { + OSL_FAIL( "Can not show the frame!" ); + } + +} + +void DocumentHolder::resizeWin( const SIZEL& rNewSize ) +{ + LockedEmbedDocument_Impl aDocLock; + + if ( m_xOleAccess.is() ) + aDocLock = m_xOleAccess->GetEmbedDocument(); + + if ( m_xFrame.is() && aDocLock.GetEmbedDocument() ) + { + uno::Reference< awt::XWindow > xWindow = m_xFrame->getContainerWindow(); + uno::Reference< awt::XView > xView( xWindow, uno::UNO_QUERY ); + + if ( xWindow.is() && xView.is() ) + { + float fScale = 1; + xView->setZoom( fScale, fScale ); + + SIZEL aOldSize; + GetExtent( &aOldSize ); + + if ( aOldSize.cx != rNewSize.cx || aOldSize.cy != rNewSize.cy ) + { + HDC hdc = GetDC( nullptr ); + SetMapMode( hdc, MM_HIMETRIC ); + + POINT aOldOffset; + aOldOffset.x = aOldSize.cx; + aOldOffset.y = aOldSize.cy; + LPtoDP( hdc, &aOldOffset, 1 ); + + POINT aNewOffset; + aNewOffset.x = rNewSize.cx; + aNewOffset.y = rNewSize.cy; + LPtoDP( hdc, &aNewOffset, 1 ); + + ReleaseDC( nullptr, hdc ); + + awt::Rectangle aWinRect = xWindow->getPosSize(); + + sal_Int32 aWidthDelta = aWinRect.Width - aOldOffset.x; + sal_Int32 aHeightDelta = aWinRect.Height - aOldOffset.y; + + if ( aWidthDelta > 0 && aHeightDelta > 0 ) + xWindow->setPosSize(0, + 0, + aNewOffset.x + aWidthDelta, + aNewOffset.y + aHeightDelta, + awt::PosSize::SIZE ); + } + } + } +} + +void DocumentHolder::setTitle(const OUString& aDocumentName) +{ + if(m_xFrame.is()) + { + if(m_aFilterName.getLength() == 0) + { + OUString aFilterName; + uno::Sequence<beans::PropertyValue> aSeq; + if(m_xDocument.is()) + { + aSeq = m_xDocument->getArgs(); + for(beans::PropertyValue const & prop : std::as_const(aSeq)) + { + if(prop.Name == "FilterName") + { + prop.Value >>= aFilterName; + break; + } + } + } + + if(aFilterName.getLength()) + { + uno::Reference<container::XNameAccess> xNameAccess( + m_xFactory->createInstance("com.sun.star.document.FilterFactory"), + uno::UNO_QUERY); + try { + if(xNameAccess.is() && + (xNameAccess->getByName(aFilterName) >>= aSeq)) + { + for(beans::PropertyValue const & prop : std::as_const(aSeq)) + if(prop.Name == "UIName") + { + prop.Value >>= m_aFilterName; + break; + } + } + } + catch(const uno::Exception& ) { + // nothing better to do here + m_aFilterName = aFilterName; + } + } + } + // set the title + OUString aTotalName(m_aFilterName + " (" + aDocumentName + ")"); + try { + m_xFrame->setTitle( aTotalName ); + } + catch( const uno::Exception& ) { + } + } + + m_aDocumentNamePart = aDocumentName; + + if(m_pInterceptor) + { + ::osl::ClearableMutexGuard aGuard( m_aMutex ); + + rtl::Reference<Interceptor> pTmpInter; + uno::Reference< frame::XDispatchProviderInterceptor > xLock( m_xInterceptorLocker ); + if ( xLock.is() && m_pInterceptor ) + pTmpInter = m_pInterceptor; + + aGuard.clear(); + + if ( pTmpInter ) + pTmpInter->generateFeatureStateEvent(); + } +} + + +void DocumentHolder::setContainerName(const OUString& aContainerName) +{ + m_aContainerName = aContainerName; +} + + +void DocumentHolder::hide() +{ + if(m_xFrame.is()) m_xFrame->deactivate(); + + //todo: sendadvise + // after hiding the window it is always allowed to InPlaceActivate it + m_bAllowInPlace = true; +} + +IDispatch* DocumentHolder::GetIDispatch() +{ + if ( !m_pIDispatch && m_xDocument.is() ) + { + uno::Reference< bridge::XBridgeSupplier2 > xSupplier( + m_xFactory->createInstance( "com.sun.star.bridge.OleBridgeSupplier2" ), uno::UNO_QUERY ); + + if ( xSupplier.is() ) + { + uno::Sequence< sal_Int8 > aProcId( 16 ); + rtl_getGlobalProcessId( reinterpret_cast<sal_uInt8*>(aProcId.getArray()) ); + + try { + uno::Any anyResult = xSupplier->createBridge( + uno::Any( m_xDocument ), + aProcId, + bridge::ModelDependent::UNO, + bridge::ModelDependent::OLE ); + + if ( auto var = o3tl::tryAccess<sal_uIntPtr>(anyResult) ) + { + VARIANT* pVariant = reinterpret_cast<VARIANT*>(*var); + if ( pVariant->vt == VT_DISPATCH ) + m_pIDispatch = pVariant->pdispVal; + + VariantClear( pVariant ); + CoTaskMemFree( pVariant ); + } + } + catch ( const uno::Exception& ) + {} + } + } + + return m_pIDispatch; +} + +HRESULT DocumentHolder::GetDocumentBorder( RECT *pRect ) +{ + if ( pRect && m_xDocument.is() ) + { + const uno::Sequence< beans::PropertyValue > aArgs = m_xDocument->getArgs(); + for ( beans::PropertyValue const & prop : aArgs ) + if ( prop.Name == "DocumentBorder" ) + { + uno::Sequence< sal_Int32 > aRect; + if ( ( prop.Value >>= aRect ) && aRect.getLength() == 4 ) + { + pRect->left = aRect[0]; + pRect->top = aRect[1]; + pRect->right = aRect[2]; + pRect->bottom = aRect[3]; + + return S_OK; + } + + break; + } + } + + return E_FAIL; +} + +HRESULT DocumentHolder::SetExtent( const SIZEL *pSize ) +{ + if ( pSize ) + { + uno::Reference< embed::XVisualObject > xVisObj( m_xDocument, uno::UNO_QUERY ); + if ( xVisObj.is() ) + { + try + { + awt::Size aNewSize( pSize->cx, pSize->cy ); + + sal_Int32 aMapMode = xVisObj->getMapUnit( DVASPECT_CONTENT ); + + // TODO/LATER: in future UNO API should be used for the conversion, currently there is no + if ( aMapMode == embed::EmbedMapUnits::TWIP ) + { + // conversion from ONE_100TH_MM + aNewSize.Width = o3tl::toTwips(aNewSize.Width, o3tl::Length::mm100); + aNewSize.Height = o3tl::toTwips(aNewSize.Height, o3tl::Length::mm100); + } + + + xVisObj->setVisualAreaSize( DVASPECT_CONTENT, aNewSize ); + + return S_OK; + } + catch( const uno::Exception& ) + {} + } + } + + return E_FAIL; +} + +HRESULT DocumentHolder::GetExtent( SIZEL *pSize ) +{ + if ( pSize ) + { + uno::Reference< embed::XVisualObject > xVisObj( m_xDocument, uno::UNO_QUERY ); + if ( xVisObj.is() ) + { + try + { + awt::Size aDocSize = xVisObj->getVisualAreaSize( DVASPECT_CONTENT ); + + sal_Int32 aMapMode = xVisObj->getMapUnit( DVASPECT_CONTENT ); + + // TODO/LATER: in future UNO API should be used for the conversion, currently there is no + if ( aMapMode == embed::EmbedMapUnits::TWIP ) + { + // conversion to ONE_100TH_MM + aDocSize.Width = o3tl::convert(aDocSize.Width, o3tl::Length::twip, o3tl::Length::mm100); + aDocSize.Height = o3tl::convert(aDocSize.Height, o3tl::Length::twip, o3tl::Length::mm100); + } + + pSize->cx = aDocSize.Width; + pSize->cy = aDocSize.Height; + + return S_OK; + } + catch( const uno::Exception& ) + {} + } + } + + return E_FAIL; +} + + +HRESULT DocumentHolder::SetContRects(LPCRECT aRect) +{ + if(m_xContainerWindow.is()) { + RECT wi = {}; + if(m_pIOleIPFrame) { + m_pIOleIPFrame->GetBorder(&wi); + m_xContainerWindow->setPosSize( + 0,0, + wi.right - wi.left, + wi.bottom - wi.top, + awt::PosSize::POSSIZE); + } + else + m_xContainerWindow->setPosSize( + 0,0, + aRect->right - aRect->left, + aRect->bottom - aRect->top, + awt::PosSize::POSSIZE); + return NOERROR; + } + else { + return ERROR; + } +} + + +HRESULT DocumentHolder::SetObjectRects(LPCRECT aRect, LPCRECT aClip) +{ + auto rect = *aRect; + rect.left -= m_aBorder.left; + rect.right += m_aBorder.right; + rect.top -= m_aBorder.top; + rect.bottom += m_aBorder.bottom; + auto clip = *aClip; + clip.left -= m_aBorder.left; + clip.right += m_aBorder.right; + clip.top -= m_aBorder.top; + clip.bottom += m_aBorder.bottom; + + if(m_pCHatchWin) + m_pCHatchWin->RectsSet(&rect, &clip); + if(m_xEditWindow.is()) { + m_xEditWindow->setVisible(false); + m_xEditWindow->setPosSize( + m_pCHatchWin ? HATCHWIN_BORDERWIDTHDEFAULT : 0, + m_pCHatchWin ? HATCHWIN_BORDERWIDTHDEFAULT : 0, + rect.right - rect.left, + rect.bottom - rect.top, + awt::PosSize::POSSIZE); + m_xEditWindow->setVisible(true); + } + return NOERROR; +} + + +css::uno::Reference< css::awt::XWindow> SAL_CALL DocumentHolder::getContainerWindow() +{ + if(m_xContainerWindow.is()) + return m_xContainerWindow; + + uno::Reference<awt::XWindow> xWin; + + uno::Reference<awt::XToolkit2> xToolkit = awt::Toolkit::create( comphelper::getComponentContext(m_xFactory) ); + + if(m_pIOleIPFrame) { + HWND hWnd; + m_pIOleIPFrame->GetWindow(&hWnd); + + uno::Sequence<sal_Int8> aProcessIdent(16); + rtl_getGlobalProcessId(reinterpret_cast<sal_uInt8*>(aProcessIdent.getArray())); + + xWin.set( + xToolkit->createSystemChild( + uno::Any(reinterpret_cast<sal_Int64>(hWnd)), + aProcessIdent, + lang::SystemDependent::SYSTEM_WIN32), + uno::UNO_QUERY); + + RECT wi = {}; + if(xWin.is() && m_pIOleIPFrame->GetBorder(&wi) == NOERROR) { + xWin->setVisible(true); + xWin->setPosSize( + 0,0, + wi.right-wi.left, + wi.bottom - wi.top, + awt::PosSize::POSSIZE); + + uno::Reference<awt::XSystemDependentWindowPeer> xSysWin( + xWin,uno::UNO_QUERY); + if(xSysWin.is()) { + uno::Any aAny = xSysWin->getWindowHandle( + aProcessIdent,lang::SystemDependent::SYSTEM_WIN32); + sal_Int64 tmp; + if( aAny >>= tmp ) + SetContainerWindowHandle(reinterpret_cast<HWND>(tmp)); + } + } + } + + m_xContainerWindow= xWin; + return xWin; +} + + +sal_Bool SAL_CALL DocumentHolder::requestDockingAreaSpace( const css::awt::Rectangle& RequestedSpace ) +{ + if(m_bOnDeactivate) + return true; + + BORDERWIDTHS bw; + SetRect(&bw, + RequestedSpace.X,RequestedSpace.Y, + RequestedSpace.Width,RequestedSpace.Height); + if( m_pIOleIPFrame ) + return m_pIOleIPFrame->RequestBorderSpace(&bw) == NOERROR ; + else + return false; +} + + +void SAL_CALL DocumentHolder::setDockingAreaSpace( const css::awt::Rectangle& BorderSpace ) +{ + if(m_bOnDeactivate) + return; + + BORDERWIDTHS bw; + SetRect(&bw, + BorderSpace.X,BorderSpace.Y, + BorderSpace.Width,BorderSpace.Height); + if( m_pIOleIPFrame ) { + RECT aRect; + GetClientRect(m_hWndxWinCont,&aRect); + HRGN hrgn1 = CreateRectRgn( + 0,0, + aRect.right,BorderSpace.Y); + HRGN hrgn2 = CreateRectRgn(aRect.right-BorderSpace.Width,0,aRect.right,aRect.bottom); + CombineRgn(hrgn1,hrgn1,hrgn2,RGN_OR); + DeleteObject(hrgn2); + hrgn2 = CreateRectRgn(0,aRect.bottom-BorderSpace.Height,aRect.right,aRect.bottom); + CombineRgn(hrgn1,hrgn1,hrgn2,RGN_OR); + DeleteObject(hrgn2); + hrgn2 = CreateRectRgn(0,0,BorderSpace.X,aRect.bottom); + CombineRgn(hrgn1,hrgn1,hrgn2,RGN_OR); + DeleteObject(hrgn2); + + SetWindowRgn(m_hWndxWinCont,hrgn1,true); + // not:: DeleteObject(hrgn1); + m_pIOleIPFrame->SetBorderSpace(&bw); + } +} + + +void SAL_CALL DocumentHolder::disposing( const css::lang::EventObject& aSource ) +{ + if ( m_xDocument.is() && m_xDocument == aSource.Source ) + { + m_pIDispatch = nullptr; + m_xDocument.clear(); + } + + if( m_xFrame.is() && m_xFrame == aSource.Source ) + m_xFrame.clear(); +} + + +void SAL_CALL +DocumentHolder::queryClosing( + const lang::EventObject& aSource, + sal_Bool /*bGetsOwnership*/ +) +{ + if (!m_bLink + && ((m_xDocument.is() && m_xDocument == aSource.Source) + || (m_xFrame.is() && m_xFrame == aSource.Source))) + throw util::CloseVetoException(); +} + + +void SAL_CALL +DocumentHolder::notifyClosing( + const lang::EventObject& aSource ) +{ + try + { + uno::Reference< util::XCloseBroadcaster > xEventBroadcaster( + aSource.Source, uno::UNO_QUERY_THROW ); + xEventBroadcaster->removeCloseListener( static_cast<util::XCloseListener*>(this) ); + } + catch( const uno::Exception& ) + {} + + if ( m_xDocument.is() && m_xDocument == aSource.Source ) + { + // can happen only in case of links + m_pIDispatch = nullptr; + m_xDocument.clear(); + m_xFrame.clear(); + + LockedEmbedDocument_Impl aDocLock = m_xOleAccess->GetEmbedDocument(); + if ( aDocLock.GetEmbedDocument() ) + aDocLock.GetEmbedDocument()->OLENotifyClosing(); + } + else if( m_xFrame.is() && m_xFrame == aSource.Source ) + m_xFrame.clear(); +} + +void SAL_CALL +DocumentHolder::queryTermination( + const lang::EventObject& /*aSource*/ +) +{ + if ( m_xDocument.is() ) + throw frame::TerminationVetoException(); +} + +void SAL_CALL +DocumentHolder::notifyTermination( + const lang::EventObject& aSource +) +{ + OSL_ENSURE( !m_xDocument.is(), "Just a disaster..." ); + uno::Reference< frame::XDesktop > xDesktop( + aSource.Source, uno::UNO_QUERY ); + + if ( xDesktop.is() ) + xDesktop->removeTerminateListener( static_cast<frame::XTerminateListener*>(this) ); +} + + +void SAL_CALL DocumentHolder::modified( const lang::EventObject& /*aEvent*/ ) +{ + if ( m_xOleAccess.is() ) + { + LockedEmbedDocument_Impl aDocLock = m_xOleAccess->GetEmbedDocument(); + if ( aDocLock.GetEmbedDocument() ) + aDocLock.GetEmbedDocument()->notify(); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/embedserv/source/embed/ed_idataobj.cxx b/embedserv/source/embed/ed_idataobj.cxx new file mode 100644 index 000000000..84886e63c --- /dev/null +++ b/embedserv/source/embed/ed_idataobj.cxx @@ -0,0 +1,293 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <embeddoc.hxx> + +#include <com/sun/star/uno/Any.h> +#include <com/sun/star/uno/Exception.hpp> +#include <com/sun/star/datatransfer/XTransferable.hpp> + + +#include <osl/thread.h> + +using namespace ::com::sun::star; + + +// EmbedDocument_Impl + + +sal_uInt64 EmbedDocument_Impl::getMetaFileHandle_Impl( bool isEnhMeta ) +{ + sal_uInt64 pResult = 0; + + uno::Reference< datatransfer::XTransferable > xTransferable( m_pDocHolder->GetDocument(), uno::UNO_QUERY ); + if ( xTransferable.is() ) + { + datatransfer::DataFlavor aFlavor; + + if ( isEnhMeta ) + { + aFlavor.MimeType = "application/x-openoffice-emf;windows_formatname=\"Image EMF\""; + aFlavor.HumanPresentableName = "Enhanced Windows MetaFile"; + } + else + { + aFlavor.MimeType = "application/x-openoffice-wmf;windows_formatname=\"Image WMF\""; + aFlavor.HumanPresentableName = "Windows GDIMetaFile"; + } + + aFlavor.DataType = cppu::UnoType<sal_uInt64>::get(); + + uno::Any aAny = xTransferable->getTransferData( aFlavor ); + aAny >>= pResult; + } + + return pResult; +} + + +// IDataObject + +COM_DECLSPEC_NOTHROW STDMETHODIMP EmbedDocument_Impl::GetData( FORMATETC * pFormatetc, STGMEDIUM * pMedium ) +{ + if ( !pFormatetc ) + return DV_E_FORMATETC; + + if ( !pMedium ) + return STG_E_MEDIUMFULL; + + if ( pFormatetc->dwAspect == DVASPECT_THUMBNAIL + || pFormatetc->dwAspect == DVASPECT_ICON + || pFormatetc->dwAspect == DVASPECT_DOCPRINT ) + return DV_E_DVASPECT; + + if ( pFormatetc->cfFormat == CF_ENHMETAFILE ) + { + if ( !( pFormatetc->tymed & TYMED_ENHMF ) ) + return DV_E_TYMED; + + HENHMETAFILE hMeta = reinterpret_cast<HENHMETAFILE>( getMetaFileHandle_Impl( true ) ); + + if ( hMeta ) + { + pMedium->tymed = TYMED_ENHMF; + pMedium->hEnhMetaFile = hMeta; + pMedium->pUnkForRelease = nullptr; + + return S_OK; + } + + return STG_E_MEDIUMFULL; + } + else if ( pFormatetc->cfFormat == CF_METAFILEPICT ) + { + if ( !( pFormatetc->tymed & TYMED_MFPICT ) ) + return DV_E_TYMED; + + HGLOBAL hMeta = reinterpret_cast<HGLOBAL>( getMetaFileHandle_Impl( false ) ); + + if ( hMeta ) + { + pMedium->tymed = TYMED_MFPICT; + pMedium->hMetaFilePict = hMeta; + pMedium->pUnkForRelease = nullptr; + + return S_OK; + } + + return STG_E_MEDIUMFULL; + } + else + { + CLIPFORMAT cf_embSource = static_cast<CLIPFORMAT>(RegisterClipboardFormatW( L"Embed Source" )); + CLIPFORMAT cf_embObj = static_cast<CLIPFORMAT>(RegisterClipboardFormatW( L"Embedded Object" )); + if ( pFormatetc->cfFormat == cf_embSource || pFormatetc->cfFormat == cf_embObj ) + { + if ( !( pFormatetc->tymed & TYMED_ISTORAGE ) ) + return DV_E_TYMED; + + CComPtr< IStorage > pNewStg; + HRESULT hr = StgCreateDocfile( nullptr, STGM_CREATE | STGM_READWRITE | STGM_DELETEONRELEASE, 0, &pNewStg ); + if ( FAILED( hr ) || !pNewStg ) return STG_E_MEDIUMFULL; + + hr = SaveTo_Impl( pNewStg ); + if ( FAILED( hr ) ) return STG_E_MEDIUMFULL; + + pMedium->tymed = TYMED_ISTORAGE; + pMedium->pstg = pNewStg; + pMedium->pstg->AddRef(); + pMedium->pUnkForRelease = pNewStg; + + return S_OK; + } + } + + return DV_E_FORMATETC; +} + +COM_DECLSPEC_NOTHROW STDMETHODIMP EmbedDocument_Impl::GetDataHere( FORMATETC * pFormatetc, STGMEDIUM * pMedium ) +{ + if ( !pFormatetc ) + return DV_E_FORMATETC; + + if ( !pMedium ) + return STG_E_MEDIUMFULL; + + if ( pFormatetc->dwAspect == DVASPECT_THUMBNAIL + || pFormatetc->dwAspect == DVASPECT_ICON + || pFormatetc->dwAspect == DVASPECT_DOCPRINT ) + return DV_E_DVASPECT; + + CLIPFORMAT cf_embSource = static_cast<CLIPFORMAT>(RegisterClipboardFormatW( L"Embed Source" )); + CLIPFORMAT cf_embObj = static_cast<CLIPFORMAT>(RegisterClipboardFormatW( L"Embedded Object" )); + + if ( pFormatetc->cfFormat == cf_embSource || pFormatetc->cfFormat == cf_embObj ) + { + if ( !( pFormatetc->tymed & TYMED_ISTORAGE ) ) + return DV_E_TYMED; + + if ( !pMedium->pstg ) return STG_E_MEDIUMFULL; + + HRESULT hr = SaveTo_Impl( pMedium->pstg ); + if ( FAILED( hr ) ) return STG_E_MEDIUMFULL; + + pMedium->tymed = TYMED_ISTORAGE; + pMedium->pUnkForRelease = nullptr; + + return S_OK; + } + + return DV_E_FORMATETC; +} + +COM_DECLSPEC_NOTHROW STDMETHODIMP EmbedDocument_Impl::QueryGetData( FORMATETC * pFormatetc ) +{ + if ( pFormatetc ) + { + if ( pFormatetc->dwAspect == DVASPECT_THUMBNAIL + || pFormatetc->dwAspect == DVASPECT_ICON + || pFormatetc->dwAspect == DVASPECT_DOCPRINT ) + return DV_E_DVASPECT; + + if ( pFormatetc->cfFormat == CF_ENHMETAFILE ) + { + if ( !( pFormatetc->tymed & TYMED_ENHMF ) ) + return DV_E_TYMED; + + return S_OK; + } + else if ( pFormatetc->cfFormat == CF_METAFILEPICT ) + { + if ( !( pFormatetc->tymed & TYMED_MFPICT ) ) + return DV_E_TYMED; + + return S_OK; + } + else + { + CLIPFORMAT cf_embSource = static_cast<CLIPFORMAT>(RegisterClipboardFormatW( L"Embed Source" )); + CLIPFORMAT cf_embObj = static_cast<CLIPFORMAT>(RegisterClipboardFormatW( L"Embedded Object" )); + if ( pFormatetc->cfFormat == cf_embSource || pFormatetc->cfFormat == cf_embObj ) + { + if ( !( pFormatetc->tymed & TYMED_ISTORAGE ) ) + return DV_E_TYMED; + + return S_OK; + } + } + } + + return DV_E_FORMATETC; + +} + +COM_DECLSPEC_NOTHROW STDMETHODIMP EmbedDocument_Impl::GetCanonicalFormatEtc( FORMATETC * pFormatetcIn, FORMATETC * pFormatetcOut ) +{ + if ( !pFormatetcIn || !pFormatetcOut ) + return DV_E_FORMATETC; + + pFormatetcOut->ptd = nullptr; + pFormatetcOut->cfFormat = pFormatetcIn->cfFormat; + pFormatetcOut->dwAspect = DVASPECT_CONTENT; + + if ( pFormatetcIn->cfFormat == CF_ENHMETAFILE ) + { + pFormatetcOut->tymed = TYMED_ENHMF; + return S_OK; + } + else if ( pFormatetcIn->cfFormat == CF_METAFILEPICT ) + { + pFormatetcOut->tymed = TYMED_MFPICT; + return S_OK; + } + else + { + CLIPFORMAT cf_embSource = static_cast<CLIPFORMAT>(RegisterClipboardFormatW( L"Embed Source" )); + CLIPFORMAT cf_embObj = static_cast<CLIPFORMAT>(RegisterClipboardFormatW( L"Embedded Object" )); + if ( pFormatetcIn->cfFormat == cf_embSource || pFormatetcIn->cfFormat == cf_embObj ) + { + pFormatetcOut->tymed = TYMED_ISTORAGE; + return S_OK; + } + } + + return DV_E_FORMATETC; +} + +COM_DECLSPEC_NOTHROW STDMETHODIMP EmbedDocument_Impl::SetData( FORMATETC * /*pFormatetc*/, STGMEDIUM * /*pMedium*/, BOOL /*fRelease*/ ) +{ + return E_NOTIMPL; +} + +COM_DECLSPEC_NOTHROW STDMETHODIMP EmbedDocument_Impl::EnumFormatEtc( DWORD dwDirection, IEnumFORMATETC ** /*ppFormatetc*/ ) +{ + if ( dwDirection == DATADIR_GET ) + return OLE_S_USEREG; + + return E_NOTIMPL; +} + +COM_DECLSPEC_NOTHROW STDMETHODIMP EmbedDocument_Impl::DAdvise( FORMATETC * pFormatetc, DWORD advf, IAdviseSink * pAdvSink, DWORD * pdwConnection ) +{ + if ( !m_pDAdviseHolder ) + if ( !SUCCEEDED( CreateDataAdviseHolder( &m_pDAdviseHolder ) ) || !m_pDAdviseHolder ) + return E_OUTOFMEMORY; + + return m_pDAdviseHolder->Advise( static_cast<IDataObject*>(this), pFormatetc, advf, pAdvSink, pdwConnection ); +} + +COM_DECLSPEC_NOTHROW STDMETHODIMP EmbedDocument_Impl::DUnadvise( DWORD dwConnection ) +{ + if ( !m_pDAdviseHolder ) + if ( !SUCCEEDED( CreateDataAdviseHolder( &m_pDAdviseHolder ) ) || !m_pDAdviseHolder ) + return E_OUTOFMEMORY; + + return m_pDAdviseHolder->Unadvise( dwConnection ); +} + +COM_DECLSPEC_NOTHROW STDMETHODIMP EmbedDocument_Impl::EnumDAdvise( IEnumSTATDATA ** ppenumAdvise ) +{ + if ( !m_pDAdviseHolder ) + if ( !SUCCEEDED( CreateDataAdviseHolder( &m_pDAdviseHolder ) ) || !m_pDAdviseHolder ) + return E_OUTOFMEMORY; + + return m_pDAdviseHolder->EnumAdvise( ppenumAdvise ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/embedserv/source/embed/ed_iinplace.cxx b/embedserv/source/embed/ed_iinplace.cxx new file mode 100644 index 000000000..eb33bf5ac --- /dev/null +++ b/embedserv/source/embed/ed_iinplace.cxx @@ -0,0 +1,79 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <embeddoc.hxx> +#include <osl/diagnose.h> + +COM_DECLSPEC_NOTHROW STDMETHODIMP EmbedDocument_Impl::GetWindow(HWND* hWnd) +{ + OSL_ENSURE(m_pDocHolder, "no document for inplace activation"); + + *hWnd = m_pDocHolder->GetTopMostWinHandle(); + if (*hWnd != nullptr) + return NOERROR; + else + return ERROR; +} + +COM_DECLSPEC_NOTHROW STDMETHODIMP EmbedDocument_Impl::ContextSensitiveHelp(BOOL) { return NOERROR; } + +COM_DECLSPEC_NOTHROW STDMETHODIMP EmbedDocument_Impl::InPlaceDeactivate() +{ + // no locking is used since the OLE must use the same thread always + if (m_bIsInVerbHandling) + return E_UNEXPECTED; + + BooleanGuard_Impl aGuard(m_bIsInVerbHandling); + + m_pDocHolder->InPlaceDeactivate(); + + // the inplace object needs the notification after the storing ( on deactivating ) + // if it happens before the storing the replacement might not be updated + notify(); + + return NOERROR; +} + +COM_DECLSPEC_NOTHROW STDMETHODIMP EmbedDocument_Impl::UIDeactivate() +{ + // no locking is used since the OLE must use the same thread always + if (m_bIsInVerbHandling) + return E_UNEXPECTED; + + BooleanGuard_Impl aGuard(m_bIsInVerbHandling); + + m_pDocHolder->UIDeactivate(); + + // the inplace object needs the notification after the storing ( on deactivating ) + // if it happens before the storing the replacement might not be updated + notify(); + + return NOERROR; +} + +COM_DECLSPEC_NOTHROW STDMETHODIMP EmbedDocument_Impl::SetObjectRects(LPCRECT aRect, LPCRECT aClip) +{ + OSL_ENSURE(m_pDocHolder, "no document for inplace activation"); + + return m_pDocHolder->SetObjectRects(aRect, aClip); +} + +COM_DECLSPEC_NOTHROW STDMETHODIMP EmbedDocument_Impl::ReactivateAndUndo() { return E_NOTIMPL; } + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/embedserv/source/embed/ed_ioleobject.cxx b/embedserv/source/embed/ed_ioleobject.cxx new file mode 100644 index 000000000..839fd8e2d --- /dev/null +++ b/embedserv/source/embed/ed_ioleobject.cxx @@ -0,0 +1,469 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <embeddoc.hxx> +#include <osl/diagnose.h> +#include <o3tl/char16_t2wchar_t.hxx> +#include <com/sun/star/beans/PropertyValue.hpp> + + +using namespace ::com::sun::star; + +// IOleObject + + +COM_DECLSPEC_NOTHROW STDMETHODIMP EmbedDocument_Impl::SetClientSite( IOleClientSite* pSite ) +{ + m_pClientSite = pSite; + return S_OK; +} + +COM_DECLSPEC_NOTHROW STDMETHODIMP EmbedDocument_Impl::GetClientSite( IOleClientSite** pSite ) +{ + *pSite = m_pClientSite; + return S_OK; +} + +COM_DECLSPEC_NOTHROW STDMETHODIMP EmbedDocument_Impl::SetHostNames( LPCOLESTR szContainerApp, LPCOLESTR szContainerObj ) +{ + // the code should be ignored for links + if ( !m_aFileName.getLength() ) + { + m_pDocHolder->setTitle(OUString(o3tl::toU(szContainerObj))); + m_pDocHolder->setContainerName(OUString(o3tl::toU(szContainerApp))); + } + + return S_OK; +} + +COM_DECLSPEC_NOTHROW STDMETHODIMP EmbedDocument_Impl::Close( DWORD dwSaveOption ) +{ + HRESULT hr = S_OK; + + if ( m_pDocHolder->HasFrame() ) + { + if ( dwSaveOption == 2 && m_aFileName.getLength() ) + { + // ask the user about saving + if ( m_pDocHolder->ExecuteSuspendCloseFrame() ) + { + m_pDocHolder->CloseDocument(); + return S_OK; + } + else + return OLE_E_PROMPTSAVECANCELLED; + } + + if ( dwSaveOption != 1 ) + hr = SaveObject(); // ADVF_DATAONSTOP); + + m_pDocHolder->CloseFrame(); + OLENotifyDeactivation(); + } + + m_pDocHolder->FreeOffice(); + m_pDocHolder->CloseDocument(); + + OLENotifyClosing(); + + return hr; +} + + +HRESULT EmbedDocument_Impl::OLENotifyClosing() +{ + AdviseSinkHashMap aAHM(m_aAdviseHashMap); + + for (auto const& advise : aAHM) + { + if (advise.second) + advise.second->OnClose(); + } + + return S_OK; + +} + +COM_DECLSPEC_NOTHROW STDMETHODIMP EmbedDocument_Impl::SetMoniker( DWORD /*dwWhichMoniker*/, IMoniker * /*pmk*/ ) +{ + return E_NOTIMPL; +} + +COM_DECLSPEC_NOTHROW STDMETHODIMP EmbedDocument_Impl::GetMoniker( DWORD /*dwAssign*/, DWORD /*dwWhichMoniker*/, IMoniker ** /*ppmk*/ ) +{ + return E_NOTIMPL; +} + +COM_DECLSPEC_NOTHROW STDMETHODIMP EmbedDocument_Impl::InitFromData( IDataObject * /*pDataObject*/, BOOL /*fCreation*/, DWORD /*dwReserved*/ ) +{ + return E_NOTIMPL; +} + +COM_DECLSPEC_NOTHROW STDMETHODIMP EmbedDocument_Impl::GetClipboardData( DWORD /*dwReserved*/, IDataObject ** /*ppDataObject*/ ) +{ + return E_NOTIMPL; +} + +/** + * Well, this is a not so very inefficient way to deliver + * + */ + +COM_DECLSPEC_NOTHROW STDMETHODIMP EmbedDocument_Impl::DoVerb( + LONG iVerb, + LPMSG, + IOleClientSite *pActiveSite, + LONG, + HWND, + LPCRECT ) +{ + // no locking is used since the OLE must use the same thread always + if ( m_bIsInVerbHandling ) + return OLEOBJ_S_CANNOT_DOVERB_NOW; + + // an object can not handle any Verbs in Hands off mode + if ( m_pMasterStorage == nullptr || m_pOwnStream == nullptr ) + return OLE_E_CANT_BINDTOSOURCE; + + + BooleanGuard_Impl aGuard( m_bIsInVerbHandling ); + + if ( iVerb == OLEIVERB_PRIMARY ) + { + if ( m_aFileName.getLength() ) + { + // that should be a link + iVerb = OLEIVERB_OPEN; + } + else + iVerb = OLEIVERB_SHOW; + } + + try + { + switch(iVerb) { + case OLEIVERB_DISCARDUNDOSTATE: + // free any undostate? + break; + case OLEIVERB_INPLACEACTIVATE: + OSL_ENSURE(m_pDocHolder,"no document for inplace activation"); + + return m_pDocHolder->InPlaceActivate(pActiveSite,FALSE); + case OLEIVERB_UIACTIVATE: + OSL_ENSURE(m_pDocHolder,"no document for inplace activation"); + + return m_pDocHolder->InPlaceActivate(pActiveSite,TRUE); + case OLEIVERB_PRIMARY: + case OLEIVERB_SHOW: + OSL_ENSURE(m_pDocHolder,"no document for inplace activation"); + + if(m_pDocHolder->isActive()) + return NOERROR; //Already active + + if(SUCCEEDED( + m_pDocHolder->InPlaceActivate( + pActiveSite,TRUE))) + return NOERROR; + + [[fallthrough]]; + case OLEIVERB_OPEN: + OSL_ENSURE(m_pDocHolder,"no document to open"); + + // the commented code could be useful in case + // outer window would be resized depending from inner one + // RECTL aEmbArea; + // m_pDocHolder->GetVisArea( &aEmbArea ); + // m_pDocHolder->show(); + // m_pDocHolder->SetVisArea( &aEmbArea ); + + if(m_pDocHolder->isActive()) + { + m_pDocHolder->InPlaceDeactivate(); + m_pDocHolder->DisableInplaceActivation(true); + } + + SIZEL aEmbSize; + m_pDocHolder->GetExtent( &aEmbSize ); + m_pDocHolder->show(); + m_pDocHolder->resizeWin( aEmbSize ); + + if ( m_pClientSite ) + m_pClientSite->OnShowWindow( TRUE ); + + notify(); + break; + case OLEIVERB_HIDE: + OSL_ENSURE(m_pDocHolder,"no document to hide"); + + if(m_pDocHolder->isActive()) + m_pDocHolder->InPlaceDeactivate(); + else { + m_pDocHolder->hide(); + + if( m_pClientSite ) + m_pClientSite->OnShowWindow(FALSE); + } + break; + default: + break; + } + } + catch( const uno::Exception& ) + { + return OLEOBJ_S_CANNOT_DOVERB_NOW; + } + + return NOERROR; +} + + +COM_DECLSPEC_NOTHROW STDMETHODIMP EmbedDocument_Impl::EnumVerbs( IEnumOLEVERB ** /*ppEnumOleVerb*/ ) +{ + return OLE_S_USEREG; +} + +COM_DECLSPEC_NOTHROW STDMETHODIMP EmbedDocument_Impl::Update() +{ + return S_OK; +// HRESULT hr = CACHE_E_NOCACHE_UPDATED; +// return hr; +} + +COM_DECLSPEC_NOTHROW STDMETHODIMP EmbedDocument_Impl::IsUpToDate() +{ + return S_OK; +} + +COM_DECLSPEC_NOTHROW STDMETHODIMP EmbedDocument_Impl::GetUserClassID( CLSID *pClsid ) +{ + return GetClassID( pClsid ); +} + +COM_DECLSPEC_NOTHROW STDMETHODIMP EmbedDocument_Impl::GetUserType( DWORD /*dwFormOfTypeUe*/, LPOLESTR * /*pszUserType*/ ) +{ + return OLE_S_USEREG; +} + +COM_DECLSPEC_NOTHROW STDMETHODIMP EmbedDocument_Impl::SetExtent( DWORD /*dwDrawAspect*/, SIZEL *psizel ) +{ + if ( !psizel ) + return E_FAIL; + + m_pDocHolder->SetExtent( psizel ); + + return S_OK; +} + +COM_DECLSPEC_NOTHROW STDMETHODIMP EmbedDocument_Impl::GetExtent( DWORD /*dwDrawAspect*/, SIZEL * psizel ) +{ + if ( !psizel ) + return E_INVALIDARG; + + if ( FAILED( m_pDocHolder->GetExtent( psizel ) ) ) + { + // return default values + psizel->cx = 500; + psizel->cy = 500; + } + + return S_OK; +} + +COM_DECLSPEC_NOTHROW STDMETHODIMP EmbedDocument_Impl::Advise( IAdviseSink *pAdvSink, DWORD *pdwConnection ) +{ + if ( m_nAdviseNum == 0xFFFFFFFF ) + return E_OUTOFMEMORY; + + pAdvSink->AddRef(); + m_aAdviseHashMap.insert( std::pair< DWORD, IAdviseSink* >( m_nAdviseNum, pAdvSink ) ); + *pdwConnection = m_nAdviseNum++; + + return S_OK; +} + +COM_DECLSPEC_NOTHROW STDMETHODIMP EmbedDocument_Impl::Unadvise( DWORD dwConnection ) +{ + auto iAdvise = m_aAdviseHashMap.find( dwConnection ); + if ( iAdvise != m_aAdviseHashMap.end() ) + { + iAdvise->second->Release(); + m_aAdviseHashMap.erase( iAdvise ); + } + else + return OLE_E_NOCONNECTION; + + return S_OK; +} + +COM_DECLSPEC_NOTHROW STDMETHODIMP EmbedDocument_Impl::EnumAdvise( IEnumSTATDATA ** /*ppenumAdvise*/ ) +{ + return E_NOTIMPL; +} + +COM_DECLSPEC_NOTHROW STDMETHODIMP EmbedDocument_Impl::GetMiscStatus( DWORD /*dwAspect*/, DWORD * /*pdwStatus*/ ) +{ + return OLE_S_USEREG; +} + +COM_DECLSPEC_NOTHROW STDMETHODIMP EmbedDocument_Impl::SetColorScheme( LOGPALETTE * /*pLogpal*/ ) +{ + return E_NOTIMPL; +} + + +// IDispatch + +COM_DECLSPEC_NOTHROW STDMETHODIMP EmbedDocument_Impl::GetTypeInfoCount(unsigned int* pctinfo) +{ + if ( m_pDocHolder->GetIDispatch() ) + return m_pDocHolder->GetIDispatch()->GetTypeInfoCount( pctinfo ); + + return E_NOTIMPL; +} + +COM_DECLSPEC_NOTHROW STDMETHODIMP EmbedDocument_Impl::GetTypeInfo(unsigned int iTInfo, LCID lcid, + ITypeInfo** ppTInfo) +{ + if ( m_pDocHolder->GetIDispatch() ) + return m_pDocHolder->GetIDispatch()->GetTypeInfo( iTInfo, lcid, ppTInfo ); + + return DISP_E_BADINDEX; // the only error that can be returned +} + +COM_DECLSPEC_NOTHROW STDMETHODIMP EmbedDocument_Impl::GetIDsOfNames( REFIID riid, + OLECHAR** rgszNames, + unsigned int cNames, + LCID lcid, + DISPID* rgDispId ) +{ + if ( m_pDocHolder->GetIDispatch() ) + return m_pDocHolder->GetIDispatch()->GetIDsOfNames( riid, rgszNames, cNames, lcid, rgDispId ); + + for ( unsigned int ind = 0; ind < cNames; ind++ ) + rgDispId[ind] = DISPID_UNKNOWN; + + return DISP_E_UNKNOWNNAME; +} + +COM_DECLSPEC_NOTHROW STDMETHODIMP EmbedDocument_Impl::Invoke( DISPID dispIdMember, + REFIID riid, + LCID lcid, + WORD wFlags, + DISPPARAMS* pDispParams, + VARIANT* pVarResult, + EXCEPINFO* pExcepInfo, + unsigned int* puArgErr ) +{ + if ( m_pDocHolder->GetIDispatch() ) + return m_pDocHolder->GetIDispatch()->Invoke( dispIdMember, + riid, + lcid, + wFlags, + pDispParams, + pVarResult, + pExcepInfo, + puArgErr ); + + return DISP_E_MEMBERNOTFOUND; +} + + +// IExternalConnection + +DWORD STDMETHODCALLTYPE EmbedDocument_Impl::AddConnection( DWORD , DWORD ) +{ + return AddRef(); +} + +DWORD STDMETHODCALLTYPE EmbedDocument_Impl::ReleaseConnection( DWORD , DWORD , BOOL ) +{ + return Release(); +} + +// C++ - methods + +HRESULT EmbedDocument_Impl::SaveObject() +{ + HRESULT hr = S_OK; + + if(m_pClientSite) { + hr = m_pClientSite->SaveObject(); + + for (auto const& advise : m_aAdviseHashMap) + if (advise.second) + advise.second->OnSave(); + } + else if ( m_aFileName.getLength() && IsDirty() == S_OK ) + { + OUString aPreservFileName = m_aFileName; + + // in case of links the containers does not provide client site sometimes + hr = Save( static_cast<LPCOLESTR>(nullptr), FALSE ); // triggers saving to the link location + SaveCompleted(o3tl::toW(aPreservFileName.getStr())); + } + + notify( false ); + + return hr; +} + + +HRESULT EmbedDocument_Impl::ShowObject() +{ + HRESULT hr = S_OK; + + if(m_pClientSite) + hr = m_pClientSite->ShowObject(); + + return hr; +} + + +void EmbedDocument_Impl::notify( bool bDataChanged ) +{ + for (auto const& advise : m_aAdviseHashMap) + if (advise.second) + advise.second->OnViewChange( DVASPECT_CONTENT, -1 ); + + if ( m_pDAdviseHolder && bDataChanged ) + m_pDAdviseHolder->SendOnDataChange( static_cast<IDataObject*>(this), 0, 0 ); +} + +void EmbedDocument_Impl::Deactivate() +{ + if ( m_pDocHolder->HasFrame() ) + { + SaveObject(); + m_pDocHolder->CloseFrame(); + OLENotifyDeactivation(); + } +} + +HRESULT EmbedDocument_Impl::OLENotifyDeactivation() +{ + HRESULT hr = S_OK; + + if ( m_pClientSite ) + hr = m_pClientSite->OnShowWindow( FALSE ); + + return hr; + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/embedserv/source/embed/ed_ipersiststr.cxx b/embedserv/source/embed/ed_ipersiststr.cxx new file mode 100644 index 000000000..698bbe13f --- /dev/null +++ b/embedserv/source/embed/ed_ipersiststr.cxx @@ -0,0 +1,967 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <sal/config.h> + +#include <string_view> + +#include <embeddoc.hxx> +#include <com/sun/star/uno/Exception.hpp> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include <com/sun/star/lang/XComponent.hpp> +#include <com/sun/star/io/TempFile.hpp> +#include <com/sun/star/io/XInputStream.hpp> +#include <com/sun/star/io/XOutputStream.hpp> +#include <com/sun/star/io/XSeekable.hpp> +#include <com/sun/star/frame/XModel.hpp> +#include <com/sun/star/frame/XLoadable.hpp> +#include <com/sun/star/util/XModifiable.hpp> +#include <com/sun/star/frame/XStorable.hpp> +#include <com/sun/star/util/URLTransformer.hpp> +#include <com/sun/star/util/XURLTransformer.hpp> + +#include <comphelper/processfactory.hxx> +#include <o3tl/char16_t2wchar_t.hxx> +#include <osl/mutex.hxx> +#include <osl/diagnose.h> +#include <sal/types.h> + +#include "guid.hxx" + +#include <string.h> + +#define EXT_STREAM_LENGTH 4 + +namespace { + +const sal_Int32 nConstBufferSize = 32000; + +} + +using namespace ::com::sun::star; + +const wchar_t aOfficeEmbedStreamName[] = L"package_stream"; +const wchar_t aExtentStreamName[] = L"properties_stream"; + +static uno::Reference< io::XInputStream > createTempXInStreamFromIStream( + uno::Reference< lang::XMultiServiceFactory > const & xFactory, + IStream *pStream ) +{ + uno::Reference< io::XInputStream > xResult; + + if ( !pStream ) + return xResult; + + uno::Reference < io::XOutputStream > xTempOut( io::TempFile::create(comphelper::getComponentContext(xFactory)), + uno::UNO_QUERY_THROW ); + ULARGE_INTEGER nNewPos; + LARGE_INTEGER const aZero = { 0, 0 }; + HRESULT hr = pStream->Seek( aZero, STREAM_SEEK_SET, &nNewPos ); + if ( FAILED( hr ) ) return xResult; + + STATSTG aStat; + hr = pStream->Stat( &aStat, STATFLAG_NONAME ); + if ( FAILED( hr ) ) return xResult; + + sal_uInt32 nSize = static_cast<sal_uInt32>(aStat.cbSize.QuadPart); + sal_uInt32 nCopied = 0; + uno::Sequence< sal_Int8 > aBuffer( nConstBufferSize ); + try + { + sal_uInt32 nRead = 0; + do + { + pStream->Read( aBuffer.getArray(), nConstBufferSize, &nRead ); + + if ( nRead < nConstBufferSize ) + aBuffer.realloc( nRead ); + + xTempOut->writeBytes( aBuffer ); + nCopied += nRead; + } while( nRead == nConstBufferSize ); + + if ( nCopied == nSize ) + { + uno::Reference < io::XSeekable > xTempSeek ( xTempOut, uno::UNO_QUERY ); + if ( xTempSeek.is() ) + { + xTempSeek->seek ( 0 ); + xResult.set( xTempOut, uno::UNO_QUERY ); + } + } + } + catch( const uno::Exception& ) + { + } + + return xResult; +} + +static HRESULT copyXTempOutToIStream( uno::Reference< io::XOutputStream > const & xTempOut, IStream* pStream ) +{ + if ( !xTempOut.is() || !pStream ) + return E_FAIL; + + uno::Reference < io::XSeekable > xTempSeek ( xTempOut, uno::UNO_QUERY ); + if ( !xTempSeek.is() ) + return E_FAIL; + + xTempSeek->seek ( 0 ); + + uno::Reference< io::XInputStream > xTempIn ( xTempOut, uno::UNO_QUERY ); + if ( !xTempSeek.is() ) + return E_FAIL; + + // Seek to zero and truncate the stream + ULARGE_INTEGER nNewPos; + LARGE_INTEGER const aZero = { 0, 0 }; + HRESULT hr = pStream->Seek( aZero, STREAM_SEEK_SET, &nNewPos ); + if ( FAILED( hr ) ) return E_FAIL; + ULARGE_INTEGER const aUZero = { 0, 0 }; + hr = pStream->SetSize( aUZero ); + if ( FAILED( hr ) ) return E_FAIL; + + uno::Sequence< sal_Int8 > aBuffer( nConstBufferSize ); + sal_uInt32 nReadBytes = 0; + + do + { + try { + nReadBytes = xTempIn->readBytes( aBuffer, nConstBufferSize ); + } + catch( const uno::Exception& ) + { + return E_FAIL; + } + + sal_uInt32 nWritten = 0; + hr = pStream->Write( aBuffer.getArray(), nReadBytes, &nWritten ); + if ( !SUCCEEDED( hr ) || nWritten != nReadBytes ) + return E_FAIL; + + } while( nReadBytes == nConstBufferSize ); + + return S_OK; +} + + +// EmbedDocument_Impl + + +EmbedDocument_Impl::EmbedDocument_Impl( const uno::Reference< lang::XMultiServiceFactory >& xFactory, const GUID* guid ) +: m_refCount( 0 ) +, m_xFactory( xFactory ) +, m_guid( *guid ) +, m_bIsDirty( false ) +, m_nAdviseNum( 0 ) +, m_bIsInVerbHandling( false ) +//, m_bLoadedFromFile( sal_False ) +{ + m_xOwnAccess = new EmbeddedDocumentInstanceAccess_Impl( this ); + m_pDocHolder = new DocumentHolder( xFactory, m_xOwnAccess ); +} + +EmbedDocument_Impl::~EmbedDocument_Impl() +{ + m_pDocHolder->FreeOffice(); + + if ( m_pDocHolder->HasFrame() && m_pDocHolder->IsLink() ) + { + // a link with frame should be only disconnected, not closed + m_pDocHolder->DisconnectFrameDocument( true ); + } + else + { + m_pDocHolder->CloseDocument(); + m_pDocHolder->CloseFrame(); + } +} + +uno::Sequence< beans::PropertyValue > EmbedDocument_Impl::fillArgsForLoading_Impl( uno::Reference< io::XInputStream > const & xStream, DWORD /*nStreamMode*/, LPCOLESTR pFilePath ) +{ + uno::Sequence< beans::PropertyValue > aArgs( xStream.is() ? 3 : 2 ); + auto pArgs = aArgs.getArray(); + pArgs[0].Name = "FilterName"; + pArgs[0].Value <<= getFilterNameFromGUID_Impl( &m_guid ); + + if ( xStream.is() ) + { + pArgs[1].Name = "InputStream"; + pArgs[1].Value <<= xStream; + pArgs[2].Name = "URL"; + pArgs[2].Value <<= OUString( "private:stream" ); + } + else + { + pArgs[1].Name = "URL"; + + OUString sDocUrl; + if ( pFilePath ) + { + uno::Reference< util::XURLTransformer > aTransformer( util::URLTransformer::create(comphelper::getComponentContext(m_xFactory)) ); + util::URL aURL; + + aURL.Complete = o3tl::toU(pFilePath); + + if ( aTransformer->parseSmart( aURL, OUString() ) ) + sDocUrl = aURL.Complete; + } + + pArgs[1].Value <<= sDocUrl; + } + + // aArgs[].Name = "ReadOnly"; + // aArgs[].Value <<= sal_False; //( ( nStreamMode & ( STGM_READWRITE | STGM_WRITE ) ) ? sal_True : sal_False ); + + return aArgs; +} + +uno::Sequence< beans::PropertyValue > EmbedDocument_Impl::fillArgsForStoring_Impl( uno::Reference< io::XOutputStream > const & xStream) +{ + uno::Sequence< beans::PropertyValue > aArgs( xStream.is() ? 2 : 1 ); + auto pArgs = aArgs.getArray(); + pArgs[0].Name = "FilterName"; + pArgs[0].Value <<= getFilterNameFromGUID_Impl( &m_guid ); + + if ( xStream.is() ) + { + pArgs[1].Name = "OutputStream"; + pArgs[1].Value <<= xStream; + } + + return aArgs; +} + +HRESULT EmbedDocument_Impl::SaveTo_Impl( IStorage* pStg ) +{ + if ( !pStg || pStg == m_pMasterStorage ) + return E_FAIL; + + // for saveto operation the master storage + // should not enter NoScribble mode + CComPtr< IStream > pOrigOwn = m_pOwnStream; + CComPtr< IStream > pOrigExt = m_pExtStream; + HRESULT hr = Save( pStg, false ); + pStg->Commit( STGC_ONLYIFCURRENT ); + m_pOwnStream = pOrigOwn; + m_pExtStream = pOrigExt; + + return hr; +} + + +// IUnknown + +COM_DECLSPEC_NOTHROW STDMETHODIMP EmbedDocument_Impl::QueryInterface(REFIID riid, void** ppv) +{ + if(IsEqualIID(riid, IID_IUnknown)) + { + AddRef(); + *ppv = static_cast<IUnknown*>(static_cast<IPersistStorage*>(this)); + return S_OK; + } + else if (IsEqualIID(riid, IID_IPersist)) + { + AddRef(); + *ppv = static_cast<IPersist*>(static_cast<IPersistStorage*>(this)); + return S_OK; + } + else if (IsEqualIID(riid, IID_IExternalConnection)) + { + AddRef(); + *ppv = static_cast<IExternalConnection*>(this); + return S_OK; + } + else if (IsEqualIID(riid, IID_IPersistStorage)) + { + AddRef(); + *ppv = static_cast<IPersistStorage*>(this); + return S_OK; + } + else if (IsEqualIID(riid, IID_IDataObject)) + { + AddRef(); + *ppv = static_cast<IDataObject*>(this); + return S_OK; + } + else if (IsEqualIID(riid, IID_IOleObject)) + { + AddRef(); + *ppv = static_cast<IOleObject*>(this); + return S_OK; + } + else if (IsEqualIID(riid, IID_IOleWindow)) + { + AddRef(); + *ppv = static_cast<IOleWindow*>(this); + return S_OK; + } + else if (IsEqualIID(riid, IID_IOleInPlaceObject)) + { + AddRef(); + *ppv = static_cast<IOleInPlaceObject*>(this); + return S_OK; + } + else if (IsEqualIID(riid, IID_IPersistFile)) + { + AddRef(); + *ppv = static_cast<IPersistFile*>(this); + return S_OK; + } + else if (IsEqualIID(riid, IID_IDispatch)) + { + AddRef(); + *ppv = static_cast<IDispatch*>(this); + return S_OK; + } + + *ppv = nullptr; + return ResultFromScode(E_NOINTERFACE); +} + +COM_DECLSPEC_NOTHROW STDMETHODIMP_(ULONG) EmbedDocument_Impl::AddRef() +{ + return osl_atomic_increment( &m_refCount); +} + +COM_DECLSPEC_NOTHROW STDMETHODIMP_(ULONG) EmbedDocument_Impl::Release() +{ + // if there is a time when the last reference is destructed, that means that only internal pointers are alive + // after the following call either the refcount is increased or the pointers are empty + if ( m_refCount == 1 ) + m_xOwnAccess->ClearEmbedDocument(); + + sal_Int32 nCount = osl_atomic_decrement( &m_refCount ); + if ( nCount == 0 ) + delete this; + return nCount; +} + + +// IPersist + +COM_DECLSPEC_NOTHROW STDMETHODIMP EmbedDocument_Impl::GetClassID( CLSID* pClassId ) +{ + *pClassId = m_guid; + return S_OK; +} + + +// IPersistStorage + +COM_DECLSPEC_NOTHROW STDMETHODIMP EmbedDocument_Impl::IsDirty() +{ + // the link modified state is controlled by the document + if ( m_bIsDirty && !m_aFileName.getLength() ) + return S_OK; + + uno::Reference< util::XModifiable > xMod( m_pDocHolder->GetDocument(), uno::UNO_QUERY ); + if ( xMod.is() ) + return xMod->isModified() ? S_OK : S_FALSE; + return S_FALSE; +} + +COM_DECLSPEC_NOTHROW STDMETHODIMP EmbedDocument_Impl::InitNew( IStorage *pStg ) +{ + HRESULT hr = CO_E_ALREADYINITIALIZED; + + if ( !m_pDocHolder->GetDocument().is() ) + { + + STATSTG aStat; + hr = pStg->Stat( &aStat, STATFLAG_NONAME ); + if ( FAILED( hr ) ) return E_FAIL; + + DWORD nStreamMode = aStat.grfMode; + + hr = E_FAIL; + if ( m_xFactory.is() && pStg ) + { + uno::Reference< frame::XModel > aDocument( + m_xFactory->createInstance( OUString(getServiceNameFromGUID_Impl( &m_guid )) ), + uno::UNO_QUERY ); + if ( aDocument.is() ) + { + m_pDocHolder->SetDocument( aDocument ); + + uno::Reference< frame::XLoadable > xLoadable( m_pDocHolder->GetDocument(), uno::UNO_QUERY ); + if( xLoadable.is() ) + { + try + { + xLoadable->initNew(); + // xLoadable->load( fillArgsForLoading_Impl( uno::Reference< io::XInputStream >(), nStreamMode ) ); + hr = S_OK; + } + catch( const uno::Exception& ) + { + } + } + + if ( hr == S_OK ) + { + wchar_t const * aCurType = getStorageTypeFromGUID_Impl( &m_guid ); // ??? + CLIPFORMAT cf = static_cast<CLIPFORMAT>(RegisterClipboardFormatW( L"Embedded Object" )); + hr = WriteFmtUserTypeStg( pStg, + cf, // ??? + const_cast<wchar_t *>(aCurType) ); + + if ( hr == S_OK ) + { + hr = pStg->CreateStream( aOfficeEmbedStreamName, + STGM_CREATE | ( nStreamMode & 0x73 ), + 0, + 0, + &m_pOwnStream ); + + if ( hr == S_OK && m_pOwnStream ) + { + hr = pStg->CreateStream( aExtentStreamName, + STGM_CREATE | ( nStreamMode & 0x73 ), + 0, + 0, + &m_pExtStream ); + + if ( hr == S_OK && m_pExtStream ) + { + + m_pMasterStorage = pStg; + m_bIsDirty = true; + } + else + hr = E_FAIL; + } + else + hr = E_FAIL; + } + else + hr = E_FAIL; + } + + if ( hr != S_OK ) + m_pDocHolder->CloseDocument(); + } + } + } + + return hr; +} + +COM_DECLSPEC_NOTHROW STDMETHODIMP EmbedDocument_Impl::Load( IStorage *pStg ) +{ + if ( m_pDocHolder->GetDocument().is() ) + return CO_E_ALREADYINITIALIZED; + + if ( !m_xFactory.is() || !pStg ) + return E_FAIL; + + HRESULT hr = E_FAIL; + + STATSTG aStat; + hr = pStg->Stat( &aStat, STATFLAG_NONAME ); + if ( FAILED( hr ) ) return E_FAIL; + + DWORD nStreamMode = aStat.grfMode; + hr = pStg->OpenStream( aOfficeEmbedStreamName, + nullptr, + nStreamMode & 0x73, + 0, + &m_pOwnStream ); + if ( !m_pOwnStream ) hr = E_FAIL; + + if ( SUCCEEDED( hr ) ) + { + hr = pStg->OpenStream( aExtentStreamName, + nullptr, + nStreamMode & 0x73, + 0, + &m_pExtStream ); + if ( !m_pExtStream ) hr = E_FAIL; + } + + // RECTL aRectToSet; + SIZEL aSizeToSet; + if ( SUCCEEDED( hr ) ) + { + ULARGE_INTEGER nNewPos; + LARGE_INTEGER const aZero = { 0, 0 }; + hr = m_pExtStream->Seek( aZero, STREAM_SEEK_SET, &nNewPos ); + if ( SUCCEEDED( hr ) ) + { + sal_uInt32 nRead; + sal_Int32 aInf[EXT_STREAM_LENGTH]; + hr = m_pExtStream->Read( aInf, sizeof aInf, &nRead ); + if ( nRead != sizeof aInf ) hr = E_FAIL; + + if ( SUCCEEDED( hr ) ) + { + // aRectToSet.left = *((sal_Int32*)aInf); + // aRectToSet.top = *((sal_Int32*)&aInf[4]); + // aRectToSet.right = *((sal_Int32*)&aInf[8]); + // aRectToSet.bottom = *((sal_Int32*)&aInf[12]); + aSizeToSet.cx = aInf[2] - aInf[0]; + aSizeToSet.cy = aInf[3] - aInf[1]; + } + } + } + + if ( SUCCEEDED( hr ) ) + { + hr = E_FAIL; + + uno::Reference < io::XInputStream > xTempIn = createTempXInStreamFromIStream( m_xFactory, m_pOwnStream ); + if ( xTempIn.is() ) + { + uno::Reference< frame::XModel > aDocument( + m_xFactory->createInstance( OUString(getServiceNameFromGUID_Impl( &m_guid )) ), + uno::UNO_QUERY ); + if ( aDocument.is() ) + { + m_pDocHolder->SetDocument( aDocument ); + + uno::Reference< frame::XLoadable > xLoadable( m_pDocHolder->GetDocument(), uno::UNO_QUERY ); + if( xLoadable.is() ) + { + try + { + xLoadable->load( fillArgsForLoading_Impl( xTempIn, nStreamMode ) ); + m_pMasterStorage = pStg; + hr = m_pDocHolder->SetExtent( &aSizeToSet ); + // hr = m_pDocHolder->SetVisArea( &aRectToSet ); + } + catch( const uno::Exception& ) + { + } + } + + if ( FAILED( hr ) ) + m_pDocHolder->CloseDocument(); + } + } + } + + if ( FAILED( hr ) ) + { + m_pOwnStream = CComPtr< IStream >(); + m_pExtStream = CComPtr< IStream >(); + hr = pStg->DestroyElement( aOfficeEmbedStreamName ); + hr = pStg->DestroyElement( aExtentStreamName ); + + OSL_ENSURE( SUCCEEDED( hr ), "Can not destroy created stream!" ); + if ( FAILED( hr ) ) + hr = E_FAIL; + } + + return hr; +} + +COM_DECLSPEC_NOTHROW STDMETHODIMP EmbedDocument_Impl::Save( IStorage *pStgSave, BOOL fSameAsLoad ) +{ + if ( !m_pDocHolder->GetDocument().is() || !m_xFactory.is() || !pStgSave || !m_pOwnStream || !m_pExtStream ) + return E_FAIL; + + CComPtr< IStream > pTargetStream; + CComPtr< IStream > pNewExtStream; + + if ( !fSameAsLoad && pStgSave != m_pMasterStorage ) + { + OSL_ENSURE( m_pMasterStorage, "How could the document be initialized without storage!??" ); + HRESULT hr = m_pMasterStorage->CopyTo( 0, nullptr, nullptr, pStgSave ); + if ( FAILED( hr ) ) return E_FAIL; + + STATSTG aStat; + hr = pStgSave->Stat( &aStat, STATFLAG_NONAME ); + if ( FAILED( hr ) ) return E_FAIL; + + DWORD nStreamMode = aStat.grfMode; + hr = pStgSave->CreateStream( aOfficeEmbedStreamName, + STGM_CREATE | ( nStreamMode & 0x73 ), + 0, + 0, + &pTargetStream ); + if ( FAILED( hr ) || !pTargetStream ) return E_FAIL; + + hr = pStgSave->CreateStream( aExtentStreamName, + STGM_CREATE | ( nStreamMode & 0x73 ), + 0, + 0, + &pNewExtStream ); + if ( FAILED( hr ) || !pNewExtStream ) return E_FAIL; + } + else + { + pTargetStream = m_pOwnStream; + pNewExtStream = m_pExtStream; + } + + HRESULT hr = E_FAIL; + + uno::Reference < io::XOutputStream > xTempOut( io::TempFile::create(comphelper::getComponentContext(m_xFactory)), + uno::UNO_QUERY_THROW ); + + uno::Reference< frame::XStorable > xStorable( m_pDocHolder->GetDocument(), uno::UNO_QUERY ); + if( xStorable.is() ) + { + try + { + xStorable->storeToURL( "private:stream", + fillArgsForStoring_Impl( xTempOut ) ); + hr = copyXTempOutToIStream( xTempOut, pTargetStream ); + if ( SUCCEEDED( hr ) ) + { + // no need to truncate the stream, the size of the stream is always the same + ULARGE_INTEGER nNewPos; + LARGE_INTEGER const aZero = { 0, 0 }; + hr = pNewExtStream->Seek( aZero, STREAM_SEEK_SET, &nNewPos ); + if ( SUCCEEDED( hr ) ) + { + SIZEL aSize; + hr = m_pDocHolder->GetExtent( &aSize ); + + if ( SUCCEEDED( hr ) ) + { + sal_uInt32 nWritten; + sal_Int32 aInf[EXT_STREAM_LENGTH] = {0, 0, aSize.cx, aSize.cy}; + + hr = pNewExtStream->Write( aInf, sizeof aInf, &nWritten ); + if ( nWritten != sizeof aInf ) hr = E_FAIL; + + if ( SUCCEEDED( hr ) ) + { + m_pOwnStream = CComPtr< IStream >(); + m_pExtStream = CComPtr< IStream >(); + if ( fSameAsLoad || pStgSave == m_pMasterStorage ) + { + uno::Reference< util::XModifiable > xMod( m_pDocHolder->GetDocument(), uno::UNO_QUERY ); + if ( xMod.is() ) + xMod->setModified( false ); + m_bIsDirty = false; + } + } + } + } + } + } + catch( const uno::Exception& ) + { + } + } + + return hr; +} + +COM_DECLSPEC_NOTHROW STDMETHODIMP EmbedDocument_Impl::SaveCompleted( IStorage *pStgNew ) +{ + // m_pOwnStream == NULL && m_pMasterStorage != NULL means the object is in NoScribble mode + // m_pOwnStream == NULL && m_pMasterStorage == NULL means the object is in HandsOff mode + + if ( m_pOwnStream || m_pExtStream ) + return E_UNEXPECTED; + + if ( !m_pMasterStorage && !pStgNew ) + return E_INVALIDARG; + + if ( pStgNew ) + m_pMasterStorage = pStgNew; + + STATSTG aStat; + HRESULT hr = m_pMasterStorage->Stat( &aStat, STATFLAG_NONAME ); + if ( FAILED( hr ) ) return E_OUTOFMEMORY; + + DWORD nStreamMode = aStat.grfMode; + hr = m_pMasterStorage->OpenStream( aOfficeEmbedStreamName, + nullptr, + nStreamMode & 0x73, + 0, + &m_pOwnStream ); + if ( FAILED( hr ) || !m_pOwnStream ) return E_OUTOFMEMORY; + + hr = m_pMasterStorage->OpenStream( aExtentStreamName, + nullptr, + nStreamMode & 0x73, + 0, + &m_pExtStream ); + if ( FAILED( hr ) || !m_pExtStream ) return E_OUTOFMEMORY; + + for (auto const& advise : m_aAdviseHashMap) + { + if ( advise.second ) + advise.second->OnSave(); + } + + return S_OK; +} + +COM_DECLSPEC_NOTHROW STDMETHODIMP EmbedDocument_Impl::HandsOffStorage() +{ + m_pMasterStorage = CComPtr< IStorage >(); + m_pOwnStream = CComPtr< IStream >(); + m_pExtStream = CComPtr< IStream >(); + + return S_OK; +} + + +// IPersistFile + +COM_DECLSPEC_NOTHROW STDMETHODIMP EmbedDocument_Impl::Load( LPCOLESTR pszFileName, DWORD /*dwMode*/ ) +{ + if ( m_pDocHolder->GetDocument().is() ) + return CO_E_ALREADYINITIALIZED; + + if ( !m_xFactory.is() ) + return E_FAIL; + + DWORD nStreamMode = STGM_CREATE | STGM_READWRITE | STGM_DELETEONRELEASE | STGM_SHARE_EXCLUSIVE; + HRESULT hr = StgCreateDocfile( nullptr, + nStreamMode , + 0, + &m_pMasterStorage ); + + if ( FAILED( hr ) || !m_pMasterStorage ) return E_FAIL; + + std::u16string_view aCurType = getServiceNameFromGUID_Impl( &m_guid ); // ??? + CLIPFORMAT cf = static_cast<CLIPFORMAT>(RegisterClipboardFormatW( L"Embedded Object" )); + hr = WriteFmtUserTypeStg( m_pMasterStorage, + cf, // ??? + const_cast<LPOLESTR>( o3tl::toW(aCurType.data())) ); + if ( FAILED( hr ) ) return E_FAIL; + + hr = m_pMasterStorage->SetClass( m_guid ); + if ( FAILED( hr ) ) return E_FAIL; + + hr = m_pMasterStorage->CreateStream( aOfficeEmbedStreamName, + STGM_CREATE | ( nStreamMode & 0x73 ), + 0, + 0, + &m_pOwnStream ); + if ( FAILED( hr ) || !m_pOwnStream ) return E_FAIL; + + hr = m_pMasterStorage->CreateStream( aExtentStreamName, + STGM_CREATE | ( nStreamMode & 0x73 ), + 0, + 0, + &m_pExtStream ); + if ( FAILED( hr ) || !m_pExtStream ) return E_FAIL; + + + uno::Reference< frame::XModel > aDocument( + m_xFactory->createInstance( OUString(getServiceNameFromGUID_Impl( &m_guid )) ), + uno::UNO_QUERY ); + if ( aDocument.is() ) + { + m_pDocHolder->SetDocument( aDocument, true ); + + uno::Reference< frame::XLoadable > xLoadable( m_pDocHolder->GetDocument(), uno::UNO_QUERY ); + if( xLoadable.is() ) + { + try + { + xLoadable->load( fillArgsForLoading_Impl( uno::Reference< io::XInputStream >(), + STGM_READWRITE, + pszFileName ) ); + hr = S_OK; + + m_aFileName = o3tl::toU(pszFileName); + } + catch( const uno::Exception& ) + { + } + } + + if ( hr == S_OK ) + { + aCurType = getServiceNameFromGUID_Impl( &m_guid ); // ??? + cf = static_cast<CLIPFORMAT>(RegisterClipboardFormatW( L"Embedded Object" )); + hr = WriteFmtUserTypeStg( m_pMasterStorage, + cf, // ??? + const_cast<LPOLESTR>( o3tl::toW(aCurType.data())) ); + + if ( SUCCEEDED( hr ) ) + { + // no need to truncate the stream, the size of the stream is always the same + ULARGE_INTEGER nNewPos; + LARGE_INTEGER const aZero = { 0, 0 }; + hr = m_pExtStream->Seek( aZero, STREAM_SEEK_SET, &nNewPos ); + if ( SUCCEEDED( hr ) ) + { + SIZEL aSize; + hr = m_pDocHolder->GetExtent( &aSize ); + + if ( SUCCEEDED( hr ) ) + { + sal_uInt32 nWritten; + sal_Int32 aInf[EXT_STREAM_LENGTH] = {0, 0, aSize.cx, aSize.cy}; + + hr = m_pExtStream->Write( aInf, sizeof aInf, &nWritten ); + if ( nWritten != sizeof aInf ) hr = E_FAIL; + } + } + } + + if ( SUCCEEDED( hr ) ) + m_bIsDirty = true; + else + hr = E_FAIL; + } + + if ( FAILED( hr ) ) + { + m_pDocHolder->CloseDocument(); + m_pOwnStream = nullptr; + m_pExtStream = nullptr; + m_pMasterStorage = nullptr; + } + } + + return hr; +} + +COM_DECLSPEC_NOTHROW STDMETHODIMP EmbedDocument_Impl::Save( LPCOLESTR pszFileName, BOOL fRemember ) +{ + if ( !m_pDocHolder->GetDocument().is() || !m_xFactory.is() ) + return E_FAIL; + + HRESULT hr = E_FAIL; + + // TODO/LATER: currently there is no hands off state implemented + try + { + uno::Reference< frame::XStorable > xStorable( m_pDocHolder->GetDocument(), uno::UNO_QUERY_THROW ); + + if ( !pszFileName ) + xStorable->store(); + else + { + util::URL aURL; + aURL.Complete = o3tl::toU( pszFileName ); + + uno::Reference< util::XURLTransformer > aTransformer( util::URLTransformer::create(comphelper::getComponentContext(m_xFactory)) ); + + if ( aTransformer->parseSmart( aURL, OUString() ) && aURL.Complete.getLength() ) + { + if ( fRemember ) + { + xStorable->storeAsURL( aURL.Complete, fillArgsForStoring_Impl( uno::Reference< io::XOutputStream >() ) ); + m_aFileName = aURL.Complete; + } + else + xStorable->storeToURL( aURL.Complete, fillArgsForStoring_Impl( uno::Reference< io::XOutputStream >() ) ); + } + } + + hr = S_OK; + } + catch( const uno::Exception& ) + { + } + + return hr; +} + +COM_DECLSPEC_NOTHROW STDMETHODIMP EmbedDocument_Impl::SaveCompleted( LPCOLESTR pszFileName ) +{ + // the different file name would mean error here + m_aFileName = o3tl::toU(pszFileName); + return S_OK; +} + +COM_DECLSPEC_NOTHROW STDMETHODIMP EmbedDocument_Impl::GetCurFile( LPOLESTR *ppszFileName ) +{ + CComPtr<IMalloc> pMalloc; + + HRESULT hr = CoGetMalloc( 1, &pMalloc ); + if ( FAILED( hr ) || !pMalloc ) return E_FAIL; + + *ppszFileName = static_cast<LPOLESTR>( pMalloc->Alloc( sizeof( sal_Unicode ) * ( m_aFileName.getLength() + 1 ) ) ); + wcsncpy( *ppszFileName, o3tl::toW(m_aFileName.getStr()), m_aFileName.getLength() + 1 ); + + return m_aFileName.getLength() ? S_OK : S_FALSE; +} + + +LockedEmbedDocument_Impl EmbeddedDocumentInstanceAccess_Impl::GetEmbedDocument() +{ + ::osl::MutexGuard aGuard( m_aMutex ); + return LockedEmbedDocument_Impl( m_pEmbedDocument ); +} + +void EmbeddedDocumentInstanceAccess_Impl::ClearEmbedDocument() +{ + ::osl::MutexGuard aGuard( m_aMutex ); + m_pEmbedDocument = nullptr; +} + + +LockedEmbedDocument_Impl::LockedEmbedDocument_Impl() +: m_pEmbedDocument( nullptr ) +{} + +LockedEmbedDocument_Impl::LockedEmbedDocument_Impl( EmbedDocument_Impl* pEmbedDocument ) +: m_pEmbedDocument( pEmbedDocument ) +{ + if ( m_pEmbedDocument ) + m_pEmbedDocument->AddRef(); +} + +LockedEmbedDocument_Impl::LockedEmbedDocument_Impl( const LockedEmbedDocument_Impl& aDocLock ) +: m_pEmbedDocument( aDocLock.m_pEmbedDocument ) +{ + if ( m_pEmbedDocument ) + m_pEmbedDocument->AddRef(); +} + +LockedEmbedDocument_Impl& LockedEmbedDocument_Impl::operator=( const LockedEmbedDocument_Impl& aDocLock ) +{ + if ( m_pEmbedDocument ) + m_pEmbedDocument->Release(); + + m_pEmbedDocument = aDocLock.m_pEmbedDocument; + if ( m_pEmbedDocument ) + m_pEmbedDocument->AddRef(); + + return *this; +} + +LockedEmbedDocument_Impl::~LockedEmbedDocument_Impl() +{ + if ( m_pEmbedDocument ) + m_pEmbedDocument->Release(); +} + +void LockedEmbedDocument_Impl::ExecuteMethod( sal_Int16 nId ) +{ + if ( m_pEmbedDocument ) + { + if ( nId == OLESERV_SAVEOBJECT ) + m_pEmbedDocument->SaveObject(); + else if ( nId == OLESERV_CLOSE ) + m_pEmbedDocument->Close( 0 ); + else if ( nId == OLESERV_NOTIFY ) + m_pEmbedDocument->notify(); + else if ( nId == OLESERV_NOTIFYCLOSING ) + m_pEmbedDocument->OLENotifyClosing(); + else if ( nId == OLESERV_SHOWOBJECT ) + m_pEmbedDocument->ShowObject(); + else if ( nId == OLESERV_DEACTIVATE ) + m_pEmbedDocument->Deactivate(); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/embedserv/source/embed/esdll.cxx b/embedserv/source/embed/esdll.cxx new file mode 100644 index 000000000..59a749078 --- /dev/null +++ b/embedserv/source/embed/esdll.cxx @@ -0,0 +1,63 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#define _WIN32_DCOM + +#include <stdafx.h> + +#include <atlbase.h> +CComModule _Module; +#include <atlcom.h> + +BEGIN_OBJECT_MAP(ObjectMap) +#if defined __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wmissing-field-initializers" +#endif +END_OBJECT_MAP() +#if defined __clang__ +#pragma clang diagnostic pop +#endif + +// DLL Entry Point + +#include <syswinwrapper.hxx> +#include <docholder.hxx> + +HINSTANCE DocumentHolder::m_hInstance; + +extern "C" BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID /*lpReserved*/) +{ + DocumentHolder::m_hInstance = hInstance; + if (!winwrap::HatchWindowRegister(hInstance)) + return FALSE; + + if (dwReason == DLL_PROCESS_ATTACH) + { + _Module.Init(ObjectMap, hInstance); + DisableThreadLibraryCalls(hInstance); + } + else if (dwReason == DLL_PROCESS_DETACH) + { + _Module.Term(); + } + return TRUE; // ok +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/embedserv/source/embed/guid.cxx b/embedserv/source/embed/guid.cxx new file mode 100644 index 000000000..5b284a8a9 --- /dev/null +++ b/embedserv/source/embed/guid.cxx @@ -0,0 +1,131 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <common.h> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include <com/sun/star/container/XNameAccess.hpp> + +#include "guid.hxx" + +wchar_t const * getStorageTypeFromGUID_Impl( GUID const * guid ) +{ + if ( *guid == OID_WriterTextServer ) + return L"soffice.StarWriterDocument.6"; + + if ( *guid == OID_WriterOASISTextServer ) + return L"LibreOffice.WriterDocument.1"; + + if ( *guid == OID_CalcServer ) + return L"soffice.StarCalcDocument.6"; + + if ( *guid == OID_CalcOASISServer ) + return L"LibreOffice.CalcDocument.1"; + + if ( *guid == OID_DrawingServer ) + return L"soffice.StarDrawDocument.6"; + + if ( *guid == OID_DrawingOASISServer ) + return L"LibreOffice.DrawDocument.1"; + + if ( *guid == OID_PresentationServer ) + return L"soffice.StarImpressDocument.6"; + + if ( *guid == OID_PresentationOASISServer ) + return L"LibreOffice.ImpressDocument.1"; + + if ( *guid == OID_MathServer ) + return L"soffice.StarMathDocument.6"; + + if ( *guid == OID_MathOASISServer ) + return L"LibreOffice.MathDocument.1"; + + return L""; +} + +std::u16string_view getServiceNameFromGUID_Impl( GUID const * guid ) +{ + if ( *guid == OID_WriterTextServer ) + return u"com.sun.star.comp.Writer.TextDocument"; + + if ( *guid == OID_WriterOASISTextServer ) + return u"com.sun.star.comp.Writer.TextDocument"; + + if ( *guid == OID_CalcServer ) + return u"com.sun.star.comp.Calc.SpreadsheetDocument"; + + if ( *guid == OID_CalcOASISServer ) + return u"com.sun.star.comp.Calc.SpreadsheetDocument"; + + if ( *guid == OID_DrawingServer ) + return u"com.sun.star.comp.Draw.DrawingDocument"; + + if ( *guid == OID_DrawingOASISServer ) + return u"com.sun.star.comp.Draw.DrawingDocument"; + + if ( *guid == OID_PresentationServer ) + return u"com.sun.star.comp.Draw.PresentationDocument"; + + if ( *guid == OID_PresentationOASISServer ) + return u"com.sun.star.comp.Draw.PresentationDocument"; + + if ( *guid == OID_MathServer ) + return u"com.sun.star.comp.Math.FormulaDocument"; + + if ( *guid == OID_MathOASISServer ) + return u"com.sun.star.comp.Math.FormulaDocument"; + + return u""; +} + +OUString getFilterNameFromGUID_Impl( GUID const * guid ) +{ + if ( *guid == OID_WriterTextServer ) + return "StarOffice XML (Writer)"; + + if ( *guid == OID_WriterOASISTextServer ) + return "writer8"; + + if ( *guid == OID_CalcServer ) + return "StarOffice XML (Calc)"; + + if ( *guid == OID_CalcOASISServer ) + return "calc8"; + + if ( *guid == OID_DrawingServer ) + return "StarOffice XML (Draw)"; + + if ( *guid == OID_DrawingOASISServer ) + return "draw8"; + + if ( *guid == OID_PresentationServer ) + return "StarOffice XML (Impress)"; + + if ( *guid == OID_PresentationOASISServer ) + return "impress8"; + + if ( *guid == OID_MathServer ) + return "StarOffice XML (Math)"; + + if ( *guid == OID_MathOASISServer ) + return "math8"; + + return OUString(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/embedserv/source/embed/guid.hxx b/embedserv/source/embed/guid.hxx new file mode 100644 index 000000000..2cbf28b5b --- /dev/null +++ b/embedserv/source/embed/guid.hxx @@ -0,0 +1,39 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#ifndef INCLUDED_EMBEDSERV_SOURCE_EMBED_GUID_HXX +#define INCLUDED_EMBEDSERV_SOURCE_EMBED_GUID_HXX + +#include <sal/config.h> + +#include <string_view> + +#include <rtl/ustring.hxx> + +#include <common.h> + +OUString getFilterNameFromGUID_Impl(GUID const*); + +std::u16string_view getServiceNameFromGUID_Impl(GUID const*); + +wchar_t const* getStorageTypeFromGUID_Impl(GUID const* guid); + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/embedserv/source/embed/iipaobj.cxx b/embedserv/source/embed/iipaobj.cxx new file mode 100644 index 000000000..008a33f11 --- /dev/null +++ b/embedserv/source/embed/iipaobj.cxx @@ -0,0 +1,116 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <iipaobj.hxx> +#include <embeddoc.hxx> + + +CIIAObj::CIIAObj(DocumentHolder* pDocHolder) + : m_refCount( 0 ), + m_rDocHolder( pDocHolder ) +{ +} + + +CIIAObj::~CIIAObj() +{ + return; +} + +/* IUnknown methods */ + +STDMETHODIMP CIIAObj::QueryInterface(REFIID riid, LPVOID* ppv) +{ + *ppv=nullptr; + + if(IID_IUnknown==riid || + IID_IOleWindow==riid || + IID_IOleInPlaceActiveObject==riid) + *ppv=this; + + //AddRef any interface we'll return. + if (nullptr!=*ppv) + { + static_cast<LPUNKNOWN>(*ppv)->AddRef(); + return NOERROR; + } + + return ResultFromScode(E_NOINTERFACE); +} + + +STDMETHODIMP_(ULONG) CIIAObj::AddRef() +{ + return osl_atomic_increment( &m_refCount); +} + +STDMETHODIMP_(ULONG) CIIAObj::Release() +{ + sal_Int32 nCount = osl_atomic_decrement( &m_refCount); + if ( nCount == 0 ) + delete this; + + return nCount; +} + +/* IOleInPlaceActiveObject methods*/ + +STDMETHODIMP CIIAObj::GetWindow(HWND *) +{ + return NOERROR; +} + +STDMETHODIMP CIIAObj::ContextSensitiveHelp(BOOL) +{ + return NOERROR; +} + +STDMETHODIMP CIIAObj::TranslateAccelerator(LPMSG) +{ + return NOERROR; +} + +STDMETHODIMP CIIAObj::OnFrameWindowActivate(BOOL) +{ + return NOERROR; +} + +STDMETHODIMP CIIAObj::OnDocWindowActivate(BOOL) +{ + return NOERROR; +} + +STDMETHODIMP CIIAObj::ResizeBorder( + LPCRECT pRect,LPOLEINPLACEUIWINDOW,BOOL bFrame) +{ + if(!bFrame) return NOERROR; + + if ( !m_rDocHolder.is() ) + return E_FAIL; + + return m_rDocHolder->SetContRects(pRect); +} + + +STDMETHODIMP CIIAObj::EnableModeless(BOOL) +{ + return NOERROR; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/embedserv/source/embed/intercept.cxx b/embedserv/source/embed/intercept.cxx new file mode 100644 index 000000000..96ca91575 --- /dev/null +++ b/embedserv/source/embed/intercept.cxx @@ -0,0 +1,470 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ +#include <cppuhelper/weak.hxx> + +#include <embeddoc.hxx> +#include <docholder.hxx> +#include <intercept.hxx> + +using namespace ::com::sun::star; + + +#define IUL 6 + +constexpr OUStringLiteral IU0 = u".uno:Save"; +constexpr OUStringLiteral IU1 = u".uno:SaveAll"; +constexpr OUStringLiteral IU2 = u".uno:CloseDoc"; +constexpr OUStringLiteral IU3 = u".uno:CloseWin"; +constexpr OUStringLiteral IU4 = u".uno:CloseFrame"; +constexpr OUStringLiteral IU5 = u".uno:SaveAs"; +const uno::Sequence< OUString > Interceptor::m_aInterceptedURL{ IU0, IU1, IU2, IU3, IU4, IU5}; + + + +void +Interceptor::addEventListener( + const uno::Reference<lang::XEventListener >& Listener ) +{ + osl::MutexGuard aGuard( m_aMutex ); + + if ( ! m_pDisposeEventListeners ) + m_pDisposeEventListeners = + new comphelper::OInterfaceContainerHelper2( m_aMutex ); + + m_pDisposeEventListeners->addInterface( Listener ); +} + + +void +Interceptor::removeEventListener( + const uno::Reference< lang::XEventListener >& Listener ) +{ + osl::MutexGuard aGuard( m_aMutex ); + + if ( m_pDisposeEventListeners ) + m_pDisposeEventListeners->removeInterface( Listener ); +} + + +void Interceptor::dispose() +{ + lang::EventObject aEvt; + aEvt.Source = static_cast< frame::XDispatch* >( this ); + + osl::MutexGuard aGuard(m_aMutex); + + if ( m_pDisposeEventListeners && m_pDisposeEventListeners->getLength() ) + m_pDisposeEventListeners->disposeAndClear( aEvt ); + + if(m_pStatCL) + m_pStatCL->disposeAndClear( aEvt ); + + m_xSlaveDispatchProvider = nullptr; + m_xMasterDispatchProvider = nullptr; +} + + +Interceptor::Interceptor( + const ::rtl::Reference< EmbeddedDocumentInstanceAccess_Impl >& xOleAccess, + DocumentHolder* pDocH, + bool bLink ) + : m_xOleAccess( xOleAccess ), + m_xDocHLocker( static_cast< ::cppu::OWeakObject* >( pDocH ) ), + m_pDocH(pDocH), + m_pDisposeEventListeners(nullptr), + m_pStatCL(nullptr), + m_bLink( bLink ) +{ +} + + +Interceptor::~Interceptor() +{ + delete m_pDisposeEventListeners; + delete m_pStatCL; + + DocumentHolder* pTmpDocH = nullptr; + uno::Reference< uno::XInterface > xLock; + { + osl::MutexGuard aGuard(m_aMutex); + xLock = m_xDocHLocker.get(); + if ( xLock.is() ) + pTmpDocH = m_pDocH; + } + + if ( pTmpDocH ) + pTmpDocH->ClearInterceptor(); +} + +void Interceptor::DisconnectDocHolder() +{ + osl::MutexGuard aGuard(m_aMutex); + m_xDocHLocker.clear(); + m_pDocH = nullptr; + m_xOleAccess = nullptr; +} + +//XDispatch +void SAL_CALL +Interceptor::dispatch( + const util::URL& URL, + const uno::Sequence< + beans::PropertyValue >& Arguments ) +{ + ::rtl::Reference< EmbeddedDocumentInstanceAccess_Impl > xOleAccess; + { + osl::MutexGuard aGuard(m_aMutex); + xOleAccess = m_xOleAccess; + } + + if ( xOleAccess.is() ) + { + LockedEmbedDocument_Impl aDocLock = xOleAccess->GetEmbedDocument(); + if ( aDocLock.GetEmbedDocument() ) + { + if( !m_bLink && URL.Complete == m_aInterceptedURL[0]) + aDocLock.GetEmbedDocument()->SaveObject(); + else if(!m_bLink + && ( URL.Complete == m_aInterceptedURL[2] || + URL.Complete == m_aInterceptedURL[3] || + URL.Complete == m_aInterceptedURL[4] ) ) + aDocLock.GetEmbedDocument()->Close( 0 ); + else if ( URL.Complete == m_aInterceptedURL[5] ) + { + uno::Sequence< beans::PropertyValue > aNewArgs = Arguments; + sal_Int32 nInd = 0; + + while( nInd < aNewArgs.getLength() ) + { + if ( aNewArgs[nInd].Name == "SaveTo" ) + { + aNewArgs.getArray()[nInd].Value <<= true; + break; + } + nInd++; + } + + if ( nInd == aNewArgs.getLength() ) + { + aNewArgs.realloc( nInd + 1 ); + auto pNewArgs = aNewArgs.getArray(); + pNewArgs[nInd].Name = "SaveTo"; + pNewArgs[nInd].Value <<= true; + } + + uno::Reference< frame::XDispatch > xDispatch = m_xSlaveDispatchProvider->queryDispatch( + URL, "_self", 0 ); + if ( xDispatch.is() ) + xDispatch->dispatch( URL, aNewArgs ); + } + } + } +} + + +void Interceptor::generateFeatureStateEvent() +{ + if( m_pStatCL ) + { + DocumentHolder* pTmpDocH = nullptr; + uno::Reference< uno::XInterface > xLock; + { + osl::MutexGuard aGuard(m_aMutex); + xLock = m_xDocHLocker.get(); + if ( xLock.is() ) + pTmpDocH = m_pDocH; + } + + OUString aTitle; + if ( pTmpDocH ) + aTitle = pTmpDocH->getTitle(); + + for(int i = 0; i < IUL; ++i) + { + if( i == 1 || (m_bLink && i != 5) ) + continue; + + comphelper::OInterfaceContainerHelper3<css::frame::XStatusListener>* pICH = + m_pStatCL->getContainer(m_aInterceptedURL[i]); + if(!pICH) + continue; + std::vector<uno::Reference<css::frame::XStatusListener> > aSeq = pICH->getElements(); + if(aSeq.empty()) + continue; + + frame::FeatureStateEvent aStateEvent; + aStateEvent.IsEnabled = true; + aStateEvent.Requery = false; + if(i == 0) + { + + aStateEvent.FeatureURL.Complete = m_aInterceptedURL[0]; + aStateEvent.FeatureDescriptor = "Update"; + aStateEvent.State <<= "($1) " + aTitle; + + } + else if ( i == 5 ) + { + aStateEvent.FeatureURL.Complete = m_aInterceptedURL[5]; + aStateEvent.FeatureDescriptor = "SaveCopyTo"; + aStateEvent.State <<= OUString("($3)"); + } + else + { + aStateEvent.FeatureURL.Complete = m_aInterceptedURL[i]; + aStateEvent.FeatureDescriptor = "Close and Return"; + aStateEvent.State <<= "($2) " + aTitle; + + } + + for(uno::Reference<css::frame::XStatusListener> const & control : std::as_const(aSeq)) + control->statusChanged(aStateEvent); + } + } +} + + +void SAL_CALL +Interceptor::addStatusListener( + const uno::Reference< + frame::XStatusListener >& Control, + const util::URL& URL ) +{ + if(!Control.is()) + return; + + if( !m_bLink && URL.Complete == m_aInterceptedURL[0] ) + { // Save + DocumentHolder* pTmpDocH = nullptr; + uno::Reference< uno::XInterface > xLock; + { + osl::MutexGuard aGuard(m_aMutex); + xLock = m_xDocHLocker.get(); + if ( xLock.is() ) + pTmpDocH = m_pDocH; + } + + OUString aTitle; + if ( pTmpDocH ) + aTitle = pTmpDocH->getTitle(); + + frame::FeatureStateEvent aStateEvent; + aStateEvent.FeatureURL.Complete = m_aInterceptedURL[0]; + aStateEvent.FeatureDescriptor = "Update"; + aStateEvent.IsEnabled = true; + aStateEvent.Requery = false; + aStateEvent.State <<= "($1) " + aTitle; + Control->statusChanged(aStateEvent); + + { + osl::MutexGuard aGuard(m_aMutex); + if(!m_pStatCL) + m_pStatCL = + new StatusChangeListenerContainer(m_aMutex); + } + + m_pStatCL->addInterface(URL.Complete,Control); + return; + } + + sal_Int32 i = 2; + if ( !m_bLink + && ( URL.Complete == m_aInterceptedURL[i] || + URL.Complete == m_aInterceptedURL[++i] || + URL.Complete == m_aInterceptedURL[++i] ) ) + { // Close and return + DocumentHolder* pTmpDocH = nullptr; + uno::Reference< uno::XInterface > xLock; + { + osl::MutexGuard aGuard(m_aMutex); + xLock = m_xDocHLocker.get(); + if ( xLock.is() ) + pTmpDocH = m_pDocH; + } + + OUString aTitle; + if ( pTmpDocH ) + aTitle = pTmpDocH->getTitle(); + + frame::FeatureStateEvent aStateEvent; + aStateEvent.FeatureURL.Complete = m_aInterceptedURL[i]; + aStateEvent.FeatureDescriptor = "Close and Return"; + aStateEvent.IsEnabled = true; + aStateEvent.Requery = false; + aStateEvent.State <<= "($2) " + aTitle; + Control->statusChanged(aStateEvent); + + + { + osl::MutexGuard aGuard(m_aMutex); + if(!m_pStatCL) + m_pStatCL = + new StatusChangeListenerContainer(m_aMutex); + } + + m_pStatCL->addInterface(URL.Complete,Control); + return; + } + + if(URL.Complete == m_aInterceptedURL[5]) + { // SaveAs + frame::FeatureStateEvent aStateEvent; + aStateEvent.FeatureURL.Complete = m_aInterceptedURL[5]; + aStateEvent.FeatureDescriptor = "SaveCopyTo"; + aStateEvent.IsEnabled = true; + aStateEvent.Requery = false; + aStateEvent.State <<= OUString("($3)"); + Control->statusChanged(aStateEvent); + + { + osl::MutexGuard aGuard(m_aMutex); + if(!m_pStatCL) + m_pStatCL = + new StatusChangeListenerContainer(m_aMutex); + } + + m_pStatCL->addInterface(URL.Complete,Control); + return; + } + +} + + +void SAL_CALL +Interceptor::removeStatusListener( + const uno::Reference< + frame::XStatusListener >& Control, + const util::URL& URL ) +{ + if(!(Control.is() && m_pStatCL)) + return; + else { + m_pStatCL->removeInterface(URL.Complete,Control); + return; + } +} + + +//XInterceptorInfo +uno::Sequence< OUString > +SAL_CALL +Interceptor::getInterceptedURLs( ) +{ + // now implemented as update + if ( m_bLink ) + return { m_aInterceptedURL[1], m_aInterceptedURL[5] }; + + return m_aInterceptedURL; +} + + +// XDispatchProvider + +uno::Reference< frame::XDispatch > SAL_CALL +Interceptor::queryDispatch( + const util::URL& URL, + const OUString& TargetFrameName, + sal_Int32 SearchFlags ) +{ + osl::MutexGuard aGuard(m_aMutex); + if( !m_bLink && URL.Complete == m_aInterceptedURL[0] ) + return static_cast<frame::XDispatch*>(this); + else if(URL.Complete == m_aInterceptedURL[1]) + return nullptr; + else if( !m_bLink && URL.Complete == m_aInterceptedURL[2] ) + return static_cast<frame::XDispatch*>(this); + else if( !m_bLink && URL.Complete == m_aInterceptedURL[3] ) + return static_cast<frame::XDispatch*>(this); + else if( !m_bLink && URL.Complete == m_aInterceptedURL[4] ) + return static_cast<frame::XDispatch*>(this); + else if(URL.Complete == m_aInterceptedURL[5]) + return static_cast<frame::XDispatch*>(this); + else { + if(m_xSlaveDispatchProvider.is()) + return m_xSlaveDispatchProvider->queryDispatch( + URL,TargetFrameName,SearchFlags); + else + return uno::Reference<frame::XDispatch>(nullptr); + } +} + +uno::Sequence< uno::Reference< frame::XDispatch > > SAL_CALL +Interceptor::queryDispatches( + const uno::Sequence<frame::DispatchDescriptor >& Requests ) +{ + uno::Sequence< uno::Reference< frame::XDispatch > > aRet; + osl::MutexGuard aGuard(m_aMutex); + if(m_xSlaveDispatchProvider.is()) + aRet = m_xSlaveDispatchProvider->queryDispatches(Requests); + else + aRet.realloc(Requests.getLength()); + auto aRetRange = asNonConstRange(aRet); + for(sal_Int32 i = 0; i < Requests.getLength(); ++i) + if ( !m_bLink && m_aInterceptedURL[0] == Requests[i].FeatureURL.Complete ) + aRetRange[i] = static_cast<frame::XDispatch*>(this); + else if(m_aInterceptedURL[1] == Requests[i].FeatureURL.Complete) + aRetRange[i] = nullptr; + else if( !m_bLink && m_aInterceptedURL[2] == Requests[i].FeatureURL.Complete ) + aRetRange[i] = static_cast<frame::XDispatch*>(this); + else if( !m_bLink && m_aInterceptedURL[3] == Requests[i].FeatureURL.Complete ) + aRetRange[i] = static_cast<frame::XDispatch*>(this); + else if( !m_bLink && m_aInterceptedURL[4] == Requests[i].FeatureURL.Complete ) + aRetRange[i] = static_cast<frame::XDispatch*>(this); + else if(m_aInterceptedURL[5] == Requests[i].FeatureURL.Complete) + aRetRange[i] = static_cast<frame::XDispatch*>(this); + + return aRet; +} + + +//XDispatchProviderInterceptor + +uno::Reference< frame::XDispatchProvider > SAL_CALL +Interceptor::getSlaveDispatchProvider( ) +{ + osl::MutexGuard aGuard(m_aMutex); + return m_xSlaveDispatchProvider; +} + +void SAL_CALL +Interceptor::setSlaveDispatchProvider( + const uno::Reference< frame::XDispatchProvider >& NewDispatchProvider ) +{ + osl::MutexGuard aGuard(m_aMutex); + m_xSlaveDispatchProvider = NewDispatchProvider; +} + + +uno::Reference< frame::XDispatchProvider > SAL_CALL +Interceptor::getMasterDispatchProvider( ) +{ + osl::MutexGuard aGuard(m_aMutex); + return m_xMasterDispatchProvider; +} + + +void SAL_CALL +Interceptor::setMasterDispatchProvider( + const uno::Reference< frame::XDispatchProvider >& NewSupplier ) +{ + osl::MutexGuard aGuard(m_aMutex); + m_xMasterDispatchProvider = NewSupplier; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/embedserv/source/embed/servprov.cxx b/embedserv/source/embed/servprov.cxx new file mode 100644 index 000000000..44eab63dd --- /dev/null +++ b/embedserv/source/embed/servprov.cxx @@ -0,0 +1,194 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <stdafx.h> +#include <servprov.hxx> +#include <embeddoc.hxx> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <cppuhelper/supportsservice.hxx> +#include <osl/diagnose.h> +#include <osl/mutex.hxx> +#include <osl/thread.h> +#include <sal/log.hxx> + +using namespace com::sun::star; + +const GUID* const guidList[ SUPPORTED_FACTORIES_NUM ] = { + &OID_WriterTextServer, + &OID_WriterOASISTextServer, + &OID_CalcServer, + &OID_CalcOASISServer, + &OID_DrawingServer, + &OID_DrawingOASISServer, + &OID_PresentationServer, + &OID_PresentationOASISServer, + &OID_MathServer, + &OID_MathOASISServer +}; + +static void o2u_attachCurrentThread() +{ + [[maybe_unused]] static thread_local bool aInit = [] + { + HRESULT hr = CoInitializeEx(nullptr, COINIT_MULTITHREADED); + if (!SUCCEEDED(hr)) + { // FIXME: is it a problem that this ends up in STA currently? + assert(RPC_E_CHANGED_MODE == hr); + SAL_INFO("embedserv.ole", + "CoInitializeEx fail: probably thread is in STA already?"); + } + return SUCCEEDED(hr); + }(); +} + + +// EmbedServer_Impl + +EmbedServer_Impl::EmbedServer_Impl( const uno::Reference<lang::XMultiServiceFactory>& xFactory): + m_xFactory( xFactory) +{ + for( int nInd = 0; nInd < SUPPORTED_FACTORIES_NUM; nInd++ ) + { + m_pOLEFactories[nInd] = new EmbedProviderFactory_Impl( m_xFactory, guidList[nInd] ); + m_pOLEFactories[nInd]->registerClass(); + } +} + +EmbedServer_Impl::~EmbedServer_Impl() +{ + for( int nInd = 0; nInd < SUPPORTED_FACTORIES_NUM; nInd++ ) + { + if ( m_pOLEFactories[nInd] ) + m_pOLEFactories[nInd]->deregisterClass(); + } +} + +OUString EmbedServer_Impl::getImplementationName() +{ + return "com.sun.star.comp.ole.EmbedServer"; +} + +sal_Bool EmbedServer_Impl::supportsService(OUString const & ServiceName) +{ + return cppu::supportsService(this, ServiceName); +} + +css::uno::Sequence<OUString> EmbedServer_Impl::getSupportedServiceNames() +{ + return css::uno::Sequence<OUString>{ + "com.sun.star.document.OleEmbeddedServerRegistration"}; +} + +// EmbedProviderFactory_Impl + +EmbedProviderFactory_Impl::EmbedProviderFactory_Impl(const uno::Reference<lang::XMultiServiceFactory>& xFactory, const GUID* pGuid) + : m_refCount( 0 ) + , m_guid( *pGuid ) + , m_xFactory( xFactory ) +{ +} + +EmbedProviderFactory_Impl::~EmbedProviderFactory_Impl() +{ +} + +bool EmbedProviderFactory_Impl::registerClass() +{ + HRESULT hresult; + + o2u_attachCurrentThread(); + + hresult = CoRegisterClassObject( + m_guid, + this, + CLSCTX_LOCAL_SERVER, + REGCLS_MULTIPLEUSE, + &m_factoryHandle); + + return (hresult == NOERROR); +} + +bool EmbedProviderFactory_Impl::deregisterClass() +{ + HRESULT hresult = CoRevokeClassObject( m_factoryHandle ); + + return (hresult == NOERROR); +} + +COM_DECLSPEC_NOTHROW STDMETHODIMP EmbedProviderFactory_Impl::QueryInterface(REFIID riid, void** ppv) +{ + if(IsEqualIID(riid, IID_IUnknown)) + { + AddRef(); + *ppv = static_cast<IUnknown*>(static_cast<IClassFactory*>(this)); + return NOERROR; + } + else if (IsEqualIID(riid, IID_IClassFactory)) + { + AddRef(); + *ppv = static_cast<IClassFactory*>(this); + return NOERROR; + } + + *ppv = nullptr; + return ResultFromScode(E_NOINTERFACE); +} + +COM_DECLSPEC_NOTHROW STDMETHODIMP_(ULONG) EmbedProviderFactory_Impl::AddRef() +{ + return osl_atomic_increment( &m_refCount); +} + +COM_DECLSPEC_NOTHROW STDMETHODIMP_(ULONG) EmbedProviderFactory_Impl::Release() +{ + ::osl::MutexGuard aGuard( ::osl::Mutex::getGlobalMutex()); + sal_Int32 nCount = --m_refCount; + if ( nCount == 0 ) + { + delete this; + } + + return nCount; +} + +COM_DECLSPEC_NOTHROW STDMETHODIMP EmbedProviderFactory_Impl::CreateInstance(IUnknown*, + REFIID riid, void** ppv) +{ + IUnknown* pEmbedDocument = static_cast<IPersistStorage*>( new EmbedDocument_Impl( m_xFactory, &m_guid ) ); + + return pEmbedDocument->QueryInterface( riid, ppv ); +} + +COM_DECLSPEC_NOTHROW STDMETHODIMP EmbedProviderFactory_Impl::LockServer( int /*fLock*/ ) +{ + return NOERROR; +} + + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +embedserv_EmbedServer( + css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const& ) +{ + auto msf = uno::Reference<lang::XMultiServiceFactory>(context->getServiceManager(), css::uno::UNO_QUERY_THROW); + return cppu::acquire(new EmbedServer_Impl(msf)); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/embedserv/source/embed/syswinwrapper.cxx b/embedserv/source/embed/syswinwrapper.cxx new file mode 100644 index 000000000..7024a054c --- /dev/null +++ b/embedserv/source/embed/syswinwrapper.cxx @@ -0,0 +1,435 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <docholder.hxx> +#include <syswinwrapper.hxx> + +/* + * CWindow::CWindow + * CWindow::~CWindow + * + * Constructor Parameters: + * hInst HINSTANCE of the task owning us. + */ + + +using namespace winwrap; + + +#define HWWL_STRUCTURE 0 + +//Notification codes for WM_COMMAND messages +#define HWN_BORDERDOUBLECLICKED 1 +#define CBHATCHWNDEXTRA (sizeof(LONG)) +#define SZCLASSHATCHWIN L"hatchwin" + +typedef CHatchWin *PCHatchWin; + +winwrap::CWindow::CWindow(HINSTANCE hInst) +{ + m_hInst=hInst; + m_hWnd=nullptr; + return; +} + +winwrap::CWindow::~CWindow() +{ + if (IsWindow(m_hWnd)) + DestroyWindow(m_hWnd); + + return; +} + + +/* + * CWindow::Window + * + * Purpose: + * Returns the window handle associated with this object. + * + * Return Value: + * HWND Window handle for this object + */ + +HWND winwrap::CWindow::Window() +{ + return m_hWnd; +} + + +/* + * CWindow::Instance + * + * Purpose: + * Returns the instance handle associated with this object. + * + * Return Value: + * HINSTANCE Instance handle of the module stored here. + */ + +HINSTANCE winwrap::CWindow::Instance() +{ + return m_hInst; +} + +/* + * HatchWindowRegister + * + * Purpose: + * Registers the hatch window class for use with CHatchWin. + * + * Parameters: + * hInst HINSTANCE under which to register. + * + * Return Value: + * BOOL TRUE if successful, FALSE otherwise. + */ + +BOOL winwrap::HatchWindowRegister(HINSTANCE hInst) +{ + WNDCLASSW wc; + + //Must have CS_DBLCLKS for border! + wc.style = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS; + wc.hInstance = hInst; + wc.cbClsExtra = 0; + wc.lpfnWndProc = HatchWndProc; + wc.cbWndExtra = CBHATCHWNDEXTRA; + wc.hIcon = nullptr; + wc.hCursor = LoadCursor(nullptr, IDC_ARROW); + wc.hbrBackground = reinterpret_cast<HBRUSH>(COLOR_WINDOW+1); + wc.lpszMenuName = nullptr; + wc.lpszClassName = SZCLASSHATCHWIN; + + return RegisterClassW(&wc) != 0; +} + + +/* + * CHatchWin:CHatchWin + * CHatchWin::~CHatchWin + * + * Constructor Parameters: + * hInst HINSTANCE of the application we're in. + */ + +CHatchWin::CHatchWin(HINSTANCE hInst,const DocumentHolder* pDocHolder) + : CWindow(hInst), + m_aTracker() +{ + m_hWnd=nullptr; + m_hWndKid=nullptr; + m_hWndAssociate=nullptr; + m_uID=0; + + m_dBorderOrg=GetProfileIntW(L"windows" + , L"OleInPlaceBorderWidth" + , HATCHWIN_BORDERWIDTHDEFAULT); + + m_dBorder=m_dBorderOrg; + SetRect(&m_rcPos, 0, 0, 0, 0); + SetRect(&m_rcClip, 0, 0, 0, 0); + + m_pDocHolder = pDocHolder; + return; +} + + +CHatchWin::~CHatchWin() +{ + /* + * Chances are this was already destroyed when a document + * was destroyed. + */ + if (nullptr!=m_hWnd && IsWindow(m_hWnd)) + DestroyWindow(m_hWnd); + + return; +} + + +/* + * CHatchWin::Init + * + * Purpose: + * Instantiates a hatch window within a given parent with a + * default rectangle. This is not initially visible. + * + * Parameters: + * hWndParent HWND of the parent of this window + * uID WORD identifier for this window (send in + * notifications to associate window). + * hWndAssoc HWND of the initial associate. + * + * Return Value: + * BOOL TRUE if the function succeeded, FALSE otherwise. + */ + +BOOL CHatchWin::Init(HWND hWndParent, WORD uID, HWND hWndAssoc) +{ + m_hWndParent = hWndParent; + m_hWnd=CreateWindowExW( + WS_EX_NOPARENTNOTIFY, SZCLASSHATCHWIN + , SZCLASSHATCHWIN, WS_CHILD | WS_CLIPSIBLINGS + | WS_CLIPCHILDREN, 0, 0, 100, 100, hWndParent + , reinterpret_cast<HMENU>(UINT_PTR(uID)), m_hInst, this); + + m_uID=uID; + m_hWndAssociate=hWndAssoc; + + return (nullptr!=m_hWnd); +} + + +void CHatchWin::SetTrans() +{ + HRGN hrgn = CreateRectRgn(0,0,0,0); + SetWindowRgn(m_hWnd,hrgn,true); +} + +/* + * CHatchWin::HwndAssociateSet + * CHatchWin::HwndAssociateGet + * + * Purpose: + * Sets (Set) or retrieves (Get) the associate window of the + * hatch window. + * + * Parameters: (Set only) + * hWndAssoc HWND to set as the associate. + * + * Return Value: + * HWND Previous (Set) or current (Get) associate + * window. + */ + +HWND CHatchWin::HwndAssociateSet(HWND hWndAssoc) +{ + HWND hWndT=m_hWndAssociate; + + m_hWndAssociate=hWndAssoc; + return hWndT; +} + + +HWND CHatchWin::HwndAssociateGet() +{ + return m_hWndAssociate; +} + + +/* + * CHatchWin::RectsSet + * + * Purpose: + * Changes the size and position of the hatch window and the child + * window within it using a position rectangle for the child and + * a clipping rectangle for the hatch window and child. The hatch + * window occupies prcPos expanded by the hatch border and clipped + * by prcClip. The child window is fit to prcPos to give the + * proper scaling, but it clipped to the hatch window which + * therefore clips it to prcClip without affecting the scaling. + * + * Parameters: + * prcPos LPRECT providing the position rectangle. + * prcClip LPRECT providing the clipping rectangle. + * + * Return Value: + * None + */ + +void CHatchWin::RectsSet(LPRECT prcPos, LPRECT prcClip) +{ + RECT rc; + RECT rcPos; + + m_rcPos=*prcPos; + m_rcClip=*prcClip; + + //Calculate the rectangle for the hatch window, then clip it. + rcPos=*prcPos; + InflateRect(&rcPos, m_dBorder, m_dBorder); + IntersectRect(&rc, &rcPos, prcClip); + + SetWindowPos(m_hWnd, nullptr, rc.left, rc.top, rc.right-rc.left + , rc.bottom-rc.top, SWP_NOZORDER | SWP_NOACTIVATE); + + /* + * Set the rectangle of the child window to be at m_dBorder + * from the top and left but with the same size as prcPos + * contains. The hatch window will clip it. + */ +// SetWindowPos(m_hWndKid, NULL, rcPos.left-rc.left+m_dBorder +// , rcPos.top-rc.top+m_dBorder, prcPos->right-prcPos->left +// , prcPos->bottom-prcPos->top, SWP_NOZORDER | SWP_NOACTIVATE); + + RECT newRC; + GetClientRect(m_hWnd,&newRC); + m_aTracker = Tracker( + &newRC, + Tracker::hatchInside | + Tracker::hatchedBorder | + Tracker::resizeInside + ); + + return; +} + + +/* + * CHatchWin::ChildSet + * + * Purpose: + * Assigns a child window to this hatch window. + * + * Parameters: + * hWndKid HWND of the child window. + * + * Return Value: + * None + */ + +void CHatchWin::ChildSet(HWND hWndKid) +{ + m_hWndKid=hWndKid; + + if (nullptr!=hWndKid) + { + SetParent(hWndKid, m_hWnd); + + //Ensure this is visible when the hatch window becomes visible. + ShowWindow(hWndKid, SW_SHOW); + } + + return; +} + + +/* + * CHatchWin::ShowHatch + * + * Purpose: + * Turns hatching on and off; turning the hatching off changes + * the size of the window to be exactly that of the child, leaving + * everything else the same. The result is that we don't have + * to turn off drawing because our own WM_PAINT will never be + * called. + * + * Parameters: + * fHatch BOOL indicating to show (TRUE) or hide (FALSE) + the hatching. + * + * Return Value: + * None + */ + +void CHatchWin::ShowHatch(BOOL fHatch) +{ + /* + * All we have to do is set the border to zero and + * call SetRects again with the last rectangles the + * child sent to us. + */ + m_dBorder=fHatch ? m_dBorderOrg : 0; + RectsSet(&m_rcPos, &m_rcClip); + return; +} + + +/* + * HatchWndProc + * + * Purpose: + * Standard window procedure for the Hatch Window + */ + +LRESULT APIENTRY winwrap::HatchWndProc( + HWND hWnd, UINT iMsg + , WPARAM wParam, LPARAM lParam) +{ + PCHatchWin phw; + HDC hDC; + PAINTSTRUCT ps; + + phw=reinterpret_cast<PCHatchWin>(GetWindowLongPtrW(hWnd, HWWL_STRUCTURE)); + POINT ptMouse; + + switch (iMsg) + { + case WM_CREATE: + phw=static_cast<PCHatchWin>(reinterpret_cast<LPCREATESTRUCT>(lParam)->lpCreateParams); + SetWindowLongPtrW(hWnd, HWWL_STRUCTURE, reinterpret_cast<LONG_PTR>(phw)); + break; + case WM_PAINT: + hDC=BeginPaint(hWnd,&ps); + //Always draw the hatching. + phw->m_aTracker.Draw(hDC); + EndPaint(hWnd,&ps); + break; + case WM_LBUTTONDOWN: + GetCursorPos(&ptMouse); + ScreenToClient(hWnd,&ptMouse); + + // track in case we have to + if(phw->m_aTracker.Track(hWnd,ptMouse,FALSE,GetParent(hWnd))) + { + RECT aRect = phw->m_aTracker.m_rect; + TransformRect(&aRect,hWnd,GetParent(hWnd)); + phw->m_pDocHolder->OnPosRectChanged(&aRect); + } + break; + case WM_LBUTTONUP: + case WM_MOUSEMOVE: + GetCursorPos(&ptMouse); + ScreenToClient(hWnd,&ptMouse); + phw->m_aTracker.SetCursor(hWnd,HTCLIENT); + break; + case WM_SETFOCUS: + //We need this since the container will SetFocus to us. + if (nullptr!=phw->m_hWndKid) + SetFocus(phw->m_hWndKid); + + break; + case WM_LBUTTONDBLCLK: + /* + * If the double click was within m_dBorder of an + * edge, send the HWN_BORDERDOUBLECLICKED notification. + * + * Because we're always sized just larger than our child + * window by the border width, we can only *get* this + * message when the mouse is on the border. So we can + * just send the notification. + */ + if (nullptr!=phw->m_hWndAssociate) + { + SendMessageW( + phw->m_hWndAssociate, WM_COMMAND, + MAKEWPARAM(phw->m_uID, HWN_BORDERDOUBLECLICKED), + reinterpret_cast<LPARAM>(hWnd)); + } + + break; + default: + return DefWindowProcW(hWnd, iMsg, wParam, lParam); + } + + return 0; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/embedserv/source/embed/tracker.cxx b/embedserv/source/embed/tracker.cxx new file mode 100644 index 000000000..2a502671b --- /dev/null +++ b/embedserv/source/embed/tracker.cxx @@ -0,0 +1,841 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <sal/config.h> + +#include <algorithm> + +#include <sal/types.h> +#include <sal/log.hxx> + +#include <stdafx.h> +#include <stddef.h> +#include <syswinwrapper.hxx> + +// windowserrorstring.hxx includes postwin.h, which #undef OPAQUE, so "#redef" it +#include <comphelper/windowserrorstring.hxx> +#ifdef OPAQUE +#error OPAQUE should not be defined!? +#endif +#define OPAQUE 2 + +static HCURSOR afxCursors[10] = { nullptr, }; +static HBRUSH afxHalftoneBrush = nullptr; + +namespace { + +// the struct below is used to determine the qualities of a particular handle +struct AFX_HANDLEINFO +{ + size_t nOffsetX; // offset within RECT for X coordinate + size_t nOffsetY; // offset within RECT for Y coordinate + int nCenterX; // adjust X by Width()/2 * this number + int nCenterY; // adjust Y by Height()/2 * this number + int nHandleX; // adjust X by handle size * this number + int nHandleY; // adjust Y by handle size * this number + int nInvertX; // handle converts to this when X inverted + int nInvertY; // handle converts to this when Y inverted +}; + +} + +// this array describes all 8 handles (clock-wise) +const AFX_HANDLEINFO afxHandleInfo[] = +{ + // corner handles (top-left, top-right, bottom-right, bottom-left + { offsetof(RECT, left), offsetof(RECT, top), 0, 0, 0, 0, 1, 3 }, + { offsetof(RECT, right), offsetof(RECT, top), 0, 0, -1, 0, 0, 2 }, + { offsetof(RECT, right), offsetof(RECT, bottom), 0, 0, -1, -1, 3, 1 }, + { offsetof(RECT, left), offsetof(RECT, bottom), 0, 0, 0, -1, 2, 0 }, + + // side handles (top, right, bottom, left) + { offsetof(RECT, left), offsetof(RECT, top), 1, 0, 0, 0, 4, 6 }, + { offsetof(RECT, right), offsetof(RECT, top), 0, 1, -1, 0, 7, 5 }, + { offsetof(RECT, left), offsetof(RECT, bottom), 1, 0, 0, -1, 6, 4 }, + { offsetof(RECT, left), offsetof(RECT, top), 0, 1, 0, 0, 5, 7 } +}; + +namespace { + +// the struct below gives us information on the layout of a RECT struct and +// the relationship between its members +struct AFX_RECTINFO +{ + size_t nOffsetAcross; // offset of opposite point (ie. left->right) + int nSignAcross; // sign relative to that point (ie. add/subtract) +}; + +} + +// this array is indexed by the offset of the RECT member / sizeof(int) +const AFX_RECTINFO afxRectInfo[] = +{ + { offsetof(RECT, right), +1 }, + { offsetof(RECT, bottom), +1 }, + { offsetof(RECT, left), -1 }, + { offsetof(RECT, top), -1 }, +}; + + +static HBRUSH HalftoneBrush() +{ + if (afxHalftoneBrush == nullptr) + { + WORD grayPattern[8]; + for (int i = 0; i < 8; i++) + grayPattern[i] = static_cast<WORD>(0x5555 << (i & 1)); + HBITMAP grayBitmap = CreateBitmap(8, 8, 1, 1, &grayPattern); + if (grayBitmap != nullptr) + { + afxHalftoneBrush = CreatePatternBrush(grayBitmap); + DeleteObject(grayBitmap); + } + } + return afxHalftoneBrush; +} + + +static void DrawDragRect( + HDC hDC,LPRECT lpRect,SIZE size, + LPRECT lpRectLast,SIZE sizeLast, + HBRUSH hBrush = nullptr,HBRUSH hBrushLast = nullptr) +{ + // first, determine the update region and select it + HRGN rgnNew; + HRGN rgnOutside,rgnInside; + rgnOutside = CreateRectRgnIndirect(lpRect); + RECT rect = *lpRect; + InflateRect(&rect,-size.cx, -size.cy); + IntersectRect(&rect,&rect,lpRect); + rgnInside = CreateRectRgnIndirect(&rect); + rgnNew = CreateRectRgn(0, 0, 0, 0); + CombineRgn(rgnNew,rgnOutside,rgnInside,RGN_XOR); + + HBRUSH hBrushOld = nullptr; + if (hBrush == nullptr) + hBrush = HalftoneBrush(); + if (hBrushLast == nullptr) + hBrushLast = hBrush; + + HRGN rgnLast(nullptr); + HRGN rgnUpdate(nullptr); + if (lpRectLast != nullptr) + { + // find difference between new region and old region + rgnLast = CreateRectRgn(0, 0, 0, 0); + SetRectRgn( + rgnOutside, + lpRectLast->left, + lpRectLast->top, + lpRectLast->right, + lpRectLast->bottom); + rect = *lpRectLast; + InflateRect(&rect,-sizeLast.cx, -sizeLast.cy); + IntersectRect(&rect,&rect, lpRectLast); + SetRectRgn(rgnInside,rect.left,rect.top,rect.right,rect.bottom); + CombineRgn(rgnLast,rgnOutside,rgnInside, RGN_XOR); + +// // only diff them if brushes are the same + if (hBrush == hBrushLast) + { + rgnUpdate = CreateRectRgn(0, 0, 0, 0); + CombineRgn(rgnUpdate,rgnLast,rgnNew, RGN_XOR); + } + } + if (hBrush != hBrushLast && lpRectLast != nullptr) + { + // brushes are different -- erase old region first + SelectClipRgn(hDC,rgnLast); + GetClipBox(hDC,&rect); + hBrushOld = static_cast<HBRUSH>(SelectObject(hDC,static_cast<HGDIOBJ>(hBrushLast))); + PatBlt(hDC,rect.left,rect.top,(rect.right-rect.left),(rect.bottom-rect.top),PATINVERT); + + SelectObject(hDC,static_cast<HGDIOBJ>(hBrushOld)); + hBrushOld = nullptr; + } + + // draw into the update/new region + SelectClipRgn(hDC,rgnUpdate); + + GetClipBox(hDC,&rect); + hBrushOld = static_cast<HBRUSH>(SelectObject(hDC, static_cast<HGDIOBJ>(hBrush))); + PatBlt(hDC,rect.left, rect.top,(rect.right-rect.left),(rect.bottom-rect.top), PATINVERT); + + // cleanup DC + if (hBrushOld != nullptr) + SelectObject(hDC, static_cast<HGDIOBJ>(hBrushOld)); + SelectClipRgn(hDC,nullptr); +} + + +void winwrap::TransformRect(LPRECT rect,HWND pWnd,HWND pWndClipTo) +{ + POINT pt; + pt.x = rect->left;pt.y = rect->top; + ClientToScreen(pWnd,&pt); + ScreenToClient(pWndClipTo,&pt); + rect->left = pt.x; rect->top = pt.y; + + pt.x = rect->right;pt.y = rect->bottom; + ClientToScreen(pWnd,&pt); + ScreenToClient(pWndClipTo,&pt); + rect->right = pt.x; rect->bottom = pt.y; +} + + +static void NormalizeRect(LPRECT rp) +{ + if(rp->left > rp->right) { + UINT tmp = rp->left; + rp->left = rp->right; + rp->right = tmp; + } + + if(rp->top > rp->bottom) { + UINT tmp = rp->top; + rp->top = rp->bottom; + rp->bottom = tmp; + } +} + + +using namespace winwrap; + + +Tracker::Tracker() +{ +} + + +Tracker::Tracker(LPCRECT lpSrcRect, UINT nStyle) +{ + Construct(); + CopyRect(&m_rect,lpSrcRect); + m_nStyle = nStyle; +} + +static HBRUSH afxHatchBrush = nullptr; +static HPEN afxBlackDottedPen = nullptr; +static int afxHandleSize = 0; + + +void Tracker::Construct() +{ + static bool bInitialized = false; + if (!bInitialized) + { + if (afxHatchBrush == nullptr) + { + // create the hatch pattern + bitmap + WORD hatchPattern[8]; + WORD wPattern = 0x1111; + for (int i = 0; i < 4; i++) + { + hatchPattern[i] = wPattern; + hatchPattern[i+4] = wPattern; + wPattern <<= 1; + } + HBITMAP hatchBitmap = CreateBitmap(8, 8, 1, 1,&hatchPattern); + + // create black hatched brush + afxHatchBrush = CreatePatternBrush(hatchBitmap); + DeleteObject(hatchBitmap); + } + + if (afxBlackDottedPen == nullptr) + { + // create black dotted pen + afxBlackDottedPen = CreatePen(PS_DOT, 0, RGB(0, 0, 0)); + } + + // get default handle size from Windows profile setting + static const WCHAR szWindows[] = L"windows"; + static const WCHAR szInplaceBorderWidth[] = L"oleinplaceborderwidth"; + afxHandleSize = GetProfileIntW(szWindows, szInplaceBorderWidth, 4); + bInitialized = true; + + afxCursors[0] = afxCursors[2] = LoadCursor(nullptr,IDC_SIZENWSE); + afxCursors[4] = afxCursors[6] = LoadCursor(nullptr,IDC_SIZENS); + afxCursors[1] = afxCursors[3] = LoadCursor(nullptr,IDC_SIZENESW); + afxCursors[5] = afxCursors[7] = LoadCursor(nullptr,IDC_SIZEWE); + afxCursors[8] = LoadCursor(nullptr,IDC_SIZEALL); + } + + m_nStyle = 0; + m_nHandleSize = afxHandleSize; + m_sizeMin.cy = m_sizeMin.cx = m_nHandleSize*2; + + SetRectEmpty(&m_rectLast); + m_sizeLast.cx = m_sizeLast.cy = 0; + m_bErase = FALSE; + m_bFinalErase = FALSE; +} + +Tracker::~Tracker() +{ +} + + +int Tracker::HitTest(POINT point) const +{ + TrackerHit hitResult = hitNothing; + + RECT rectTrue; + GetTrueRect(&rectTrue); + NormalizeRect(&rectTrue); + if (PtInRect(&rectTrue,point)) + { + if ((m_nStyle & (resizeInside|resizeOutside)) != 0) + hitResult = static_cast<TrackerHit>(HitTestHandles(point)); + else + hitResult = hitMiddle; + } + return hitResult; +} + + +BOOL Tracker::SetCursor(HWND pWnd, UINT nHitTest) const +{ + // trackers should only be in client area + if (nHitTest != HTCLIENT) + return FALSE; + + // convert cursor position to client co-ordinates + POINT point; + GetCursorPos(&point); + ScreenToClient(pWnd,&point); + + // do hittest and normalize hit + int nHandle = HitTestHandles(point); + if (nHandle < 0) + return FALSE; + + // need to normalize the hittest such that we get proper cursors + nHandle = NormalizeHit(nHandle); + + // handle special case of hitting area between handles + // (logically the same -- handled as a move -- but different cursor) + if (nHandle == hitMiddle && !PtInRect(&m_rect,point)) + { + // only for trackers with hatchedBorder (ie. in-place resizing) + if (m_nStyle & hatchedBorder) + nHandle = TrackerHit(9); + } + + ::SetCursor(afxCursors[nHandle]); + return TRUE; +} + + +BOOL Tracker::Track(HWND hWnd,POINT point,BOOL bAllowInvert, + HWND hWndClipTo) +{ + // perform hit testing on the handles + int nHandle = HitTestHandles(point); + if (nHandle < 0) + { + // didn't hit a handle, so just return FALSE + return FALSE; + } + + // otherwise, call helper function to do the tracking + m_bAllowInvert = bAllowInvert; + SetCursor(hWnd,nHandle); + return TrackHandle(nHandle, hWnd, point, hWndClipTo); +} + + +BOOL Tracker::TrackHandle(int nHandle,HWND hWnd,POINT point,HWND hWndClipTo) +{ + // don't handle if capture already set + if (GetCapture() != nullptr) + return FALSE; + + // save original width & height in pixels + int nWidth = m_rect.right - m_rect.left; + int nHeight = m_rect.bottom - m_rect.top; + + // set capture to the window which received this message + SetCapture(hWnd); + UpdateWindow(hWnd); + if (hWndClipTo != nullptr) + UpdateWindow(hWndClipTo); + RECT rectSave = m_rect; + + // find out what x/y coords we are supposed to modify + int *px, *py; + int xDiff, yDiff; + GetModifyPointers(nHandle, &px, &py, &xDiff, &yDiff); + xDiff = point.x - xDiff; + yDiff = point.y - yDiff; + + // get DC for drawing + HDC hDrawDC; + if (hWndClipTo != nullptr) + { + // clip to arbitrary window by using adjusted Window DC + hDrawDC = GetDCEx(hWndClipTo,nullptr, DCX_CACHE); + } + else + { + // otherwise, just use normal DC + hDrawDC = GetDC(hWnd); + } + + RECT rectOld; + bool bMoved = false; + + // get messages until capture lost or cancelled/accepted + for (;;) + { + MSG msg; + int const bRet = GetMessageW(&msg, nullptr, 0, 0); + SAL_WARN_IF(-1 == bRet, "embedserv", "GetMessageW failed: " << WindowsErrorString(GetLastError())); + if (-1 == bRet || 0 == bRet) + break; + + if (GetCapture() != hWnd) + break; + + switch (msg.message) + { + // handle movement/accept messages + case WM_LBUTTONUP: + case WM_MOUSEMOVE: + rectOld = m_rect; + // handle resize cases (and part of move) + if (px != nullptr) + *px = static_cast<int>(static_cast<short>(LOWORD(msg.lParam))) - xDiff; + if (py != nullptr) + *py = static_cast<int>(static_cast<short>(HIWORD(msg.lParam))) - yDiff; + + // handle move case + if (nHandle == hitMiddle) + { + m_rect.right = m_rect.left + nWidth; + m_rect.bottom = m_rect.top + nHeight; + } + // allow caller to adjust the rectangle if necessary + AdjustRect(nHandle,&m_rect); + + // only redraw and callback if the rect actually changed! + m_bFinalErase = (msg.message == WM_LBUTTONUP); + if (!EqualRect(&rectOld,&m_rect) || m_bFinalErase) + { + if (bMoved) + { + m_bErase = TRUE; + DrawTrackerRect(&rectOld,hWndClipTo,hDrawDC,hWnd); + } + OnChangedRect(rectOld); + if (msg.message != WM_LBUTTONUP) + bMoved = true; + } + if (m_bFinalErase) + goto ExitLoop; + + if (!EqualRect(&rectOld,&m_rect)) + { + m_bErase = FALSE; + DrawTrackerRect(&m_rect,hWndClipTo,hDrawDC,hWnd); + } + break; + + // handle cancel messages + case WM_KEYDOWN: + if (msg.wParam != VK_ESCAPE) + break; + [[fallthrough]]; + case WM_RBUTTONDOWN: + if (bMoved) + { + m_bErase = m_bFinalErase = TRUE; + DrawTrackerRect(&m_rect, hWndClipTo, hDrawDC, hWnd); + } + m_rect = rectSave; + goto ExitLoop; + + // just dispatch rest of the messages + default: + DispatchMessageW(&msg); + break; + } + } + + ExitLoop: + if (hWndClipTo != nullptr) + ReleaseDC(hWndClipTo,hDrawDC); + else + ReleaseDC(hWnd,hDrawDC); + ReleaseCapture(); + + // restore rect in case bMoved is still FALSE + if (!bMoved) + m_rect = rectSave; + m_bFinalErase = FALSE; + m_bErase = FALSE; + + // return TRUE only if rect has changed + return !EqualRect(&rectSave,&m_rect); +} + + +void Tracker::OnChangedRect(const RECT& /*rectOld*/) +{ +} + + +void Tracker::AdjustRect(int nHandle, LPRECT) +{ + if(nHandle == hitMiddle) + return; + + // convert the handle into locations within m_rect + int *px, *py; + GetModifyPointers(nHandle, &px, &py, nullptr, nullptr); + + // enforce minimum width + int nNewWidth = m_rect.right - m_rect.left; + int nAbsWidth = m_bAllowInvert ? abs(nNewWidth) : nNewWidth; + if (px != nullptr && nAbsWidth < m_sizeMin.cx) + { + nNewWidth = nAbsWidth != 0 ? nNewWidth / nAbsWidth : 1; + const AFX_RECTINFO* pRectInfo = + &afxRectInfo[px - reinterpret_cast<int*>(&m_rect)]; + *px = *reinterpret_cast<int*>(reinterpret_cast<BYTE*>(&m_rect) + pRectInfo->nOffsetAcross) + + nNewWidth * m_sizeMin.cx * -pRectInfo->nSignAcross; + } + + // enforce minimum height + int nNewHeight = m_rect.bottom - m_rect.top; + int nAbsHeight = m_bAllowInvert ? abs(nNewHeight) : nNewHeight; + if (py != nullptr && nAbsHeight < m_sizeMin.cy) + { + nNewHeight = nAbsHeight != 0 ? nNewHeight / nAbsHeight : 1; + const AFX_RECTINFO* pRectInfo = + &afxRectInfo[py - reinterpret_cast<int*>(&m_rect)]; + *py = *reinterpret_cast<int*>(reinterpret_cast<BYTE*>(&m_rect) + pRectInfo->nOffsetAcross) + + nNewHeight * m_sizeMin.cy * -pRectInfo->nSignAcross; + } +} + + +void Tracker::DrawTrackerRect( + LPRECT lpRect,HWND pWndClipTo,HDC pDC,HWND pWnd) +{ + // first, normalize the rectangle for drawing + RECT rect = *lpRect; + NormalizeRect(&rect); + + // convert to client coordinates + if (pWndClipTo != nullptr) + TransformRect(&rect,pWnd,pWndClipTo); + + SIZE size; + size.cx = 0; size.cy = 0; + if (!m_bFinalErase) + { + // otherwise, size depends on the style + if (m_nStyle & hatchedBorder) + { + size.cx = size.cy = std::max(1,GetHandleSize(&rect)-1); + InflateRect(&rect,size.cx,size.cy); + } + else + { + size.cx = 1; // CX_BORDER; + size.cy = 1; // CY_BORDER; + } + } + + // and draw it + if (m_bFinalErase || !m_bErase) + DrawDragRect(pDC,&rect,size,&m_rectLast,m_sizeLast); + + // remember last rectangles + m_rectLast = rect; + m_sizeLast = size; +} + + +void Tracker::Draw(HDC hDC) const +{ + // set initial DC state + SetMapMode(hDC,MM_TEXT); + SetViewportOrgEx(hDC,0, 0,nullptr); + SetWindowOrgEx(hDC,0, 0,nullptr); + + // get normalized rectangle + RECT rect = m_rect; + NormalizeRect(&rect); + + HPEN pOldPen = nullptr; + HBRUSH pOldBrush = nullptr; + HGDIOBJ pTemp; + int nOldROP; + + // draw lines + if ((m_nStyle & (dottedLine|solidLine)) != 0) + { + if (m_nStyle & dottedLine) + pOldPen = static_cast<HPEN>(SelectObject(hDC,afxBlackDottedPen)); + else + pOldPen = static_cast<HPEN>(SelectObject(hDC,reinterpret_cast<HGDIOBJ>(BLACK_PEN))); + pOldBrush = static_cast<HBRUSH>(SelectObject(hDC,reinterpret_cast<HGDIOBJ>(NULL_BRUSH))); + nOldROP = SetROP2(hDC,R2_COPYPEN); + InflateRect(&rect,+1, +1); // borders are one pixel outside + Rectangle(hDC,rect.left, rect.top, rect.right, rect.bottom); + SetROP2(hDC,nOldROP); + } + + // if hatchBrush is going to be used, need to unrealize it + if ((m_nStyle & (hatchInside|hatchedBorder)) != 0) + UnrealizeObject(static_cast<HGDIOBJ>(afxHatchBrush)); + + // hatch inside + if ((m_nStyle & hatchInside) != 0) + { + pTemp = SelectObject(hDC,reinterpret_cast<HGDIOBJ>(NULL_PEN)); + if (pOldPen == nullptr) + pOldPen = static_cast<HPEN>(pTemp); + pTemp = SelectObject(hDC,static_cast<HGDIOBJ>(afxHatchBrush)); + if (pOldBrush == nullptr) + pOldBrush = static_cast<HBRUSH>(pTemp); + SetBkMode(hDC,TRANSPARENT); + nOldROP = SetROP2(hDC,R2_MASKNOTPEN); + Rectangle(hDC,rect.left+1, rect.top+1, rect.right, rect.bottom); + SetROP2(hDC,nOldROP); + } + + // draw hatched border + if ((m_nStyle & hatchedBorder) != 0) + { + pTemp = SelectObject(hDC,static_cast<HGDIOBJ>(afxHatchBrush)); + if (pOldBrush == nullptr) + pOldBrush = static_cast<HBRUSH>(pTemp); + SetBkMode(hDC,OPAQUE); + RECT rectTrue; + GetTrueRect(&rectTrue); + PatBlt(hDC,rectTrue.left, rectTrue.top, rectTrue.right-rectTrue.left, + rect.top-rectTrue.top, 0x000F0001 /* Pn */); + PatBlt(hDC,rectTrue.left, rect.bottom, + rectTrue.right-rectTrue.left, + rectTrue.bottom-rect.bottom, 0x000F0001 /* Pn */); + PatBlt(hDC,rectTrue.left, rect.top, rect.left-rectTrue.left, + rect.bottom-rect.top, 0x000F0001 /* Pn */); + PatBlt(hDC,rect.right, rect.top, rectTrue.right-rect.right, + rect.bottom-rect.top, 0x000F0001 /* Pn */); + } + + // draw resize handles + if ((m_nStyle & (resizeInside|resizeOutside)) != 0) + { + UINT mask = GetHandleMask(); + HBRUSH hbrush = CreateSolidBrush(RGB(0,0,0)); + for (int i = 0; i < 8; ++i) + { + if (mask & (1<<i)) + { + GetHandleRect(static_cast<TrackerHit>(i), &rect); + // FillSolidRect(hDC,rect, RGB(0, 0, 0)); + FillRect(hDC,&rect,hbrush); + } + } + DeleteObject(hbrush); + } + + // cleanup pDC state + if (pOldPen != nullptr) + SelectObject(hDC,pOldPen); + if (pOldBrush != nullptr) + SelectObject(hDC,pOldBrush); + RestoreDC(hDC,-1); +} + + +void Tracker::GetHandleRect(int nHandle,RECT* pHandleRect) const +{ + // get normalized rectangle of the tracker + RECT rectT = m_rect; + NormalizeRect(&rectT); + if ((m_nStyle & (solidLine|dottedLine)) != 0) + InflateRect(&rectT,+1, +1); + + // since the rectangle itself was normalized, we also have to invert the + // resize handles. + nHandle = NormalizeHit(nHandle); + + // handle case of resize handles outside the tracker + int size = GetHandleSize(); + if (m_nStyle & resizeOutside) + InflateRect(&rectT,size-1, size-1); + + // calculate position of the resize handle + int nWidth = rectT.right - rectT.left; + int nHeight = rectT.bottom - rectT.top; + RECT rect; + const AFX_HANDLEINFO* pHandleInfo = &afxHandleInfo[nHandle]; + rect.left = *reinterpret_cast<int*>(reinterpret_cast<BYTE*>(&rectT) + pHandleInfo->nOffsetX); + rect.top = *reinterpret_cast<int*>(reinterpret_cast<BYTE*>(&rectT) + pHandleInfo->nOffsetY); + rect.left += size * pHandleInfo->nHandleX; + rect.top += size * pHandleInfo->nHandleY; + rect.left += pHandleInfo->nCenterX * (nWidth - size) / 2; + rect.top += pHandleInfo->nCenterY * (nHeight - size) / 2; + rect.right = rect.left + size; + rect.bottom = rect.top + size; + + *pHandleRect = rect; +} + + +int Tracker::GetHandleSize(LPRECT lpRect) const +{ + LPCRECT rect = lpRect == nullptr ? &m_rect : lpRect; + + int size = m_nHandleSize; + if (!(m_nStyle & resizeOutside)) + { + // make sure size is small enough for the size of the rect + int sizeMax = std::min(abs(rect->right - rect->left), + abs(rect->bottom - rect->top)); + if (size * 2 > sizeMax) + size = sizeMax / 2; + } + return size; +} + + +UINT Tracker::GetHandleMask() const +{ + UINT mask = 0x0F; // always have 4 corner handles + int size = m_nHandleSize*3; + if (abs(m_rect.right - m_rect.left) - size > 4) + mask |= 0x50; + if (abs(m_rect.bottom - m_rect.top) - size > 4) + mask |= 0xA0; + return mask; +} + + +void Tracker::GetTrueRect(LPRECT lpTrueRect) const +{ + RECT rect = m_rect; + NormalizeRect(&rect); + int nInflateBy = 0; + if ((m_nStyle & (resizeOutside|hatchedBorder)) != 0) + nInflateBy += GetHandleSize() - 1; + if ((m_nStyle & (solidLine|dottedLine)) != 0) + ++nInflateBy; + InflateRect(&rect,nInflateBy, nInflateBy); + *lpTrueRect = rect; +} + + +int Tracker::NormalizeHit(int nHandle) const +{ + if (nHandle == hitMiddle || nHandle == hitNothing) + return nHandle; + const AFX_HANDLEINFO* pHandleInfo = &afxHandleInfo[nHandle]; + if (m_rect.right - m_rect.left < 0) + { + nHandle = static_cast<TrackerHit>(pHandleInfo->nInvertX); + pHandleInfo = &afxHandleInfo[nHandle]; + } + if (m_rect.bottom - m_rect.top < 0) + nHandle = static_cast<TrackerHit>(pHandleInfo->nInvertY); + return nHandle; +} + + +int Tracker::HitTestHandles(POINT point) const +{ + RECT rect; + UINT mask = GetHandleMask(); + + // see if hit anywhere inside the tracker + GetTrueRect(&rect); + if (!PtInRect(&rect,point)) + return hitNothing; // totally missed + + // see if we hit a handle + for (int i = 0; i < 8; ++i) + { + if (mask & (1<<i)) + { + GetHandleRect(static_cast<TrackerHit>(i), &rect); + if (PtInRect(&rect,point)) + return static_cast<TrackerHit>(i); + } + } + + // last of all, check for non-hit outside of object, between resize handles + if ((m_nStyle & hatchedBorder) == 0) + { + rect = m_rect; + NormalizeRect(&rect); + if ((m_nStyle & (dottedLine|solidLine)) != 0) + InflateRect(&rect,+1, +1); + if (!PtInRect(&rect,point)) + return hitNothing; // must have been between resize handles + } + return hitMiddle; // no handle hit, but hit object (or object border) +} + + +void Tracker::GetModifyPointers( + int nHandle, int** ppx, int** ppy, int* px, int* py) +{ + if (nHandle == hitMiddle) + nHandle = hitTopLeft; // same as hitting top-left + + *ppx = nullptr; + *ppy = nullptr; + + // fill in the part of the rect that this handle modifies + // (Note: handles that map to themselves along a given axis when that + // axis is inverted don't modify the value on that axis) + + const AFX_HANDLEINFO* pHandleInfo = &afxHandleInfo[nHandle]; + if (pHandleInfo->nInvertX != nHandle) + { + *ppx = reinterpret_cast<int*>(reinterpret_cast<BYTE*>(&m_rect) + pHandleInfo->nOffsetX); + if (px != nullptr) + *px = **ppx; + } + else + { + // middle handle on X axis + if (px != nullptr) + *px = m_rect.left + (m_rect.left-m_rect.right) / 2; + } + if (pHandleInfo->nInvertY != nHandle) + { + *ppy = reinterpret_cast<int*>(reinterpret_cast<BYTE*>(&m_rect) + pHandleInfo->nOffsetY); + if (py != nullptr) + *py = **ppy; + } + else + { + // middle handle on Y axis + if (py != nullptr) + *py = m_rect.top + (m_rect.top-m_rect.bottom) / 2; + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |