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