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