1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <svl/zforlist.hxx>
22 #include "DataBrowser.hxx"
23 #include "DataBrowserModel.hxx"
24 #include <strings.hrc>
25 #include <DataSeriesHelper.hxx>
26 #include <DiagramHelper.hxx>
27 #include <CommonConverters.hxx>
28 #include <NumberFormatterWrapper.hxx>
29 #include <servicenames_charttypes.hxx>
31 #include <bitmaps.hlst>
34 #include <vcl/weld.hxx>
35 #include <vcl/settings.hxx>
36 #include <vcl/svapp.hxx>
37 #include <vcl/virdev.hxx>
38 #include <rtl/math.hxx>
39 #include <osl/diagnose.h>
40 #include <toolkit/helper/vclunohelper.hxx>
42 #include <com/sun/star/util/XCloneable.hpp>
43 #include <com/sun/star/chart2/XChartDocument.hpp>
44 #include <com/sun/star/chart2/XChartType.hpp>
45 #include <com/sun/star/container/XIndexReplace.hpp>
50 using namespace ::com::sun::star
;
51 using ::com::sun::star::uno::Reference
;
53 using namespace ::svt
;
57 /* BrowserMode::COLUMNSELECTION : single cells may be selected rather than only
59 BrowserMode::(H|V)LINES : show horizontal or vertical grid-lines
60 BrowserMode::AUTO_(H|V)SCROLL : scroll automated horizontally or vertically when
61 cursor is moved beyond the edge of the dialog
62 BrowserMode::HIDESELECT : Do not mark the current row with selection color
64 ! BrowserMode::HIDECURSOR would prevent flickering in edit fields, but navigating
65 with shift up/down, and entering non-editable cells would be problematic,
66 e.g. the first cell, or when being in read-only mode
68 const BrowserMode BrowserStdFlags
= BrowserMode::COLUMNSELECTION
|
69 BrowserMode::HLINES
| BrowserMode::VLINES
|
70 BrowserMode::AUTO_HSCROLL
| BrowserMode::AUTO_VSCROLL
|
71 BrowserMode::HIDESELECT
;
73 sal_Int32
lcl_getRowInData( long nRow
)
75 return static_cast< sal_Int32
>( nRow
);
78 sal_Int32
lcl_getColumnInData( sal_uInt16 nCol
)
80 return static_cast< sal_Int32
>( nCol
) - 1;
83 } // anonymous namespace
91 class SeriesHeaderEdit
94 explicit SeriesHeaderEdit(std::unique_ptr
<weld::Entry
> xControl
);
96 void setStartColumn( sal_Int32 nStartColumn
);
97 sal_Int32
getStartColumn() const { return m_nStartColumn
;}
98 void SetShowWarningBox( bool bShowWarning
);
100 OUString
GetText() const { return m_xControl
->get_text(); }
101 void SetText(const OUString
& rText
) { m_xControl
->set_text(rText
); }
103 bool HasFocus() const { return m_xControl
->has_focus(); }
105 void Hide() { m_xControl
->hide(); }
106 void Show() { m_xControl
->show(); }
108 void set_size_request(int nWidth
, int nHeight
) { m_xControl
->set_size_request(nWidth
, nHeight
); }
109 void set_margin_left(int nLeft
) { m_xControl
->set_margin_left(nLeft
); }
111 void SetModifyHdl(const Link
<SeriesHeaderEdit
&,void>& rLink
) { m_aModifyHdl
= rLink
; }
112 void SetGetFocusHdl(const Link
<SeriesHeaderEdit
&,void>& rLink
) { m_aFocusInHdl
= rLink
; }
115 DECL_LINK(NameEdited
, weld::Entry
&, void);
116 DECL_LINK(NameFocusIn
, weld::Widget
&, void);
117 DECL_LINK(MousePressHdl
, const MouseEvent
&, bool);
119 std::unique_ptr
<weld::Entry
> m_xControl
;
120 Link
<SeriesHeaderEdit
&,void> m_aModifyHdl
;
121 Link
<SeriesHeaderEdit
&,void> m_aFocusInHdl
;
122 sal_Int32 m_nStartColumn
;
123 bool m_bShowWarningBox
;
126 SeriesHeaderEdit::SeriesHeaderEdit(std::unique_ptr
<weld::Entry
> xControl
)
127 : m_xControl(std::move(xControl
))
129 , m_bShowWarningBox(false)
131 m_xControl
->set_help_id(HID_SCH_DATA_SERIES_LABEL
);
132 m_xControl
->connect_changed(LINK(this, SeriesHeaderEdit
, NameEdited
));
133 m_xControl
->connect_focus_in(LINK(this, SeriesHeaderEdit
, NameFocusIn
));
134 m_xControl
->connect_mouse_press(LINK(this, SeriesHeaderEdit
, MousePressHdl
));
137 IMPL_LINK_NOARG(SeriesHeaderEdit
, NameEdited
, weld::Entry
&, void)
139 m_aModifyHdl
.Call(*this);
142 IMPL_LINK_NOARG(SeriesHeaderEdit
, NameFocusIn
, weld::Widget
&, void)
144 m_aFocusInHdl
.Call(*this);
147 void SeriesHeaderEdit::setStartColumn( sal_Int32 nStartColumn
)
149 m_nStartColumn
= nStartColumn
;
152 void SeriesHeaderEdit::SetShowWarningBox( bool bShowWarning
)
154 m_bShowWarningBox
= bShowWarning
;
157 IMPL_LINK_NOARG(SeriesHeaderEdit
, MousePressHdl
, const MouseEvent
&, bool)
159 if (m_bShowWarningBox
)
161 std::unique_ptr
<weld::MessageDialog
> xWarn(Application::CreateMessageDialog(m_xControl
.get(),
162 VclMessageType::Warning
, VclButtonsType::Ok
,
163 SchResId(STR_INVALID_NUMBER
)));
173 explicit SeriesHeader(weld::Container
* pParent
, weld::Container
* pColorParent
);
176 void SetColor( const Color
& rCol
);
178 void SetWidth( sal_Int32 nWidth
);
179 void SetChartType( const Reference
< chart2::XChartType
> & xChartType
,
180 bool bSwapXAndYAxis
);
181 void SetSeriesName( const OUString
& rName
);
182 void SetRange( sal_Int32 nStartCol
, sal_Int32 nEndCol
);
184 void SetPixelWidth( sal_Int32 nWidth
);
186 sal_Int32
GetStartColumn() const { return m_nStartCol
;}
187 sal_Int32
GetEndColumn() const { return m_nEndCol
;}
189 static const sal_Int32 nSymbolHeight
= 10;
190 static const sal_Int32 nSymbolDistance
= 2;
192 static sal_Int32
GetRelativeAppFontXPosForNameField() { return nSymbolHeight
+ nSymbolDistance
; }
197 /** call this before destroying the class. This notifies the listeners to
198 changes of the edit field for the series name.
202 void SetGetFocusHdl(const Link
<SeriesHeaderEdit
&,void>& rLink
);
204 void SetEditChangedHdl( const Link
<SeriesHeaderEdit
&,void> & rLink
);
206 bool HasFocus() const;
209 Timer m_aUpdateDataTimer
;
211 std::unique_ptr
<weld::Builder
> m_xBuilder1
;
212 std::unique_ptr
<weld::Builder
> m_xBuilder2
;
214 std::unique_ptr
<weld::Container
> m_xContainer1
;
215 std::unique_ptr
<weld::Container
> m_xContainer2
;
216 std::unique_ptr
<weld::Image
> m_spSymbol
;
217 std::unique_ptr
<SeriesHeaderEdit
> m_spSeriesName
;
218 std::unique_ptr
<weld::Image
> m_spColorBar
;
219 VclPtr
< OutputDevice
> m_xDevice
;
220 Link
<SeriesHeaderEdit
&,void> m_aChangeLink
;
223 void notifyChanges();
224 DECL_LINK( ImplUpdateDataHdl
, Timer
*, void );
225 DECL_LINK( SeriesNameEdited
, SeriesHeaderEdit
&, void );
227 static OUString
GetChartTypeImage(
228 const Reference
< chart2::XChartType
> & xChartType
,
232 sal_Int32 m_nStartCol
, m_nEndCol
;
234 bool m_bSeriesNameChangePending
;
237 SeriesHeader::SeriesHeader(weld::Container
* pParent
, weld::Container
* pColorParent
)
238 : m_aUpdateDataTimer("UpdateDataTimer")
239 , m_xBuilder1(Application::CreateBuilder(pParent
, "modules/schart/ui/columnfragment.ui"))
240 , m_xBuilder2(Application::CreateBuilder(pColorParent
, "modules/schart/ui/imagefragment.ui"))
241 , m_xContainer1(m_xBuilder1
->weld_container("container"))
242 , m_xContainer2(m_xBuilder2
->weld_container("container"))
243 , m_spSymbol(m_xBuilder1
->weld_image("image"))
244 , m_spSeriesName(new SeriesHeaderEdit(m_xBuilder1
->weld_entry("entry")))
245 , m_spColorBar(m_xBuilder2
->weld_image("image"))
246 , m_xDevice(Application::GetDefaultDevice())
250 , m_bSeriesNameChangePending( false )
252 m_aUpdateDataTimer
.SetInvokeHandler(LINK(this, SeriesHeader
, ImplUpdateDataHdl
));
253 m_aUpdateDataTimer
.SetDebugName( "SeriesHeader UpdateDataTimer" );
254 m_aUpdateDataTimer
.SetTimeout(4 * EDIT_UPDATEDATA_TIMEOUT
);
256 m_spSeriesName
->SetModifyHdl(LINK(this, SeriesHeader
, SeriesNameEdited
));
260 SeriesHeader::~SeriesHeader()
262 m_aUpdateDataTimer
.Stop();
268 void SeriesHeader::notifyChanges()
270 m_aChangeLink
.Call(*m_spSeriesName
);
271 m_bSeriesNameChangePending
= false;
274 void SeriesHeader::applyChanges()
276 if( m_bSeriesNameChangePending
)
282 void SeriesHeader::SetColor( const Color
& rCol
)
287 void SeriesHeader::SetPos()
290 Size
aSize( nSymbolHeight
, nSymbolHeight
);
291 aSize
= m_xDevice
->LogicToPixel(aSize
, MapMode(MapUnit::MapAppFont
));
292 m_spSymbol
->set_size_request(aSize
.Width(), aSize
.Height());
294 // series name edit field
295 aSize
.setWidth(nSymbolDistance
);
296 aSize
= m_xDevice
->LogicToPixel(aSize
, MapMode(MapUnit::MapAppFont
));
297 m_spSeriesName
->set_margin_left(aSize
.Width() + 2);
298 aSize
.setWidth( m_nWidth
- nSymbolHeight
- nSymbolDistance
);
299 sal_Int32 nHeight
= 12;
300 aSize
.setHeight( nHeight
);
301 aSize
= m_xDevice
->LogicToPixel(aSize
, MapMode(MapUnit::MapAppFont
));
302 m_spSeriesName
->set_size_request(aSize
.Width(), aSize
.Height());
306 aSize
= m_xDevice
->LogicToPixel(aSize
, MapMode(MapUnit::MapAppFont
));
307 m_spColorBar
->set_margin_left(aSize
.Width() + 2);
309 aSize
.setWidth( m_nWidth
- 1 );
310 aSize
.setHeight( nHeight
);
311 aSize
= m_xDevice
->LogicToPixel(aSize
, MapMode(MapUnit::MapAppFont
));
312 m_spColorBar
->set_size_request(aSize
.Width(), aSize
.Height());
314 auto xVirDev(m_spColorBar
->create_virtual_device());
315 xVirDev
->SetOutputSizePixel(aSize
);
316 xVirDev
->SetFillColor(m_aColor
);
317 xVirDev
->SetLineColor(m_aColor
);
318 xVirDev
->DrawRect(tools::Rectangle(Point(0, 0), aSize
));
319 m_spColorBar
->set_image(xVirDev
.get());
322 void SeriesHeader::SetWidth( sal_Int32 nWidth
)
328 void SeriesHeader::SetPixelWidth( sal_Int32 nWidth
)
330 SetWidth( m_xDevice
->PixelToLogic(Size(nWidth
, 0), MapMode(MapUnit::MapAppFont
)).getWidth());
333 void SeriesHeader::SetChartType(
334 const Reference
< chart2::XChartType
> & xChartType
,
338 m_spSymbol
->set_from_icon_name( GetChartTypeImage( xChartType
, bSwapXAndYAxis
) );
341 void SeriesHeader::SetSeriesName( const OUString
& rName
)
343 m_spSeriesName
->SetText(rName
);
346 void SeriesHeader::SetRange( sal_Int32 nStartCol
, sal_Int32 nEndCol
)
348 m_nStartCol
= nStartCol
;
349 m_nEndCol
= std::max(nEndCol
, nStartCol
);
350 m_spSeriesName
->setStartColumn( nStartCol
);
353 void SeriesHeader::Show()
356 m_spSeriesName
->Show();
357 m_spColorBar
->show();
360 void SeriesHeader::Hide()
363 m_spSeriesName
->Hide();
364 m_spColorBar
->hide();
367 void SeriesHeader::SetEditChangedHdl( const Link
<SeriesHeaderEdit
&,void> & rLink
)
369 m_aChangeLink
= rLink
;
372 IMPL_LINK_NOARG(SeriesHeader
, ImplUpdateDataHdl
, Timer
*, void)
377 IMPL_LINK_NOARG(SeriesHeader
, SeriesNameEdited
, SeriesHeaderEdit
&, void)
379 m_bSeriesNameChangePending
= true;
380 m_aUpdateDataTimer
.Start();
383 void SeriesHeader::SetGetFocusHdl( const Link
<SeriesHeaderEdit
&,void>& rLink
)
385 m_spSeriesName
->SetGetFocusHdl( rLink
);
388 bool SeriesHeader::HasFocus() const
390 return m_spSeriesName
->HasFocus();
393 OUString
SeriesHeader::GetChartTypeImage(
394 const Reference
< chart2::XChartType
> & xChartType
,
399 if( !xChartType
.is())
401 OUString
aChartTypeName( xChartType
->getChartType());
403 if( aChartTypeName
== CHART2_SERVICE_NAME_CHARTTYPE_AREA
)
405 aResult
= BMP_TYPE_AREA
;
407 else if( aChartTypeName
== CHART2_SERVICE_NAME_CHARTTYPE_COLUMN
)
410 aResult
= BMP_TYPE_BAR
;
412 aResult
= BMP_TYPE_COLUMN
;
414 else if( aChartTypeName
== CHART2_SERVICE_NAME_CHARTTYPE_LINE
)
416 aResult
= BMP_TYPE_LINE
;
418 else if( aChartTypeName
== CHART2_SERVICE_NAME_CHARTTYPE_SCATTER
)
420 aResult
= BMP_TYPE_XY
;
422 else if( aChartTypeName
== CHART2_SERVICE_NAME_CHARTTYPE_PIE
)
424 aResult
= BMP_TYPE_PIE
;
426 else if( aChartTypeName
== CHART2_SERVICE_NAME_CHARTTYPE_NET
427 || aChartTypeName
== CHART2_SERVICE_NAME_CHARTTYPE_FILLED_NET
)
429 aResult
= BMP_TYPE_NET
;
431 else if( aChartTypeName
== CHART2_SERVICE_NAME_CHARTTYPE_CANDLESTICK
)
433 // @todo: correct image for candle-stick type
434 aResult
= BMP_TYPE_STOCK
;
436 else if( aChartTypeName
== CHART2_SERVICE_NAME_CHARTTYPE_BUBBLE
)
438 aResult
= BMP_TYPE_BUBBLE
;
449 /** returns false, if no header as the focus.
451 If a header has the focus, true is returned and the index of the header
452 with focus is set at pIndex if pOutIndex is not 0.
454 bool lcl_SeriesHeaderHasFocus(
455 const std::vector
< std::shared_ptr
< ::chart::impl::SeriesHeader
> > & rSeriesHeader
,
456 sal_Int32
* pOutIndex
= nullptr )
458 sal_Int32 nIndex
= 0;
459 for (auto const& elem
: rSeriesHeader
)
472 sal_Int32
lcl_getColumnInDataOrHeader(
473 sal_uInt16 nCol
, const std::vector
< std::shared_ptr
< ::chart::impl::SeriesHeader
> > & rSeriesHeader
)
475 sal_Int32 nColIdx
= 0;
476 bool bHeaderHasFocus( lcl_SeriesHeaderHasFocus( rSeriesHeader
, &nColIdx
));
478 if( bHeaderHasFocus
)
479 nColIdx
= lcl_getColumnInData( static_cast< sal_uInt16
>( rSeriesHeader
[nColIdx
]->GetStartColumn()));
481 nColIdx
= lcl_getColumnInData( nCol
);
486 } // anonymous namespace
488 DataBrowser::DataBrowser(const css::uno::Reference
<css::awt::XWindow
> &rParent
,
489 weld::Container
* pColumns
, weld::Container
* pColors
) :
490 ::svt::EditBrowseBox(VCLUnoHelper::GetWindow(rParent
),
491 EditBrowseBoxFlags::SMART_TAB_TRAVEL
| EditBrowseBoxFlags::HANDLE_COLUMN_TEXT
,
492 WB_BORDER
| WB_TABSTOP
, BrowserStdFlags
),
494 m_bIsReadOnly( false ),
495 m_bDataValid( true ),
496 m_aNumberEditField( VclPtr
<FormattedField
>::Create( & EditBrowseBox::GetDataWindow(), WB_NOBORDER
) ),
497 m_aTextEditField( VclPtr
<Edit
>::Create( & EditBrowseBox::GetDataWindow(), WB_NOBORDER
) ),
498 m_pColumnsWin(pColumns
),
499 m_pColorsWin(pColors
),
500 m_rNumberEditController( new ::svt::FormattedFieldCellController( m_aNumberEditField
.get() )),
501 m_rTextEditController( new ::svt::EditCellController( m_aTextEditField
.get() ))
504 ::rtl::math::setNan( & fNan
);
505 m_aNumberEditField
->SetDefaultValue( fNan
);
506 m_aNumberEditField
->TreatAsNumber( true );
510 DataBrowser::~DataBrowser()
515 void DataBrowser::dispose()
517 m_aNumberEditField
.disposeAndClear();
518 m_aTextEditField
.disposeAndClear();
519 ::svt::EditBrowseBox::dispose();
522 bool DataBrowser::MayInsertRow() const
524 return ! IsReadOnly()
525 && ( !lcl_SeriesHeaderHasFocus( m_aSeriesHeaders
));
528 bool DataBrowser::MayInsertColumn() const
530 return ! IsReadOnly();
533 bool DataBrowser::MayDeleteRow() const
535 return ! IsReadOnly()
536 && ( !lcl_SeriesHeaderHasFocus( m_aSeriesHeaders
))
537 && ( GetCurRow() >= 0 )
538 && ( GetRowCount() > 1 );
541 bool DataBrowser::MayDeleteColumn() const
543 // if a series header has the focus
544 if( lcl_SeriesHeaderHasFocus( m_aSeriesHeaders
))
547 return ! IsReadOnly()
548 && ( GetCurColumnId() > 1 )
549 && ( ColCount() > 2 );
552 bool DataBrowser::MayMoveUpRows() const
554 return ! IsReadOnly()
555 && ( !lcl_SeriesHeaderHasFocus( m_aSeriesHeaders
))
556 && ( GetCurRow() > 0 )
557 && ( GetCurRow() <= GetRowCount() - 1 );
560 bool DataBrowser::MayMoveDownRows() const
562 return ! IsReadOnly()
563 && ( !lcl_SeriesHeaderHasFocus( m_aSeriesHeaders
))
564 && ( GetCurRow() >= 0 )
565 && ( GetCurRow() < GetRowCount() - 1 );
568 bool DataBrowser::MayMoveLeftColumns() const
570 // if a series header (except the last one) has the focus
572 sal_Int32
nColIndex(0);
573 if( lcl_SeriesHeaderHasFocus( m_aSeriesHeaders
, &nColIndex
))
574 return (static_cast< sal_uInt32
>( nColIndex
) <= (m_aSeriesHeaders
.size() - 1)) && (static_cast< sal_uInt32
>( nColIndex
) != 0);
577 sal_Int32 nColIdx
= lcl_getColumnInDataOrHeader( GetCurColumnId(), m_aSeriesHeaders
);
578 return ! IsReadOnly()
580 && ( nColIdx
<= ColCount() - 2 )
581 && m_apDataBrowserModel
.get()
582 && !m_apDataBrowserModel
->isCategoriesColumn( nColIdx
);
585 bool DataBrowser::MayMoveRightColumns() const
587 // if a series header (except the last one) has the focus
589 sal_Int32
nColIndex(0);
590 if( lcl_SeriesHeaderHasFocus( m_aSeriesHeaders
, &nColIndex
))
591 return (static_cast< sal_uInt32
>( nColIndex
) < (m_aSeriesHeaders
.size() - 1));
594 sal_Int32 nColIdx
= lcl_getColumnInDataOrHeader( GetCurColumnId(), m_aSeriesHeaders
);
595 return ! IsReadOnly()
597 && ( nColIdx
< ColCount()-2 )
598 && m_apDataBrowserModel
.get()
599 && !m_apDataBrowserModel
->isCategoriesColumn( nColIdx
);
602 void DataBrowser::clearHeaders()
604 for( const auto& spHeader
: m_aSeriesHeaders
)
605 spHeader
->applyChanges();
606 m_aSeriesHeaders
.clear();
609 void DataBrowser::RenewTable()
611 if (!m_apDataBrowserModel
)
614 long nOldRow
= GetCurRow();
615 sal_uInt16 nOldColId
= GetCurColumnId();
617 bool bLastUpdateMode
= GetUpdateMode();
618 SetUpdateMode( false );
626 RowRemoved( 1, GetRowCount() );
629 InsertHandleColumn( static_cast< sal_uInt16
>(
630 GetDataWindow().LogicToPixel( Size( 42, 0 )).getWidth() ));
632 OUString
aDefaultSeriesName(SchResId(STR_COLUMN_LABEL
));
633 replaceParamterInString( aDefaultSeriesName
, "%COLUMNNUMBER", OUString::number( 24 ) );
634 sal_Int32 nColumnWidth
= GetDataWindow().GetTextWidth( aDefaultSeriesName
)
635 + GetDataWindow().LogicToPixel(Point(8 + impl::SeriesHeader::GetRelativeAppFontXPosForNameField(), 0), MapMode(MapUnit::MapAppFont
)).X();
636 sal_Int32 nColumnCount
= m_apDataBrowserModel
->getColumnCount();
637 // nRowCount is a member of a base class
638 sal_Int32 nRowCountLocal
= m_apDataBrowserModel
->getMaxRowCount();
639 for( sal_Int32 nColIdx
=1; nColIdx
<=nColumnCount
; ++nColIdx
)
641 InsertDataColumn( static_cast< sal_uInt16
>( nColIdx
), GetColString( nColIdx
), nColumnWidth
);
644 RowInserted( 1, nRowCountLocal
);
645 GoToRow( std::min( nOldRow
, GetRowCount() - 1 ));
646 GoToColumnId( std::min( nOldColId
, static_cast< sal_uInt16
>( ColCount() - 1 )));
648 // fill series headers
650 const DataBrowserModel::tDataHeaderVector
& aHeaders( m_apDataBrowserModel
->getDataHeaders());
651 Link
<impl::SeriesHeaderEdit
&,void> aFocusLink( LINK( this, DataBrowser
, SeriesHeaderGotFocus
));
652 Link
<impl::SeriesHeaderEdit
&,void> aSeriesHeaderChangedLink( LINK( this, DataBrowser
, SeriesHeaderChanged
));
654 for (auto const& elemHeader
: aHeaders
)
656 std::shared_ptr
< impl::SeriesHeader
> spHeader( new impl::SeriesHeader( m_pColumnsWin
, m_pColorsWin
));
657 Reference
< beans::XPropertySet
> xSeriesProp( elemHeader
.m_xDataSeries
, uno::UNO_QUERY
);
658 sal_Int32 nColor
= 0;
659 // @todo: Set "DraftColor", i.e. interpolated colors for gradients, bitmaps, etc.
660 if( xSeriesProp
.is() &&
661 ( xSeriesProp
->getPropertyValue( "Color" ) >>= nColor
))
662 spHeader
->SetColor( Color( nColor
));
663 spHeader
->SetChartType( elemHeader
.m_xChartType
, elemHeader
.m_bSwapXAndYAxis
);
664 spHeader
->SetSeriesName(
665 DataSeriesHelper::getDataSeriesLabel(
666 elemHeader
.m_xDataSeries
,
667 (elemHeader
.m_xChartType
.is() ?
668 elemHeader
.m_xChartType
->getRoleOfSequenceForSeriesLabel() :
669 OUString("values-y"))));
670 // index is 1-based, as 0 is for the column that contains the row-numbers
671 spHeader
->SetRange( elemHeader
.m_nStartColumn
+ 1, elemHeader
.m_nEndColumn
+ 1 );
672 spHeader
->SetGetFocusHdl( aFocusLink
);
673 spHeader
->SetEditChangedHdl( aSeriesHeaderChangedLink
);
674 m_aSeriesHeaders
.push_back( spHeader
);
677 ImplAdjustHeaderControls();
678 SetUpdateMode( bLastUpdateMode
);
683 OUString
DataBrowser::GetColString( sal_Int32 nColumnId
) const
685 OSL_ASSERT(m_apDataBrowserModel
);
687 return m_apDataBrowserModel
->getRoleOfColumn( nColumnId
- 1 );
691 OUString
DataBrowser::GetCellText( long nRow
, sal_uInt16 nColumnId
) const
697 aResult
= OUString::number(static_cast< sal_Int32
>( nRow
) + 1);
699 else if( nRow
>= 0 && m_apDataBrowserModel
.get())
701 sal_Int32 nColIndex
= static_cast< sal_Int32
>( nColumnId
) - 1;
703 if( m_apDataBrowserModel
->getCellType( nColIndex
) == DataBrowserModel::NUMBER
)
705 double fData( m_apDataBrowserModel
->getCellNumber( nColIndex
, nRow
));
708 if( ! ::rtl::math::isNan( fData
) &&
709 m_spNumberFormatterWrapper
.get() )
711 bool bColorChanged
= false;
712 aResult
= m_spNumberFormatterWrapper
->getFormattedString(
713 GetNumberFormatKey( nColumnId
),
714 fData
, nLabelColor
, bColorChanged
);
717 else if( m_apDataBrowserModel
->getCellType( nColIndex
) == DataBrowserModel::TEXTORDATE
)
719 uno::Any aAny
= m_apDataBrowserModel
->getCellAny( nColIndex
, nRow
);
724 else if( aAny
>>=fDouble
)
726 if( ! ::rtl::math::isNan( fDouble
) && m_spNumberFormatterWrapper
.get() )
728 // If a numberformat was available here we could directly
729 // obtain the corresponding edit format in
730 // getDateTimeInputNumberFormat() instead of doing the
732 sal_Int32 nNumberFormat
= DiagramHelper::getDateTimeInputNumberFormat(
733 Reference
< util::XNumberFormatsSupplier
>( m_xChartDoc
, uno::UNO_QUERY
), fDouble
);
735 bool bColorChanged
= false;
736 aResult
= m_spNumberFormatterWrapper
->getFormattedString(
737 nNumberFormat
, fDouble
, nLabelColor
, bColorChanged
);
743 OSL_ASSERT( m_apDataBrowserModel
->getCellType( nColIndex
) == DataBrowserModel::TEXT
);
744 aResult
= m_apDataBrowserModel
->getCellText( nColIndex
, nRow
);
751 double DataBrowser::GetCellNumber( long nRow
, sal_uInt16 nColumnId
) const
754 ::rtl::math::setNan( & fResult
);
756 if(( nColumnId
>= 1 ) && ( nRow
>= 0 ) &&
757 m_apDataBrowserModel
.get())
759 fResult
= m_apDataBrowserModel
->getCellNumber(
760 static_cast< sal_Int32
>( nColumnId
) - 1, nRow
);
766 void DataBrowser::Resize()
768 bool bLastUpdateMode
= GetUpdateMode();
769 SetUpdateMode( false );
771 ::svt::EditBrowseBox::Resize();
772 ImplAdjustHeaderControls();
773 SetUpdateMode( bLastUpdateMode
);
776 void DataBrowser::SetReadOnly( bool bNewState
)
778 if( m_bIsReadOnly
!= bNewState
)
780 m_bIsReadOnly
= bNewState
;
786 void DataBrowser::CursorMoved()
788 EditBrowseBox::CursorMoved();
790 if( GetUpdateMode() )
791 m_aCursorMovedHdlLink
.Call( this );
794 void DataBrowser::MouseButtonDown( const BrowserMouseEvent
& rEvt
)
799 EditBrowseBox::MouseButtonDown( rEvt
);
802 void DataBrowser::ShowWarningBox()
804 std::unique_ptr
<weld::MessageDialog
> xWarn(Application::CreateMessageDialog(GetFrameWeld(),
805 VclMessageType::Warning
, VclButtonsType::Ok
,
806 SchResId(STR_INVALID_NUMBER
)));
810 bool DataBrowser::ShowQueryBox()
812 std::unique_ptr
<weld::MessageDialog
> xQueryBox(Application::CreateMessageDialog(GetFrameWeld(),
813 VclMessageType::Question
, VclButtonsType::YesNo
,
814 SchResId(STR_DATA_EDITOR_INCORRECT_INPUT
)));
815 return xQueryBox
->run() == RET_YES
;
818 bool DataBrowser::IsDataValid() const
821 const sal_Int32 nCol
= lcl_getColumnInData( GetCurColumnId());
823 if( m_apDataBrowserModel
->getCellType( nCol
) == DataBrowserModel::NUMBER
)
825 sal_uInt32 nDummy
= 0;
827 OUString
aText( m_aNumberEditField
->GetText());
829 if( !aText
.isEmpty() &&
830 m_spNumberFormatterWrapper
.get() &&
831 m_spNumberFormatterWrapper
->getSvNumberFormatter() &&
832 ! m_spNumberFormatterWrapper
->getSvNumberFormatter()->IsNumberFormat(
833 aText
, nDummy
, fDummy
))
842 void DataBrowser::CellModified()
844 m_bDataValid
= IsDataValid();
845 m_aCursorMovedHdlLink
.Call( this );
848 void DataBrowser::SetDataFromModel(
849 const Reference
< chart2::XChartDocument
> & xChartDoc
,
850 const Reference
< uno::XComponentContext
> & xContext
)
852 m_xChartDoc
.set( xChartDoc
);
854 m_apDataBrowserModel
.reset( new DataBrowserModel( m_xChartDoc
, xContext
));
855 m_spNumberFormatterWrapper
.reset(
856 new NumberFormatterWrapper(
857 Reference
< util::XNumberFormatsSupplier
>( m_xChartDoc
, uno::UNO_QUERY
)));
859 m_aNumberEditField
->SetFormatter( m_spNumberFormatterWrapper
->getSvNumberFormatter() );
863 const sal_Int32 nColCnt
= m_apDataBrowserModel
->getColumnCount();
864 const sal_Int32 nRowCnt
= m_apDataBrowserModel
->getMaxRowCount();
865 if( nRowCnt
&& nColCnt
)
872 void DataBrowser::InsertColumn()
874 sal_Int32 nColIdx
= lcl_getColumnInDataOrHeader( GetCurColumnId(), m_aSeriesHeaders
);
877 m_apDataBrowserModel
.get())
879 // save changes made to edit-field
883 m_apDataBrowserModel
->insertDataSeries( nColIdx
);
888 void DataBrowser::InsertTextColumn()
890 sal_Int32 nColIdx
= lcl_getColumnInDataOrHeader( GetCurColumnId(), m_aSeriesHeaders
);
893 m_apDataBrowserModel
.get())
895 // save changes made to edit-field
899 m_apDataBrowserModel
->insertComplexCategoryLevel( nColIdx
);
904 void DataBrowser::RemoveColumn()
906 sal_Int32 nColIdx
= lcl_getColumnInDataOrHeader( GetCurColumnId(), m_aSeriesHeaders
);
909 m_apDataBrowserModel
.get())
911 // save changes made to edit-field
916 m_apDataBrowserModel
->removeDataSeriesOrComplexCategoryLevel( nColIdx
);
921 void DataBrowser::InsertRow()
923 sal_Int32 nRowIdx
= lcl_getRowInData( GetCurRow());
926 m_apDataBrowserModel
.get())
928 // save changes made to edit-field
932 m_apDataBrowserModel
->insertDataPointForAllSeries( nRowIdx
);
937 void DataBrowser::RemoveRow()
939 sal_Int32 nRowIdx
= lcl_getRowInData( GetCurRow());
942 m_apDataBrowserModel
.get())
944 // save changes made to edit-field
949 m_apDataBrowserModel
->removeDataPointForAllSeries( nRowIdx
);
954 void DataBrowser::MoveLeftColumn()
956 sal_Int32 nColIdx
= lcl_getColumnInDataOrHeader( GetCurColumnId(), m_aSeriesHeaders
);
959 m_apDataBrowserModel
.get())
961 // save changes made to edit-field
965 m_apDataBrowserModel
->swapDataSeries( nColIdx
- 1 );
967 // keep cursor in swapped column
968 if(( 0 < GetCurColumnId() ) && ( GetCurColumnId() <= ColCount() - 1 ))
970 Dispatch( BROWSER_CURSORLEFT
);
976 void DataBrowser::MoveRightColumn()
978 sal_Int32 nColIdx
= lcl_getColumnInDataOrHeader( GetCurColumnId(), m_aSeriesHeaders
);
981 m_apDataBrowserModel
.get())
983 // save changes made to edit-field
987 m_apDataBrowserModel
->swapDataSeries( nColIdx
);
989 // keep cursor in swapped column
990 if( GetCurColumnId() < ColCount() - 1 )
992 Dispatch( BROWSER_CURSORRIGHT
);
998 void DataBrowser::MoveUpRow()
1000 sal_Int32 nRowIdx
= lcl_getRowInData( GetCurRow());
1003 m_apDataBrowserModel
.get())
1005 // save changes made to edit-field
1009 m_apDataBrowserModel
->swapDataPointForAllSeries( nRowIdx
- 1 );
1011 // keep cursor in swapped row
1012 if(( 0 < GetCurRow() ) && ( GetCurRow() <= GetRowCount() - 1 ))
1014 Dispatch( BROWSER_CURSORUP
);
1020 void DataBrowser::MoveDownRow()
1022 sal_Int32 nRowIdx
= lcl_getRowInData( GetCurRow());
1025 m_apDataBrowserModel
.get())
1027 // save changes made to edit-field
1031 m_apDataBrowserModel
->swapDataPointForAllSeries( nRowIdx
);
1033 // keep cursor in swapped row
1034 if( GetCurRow() < GetRowCount() - 1 )
1036 Dispatch( BROWSER_CURSORDOWN
);
1042 void DataBrowser::SetCursorMovedHdl( const Link
<DataBrowser
*,void>& rLink
)
1044 m_aCursorMovedHdlLink
= rLink
;
1047 // implementations for ::svt::EditBrowseBox (pure virtual methods)
1048 void DataBrowser::PaintCell(
1049 OutputDevice
& rDev
, const tools::Rectangle
& rRect
, sal_uInt16 nColumnId
) const
1051 Point
aPos( rRect
.TopLeft());
1054 OUString aText
= GetCellText( m_nSeekRow
, nColumnId
);
1055 Size
TxtSize( GetDataWindow().GetTextWidth( aText
), GetDataWindow().GetTextHeight());
1058 if( aPos
.X() < rRect
.Right() || aPos
.X() + TxtSize
.Width() > rRect
.Right() ||
1059 aPos
.Y() < rRect
.Top() || aPos
.Y() + TxtSize
.Height() > rRect
.Bottom())
1060 rDev
.SetClipRegion(vcl::Region(rRect
));
1062 // allow for a disabled control ...
1063 bool bEnabled
= IsEnabled();
1064 Color aOriginalColor
= rDev
.GetTextColor();
1066 rDev
.SetTextColor( GetSettings().GetStyleSettings().GetDisableColor() );
1069 rDev
.DrawText( aPos
, aText
);
1071 // reset the color (if necessary)
1073 rDev
.SetTextColor( aOriginalColor
);
1075 if( rDev
.IsClipRegion())
1076 rDev
.SetClipRegion();
1079 bool DataBrowser::SeekRow( long nRow
)
1081 if( ! EditBrowseBox::SeekRow( nRow
))
1092 bool DataBrowser::IsTabAllowed( bool bForward
) const
1094 long nRow
= GetCurRow();
1095 long nCol
= GetCurColumnId();
1097 // column 0 is header-column
1098 long nBadCol
= bForward
1099 ? GetColumnCount() - 1
1101 long nBadRow
= bForward
1107 const_cast< DataBrowser
* >( this )->ShowWarningBox();
1111 return ( nRow
!= nBadRow
||
1115 ::svt::CellController
* DataBrowser::GetController( long /*nRow*/, sal_uInt16 nCol
)
1120 if( CellContainsNumbers( nCol
))
1122 m_aNumberEditField
->UseInputStringForFormatting();
1123 m_aNumberEditField
->SetFormatKey( GetNumberFormatKey( nCol
));
1124 return m_rNumberEditController
.get();
1127 return m_rTextEditController
.get();
1130 void DataBrowser::InitController(
1131 ::svt::CellControllerRef
& rController
, long nRow
, sal_uInt16 nCol
)
1133 if( rController
== m_rTextEditController
)
1135 OUString
aText( GetCellText( nRow
, nCol
) );
1136 m_aTextEditField
->SetText( aText
);
1137 m_aTextEditField
->SetSelection( Selection( 0, aText
.getLength() ));
1139 else if( rController
== m_rNumberEditController
)
1141 // treat invalid and empty text as Nan
1142 m_aNumberEditField
->EnableNotANumber( true );
1143 if( ::rtl::math::isNan( GetCellNumber( nRow
, nCol
)))
1144 m_aNumberEditField
->SetTextValue( OUString());
1146 m_aNumberEditField
->SetValue( GetCellNumber( nRow
, nCol
) );
1147 OUString
aText( m_aNumberEditField
->GetText());
1148 m_aNumberEditField
->SetSelection( Selection( 0, aText
.getLength()));
1152 OSL_FAIL( "Invalid Controller" );
1156 bool DataBrowser::CellContainsNumbers( sal_uInt16 nCol
) const
1158 if (!m_apDataBrowserModel
)
1160 return m_apDataBrowserModel
->getCellType( lcl_getColumnInData( nCol
)) == DataBrowserModel::NUMBER
;
1163 sal_uInt32
DataBrowser::GetNumberFormatKey( sal_uInt16 nCol
) const
1165 if (!m_apDataBrowserModel
)
1167 return m_apDataBrowserModel
->getNumberFormatKey( lcl_getColumnInData( nCol
) );
1170 bool DataBrowser::isDateTimeString( const OUString
& aInputString
, double& fOutDateTimeValue
)
1172 sal_uInt32 nNumberFormat
=0;
1173 SvNumberFormatter
* pSvNumberFormatter
= m_spNumberFormatterWrapper
.get() ? m_spNumberFormatterWrapper
->getSvNumberFormatter() : nullptr;
1174 if( !aInputString
.isEmpty() && pSvNumberFormatter
&& pSvNumberFormatter
->IsNumberFormat( aInputString
, nNumberFormat
, fOutDateTimeValue
) )
1176 SvNumFormatType nType
= pSvNumberFormatter
->GetType( nNumberFormat
);
1177 return (nType
& SvNumFormatType::DATE
) || (nType
& SvNumFormatType::TIME
);
1182 bool DataBrowser::SaveModified()
1184 if( ! IsModified() )
1187 bool bChangeValid
= true;
1189 const sal_Int32 nRow
= lcl_getRowInData( GetCurRow());
1190 const sal_Int32 nCol
= lcl_getColumnInData( GetCurColumnId());
1192 OSL_ENSURE( nRow
>= 0 || nCol
>= 0, "This cell should not be modified!" );
1194 SvNumberFormatter
* pSvNumberFormatter
= m_spNumberFormatterWrapper
.get() ? m_spNumberFormatterWrapper
->getSvNumberFormatter() : nullptr;
1195 switch( m_apDataBrowserModel
->getCellType( nCol
))
1197 case DataBrowserModel::NUMBER
:
1199 sal_uInt32 nDummy
= 0;
1200 double fDummy
= 0.0;
1201 OUString
aText( m_aNumberEditField
->GetText());
1202 // an empty string is valid, if no numberformatter exists, all
1203 // values are treated as valid
1204 if( !aText
.isEmpty() && pSvNumberFormatter
&&
1205 ! pSvNumberFormatter
->IsNumberFormat( aText
, nDummy
, fDummy
) )
1207 bChangeValid
= false;
1211 double fData
= m_aNumberEditField
->GetValue();
1212 bChangeValid
= m_apDataBrowserModel
->setCellNumber( nCol
, nRow
, fData
);
1216 case DataBrowserModel::TEXTORDATE
:
1218 OUString
aText( m_aTextEditField
->GetText() );
1219 double fValue
= 0.0;
1220 bChangeValid
= false;
1221 if( isDateTimeString( aText
, fValue
) )
1222 bChangeValid
= m_apDataBrowserModel
->setCellAny( nCol
, nRow
, uno::Any( fValue
) );
1224 bChangeValid
= m_apDataBrowserModel
->setCellAny( nCol
, nRow
, uno::Any( aText
) );
1227 case DataBrowserModel::TEXT
:
1229 OUString
aText( m_aTextEditField
->GetText());
1230 bChangeValid
= m_apDataBrowserModel
->setCellText( nCol
, nRow
, aText
);
1235 // the first valid change changes this to true
1238 RowModified( GetCurRow(), GetCurColumnId());
1239 ::svt::CellController
* pCtrl
= GetController( GetCurRow(), GetCurColumnId());
1241 pCtrl
->ClearModified();
1244 return bChangeValid
;
1247 bool DataBrowser::EndEditing()
1251 // apply changes made to series headers
1252 for( const auto& spHeader
: m_aSeriesHeaders
)
1253 spHeader
->applyChanges();
1258 return ShowQueryBox();
1261 void DataBrowser::ColumnResized( sal_uInt16 nColId
)
1263 bool bLastUpdateMode
= GetUpdateMode();
1264 SetUpdateMode( false );
1266 EditBrowseBox::ColumnResized( nColId
);
1267 ImplAdjustHeaderControls();
1268 SetUpdateMode( bLastUpdateMode
);
1271 void DataBrowser::EndScroll()
1273 bool bLastUpdateMode
= GetUpdateMode();
1274 SetUpdateMode( false );
1276 EditBrowseBox::EndScroll();
1277 RenewSeriesHeaders();
1279 SetUpdateMode( bLastUpdateMode
);
1282 void DataBrowser::RenewSeriesHeaders()
1285 DataBrowserModel::tDataHeaderVector
aHeaders( m_apDataBrowserModel
->getDataHeaders());
1286 Link
<impl::SeriesHeaderEdit
&,void> aFocusLink( LINK( this, DataBrowser
, SeriesHeaderGotFocus
));
1287 Link
<impl::SeriesHeaderEdit
&,void> aSeriesHeaderChangedLink( LINK( this, DataBrowser
, SeriesHeaderChanged
));
1289 for (auto const& elemHeader
: aHeaders
)
1291 std::shared_ptr
< impl::SeriesHeader
> spHeader( new impl::SeriesHeader( m_pColumnsWin
, m_pColorsWin
));
1292 Reference
< beans::XPropertySet
> xSeriesProp(elemHeader
.m_xDataSeries
, uno::UNO_QUERY
);
1293 sal_Int32 nColor
= 0;
1294 if( xSeriesProp
.is() &&
1295 ( xSeriesProp
->getPropertyValue( "Color" ) >>= nColor
))
1296 spHeader
->SetColor( Color( nColor
));
1297 spHeader
->SetChartType( elemHeader
.m_xChartType
, elemHeader
.m_bSwapXAndYAxis
);
1298 spHeader
->SetSeriesName(
1299 DataSeriesHelper::getDataSeriesLabel(
1300 elemHeader
.m_xDataSeries
,
1301 (elemHeader
.m_xChartType
.is() ?
1302 elemHeader
.m_xChartType
->getRoleOfSequenceForSeriesLabel() :
1303 OUString( "values-y"))));
1304 spHeader
->SetRange( elemHeader
.m_nStartColumn
+ 1, elemHeader
.m_nEndColumn
+ 1 );
1305 spHeader
->SetGetFocusHdl( aFocusLink
);
1306 spHeader
->SetEditChangedHdl( aSeriesHeaderChangedLink
);
1307 m_aSeriesHeaders
.push_back( spHeader
);
1310 ImplAdjustHeaderControls();
1313 void DataBrowser::ImplAdjustHeaderControls()
1315 sal_uInt16 nColCount
= GetColumnCount();
1316 sal_uInt32 nCurrentPos
= GetPosPixel().getX();
1317 sal_uInt32 nMaxPos
= nCurrentPos
+ GetOutputSizePixel().getWidth();
1318 sal_uInt32 nStartPos
= nCurrentPos
;
1320 // width of header column
1321 nCurrentPos
+= GetColumnWidth( 0 );
1323 weld::Container
* pWin
= m_pColumnsWin
;
1324 weld::Container
* pColorWin
= m_pColorsWin
;
1325 pWin
->set_margin_left(nCurrentPos
);
1326 pColorWin
->set_margin_left(nCurrentPos
);
1328 tSeriesHeaderContainer::iterator
aIt( m_aSeriesHeaders
.begin());
1329 sal_uInt16 i
= GetFirstVisibleColNumber();
1330 while( (aIt
!= m_aSeriesHeaders
.end()) && ((*aIt
)->GetStartColumn() < i
) )
1335 for( ; i
< nColCount
&& aIt
!= m_aSeriesHeaders
.end(); ++i
)
1337 if( (*aIt
)->GetStartColumn() == i
)
1338 nStartPos
= nCurrentPos
;
1340 nCurrentPos
+= (GetColumnWidth( i
));
1342 if( (*aIt
)->GetEndColumn() == i
)
1344 if( nStartPos
< nMaxPos
)
1346 (*aIt
)->SetPixelWidth( nCurrentPos
- nStartPos
- 3 );
1351 pWin
->set_margin_left(nStartPos
);
1352 pColorWin
->set_margin_left(nStartPos
);
1353 pWin
= pColorWin
= nullptr;
1364 IMPL_LINK( DataBrowser
, SeriesHeaderGotFocus
, impl::SeriesHeaderEdit
&, rEdit
, void )
1366 rEdit
.SetShowWarningBox( !m_bDataValid
);
1372 MakeFieldVisible( GetCurRow(), static_cast< sal_uInt16
>( rEdit
.getStartColumn()) );
1374 m_aCursorMovedHdlLink
.Call( this );
1378 IMPL_LINK( DataBrowser
, SeriesHeaderChanged
, impl::SeriesHeaderEdit
&, rEdit
, void )
1380 Reference
< chart2::XDataSeries
> xSeries(
1381 m_apDataBrowserModel
->getDataSeriesByColumn( rEdit
.getStartColumn() - 1 ));
1382 Reference
< chart2::data::XDataSource
> xSource( xSeries
, uno::UNO_QUERY
);
1385 Reference
< chart2::XChartType
> xChartType(
1386 m_apDataBrowserModel
->getHeaderForSeries( xSeries
).m_xChartType
);
1387 if( xChartType
.is())
1389 Reference
< chart2::data::XLabeledDataSequence
> xLabeledSeq(
1390 DataSeriesHelper::getDataSequenceByRole( xSource
, xChartType
->getRoleOfSequenceForSeriesLabel()));
1391 if( xLabeledSeq
.is())
1393 Reference
< container::XIndexReplace
> xIndexReplace( xLabeledSeq
->getLabel(), uno::UNO_QUERY
);
1394 if( xIndexReplace
.is())
1395 xIndexReplace
->replaceByIndex(
1396 0, uno::Any( rEdit
.GetText()));
1402 } // namespace chart
1404 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */