1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
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/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
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 .
18  */
19 
20 #include "BasicViewFactory.hxx"
21 
22 #include <framework/ViewShellWrapper.hxx>
23 #include <framework/FrameworkHelper.hxx>
24 #include <com/sun/star/drawing/framework/XControllerManager.hpp>
25 #include <com/sun/star/lang/IllegalArgumentException.hpp>
26 #include <framework/Pane.hxx>
27 #include <DrawController.hxx>
28 #include <ViewShellBase.hxx>
29 #include <ViewShellManager.hxx>
30 #include <DrawDocShell.hxx>
31 #include <DrawViewShell.hxx>
32 #include <GraphicViewShell.hxx>
33 #include <OutlineViewShell.hxx>
34 #include <PresentationViewShell.hxx>
35 #include <SlideSorterViewShell.hxx>
36 #include <FrameView.hxx>
37 #include <Window.hxx>
38 
39 #include <sfx2/viewfrm.hxx>
40 #include <vcl/wrkwin.hxx>
41 #include <toolkit/helper/vclunohelper.hxx>
42 
43 
44 using namespace ::com::sun::star;
45 using namespace ::com::sun::star::uno;
46 using namespace ::com::sun::star::lang;
47 using namespace ::com::sun::star::drawing::framework;
48 
49 using ::sd::framework::FrameworkHelper;
50 
51 namespace sd { namespace framework {
52 
53 //===== ViewDescriptor ========================================================
54 
55 class BasicViewFactory::ViewDescriptor
56 {
57 public:
58     Reference<XResource> mxView;
59     std::shared_ptr<sd::ViewShell> mpViewShell;
60     Reference<XResourceId> mxViewId;
CompareView(const std::shared_ptr<ViewDescriptor> & rpDescriptor,const Reference<XResource> & rxView)61     static bool CompareView (const std::shared_ptr<ViewDescriptor>& rpDescriptor,
62         const Reference<XResource>& rxView)
63     { return rpDescriptor->mxView.get() == rxView.get(); }
64 };
65 
66 //===== BasicViewFactory::ViewShellContainer ==================================
67 
68 class BasicViewFactory::ViewShellContainer
69     : public ::std::vector<std::shared_ptr<ViewDescriptor> >
70 {
71 public:
ViewShellContainer()72     ViewShellContainer() {};
73 };
74 
75 class BasicViewFactory::ViewCache
76     : public ::std::vector<std::shared_ptr<ViewDescriptor> >
77 {
78 public:
ViewCache()79     ViewCache() {};
80 };
81 
82 //===== ViewFactory ===========================================================
83 
BasicViewFactory()84 BasicViewFactory::BasicViewFactory ()
85     : BasicViewFactoryInterfaceBase(MutexOwner::maMutex),
86       mxConfigurationController(),
87       mpViewShellContainer(new ViewShellContainer()),
88       mpBase(nullptr),
89       mpFrameView(nullptr),
90       mpWindow(VclPtr<WorkWindow>::Create(nullptr,WB_STDWORK)),
91       mpViewCache(new ViewCache()),
92       mxLocalPane(new Pane(Reference<XResourceId>(), mpWindow.get()))
93 {
94 }
95 
~BasicViewFactory()96 BasicViewFactory::~BasicViewFactory()
97 {
98 }
99 
disposing()100 void SAL_CALL BasicViewFactory::disposing()
101 {
102     // Disconnect from the frame view.
103     if (mpFrameView != nullptr)
104     {
105         mpFrameView->Disconnect();
106         mpFrameView = nullptr;
107     }
108 
109     // Release the view cache.
110     for (const auto& rxView : *mpViewCache)
111     {
112         ReleaseView(rxView, true);
113     }
114 
115     // Release the view shell container.  At this point no one other than us
116     // should hold references to the view shells (at the moment this is a
117     // trivial requirement, because no one other than us holds a shared
118     // pointer).
119     //    ViewShellContainer::const_iterator iView;
120     for (const auto& rxView : *mpViewShellContainer)
121     {
122         OSL_ASSERT(rxView->mpViewShell.use_count() == 1);
123     }
124     mpViewShellContainer.reset();
125 }
126 
createResource(const Reference<XResourceId> & rxViewId)127 Reference<XResource> SAL_CALL BasicViewFactory::createResource (
128     const Reference<XResourceId>& rxViewId)
129 {
130     Reference<XResource> xView;
131     const bool bIsCenterPane (
132         rxViewId->isBoundToURL(FrameworkHelper::msCenterPaneURL, AnchorBindingMode_DIRECT));
133 
134     // Get the pane for the anchor URL.
135     Reference<XPane> xPane;
136     if (mxConfigurationController.is())
137         xPane.set(mxConfigurationController->getResource(rxViewId->getAnchor()), UNO_QUERY);
138 
139     // For main views use the frame view of the last main view.
140     ::sd::FrameView* pFrameView = nullptr;
141     if (xPane.is() && bIsCenterPane)
142     {
143         pFrameView = mpFrameView;
144     }
145 
146     // Get Window pointer for XWindow of the pane.
147     vcl::Window* pWindow = nullptr;
148     if (xPane.is())
149         pWindow = VCLUnoHelper::GetWindow(xPane->getWindow()).get();
150 
151     // Get the view frame.
152     SfxViewFrame* pFrame = nullptr;
153     if (mpBase != nullptr)
154         pFrame = mpBase->GetViewFrame();
155 
156     if (pFrame != nullptr && mpBase!=nullptr && pWindow!=nullptr)
157     {
158         // Try to get the view from the cache.
159         std::shared_ptr<ViewDescriptor> pDescriptor (GetViewFromCache(rxViewId, xPane));
160 
161         // When the requested view is not in the cache then create a new view.
162         if (pDescriptor == nullptr)
163         {
164             pDescriptor = CreateView(rxViewId, *pFrame, *pWindow, xPane, pFrameView, bIsCenterPane);
165         }
166 
167         if (pDescriptor != nullptr)
168             xView = pDescriptor->mxView;
169 
170         mpViewShellContainer->push_back(pDescriptor);
171 
172         if (bIsCenterPane)
173             ActivateCenterView(pDescriptor);
174         else
175             pWindow->Resize();
176     }
177 
178     return xView;
179 }
180 
releaseResource(const Reference<XResource> & rxView)181 void SAL_CALL BasicViewFactory::releaseResource (const Reference<XResource>& rxView)
182 {
183     if ( ! rxView.is())
184         throw lang::IllegalArgumentException();
185 
186     if (!rxView.is() || !mpBase)
187         return;
188 
189     ViewShellContainer::iterator iViewShell (
190         ::std::find_if(
191             mpViewShellContainer->begin(),
192             mpViewShellContainer->end(),
193             [&] (std::shared_ptr<ViewDescriptor> const& pVD) {
194                 return ViewDescriptor::CompareView(pVD, rxView);
195             } ));
196     if (iViewShell == mpViewShellContainer->end())
197     {
198         throw lang::IllegalArgumentException();
199     }
200 
201     std::shared_ptr<ViewShell> pViewShell ((*iViewShell)->mpViewShell);
202 
203     if ((*iViewShell)->mxViewId->isBoundToURL(
204         FrameworkHelper::msCenterPaneURL, AnchorBindingMode_DIRECT))
205     {
206         // Obtain a pointer to and connect to the frame view of the
207         // view.  The next view, that is created, will be
208         // initialized with this frame view.
209         if (mpFrameView == nullptr)
210         {
211             mpFrameView = pViewShell->GetFrameView();
212             if (mpFrameView)
213                 mpFrameView->Connect();
214         }
215 
216         // With the view in the center pane the sub controller is
217         // released, too.
218         mpBase->GetDrawController().SetSubController(
219             Reference<drawing::XDrawSubController>());
220 
221         SfxViewShell* pSfxViewShell = pViewShell->GetViewShell();
222         if (pSfxViewShell != nullptr)
223             pSfxViewShell->DisconnectAllClients();
224     }
225 
226     ReleaseView(*iViewShell, false);
227 
228     mpViewShellContainer->erase(iViewShell);
229 }
230 
initialize(const Sequence<Any> & aArguments)231 void SAL_CALL BasicViewFactory::initialize (const Sequence<Any>& aArguments)
232 {
233     if (!aArguments.hasElements())
234         return;
235 
236     try
237     {
238         // Get the XController from the first argument.
239         Reference<frame::XController> xController (aArguments[0], UNO_QUERY_THROW);
240 
241         // Tunnel through the controller to obtain a ViewShellBase.
242         Reference<lang::XUnoTunnel> xTunnel (xController, UNO_QUERY_THROW);
243         ::sd::DrawController* pController = reinterpret_cast<sd::DrawController*>(
244             xTunnel->getSomething(sd::DrawController::getUnoTunnelId()));
245         if (pController != nullptr)
246             mpBase = pController->GetViewShellBase();
247 
248         // Register the factory for its supported views.
249         Reference<XControllerManager> xCM (xController,UNO_QUERY_THROW);
250         mxConfigurationController = xCM->getConfigurationController();
251         if ( ! mxConfigurationController.is())
252             throw RuntimeException();
253         mxConfigurationController->addResourceFactory(FrameworkHelper::msImpressViewURL, this);
254         mxConfigurationController->addResourceFactory(FrameworkHelper::msDrawViewURL, this);
255         mxConfigurationController->addResourceFactory(FrameworkHelper::msOutlineViewURL, this);
256         mxConfigurationController->addResourceFactory(FrameworkHelper::msNotesViewURL, this);
257         mxConfigurationController->addResourceFactory(FrameworkHelper::msHandoutViewURL, this);
258         mxConfigurationController->addResourceFactory(FrameworkHelper::msPresentationViewURL, this);
259         mxConfigurationController->addResourceFactory(FrameworkHelper::msSlideSorterURL, this);
260     }
261     catch (RuntimeException&)
262     {
263         mpBase = nullptr;
264         if (mxConfigurationController.is())
265             mxConfigurationController->removeResourceFactoryForReference(this);
266         throw;
267     }
268 }
269 
CreateView(const Reference<XResourceId> & rxViewId,SfxViewFrame & rFrame,vcl::Window & rWindow,const Reference<XPane> & rxPane,FrameView * pFrameView,const bool bIsCenterPane)270 std::shared_ptr<BasicViewFactory::ViewDescriptor> BasicViewFactory::CreateView (
271     const Reference<XResourceId>& rxViewId,
272     SfxViewFrame& rFrame,
273     vcl::Window& rWindow,
274     const Reference<XPane>& rxPane,
275     FrameView* pFrameView,
276     const bool bIsCenterPane)
277 {
278     std::shared_ptr<ViewDescriptor> pDescriptor (new ViewDescriptor);
279 
280     pDescriptor->mpViewShell = CreateViewShell(
281         rxViewId,
282         rFrame,
283         rWindow,
284         pFrameView);
285     pDescriptor->mxViewId = rxViewId;
286 
287     if (pDescriptor->mpViewShell != nullptr)
288     {
289         pDescriptor->mpViewShell->Init(bIsCenterPane);
290         mpBase->GetViewShellManager()->ActivateViewShell(pDescriptor->mpViewShell.get());
291 
292         Reference<awt::XWindow> xWindow(rxPane->getWindow());
293         rtl::Reference<ViewShellWrapper> wrapper(new ViewShellWrapper(
294             pDescriptor->mpViewShell,
295             rxViewId,
296             xWindow));
297 
298         // register ViewShellWrapper on pane window
299         if (xWindow.is())
300         {
301             xWindow->addWindowListener(wrapper.get());
302             if (pDescriptor->mpViewShell != nullptr)
303             {
304                 pDescriptor->mpViewShell->Resize();
305             }
306         }
307 
308         pDescriptor->mxView = wrapper.get();
309     }
310 
311     return pDescriptor;
312 }
313 
CreateViewShell(const Reference<XResourceId> & rxViewId,SfxViewFrame & rFrame,vcl::Window & rWindow,FrameView * pFrameView)314 std::shared_ptr<ViewShell> BasicViewFactory::CreateViewShell (
315     const Reference<XResourceId>& rxViewId,
316     SfxViewFrame& rFrame,
317     vcl::Window& rWindow,
318     FrameView* pFrameView)
319 {
320     std::shared_ptr<ViewShell> pViewShell;
321     const OUString& rsViewURL (rxViewId->getResourceURL());
322     if (rsViewURL == FrameworkHelper::msImpressViewURL)
323     {
324         pViewShell.reset(
325             new DrawViewShell(
326                 *mpBase,
327                 &rWindow,
328                 PageKind::Standard,
329                 pFrameView));
330         pViewShell->GetContentWindow()->set_id("impress_win");
331     }
332     else if (rsViewURL == FrameworkHelper::msDrawViewURL)
333     {
334         pViewShell.reset(
335             new GraphicViewShell (
336                 *mpBase,
337                 &rWindow,
338                 pFrameView));
339         pViewShell->GetContentWindow()->set_id("draw_win");
340     }
341     else if (rsViewURL == FrameworkHelper::msOutlineViewURL)
342     {
343         pViewShell.reset(
344             new OutlineViewShell (
345                 &rFrame,
346                 *mpBase,
347                 &rWindow,
348                 pFrameView));
349         pViewShell->GetContentWindow()->set_id("outline_win");
350     }
351     else if (rsViewURL == FrameworkHelper::msNotesViewURL)
352     {
353         pViewShell.reset(
354             new DrawViewShell(
355                 *mpBase,
356                 &rWindow,
357                 PageKind::Notes,
358                 pFrameView));
359         pViewShell->GetContentWindow()->set_id("notes_win");
360     }
361     else if (rsViewURL == FrameworkHelper::msHandoutViewURL)
362     {
363         pViewShell.reset(
364             new DrawViewShell(
365                 *mpBase,
366                 &rWindow,
367                 PageKind::Handout,
368                 pFrameView));
369         pViewShell->GetContentWindow()->set_id("handout_win");
370     }
371     else if (rsViewURL == FrameworkHelper::msPresentationViewURL)
372     {
373         pViewShell.reset(
374             new PresentationViewShell(
375                 *mpBase,
376                 &rWindow,
377                 pFrameView));
378         pViewShell->GetContentWindow()->set_id("presentation_win");
379     }
380     else if (rsViewURL == FrameworkHelper::msSlideSorterURL)
381     {
382         pViewShell = ::sd::slidesorter::SlideSorterViewShell::Create (
383             &rFrame,
384             *mpBase,
385             &rWindow,
386             pFrameView);
387         pViewShell->GetContentWindow()->set_id("slidesorter");
388     }
389 
390     return pViewShell;
391 }
392 
ReleaseView(const std::shared_ptr<ViewDescriptor> & rpDescriptor,bool bDoNotCache)393 void BasicViewFactory::ReleaseView (
394     const std::shared_ptr<ViewDescriptor>& rpDescriptor,
395     bool bDoNotCache)
396 {
397     bool bIsCacheable (!bDoNotCache && IsCacheable(rpDescriptor));
398 
399     if (bIsCacheable)
400     {
401         Reference<XRelocatableResource> xResource (rpDescriptor->mxView, UNO_QUERY);
402         if (xResource.is())
403         {
404             if (mxLocalPane.is())
405                 if (xResource->relocateToAnchor(mxLocalPane))
406                     mpViewCache->push_back(rpDescriptor);
407                 else
408                     bIsCacheable = false;
409             else
410                 bIsCacheable = false;
411         }
412         else
413         {
414             bIsCacheable = false;
415         }
416     }
417 
418     if ( ! bIsCacheable)
419     {
420         // Shut down the current view shell.
421         rpDescriptor->mpViewShell->Shutdown ();
422         mpBase->GetDocShell()->Disconnect(rpDescriptor->mpViewShell.get());
423         mpBase->GetViewShellManager()->DeactivateViewShell(rpDescriptor->mpViewShell.get());
424 
425         Reference<XComponent> xComponent (rpDescriptor->mxView, UNO_QUERY);
426         if (xComponent.is())
427             xComponent->dispose();
428     }
429 }
430 
IsCacheable(const std::shared_ptr<ViewDescriptor> & rpDescriptor)431 bool BasicViewFactory::IsCacheable (const std::shared_ptr<ViewDescriptor>& rpDescriptor)
432 {
433     bool bIsCacheable (false);
434 
435     Reference<XRelocatableResource> xResource (rpDescriptor->mxView, UNO_QUERY);
436     if (xResource.is())
437     {
438         static ::std::vector<Reference<XResourceId> > s_aCacheableResources = [&]()
439         {
440             ::std::vector<Reference<XResourceId> > tmp;
441             std::shared_ptr<FrameworkHelper> pHelper (FrameworkHelper::Instance(*mpBase));
442 
443             // The slide sorter and the task panel are cacheable and relocatable.
444             tmp.push_back(FrameworkHelper::CreateResourceId(
445                 FrameworkHelper::msSlideSorterURL, FrameworkHelper::msLeftDrawPaneURL));
446             tmp.push_back(FrameworkHelper::CreateResourceId(
447                 FrameworkHelper::msSlideSorterURL, FrameworkHelper::msLeftImpressPaneURL));
448             return tmp;
449         }();
450 
451         bIsCacheable = std::any_of(s_aCacheableResources.begin(), s_aCacheableResources.end(),
452             [&rpDescriptor](const Reference<XResourceId>& rxId) { return rxId->compareTo(rpDescriptor->mxViewId) == 0; });
453     }
454 
455     return bIsCacheable;
456 }
457 
GetViewFromCache(const Reference<XResourceId> & rxViewId,const Reference<XPane> & rxPane)458 std::shared_ptr<BasicViewFactory::ViewDescriptor> BasicViewFactory::GetViewFromCache (
459     const Reference<XResourceId>& rxViewId,
460     const Reference<XPane>& rxPane)
461 {
462     std::shared_ptr<ViewDescriptor> pDescriptor;
463 
464     // Search for the requested view in the cache.
465     ViewCache::iterator iEntry = std::find_if(mpViewCache->begin(), mpViewCache->end(),
466         [&rxViewId](const ViewCache::value_type& rxEntry) { return rxEntry->mxViewId->compareTo(rxViewId) == 0; });
467     if (iEntry != mpViewCache->end())
468     {
469         pDescriptor = *iEntry;
470         mpViewCache->erase(iEntry);
471     }
472 
473     // When the view has been found then relocate it to the given pane and
474     // remove it from the cache.
475     if (pDescriptor != nullptr)
476     {
477         bool bRelocationSuccessfull (false);
478         Reference<XRelocatableResource> xResource (pDescriptor->mxView, UNO_QUERY);
479         if (xResource.is() && rxPane.is())
480         {
481             if (xResource->relocateToAnchor(rxPane))
482                 bRelocationSuccessfull = true;
483         }
484 
485         if ( ! bRelocationSuccessfull)
486         {
487             ReleaseView(pDescriptor, true);
488             pDescriptor.reset();
489         }
490     }
491 
492     return pDescriptor;
493 }
494 
ActivateCenterView(const std::shared_ptr<ViewDescriptor> & rpDescriptor)495 void BasicViewFactory::ActivateCenterView (
496     const std::shared_ptr<ViewDescriptor>& rpDescriptor)
497 {
498     mpBase->GetDocShell()->Connect(rpDescriptor->mpViewShell.get());
499 
500     // During the creation of the new sub-shell, resize requests were not
501     // forwarded to it because it was not yet registered.  Therefore, we
502     // have to request a resize now.
503     rpDescriptor->mpViewShell->UIFeatureChanged();
504     if (mpBase->GetDocShell()->IsInPlaceActive())
505         mpBase->GetViewFrame()->Resize(true);
506 
507     mpBase->GetDrawController().SetSubController(
508         rpDescriptor->mpViewShell->CreateSubController());
509 }
510 
511 } } // end of namespace sd::framework
512 
513 
514 extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
com_sun_star_comp_Draw_framework_BasicViewFactory_get_implementation(css::uno::XComponentContext *,css::uno::Sequence<css::uno::Any> const &)515 com_sun_star_comp_Draw_framework_BasicViewFactory_get_implementation(css::uno::XComponentContext*,
516                                                                      css::uno::Sequence<css::uno::Any> const &)
517 {
518     return cppu::acquire(new sd::framework::BasicViewFactory);
519 }
520 
521 
522 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
523