Coverage Report

Created: 2026-10-14 09:33

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