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