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