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