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::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(std::make_shared<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());
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     auto pDescriptor = std::make_shared<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);
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 =
325             std::make_shared<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 = std::shared_ptr<GraphicViewShell>(
335                 new GraphicViewShell(*mpBase, &rWindow, pFrameView),
336                 o3tl::default_delete<GraphicViewShell>());
337         pViewShell->GetContentWindow()->set_id("draw_win");
338     }
339     else if (rsViewURL == FrameworkHelper::msOutlineViewURL)
340     {
341         pViewShell =
342             std::make_shared<OutlineViewShell>(
343                 &rFrame,
344                 *mpBase,
345                 &rWindow,
346                 pFrameView);
347         pViewShell->GetContentWindow()->set_id("outline_win");
348     }
349     else if (rsViewURL == FrameworkHelper::msNotesViewURL)
350     {
351         pViewShell =
352             std::make_shared<DrawViewShell>(
353                 *mpBase,
354                 &rWindow,
355                 PageKind::Notes,
356                 pFrameView);
357         pViewShell->GetContentWindow()->set_id("notes_win");
358     }
359     else if (rsViewURL == FrameworkHelper::msHandoutViewURL)
360     {
361         pViewShell =
362             std::make_shared<DrawViewShell>(
363                 *mpBase,
364                 &rWindow,
365                 PageKind::Handout,
366                 pFrameView);
367         pViewShell->GetContentWindow()->set_id("handout_win");
368     }
369     else if (rsViewURL == FrameworkHelper::msPresentationViewURL)
370     {
371         pViewShell =
372             std::make_shared<PresentationViewShell>(
373                 *mpBase,
374                 &rWindow,
375                 pFrameView);
376         pViewShell->GetContentWindow()->set_id("presentation_win");
377     }
378     else if (rsViewURL == FrameworkHelper::msSlideSorterURL)
379     {
380         pViewShell = ::sd::slidesorter::SlideSorterViewShell::Create (
381             &rFrame,
382             *mpBase,
383             &rWindow,
384             pFrameView);
385         pViewShell->GetContentWindow()->set_id("slidesorter");
386     }
387 
388     return pViewShell;
389 }
390 
ReleaseView(const std::shared_ptr<ViewDescriptor> & rpDescriptor,bool bDoNotCache)391 void BasicViewFactory::ReleaseView (
392     const std::shared_ptr<ViewDescriptor>& rpDescriptor,
393     bool bDoNotCache)
394 {
395     bool bIsCacheable (!bDoNotCache && IsCacheable(rpDescriptor));
396 
397     if (bIsCacheable)
398     {
399         Reference<XRelocatableResource> xResource (rpDescriptor->mxView, UNO_QUERY);
400         if (xResource.is())
401         {
402             if (mxLocalPane.is())
403                 if (xResource->relocateToAnchor(mxLocalPane))
404                     mpViewCache->push_back(rpDescriptor);
405                 else
406                     bIsCacheable = false;
407             else
408                 bIsCacheable = false;
409         }
410         else
411         {
412             bIsCacheable = false;
413         }
414     }
415 
416     if ( ! bIsCacheable)
417     {
418         // Shut down the current view shell.
419         rpDescriptor->mpViewShell->Shutdown ();
420         mpBase->GetDocShell()->Disconnect(rpDescriptor->mpViewShell.get());
421         mpBase->GetViewShellManager()->DeactivateViewShell(rpDescriptor->mpViewShell.get());
422 
423         Reference<XComponent> xComponent (rpDescriptor->mxView, UNO_QUERY);
424         if (xComponent.is())
425             xComponent->dispose();
426     }
427 }
428 
IsCacheable(const std::shared_ptr<ViewDescriptor> & rpDescriptor)429 bool BasicViewFactory::IsCacheable (const std::shared_ptr<ViewDescriptor>& rpDescriptor)
430 {
431     bool bIsCacheable (false);
432 
433     Reference<XRelocatableResource> xResource (rpDescriptor->mxView, UNO_QUERY);
434     if (xResource.is())
435     {
436         static ::std::vector<Reference<XResourceId> > s_aCacheableResources = [&]()
437         {
438             ::std::vector<Reference<XResourceId> > tmp;
439             FrameworkHelper::Instance(*mpBase);
440 
441             // The slide sorter and the task panel are cacheable and relocatable.
442             tmp.push_back(FrameworkHelper::CreateResourceId(
443                 FrameworkHelper::msSlideSorterURL, FrameworkHelper::msLeftDrawPaneURL));
444             tmp.push_back(FrameworkHelper::CreateResourceId(
445                 FrameworkHelper::msSlideSorterURL, FrameworkHelper::msLeftImpressPaneURL));
446             return tmp;
447         }();
448 
449         bIsCacheable = std::any_of(s_aCacheableResources.begin(), s_aCacheableResources.end(),
450             [&rpDescriptor](const Reference<XResourceId>& rxId) { return rxId->compareTo(rpDescriptor->mxViewId) == 0; });
451     }
452 
453     return bIsCacheable;
454 }
455 
GetViewFromCache(const Reference<XResourceId> & rxViewId,const Reference<XPane> & rxPane)456 std::shared_ptr<BasicViewFactory::ViewDescriptor> BasicViewFactory::GetViewFromCache (
457     const Reference<XResourceId>& rxViewId,
458     const Reference<XPane>& rxPane)
459 {
460     std::shared_ptr<ViewDescriptor> pDescriptor;
461 
462     // Search for the requested view in the cache.
463     ViewCache::iterator iEntry = std::find_if(mpViewCache->begin(), mpViewCache->end(),
464         [&rxViewId](const ViewCache::value_type& rxEntry) { return rxEntry->mxViewId->compareTo(rxViewId) == 0; });
465     if (iEntry != mpViewCache->end())
466     {
467         pDescriptor = *iEntry;
468         mpViewCache->erase(iEntry);
469     }
470 
471     // When the view has been found then relocate it to the given pane and
472     // remove it from the cache.
473     if (pDescriptor != nullptr)
474     {
475         bool bRelocationSuccessfull (false);
476         Reference<XRelocatableResource> xResource (pDescriptor->mxView, UNO_QUERY);
477         if (xResource.is() && rxPane.is())
478         {
479             if (xResource->relocateToAnchor(rxPane))
480                 bRelocationSuccessfull = true;
481         }
482 
483         if ( ! bRelocationSuccessfull)
484         {
485             ReleaseView(pDescriptor, true);
486             pDescriptor.reset();
487         }
488     }
489 
490     return pDescriptor;
491 }
492 
ActivateCenterView(const std::shared_ptr<ViewDescriptor> & rpDescriptor)493 void BasicViewFactory::ActivateCenterView (
494     const std::shared_ptr<ViewDescriptor>& rpDescriptor)
495 {
496     mpBase->GetDocShell()->Connect(rpDescriptor->mpViewShell.get());
497 
498     // During the creation of the new sub-shell, resize requests were not
499     // forwarded to it because it was not yet registered.  Therefore, we
500     // have to request a resize now.
501     rpDescriptor->mpViewShell->UIFeatureChanged();
502     if (mpBase->GetDocShell()->IsInPlaceActive())
503         mpBase->GetViewFrame()->Resize(true);
504 
505     mpBase->GetDrawController().SetSubController(
506         rpDescriptor->mpViewShell->CreateSubController());
507 }
508 
509 } // end of namespace sd::framework
510 
511 
512 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 &)513 com_sun_star_comp_Draw_framework_BasicViewFactory_get_implementation(css::uno::XComponentContext*,
514                                                                      css::uno::Sequence<css::uno::Any> const &)
515 {
516     return cppu::acquire(new sd::framework::BasicViewFactory);
517 }
518 
519 
520 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
521