/* -*- 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 .
*/
// 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());
}
}
// 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 ------------------------------
// 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; elseif ( aURL.Complete == URL_CLOSEWIN )
m_eOperation = E_CLOSE_WIN; elseif ( aURL.Complete == URL_CLOSEFRAME )
m_eOperation = E_CLOSE_FRAME; else
{
aWriteLock.clear(); // <- SAFE ------------------------------
if (m_pSysWindow && m_pSysWindow->GetCloseHdl().IsSet())
{ // The closing frame has its own close handler. Call it instead.
m_pSysWindow->GetCloseHdl().Call(*m_pSysWindow);
// 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.
/** @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;
// 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);
// 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! elseif (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. elseif (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;
} elseif( SvtModuleOptions().IsModuleInstalled(SvtModuleOptions::EModule::STARTMODULE) )
bEstablishBackingMode = true; elseif (bHasActiveConnections)
bCloseFrame = true; else
bTerminateApp = true;
}
}
}
// Do it now ... bool bSuccess = false; if (bCloseFrame)
bSuccess = implts_closeFrame(); elseif (bEstablishBackingMode) #ifdefined MACOSX
{ // on mac close down, quickstarter keeps the process alive // however if someone has shut down the quickstarter // behave as any other platform
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()) returntrue;
// 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;
}
size_t c = aCheck.m_lModelFrames.size();
size_t i = 0; for (i=0; i<c; ++i)
{ if (!fpf::closeIt(aCheck.m_lModelFrames[i])) returnfalse;
}
}
// 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) returnfalse;
}
}
// 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. returntrue;
}
// frame already dead ? => so it's closed ... it's closed ... if ( ! xFrame.is() ) returntrue;
// 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)) returnfalse;
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;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.