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 <memory>
21 #include <sal/config.h>
22 
23 #include <ChartController.hxx>
24 #include <servicenames.hxx>
25 #include <ResId.hxx>
26 #include <dlg_DataSource.hxx>
27 #include <ChartModel.hxx>
28 #include <ChartModelHelper.hxx>
29 #include "ControllerCommandDispatch.hxx"
30 #include <strings.hrc>
31 #include <chartview/ExplicitValueProvider.hxx>
32 #include <ChartViewHelper.hxx>
33 
34 #include <ChartWindow.hxx>
35 #include <chartview/DrawModelWrapper.hxx>
36 #include <DrawViewWrapper.hxx>
37 #include <ObjectIdentifier.hxx>
38 #include <DiagramHelper.hxx>
39 #include <ControllerLockGuard.hxx>
40 #include "UndoGuard.hxx"
41 #include "ChartDropTargetHelper.hxx"
42 
43 #include <dlg_ChartType.hxx>
44 #include <AccessibleChartView.hxx>
45 #include "DrawCommandDispatch.hxx"
46 #include "ShapeController.hxx"
47 #include "UndoActions.hxx"
48 #include <ViewElementListProvider.hxx>
49 
50 #include <cppuhelper/supportsservice.hxx>
51 #include <comphelper/servicehelper.hxx>
52 
53 #include <com/sun/star/awt/XWindowPeer.hpp>
54 #include <com/sun/star/chart2/XChartDocument.hpp>
55 #include <com/sun/star/chart2/data/XDataReceiver.hpp>
56 #include <com/sun/star/frame/XController2.hpp>
57 #include <com/sun/star/util/CloseVetoException.hpp>
58 #include <com/sun/star/util/XModeChangeBroadcaster.hpp>
59 #include <com/sun/star/util/XModifyBroadcaster.hpp>
60 #include <com/sun/star/frame/LayoutManagerEvents.hpp>
61 #include <com/sun/star/frame/XLayoutManagerEventBroadcaster.hpp>
62 #include <com/sun/star/document/XUndoManagerSupplier.hpp>
63 #include <com/sun/star/ui/XSidebar.hpp>
64 #include <com/sun/star/chart2/XChartTypeContainer.hpp>
65 #include <com/sun/star/chart2/XCoordinateSystemContainer.hpp>
66 #include <com/sun/star/chart2/XDataProviderAccess.hpp>
67 
68 #include <sal/log.hxx>
69 #include <tools/debug.hxx>
70 #include <svx/sidebar/SelectionChangeHandler.hxx>
71 #include <toolkit/helper/vclunohelper.hxx>
72 #include <vcl/svapp.hxx>
73 #include <vcl/weld.hxx>
74 #include <osl/mutex.hxx>
75 
76 #include <sfx2/sidebar/SidebarController.hxx>
77 
78 #include <com/sun/star/frame/XLayoutManager.hpp>
79 
80 // this is needed to properly destroy the unique_ptr to the AcceleratorExecute
81 // object in the DTOR
82 #include <svtools/acceleratorexecute.hxx>
83 #include <svx/ActionDescriptionProvider.hxx>
84 #include <tools/diagnose_ex.h>
85 
86 // enable the following define to let the controller listen to model changes and
87 // react on this by rebuilding the view
88 #define TEST_ENABLE_MODIFY_LISTENER
89 
90 namespace chart
91 {
92 
93 using namespace ::com::sun::star;
94 using namespace ::com::sun::star::accessibility;
95 using namespace ::com::sun::star::chart2;
96 using ::com::sun::star::uno::Reference;
97 using ::com::sun::star::uno::Sequence;
98 
ChartController(uno::Reference<uno::XComponentContext> const & xContext)99 ChartController::ChartController(uno::Reference<uno::XComponentContext> const & xContext) :
100     m_aLifeTimeManager( nullptr ),
101     m_bSuspended( false ),
102     m_xCC(xContext), //@todo is it allowed to hold this context??
103     m_aModelMutex(),
104     m_aModel( nullptr, m_aModelMutex ),
105     m_xViewWindow(),
106     m_xChartView(),
107     m_pDrawModelWrapper(),
108     m_eDragMode(SdrDragMode::Move),
109     m_bWaitingForDoubleClick(false),
110     m_bWaitingForMouseUp(false),
111     m_bFieldButtonDown(false),
112     m_bConnectingToView(false),
113     m_bDisposed(false),
114     m_aDispatchContainer( m_xCC ),
115     m_eDrawMode( CHARTDRAW_SELECT ),
116     mpSelectionChangeHandler(new svx::sidebar::SelectionChangeHandler(
117             [this]() { return this->GetContextName(); },
118                 this, vcl::EnumContext::Context::Cell))
119 {
120     m_aDoubleClickTimer.SetInvokeHandler( LINK( this, ChartController, DoubleClickWaitingHdl ) );
121 }
122 
~ChartController()123 ChartController::~ChartController()
124 {
125     stopDoubleClickWaiting();
126 }
127 
TheModel(const uno::Reference<frame::XModel> & xModel)128 ChartController::TheModel::TheModel( const uno::Reference< frame::XModel > & xModel ) :
129     m_xModel( xModel ),
130     m_bOwnership( true )
131 {
132     m_xCloseable =
133         uno::Reference< util::XCloseable >( xModel, uno::UNO_QUERY );
134 }
135 
~TheModel()136 ChartController::TheModel::~TheModel()
137 {
138 }
139 
addListener(ChartController * pController)140 void ChartController::TheModel::addListener( ChartController* pController )
141 {
142     if(m_xCloseable.is())
143     {
144         //if you need to be able to veto against the destruction of the model
145         // you must add as a close listener
146 
147         //otherwise you 'can' add as closelistener or 'must' add as dispose event listener
148 
149         m_xCloseable->addCloseListener(
150             static_cast<util::XCloseListener*>(pController) );
151     }
152     else if( m_xModel.is() )
153     {
154         //we need to add as dispose event listener
155         m_xModel->addEventListener(
156             static_cast<util::XCloseListener*>(pController) );
157     }
158 
159 }
160 
removeListener(ChartController * pController)161 void ChartController::TheModel::removeListener(  ChartController* pController )
162 {
163     if(m_xCloseable.is())
164         m_xCloseable->removeCloseListener(
165             static_cast<util::XCloseListener*>(pController) );
166 
167     else if( m_xModel.is() )
168         m_xModel->removeEventListener(
169             static_cast<util::XCloseListener*>(pController) );
170 }
171 
tryTermination()172 void ChartController::TheModel::tryTermination()
173 {
174     if(!m_bOwnership)
175         return;
176 
177     try
178     {
179         if(m_xCloseable.is())
180         {
181             try
182             {
183                 //@todo ? are we allowed to use sal_True here if we have the explicit ownership?
184                 //I think yes, because there might be other CloseListeners later in the list which might be interested still
185                 //but make sure that we do not throw the CloseVetoException here ourselves
186                 //so stop listening before trying to terminate or check the source of queryclosing event
187                 m_xCloseable->close(true);
188 
189                 m_bOwnership                = false;
190             }
191             catch( const util::CloseVetoException& )
192             {
193                 //since we have indicated to give up the ownership with parameter true in close call
194                 //the one who has thrown the CloseVetoException is the new owner
195 
196                 SAL_WARN_IF( m_bOwnership, "chart2.main", "a well known owner has caught a CloseVetoException after calling close(true)");
197                 m_bOwnership                = false;
198                 return;
199             }
200 
201         }
202         else if( m_xModel.is() )
203         {
204             //@todo correct??
205             m_xModel->dispose();
206             return;
207         }
208     }
209     catch(const uno::Exception&)
210     {
211         DBG_UNHANDLED_EXCEPTION( "chart2", "Termination of model failed" );
212     }
213 }
214 
TheModelRef(TheModel * pTheModel,osl::Mutex & rMutex)215 ChartController::TheModelRef::TheModelRef( TheModel* pTheModel, osl::Mutex& rMutex ) :
216     m_rModelMutex(rMutex)
217 {
218     osl::Guard< osl::Mutex > aGuard( m_rModelMutex );
219     m_xTheModel = pTheModel;
220 }
TheModelRef(const TheModelRef & rTheModel,::osl::Mutex & rMutex)221 ChartController::TheModelRef::TheModelRef( const TheModelRef& rTheModel, ::osl::Mutex& rMutex ) :
222     m_rModelMutex(rMutex)
223 {
224     osl::Guard< osl::Mutex > aGuard( m_rModelMutex );
225     m_xTheModel = rTheModel.m_xTheModel;
226 }
operator =(TheModel * pTheModel)227 ChartController::TheModelRef& ChartController::TheModelRef::operator=(TheModel* pTheModel)
228 {
229     osl::Guard< osl::Mutex > aGuard( m_rModelMutex );
230     m_xTheModel = pTheModel;
231     return *this;
232 }
operator =(const TheModelRef & rTheModel)233 ChartController::TheModelRef& ChartController::TheModelRef::operator=(const TheModelRef& rTheModel)
234 {
235     osl::Guard< osl::Mutex > aGuard( m_rModelMutex );
236     m_xTheModel = rTheModel.operator->();
237     return *this;
238 }
~TheModelRef()239 ChartController::TheModelRef::~TheModelRef()
240 {
241     osl::Guard< osl::Mutex > aGuard( m_rModelMutex );
242     m_xTheModel.clear();
243 }
is() const244 bool ChartController::TheModelRef::is() const
245 {
246     return m_xTheModel.is();
247 }
248 
249 namespace {
250 
getChartType(const css::uno::Reference<css::chart2::XChartDocument> & xChartDoc)251 css::uno::Reference<css::chart2::XChartType> getChartType(
252         const css::uno::Reference<css::chart2::XChartDocument>& xChartDoc)
253 {
254     Reference <chart2::XDiagram > xDiagram = xChartDoc->getFirstDiagram();
255     if (!xDiagram.is()) {
256         return css::uno::Reference<css::chart2::XChartType>();
257     }
258 
259     Reference< chart2::XCoordinateSystemContainer > xCooSysContainer( xDiagram, uno::UNO_QUERY_THROW );
260 
261     Sequence< Reference< chart2::XCoordinateSystem > > xCooSysSequence( xCooSysContainer->getCoordinateSystems());
262     if (!xCooSysSequence.hasElements()) {
263         return css::uno::Reference<css::chart2::XChartType>();
264     }
265 
266     Reference< chart2::XChartTypeContainer > xChartTypeContainer( xCooSysSequence[0], uno::UNO_QUERY_THROW );
267 
268     Sequence< Reference< chart2::XChartType > > xChartTypeSequence( xChartTypeContainer->getChartTypes() );
269 
270     return xChartTypeSequence[0];
271 }
272 
273 }
274 
GetContextName()275 OUString ChartController::GetContextName()
276 {
277     if (m_bDisposed)
278         return OUString();
279 
280     uno::Any aAny = getSelection();
281     if (!aAny.hasValue())
282         return "Chart";
283 
284     OUString aCID;
285     aAny >>= aCID;
286 
287     if (aCID.isEmpty())
288         return "Chart";
289 
290     ObjectType eObjectID = ObjectIdentifier::getObjectType(aCID);
291     switch (eObjectID)
292     {
293         case OBJECTTYPE_DATA_SERIES:
294             return "Series";
295         case OBJECTTYPE_DATA_ERRORS_X:
296         case OBJECTTYPE_DATA_ERRORS_Y:
297         case OBJECTTYPE_DATA_ERRORS_Z:
298             return "ErrorBar";
299         case OBJECTTYPE_AXIS:
300             return "Axis";
301         case OBJECTTYPE_GRID:
302             return "Grid";
303         case OBJECTTYPE_DIAGRAM:
304             {
305                 css::uno::Reference<css::chart2::XChartType> xChartType = getChartType(css::uno::Reference<css::chart2::XChartDocument>(getModel(), uno::UNO_QUERY));
306                 if (xChartType.is() && xChartType->getChartType() == "com.sun.star.chart2.PieChartType")
307                     return "ChartElements";
308                 break;
309             }
310         case OBJECTTYPE_DATA_CURVE:
311         case OBJECTTYPE_DATA_AVERAGE_LINE:
312             return "Trendline";
313         default:
314         break;
315     }
316 
317     return "Chart";
318 }
319 
320 // private methods
321 
impl_isDisposedOrSuspended() const322 bool ChartController::impl_isDisposedOrSuspended() const
323 {
324     if( m_aLifeTimeManager.impl_isDisposed() )
325         return true;
326 
327     if( m_bSuspended )
328     {
329         OSL_FAIL( "This Controller is suspended" );
330         return true;
331     }
332     return false;
333 }
334 
335 // lang::XServiceInfo
336 
getImplementationName()337 OUString SAL_CALL ChartController::getImplementationName()
338 {
339     return CHART_CONTROLLER_SERVICE_IMPLEMENTATION_NAME;
340 }
341 
supportsService(const OUString & rServiceName)342 sal_Bool SAL_CALL ChartController::supportsService( const OUString& rServiceName )
343 {
344     return cppu::supportsService(this, rServiceName);
345 }
346 
getSupportedServiceNames()347 css::uno::Sequence< OUString > SAL_CALL ChartController::getSupportedServiceNames()
348 {
349     return {
350         CHART_CONTROLLER_SERVICE_NAME,
351         "com.sun.star.frame.Controller"
352         //// @todo : add additional services if you support any further
353     };
354 }
355 
356 namespace {
357 
getSidebarFromModel(const uno::Reference<frame::XModel> & xModel)358 uno::Reference<ui::XSidebar> getSidebarFromModel(const uno::Reference<frame::XModel>& xModel)
359 {
360     uno::Reference<container::XChild> xChild(xModel, uno::UNO_QUERY);
361     if (!xChild.is())
362         return nullptr;
363 
364     uno::Reference<frame::XModel> xParent (xChild->getParent(), uno::UNO_QUERY);
365     if (!xParent.is())
366         return nullptr;
367 
368     uno::Reference<frame::XController2> xController(xParent->getCurrentController(), uno::UNO_QUERY);
369     if (!xController.is())
370         return nullptr;
371 
372     uno::Reference<ui::XSidebarProvider> xSidebarProvider  = xController->getSidebar();
373     if (!xSidebarProvider.is())
374         return nullptr;
375 
376     return xSidebarProvider->getSidebar();
377 }
378 
379 }
380 
381 // XController
382 
attachFrame(const uno::Reference<frame::XFrame> & xFrame)383 void SAL_CALL ChartController::attachFrame(
384     const uno::Reference<frame::XFrame>& xFrame )
385 {
386     SolarMutexGuard aGuard;
387 
388     if( impl_isDisposedOrSuspended() ) //@todo? allow attaching the frame while suspended?
389         return; //behave passive if already disposed or suspended
390 
391     mpSelectionChangeHandler->Connect();
392 
393     uno::Reference<ui::XSidebar> xSidebar = getSidebarFromModel(getModel());
394     if (xSidebar.is())
395     {
396         auto pSidebar = dynamic_cast<sfx2::sidebar::SidebarController*>(xSidebar.get());
397         assert(pSidebar);
398         sfx2::sidebar::SidebarController::registerSidebarForFrame(pSidebar, this);
399         pSidebar->updateModel(getModel());
400         css::lang::EventObject aEvent;
401         mpSelectionChangeHandler->selectionChanged(aEvent);
402     }
403 
404     if(m_xFrame.is()) //what happens, if we do have a Frame already??
405     {
406         //@todo? throw exception?
407         OSL_FAIL( "there is already a frame attached to the controller" );
408         return;
409     }
410 
411     //--attach frame
412     m_xFrame = xFrame; //the frameloader is responsible to call xFrame->setComponent
413 
414     //add as disposelistener to the frame (due to persistent reference) ??...:
415 
416     //the frame is considered to be owner of this controller and will live longer than we do
417     //the frame or the disposer of the frame has the duty to call suspend and dispose on this object
418     //so we do not need to add as lang::XEventListener for DisposingEvents right?
419 
420     //@todo nothing right???
421 
422     //create view @todo is this the correct place here??
423 
424     vcl::Window* pParent = nullptr;
425     //get the window parent from the frame to use as parent for our new window
426     if(xFrame.is())
427     {
428         uno::Reference<awt::XWindow> xContainerWindow = xFrame->getContainerWindow();
429         if (xContainerWindow)
430             xContainerWindow->setVisible(true);
431         pParent = VCLUnoHelper::GetWindow( xContainerWindow );
432     }
433 
434     {
435         // calls to VCL
436         SolarMutexGuard aSolarGuard;
437         auto pChartWindow = VclPtr<ChartWindow>::Create(this,pParent,pParent?pParent->GetStyle():0);
438         pChartWindow->SetBackground();//no Background
439         m_xViewWindow.set( pChartWindow->GetComponentInterface(), uno::UNO_QUERY );
440         pChartWindow->Show();
441         m_apDropTargetHelper.reset(
442             new ChartDropTargetHelper( pChartWindow->GetDropTarget(),
443                                        uno::Reference< chart2::XChartDocument >( getModel(), uno::UNO_QUERY )));
444 
445         impl_createDrawViewController();
446     }
447 
448     //create the menu
449     {
450         uno::Reference< beans::XPropertySet > xPropSet( xFrame, uno::UNO_QUERY );
451         if( xPropSet.is() )
452         {
453             try
454             {
455                 uno::Reference< css::frame::XLayoutManager > xLayoutManager;
456                 xPropSet->getPropertyValue( "LayoutManager" ) >>= xLayoutManager;
457                 if ( xLayoutManager.is() )
458                 {
459                     xLayoutManager->lock();
460                     xLayoutManager->requestElement( "private:resource/menubar/menubar" );
461                     //@todo: createElement should become unnecessary, remove when #i79198# is fixed
462                     xLayoutManager->createElement( "private:resource/toolbar/standardbar" );
463                     xLayoutManager->requestElement( "private:resource/toolbar/standardbar" );
464                     //@todo: createElement should become unnecessary, remove when #i79198# is fixed
465                     xLayoutManager->createElement( "private:resource/toolbar/toolbar" );
466                     xLayoutManager->requestElement( "private:resource/toolbar/toolbar" );
467 
468                     // #i12587# support for shapes in chart
469                     xLayoutManager->createElement( "private:resource/toolbar/drawbar" );
470                     xLayoutManager->requestElement( "private:resource/toolbar/drawbar" );
471 
472                     xLayoutManager->requestElement( "private:resource/statusbar/statusbar" );
473                     xLayoutManager->unlock();
474 
475                     // add as listener to get notified when
476                     m_xLayoutManagerEventBroadcaster.set( xLayoutManager, uno::UNO_QUERY );
477                     if( m_xLayoutManagerEventBroadcaster.is())
478                         m_xLayoutManagerEventBroadcaster->addLayoutManagerEventListener( this );
479                 }
480             }
481             catch( const uno::Exception & )
482             {
483                 DBG_UNHANDLED_EXCEPTION("chart2");
484             }
485         }
486     }
487 }
488 
489 //XModeChangeListener
modeChanged(const util::ModeChangeEvent & rEvent)490 void SAL_CALL ChartController::modeChanged( const util::ModeChangeEvent& rEvent )
491 {
492     SolarMutexGuard aGuard;
493     auto pChartWindow(GetChartWindow());
494     //adjust controller to view status changes
495 
496     if( rEvent.NewMode == "dirty" )
497     {
498         //the view has become dirty, we should repaint it if we have a window
499         if( pChartWindow )
500             pChartWindow->ForceInvalidate();
501     }
502     else if( rEvent.NewMode == "invalid" )
503     {
504         //the view is about to become invalid so end all actions on it
505         impl_invalidateAccessible();
506         if( m_pDrawViewWrapper && m_pDrawViewWrapper->IsTextEdit() )
507             this->EndTextEdit();
508         if( m_pDrawViewWrapper )
509         {
510             m_pDrawViewWrapper->UnmarkAll();
511             m_pDrawViewWrapper->HideSdrPage();
512         }
513     }
514     else
515     {
516         //the view was rebuild so we can start some actions on it again
517         if( !m_bConnectingToView )
518         {
519             if(pChartWindow && m_aModel.is() )
520             {
521                 m_bConnectingToView = true;
522 
523                 GetDrawModelWrapper();
524                 if(m_pDrawModelWrapper)
525                 {
526                     {
527                         if( m_pDrawViewWrapper )
528                             m_pDrawViewWrapper->ReInit();
529                     }
530 
531                     //reselect object
532                     if( m_aSelection.hasSelection() )
533                         this->impl_selectObjectAndNotiy();
534                     else
535                         ChartModelHelper::triggerRangeHighlighting( getModel() );
536 
537                     impl_initializeAccessible();
538 
539                     {
540                         if( pChartWindow )
541                             pChartWindow->Invalidate();
542                     }
543                 }
544 
545                 m_bConnectingToView = false;
546             }
547         }
548     }
549 }
550 
attachModel(const uno::Reference<frame::XModel> & xModel)551 sal_Bool SAL_CALL ChartController::attachModel( const uno::Reference< frame::XModel > & xModel )
552 {
553     impl_invalidateAccessible();
554 
555     //is called to attach the controller to a new model.
556     //return true if attach was successfully, false otherwise (e.g. if you do not work with a model)
557 
558     SolarMutexResettableGuard aGuard;
559     if( impl_isDisposedOrSuspended() ) //@todo? allow attaching a new model while suspended?
560         return false; //behave passive if already disposed or suspended
561     aGuard.clear();
562 
563     TheModelRef aNewModelRef( new TheModel( xModel), m_aModelMutex);
564     TheModelRef aOldModelRef(m_aModel,m_aModelMutex);
565     m_aModel = aNewModelRef;
566 
567     //--handle relations to the old model if any
568     if( aOldModelRef.is() )
569     {
570         uno::Reference< util::XModeChangeBroadcaster > xViewBroadcaster( m_xChartView, uno::UNO_QUERY );
571         if( xViewBroadcaster.is() )
572             xViewBroadcaster->removeModeChangeListener(this);
573         m_pDrawModelWrapper.reset();
574 
575         aOldModelRef->removeListener( this );
576  #ifdef TEST_ENABLE_MODIFY_LISTENER
577         uno::Reference< util::XModifyBroadcaster > xMBroadcaster( aOldModelRef->getModel(),uno::UNO_QUERY );
578         if( xMBroadcaster.is())
579             xMBroadcaster->removeModifyListener( this );
580 #endif
581     }
582 
583     //--handle relations to the new model
584     aNewModelRef->addListener( this );
585 
586     aGuard.reset(); // lock for m_aDispatchContainer access
587     // set new model at dispatchers
588     m_aDispatchContainer.setModel( aNewModelRef->getModel());
589     rtl::Reference<ControllerCommandDispatch> pDispatch = new ControllerCommandDispatch( m_xCC, this, &m_aDispatchContainer );
590     pDispatch->initialize();
591 
592     // the dispatch container will return "this" for all commands returned by
593     // impl_getAvailableCommands().  That means, for those commands dispatch()
594     // is called here at the ChartController.
595     m_aDispatchContainer.setChartDispatch( pDispatch, impl_getAvailableCommands() );
596 
597     rtl::Reference<DrawCommandDispatch> pDrawDispatch = new DrawCommandDispatch( m_xCC, this );
598     pDrawDispatch->initialize();
599     m_aDispatchContainer.setDrawCommandDispatch( pDrawDispatch.get() );
600 
601     rtl::Reference<ShapeController> pShapeController = new ShapeController( m_xCC, this );
602     pShapeController->initialize();
603     m_aDispatchContainer.setShapeController( pShapeController.get() );
604     aGuard.clear();
605 
606 #ifdef TEST_ENABLE_MODIFY_LISTENER
607     uno::Reference< util::XModifyBroadcaster > xMBroadcaster( aNewModelRef->getModel(),uno::UNO_QUERY );
608     if( xMBroadcaster.is())
609         xMBroadcaster->addModifyListener( this );
610 #endif
611 
612     // #i119999# Do not do this per default to allow the user to deselect the chart OLE with a single press to ESC
613     // select chart area per default:
614     // select( uno::Any( ObjectIdentifier::createClassifiedIdentifier( OBJECTTYPE_PAGE, OUString() ) ) );
615 
616     uno::Reference< lang::XMultiServiceFactory > xFact( getModel(), uno::UNO_QUERY );
617     if( xFact.is())
618     {
619         m_xChartView = xFact->createInstance( CHART_VIEW_SERVICE_NAME );
620         GetDrawModelWrapper();
621         uno::Reference< util::XModeChangeBroadcaster > xViewBroadcaster( m_xChartView, uno::UNO_QUERY );
622         if( xViewBroadcaster.is() )
623             xViewBroadcaster->addModeChangeListener(this);
624     }
625 
626     //the frameloader is responsible to call xModel->connectController
627     {
628         SolarMutexGuard aGuard2;
629         auto pChartWindow(GetChartWindow());
630         if( pChartWindow )
631             pChartWindow->Invalidate();
632     }
633 
634     uno::Reference< document::XUndoManagerSupplier > xSuppUndo( getModel(), uno::UNO_QUERY_THROW );
635     m_xUndoManager.set( xSuppUndo->getUndoManager(), uno::UNO_SET_THROW );
636 
637     return true;
638 }
639 
getFrame()640 uno::Reference< frame::XFrame > SAL_CALL ChartController::getFrame()
641 {
642     //provides access to owner frame of this controller
643     //return the frame containing this controller
644 
645     return m_xFrame;
646 }
647 
getModel()648 uno::Reference< frame::XModel > SAL_CALL ChartController::getModel()
649 {
650     //provides access to currently attached model
651     //returns the currently attached model
652 
653     //return nothing, if you do not have a model
654     TheModelRef aModelRef( m_aModel, m_aModelMutex);
655     if(aModelRef.is())
656         return aModelRef->getModel();
657 
658     return uno::Reference< frame::XModel > ();
659 }
660 
getViewData()661 uno::Any SAL_CALL ChartController::getViewData()
662 {
663     //provides access to current view status
664     //set of data that can be used to restore the current view status at later time
665     //  by using XController::restoreViewData()
666 
667     SolarMutexGuard aGuard;
668     if( impl_isDisposedOrSuspended() )
669         return uno::Any(); //behave passive if already disposed or suspended //@todo? or throw an exception??
670 
671     //-- collect current view state
672     uno::Any aRet;
673     //// @todo integrate specialized implementation
674 
675     return aRet;
676 }
677 
restoreViewData(const uno::Any &)678 void SAL_CALL ChartController::restoreViewData(
679     const uno::Any& /* Value */ )
680 {
681     //restores the view status using the data gotten from a previous call to XController::getViewData()
682 
683     SolarMutexGuard aGuard;
684     if( impl_isDisposedOrSuspended() )
685         return; //behave passive if already disposed or suspended //@todo? or throw an exception??
686 
687     //// @todo integrate specialized implementation
688 }
689 
suspend(sal_Bool bSuspend)690 sal_Bool SAL_CALL ChartController::suspend( sal_Bool bSuspend )
691 {
692     //is called to prepare the controller for closing the view
693     //bSuspend==true: force the controller to suspend his work
694     //bSuspend==false try to reactivate the controller
695     //returns true if request was accepted and of course successfully finished, false otherwise
696 
697     //we may show dialogs here to ask the user for saving changes ... @todo?
698 
699     SolarMutexGuard aGuard;
700     if( m_aLifeTimeManager.impl_isDisposed() )
701         return false; //behave passive if already disposed, return false because request was not accepted //@todo? correct
702 
703     if(bool(bSuspend) == m_bSuspended)
704     {
705         OSL_FAIL( "new suspend mode equals old suspend mode" );
706         return true;
707     }
708 
709     //change suspend mode
710     m_bSuspended = bSuspend;
711     return true;
712 }
713 
impl_createDrawViewController()714 void ChartController::impl_createDrawViewController()
715 {
716     SolarMutexGuard aGuard;
717     if(!m_pDrawViewWrapper)
718     {
719         if( m_pDrawModelWrapper )
720         {
721             m_pDrawViewWrapper.reset( new DrawViewWrapper(m_pDrawModelWrapper->getSdrModel(),GetChartWindow()->GetOutDev()) );
722             m_pDrawViewWrapper->attachParentReferenceDevice( getModel() );
723         }
724     }
725 }
726 
impl_deleteDrawViewController()727 void ChartController::impl_deleteDrawViewController()
728 {
729     if( m_pDrawViewWrapper )
730     {
731         SolarMutexGuard aGuard;
732         if( m_pDrawViewWrapper->IsTextEdit() )
733             this->EndTextEdit();
734         m_pDrawViewWrapper.reset();
735     }
736 }
737 
738 // XComponent (base of XController)
739 
dispose()740 void SAL_CALL ChartController::dispose()
741 {
742     m_bDisposed = true;
743 
744     mpSelectionChangeHandler->selectionChanged(css::lang::EventObject());
745     mpSelectionChangeHandler->Disconnect();
746 
747     if (getModel().is())
748     {
749         uno::Reference<ui::XSidebar> xSidebar = getSidebarFromModel(getModel());
750         if (sfx2::sidebar::SidebarController* pSidebar = dynamic_cast<sfx2::sidebar::SidebarController*>(xSidebar.get()))
751         {
752             sfx2::sidebar::SidebarController::unregisterSidebarForFrame(pSidebar, this);
753         }
754     }
755 
756     try
757     {
758         //This object should release all resources and references in the
759         //easiest possible manner
760         //This object must notify all registered listeners using the method
761         //<member>XEventListener::disposing</member>
762 
763         //hold no mutex
764         if( !m_aLifeTimeManager.dispose() )
765             return;
766 
767 //  OSL_ENSURE( m_bSuspended, "dispose was called but controller is not suspended" );
768 
769         this->stopDoubleClickWaiting();
770 
771         //end range highlighting
772         if( m_aModel.is())
773         {
774             uno::Reference< view::XSelectionChangeListener > xSelectionChangeListener;
775             uno::Reference< chart2::data::XDataReceiver > xDataReceiver( getModel(), uno::UNO_QUERY );
776             if( xDataReceiver.is() )
777                 xSelectionChangeListener.set( xDataReceiver->getRangeHighlighter(), uno::UNO_QUERY );
778             if( xSelectionChangeListener.is() )
779             {
780                 uno::Reference< frame::XController > xController( this );
781                 lang::EventObject aEvent( xController );
782                 xSelectionChangeListener->disposing( aEvent );
783             }
784         }
785 
786         //--release all resources and references
787         {
788             uno::Reference< util::XModeChangeBroadcaster > xViewBroadcaster( m_xChartView, uno::UNO_QUERY );
789             if( xViewBroadcaster.is() )
790                 xViewBroadcaster->removeModeChangeListener(this);
791 
792             impl_invalidateAccessible();
793             SolarMutexGuard aSolarGuard;
794             impl_deleteDrawViewController();
795             m_pDrawModelWrapper.reset();
796 
797             m_apDropTargetHelper.reset();
798 
799             //the accessible view is disposed within window destructor of m_pChartWindow
800             if(m_xViewWindow.is())
801                 m_xViewWindow->dispose(); //ChartWindow is deleted via UNO due to dispose of m_xViewWindow (triggered by Framework (Controller pretends to be XWindow also))
802             m_xChartView.clear();
803         }
804 
805         // remove as listener to layout manager events
806         if( m_xLayoutManagerEventBroadcaster.is())
807         {
808             m_xLayoutManagerEventBroadcaster->removeLayoutManagerEventListener( this );
809             m_xLayoutManagerEventBroadcaster.set( nullptr );
810         }
811 
812         m_xFrame.clear();
813         m_xUndoManager.clear();
814 
815         TheModelRef aModelRef( m_aModel, m_aModelMutex);
816         m_aModel = nullptr;
817 
818         if( aModelRef.is())
819         {
820             uno::Reference< frame::XModel > xModel( aModelRef->getModel() );
821             if(xModel.is())
822                 xModel->disconnectController( uno::Reference< frame::XController >( this ));
823 
824             aModelRef->removeListener( this );
825 #ifdef TEST_ENABLE_MODIFY_LISTENER
826             try
827             {
828                 uno::Reference< util::XModifyBroadcaster > xMBroadcaster( aModelRef->getModel(),uno::UNO_QUERY );
829                 if( xMBroadcaster.is())
830                     xMBroadcaster->removeModifyListener( this );
831             }
832             catch( const uno::Exception & )
833             {
834                 DBG_UNHANDLED_EXCEPTION("chart2");
835             }
836 #endif
837             aModelRef->tryTermination();
838         }
839 
840         //// @todo integrate specialized implementation
841         //e.g. release further resources and references
842 
843         SolarMutexGuard g;
844         m_aDispatchContainer.DisposeAndClear();
845     }
846     catch( const uno::Exception & )
847     {
848         DBG_UNHANDLED_EXCEPTION("chart2");
849         assert(!m_xChartView.is());
850     }
851  }
852 
addEventListener(const uno::Reference<lang::XEventListener> & xListener)853 void SAL_CALL ChartController::addEventListener(
854     const uno::Reference<lang::XEventListener>& xListener )
855 {
856     SolarMutexGuard aGuard;
857     if( impl_isDisposedOrSuspended() )//@todo? allow adding of listeners in suspend mode?
858         return; //behave passive if already disposed or suspended
859 
860     //--add listener
861     m_aLifeTimeManager.m_aListenerContainer.addInterface( cppu::UnoType<lang::XEventListener>::get(), xListener );
862 }
863 
removeEventListener(const uno::Reference<lang::XEventListener> & xListener)864 void SAL_CALL ChartController::removeEventListener(
865     const uno::Reference<lang::XEventListener>& xListener )
866 {
867     SolarMutexGuard aGuard;
868     if( m_aLifeTimeManager.impl_isDisposed(false) )
869         return; //behave passive if already disposed or suspended
870 
871     //--remove listener
872     m_aLifeTimeManager.m_aListenerContainer.removeInterface( cppu::UnoType<lang::XEventListener>::get(), xListener );
873 }
874 
875 // util::XCloseListener
queryClosing(const lang::EventObject & rSource,sal_Bool)876 void SAL_CALL ChartController::queryClosing(
877     const lang::EventObject& rSource,
878     sal_Bool /*bGetsOwnership*/ )
879 {
880     //do not use the m_aControllerMutex here because this call is not allowed to block
881 
882     TheModelRef aModelRef( m_aModel, m_aModelMutex);
883 
884     if( !aModelRef.is() )
885         return;
886 
887     if( aModelRef->getModel() != rSource.Source )
888     {
889         OSL_FAIL( "queryClosing was called on a controller from an unknown source" );
890         return;
891     }
892 
893     //@ todo prepare to closing model -> don't start any further hindering actions
894 }
895 
notifyClosing(const lang::EventObject & rSource)896 void SAL_CALL ChartController::notifyClosing(
897     const lang::EventObject& rSource )
898 {
899     //Listener should deregister himself and release all references to the closing object.
900 
901     TheModelRef aModelRef( m_aModel, m_aModelMutex);
902     if( !impl_releaseThisModel( rSource.Source ) )
903         return;
904 
905     //--stop listening to the closing model
906     aModelRef->removeListener( this );
907 
908     // #i79087# If the model using this controller is closed, the frame is
909     // expected to be closed as well
910     Reference< util::XCloseable > xFrameCloseable( m_xFrame, uno::UNO_QUERY );
911     if( xFrameCloseable.is())
912     {
913         try
914         {
915             xFrameCloseable->close( false /* DeliverOwnership */ );
916             m_xFrame.clear();
917         }
918         catch( const util::CloseVetoException & )
919         {
920             // closing was vetoed
921         }
922     }
923 }
924 
impl_releaseThisModel(const uno::Reference<uno::XInterface> & xModel)925 bool ChartController::impl_releaseThisModel(
926     const uno::Reference< uno::XInterface > & xModel )
927 {
928     bool bReleaseModel = false;
929     {
930         ::osl::Guard< ::osl::Mutex > aGuard( m_aModelMutex );
931         if( m_aModel.is() && m_aModel->getModel() == xModel )
932         {
933             m_aModel = nullptr;
934             m_xUndoManager.clear();
935             bReleaseModel = true;
936         }
937     }
938     if( bReleaseModel )
939     {
940         SolarMutexGuard g;
941         m_aDispatchContainer.setModel( nullptr );
942     }
943     return bReleaseModel;
944 }
945 
946 // util::XEventListener (base of XCloseListener)
disposing(const lang::EventObject & rSource)947 void SAL_CALL ChartController::disposing(
948     const lang::EventObject& rSource )
949 {
950     if( !impl_releaseThisModel( rSource.Source ))
951     {
952         if( rSource.Source == m_xLayoutManagerEventBroadcaster )
953             m_xLayoutManagerEventBroadcaster.set( nullptr );
954     }
955 }
956 
layoutEvent(const lang::EventObject & aSource,sal_Int16 eLayoutEvent,const uno::Any &)957 void SAL_CALL ChartController::layoutEvent(
958     const lang::EventObject& aSource,
959     sal_Int16 eLayoutEvent,
960     const uno::Any& /* aInfo */ )
961 {
962     if( eLayoutEvent == frame::LayoutManagerEvents::MERGEDMENUBAR )
963     {
964         Reference< frame::XLayoutManager > xLM( aSource.Source, uno::UNO_QUERY );
965         if( xLM.is())
966         {
967             xLM->createElement(  "private:resource/statusbar/statusbar" );
968             xLM->requestElement( "private:resource/statusbar/statusbar" );
969         }
970     }
971 }
972 
973 // XDispatchProvider (required interface)
974 
975 namespace
976 {
977 
lcl_isFormatObjectCommand(std::u16string_view aCommand)978 bool lcl_isFormatObjectCommand( std::u16string_view aCommand )
979 {
980     return aCommand == u"MainTitle"
981         || aCommand == u"SubTitle"
982         || aCommand == u"XTitle"
983         || aCommand == u"YTitle"
984         || aCommand == u"ZTitle"
985         || aCommand == u"SecondaryXTitle"
986         || aCommand == u"SecondaryYTitle"
987         || aCommand == u"AllTitles"
988         || aCommand == u"DiagramAxisX"
989         || aCommand == u"DiagramAxisY"
990         || aCommand == u"DiagramAxisZ"
991         || aCommand == u"DiagramAxisA"
992         || aCommand == u"DiagramAxisB"
993         || aCommand == u"DiagramAxisAll"
994         || aCommand == u"DiagramGridXMain"
995         || aCommand == u"DiagramGridYMain"
996         || aCommand == u"DiagramGridZMain"
997         || aCommand == u"DiagramGridXHelp"
998         || aCommand == u"DiagramGridYHelp"
999         || aCommand == u"DiagramGridZHelp"
1000         || aCommand == u"DiagramGridAll"
1001 
1002         || aCommand == u"DiagramWall"
1003         || aCommand == u"DiagramFloor"
1004         || aCommand == u"DiagramArea"
1005         || aCommand == u"Legend"
1006 
1007         || aCommand == u"FormatWall"
1008         || aCommand == u"FormatFloor"
1009         || aCommand == u"FormatChartArea"
1010         || aCommand == u"FormatLegend"
1011 
1012         || aCommand == u"FormatTitle"
1013         || aCommand == u"FormatAxis"
1014         || aCommand == u"FormatDataSeries"
1015         || aCommand == u"FormatDataPoint"
1016         || aCommand == u"FormatDataLabels"
1017         || aCommand == u"FormatDataLabel"
1018         || aCommand == u"FormatXErrorBars"
1019         || aCommand == u"FormatYErrorBars"
1020         || aCommand == u"FormatMeanValue"
1021         || aCommand == u"FormatTrendline"
1022         || aCommand == u"FormatTrendlineEquation"
1023         || aCommand == u"FormatStockLoss"
1024         || aCommand == u"FormatStockGain"
1025         || aCommand == u"FormatMajorGrid"
1026         || aCommand == u"FormatMinorGrid";
1027 }
1028 
1029 } // anonymous namespace
1030 
1031 uno::Reference<frame::XDispatch> SAL_CALL
queryDispatch(const util::URL & rURL,const OUString & rTargetFrameName,sal_Int32)1032     ChartController::queryDispatch(
1033         const util::URL& rURL,
1034         const OUString& rTargetFrameName,
1035         sal_Int32 /* nSearchFlags */)
1036 {
1037     SolarMutexGuard aGuard;
1038 
1039     if ( !m_aLifeTimeManager.impl_isDisposed() && getModel().is() )
1040     {
1041         if( !rTargetFrameName.isEmpty() && rTargetFrameName == "_self" )
1042             return m_aDispatchContainer.getDispatchForURL( rURL );
1043     }
1044     return uno::Reference< frame::XDispatch > ();
1045 }
1046 
1047 uno::Sequence<uno::Reference<frame::XDispatch > >
queryDispatches(const uno::Sequence<frame::DispatchDescriptor> & xDescripts)1048     ChartController::queryDispatches(
1049         const uno::Sequence<frame::DispatchDescriptor>& xDescripts )
1050 {
1051     SolarMutexGuard g;
1052 
1053     if ( !m_aLifeTimeManager.impl_isDisposed() )
1054     {
1055         return m_aDispatchContainer.getDispatchesForURLs( xDescripts );
1056     }
1057     return uno::Sequence<uno::Reference<frame::XDispatch > > ();
1058 }
1059 
1060 // frame::XDispatch
1061 
dispatch(const util::URL & rURL,const uno::Sequence<beans::PropertyValue> & rArgs)1062 void SAL_CALL ChartController::dispatch(
1063     const util::URL& rURL,
1064     const uno::Sequence< beans::PropertyValue >& rArgs )
1065 {
1066     OUString aCommand = rURL.Path;
1067 
1068     if(aCommand == "LOKSetTextSelection")
1069     {
1070         if (rArgs.getLength() == 3)
1071         {
1072             sal_Int32 nType = -1;
1073             rArgs[0].Value >>= nType;
1074             sal_Int32 nX = 0;
1075             rArgs[1].Value >>= nX;
1076             sal_Int32 nY = 0;
1077             rArgs[2].Value >>= nY;
1078             executeDispatch_LOKSetTextSelection(nType, nX, nY);
1079         }
1080     }
1081     else if (aCommand == "LOKTransform")
1082     {
1083         if (rArgs[0].Name == "Action")
1084         {
1085             OUString sAction;
1086             if ((rArgs[0].Value >>= sAction) && sAction == "PieSegmentDragging")
1087             {
1088                 if (rArgs[1].Name == "Offset")
1089                 {
1090                     sal_Int32 nOffset;
1091                     if (rArgs[1].Value >>= nOffset)
1092                     {
1093                         this->executeDispatch_LOKPieSegmentDragging(nOffset);
1094                     }
1095                 }
1096             }
1097         }
1098         else
1099         {
1100             this->executeDispatch_PositionAndSize(&rArgs);
1101         }
1102     }
1103     else if(aCommand == "FillColor")
1104     {
1105         if (rArgs.getLength() > 0)
1106         {
1107             sal_uInt32 nColor;
1108             if (rArgs[0].Value >>= nColor)
1109                 this->executeDispatch_FillColor(nColor);
1110         }
1111     }
1112     else if(aCommand == "XLineColor")
1113     {
1114         if (rArgs.getLength() > 0)
1115         {
1116             sal_Int32 nColor = -1;
1117             rArgs[0].Value >>= nColor;
1118             this->executeDispatch_LineColor(nColor);
1119         }
1120     }
1121     else if(aCommand == "LineWidth")
1122     {
1123         if (rArgs.getLength() > 0)
1124         {
1125             sal_Int32 nWidth = -1;
1126             rArgs[0].Value >>= nWidth;
1127             this->executeDispatch_LineWidth(nWidth);
1128         }
1129     }
1130     else if(aCommand.startsWith("FillGradient"))
1131     {
1132         this->executeDispatch_FillGradient(aCommand.copy(aCommand.indexOf('=') + 1));
1133     }
1134     else if(aCommand == "Paste")
1135         this->executeDispatch_Paste();
1136     else if(aCommand == "Copy" )
1137         this->executeDispatch_Copy();
1138     else if(aCommand == "Cut" )
1139         this->executeDispatch_Cut();
1140     else if(aCommand == "DataRanges" )
1141         this->executeDispatch_SourceData();
1142     else if(aCommand == "Update" ) //Update Chart
1143     {
1144         ChartViewHelper::setViewToDirtyState( getModel() );
1145         SolarMutexGuard aGuard;
1146         auto pChartWindow(GetChartWindow());
1147         if( pChartWindow )
1148             pChartWindow->Invalidate();
1149     }
1150     else if(aCommand == "DiagramData" )
1151         this->executeDispatch_EditData();
1152     //insert objects
1153     else if( aCommand == "InsertTitles"
1154         || aCommand == "InsertMenuTitles")
1155         this->executeDispatch_InsertTitles();
1156     else if( aCommand == "InsertMenuLegend" )
1157         this->executeDispatch_OpenLegendDialog();
1158     else if( aCommand == "InsertLegend" )
1159         this->executeDispatch_InsertLegend();
1160     else if( aCommand == "DeleteLegend" )
1161         this->executeDispatch_DeleteLegend();
1162     else if( aCommand == "InsertMenuDataLabels" )
1163         this->executeDispatch_InsertMenu_DataLabels();
1164     else if( aCommand == "InsertMenuAxes"
1165         || aCommand == "InsertRemoveAxes" )
1166         this->executeDispatch_InsertAxes();
1167     else if( aCommand == "InsertMenuGrids" )
1168         this->executeDispatch_InsertGrid();
1169     else if( aCommand == "InsertMenuTrendlines" )
1170         this->executeDispatch_InsertMenu_Trendlines();
1171     else if( aCommand == "InsertMenuMeanValues" )
1172         this->executeDispatch_InsertMenu_MeanValues();
1173     else if( aCommand == "InsertMenuXErrorBars" )
1174         this->executeDispatch_InsertErrorBars(false);
1175     else if( aCommand == "InsertMenuYErrorBars" )
1176         this->executeDispatch_InsertErrorBars(true);
1177     else if( aCommand == "InsertSymbol" )
1178          this->executeDispatch_InsertSpecialCharacter();
1179     else if( aCommand == "InsertTrendline" )
1180          this->executeDispatch_InsertTrendline();
1181     else if( aCommand == "DeleteTrendline" )
1182          this->executeDispatch_DeleteTrendline();
1183     else if( aCommand == "InsertMeanValue" )
1184         this->executeDispatch_InsertMeanValue();
1185     else if( aCommand == "DeleteMeanValue" )
1186         this->executeDispatch_DeleteMeanValue();
1187     else if( aCommand == "InsertXErrorBars" )
1188         this->executeDispatch_InsertErrorBars(false);
1189     else if( aCommand == "InsertYErrorBars" )
1190         this->executeDispatch_InsertErrorBars(true);
1191     else if( aCommand == "DeleteXErrorBars" )
1192         this->executeDispatch_DeleteErrorBars(false);
1193     else if( aCommand == "DeleteYErrorBars" )
1194         this->executeDispatch_DeleteErrorBars(true);
1195     else if( aCommand == "InsertTrendlineEquation" )
1196          this->executeDispatch_InsertTrendlineEquation();
1197     else if( aCommand == "DeleteTrendlineEquation" )
1198          this->executeDispatch_DeleteTrendlineEquation();
1199     else if( aCommand == "InsertTrendlineEquationAndR2" )
1200          this->executeDispatch_InsertTrendlineEquation( true );
1201     else if( aCommand == "InsertR2Value" )
1202          this->executeDispatch_InsertR2Value();
1203     else if( aCommand == "DeleteR2Value")
1204          this->executeDispatch_DeleteR2Value();
1205     else if( aCommand == "InsertDataLabels" )
1206         this->executeDispatch_InsertDataLabels();
1207     else if( aCommand == "InsertDataLabel" )
1208         this->executeDispatch_InsertDataLabel();
1209     else if( aCommand == "DeleteDataLabels")
1210         this->executeDispatch_DeleteDataLabels();
1211     else if( aCommand == "DeleteDataLabel" )
1212         this->executeDispatch_DeleteDataLabel();
1213     else if( aCommand == "ResetAllDataPoints" )
1214         this->executeDispatch_ResetAllDataPoints();
1215     else if( aCommand == "ResetDataPoint" )
1216         this->executeDispatch_ResetDataPoint();
1217     else if( aCommand == "InsertAxis" )
1218         this->executeDispatch_InsertAxis();
1219     else if( aCommand == "InsertMajorGrid" )
1220         this->executeDispatch_InsertMajorGrid();
1221     else if( aCommand == "InsertMinorGrid" )
1222         this->executeDispatch_InsertMinorGrid();
1223     else if( aCommand == "InsertAxisTitle" )
1224         this->executeDispatch_InsertAxisTitle();
1225     else if( aCommand == "DeleteAxis" )
1226         this->executeDispatch_DeleteAxis();
1227     else if( aCommand == "DeleteMajorGrid")
1228         this->executeDispatch_DeleteMajorGrid();
1229     else if( aCommand == "DeleteMinorGrid" )
1230         this->executeDispatch_DeleteMinorGrid();
1231     //format objects
1232     else if( aCommand == "FormatSelection" )
1233         this->executeDispatch_ObjectProperties();
1234     else if( aCommand == "TransformDialog" )
1235     {
1236         if ( isShapeContext() )
1237         {
1238             this->impl_ShapeControllerDispatch( rURL, rArgs );
1239         }
1240         else
1241         {
1242             this->executeDispatch_PositionAndSize();
1243         }
1244     }
1245     else if( lcl_isFormatObjectCommand(aCommand) )
1246         this->executeDispatch_FormatObject(rURL.Path);
1247     //more format
1248     else if( aCommand == "DiagramType" )
1249         this->executeDispatch_ChartType();
1250     else if( aCommand == "View3D" )
1251         this->executeDispatch_View3D();
1252     else if ( aCommand == "Forward" )
1253     {
1254         if ( isShapeContext() )
1255         {
1256             this->impl_ShapeControllerDispatch( rURL, rArgs );
1257         }
1258         else
1259         {
1260             this->executeDispatch_MoveSeries( true );
1261         }
1262     }
1263     else if ( aCommand == "Backward" )
1264     {
1265         if ( isShapeContext() )
1266         {
1267             this->impl_ShapeControllerDispatch( rURL, rArgs );
1268         }
1269         else
1270         {
1271             this->executeDispatch_MoveSeries( false );
1272         }
1273     }
1274     else if( aCommand == "NewArrangement")
1275         this->executeDispatch_NewArrangement();
1276     else if( aCommand == "ToggleLegend" )
1277         this->executeDispatch_ToggleLegend();
1278     else if( aCommand == "ToggleGridHorizontal" )
1279         this->executeDispatch_ToggleGridHorizontal();
1280     else if( aCommand == "ToggleGridVertical" )
1281         this->executeDispatch_ToggleGridVertical();
1282     else if( aCommand == "ScaleText" )
1283         this->executeDispatch_ScaleText();
1284     else if( aCommand == "StatusBarVisible" )
1285     {
1286         // workaround: this should not be necessary.
1287         uno::Reference< beans::XPropertySet > xPropSet( m_xFrame, uno::UNO_QUERY );
1288         if( xPropSet.is() )
1289         {
1290             uno::Reference< css::frame::XLayoutManager > xLayoutManager;
1291             xPropSet->getPropertyValue( "LayoutManager" ) >>= xLayoutManager;
1292             if ( xLayoutManager.is() )
1293             {
1294                 bool bIsVisible( xLayoutManager->isElementVisible( "private:resource/statusbar/statusbar" ));
1295                 if( bIsVisible )
1296                 {
1297                     xLayoutManager->hideElement( "private:resource/statusbar/statusbar" );
1298                     xLayoutManager->destroyElement( "private:resource/statusbar/statusbar" );
1299                 }
1300                 else
1301                 {
1302                     xLayoutManager->createElement( "private:resource/statusbar/statusbar" );
1303                     xLayoutManager->showElement( "private:resource/statusbar/statusbar" );
1304                 }
1305                 // @todo: update menu state (checkmark next to "Statusbar").
1306             }
1307         }
1308     }
1309 }
1310 
addStatusListener(const uno::Reference<frame::XStatusListener> &,const util::URL &)1311 void SAL_CALL ChartController::addStatusListener(
1312     const uno::Reference<frame::XStatusListener >& /* xControl */,
1313     const util::URL& /* aURL */ )
1314 {
1315     //@todo
1316 }
1317 
removeStatusListener(const uno::Reference<frame::XStatusListener> &,const util::URL &)1318 void SAL_CALL ChartController::removeStatusListener(
1319     const uno::Reference<frame::XStatusListener >& /* xControl */,
1320     const util::URL& /* aURL */ )
1321 {
1322     //@todo
1323 }
1324 
1325 // XContextMenuInterception (optional interface)
registerContextMenuInterceptor(const uno::Reference<ui::XContextMenuInterceptor> &)1326 void SAL_CALL ChartController::registerContextMenuInterceptor(
1327     const uno::Reference< ui::XContextMenuInterceptor >& /* xInterceptor */)
1328 {
1329     //@todo
1330 }
1331 
releaseContextMenuInterceptor(const uno::Reference<ui::XContextMenuInterceptor> &)1332 void SAL_CALL ChartController::releaseContextMenuInterceptor(
1333     const uno::Reference< ui::XContextMenuInterceptor > & /* xInterceptor */)
1334 {
1335     //@todo
1336 }
1337 
1338 // ____ XEmbeddedClient ____
1339 // implementation see: ChartController_EditData.cxx
1340 
executeDispatch_ChartType()1341 void ChartController::executeDispatch_ChartType()
1342 {
1343     UndoLiveUpdateGuard aUndoGuard(
1344         SchResId( STR_ACTION_EDIT_CHARTTYPE ), m_xUndoManager );
1345 
1346     SolarMutexGuard aSolarGuard;
1347     //prepare and open dialog
1348     ChartTypeDialog aDlg(GetChartFrame(), getModel());
1349     if (aDlg.run() == RET_OK)
1350     {
1351         impl_adaptDataSeriesAutoResize();
1352         aUndoGuard.commit();
1353     }
1354 }
1355 
executeDispatch_SourceData()1356 void ChartController::executeDispatch_SourceData()
1357 {
1358     //convert properties to ItemSet
1359     uno::Reference< XChartDocument >   xChartDoc( getModel(), uno::UNO_QUERY );
1360     OSL_ENSURE( xChartDoc.is(), "Invalid XChartDocument" );
1361     if( !xChartDoc.is() )
1362         return;
1363 
1364     // If there is a data table we should ask user if we really want to destroy it
1365     // and switch to data ranges.
1366     ChartModel& rModel = dynamic_cast<ChartModel&>(*xChartDoc);
1367     if ( rModel.hasInternalDataProvider() )
1368     {
1369         // Check if we will able to create data provider later
1370         css::uno::Reference< com::sun::star::chart2::XDataProviderAccess > xCreatorDoc(
1371             rModel.getParent(), uno::UNO_QUERY);
1372         if (!xCreatorDoc.is())
1373             return;
1374 
1375         SolarMutexGuard aSolarGuard;
1376 
1377         std::unique_ptr<weld::MessageDialog> xQueryBox(Application::CreateMessageDialog(GetChartFrame(),
1378                                                        VclMessageType::Question, VclButtonsType::YesNo, SchResId(STR_DLG_REMOVE_DATA_TABLE)));
1379         // If "No" then just return
1380         if (xQueryBox->run() == RET_NO)
1381             return;
1382 
1383         // Remove data table
1384         rModel.removeDataProviders();
1385 
1386         // Ask parent document to create new data provider
1387 
1388         uno::Reference< data::XDataProvider > xDataProvider = xCreatorDoc->createDataProvider();
1389         SAL_WARN_IF( !xDataProvider.is(), "chart2.main", "Data provider was not created" );
1390         if (xDataProvider.is())
1391         {
1392             rModel.attachDataProvider(xDataProvider);
1393         }
1394     }
1395 
1396     UndoLiveUpdateGuard aUndoGuard(
1397         SchResId(STR_ACTION_EDIT_DATA_RANGES), m_xUndoManager);
1398 
1399     SolarMutexGuard aSolarGuard;
1400     ::chart::DataSourceDialog aDlg(GetChartFrame(), xChartDoc, m_xCC);
1401     if (aDlg.run() == RET_OK)
1402     {
1403         impl_adaptDataSeriesAutoResize();
1404         aUndoGuard.commit();
1405     }
1406 }
1407 
executeDispatch_MoveSeries(bool bForward)1408 void ChartController::executeDispatch_MoveSeries( bool bForward )
1409 {
1410     ControllerLockGuardUNO aCLGuard( getModel() );
1411 
1412     //get selected series
1413     OUString aObjectCID(m_aSelection.getSelectedCID());
1414     uno::Reference< XDataSeries > xGivenDataSeries( ObjectIdentifier::getDataSeriesForCID( //yyy todo also legend entries and labels?
1415             aObjectCID, getModel() ) );
1416 
1417     UndoGuardWithSelection aUndoGuard(
1418         ActionDescriptionProvider::createDescription(
1419             (bForward ? ActionDescriptionProvider::ActionType::MoveToTop : ActionDescriptionProvider::ActionType::MoveToBottom),
1420             SchResId(STR_OBJECT_DATASERIES)),
1421         m_xUndoManager );
1422 
1423     bool bChanged = DiagramHelper::moveSeries( ChartModelHelper::findDiagram( getModel() ), xGivenDataSeries, bForward );
1424     if( bChanged )
1425     {
1426         m_aSelection.setSelection( ObjectIdentifier::getMovedSeriesCID( aObjectCID, bForward ) );
1427         aUndoGuard.commit();
1428     }
1429 }
1430 
1431 // ____ XMultiServiceFactory ____
1432 uno::Reference< uno::XInterface > SAL_CALL
createInstance(const OUString & aServiceSpecifier)1433     ChartController::createInstance( const OUString& aServiceSpecifier )
1434 {
1435     uno::Reference< uno::XInterface > xResult;
1436 
1437     if( aServiceSpecifier == CHART_ACCESSIBLE_TEXT_SERVICE_NAME )
1438         xResult.set( impl_createAccessibleTextContext());
1439     return xResult;
1440 }
1441 
1442 uno::Reference< uno::XInterface > SAL_CALL
createInstanceWithArguments(const OUString & ServiceSpecifier,const uno::Sequence<uno::Any> &)1443     ChartController::createInstanceWithArguments(
1444         const OUString& ServiceSpecifier,
1445         const uno::Sequence< uno::Any >& /* Arguments */ )
1446 {
1447     // ignore Arguments
1448     return createInstance( ServiceSpecifier );
1449 }
1450 
1451 uno::Sequence< OUString > SAL_CALL
getAvailableServiceNames()1452     ChartController::getAvailableServiceNames()
1453 {
1454     uno::Sequence< OUString > aServiceNames { CHART_ACCESSIBLE_TEXT_SERVICE_NAME };
1455     return aServiceNames;
1456 }
1457 
1458 // ____ XModifyListener ____
modified(const lang::EventObject &)1459 void SAL_CALL ChartController::modified(
1460     const lang::EventObject& /* aEvent */ )
1461 {
1462     // the source can also be a subobject of the ChartModel
1463     // @todo: change the source in ChartModel to always be the model itself ?
1464     //todo? update menu states ?
1465 }
1466 
NotifyUndoActionHdl(std::unique_ptr<SdrUndoAction> pUndoAction)1467 void ChartController::NotifyUndoActionHdl( std::unique_ptr<SdrUndoAction> pUndoAction )
1468 {
1469     ENSURE_OR_RETURN_VOID( pUndoAction, "invalid Undo action" );
1470 
1471     OUString aObjectCID = m_aSelection.getSelectedCID();
1472     if ( !aObjectCID.isEmpty() )
1473         return;
1474 
1475     try
1476     {
1477         const Reference< document::XUndoManagerSupplier > xSuppUndo( getModel(), uno::UNO_QUERY_THROW );
1478         const Reference< document::XUndoManager > xUndoManager( xSuppUndo->getUndoManager(), uno::UNO_SET_THROW );
1479         const Reference< document::XUndoAction > xAction( new impl::ShapeUndoElement( std::move(pUndoAction) ) );
1480         xUndoManager->addUndoAction( xAction );
1481     }
1482     catch( const uno::Exception& )
1483     {
1484         DBG_UNHANDLED_EXCEPTION("chart2");
1485     }
1486 }
1487 
GetDrawModelWrapper()1488 DrawModelWrapper* ChartController::GetDrawModelWrapper()
1489 {
1490     if( !m_pDrawModelWrapper )
1491     {
1492         ExplicitValueProvider* pProvider = comphelper::getUnoTunnelImplementation<ExplicitValueProvider>( m_xChartView );
1493         if( pProvider )
1494             m_pDrawModelWrapper = pProvider->getDrawModelWrapper();
1495         if ( m_pDrawModelWrapper )
1496         {
1497             m_pDrawModelWrapper->getSdrModel().SetNotifyUndoActionHdl(
1498                 std::bind(&ChartController::NotifyUndoActionHdl, this, std::placeholders::_1) );
1499         }
1500     }
1501     return m_pDrawModelWrapper.get();
1502 }
1503 
GetDrawViewWrapper()1504 DrawViewWrapper* ChartController::GetDrawViewWrapper()
1505 {
1506     if ( !m_pDrawViewWrapper )
1507     {
1508         impl_createDrawViewController();
1509     }
1510     return m_pDrawViewWrapper.get();
1511 }
1512 
1513 
GetChartWindow() const1514 ChartWindow* ChartController::GetChartWindow() const
1515 {
1516     // clients getting the naked VCL Window from UNO should always have the
1517     // solar mutex (and keep it over the lifetime of this ptr), as VCL might
1518     // might deinit otherwise
1519     DBG_TESTSOLARMUTEX();
1520     if(!m_xViewWindow.is())
1521         return nullptr;
1522     return dynamic_cast<ChartWindow*>(VCLUnoHelper::GetWindow(m_xViewWindow));
1523 }
1524 
GetChartFrame()1525 weld::Window* ChartController::GetChartFrame()
1526 {
1527     // clients getting the naked VCL Window from UNO should always have the
1528     // solar mutex (and keep it over the lifetime of this ptr), as VCL might
1529     // might deinit otherwise
1530     DBG_TESTSOLARMUTEX();
1531     return Application::GetFrameWeld(m_xViewWindow);
1532 }
1533 
isAdditionalShapeSelected() const1534 bool ChartController::isAdditionalShapeSelected() const
1535 {
1536     return m_aSelection.isAdditionalShapeSelected();
1537 }
1538 
SetAndApplySelection(const Reference<drawing::XShape> & rxShape)1539 void ChartController::SetAndApplySelection(const Reference<drawing::XShape>& rxShape)
1540 {
1541     if(rxShape.is())
1542     {
1543         m_aSelection.setSelection(rxShape);
1544         m_aSelection.applySelection(GetDrawViewWrapper());
1545     }
1546 }
1547 
1548 
1549 
CreateAccessible()1550 uno::Reference< XAccessible > ChartController::CreateAccessible()
1551 {
1552     uno::Reference< XAccessible > xResult = new AccessibleChartView( GetDrawViewWrapper() );
1553     impl_initializeAccessible( uno::Reference< lang::XInitialization >( xResult, uno::UNO_QUERY ) );
1554     return xResult;
1555 }
1556 
impl_invalidateAccessible()1557 void ChartController::impl_invalidateAccessible()
1558 {
1559     SolarMutexGuard aGuard;
1560     auto pChartWindow(GetChartWindow());
1561     if( pChartWindow )
1562     {
1563         Reference< lang::XInitialization > xInit( pChartWindow->GetAccessible(false), uno::UNO_QUERY );
1564         if(xInit.is())
1565         {
1566             uno::Sequence< uno::Any > aArguments(3);//empty arguments -> invalid accessible
1567             xInit->initialize(aArguments);
1568         }
1569     }
1570 }
impl_initializeAccessible()1571 void ChartController::impl_initializeAccessible()
1572 {
1573     SolarMutexGuard aGuard;
1574     auto pChartWindow(GetChartWindow());
1575     if( pChartWindow )
1576         this->impl_initializeAccessible( Reference< lang::XInitialization >( pChartWindow->GetAccessible(false), uno::UNO_QUERY ) );
1577 }
impl_initializeAccessible(const uno::Reference<lang::XInitialization> & xInit)1578 void ChartController::impl_initializeAccessible( const uno::Reference< lang::XInitialization >& xInit )
1579 {
1580     if(!xInit.is())
1581         return;
1582 
1583     uno::Sequence< uno::Any > aArguments(5);
1584     aArguments[0] <<= uno::Reference<view::XSelectionSupplier>(this);
1585     aArguments[1] <<= getModel();
1586     aArguments[2] <<= m_xChartView;
1587     uno::Reference< XAccessible > xParent;
1588     {
1589         SolarMutexGuard aGuard;
1590         auto pChartWindow(GetChartWindow());
1591         if( pChartWindow )
1592         {
1593             vcl::Window* pParentWin( pChartWindow->GetAccessibleParentWindow());
1594             if( pParentWin )
1595                 xParent.set( pParentWin->GetAccessible());
1596         }
1597     }
1598     aArguments[3] <<= xParent;
1599     aArguments[4] <<= m_xViewWindow;
1600 
1601     xInit->initialize(aArguments);
1602 }
1603 
impl_getAvailableCommands()1604 const o3tl::sorted_vector< OUString >& ChartController::impl_getAvailableCommands()
1605 {
1606     static const o3tl::sorted_vector< OUString > s_AvailableCommands {
1607         // commands for container forward
1608         "AddDirect",           "NewDoc",                "Open",
1609         "Save",                "SaveAs",                "SendMail",
1610         "EditDoc",             "ExportDirectToPDF",     "PrintDefault",
1611 
1612         // own commands
1613         "Cut",                "Copy",                 "Paste",
1614         "DataRanges",         "DiagramData",
1615         // insert objects
1616         "InsertMenuTitles",   "InsertTitles",
1617         "InsertMenuLegend",   "InsertLegend",         "DeleteLegend",
1618         "InsertMenuDataLabels",
1619         "InsertMenuAxes",     "InsertRemoveAxes",         "InsertMenuGrids",
1620         "InsertSymbol",
1621         "InsertTrendlineEquation",  "InsertTrendlineEquationAndR2",
1622         "InsertR2Value",      "DeleteR2Value",
1623         "InsertMenuTrendlines",  "InsertTrendline",
1624         "InsertMenuMeanValues", "InsertMeanValue",
1625         "InsertMenuXErrorBars",  "InsertXErrorBars",
1626         "InsertMenuYErrorBars",   "InsertYErrorBars",
1627         "InsertDataLabels",   "InsertDataLabel",
1628         "DeleteTrendline",    "DeleteMeanValue",      "DeleteTrendlineEquation",
1629         "DeleteXErrorBars",   "DeleteYErrorBars",
1630         "DeleteDataLabels",   "DeleteDataLabel",
1631         //format objects
1632         "FormatSelection",     "TransformDialog",
1633         "DiagramType",        "View3D",
1634         "Forward",            "Backward",
1635         "MainTitle",          "SubTitle",
1636         "XTitle",             "YTitle",               "ZTitle",
1637         "SecondaryXTitle",    "SecondaryYTitle",
1638         "AllTitles",          "Legend",
1639         "DiagramAxisX",       "DiagramAxisY",         "DiagramAxisZ",
1640         "DiagramAxisA",       "DiagramAxisB",         "DiagramAxisAll",
1641         "DiagramGridXMain",   "DiagramGridYMain",     "DiagramGridZMain",
1642         "DiagramGridXHelp",   "DiagramGridYHelp",     "DiagramGridZHelp",
1643         "DiagramGridAll",
1644         "DiagramWall",        "DiagramFloor",         "DiagramArea",
1645 
1646         //context menu - format objects entries
1647         "FormatWall",        "FormatFloor",         "FormatChartArea",
1648         "FormatLegend",
1649 
1650         "FormatAxis",           "FormatTitle",
1651         "FormatDataSeries",     "FormatDataPoint",
1652         "ResetAllDataPoints",   "ResetDataPoint",
1653         "FormatDataLabels",     "FormatDataLabel",
1654         "FormatMeanValue",      "FormatTrendline",      "FormatTrendlineEquation",
1655         "FormatXErrorBars",     "FormatYErrorBars",
1656         "FormatStockLoss",      "FormatStockGain",
1657 
1658         "FormatMajorGrid",      "InsertMajorGrid",      "DeleteMajorGrid",
1659         "FormatMinorGrid",      "InsertMinorGrid",      "DeleteMinorGrid",
1660         "InsertAxis",           "DeleteAxis",           "InsertAxisTitle",
1661 
1662         // toolbar commands
1663         "ToggleGridHorizontal", "ToggleGridVertical", "ToggleLegend",         "ScaleText",
1664         "NewArrangement",     "Update",
1665         "DefaultColors",      "BarWidth",             "NumberOfLines",
1666         "ArrangeRow",
1667         "StatusBarVisible",
1668         "ChartElementSelector"};
1669     return s_AvailableCommands;
1670 }
1671 
getViewElementListProvider()1672 ViewElementListProvider ChartController::getViewElementListProvider()
1673 {
1674     return ViewElementListProvider(m_pDrawModelWrapper.get());
1675 }
1676 
1677 } //namespace chart
1678 
1679 extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
com_sun_star_comp_chart2_ChartController_get_implementation(css::uno::XComponentContext * context,css::uno::Sequence<css::uno::Any> const &)1680 com_sun_star_comp_chart2_ChartController_get_implementation(css::uno::XComponentContext *context,
1681                                                             css::uno::Sequence<css::uno::Any> const &)
1682 {
1683     return cppu::acquire(new chart::ChartController(context));
1684 }
1685 
1686 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
1687