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