fix baseline build (old cairo) - 'cairo_rectangle_int_t' does not name a type
[LibreOffice.git] / framework / source / dispatch / closedispatcher.cxx
blob9bfd5588654cbd0648b53be220829f45370aaeba
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <dispatch/closedispatcher.hxx>
21 #include <pattern/frame.hxx>
22 #include <framework/framelistanalyzer.hxx>
23 #include <services.h>
24 #include <general.h>
26 #include <com/sun/star/frame/Desktop.hpp>
27 #include <com/sun/star/frame/XController.hpp>
28 #include <com/sun/star/frame/CommandGroup.hpp>
29 #include <com/sun/star/frame/StartModule.hpp>
30 #include <com/sun/star/lang/DisposedException.hpp>
31 #include <com/sun/star/awt/XTopWindow.hpp>
32 #include <com/sun/star/document/XActionLockable.hpp>
33 #include <com/sun/star/beans/XFastPropertySet.hpp>
34 #include <toolkit/helper/vclunohelper.hxx>
36 #include <vcl/window.hxx>
37 #include <vcl/svapp.hxx>
38 #include <vcl/syswin.hxx>
39 #include <osl/mutex.hxx>
40 #include <unotools/moduleoptions.hxx>
41 #include <comphelper/processfactory.hxx>
43 using namespace com::sun::star;
45 namespace framework{
47 #ifdef fpf
48 #error "Who uses \"fpf\" as define. It will overwrite my namespace alias ..."
49 #endif
50 namespace fpf = ::framework::pattern::frame;
52 const char URL_CLOSEDOC[] = ".uno:CloseDoc";
53 const char URL_CLOSEWIN[] = ".uno:CloseWin";
54 const char URL_CLOSEFRAME[] = ".uno:CloseFrame";
56 CloseDispatcher::CloseDispatcher(const css::uno::Reference< css::uno::XComponentContext >& rxContext ,
57 const css::uno::Reference< css::frame::XFrame >& xFrame ,
58 const OUString& sTarget)
59 : m_xContext (rxContext )
60 , m_aAsyncCallback(
61 new vcl::EventPoster(LINK(this, CloseDispatcher, impl_asyncCallback)))
62 , m_eOperation(E_CLOSE_DOC)
63 , m_lStatusListener(m_mutex)
64 , m_pSysWindow(NULL)
66 uno::Reference<frame::XFrame> xTarget = static_impl_searchRightTargetFrame(xFrame, sTarget);
67 m_xCloseFrame = xTarget;
69 // Try to retrieve the system window instance of the closing frame.
70 uno::Reference<awt::XWindow> xWindow = xTarget->getContainerWindow();
71 if (xWindow.is())
73 vcl::Window* pWindow = VCLUnoHelper::GetWindow(xWindow);
74 if (pWindow->IsSystemWindow())
75 m_pSysWindow = dynamic_cast<SystemWindow*>(pWindow);
79 CloseDispatcher::~CloseDispatcher()
81 SolarMutexGuard g;
82 m_aAsyncCallback.reset();
83 m_pSysWindow.reset();
86 void SAL_CALL CloseDispatcher::dispatch(const css::util::URL& aURL ,
87 const css::uno::Sequence< css::beans::PropertyValue >& lArguments)
88 throw(css::uno::RuntimeException, std::exception)
90 dispatchWithNotification(aURL, lArguments, css::uno::Reference< css::frame::XDispatchResultListener >());
93 css::uno::Sequence< sal_Int16 > SAL_CALL CloseDispatcher::getSupportedCommandGroups()
94 throw(css::uno::RuntimeException, std::exception)
96 css::uno::Sequence< sal_Int16 > lGroups(2);
97 lGroups[0] = css::frame::CommandGroup::VIEW;
98 lGroups[1] = css::frame::CommandGroup::DOCUMENT;
99 return lGroups;
102 css::uno::Sequence< css::frame::DispatchInformation > SAL_CALL CloseDispatcher::getConfigurableDispatchInformation(sal_Int16 nCommandGroup)
103 throw(css::uno::RuntimeException, std::exception)
105 if (nCommandGroup == css::frame::CommandGroup::VIEW)
107 /* Attention: Dont add .uno:CloseFrame here. Because its not really
108 a configurable feature ... and further it does not have
109 a valid UIName entry inside the GenericCommands.xcu ... */
110 css::uno::Sequence< css::frame::DispatchInformation > lViewInfos(1);
111 lViewInfos[0].Command = URL_CLOSEWIN;
112 lViewInfos[0].GroupId = css::frame::CommandGroup::VIEW;
113 return lViewInfos;
115 else if (nCommandGroup == css::frame::CommandGroup::DOCUMENT)
117 css::uno::Sequence< css::frame::DispatchInformation > lDocInfos(1);
118 lDocInfos[0].Command = URL_CLOSEDOC;
119 lDocInfos[0].GroupId = css::frame::CommandGroup::DOCUMENT;
120 return lDocInfos;
123 return css::uno::Sequence< css::frame::DispatchInformation >();
126 void SAL_CALL CloseDispatcher::addStatusListener(const css::uno::Reference< css::frame::XStatusListener >& /*xListener*/,
127 const css::util::URL& /*aURL*/ )
128 throw(css::uno::RuntimeException, std::exception)
132 void SAL_CALL CloseDispatcher::removeStatusListener(const css::uno::Reference< css::frame::XStatusListener >& /*xListener*/,
133 const css::util::URL& /*aURL*/ )
134 throw(css::uno::RuntimeException, std::exception)
138 void SAL_CALL CloseDispatcher::dispatchWithNotification(const css::util::URL& aURL ,
139 const css::uno::Sequence< css::beans::PropertyValue >& lArguments,
140 const css::uno::Reference< css::frame::XDispatchResultListener >& xListener )
141 throw(css::uno::RuntimeException, std::exception)
143 // SAFE -> ----------------------------------
144 SolarMutexClearableGuard aWriteLock;
146 // This reference indicates, that we was already called before and
147 // our asynchronous process was not finished yet.
148 // We have to reject double calls. Otherwhise we risk,
149 // that we try to close an already closed resource ...
150 // And its no problem to do nothing then. The UI user will try it again, if
151 // non of these jobs was successfully.
152 if (m_xSelfHold.is())
154 aWriteLock.clear();
155 // <- SAFE ------------------------------
157 implts_notifyResultListener(
158 xListener,
159 css::frame::DispatchResultState::DONTKNOW,
160 css::uno::Any());
161 return;
164 // First we have to check, if this dispatcher is used right. Means if valid URLs are used.
165 // If not - we have to break this operation. But an optional listener must be informed.
166 // BTW: We save the information about the requested operation. Because
167 // we need it later.
168 if ( aURL.Complete == URL_CLOSEDOC )
169 m_eOperation = E_CLOSE_DOC;
170 else if ( aURL.Complete == URL_CLOSEWIN )
171 m_eOperation = E_CLOSE_WIN;
172 else if ( aURL.Complete == URL_CLOSEFRAME )
173 m_eOperation = E_CLOSE_FRAME;
174 else
176 aWriteLock.clear();
177 // <- SAFE ------------------------------
179 implts_notifyResultListener(
180 xListener,
181 css::frame::DispatchResultState::FAILURE,
182 css::uno::Any());
183 return;
186 if (m_pSysWindow && m_pSysWindow->GetCloseHdl().IsSet())
188 // The closing frame has its own close handler. Call it instead.
189 m_pSysWindow->GetCloseHdl().Call(m_pSysWindow);
190 return;
193 // OK - URLs are the right ones.
194 // But we can't execute synchronously :-)
195 // May we are called from a generic key-input handler,
196 // which isn't aware that this call kill its own environment ...
197 // Do it asynchronous everytimes!
199 // But dont forget to hold usself alive.
200 // We are called back from an environment, which doesn't know an uno reference.
201 // They call us back by using our c++ interface.
203 m_xResultListener = xListener;
204 m_xSelfHold = css::uno::Reference< css::uno::XInterface >(static_cast< ::cppu::OWeakObject* >(this), css::uno::UNO_QUERY);
206 aWriteLock.clear();
207 // <- SAFE ----------------------------------
209 bool bIsSynchron = false;
210 for (sal_Int32 nArgs=0; nArgs<lArguments.getLength(); nArgs++ )
212 if ( lArguments[nArgs].Name == "SynchronMode" )
214 lArguments[nArgs].Value >>= bIsSynchron;
215 break;
219 if ( bIsSynchron )
220 impl_asyncCallback(0);
221 else
223 SolarMutexGuard g;
224 m_aAsyncCallback->Post(0);
229 @short asynchronous callback
230 @descr We start all actions inside this object asnychronoue.
231 (see comments there).
232 Now we do the following:
233 - close all views to the same document, if needed and possible
234 - make the current frame empty
235 ! This step is necessary to handle errors during closing the
236 document inside the frame. May the document shows a dialog and
237 the user ignore it. Then the state of the office can be changed
238 during we try to close frame and document.
239 - check the environment (menas count open frames - exlcuding our
240 current one)
241 - decide then, if we must close this frame only, establish the backing mode
242 or shutdown the whole application.
244 IMPL_LINK_NOARG(CloseDispatcher, impl_asyncCallback)
249 // Allow calling of XController->suspend() everytimes.
250 // Dispatch is an UI functionality. We implement such dispatch object here.
251 // And further XController->suspend() was designed to bring an UI ...
252 bool bAllowSuspend = true;
253 bool bControllerSuspended = false;
255 bool bCloseAllViewsToo;
256 EOperation eOperation;
257 css::uno::Reference< css::uno::XComponentContext > xContext;
258 css::uno::Reference< css::frame::XFrame > xCloseFrame;
259 css::uno::Reference< css::frame::XDispatchResultListener > xListener;
261 SolarMutexGuard g;
263 // Closing of all views, related to the same document, is allowed
264 // only if the dispatched URL was ".uno:CloseDoc"!
265 bCloseAllViewsToo = (m_eOperation == E_CLOSE_DOC);
267 eOperation = m_eOperation;
268 xContext = m_xContext;
269 xCloseFrame.set(m_xCloseFrame.get(), css::uno::UNO_QUERY);
270 xListener = m_xResultListener;
273 // frame already dead ?!
274 // Nothing to do !
275 if (! xCloseFrame.is())
276 return 0;
278 bool bCloseFrame = false;
279 bool bEstablishBackingMode = false;
280 bool bTerminateApp = false;
282 // Analyze the environment a first time.
283 // If we found some special cases, we can
284 // make some decisions erliar!
285 css::uno::Reference< css::frame::XFramesSupplier > xDesktop( css::frame::Desktop::create(xContext), css::uno::UNO_QUERY_THROW);
286 FrameListAnalyzer aCheck1(xDesktop, xCloseFrame, FrameListAnalyzer::E_HELP | FrameListAnalyzer::E_BACKINGCOMPONENT);
288 // a) If the current frame (where the close dispatch was requested for) does not have
289 // any parent frame ... it will close this frame only. Such frame isn't part of the
290 // global desktop tree ... and such frames are used as "implementation details" only.
291 // E.g. the live previews of our wizards doing such things. And then the owner of the frame
292 // is responsible for closing the application or accepting closing of the application
293 // by others.
294 if ( ! xCloseFrame->getCreator().is())
295 bCloseFrame = true;
297 // b) The help window can't disagree with any request.
298 // Because it doesn't implement a controller - it uses a window only.
299 // Further it can't be the last open frame - if we do all other things
300 // right inside this CloseDispatcher implementation.
301 // => close it!
302 else if (aCheck1.m_bReferenceIsHelp)
303 bCloseFrame = true;
305 // c) If we are already in "backing mode", we have to terminate
306 // the application, if this special frame is closed.
307 // It doesn't matter, how many other frames (can be the help or hidden frames only)
308 // are open then.
309 // => terminate the application!
310 else if (aCheck1.m_bReferenceIsBacking)
311 bTerminateApp = true;
313 // d) Otherwhise we have to: close all views to the same document, close the
314 // document inside our own frame and decide then again, what has to be done!
315 else
317 if (implts_prepareFrameForClosing(m_xCloseFrame, bAllowSuspend, bCloseAllViewsToo, bControllerSuspended))
319 // OK; this frame is empty now.
320 // Check the environment again to decide, what is the next step.
321 FrameListAnalyzer aCheck2(xDesktop, xCloseFrame, FrameListAnalyzer::E_ALL);
323 // c1) there is as minimum 1 frame open, which is visible and contains a document
324 // different from our one. And its not the help!
325 // => close our frame only - nothing else.
326 if (aCheck2.m_lOtherVisibleFrames.getLength()>0)
327 bCloseFrame = true;
328 else
330 // c2) if we close the current view ... but not all other views
331 // to the same document, we must close the current frame only!
332 // Because implts_closeView() suspended this view only - does not
333 // close the frame.
334 if (
335 (!bCloseAllViewsToo ) &&
336 (aCheck2.m_lModelFrames.getLength() > 0)
338 bCloseFrame = true;
340 else
341 // c3) there is no other (visible) frame open ...
342 // The help module will be ignored everytimes!
343 // But we have to decide if we must terminate the
344 // application or establish the backing mode now.
345 // And that depends from the dispatched URL ...
347 if (eOperation == E_CLOSE_FRAME)
348 bTerminateApp = true;
349 else if( SvtModuleOptions().IsModuleInstalled(SvtModuleOptions::EModule::STARTMODULE) )
350 bEstablishBackingMode = true;
351 else
352 bTerminateApp = true;
357 // Do it now ...
358 bool bSuccess = false;
359 if (bCloseFrame)
360 bSuccess = implts_closeFrame();
361 else if (bEstablishBackingMode)
362 #if defined MACOSX
364 // on mac close down, quickstarter keeps the process alive
365 // however if someone has shut down the quickstarter
366 // behave as any other platform
368 bool bQuickstarterRunning = false;
369 // get quickstart service
372 css::uno::Reference< css::beans::XFastPropertySet > xSet( xContext->getServiceManager()->createInstanceWithContext(IMPLEMENTATIONNAME_QUICKLAUNCHER, xContext), css::uno::UNO_QUERY_THROW );
373 if( xSet.is() )
375 css::uno::Any aVal( xSet->getFastPropertyValue( 0 ) );
376 sal_Bool bState = sal_False;
377 if( aVal >>= bState )
378 bQuickstarterRunning = bState;
381 catch( const css::uno::Exception& )
384 bSuccess = bQuickstarterRunning ? implts_terminateApplication() : implts_establishBackingMode();
386 #else
387 bSuccess = implts_establishBackingMode();
388 #endif
389 else if (bTerminateApp)
390 bSuccess = implts_terminateApplication();
392 if (
393 ( ! bSuccess ) &&
394 ( bControllerSuspended )
397 css::uno::Reference< css::frame::XController > xController = xCloseFrame->getController();
398 if (xController.is())
399 xController->suspend(sal_False);
402 // inform listener
403 sal_Int16 nState = css::frame::DispatchResultState::FAILURE;
404 if (bSuccess)
405 nState = css::frame::DispatchResultState::SUCCESS;
406 implts_notifyResultListener(xListener, nState, css::uno::Any());
408 SolarMutexGuard g;
409 // This method was called asynchronous from our main thread by using a pointer.
410 // We reached this method only, by using a reference to ourself :-)
411 // Further this member is used to detect still running and not yet finished
412 // ansynchronous operations. So its time now to release this reference.
413 // But hold it temp alive. Otherwise we die before we can finish this method really :-))
414 css::uno::Reference< css::uno::XInterface > xTempHold = m_xSelfHold;
415 m_xSelfHold.clear();
416 m_xResultListener.clear();
418 catch(const css::lang::DisposedException&)
422 return 0;
425 bool CloseDispatcher::implts_prepareFrameForClosing(const css::uno::Reference< css::frame::XFrame >& xFrame ,
426 bool bAllowSuspend ,
427 bool bCloseAllOtherViewsToo,
428 bool& bControllerSuspended )
430 // Frame already dead ... so this view is closed ... is closed ... is ... .-)
431 if (! xFrame.is())
432 return true;
434 // Close all views to the same document ... if forced to do so.
435 // But dont touch our own frame here!
436 // We must do so ... because the may be following controller->suspend()
437 // will show the "save/discard/cancel" dialog for the last view only!
438 if (bCloseAllOtherViewsToo)
440 css::uno::Reference< css::uno::XComponentContext > xContext;
442 SolarMutexGuard g;
443 xContext = m_xContext;
446 css::uno::Reference< css::frame::XFramesSupplier > xDesktop( css::frame::Desktop::create( xContext ), css::uno::UNO_QUERY_THROW);
447 FrameListAnalyzer aCheck(xDesktop, xFrame, FrameListAnalyzer::E_ALL);
449 sal_Int32 c = aCheck.m_lModelFrames.getLength();
450 sal_Int32 i = 0;
451 for (i=0; i<c; ++i)
453 if (!fpf::closeIt(aCheck.m_lModelFrames[i], false))
454 return false;
458 // If allowed - inform user about modified documents or
459 // still running jobs (e.g. printing).
460 if (bAllowSuspend)
462 css::uno::Reference< css::frame::XController > xController = xFrame->getController();
463 if (xController.is()) // some views dont uses a controller .-( (e.g. the help window)
465 bControllerSuspended = xController->suspend(sal_True);
466 if (! bControllerSuspended)
467 return false;
471 // dont remove the component really by e.g. calling setComponent(null, null).
472 // It's enough to suspend the controller.
473 // If we close the frame later this controller doesn't show the same dialog again.
474 return true;
477 bool CloseDispatcher::implts_closeFrame()
479 css::uno::Reference< css::frame::XFrame > xFrame;
481 SolarMutexGuard g;
482 xFrame.set(m_xCloseFrame.get(), css::uno::UNO_QUERY);
485 // frame already dead ? => so it's closed ... it's closed ...
486 if ( ! xFrame.is() )
487 return true;
489 // dont deliver ownership; our "UI user" will try it again if it failed.
490 // OK - he will get an empty frame then. But normally an empty frame
491 // should be closeable always :-)
492 if (!fpf::closeIt(xFrame, false))
493 return false;
496 SolarMutexGuard g;
497 m_xCloseFrame = css::uno::WeakReference< css::frame::XFrame >();
500 return true;
503 bool CloseDispatcher::implts_establishBackingMode()
505 css::uno::Reference< css::uno::XComponentContext > xContext;
506 css::uno::Reference< css::frame::XFrame > xFrame;
508 SolarMutexGuard g;
509 xContext = m_xContext;
510 xFrame.set(m_xCloseFrame.get(), css::uno::UNO_QUERY);
513 if (!xFrame.is())
514 return false;
516 css::uno::Reference < css::document::XActionLockable > xLock( xFrame, css::uno::UNO_QUERY );
517 if ( xLock.is() && xLock->isActionLocked() )
518 return false;
520 css::uno::Reference< css::awt::XWindow > xContainerWindow = xFrame->getContainerWindow();
522 css::uno::Reference< css::frame::XController > xStartModule = css::frame::StartModule::createWithParentWindow(
523 xContext, xContainerWindow);
525 // Attention: You MUST(!) call setComponent() before you call attachFrame().
526 css::uno::Reference< css::awt::XWindow > xBackingWin(xStartModule, css::uno::UNO_QUERY);
527 xFrame->setComponent(xBackingWin, xStartModule);
528 xStartModule->attachFrame(xFrame);
529 xContainerWindow->setVisible(sal_True);
531 return true;
534 bool CloseDispatcher::implts_terminateApplication()
536 css::uno::Reference< css::uno::XComponentContext > xContext;
538 SolarMutexGuard g;
539 xContext = m_xContext;
542 css::uno::Reference< css::frame::XDesktop2 > xDesktop = css::frame::Desktop::create( xContext );
544 return xDesktop->terminate();
547 void CloseDispatcher::implts_notifyResultListener(const css::uno::Reference< css::frame::XDispatchResultListener >& xListener,
548 sal_Int16 nState ,
549 const css::uno::Any& aResult )
551 if (!xListener.is())
552 return;
554 css::frame::DispatchResultEvent aEvent(
555 css::uno::Reference< css::uno::XInterface >(static_cast< ::cppu::OWeakObject* >(this), css::uno::UNO_QUERY),
556 nState,
557 aResult);
559 xListener->dispatchFinished(aEvent);
562 css::uno::Reference< css::frame::XFrame > CloseDispatcher::static_impl_searchRightTargetFrame(const css::uno::Reference< css::frame::XFrame >& xFrame ,
563 const OUString& sTarget)
565 if (sTarget.equalsIgnoreAsciiCase("_self"))
566 return xFrame;
568 OSL_ENSURE(sTarget.isEmpty(), "CloseDispatch used for unexpected target. Magic things will happen now .-)");
570 css::uno::Reference< css::frame::XFrame > xTarget = xFrame;
571 while(true)
573 // a) top frames wil be closed
574 if (xTarget->isTop())
575 return xTarget;
577 // b) even child frame containing top level windows (e.g. query designer of database) will be closed
578 css::uno::Reference< css::awt::XWindow > xWindow = xTarget->getContainerWindow();
579 css::uno::Reference< css::awt::XTopWindow > xTopWindowCheck(xWindow, css::uno::UNO_QUERY);
580 if (xTopWindowCheck.is())
582 // b1) Note: Toolkit interface XTopWindow sometimes is used by real VCL-child-windows also .-)
583 // Be sure that these window is really a "top system window".
584 // Attention ! Checking Window->GetParent() isn't the right approach here.
585 // Because sometimes VCL create "implicit border windows" as parents even we created
586 // a simple XWindow using the toolkit only .-(
587 SolarMutexGuard aSolarLock;
588 vcl::Window* pWindow = VCLUnoHelper::GetWindow( xWindow );
589 if (
590 (pWindow ) &&
591 (pWindow->IsSystemWindow())
593 return xTarget;
596 // c) try to find better results on parent frame
597 // If no parent frame exists (because this frame is used outside the desktop tree)
598 // the given frame must be used directly.
599 css::uno::Reference< css::frame::XFrame > xParent(xTarget->getCreator(), css::uno::UNO_QUERY);
600 if ( ! xParent.is())
601 return xTarget;
603 // c1) check parent frame inside next loop ...
604 xTarget = xParent;
608 } // namespace framework
610 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */