6f1508f4de
and cid#1555770 COPY_INSTEAD_OF_MOVE cid#1555788 COPY_INSTEAD_OF_MOVE cid#1555789 COPY_INSTEAD_OF_MOVE cid#1555798 COPY_INSTEAD_OF_MOVE cid#1555816 COPY_INSTEAD_OF_MOVE cid#1555822 COPY_INSTEAD_OF_MOVE cid#1555835 COPY_INSTEAD_OF_MOVE cid#1555845 COPY_INSTEAD_OF_MOVE cid#1555859 COPY_INSTEAD_OF_MOVE cid#1555864 COPY_INSTEAD_OF_MOVE cid#1555868 COPY_INSTEAD_OF_MOVE cid#1555892 COPY_INSTEAD_OF_MOVE cid#1555896 COPY_INSTEAD_OF_MOVE cid#1555921 COPY_INSTEAD_OF_MOVE cid#1555932 COPY_INSTEAD_OF_MOVE cid#1555935 COPY_INSTEAD_OF_MOVE cid#1555944 COPY_INSTEAD_OF_MOVE cid#1555952 COPY_INSTEAD_OF_MOVE cid#1555985 COPY_INSTEAD_OF_MOVE cid#1556024 COPY_INSTEAD_OF_MOVE cid#1556038 COPY_INSTEAD_OF_MOVE cid#1556042 COPY_INSTEAD_OF_MOVE cid#1556044 COPY_INSTEAD_OF_MOVE cid#1556060 COPY_INSTEAD_OF_MOVE cid#1556083 COPY_INSTEAD_OF_MOVE cid#1556085 COPY_INSTEAD_OF_MOVE cid#1556090 COPY_INSTEAD_OF_MOVE cid#1556136 COPY_INSTEAD_OF_MOVE cid#1556157 COPY_INSTEAD_OF_MOVE cid#1556159 COPY_INSTEAD_OF_MOVE cid#1556172 COPY_INSTEAD_OF_MOVE cid#1556179 COPY_INSTEAD_OF_MOVE cid#1556187 COPY_INSTEAD_OF_MOVE cid#1556255 COPY_INSTEAD_OF_MOVE cid#1556256 COPY_INSTEAD_OF_MOVE cid#1556266 COPY_INSTEAD_OF_MOVE cid#1556275 COPY_INSTEAD_OF_MOVE cid#1556290 COPY_INSTEAD_OF_MOVE cid#1556294 COPY_INSTEAD_OF_MOVE cid#1556301 COPY_INSTEAD_OF_MOVE cid#1556311 COPY_INSTEAD_OF_MOVE cid#1556318 COPY_INSTEAD_OF_MOVE cid#1556326 COPY_INSTEAD_OF_MOVE cid#1556369 COPY_INSTEAD_OF_MOVE cid#1556374 COPY_INSTEAD_OF_MOVE cid#1556387 COPY_INSTEAD_OF_MOVE cid#1556388 COPY_INSTEAD_OF_MOVE cid#1556417 COPY_INSTEAD_OF_MOVE cid#1556425 COPY_INSTEAD_OF_MOVE cid#1556435 COPY_INSTEAD_OF_MOVE cid#1556495 COPY_INSTEAD_OF_MOVE cid#1556497 COPY_INSTEAD_OF_MOVE cid#1556501 COPY_INSTEAD_OF_MOVE cid#1556503 COPY_INSTEAD_OF_MOVE cid#1556520 COPY_INSTEAD_OF_MOVE cid#1556523 COPY_INSTEAD_OF_MOVE cid#1556562 COPY_INSTEAD_OF_MOVE cid#1556573 COPY_INSTEAD_OF_MOVE cid#1556576 COPY_INSTEAD_OF_MOVE cid#1556598 COPY_INSTEAD_OF_MOVE cid#1556615 COPY_INSTEAD_OF_MOVE cid#1556626 COPY_INSTEAD_OF_MOVE cid#1556671 COPY_INSTEAD_OF_MOVE cid#1556689 COPY_INSTEAD_OF_MOVE cid#1556701 COPY_INSTEAD_OF_MOVE cid#1556713 COPY_INSTEAD_OF_MOVE cid#1556758 COPY_INSTEAD_OF_MOVE cid#1556759 COPY_INSTEAD_OF_MOVE cid#1556788 COPY_INSTEAD_OF_MOVE cid#1556811 COPY_INSTEAD_OF_MOVE cid#1556821 COPY_INSTEAD_OF_MOVE cid#1556824 COPY_INSTEAD_OF_MOVE cid#1556825 COPY_INSTEAD_OF_MOVE cid#1556862 COPY_INSTEAD_OF_MOVE Change-Id: I4925a79688a983bb07252600430039ec0bcb75b8 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/175678 Reviewed-by: Caolán McNamara <caolan.mcnamara@collabora.com> Tested-by: Jenkins
614 lines
24 KiB
C++
614 lines
24 KiB
C++
/* -*- 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 <dispatch/closedispatcher.hxx>
|
|
#include <pattern/frame.hxx>
|
|
#include <framework/framelistanalyzer.hxx>
|
|
#include <services.h>
|
|
|
|
#include <com/sun/star/bridge/BridgeFactory.hpp>
|
|
#include <com/sun/star/bridge/XBridgeFactory2.hpp>
|
|
#include <com/sun/star/frame/Desktop.hpp>
|
|
#include <com/sun/star/frame/DispatchResultState.hpp>
|
|
#include <com/sun/star/frame/XController.hpp>
|
|
#include <com/sun/star/frame/CommandGroup.hpp>
|
|
#include <com/sun/star/frame/StartModule.hpp>
|
|
#include <com/sun/star/lang/DisposedException.hpp>
|
|
#include <com/sun/star/awt/XTopWindow.hpp>
|
|
#include <com/sun/star/document/XActionLockable.hpp>
|
|
#include <com/sun/star/beans/XFastPropertySet.hpp>
|
|
#include <toolkit/helper/vclunohelper.hxx>
|
|
|
|
#include <osl/diagnose.h>
|
|
#include <utility>
|
|
#include <vcl/window.hxx>
|
|
#include <vcl/svapp.hxx>
|
|
#include <vcl/syswin.hxx>
|
|
#include <unotools/moduleoptions.hxx>
|
|
#include <o3tl/string_view.hxx>
|
|
|
|
using namespace com::sun::star;
|
|
|
|
namespace framework{
|
|
|
|
#ifdef fpf
|
|
#error "Who uses \"fpf\" as define. It will overwrite my namespace alias ..."
|
|
#endif
|
|
namespace fpf = ::framework::pattern::frame;
|
|
|
|
constexpr OUString URL_CLOSEDOC = u".uno:CloseDoc"_ustr;
|
|
constexpr OUString URL_CLOSEWIN = u".uno:CloseWin"_ustr;
|
|
const char URL_CLOSEFRAME[] = ".uno:CloseFrame";
|
|
|
|
CloseDispatcher::CloseDispatcher(css::uno::Reference< css::uno::XComponentContext > xContext ,
|
|
const css::uno::Reference< css::frame::XFrame >& xFrame ,
|
|
std::u16string_view sTarget)
|
|
: m_xContext(std::move(xContext))
|
|
, m_aAsyncCallback(
|
|
new vcl::EventPoster(LINK(this, CloseDispatcher, impl_asyncCallback)))
|
|
, m_eOperation(E_CLOSE_DOC)
|
|
, m_pSysWindow(nullptr)
|
|
{
|
|
uno::Reference<frame::XFrame> xTarget = static_impl_searchRightTargetFrame(xFrame, sTarget);
|
|
m_xCloseFrame = xTarget;
|
|
|
|
// Try to retrieve the system window instance of the closing frame.
|
|
uno::Reference<awt::XWindow> xWindow = xTarget->getContainerWindow();
|
|
if (xWindow.is())
|
|
{
|
|
VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow(xWindow);
|
|
if (pWindow->IsSystemWindow())
|
|
m_pSysWindow = dynamic_cast<SystemWindow*>(pWindow.get());
|
|
}
|
|
}
|
|
|
|
CloseDispatcher::~CloseDispatcher()
|
|
{
|
|
SolarMutexGuard g;
|
|
m_aAsyncCallback.reset();
|
|
m_pSysWindow.reset();
|
|
}
|
|
|
|
void SAL_CALL CloseDispatcher::dispatch(const css::util::URL& aURL ,
|
|
const css::uno::Sequence< css::beans::PropertyValue >& lArguments)
|
|
{
|
|
dispatchWithNotification(aURL, lArguments, css::uno::Reference< css::frame::XDispatchResultListener >());
|
|
}
|
|
|
|
css::uno::Sequence< sal_Int16 > SAL_CALL CloseDispatcher::getSupportedCommandGroups()
|
|
{
|
|
return css::uno::Sequence< sal_Int16 >{css::frame::CommandGroup::VIEW, css::frame::CommandGroup::DOCUMENT};
|
|
}
|
|
|
|
css::uno::Sequence< css::frame::DispatchInformation > SAL_CALL CloseDispatcher::getConfigurableDispatchInformation(sal_Int16 nCommandGroup)
|
|
{
|
|
if (nCommandGroup == css::frame::CommandGroup::VIEW)
|
|
{
|
|
/* Attention: Don't add .uno:CloseFrame here. Because it's not really
|
|
a configurable feature ... and further it does not have
|
|
a valid UIName entry inside the GenericCommands.xcu ... */
|
|
css::uno::Sequence< css::frame::DispatchInformation > lViewInfos{
|
|
{ URL_CLOSEWIN, css::frame::CommandGroup::VIEW }
|
|
};
|
|
return lViewInfos;
|
|
}
|
|
else if (nCommandGroup == css::frame::CommandGroup::DOCUMENT)
|
|
{
|
|
css::uno::Sequence< css::frame::DispatchInformation > lDocInfos{
|
|
{ URL_CLOSEDOC, css::frame::CommandGroup::DOCUMENT }
|
|
};
|
|
return lDocInfos;
|
|
}
|
|
|
|
return css::uno::Sequence< css::frame::DispatchInformation >();
|
|
}
|
|
|
|
void SAL_CALL CloseDispatcher::addStatusListener(const css::uno::Reference< css::frame::XStatusListener >& /*xListener*/,
|
|
const css::util::URL& /*aURL*/ )
|
|
{
|
|
}
|
|
|
|
void SAL_CALL CloseDispatcher::removeStatusListener(const css::uno::Reference< css::frame::XStatusListener >& /*xListener*/,
|
|
const css::util::URL& /*aURL*/ )
|
|
{
|
|
}
|
|
|
|
void SAL_CALL CloseDispatcher::dispatchWithNotification(const css::util::URL& aURL ,
|
|
const css::uno::Sequence< css::beans::PropertyValue >& lArguments,
|
|
const css::uno::Reference< css::frame::XDispatchResultListener >& xListener )
|
|
{
|
|
// SAFE -> ----------------------------------
|
|
SolarMutexClearableGuard aWriteLock;
|
|
|
|
// This reference indicates, that we were already called before and
|
|
// our asynchronous process was not finished yet.
|
|
// We have to reject double calls. Otherwise we risk,
|
|
// that we try to close an already closed resource...
|
|
// And it is no problem to do nothing then. The UI user will try it again, if
|
|
// non of these jobs was successful.
|
|
if (m_xSelfHold.is())
|
|
{
|
|
aWriteLock.clear();
|
|
// <- SAFE ------------------------------
|
|
|
|
implts_notifyResultListener(
|
|
xListener,
|
|
css::frame::DispatchResultState::DONTKNOW,
|
|
css::uno::Any());
|
|
return;
|
|
}
|
|
|
|
// First we have to check, if this dispatcher is used right. Means if valid URLs are used.
|
|
// If not - we have to break this operation. But an optional listener must be informed.
|
|
// BTW: We save the information about the requested operation. Because
|
|
// we need it later.
|
|
if ( aURL.Complete == URL_CLOSEDOC )
|
|
m_eOperation = E_CLOSE_DOC;
|
|
else if ( aURL.Complete == URL_CLOSEWIN )
|
|
m_eOperation = E_CLOSE_WIN;
|
|
else if ( aURL.Complete == URL_CLOSEFRAME )
|
|
m_eOperation = E_CLOSE_FRAME;
|
|
else
|
|
{
|
|
aWriteLock.clear();
|
|
// <- SAFE ------------------------------
|
|
|
|
implts_notifyResultListener(
|
|
xListener,
|
|
css::frame::DispatchResultState::FAILURE,
|
|
css::uno::Any());
|
|
return;
|
|
}
|
|
|
|
if (m_pSysWindow && m_pSysWindow->GetCloseHdl().IsSet())
|
|
{
|
|
// The closing frame has its own close handler. Call it instead.
|
|
m_pSysWindow->GetCloseHdl().Call(*m_pSysWindow);
|
|
|
|
aWriteLock.clear();
|
|
// <- SAFE ------------------------------
|
|
|
|
implts_notifyResultListener(
|
|
xListener,
|
|
css::frame::DispatchResultState::SUCCESS,
|
|
css::uno::Any());
|
|
|
|
return;
|
|
}
|
|
|
|
// OK - URLs are the right ones.
|
|
// But we can't execute synchronously :-)
|
|
// May we are called from a generic key-input handler,
|
|
// which isn't aware that this call kill its own environment...
|
|
// Do it asynchronous everytimes!
|
|
|
|
// But don't forget to hold ourselves alive.
|
|
// We are called back from an environment, which doesn't know a uno reference.
|
|
// They call us back by using our c++ interface.
|
|
|
|
m_xResultListener = xListener;
|
|
m_xSelfHold.set(static_cast< ::cppu::OWeakObject* >(this), css::uno::UNO_QUERY);
|
|
|
|
aWriteLock.clear();
|
|
// <- SAFE ----------------------------------
|
|
|
|
bool bIsSynchron = false;
|
|
for (const css::beans::PropertyValue& rArg : lArguments )
|
|
{
|
|
if ( rArg.Name == "SynchronMode" )
|
|
{
|
|
rArg.Value >>= bIsSynchron;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( bIsSynchron )
|
|
impl_asyncCallback(nullptr);
|
|
else
|
|
{
|
|
SolarMutexGuard g;
|
|
m_aAsyncCallback->Post();
|
|
}
|
|
}
|
|
|
|
/**
|
|
@short asynchronous callback
|
|
@descr We start all actions inside this object asynchronous
|
|
(see comments there).
|
|
Now we do the following:
|
|
- close all views to the same document, if needed and possible
|
|
- make the current frame empty
|
|
! This step is necessary to handle errors during closing the
|
|
document inside the frame. May the document shows a dialog and
|
|
the user ignore it. Then the state of the office can be changed
|
|
during we try to close frame and document.
|
|
- check the environment (means count open frames - excluding our
|
|
current one)
|
|
- decide then, if we must close this frame only, establish the backing mode
|
|
or shutdown the whole application.
|
|
*/
|
|
IMPL_LINK_NOARG(CloseDispatcher, impl_asyncCallback, LinkParamNone*, void)
|
|
{
|
|
try
|
|
{
|
|
|
|
// Allow calling of XController->suspend() everytimes.
|
|
// Dispatch is an UI functionality. We implement such dispatch object here.
|
|
// And further XController->suspend() was designed to bring an UI ...
|
|
bool bControllerSuspended = false;
|
|
|
|
bool bCloseAllViewsToo;
|
|
EOperation eOperation;
|
|
css::uno::Reference< css::uno::XComponentContext > xContext;
|
|
css::uno::Reference< css::frame::XFrame > xCloseFrame;
|
|
css::uno::Reference< css::frame::XDispatchResultListener > xListener;
|
|
{
|
|
SolarMutexGuard g;
|
|
|
|
// Closing of all views, related to the same document, is allowed
|
|
// only if the dispatched URL was ".uno:CloseDoc"!
|
|
bCloseAllViewsToo = (m_eOperation == E_CLOSE_DOC);
|
|
|
|
eOperation = m_eOperation;
|
|
xContext = m_xContext;
|
|
xCloseFrame.set(m_xCloseFrame.get(), css::uno::UNO_QUERY);
|
|
xListener = m_xResultListener;
|
|
}
|
|
|
|
// frame already dead ?!
|
|
// Nothing to do !
|
|
if (! xCloseFrame.is())
|
|
return;
|
|
|
|
bool bCloseFrame = false;
|
|
bool bEstablishBackingMode = false;
|
|
bool bTerminateApp = false;
|
|
|
|
// Analyze the environment a first time.
|
|
// If we found some special cases, we can
|
|
// make some decisions earlier!
|
|
css::uno::Reference< css::frame::XFramesSupplier > xDesktop( css::frame::Desktop::create(xContext), css::uno::UNO_QUERY_THROW);
|
|
FrameListAnalyzer aCheck1(xDesktop, xCloseFrame, FrameAnalyzerFlags::Help | FrameAnalyzerFlags::BackingComponent);
|
|
|
|
// Check for existing UNO connections.
|
|
// NOTE: There is a race between checking this and connections being created/destroyed before
|
|
// we close the frame / terminate the app.
|
|
css::uno::Reference<css::bridge::XBridgeFactory2> bridgeFac( css::bridge::BridgeFactory::create(xContext) );
|
|
bool bHasActiveConnections = bridgeFac->getExistingBridges().hasElements();
|
|
|
|
// a) If the current frame (where the close dispatch was requested for) does not have
|
|
// any parent frame ... it will close this frame only. Such frame isn't part of the
|
|
// global desktop tree ... and such frames are used as "implementation details" only.
|
|
// E.g. the live previews of our wizards doing such things. And then the owner of the frame
|
|
// is responsible for closing the application or accepting closing of the application
|
|
// by others.
|
|
if ( ! xCloseFrame->getCreator().is())
|
|
bCloseFrame = true;
|
|
|
|
// b) The help window can't disagree with any request.
|
|
// Because it doesn't implement a controller - it uses a window only.
|
|
// Further it can't be the last open frame - if we do all other things
|
|
// right inside this CloseDispatcher implementation.
|
|
// => close it!
|
|
else if (aCheck1.m_bReferenceIsHelp)
|
|
bCloseFrame = true;
|
|
|
|
// c) If we are already in "backing mode", we terminate the application, if no active UNO connections are found.
|
|
// If there is an active UNO connection, we only close the frame and leave the application alive.
|
|
// It doesn't matter, how many other frames (can be the help or hidden frames only) are open then.
|
|
else if (aCheck1.m_bReferenceIsBacking) {
|
|
if (bHasActiveConnections)
|
|
bCloseFrame = true;
|
|
else
|
|
bTerminateApp = true;
|
|
}
|
|
|
|
// d) Otherwise we have to: close all views to the same document, close the
|
|
// document inside our own frame and decide then again, what has to be done!
|
|
else
|
|
{
|
|
if (implts_prepareFrameForClosing(m_xCloseFrame, bCloseAllViewsToo, bControllerSuspended))
|
|
{
|
|
// OK; this frame is empty now.
|
|
// Check the environment again to decide, what is the next step.
|
|
FrameListAnalyzer aCheck2(xDesktop, xCloseFrame, FrameAnalyzerFlags::All);
|
|
|
|
// c1) there is as minimum 1 frame open, which is visible and contains a document
|
|
// different from our one. And it's not the help!
|
|
// (tdf#30920 consider that closing a frame which is not the backing window (start center) while there is
|
|
// another frame that is the backing window open only closes the frame, and not terminate the app, so
|
|
// closing the license frame doesn't terminate the app if launched from the start center)
|
|
// => close our frame only - nothing else.
|
|
if (!aCheck2.m_lOtherVisibleFrames.empty() || (!aCheck2.m_bReferenceIsBacking && aCheck2.m_xBackingComponent.is()))
|
|
bCloseFrame = true;
|
|
|
|
// c2) if we close the current view ... but not all other views
|
|
// to the same document, we must close the current frame only!
|
|
// Because implts_closeView() suspended this view only - does not
|
|
// close the frame.
|
|
if (
|
|
(!bCloseAllViewsToo ) &&
|
|
(!aCheck2.m_lModelFrames.empty())
|
|
)
|
|
bCloseFrame = true;
|
|
|
|
else
|
|
// c3) there is no other (visible) frame open ...
|
|
// The help module will be ignored everytimes!
|
|
// But we have to decide if we must terminate the
|
|
// application or establish the backing mode now.
|
|
// And that depends from the dispatched URL ...
|
|
{
|
|
if (eOperation == E_CLOSE_FRAME)
|
|
{
|
|
if (bHasActiveConnections)
|
|
bCloseFrame = true;
|
|
else
|
|
bTerminateApp = true;
|
|
}
|
|
else if( SvtModuleOptions().IsModuleInstalled(SvtModuleOptions::EModule::STARTMODULE) )
|
|
bEstablishBackingMode = true;
|
|
else if (bHasActiveConnections)
|
|
bCloseFrame = true;
|
|
else
|
|
bTerminateApp = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Do it now ...
|
|
bool bSuccess = false;
|
|
if (bCloseFrame)
|
|
bSuccess = implts_closeFrame();
|
|
else if (bEstablishBackingMode)
|
|
#if defined MACOSX
|
|
{
|
|
// on mac close down, quickstarter keeps the process alive
|
|
// however if someone has shut down the quickstarter
|
|
// behave as any other platform
|
|
|
|
bool bQuickstarterRunning = false;
|
|
// get quickstart service
|
|
try
|
|
{
|
|
css::uno::Reference< css::beans::XFastPropertySet > xSet( xContext->getServiceManager()->createInstanceWithContext(IMPLEMENTATIONNAME_QUICKLAUNCHER, xContext), css::uno::UNO_QUERY_THROW );
|
|
css::uno::Any aVal( xSet->getFastPropertyValue( 0 ) );
|
|
bool bState = false;
|
|
if( aVal >>= bState )
|
|
bQuickstarterRunning = bState;
|
|
}
|
|
catch( const css::uno::Exception& )
|
|
{
|
|
}
|
|
bSuccess = bQuickstarterRunning ? implts_terminateApplication() : implts_establishBackingMode();
|
|
}
|
|
#else
|
|
bSuccess = implts_establishBackingMode();
|
|
#endif
|
|
else if (bTerminateApp)
|
|
bSuccess = implts_terminateApplication();
|
|
|
|
if ( ! bSuccess && bControllerSuspended )
|
|
{
|
|
css::uno::Reference< css::frame::XController > xController = xCloseFrame->getController();
|
|
if (xController.is())
|
|
xController->suspend(false);
|
|
}
|
|
|
|
// inform listener
|
|
sal_Int16 nState = css::frame::DispatchResultState::FAILURE;
|
|
if (bSuccess)
|
|
nState = css::frame::DispatchResultState::SUCCESS;
|
|
implts_notifyResultListener(xListener, nState, css::uno::Any());
|
|
|
|
SolarMutexGuard g;
|
|
// This method was called asynchronous from our main thread by using a pointer.
|
|
// We reached this method only, by using a reference to ourself :-)
|
|
// Further this member is used to detect still running and not yet finished
|
|
// asynchronous operations. So it's time now to release this reference.
|
|
// But hold it temp alive. Otherwise we die before we can finish this method really :-))
|
|
css::uno::Reference< css::uno::XInterface > xTempHold = m_xSelfHold;
|
|
m_xSelfHold.clear();
|
|
m_xResultListener.clear();
|
|
}
|
|
catch(const css::lang::DisposedException&)
|
|
{
|
|
}
|
|
}
|
|
|
|
bool CloseDispatcher::implts_prepareFrameForClosing(const css::uno::Reference< css::frame::XFrame >& xFrame,
|
|
bool bCloseAllOtherViewsToo,
|
|
bool& bControllerSuspended )
|
|
{
|
|
// Frame already dead ... so this view is closed ... is closed ... is ... .-)
|
|
if (! xFrame.is())
|
|
return true;
|
|
|
|
// Close all views to the same document ... if forced to do so.
|
|
// But don't touch our own frame here!
|
|
// We must do so ... because the may be following controller->suspend()
|
|
// will show the "save/discard/cancel" dialog for the last view only!
|
|
if (bCloseAllOtherViewsToo)
|
|
{
|
|
css::uno::Reference< css::uno::XComponentContext > xContext;
|
|
{
|
|
SolarMutexGuard g;
|
|
xContext = m_xContext;
|
|
}
|
|
|
|
css::uno::Reference< css::frame::XFramesSupplier > xDesktop( css::frame::Desktop::create( xContext ), css::uno::UNO_QUERY_THROW);
|
|
FrameListAnalyzer aCheck(xDesktop, xFrame, FrameAnalyzerFlags::All);
|
|
|
|
size_t c = aCheck.m_lModelFrames.size();
|
|
size_t i = 0;
|
|
for (i=0; i<c; ++i)
|
|
{
|
|
if (!fpf::closeIt(aCheck.m_lModelFrames[i]))
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Inform user about modified documents or still running jobs (e.g. printing).
|
|
{
|
|
css::uno::Reference< css::frame::XController > xController = xFrame->getController();
|
|
if (xController.is()) // some views don't uses a controller .-( (e.g. the help window)
|
|
{
|
|
bControllerSuspended = xController->suspend(true);
|
|
if (! bControllerSuspended)
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// don't remove the component really by e.g. calling setComponent(null, null).
|
|
// It's enough to suspend the controller.
|
|
// If we close the frame later this controller doesn't show the same dialog again.
|
|
return true;
|
|
}
|
|
|
|
bool CloseDispatcher::implts_closeFrame()
|
|
{
|
|
css::uno::Reference< css::frame::XFrame > xFrame;
|
|
{
|
|
SolarMutexGuard g;
|
|
xFrame.set(m_xCloseFrame.get(), css::uno::UNO_QUERY);
|
|
}
|
|
|
|
// frame already dead ? => so it's closed ... it's closed ...
|
|
if ( ! xFrame.is() )
|
|
return true;
|
|
|
|
// don't deliver ownership; our "UI user" will try it again if it failed.
|
|
// OK - he will get an empty frame then. But normally an empty frame
|
|
// should be closeable always :-)
|
|
if (!fpf::closeIt(xFrame))
|
|
return false;
|
|
|
|
{
|
|
SolarMutexGuard g;
|
|
m_xCloseFrame.clear();
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool CloseDispatcher::implts_establishBackingMode()
|
|
{
|
|
css::uno::Reference< css::uno::XComponentContext > xContext;
|
|
css::uno::Reference< css::frame::XFrame > xFrame;
|
|
{
|
|
SolarMutexGuard g;
|
|
xContext = m_xContext;
|
|
xFrame.set(m_xCloseFrame.get(), css::uno::UNO_QUERY);
|
|
}
|
|
|
|
if (!xFrame.is())
|
|
return false;
|
|
|
|
css::uno::Reference < css::document::XActionLockable > xLock( xFrame, css::uno::UNO_QUERY );
|
|
if ( xLock.is() && xLock->isActionLocked() )
|
|
return false;
|
|
|
|
css::uno::Reference< css::awt::XWindow > xContainerWindow = xFrame->getContainerWindow();
|
|
|
|
css::uno::Reference< css::frame::XController > xStartModule = css::frame::StartModule::createWithParentWindow(
|
|
xContext, xContainerWindow);
|
|
|
|
// Attention: You MUST(!) call setComponent() before you call attachFrame().
|
|
css::uno::Reference< css::awt::XWindow > xBackingWin(xStartModule, css::uno::UNO_QUERY);
|
|
xFrame->setComponent(xBackingWin, xStartModule);
|
|
xStartModule->attachFrame(xFrame);
|
|
xContainerWindow->setVisible(true);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool CloseDispatcher::implts_terminateApplication()
|
|
{
|
|
css::uno::Reference< css::uno::XComponentContext > xContext;
|
|
{
|
|
SolarMutexGuard g;
|
|
xContext = m_xContext;
|
|
}
|
|
|
|
css::uno::Reference< css::frame::XDesktop2 > xDesktop = css::frame::Desktop::create( xContext );
|
|
|
|
return xDesktop->terminate();
|
|
}
|
|
|
|
void CloseDispatcher::implts_notifyResultListener(const css::uno::Reference< css::frame::XDispatchResultListener >& xListener,
|
|
sal_Int16 nState ,
|
|
const css::uno::Any& aResult )
|
|
{
|
|
if (!xListener.is())
|
|
return;
|
|
|
|
css::frame::DispatchResultEvent aEvent(
|
|
css::uno::Reference< css::uno::XInterface >(static_cast< ::cppu::OWeakObject* >(this), css::uno::UNO_QUERY),
|
|
nState,
|
|
aResult);
|
|
|
|
xListener->dispatchFinished(aEvent);
|
|
}
|
|
|
|
css::uno::Reference< css::frame::XFrame > CloseDispatcher::static_impl_searchRightTargetFrame(const css::uno::Reference< css::frame::XFrame >& xFrame ,
|
|
std::u16string_view sTarget)
|
|
{
|
|
if (o3tl::equalsIgnoreAsciiCase(sTarget, u"_self"))
|
|
return xFrame;
|
|
|
|
OSL_ENSURE(sTarget.empty(), "CloseDispatch used for unexpected target. Magic things will happen now .-)");
|
|
|
|
css::uno::Reference< css::frame::XFrame > xTarget = xFrame;
|
|
while(true)
|
|
{
|
|
// a) top frames will be closed
|
|
if (xTarget->isTop())
|
|
return xTarget;
|
|
|
|
// b) even child frame containing top level windows (e.g. query designer of database) will be closed
|
|
css::uno::Reference< css::awt::XWindow > xWindow = xTarget->getContainerWindow();
|
|
css::uno::Reference< css::awt::XTopWindow > xTopWindowCheck(xWindow, css::uno::UNO_QUERY);
|
|
if (xTopWindowCheck.is())
|
|
{
|
|
// b1) Note: Toolkit interface XTopWindow sometimes is used by real VCL-child-windows also .-)
|
|
// Be sure that these window is really a "top system window".
|
|
// Attention ! Checking Window->GetParent() isn't the right approach here.
|
|
// Because sometimes VCL create "implicit border windows" as parents even we created
|
|
// a simple XWindow using the toolkit only .-(
|
|
SolarMutexGuard aSolarLock;
|
|
VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow( xWindow );
|
|
if ( pWindow && pWindow->IsSystemWindow() )
|
|
return xTarget;
|
|
}
|
|
|
|
// c) try to find better results on parent frame
|
|
// If no parent frame exists (because this frame is used outside the desktop tree)
|
|
// the given frame must be used directly.
|
|
css::uno::Reference< css::frame::XFrame > xParent = xTarget->getCreator();
|
|
if ( ! xParent.is())
|
|
return xTarget;
|
|
|
|
// c1) check parent frame inside next loop ...
|
|
xTarget = std::move(xParent);
|
|
}
|
|
}
|
|
|
|
} // namespace framework
|
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|