Coverage Report

Created: 2026-09-28 10:59

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/chart2/source/controller/dialogs/DataBrowser.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 <svl/zforlist.hxx>
21
22
#include "DataBrowser.hxx"
23
#include <DataBrowserModel.hxx>
24
#include <strings.hrc>
25
#include <DataSeries.hxx>
26
#include <DataSeriesHelper.hxx>
27
#include <DiagramHelper.hxx>
28
#include <CommonConverters.hxx>
29
#include <NumberFormatterWrapper.hxx>
30
#include <servicenames_charttypes.hxx>
31
#include <ResId.hxx>
32
#include <bitmaps.hlst>
33
#include <helpids.h>
34
#include <ChartModel.hxx>
35
#include <ChartType.hxx>
36
37
#include <tools/mapunit.hxx>
38
#include <vcl/weld/Builder.hxx>
39
#include <vcl/weld/Image.hxx>
40
#include <vcl/weld/MessageDialog.hxx>
41
#include <vcl/settings.hxx>
42
#include <vcl/svapp.hxx>
43
#include <vcl/virdev.hxx>
44
#include <o3tl/safeint.hxx>
45
#include <osl/diagnose.h>
46
#include <svl/numformat.hxx>
47
#include <toolkit/helper/vclunohelper.hxx>
48
49
#include <com/sun/star/container/XIndexReplace.hpp>
50
51
#include <algorithm>
52
#include <limits>
53
54
55
using namespace ::com::sun::star;
56
using ::com::sun::star::uno::Reference;
57
58
using namespace ::svt;
59
60
namespace
61
{
62
/*  BrowserMode::COLUMNSELECTION : single cells may be selected rather than only
63
                                   entire rows
64
    BrowserMode::(H|V)LINES : show horizontal or vertical grid-lines
65
    BrowserMode::AUTO_(H|V)SCROLL : scroll automated horizontally or vertically when
66
                                    cursor is moved beyond the edge of the dialog
67
    BrowserMode::HIDESELECT : Do not mark the current row with selection color
68
                              (usually blue)
69
  ! BrowserMode::HIDECURSOR would prevent flickering in edit fields, but navigating
70
        with shift up/down, and entering non-editable cells would be problematic,
71
        e.g.  the first cell, or when being in read-only mode
72
*/
73
const BrowserMode BrowserStdFlags = BrowserMode::COLUMNSELECTION |
74
                                    BrowserMode::HLINES | BrowserMode::VLINES |
75
                                    BrowserMode::AUTO_HSCROLL | BrowserMode::AUTO_VSCROLL |
76
                                    BrowserMode::HIDESELECT;
77
78
sal_Int32 lcl_getColumnInData( sal_uInt16 nCol )
79
0
{
80
0
    return static_cast< sal_Int32 >( nCol ) - 1;
81
0
}
82
83
} // anonymous namespace
84
85
namespace chart
86
{
87
88
namespace impl
89
{
90
91
class SeriesHeaderEdit
92
{
93
public:
94
    explicit SeriesHeaderEdit(std::unique_ptr<weld::Entry> xControl, sal_Int32 nHeader);
95
96
    void setStartColumn( sal_Int32 nStartColumn );
97
0
    sal_Int32 getStartColumn() const { return m_nStartColumn;}
98
    void SetShowWarningBox( bool bShowWarning );
99
100
0
    OUString GetText() const { return m_xControl->get_text(); }
101
0
    void SetText(const OUString& rText) { m_xControl->set_text(rText); }
102
103
0
    bool HasFocus() const { return m_xControl->has_focus(); }
104
105
0
    void set_size_request(int nWidth, int nHeight) { m_xControl->set_size_request(nWidth, nHeight); }
106
0
    void set_margin_start(int nLeft) { m_xControl->set_margin_start(nLeft); }
107
108
0
    void SetModifyHdl(const Link<SeriesHeaderEdit&,void>& rLink) { m_aModifyHdl = rLink; }
109
0
    void SetGetFocusHdl(const Link<SeriesHeaderEdit&,void>& rLink) { m_aFocusInHdl = rLink; }
110
111
private:
112
    DECL_LINK(NameEdited, weld::TextWidget&, void);
113
    DECL_LINK(NameFocusIn, weld::Widget&, void);
114
    DECL_LINK(MousePressHdl, const MouseEvent&, bool);
115
116
    std::unique_ptr<weld::Entry> m_xControl;
117
    Link<SeriesHeaderEdit&,void> m_aModifyHdl;
118
    Link<SeriesHeaderEdit&,void> m_aFocusInHdl;
119
    sal_Int32 m_nStartColumn;
120
    bool m_bShowWarningBox;
121
};
122
123
SeriesHeaderEdit::SeriesHeaderEdit(std::unique_ptr<weld::Entry> xControl, sal_Int32 nHeader)
124
0
    : m_xControl(std::move(xControl))
125
0
    , m_nStartColumn(0)
126
0
    , m_bShowWarningBox(false)
127
0
{
128
0
    m_xControl->set_help_id(HID_SCH_DATA_SERIES_LABEL);
129
0
    m_xControl->connect_changed(LINK(this, SeriesHeaderEdit, NameEdited));
130
0
    m_xControl->connect_focus_in(LINK(this, SeriesHeaderEdit, NameFocusIn));
131
0
    m_xControl->connect_mouse_press(LINK(this, SeriesHeaderEdit, MousePressHdl));
132
133
0
    m_xControl->set_buildable_name(m_xControl->get_buildable_name() + OUString::number(nHeader));
134
0
}
135
136
IMPL_LINK_NOARG(SeriesHeaderEdit, NameEdited, weld::TextWidget&, void)
137
0
{
138
0
    m_aModifyHdl.Call(*this);
139
0
}
140
141
IMPL_LINK_NOARG(SeriesHeaderEdit, NameFocusIn, weld::Widget&, void)
142
0
{
143
0
    m_aFocusInHdl.Call(*this);
144
0
}
145
146
void SeriesHeaderEdit::setStartColumn( sal_Int32 nStartColumn )
147
0
{
148
0
    m_nStartColumn = nStartColumn;
149
0
}
150
151
void SeriesHeaderEdit::SetShowWarningBox( bool bShowWarning )
152
0
{
153
0
    m_bShowWarningBox = bShowWarning;
154
0
}
155
156
IMPL_LINK_NOARG(SeriesHeaderEdit, MousePressHdl, const MouseEvent&, bool)
157
0
{
158
0
    if (m_bShowWarningBox)
159
0
    {
160
0
        std::unique_ptr<weld::MessageDialog> xWarn(Application::CreateMessageDialog(m_xControl.get(),
161
0
                                                   VclMessageType::Warning, VclButtonsType::Ok,
162
0
                                                   SchResId(STR_INVALID_NUMBER)));
163
0
        xWarn->run();
164
0
    }
165
166
0
    return false;
167
0
}
168
169
class SeriesHeader
170
{
171
public:
172
    explicit SeriesHeader(weld::Box* pParent, weld::Box* pColorParent, sal_Int32 nHeader);
173
            ~SeriesHeader();
174
175
    void SetColor( const Color & rCol );
176
    void SetPos();
177
    void SetWidth( sal_Int32 nWidth );
178
    void SetChartType( const rtl::Reference< ::chart::ChartType > & xChartType,
179
                       bool bSwapXAndYAxis );
180
    void SetSeriesName( const OUString & rName );
181
    void SetRange( sal_Int32 nStartCol, sal_Int32 nEndCol );
182
183
    void SetPixelWidth( sal_Int32 nWidth );
184
185
0
    sal_Int32 GetStartColumn() const { return m_nStartCol;}
186
0
    sal_Int32 GetEndColumn() const { return m_nEndCol;}
187
188
    static const sal_Int32 nSymbolHeight = 10;
189
    static const sal_Int32 nSymbolDistance = 2;
190
191
0
    static sal_Int32 GetRelativeAppFontXPosForNameField() { return nSymbolHeight + nSymbolDistance; }
192
193
    void Show();
194
    void Hide();
195
196
    /** call this before destroying the class.  This notifies the listeners to
197
        changes of the edit field for the series name.
198
     */
199
    void applyChanges();
200
201
    void SetGetFocusHdl(const Link<SeriesHeaderEdit&,void>& rLink);
202
203
    void SetEditChangedHdl( const Link<SeriesHeaderEdit&,void> & rLink );
204
205
    bool HasFocus() const;
206
207
private:
208
    Timer m_aUpdateDataTimer;
209
210
    std::unique_ptr<weld::Builder> m_xBuilder1;
211
    std::unique_ptr<weld::Builder> m_xBuilder2;
212
213
    weld::Box* m_pParent;
214
    weld::Box* m_pColorParent;
215
216
    std::unique_ptr<weld::Container> m_xContainer1;
217
    std::unique_ptr<weld::Container> m_xContainer2;
218
    std::unique_ptr<weld::Image> m_spSymbol;
219
    std::unique_ptr<SeriesHeaderEdit> m_spSeriesName;
220
    std::unique_ptr<weld::Image> m_spColorBar;
221
    VclPtr< OutputDevice> m_xDevice;
222
    Link<SeriesHeaderEdit&,void> m_aChangeLink;
223
    Color m_aColor;
224
225
    void notifyChanges();
226
    DECL_LINK( ImplUpdateDataHdl, Timer*, void );
227
    DECL_LINK( SeriesNameEdited, SeriesHeaderEdit&, void );
228
229
    static OUString GetChartTypeImage(
230
        const rtl::Reference< ::chart::ChartType > & xChartType,
231
        bool bSwapXAndYAxis
232
        );
233
234
    sal_Int32 m_nStartCol, m_nEndCol;
235
    sal_Int32 m_nWidth;
236
    bool      m_bSeriesNameChangePending;
237
};
238
239
SeriesHeader::SeriesHeader(weld::Box* pParent, weld::Box* pColorParent, sal_Int32 nHeader)
240
0
    : m_aUpdateDataTimer( "SeriesHeader UpdateDataTimer" )
241
0
    , m_xBuilder1(Application::CreateBuilder(pParent, u"modules/schart/ui/columnfragment.ui"_ustr))
242
0
    , m_xBuilder2(Application::CreateBuilder(pColorParent, u"modules/schart/ui/imagefragment.ui"_ustr))
243
0
    , m_pParent(pParent)
244
0
    , m_pColorParent(pColorParent)
245
0
    , m_xContainer1(m_xBuilder1->weld_container(u"container"_ustr))
246
0
    , m_xContainer2(m_xBuilder2->weld_container(u"container"_ustr))
247
0
    , m_spSymbol(m_xBuilder1->weld_image(u"image"_ustr))
248
0
    , m_spSeriesName(new SeriesHeaderEdit(m_xBuilder1->weld_entry(u"entry"_ustr), nHeader))
249
0
    , m_spColorBar(m_xBuilder2->weld_image(u"image"_ustr))
250
0
    , m_xDevice(Application::GetDefaultDevice())
251
0
    , m_nStartCol( 0 )
252
0
    , m_nEndCol( 0 )
253
0
    , m_nWidth( 42 )
254
0
    , m_bSeriesNameChangePending( false )
255
0
{
256
0
    m_aUpdateDataTimer.SetInvokeHandler(LINK(this, SeriesHeader, ImplUpdateDataHdl));
257
0
    m_aUpdateDataTimer.SetTimeout(4 * EDIT_UPDATEDATA_TIMEOUT);
258
259
0
    m_spSeriesName->SetModifyHdl(LINK(this, SeriesHeader, SeriesNameEdited));
260
0
    Show();
261
0
}
262
263
SeriesHeader::~SeriesHeader()
264
0
{
265
0
    m_aUpdateDataTimer.Stop();
266
0
    m_pParent->move(m_xContainer1.get(), nullptr);
267
0
    m_pColorParent->move(m_xContainer2.get(), nullptr);
268
0
}
269
270
void SeriesHeader::notifyChanges()
271
0
{
272
0
    m_aChangeLink.Call(*m_spSeriesName);
273
0
    m_bSeriesNameChangePending = false;
274
0
}
275
276
void SeriesHeader::applyChanges()
277
0
{
278
0
    if( m_bSeriesNameChangePending )
279
0
    {
280
0
        notifyChanges();
281
0
    }
282
0
}
283
284
void SeriesHeader::SetColor( const Color & rCol )
285
0
{
286
0
    m_aColor = rCol;
287
0
}
288
289
void SeriesHeader::SetPos()
290
0
{
291
    // chart type symbol
292
0
    Size aSize( nSymbolHeight, nSymbolHeight );
293
0
    aSize = m_xDevice->LogicToPixel(aSize, MapMode(MapUnit::MapAppFont));
294
0
    m_spSymbol->set_size_request(aSize.Width(), aSize.Height());
295
296
    // series name edit field
297
0
    m_spSeriesName->set_margin_start(2);
298
299
0
    sal_Int32 nHeightPx = m_xDevice->LogicToPixel(Size(0, 12), MapMode(MapUnit::MapAppFont)).Height();
300
0
    m_spSeriesName->set_size_request(m_nWidth - aSize.Width() - 2, nHeightPx);
301
302
    // color bar
303
0
    nHeightPx = m_xDevice->LogicToPixel(Size(0, 3), MapMode(MapUnit::MapAppFont)).Height();
304
0
    m_spColorBar->set_size_request(m_nWidth, nHeightPx);
305
306
0
    ScopedVclPtr<VirtualDevice> xVirDev(m_spColorBar->create_virtual_device());
307
0
    xVirDev->SetOutputSizePixel(Size(m_nWidth, nHeightPx));
308
0
    xVirDev->SetFillColor(m_aColor);
309
0
    xVirDev->SetLineColor(m_aColor);
310
0
    xVirDev->DrawRect(tools::Rectangle(Point(0, 0), aSize));
311
0
    m_spColorBar->set_image(xVirDev.get());
312
0
}
313
314
void SeriesHeader::SetWidth( sal_Int32 nWidth )
315
0
{
316
0
    m_nWidth = nWidth;
317
0
    SetPos();
318
0
}
319
320
void SeriesHeader::SetPixelWidth( sal_Int32 nWidth )
321
0
{
322
0
    SetWidth(nWidth);
323
0
}
324
325
void SeriesHeader::SetChartType(
326
    const rtl::Reference< ChartType > & xChartType,
327
    bool bSwapXAndYAxis
328
)
329
0
{
330
0
    m_spSymbol->set_from_icon_name( GetChartTypeImage( xChartType, bSwapXAndYAxis ) );
331
0
}
332
333
void SeriesHeader::SetSeriesName( const OUString & rName )
334
0
{
335
0
    m_spSeriesName->SetText(rName);
336
0
}
337
338
void SeriesHeader::SetRange( sal_Int32 nStartCol, sal_Int32 nEndCol )
339
0
{
340
0
    m_nStartCol = nStartCol;
341
0
    m_nEndCol = std::max(nEndCol, nStartCol);
342
0
    m_spSeriesName->setStartColumn( nStartCol );
343
0
}
344
345
void SeriesHeader::Show()
346
0
{
347
0
    m_xContainer1->show();
348
0
    m_xContainer2->show();
349
0
}
350
351
void SeriesHeader::Hide()
352
0
{
353
0
    m_xContainer1->hide();
354
0
    m_xContainer2->hide();
355
0
}
356
357
void SeriesHeader::SetEditChangedHdl( const Link<SeriesHeaderEdit&,void> & rLink )
358
0
{
359
0
    m_aChangeLink = rLink;
360
0
}
361
362
IMPL_LINK_NOARG(SeriesHeader, ImplUpdateDataHdl, Timer*, void)
363
0
{
364
0
    notifyChanges();
365
0
}
366
367
IMPL_LINK_NOARG(SeriesHeader, SeriesNameEdited, SeriesHeaderEdit&, void)
368
0
{
369
0
    m_bSeriesNameChangePending = true;
370
0
    m_aUpdateDataTimer.Start();
371
0
}
372
373
void SeriesHeader::SetGetFocusHdl( const Link<SeriesHeaderEdit&,void>& rLink )
374
0
{
375
0
    m_spSeriesName->SetGetFocusHdl( rLink );
376
0
}
377
378
bool SeriesHeader::HasFocus() const
379
0
{
380
0
    return m_spSeriesName->HasFocus();
381
0
}
382
383
OUString SeriesHeader::GetChartTypeImage(
384
    const rtl::Reference< ChartType > & xChartType,
385
    bool bSwapXAndYAxis
386
)
387
0
{
388
0
    OUString aResult;
389
0
    if( !xChartType.is())
390
0
        return aResult;
391
0
    OUString aChartTypeName( xChartType->getChartType());
392
393
0
    if( aChartTypeName == CHART2_SERVICE_NAME_CHARTTYPE_AREA )
394
0
    {
395
0
        aResult = BMP_TYPE_AREA;
396
0
    }
397
0
    else if( aChartTypeName == CHART2_SERVICE_NAME_CHARTTYPE_COLUMN )
398
0
    {
399
0
        if( bSwapXAndYAxis )
400
0
            aResult = BMP_TYPE_BAR;
401
0
        else
402
0
            aResult = BMP_TYPE_COLUMN;
403
0
    }
404
0
    else if( aChartTypeName == CHART2_SERVICE_NAME_CHARTTYPE_LINE )
405
0
    {
406
0
        aResult = BMP_TYPE_LINE;
407
0
    }
408
0
    else if( aChartTypeName == CHART2_SERVICE_NAME_CHARTTYPE_SCATTER )
409
0
    {
410
0
        aResult = BMP_TYPE_XY;
411
0
    }
412
0
    else if( aChartTypeName == CHART2_SERVICE_NAME_CHARTTYPE_PIE )
413
0
    {
414
0
        aResult = BMP_TYPE_PIE;
415
0
    }
416
0
    else if( aChartTypeName == CHART2_SERVICE_NAME_CHARTTYPE_NET
417
0
          || aChartTypeName == CHART2_SERVICE_NAME_CHARTTYPE_FILLED_NET )
418
0
    {
419
0
        aResult = BMP_TYPE_NET;
420
0
    }
421
0
    else if( aChartTypeName == CHART2_SERVICE_NAME_CHARTTYPE_CANDLESTICK )
422
0
    {
423
        // @todo: correct image for candle-stick type
424
0
        aResult = BMP_TYPE_STOCK;
425
0
    }
426
0
    else if( aChartTypeName == CHART2_SERVICE_NAME_CHARTTYPE_BUBBLE )
427
0
    {
428
0
        aResult = BMP_TYPE_BUBBLE;
429
0
    }
430
0
    else if( aChartTypeName == CHART2_SERVICE_NAME_CHARTTYPE_CORRELATIONCIRCLE )
431
0
    {
432
0
        aResult = BMP_TYPE_CORRELATION_CIRCLE;
433
0
    }
434
435
0
    return aResult;
436
0
}
437
438
} // namespace impl
439
440
namespace
441
{
442
443
/** returns false, if no header as the focus.
444
445
    If a header has the focus, true is returned and the index of the header
446
    with focus is set at pIndex if pOutIndex is not 0.
447
*/
448
bool lcl_SeriesHeaderHasFocus(
449
    const std::vector< std::shared_ptr< ::chart::impl::SeriesHeader > > & rSeriesHeader,
450
    sal_Int32 * pOutIndex = nullptr )
451
0
{
452
0
    sal_Int32 nIndex = 0;
453
0
    for (auto const& elem : rSeriesHeader)
454
0
    {
455
0
        if(elem->HasFocus())
456
0
        {
457
0
            if( pOutIndex )
458
0
                *pOutIndex = nIndex;
459
0
            return true;
460
0
        }
461
0
        ++nIndex;
462
0
    }
463
0
    return false;
464
0
}
465
466
sal_Int32 lcl_getColumnInDataOrHeader(
467
    sal_uInt16 nCol, const std::vector< std::shared_ptr< ::chart::impl::SeriesHeader > > & rSeriesHeader )
468
0
{
469
0
    sal_Int32 nColIdx = 0;
470
0
    bool bHeaderHasFocus( lcl_SeriesHeaderHasFocus( rSeriesHeader, &nColIdx ));
471
472
0
    if( bHeaderHasFocus )
473
0
        nColIdx = lcl_getColumnInData( static_cast< sal_uInt16 >( rSeriesHeader[nColIdx]->GetStartColumn()));
474
0
    else
475
0
        nColIdx = lcl_getColumnInData( nCol );
476
477
0
    return nColIdx;
478
0
}
479
480
} // anonymous namespace
481
482
DataBrowser::DataBrowser(const css::uno::Reference<css::awt::XWindow> &rParent,
483
                         weld::Box* pColumns, weld::Box* pColors) :
484
0
    ::svt::EditBrowseBox(VCLUnoHelper::GetWindow(rParent),
485
0
            EditBrowseBoxFlags::SMART_TAB_TRAVEL | EditBrowseBoxFlags::HANDLE_COLUMN_TEXT,
486
0
            WB_BORDER | WB_TABSTOP, BrowserStdFlags ),
487
0
    m_nSeekRow( 0 ),
488
0
    m_bIsReadOnly( false ),
489
0
    m_bDataValid( true ),
490
0
    m_aNumberEditField(VclPtr<FormattedControl>::Create(&EditBrowseBox::GetDataWindow(), false)),
491
0
    m_aTextEditField(VclPtr<EditControl>::Create(&EditBrowseBox::GetDataWindow())),
492
0
    m_pColumnsWin(pColumns),
493
0
    m_pColorsWin(pColors),
494
0
    m_rNumberEditController( new ::svt::FormattedFieldCellController( m_aNumberEditField.get() )),
495
0
    m_rTextEditController( new ::svt::EditCellController( m_aTextEditField.get() ))
496
0
{
497
0
    Formatter& rFormatter = m_aNumberEditField->get_formatter();
498
0
    rFormatter.SetDefaultValue( std::numeric_limits<double>::quiet_NaN() );
499
0
    rFormatter.TreatAsNumber( true );
500
0
    RenewTable();
501
0
}
Unexecuted instantiation: chart::DataBrowser::DataBrowser(com::sun::star::uno::Reference<com::sun::star::awt::XWindow> const&, weld::Box*, weld::Box*)
Unexecuted instantiation: chart::DataBrowser::DataBrowser(com::sun::star::uno::Reference<com::sun::star::awt::XWindow> const&, weld::Box*, weld::Box*)
502
503
DataBrowser::~DataBrowser()
504
0
{
505
0
    disposeOnce();
506
0
}
507
508
void DataBrowser::dispose()
509
0
{
510
0
    m_aSeriesHeaders.clear();
511
0
    m_aNumberEditField.disposeAndClear();
512
0
    m_aTextEditField.disposeAndClear();
513
0
    ::svt::EditBrowseBox::dispose();
514
0
}
515
516
bool DataBrowser::MayInsertRow() const
517
0
{
518
0
    return ! IsReadOnly()
519
0
        && ( !lcl_SeriesHeaderHasFocus( m_aSeriesHeaders ));
520
0
}
521
522
bool DataBrowser::MayInsertColumn() const
523
0
{
524
0
    return ! IsReadOnly();
525
0
}
526
527
bool DataBrowser::MayDeleteRow() const
528
0
{
529
0
    return ! IsReadOnly()
530
0
        && ( !lcl_SeriesHeaderHasFocus( m_aSeriesHeaders ))
531
0
        && ( GetCurRow() >= 0 )
532
0
        && ( GetRowCount() > 1 );
533
0
}
534
535
bool DataBrowser::MayDeleteColumn() const
536
0
{
537
    // if a series header has the focus
538
0
    if( lcl_SeriesHeaderHasFocus( m_aSeriesHeaders ))
539
0
        return true;
540
541
0
    return ! IsReadOnly()
542
0
        && ( GetCurColumnId() > 1 )
543
0
        && ( ColCount() > 2 );
544
0
}
545
546
bool DataBrowser::MayMoveUpRows() const
547
0
{
548
0
    return ! IsReadOnly()
549
0
        && ( !lcl_SeriesHeaderHasFocus( m_aSeriesHeaders ))
550
0
        && ( GetCurRow() > 0 )
551
0
        && ( GetCurRow() <= GetRowCount() - 1 );
552
0
}
553
554
bool DataBrowser::MayMoveDownRows() const
555
0
{
556
0
    return ! IsReadOnly()
557
0
        && ( !lcl_SeriesHeaderHasFocus( m_aSeriesHeaders ))
558
0
        && ( GetCurRow() >= 0 )
559
0
        && ( GetCurRow() < GetRowCount() - 1 );
560
0
}
561
562
bool DataBrowser::MayMoveLeftColumns() const
563
0
{
564
    // if a series header (except the last one) has the focus
565
0
    {
566
0
        sal_Int32 nColIndex(0);
567
0
        if( lcl_SeriesHeaderHasFocus( m_aSeriesHeaders, &nColIndex ))
568
0
            return (o3tl::make_unsigned( nColIndex ) <= (m_aSeriesHeaders.size() - 1)) && (static_cast< sal_uInt32 >( nColIndex ) != 0);
569
0
    }
570
571
0
    sal_Int32 nColIdx = lcl_getColumnInDataOrHeader( GetCurColumnId(), m_aSeriesHeaders );
572
0
    return ! IsReadOnly()
573
0
        && ( nColIdx > 1 )
574
0
        && ( nColIdx <= ColCount() - 2 )
575
0
        && m_apDataBrowserModel
576
0
        && !m_apDataBrowserModel->isCategoriesColumn( nColIdx );
577
0
}
578
579
bool DataBrowser::MayMoveRightColumns() const
580
0
{
581
    // if a series header (except the last one) has the focus
582
0
    {
583
0
        sal_Int32 nColIndex(0);
584
0
        if( lcl_SeriesHeaderHasFocus( m_aSeriesHeaders, &nColIndex ))
585
0
            return (o3tl::make_unsigned( nColIndex ) < (m_aSeriesHeaders.size() - 1));
586
0
    }
587
588
0
    sal_Int32 nColIdx = lcl_getColumnInDataOrHeader( GetCurColumnId(), m_aSeriesHeaders );
589
0
    return ! IsReadOnly()
590
0
        && ( nColIdx > 0 )
591
0
        && ( nColIdx < ColCount()-2 )
592
0
        && m_apDataBrowserModel
593
0
        && !m_apDataBrowserModel->isCategoriesColumn( nColIdx );
594
0
}
595
596
void DataBrowser::clearHeaders()
597
0
{
598
0
    for( const auto& spHeader : m_aSeriesHeaders )
599
0
        spHeader->applyChanges();
600
0
    m_aSeriesHeaders.clear();
601
0
}
602
603
void DataBrowser::RenewTable()
604
0
{
605
0
    if (!m_apDataBrowserModel)
606
0
        return;
607
608
0
    sal_Int32  nOldRow     = GetCurRow();
609
0
    sal_uInt16 nOldColId   = GetCurColumnId();
610
611
0
    bool bLastUpdateMode = GetUpdateMode();
612
0
    SetUpdateMode( false );
613
614
0
    if( IsModified() )
615
0
        SaveModified();
616
617
0
    DeactivateCell();
618
619
0
    RemoveColumns();
620
0
    RowRemoved( 1, GetRowCount() );
621
622
    // for row numbers
623
0
    InsertHandleColumn( static_cast< sal_uInt16 >(
624
0
                            GetDataWindow().LogicToPixel( Size( 42, 0 )).getWidth() ));
625
626
0
    OUString aDefaultSeriesName(SchResId(STR_COLUMN_LABEL));
627
0
    replaceParamterInString( aDefaultSeriesName, u"%COLUMNNUMBER", OUString::number( 24 ) );
628
0
    sal_Int32 nColumnWidth = GetDataWindow().GetTextWidth( aDefaultSeriesName )
629
0
        + GetDataWindow().LogicToPixel(Point(8 + impl::SeriesHeader::GetRelativeAppFontXPosForNameField(), 0), MapMode(MapUnit::MapAppFont)).X();
630
0
    sal_Int32 nColumnCount = m_apDataBrowserModel->getColumnCount();
631
    // nRowCount is a member of a base class
632
0
    sal_Int32 nRowCountLocal = m_apDataBrowserModel->getMaxRowCount();
633
0
    for( sal_Int32 nColIdx=1; nColIdx<=nColumnCount; ++nColIdx )
634
0
    {
635
0
        InsertDataColumn( static_cast< sal_uInt16 >( nColIdx ), GetColString( nColIdx ), nColumnWidth );
636
0
    }
637
638
0
    RowInserted( 1, nRowCountLocal );
639
0
    GoToRow( std::min( nOldRow, GetRowCount() - 1 ));
640
0
    GoToColumnId( std::min( nOldColId, static_cast< sal_uInt16 >( ColCount() - 1 )));
641
642
    // fill series headers
643
0
    clearHeaders();
644
0
    const DataBrowserModel::tDataHeaderVector& aHeaders( m_apDataBrowserModel->getDataHeaders());
645
0
    Link<impl::SeriesHeaderEdit&,void> aFocusLink( LINK( this, DataBrowser, SeriesHeaderGotFocus ));
646
0
    Link<impl::SeriesHeaderEdit&,void> aSeriesHeaderChangedLink( LINK( this, DataBrowser, SeriesHeaderChanged ));
647
648
0
    for (size_t i = 0; i < aHeaders.size(); ++i)
649
0
    {
650
0
        auto const& elemHeader = aHeaders[i];
651
0
        auto spHeader = std::make_shared<impl::SeriesHeader>( m_pColumnsWin, m_pColorsWin, i);
652
0
        Color nColor;
653
        // @todo: Set "DraftColor", i.e. interpolated colors for gradients, bitmaps, etc.
654
0
        if( elemHeader.m_xDataSeries.is() &&
655
0
            ( elemHeader.m_xDataSeries->getPropertyValue( u"Color"_ustr ) >>= nColor ))
656
0
            spHeader->SetColor( nColor );
657
0
        spHeader->SetChartType( elemHeader.m_xChartType, elemHeader.m_bSwapXAndYAxis );
658
0
        spHeader->SetSeriesName(
659
0
            elemHeader.m_xDataSeries->getLabelForRole(
660
0
                        elemHeader.m_xChartType.is() ?
661
0
                         elemHeader.m_xChartType->getRoleOfSequenceForSeriesLabel() :
662
0
                         u"values-y"_ustr));
663
        // index is 1-based, as 0 is for the column that contains the row-numbers
664
0
        spHeader->SetRange( elemHeader.m_nStartColumn + 1, elemHeader.m_nEndColumn + 1 );
665
0
        spHeader->SetGetFocusHdl( aFocusLink );
666
0
        spHeader->SetEditChangedHdl( aSeriesHeaderChangedLink );
667
0
        m_aSeriesHeaders.push_back(std::move(spHeader));
668
0
    }
669
670
0
    ImplAdjustHeaderControls();
671
0
    SetUpdateMode( bLastUpdateMode );
672
0
    ActivateCell();
673
0
    Invalidate();
674
0
}
675
676
const OUString & DataBrowser::GetColString( sal_Int32 nColumnId ) const
677
0
{
678
0
    OSL_ASSERT(m_apDataBrowserModel);
679
0
    if( nColumnId > 0 )
680
0
        return m_apDataBrowserModel->getRoleOfColumn( nColumnId - 1 );
681
0
    return EMPTY_OUSTRING;
682
0
}
683
684
OUString DataBrowser::GetCellText( sal_Int32 nRow, sal_uInt16 nColumnId ) const
685
0
{
686
0
    OUString aResult;
687
688
0
    if( nColumnId == 0 )
689
0
    {
690
0
        aResult = OUString::number(nRow + 1);
691
0
    }
692
0
    else if( nRow >= 0 && m_apDataBrowserModel)
693
0
    {
694
0
        sal_Int32 nColIndex = static_cast< sal_Int32 >( nColumnId ) - 1;
695
696
0
        if( m_apDataBrowserModel->getCellType( nColIndex ) == DataBrowserModel::NUMBER )
697
0
        {
698
0
            double fData( m_apDataBrowserModel->getCellNumber( nColIndex, nRow ));
699
700
0
            if( ! std::isnan( fData ) &&
701
0
                m_spNumberFormatterWrapper )
702
0
            {
703
0
                bool bColorChanged = false;
704
0
                Color nLabelColor;
705
0
                aResult = m_spNumberFormatterWrapper->getFormattedString(
706
0
                                      GetNumberFormatKey( nColumnId ),
707
0
                                      fData, nLabelColor, bColorChanged );
708
0
            }
709
0
        }
710
0
        else if( m_apDataBrowserModel->getCellType( nColIndex ) == DataBrowserModel::TEXTORDATE )
711
0
        {
712
0
            uno::Any aAny = m_apDataBrowserModel->getCellAny( nColIndex, nRow );
713
0
            OUString aText;
714
0
            double fDouble=0.0;
715
0
            if( aAny>>=aText )
716
0
                aResult = aText;
717
0
            else if( aAny>>=fDouble )
718
0
            {
719
0
                if( ! std::isnan( fDouble ) && m_spNumberFormatterWrapper )
720
0
                {
721
                    // If a numberformat was available here we could directly
722
                    // obtain the corresponding edit format in
723
                    // getDateTimeInputNumberFormat() instead of doing the
724
                    // guess work.
725
0
                    sal_Int32 nNumberFormat = DiagramHelper::getDateTimeInputNumberFormat(
726
0
                            m_xChartDoc, fDouble );
727
0
                    Color nLabelColor;
728
0
                    bool bColorChanged = false;
729
0
                    aResult = m_spNumberFormatterWrapper->getFormattedString(
730
0
                        nNumberFormat, fDouble, nLabelColor, bColorChanged );
731
0
                }
732
0
            }
733
0
        }
734
0
        else
735
0
        {
736
0
            OSL_ASSERT( m_apDataBrowserModel->getCellType( nColIndex ) == DataBrowserModel::TEXT );
737
0
            aResult = m_apDataBrowserModel->getCellText( nColIndex, nRow );
738
0
        }
739
0
    }
740
741
0
    return aResult;
742
0
}
743
744
double DataBrowser::GetCellNumber( sal_Int32 nRow, sal_uInt16 nColumnId ) const
745
0
{
746
0
    if(( nColumnId >= 1 ) && ( nRow >= 0 ) && m_apDataBrowserModel)
747
0
    {
748
0
        return m_apDataBrowserModel->getCellNumber(
749
0
            static_cast< sal_Int32 >( nColumnId ) - 1, nRow );
750
0
    }
751
752
0
    return std::numeric_limits<double>::quiet_NaN();
753
0
}
754
755
void DataBrowser::Resize()
756
0
{
757
0
    bool bLastUpdateMode = GetUpdateMode();
758
0
    SetUpdateMode( false );
759
760
0
    ::svt::EditBrowseBox::Resize();
761
0
    ImplAdjustHeaderControls();
762
0
    SetUpdateMode( bLastUpdateMode );
763
0
}
764
765
void DataBrowser::SetReadOnly( bool bNewState )
766
0
{
767
0
    if( m_bIsReadOnly != bNewState )
768
0
    {
769
0
        m_bIsReadOnly = bNewState;
770
0
        Invalidate();
771
0
        DeactivateCell();
772
0
    }
773
0
}
774
775
void DataBrowser::CursorMoved()
776
0
{
777
0
    EditBrowseBox::CursorMoved();
778
779
0
    if( GetUpdateMode() )
780
0
        m_aCursorMovedHdlLink.Call( this );
781
0
}
782
783
void DataBrowser::MouseButtonDown( const BrowserMouseEvent& rEvt )
784
0
{
785
0
    if( !m_bDataValid )
786
0
        ShowWarningBox();
787
0
    else
788
0
        EditBrowseBox::MouseButtonDown( rEvt );
789
0
}
790
791
void DataBrowser::ShowWarningBox()
792
0
{
793
0
    std::unique_ptr<weld::MessageDialog> xWarn(Application::CreateMessageDialog(GetFrameWeld(),
794
0
                                               VclMessageType::Warning, VclButtonsType::Ok,
795
0
                                               SchResId(STR_INVALID_NUMBER)));
796
0
    xWarn->run();
797
0
}
798
799
bool DataBrowser::ShowQueryBox()
800
0
{
801
0
    std::unique_ptr<weld::MessageDialog> xQueryBox(Application::CreateMessageDialog(GetFrameWeld(),
802
0
                                                   VclMessageType::Question, VclButtonsType::YesNo,
803
0
                                                   SchResId(STR_DATA_EDITOR_INCORRECT_INPUT)));
804
0
    return xQueryBox->run() == RET_YES;
805
0
}
806
807
bool DataBrowser::IsDataValid() const
808
0
{
809
0
    bool bValid = true;
810
0
    const sal_Int32 nCol = lcl_getColumnInData( GetCurColumnId());
811
812
0
    if( m_apDataBrowserModel->getCellType( nCol ) == DataBrowserModel::NUMBER )
813
0
    {
814
0
        sal_uInt32 nDummy = 0;
815
0
        double fDummy = 0.0;
816
0
        OUString aText(m_aNumberEditField->get_widget().get_text());
817
818
0
        if( !aText.isEmpty() &&
819
0
            m_spNumberFormatterWrapper &&
820
0
            m_spNumberFormatterWrapper->getSvNumberFormatter() &&
821
0
            ! m_spNumberFormatterWrapper->getSvNumberFormatter()->IsNumberFormat(
822
0
              aText, nDummy, fDummy ))
823
0
        {
824
0
            bValid = false;
825
0
        }
826
0
    }
827
828
0
    return bValid;
829
0
}
830
831
void DataBrowser::CellModified()
832
0
{
833
0
    m_bDataValid = IsDataValid();
834
0
    m_aCursorMovedHdlLink.Call( this );
835
0
}
836
837
void DataBrowser::SetDataFromModel(
838
    const rtl::Reference<::chart::ChartModel> & xChartDoc )
839
0
{
840
0
    m_xChartDoc = xChartDoc;
841
842
0
    m_apDataBrowserModel.reset( new DataBrowserModel( m_xChartDoc ));
843
0
    m_spNumberFormatterWrapper =
844
0
        std::make_shared<NumberFormatterWrapper>(m_xChartDoc);
845
846
0
    Formatter& rFormatter = m_aNumberEditField->get_formatter();
847
0
    rFormatter.SetFormatter( m_spNumberFormatterWrapper->getSvNumberFormatter() );
848
849
0
    RenewTable();
850
851
0
    const sal_Int32 nColCnt  = m_apDataBrowserModel->getColumnCount();
852
0
    const sal_Int32 nRowCnt =  m_apDataBrowserModel->getMaxRowCount();
853
0
    if( nRowCnt && nColCnt )
854
0
    {
855
0
        GoToRow( 0 );
856
0
        GoToColumnId( 1 );
857
0
    }
858
0
}
859
860
void DataBrowser::InsertColumn()
861
0
{
862
0
    sal_Int32 nColIdx = lcl_getColumnInDataOrHeader( GetCurColumnId(), m_aSeriesHeaders );
863
864
0
    if( nColIdx >= 0 && m_apDataBrowserModel)
865
0
    {
866
        // save changes made to edit-field
867
0
        if( IsModified() )
868
0
            SaveModified();
869
870
0
        m_apDataBrowserModel->insertDataSeries( nColIdx );
871
0
        RenewTable();
872
0
    }
873
0
}
874
875
void DataBrowser::InsertTextColumn()
876
0
{
877
0
    sal_Int32 nColIdx = lcl_getColumnInDataOrHeader( GetCurColumnId(), m_aSeriesHeaders );
878
879
0
    if( nColIdx >= 0 && m_apDataBrowserModel)
880
0
    {
881
        // save changes made to edit-field
882
0
        if( IsModified() )
883
0
            SaveModified();
884
885
0
        m_apDataBrowserModel->insertComplexCategoryLevel( nColIdx );
886
0
        RenewTable();
887
0
    }
888
0
}
889
890
void DataBrowser::RemoveColumn()
891
0
{
892
0
    sal_Int32 nColIdx = lcl_getColumnInDataOrHeader( GetCurColumnId(), m_aSeriesHeaders );
893
894
0
    if( nColIdx >= 0 && m_apDataBrowserModel)
895
0
    {
896
        // save changes made to edit-field
897
0
        if( IsModified() )
898
0
            SaveModified();
899
900
0
        m_bDataValid = true;
901
0
        m_apDataBrowserModel->removeDataSeriesOrComplexCategoryLevel( nColIdx );
902
0
        RenewTable();
903
0
    }
904
0
}
905
906
void DataBrowser::InsertRow()
907
0
{
908
0
    sal_Int32 nRowIdx = GetCurRow();
909
910
0
    if( nRowIdx >= 0 && m_apDataBrowserModel)
911
0
    {
912
        // save changes made to edit-field
913
0
        if( IsModified() )
914
0
            SaveModified();
915
916
0
        m_apDataBrowserModel->insertDataPointForAllSeries( nRowIdx );
917
0
        RenewTable();
918
0
    }
919
0
}
920
921
void DataBrowser::RemoveRow()
922
0
{
923
0
    sal_Int32 nRowIdx = GetCurRow();
924
925
0
    if( nRowIdx >= 0 && m_apDataBrowserModel)
926
0
    {
927
        // save changes made to edit-field
928
0
        if( IsModified() )
929
0
            SaveModified();
930
931
0
        m_bDataValid = true;
932
0
        m_apDataBrowserModel->removeDataPointForAllSeries( nRowIdx );
933
0
        RenewTable();
934
0
    }
935
0
}
936
937
void DataBrowser::MoveLeftColumn()
938
0
{
939
0
    sal_Int32 nColIdx = lcl_getColumnInDataOrHeader( GetCurColumnId(), m_aSeriesHeaders );
940
941
0
    if( !(nColIdx > 0 && m_apDataBrowserModel))
942
0
        return;
943
944
    // save changes made to edit-field
945
0
    if( IsModified() )
946
0
        SaveModified();
947
948
0
    m_apDataBrowserModel->swapDataSeries( nColIdx - 1 );
949
950
    // keep cursor in swapped column
951
0
    if(( 0 < GetCurColumnId() ) && ( GetCurColumnId() <= ColCount() - 1 ))
952
0
    {
953
0
        Dispatch(BrowserDispatchId::CURSORLEFT);
954
0
    }
955
0
    RenewTable();
956
0
}
957
958
void DataBrowser::MoveRightColumn()
959
0
{
960
0
    sal_Int32 nColIdx = lcl_getColumnInDataOrHeader( GetCurColumnId(), m_aSeriesHeaders );
961
962
0
    if( !(nColIdx >= 0 && m_apDataBrowserModel))
963
0
        return;
964
965
    // save changes made to edit-field
966
0
    if( IsModified() )
967
0
        SaveModified();
968
969
0
    m_apDataBrowserModel->swapDataSeries( nColIdx );
970
971
    // keep cursor in swapped column
972
0
    if( GetCurColumnId() < ColCount() - 1 )
973
0
    {
974
0
        Dispatch(BrowserDispatchId::CURSORRIGHT);
975
0
    }
976
0
    RenewTable();
977
0
}
978
979
void DataBrowser::MoveUpRow()
980
0
{
981
0
    sal_Int32 nRowIdx = GetCurRow();
982
983
0
    if( !(nRowIdx > 0 && m_apDataBrowserModel))
984
0
        return;
985
986
    // save changes made to edit-field
987
0
    if( IsModified() )
988
0
        SaveModified();
989
990
0
    m_apDataBrowserModel->swapDataPointForAllSeries( nRowIdx - 1 );
991
992
    // keep cursor in swapped row
993
0
    if(( 0 < GetCurRow() ) && ( GetCurRow() <= GetRowCount() - 1 ))
994
0
    {
995
0
        Dispatch(BrowserDispatchId::CURSORUP);
996
0
    }
997
0
    RenewTable();
998
0
}
999
1000
void DataBrowser::MoveDownRow()
1001
0
{
1002
0
    sal_Int32 nRowIdx = GetCurRow();
1003
1004
0
    if( !(nRowIdx >= 0 && m_apDataBrowserModel))
1005
0
        return;
1006
1007
    // save changes made to edit-field
1008
0
    if( IsModified() )
1009
0
        SaveModified();
1010
1011
0
    m_apDataBrowserModel->swapDataPointForAllSeries( nRowIdx );
1012
1013
    // keep cursor in swapped row
1014
0
    if( GetCurRow() < GetRowCount() - 1 )
1015
0
    {
1016
0
        Dispatch(BrowserDispatchId::CURSORDOWN);
1017
0
    }
1018
0
    RenewTable();
1019
0
}
1020
1021
void DataBrowser::SetCursorMovedHdl( const Link<DataBrowser*,void>& rLink )
1022
0
{
1023
0
    m_aCursorMovedHdlLink = rLink;
1024
0
}
1025
1026
// implementations for ::svt::EditBrowseBox (pure virtual methods)
1027
void DataBrowser::PaintCell(
1028
    OutputDevice& rDev, const tools::Rectangle& rRect, sal_uInt16 nColumnId ) const
1029
0
{
1030
0
    Point aPos( rRect.TopLeft());
1031
0
    aPos.AdjustX(1 );
1032
1033
0
    OUString aText = GetCellText( m_nSeekRow, nColumnId );
1034
0
    Size TxtSize( GetDataWindow().GetTextWidth( aText ), GetDataWindow().GetTextHeight());
1035
1036
    // clipping
1037
0
    if( aPos.X() < rRect.Right() || aPos.X() + TxtSize.Width() > rRect.Right() ||
1038
0
        aPos.Y() < rRect.Top() || aPos.Y() + TxtSize.Height() > rRect.Bottom())
1039
0
        rDev.SetClipRegion(vcl::Region(rRect));
1040
1041
    // allow for a disabled control ...
1042
0
    bool bEnabled = IsEnabled();
1043
0
    Color aOriginalColor = rDev.GetTextColor();
1044
0
    if( ! bEnabled )
1045
0
        rDev.SetTextColor( GetSettings().GetStyleSettings().GetDisableColor() );
1046
1047
    // draw the text
1048
0
    rDev.DrawText( aPos, aText );
1049
1050
    // reset the color (if necessary)
1051
0
    if( ! bEnabled )
1052
0
        rDev.SetTextColor( aOriginalColor );
1053
1054
0
    if( rDev.IsClipRegion())
1055
0
        rDev.SetClipRegion();
1056
0
}
1057
1058
bool DataBrowser::SeekRow( sal_Int32 nRow )
1059
0
{
1060
0
    if( ! EditBrowseBox::SeekRow( nRow ))
1061
0
        return false;
1062
1063
0
    if( nRow < 0 )
1064
0
        m_nSeekRow = - 1;
1065
0
    else
1066
0
        m_nSeekRow = nRow;
1067
1068
0
    return true;
1069
0
}
1070
1071
bool DataBrowser::IsTabAllowed( bool bForward ) const
1072
0
{
1073
0
    sal_Int32 nRow = GetCurRow();
1074
0
    sal_Int32 nCol = GetCurColumnId();
1075
1076
    // column 0 is header-column
1077
0
    sal_Int32 nBadCol = bForward
1078
0
        ? GetColumnCount() - 1
1079
0
        : 1;
1080
0
    sal_Int32 nBadRow = bForward
1081
0
        ? GetRowCount() - 1
1082
0
        : 0;
1083
1084
0
    if( !m_bDataValid )
1085
0
    {
1086
0
        const_cast< DataBrowser* >( this )->ShowWarningBox();
1087
0
        return false;
1088
0
    }
1089
1090
0
    return ( nRow != nBadRow ||
1091
0
             nCol != nBadCol );
1092
0
}
1093
1094
::svt::CellController* DataBrowser::GetController( sal_Int32 /*nRow*/, sal_uInt16 nCol )
1095
0
{
1096
0
    if( m_bIsReadOnly )
1097
0
        return nullptr;
1098
1099
0
    if( CellContainsNumbers( nCol ))
1100
0
    {
1101
0
        Formatter& rFormatter = m_aNumberEditField->get_formatter();
1102
0
        rFormatter.UseInputStringForFormatting();
1103
0
        rFormatter.SetFormatKey( GetNumberFormatKey( nCol ));
1104
0
        return m_rNumberEditController.get();
1105
0
    }
1106
1107
0
    return m_rTextEditController.get();
1108
0
}
1109
1110
void DataBrowser::InitController(
1111
    ::svt::CellControllerRef& rController, sal_Int32 nRow, sal_uInt16 nCol )
1112
0
{
1113
0
    if( rController == m_rTextEditController )
1114
0
    {
1115
0
        OUString aText( GetCellText( nRow, nCol ) );
1116
0
        weld::Entry& rEntry = m_aTextEditField->get_widget();
1117
0
        rEntry.set_text(aText);
1118
0
        rEntry.select_region(0, -1);
1119
0
    }
1120
0
    else if( rController == m_rNumberEditController )
1121
0
    {
1122
        // treat invalid and empty text as Nan
1123
0
        Formatter& rFormatter = m_aNumberEditField->get_formatter();
1124
0
        rFormatter.EnableNotANumber( true );
1125
0
        if( std::isnan( GetCellNumber( nRow, nCol )))
1126
0
            rFormatter.SetTextValue( OUString());
1127
0
        else
1128
0
            rFormatter.SetValue( GetCellNumber( nRow, nCol ) );
1129
0
        weld::Entry& rEntry = m_aNumberEditField->get_widget();
1130
0
        rEntry.select_region(0, -1);
1131
0
    }
1132
0
    else
1133
0
    {
1134
0
        OSL_FAIL( "Invalid Controller" );
1135
0
    }
1136
0
}
1137
1138
bool DataBrowser::CellContainsNumbers( sal_uInt16 nCol ) const
1139
0
{
1140
0
    if (!m_apDataBrowserModel)
1141
0
        return false;
1142
0
    return m_apDataBrowserModel->getCellType( lcl_getColumnInData( nCol )) == DataBrowserModel::NUMBER;
1143
0
}
1144
1145
sal_uInt32 DataBrowser::GetNumberFormatKey( sal_uInt16 nCol ) const
1146
0
{
1147
0
    if (!m_apDataBrowserModel)
1148
0
        return 0;
1149
0
    return m_apDataBrowserModel->getNumberFormatKey( lcl_getColumnInData( nCol ) );
1150
0
}
1151
1152
bool DataBrowser::isDateTimeString( const OUString& aInputString, double& fOutDateTimeValue )
1153
0
{
1154
0
    sal_uInt32 nNumberFormat=0;
1155
0
    SvNumberFormatter* pSvNumberFormatter = m_spNumberFormatterWrapper ? m_spNumberFormatterWrapper->getSvNumberFormatter() : nullptr;
1156
0
    if( !aInputString.isEmpty() &&  pSvNumberFormatter && pSvNumberFormatter->IsNumberFormat( aInputString, nNumberFormat, fOutDateTimeValue ) )
1157
0
    {
1158
0
        SvNumFormatType nType = pSvNumberFormatter->GetType( nNumberFormat);
1159
0
        return (nType & SvNumFormatType::DATE) || (nType & SvNumFormatType::TIME);
1160
0
    }
1161
0
    return false;
1162
0
}
1163
1164
bool DataBrowser::SaveModified()
1165
0
{
1166
0
    if( ! IsModified() )
1167
0
        return true;
1168
1169
0
    bool bChangeValid = true;
1170
1171
0
    const sal_Int32 nRow = GetCurRow();
1172
0
    const sal_Int32 nCol = lcl_getColumnInData( GetCurColumnId());
1173
1174
0
    OSL_ENSURE( nRow >= 0 || nCol >= 0, "This cell should not be modified!" );
1175
1176
0
    SvNumberFormatter* pSvNumberFormatter = m_spNumberFormatterWrapper ? m_spNumberFormatterWrapper->getSvNumberFormatter() : nullptr;
1177
0
    switch( m_apDataBrowserModel->getCellType( nCol ))
1178
0
    {
1179
0
        case DataBrowserModel::NUMBER:
1180
0
        {
1181
0
            sal_uInt32 nDummy = 0;
1182
0
            double fDummy = 0.0;
1183
0
            OUString aText(m_aNumberEditField->get_widget().get_text());
1184
            // an empty string is valid, if no numberformatter exists, all
1185
            // values are treated as valid
1186
0
            if( !aText.isEmpty() && pSvNumberFormatter &&
1187
0
                ! pSvNumberFormatter->IsNumberFormat( aText, nDummy, fDummy ) )
1188
0
            {
1189
0
                bChangeValid = false;
1190
0
            }
1191
0
            else
1192
0
            {
1193
0
                Formatter& rFormatter = m_aNumberEditField->get_formatter();
1194
0
                double fData = rFormatter.GetValue();
1195
0
                bChangeValid = m_apDataBrowserModel->setCellNumber( nCol, nRow, fData );
1196
0
            }
1197
0
        }
1198
0
        break;
1199
0
        case DataBrowserModel::TEXTORDATE:
1200
0
        {
1201
0
            weld::Entry& rEntry = m_aTextEditField->get_widget();
1202
0
            OUString aText(rEntry.get_text());
1203
0
            double fValue = 0.0;
1204
0
            bChangeValid = false;
1205
0
            if( isDateTimeString( aText, fValue ) )
1206
0
                bChangeValid = m_apDataBrowserModel->setCellAny( nCol, nRow, uno::Any( fValue ) );
1207
0
            if(!bChangeValid)
1208
0
                bChangeValid = m_apDataBrowserModel->setCellAny( nCol, nRow, uno::Any( aText ) );
1209
0
        }
1210
0
        break;
1211
0
        case DataBrowserModel::TEXT:
1212
0
        {
1213
0
            weld::Entry& rEntry = m_aTextEditField->get_widget();
1214
0
            OUString aText(rEntry.get_text());
1215
0
            bChangeValid = m_apDataBrowserModel->setCellText( nCol, nRow, aText );
1216
0
        }
1217
0
        break;
1218
0
    }
1219
1220
    // the first valid change changes this to true
1221
0
    if( bChangeValid )
1222
0
    {
1223
0
        RowModified( GetCurRow(), GetCurColumnId());
1224
0
        ::svt::CellController* pCtrl = GetController( GetCurRow(), GetCurColumnId());
1225
0
        if( pCtrl )
1226
0
            pCtrl->SaveValue();
1227
0
    }
1228
1229
0
    return bChangeValid;
1230
0
}
1231
1232
bool DataBrowser::EndEditing()
1233
0
{
1234
0
    SaveModified();
1235
1236
    // apply changes made to series headers
1237
0
    for( const auto& spHeader : m_aSeriesHeaders )
1238
0
        spHeader->applyChanges();
1239
1240
0
    if( m_bDataValid )
1241
0
        return true;
1242
0
    else
1243
0
        return ShowQueryBox();
1244
0
}
1245
1246
void DataBrowser::ColumnResized( sal_uInt16 nColId )
1247
0
{
1248
0
    bool bLastUpdateMode = GetUpdateMode();
1249
0
    SetUpdateMode( false );
1250
1251
0
    EditBrowseBox::ColumnResized( nColId );
1252
0
    ImplAdjustHeaderControls();
1253
0
    SetUpdateMode( bLastUpdateMode );
1254
0
}
1255
1256
void DataBrowser::EndScroll()
1257
0
{
1258
0
    bool bLastUpdateMode = GetUpdateMode();
1259
0
    SetUpdateMode( false );
1260
1261
0
    EditBrowseBox::EndScroll();
1262
0
    RenewSeriesHeaders();
1263
1264
0
    SetUpdateMode( bLastUpdateMode );
1265
0
}
1266
1267
void DataBrowser::RenewSeriesHeaders()
1268
0
{
1269
0
    clearHeaders();
1270
0
    DataBrowserModel::tDataHeaderVector aHeaders( m_apDataBrowserModel->getDataHeaders());
1271
0
    Link<impl::SeriesHeaderEdit&,void> aFocusLink( LINK( this, DataBrowser, SeriesHeaderGotFocus ));
1272
0
    Link<impl::SeriesHeaderEdit&,void> aSeriesHeaderChangedLink( LINK( this, DataBrowser, SeriesHeaderChanged ));
1273
1274
1275
0
    for (size_t i = 0; i < aHeaders.size(); ++i)
1276
0
    {
1277
0
        auto const& elemHeader = aHeaders[i];
1278
0
        auto spHeader = std::make_shared<impl::SeriesHeader>( m_pColumnsWin, m_pColorsWin, i );
1279
0
        Color nColor;
1280
0
        if( elemHeader.m_xDataSeries.is() &&
1281
0
            ( elemHeader.m_xDataSeries->getPropertyValue( u"Color"_ustr ) >>= nColor ))
1282
0
            spHeader->SetColor( nColor );
1283
0
        spHeader->SetChartType( elemHeader.m_xChartType, elemHeader.m_bSwapXAndYAxis );
1284
0
        spHeader->SetSeriesName(
1285
0
            elemHeader.m_xDataSeries->getLabelForRole(
1286
0
                        elemHeader.m_xChartType.is() ?
1287
0
                         elemHeader.m_xChartType->getRoleOfSequenceForSeriesLabel() :
1288
0
                         u"values-y"_ustr));
1289
0
        spHeader->SetRange( elemHeader.m_nStartColumn + 1, elemHeader.m_nEndColumn + 1 );
1290
0
        spHeader->SetGetFocusHdl( aFocusLink );
1291
0
        spHeader->SetEditChangedHdl( aSeriesHeaderChangedLink );
1292
0
        m_aSeriesHeaders.push_back(std::move(spHeader));
1293
0
    }
1294
1295
0
    ImplAdjustHeaderControls();
1296
0
}
1297
1298
void DataBrowser::ImplAdjustHeaderControls()
1299
0
{
1300
0
    sal_uInt16 nColCount = GetColumnCount();
1301
0
    sal_uInt32 nCurrentPos = GetPosPixel().getX();
1302
0
    sal_uInt32 nMaxPos = nCurrentPos + GetOutputSizePixel().getWidth();
1303
0
    sal_uInt32 nStartPos = nCurrentPos;
1304
1305
    // width of header column
1306
0
    nCurrentPos +=  GetColumnWidth( 0 );
1307
1308
0
    weld::Container* pWin = m_pColumnsWin;
1309
0
    weld::Container* pColorWin = m_pColorsWin;
1310
0
    pWin->set_margin_start(nCurrentPos);
1311
0
    pColorWin->set_margin_start(nCurrentPos);
1312
1313
0
    tSeriesHeaderContainer::iterator aIt( m_aSeriesHeaders.begin());
1314
0
    sal_uInt16 i = GetFirstVisibleColNumber();
1315
0
    while( (aIt != m_aSeriesHeaders.end()) && ((*aIt)->GetStartColumn() < i) )
1316
0
    {
1317
0
        (*aIt)->Hide();
1318
0
        ++aIt;
1319
0
    }
1320
0
    for( ; i < nColCount && aIt != m_aSeriesHeaders.end(); ++i )
1321
0
    {
1322
0
        if( (*aIt)->GetStartColumn() == i )
1323
0
            nStartPos = nCurrentPos;
1324
1325
0
        nCurrentPos += (GetColumnWidth( i ));
1326
1327
0
        if( (*aIt)->GetEndColumn() == i )
1328
0
        {
1329
0
            if( nStartPos < nMaxPos )
1330
0
            {
1331
0
                (*aIt)->SetPixelWidth( nCurrentPos - nStartPos );
1332
0
                (*aIt)->Show();
1333
1334
0
                if (pWin)
1335
0
                {
1336
0
                    pWin->set_margin_start(nStartPos);
1337
0
                    pColorWin->set_margin_start(nStartPos);
1338
0
                    pWin = pColorWin = nullptr;
1339
0
                }
1340
1341
0
            }
1342
0
            else
1343
0
                (*aIt)->Hide();
1344
0
            ++aIt;
1345
0
        }
1346
0
    }
1347
0
}
1348
1349
IMPL_LINK( DataBrowser, SeriesHeaderGotFocus, impl::SeriesHeaderEdit&, rEdit, void )
1350
0
{
1351
0
    rEdit.SetShowWarningBox( !m_bDataValid );
1352
1353
0
    if( !m_bDataValid )
1354
0
        GoToCell( 0, 0 );
1355
0
    else
1356
0
    {
1357
0
        MakeFieldVisible( GetCurRow(), static_cast< sal_uInt16 >( rEdit.getStartColumn()) );
1358
0
        ActivateCell();
1359
0
        m_aCursorMovedHdlLink.Call( this );
1360
0
    }
1361
0
}
1362
1363
IMPL_LINK( DataBrowser, SeriesHeaderChanged, impl::SeriesHeaderEdit&, rEdit, void )
1364
0
{
1365
0
    rtl::Reference< DataSeries > xSeries =
1366
0
        m_apDataBrowserModel->getDataSeriesByColumn( rEdit.getStartColumn() - 1 );
1367
0
    if( !xSeries.is())
1368
0
        return;
1369
1370
0
    rtl::Reference< ChartType > xChartType(
1371
0
        m_apDataBrowserModel->getHeaderForSeries( xSeries ).m_xChartType );
1372
0
    if( xChartType.is())
1373
0
    {
1374
0
        uno::Reference< chart2::data::XLabeledDataSequence > xLabeledSeq =
1375
0
            DataSeriesHelper::getDataSequenceByRole( xSeries, xChartType->getRoleOfSequenceForSeriesLabel());
1376
0
        if( xLabeledSeq.is())
1377
0
        {
1378
0
            Reference< container::XIndexReplace > xIndexReplace( xLabeledSeq->getLabel(), uno::UNO_QUERY );
1379
0
            if( xIndexReplace.is())
1380
0
                xIndexReplace->replaceByIndex(
1381
0
                    0, uno::Any( rEdit.GetText()));
1382
0
        }
1383
0
    }
1384
0
}
1385
1386
} // namespace chart
1387
1388
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */