xref: /AOO41X/main/chart2/source/controller/dialogs/tp_DataSource.cxx (revision 0deba7fbda3d9908785c25a443701a293b6f4e71)
1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_chart2.hxx"
26 
27 #include "tp_DataSource.hxx"
28 #include "tp_DataSource.hrc"
29 #include "Strings.hrc"
30 #include "ResId.hxx"
31 #include "chartview/ChartSfxItemIds.hxx"
32 #include "macros.hxx"
33 #include "ChartTypeTemplateProvider.hxx"
34 #include "RangeSelectionHelper.hxx"
35 #include "DataSeriesHelper.hxx"
36 #include "tp_DataSourceControls.hxx"
37 #include "ControllerLockGuard.hxx"
38 #include "DataSourceHelper.hxx"
39 #include <com/sun/star/sheet/XRangeSelection.hpp>
40 #include <com/sun/star/table/XCellRange.hpp>
41 #include <com/sun/star/chart2/XChartType.hpp>
42 #include <com/sun/star/chart2/XChartTypeTemplate.hpp>
43 #include <com/sun/star/util/XModifiable.hpp>
44 #include <com/sun/star/chart2/data/XDataSink.hpp>
45 
46 // for RET_OK
47 #include <vcl/msgbox.hxx>
48 #include <rtl/ustrbuf.hxx>
49 
50 #include <functional>
51 #include <algorithm>
52 #include <map>
53 
54 using namespace ::com::sun::star;
55 using namespace ::com::sun::star::chart2;
56 
57 using ::com::sun::star::uno::Reference;
58 using ::com::sun::star::uno::Sequence;
59 using ::rtl::OUString;
60 using ::rtl::OUStringBuffer;
61 
62 // --------------------------------------------------------------------------------
63 
64 namespace
65 {
66 
67 const OUString lcl_aLabelRole( RTL_CONSTASCII_USTRINGPARAM( "label" ));
68 
69 String lcl_GetRoleLBEntry(
70     const OUString & rRole, const OUString & rRange )
71 {
72 //IAccessibility2 Implementation 2009-----
73     //String aEntry( rRole );
74     //aEntry += '\t';
75     //aEntry += String(
76     //    ::chart::DialogModel::ConvertRoleFromInternalToUI( rRole ));
77     String aEntry(::chart::DialogModel::ConvertRoleFromInternalToUI( rRole ));
78 	//-----IAccessibility2 Implementation 2009
79     aEntry += '\t';
80     aEntry += String( rRange );
81 
82     return aEntry;
83 }
84 
85 void lcl_UpdateCurrentRange(
86     SvTabListBox & rOutListBox,
87     const OUString & rRole, const OUString & rRange )
88 {
89     SvLBoxEntry * pEntry = rOutListBox.FirstSelected();
90     if( pEntry )
91         rOutListBox.SetEntryText( lcl_GetRoleLBEntry( rRole, rRange ), pEntry );
92 }
93 
94 bool lcl_UpdateCurrentSeriesName(
95     SvTreeListBox & rOutListBox )
96 {
97     bool bResult = false;
98     ::chart::SeriesEntry * pEntry = dynamic_cast< ::chart::SeriesEntry * >( rOutListBox.FirstSelected());
99     if( pEntry &&
100         pEntry->m_xDataSeries.is() &&
101         pEntry->m_xChartType.is())
102     {
103         String aLabel( ::chart::DataSeriesHelper::getDataSeriesLabel(
104                            pEntry->m_xDataSeries,
105                            pEntry->m_xChartType->getRoleOfSequenceForSeriesLabel()));
106         if( aLabel.Len())
107         {
108             rOutListBox.SetEntryText( pEntry, aLabel );
109             bResult = true;
110         }
111     }
112     return bResult;
113 }
114 
115 OUString lcl_GetSelectedRole( const SvTabListBox & rRoleListBox, bool bUITranslated = false )
116 {
117     OUString aResult;
118     SvLBoxEntry * pEntry = rRoleListBox.FirstSelected();
119     if( pEntry )
120         aResult = OUString( rRoleListBox.GetEntryText( pEntry,
121                                                        bUITranslated ? 1 : 0 ));
122     return aResult;
123 }
124 
125 OUString lcl_GetSelectedRolesRange( const SvTabListBox & rRoleListBox )
126 {
127     OUString aResult;
128     SvLBoxEntry * pEntry = rRoleListBox.FirstSelected();
129     if( pEntry )
130         aResult = OUString( rRoleListBox.GetEntryText( pEntry, 2 ));
131     return aResult;
132 }
133 
134 OUString lcl_GetSequenceNameForLabel( ::chart::SeriesEntry * pEntry )
135 {
136     OUString aResult( RTL_CONSTASCII_USTRINGPARAM("values-y"));
137     if( pEntry &&
138         pEntry->m_xChartType.is())
139     {
140         aResult = pEntry->m_xChartType->getRoleOfSequenceForSeriesLabel();
141     }
142     return aResult;
143 }
144 
145 //IAccessibility2 Implementation 2009-----
146 static long lcl_pRoleListBoxTabs[] =
147 	{	2,        // Number of Tabs
148 		0, 75
149 	};
150 //-----IAccessibility2 Implementation 2009
151 
152 void lcl_ShowChooserButton(
153     ::chart::RangeSelectionButton & rChooserButton,
154     Edit & rEditField,
155     sal_Bool bShow )
156 {
157     if( rChooserButton.IsVisible() != bShow )
158     {
159         rChooserButton.Show( bShow );
160         sal_Int32 nWidhtDiff = 12 + 4;
161         if( bShow )
162             nWidhtDiff = -nWidhtDiff;
163         Size aSize = rChooserButton.PixelToLogic( rEditField.GetSizePixel(), MAP_APPFONT );
164         aSize.setWidth( aSize.getWidth() + nWidhtDiff );
165         rEditField.SetSizePixel( rChooserButton.LogicToPixel( aSize, MAP_APPFONT ));
166     }
167 }
168 
169 void lcl_enableRangeChoosing( bool bEnable, Dialog * pDialog )
170 {
171     if( pDialog )
172     {
173         pDialog->Show( bEnable ? sal_False : sal_True );
174         pDialog->SetModalInputMode( bEnable ? sal_False : sal_True );
175     }
176 }
177 
178 void lcl_addLSequenceToDataSource(
179     const Reference< chart2::data::XLabeledDataSequence > & xLSequence,
180     const Reference< chart2::data::XDataSource > & xSource )
181 {
182     Reference< data::XDataSink > xSink( xSource, uno::UNO_QUERY );
183     if( xSink.is())
184     {
185         Sequence< Reference< chart2::data::XLabeledDataSequence > > aData( xSource->getDataSequences());
186         aData.realloc( aData.getLength() + 1 );
187         aData[ aData.getLength() - 1 ] = xLSequence;
188         xSink->setData( aData );
189     }
190 }
191 
192 Reference< chart2::data::XLabeledDataSequence > lcl_findLSequenceWithOnlyLabel(
193     const Reference< chart2::data::XDataSource > & xDataSource )
194 {
195     Reference< chart2::data::XLabeledDataSequence > xResult;
196     Sequence< Reference< chart2::data::XLabeledDataSequence > > aSequences( xDataSource->getDataSequences());
197 
198     for( sal_Int32 i=0; i<aSequences.getLength(); ++i )
199     {
200         // no values are set but a label exists
201         if( ! aSequences[i]->getValues().is() &&
202             aSequences[i]->getLabel().is())
203         {
204             xResult.set( aSequences[i] );
205             break;
206         }
207     }
208 
209     return xResult;
210 }
211 
212 void lcl_shiftControlY( Control & rControl, long nYOffset )
213 {
214     Point aPos( rControl.GetPosPixel());
215     aPos.setY( aPos.getY() + nYOffset );
216     rControl.SetPosPixel( aPos );
217 }
218 
219 void lcl_increaseHeightOfControl( Control & rControl, long nYOffset )
220 {
221     Size aSize( rControl.GetSizePixel());
222     aSize.setHeight( aSize.getHeight () + nYOffset );
223     rControl.SetSizePixel( aSize );
224 }
225 
226 } //  anonymous namespace
227 
228 // --------------------------------------------------------------------------------
229 
230 namespace chart
231 {
232 
233 DataSourceTabPage::DataSourceTabPage(
234     Window * pParent,
235     DialogModel & rDialogModel,
236     ChartTypeTemplateProvider* pTemplateProvider,
237     Dialog * pParentDialog,
238     bool bHideDescription /* = false */ ) :
239         ::svt::OWizardPage( pParent, SchResId( TP_DATA_SOURCE )),
240 
241     m_aFT_CAPTION     ( this, SchResId( FT_CAPTION_FOR_WIZARD )),
242     m_aFT_SERIES      ( this, SchResId( FT_SERIES      )),
243     m_apLB_SERIES( new SeriesListBox( this, SchResId( LB_SERIES ))),
244     m_aBTN_ADD        ( this, SchResId( BTN_ADD        )),
245     m_aBTN_REMOVE     ( this, SchResId( BTN_REMOVE     )),
246     m_aBTN_UP         ( this, SchResId( BTN_UP         )),
247     m_aBTN_DOWN       ( this, SchResId( BTN_DOWN       )),
248     m_aFT_ROLE        ( this, SchResId( FT_ROLE        )),
249     m_aLB_ROLE        ( this, SchResId( LB_ROLE        )),
250     m_aFT_RANGE       ( this, SchResId( FT_RANGE       )),
251     m_aEDT_RANGE      ( this, SchResId( EDT_RANGE      )),
252     m_aIMB_RANGE_MAIN ( this, SchResId( IMB_RANGE_MAIN )),
253     m_aFT_CATEGORIES  ( this, SchResId( FT_CATEGORIES  )),
254     m_aFT_DATALABELS  ( this, SchResId( FT_DATALABELS  )),
255     m_aEDT_CATEGORIES ( this, SchResId( EDT_CATEGORIES )),
256     m_aIMB_RANGE_CAT  ( this, SchResId( IMB_RANGE_CAT  )),
257 
258     m_pTemplateProvider( pTemplateProvider ),
259     m_rDialogModel( rDialogModel ),
260 
261     m_pCurrentRangeChoosingField( 0 ),
262     m_bIsDirty( false ),
263     m_pParentDialog( pParentDialog ),
264     m_pTabPageNotifiable( dynamic_cast< TabPageNotifiable * >( pParentDialog ))
265 {
266     FreeResource();
267 
268     if( bHideDescription )
269     {
270         // note: the offset should be a negative value for shifting upwards, the
271         // 4 is for the offset difference between a wizard page and a tab-page
272         long nYOffset = - ( m_aFT_SERIES.GetPosPixel().getY() - m_aFT_CAPTION.GetPosPixel().getY() + 4 );
273         long nUpShift = - 2;
274         long nYResize = - (nYOffset - nUpShift);
275         m_aFT_CAPTION.Hide();
276 
277         // shift list boxes and enlarge them by the space saved by hiding the caption
278         lcl_shiftControlY( m_aFT_SERIES, nYOffset );
279         lcl_shiftControlY( *(m_apLB_SERIES.get()), nYOffset );
280         lcl_increaseHeightOfControl( *(m_apLB_SERIES.get()), nYResize );
281 
282         lcl_shiftControlY( m_aFT_ROLE, nYOffset );
283         lcl_shiftControlY( m_aLB_ROLE, nYOffset );
284         lcl_increaseHeightOfControl( m_aLB_ROLE, nYResize );
285 
286         lcl_shiftControlY( m_aBTN_ADD, nUpShift );
287         lcl_shiftControlY( m_aBTN_REMOVE, nUpShift );
288         lcl_shiftControlY( m_aBTN_UP, nUpShift );
289         lcl_shiftControlY( m_aBTN_DOWN, nUpShift );
290         lcl_shiftControlY( m_aFT_RANGE, nUpShift );
291         lcl_shiftControlY( m_aEDT_RANGE, nUpShift );
292         lcl_shiftControlY( m_aIMB_RANGE_MAIN, nUpShift );
293         lcl_shiftControlY( m_aFT_CATEGORIES, nUpShift );
294         lcl_shiftControlY( m_aFT_DATALABELS, nUpShift );
295         lcl_shiftControlY( m_aEDT_CATEGORIES, nUpShift );
296         lcl_shiftControlY( m_aIMB_RANGE_CAT, nUpShift );
297     }
298     else
299     {
300         // make font of caption bold
301         Font aFont( m_aFT_CAPTION.GetControlFont() );
302         aFont.SetWeight( WEIGHT_BOLD );
303         m_aFT_CAPTION.SetControlFont( aFont );
304 
305         // no mnemonic
306         m_aFT_CAPTION.SetStyle( m_aFT_CAPTION.GetStyle() | WB_NOLABEL );
307     }
308 
309     m_aFixedTextRange = OUString( m_aFT_RANGE.GetText() );
310     this->SetText( String( SchResId( STR_OBJECT_DATASERIES_PLURAL ) ) );
311 
312     // set handlers
313     m_apLB_SERIES->SetSelectHdl( LINK( this, DataSourceTabPage, SeriesSelectionChangedHdl ));
314 
315 	m_aLB_ROLE.SetStyle( m_aLB_ROLE.GetStyle() | WB_HSCROLL | WB_CLIPCHILDREN );
316     m_aLB_ROLE.SetSelectionMode( SINGLE_SELECTION );
317     m_aLB_ROLE.SetSelectHdl( LINK( this, DataSourceTabPage, RoleSelectionChangedHdl ));
318 
319     m_aEDT_RANGE.SetKeyInputHdl( LINK( this, DataSourceTabPage, MainRangeButtonClickedHdl ));
320     m_aEDT_CATEGORIES.SetKeyInputHdl( LINK( this, DataSourceTabPage, CategoriesRangeButtonClickedHdl ));
321 
322     m_aIMB_RANGE_MAIN.SetClickHdl( LINK( this, DataSourceTabPage, MainRangeButtonClickedHdl ));
323     m_aIMB_RANGE_CAT.SetClickHdl( LINK( this, DataSourceTabPage, CategoriesRangeButtonClickedHdl ));
324 
325     m_aBTN_ADD.SetClickHdl( LINK( this, DataSourceTabPage, AddButtonClickedHdl ));
326     m_aBTN_REMOVE.SetClickHdl( LINK( this, DataSourceTabPage, RemoveButtonClickedHdl ));
327 
328     m_aBTN_UP.SetClickHdl( LINK( this, DataSourceTabPage, UpButtonClickedHdl ));
329     m_aBTN_DOWN.SetClickHdl( LINK( this, DataSourceTabPage, DownButtonClickedHdl ));
330 
331     m_aEDT_RANGE.SetModifyHdl( LINK( this, DataSourceTabPage, RangeModifiedHdl ));
332     m_aEDT_CATEGORIES.SetModifyHdl( LINK( this, DataSourceTabPage, RangeModifiedHdl ));
333     m_aEDT_RANGE.SetUpdateDataHdl( LINK( this, DataSourceTabPage, RangeUpdateDataHdl ));
334     m_aEDT_CATEGORIES.SetUpdateDataHdl( LINK( this, DataSourceTabPage, RangeUpdateDataHdl ));
335 
336     // #i75179# enable setting the background to a different color
337     m_aEDT_RANGE.SetStyle( m_aEDT_RANGE.GetStyle() | WB_FORCECTRLBACKGROUND );
338     m_aEDT_CATEGORIES.SetStyle( m_aEDT_CATEGORIES.GetStyle() | WB_FORCECTRLBACKGROUND );
339 
340     // set symbol font for arrows
341     // note: StarSymbol is substituted to OpenSymbol for OOo
342     Font aSymbolFont( m_aBTN_UP.GetFont());
343     aSymbolFont.SetName( String( RTL_CONSTASCII_USTRINGPARAM( "StarSymbol" )));
344     m_aBTN_UP.SetControlFont( aSymbolFont );
345     m_aBTN_DOWN.SetControlFont( aSymbolFont );
346 
347     // set button text
348     sal_Unicode cBlackUpPointingTriangle( 0x25b2 );
349     sal_Unicode cBlackDownPointingTriangle( 0x25bc );
350     m_aBTN_UP.SetText( String( cBlackUpPointingTriangle ));
351     m_aBTN_DOWN.SetText( String( cBlackDownPointingTriangle ));
352 
353     // init controls
354     m_aLB_ROLE.SetTabs( lcl_pRoleListBoxTabs, MAP_APPFONT );
355     m_aLB_ROLE.Show();
356 
357     updateControlsFromDialogModel();
358 
359     // select first series
360     if( m_apLB_SERIES->First())
361         m_apLB_SERIES->Select( m_apLB_SERIES->First());
362     m_apLB_SERIES->GrabFocus();
363 	m_aBTN_UP.SetAccessibleName(String(SchResId(STR_BUTTON_UP)));
364 	m_aBTN_DOWN.SetAccessibleName(String(SchResId(STR_BUTTON_DOWN)));
365 }
366 
367 DataSourceTabPage::~DataSourceTabPage()
368 {}
369 
370 void DataSourceTabPage::ActivatePage()
371 {
372     OWizardPage::ActivatePage();
373     updateControlsFromDialogModel();
374 }
375 
376 void DataSourceTabPage::initializePage()
377 {
378 }
379 
380 void DataSourceTabPage::DeactivatePage()
381 {
382     commitPage();
383     svt::OWizardPage::DeactivatePage();
384 }
385 
386 void DataSourceTabPage::commitPage()
387 {
388     commitPage(::svt::WizardTypes::eFinish);
389 }
390 
391 sal_Bool DataSourceTabPage::commitPage( ::svt::WizardTypes::CommitPageReason /*eReason*/ )
392 {
393     //ranges may have been edited in the meanwhile (dirty is true in that case here)
394     if( isValid() )
395     {
396         updateModelFromControl( 0 /*update all*/ );
397         return sal_True;//return false if this page should not be left
398     }
399     else
400         return sal_False;
401 }
402 
403 bool DataSourceTabPage::isRangeFieldContentValid( Edit & rEdit )
404 {
405     OUString aRange( rEdit.GetText());
406     bool bIsValid = ( aRange.getLength() == 0 ) ||
407         m_rDialogModel.getRangeSelectionHelper()->verifyCellRange( aRange );
408 
409     if( bIsValid )
410     {
411         rEdit.SetControlForeground();
412         rEdit.SetControlBackground();
413     }
414     else
415     {
416         rEdit.SetControlBackground( RANGE_SELECTION_INVALID_RANGE_BACKGROUND_COLOR );
417         rEdit.SetControlForeground( RANGE_SELECTION_INVALID_RANGE_FOREGROUND_COLOR );
418     }
419 
420     return bIsValid;
421 }
422 
423 bool DataSourceTabPage::isValid()
424 {
425     bool bRoleRangeValid = true;
426     bool bCategoriesRangeValid = true;
427     bool bHasSelectedEntry = (m_apLB_SERIES->FirstSelected() != 0);
428 
429     if( bHasSelectedEntry )
430         bRoleRangeValid = isRangeFieldContentValid( m_aEDT_RANGE );
431     if( m_aEDT_CATEGORIES.IsEnabled() )
432         bCategoriesRangeValid = isRangeFieldContentValid( m_aEDT_CATEGORIES );
433     bool bValid = ( bRoleRangeValid && bCategoriesRangeValid );
434 
435     if( m_pTabPageNotifiable )
436     {
437         if( bValid )
438             m_pTabPageNotifiable->setValidPage( this );
439         else
440             m_pTabPageNotifiable->setInvalidPage( this );
441     }
442 
443     return bValid;
444 }
445 
446 void DataSourceTabPage::setDirty()
447 {
448     m_bIsDirty = true;
449 }
450 
451 void DataSourceTabPage::updateControlsFromDialogModel()
452 {
453     // series
454     fillSeriesListBox();
455     SeriesSelectionChangedHdl( 0 );
456 
457     // categories
458     m_aEDT_CATEGORIES.SetText( String( m_rDialogModel.getCategoriesRange() ));
459 
460     updateControlState();
461 }
462 
463 void DataSourceTabPage::fillSeriesListBox()
464 {
465     m_apLB_SERIES->SetUpdateMode( sal_False );
466 
467     Reference< XDataSeries > xSelected;
468     SeriesEntry * pEntry = dynamic_cast< SeriesEntry * >( m_apLB_SERIES->FirstSelected());
469     if( pEntry )
470         xSelected.set( pEntry->m_xDataSeries );
471 
472     bool bHasSelectedEntry = (pEntry != 0);
473     SvLBoxEntry * pSelectedEntry = 0;
474     m_apLB_SERIES->Clear();
475 
476     ::std::vector< DialogModel::tSeriesWithChartTypeByName > aSeries(
477         m_rDialogModel.getAllDataSeriesWithLabel() );
478 
479     sal_Int32 nUnnamedSeriesIndex = 1;
480     for( ::std::vector< DialogModel::tSeriesWithChartTypeByName >::const_iterator aIt = aSeries.begin();
481          aIt != aSeries.end(); ++aIt )
482     {
483         String aLabel( (*aIt).first );
484         if( !aLabel.Len())
485         {
486             if( nUnnamedSeriesIndex > 1 )
487             {
488                 OUString aResString( String( ::chart::SchResId( STR_DATA_UNNAMED_SERIES_WITH_INDEX )));
489 
490                 // replace index of unnamed series
491                 const OUString aReplacementStr( RTL_CONSTASCII_USTRINGPARAM( "%NUMBER" ));
492                 sal_Int32 nIndex = aResString.indexOf( aReplacementStr );
493                 if( nIndex != -1 )
494                     aLabel = String( aResString.replaceAt(
495                                          nIndex, aReplacementStr.getLength(),
496                                          String::CreateFromInt32( nUnnamedSeriesIndex )));
497             }
498             if( aLabel.Len() == 0 )
499                 aLabel = String( ::chart::SchResId( STR_DATA_UNNAMED_SERIES ));
500 
501             ++nUnnamedSeriesIndex;
502         }
503         pEntry = dynamic_cast< SeriesEntry * >(
504             m_apLB_SERIES->InsertEntry( aLabel ));
505         if( pEntry )
506         {
507             pEntry->m_xDataSeries.set( (*aIt).second.first );
508             pEntry->m_xChartType.set(  (*aIt).second.second );
509             if( bHasSelectedEntry && ((*aIt).second.first == xSelected))
510                 pSelectedEntry = pEntry;
511         }
512     }
513 
514     if( bHasSelectedEntry && pSelectedEntry )
515         m_apLB_SERIES->Select( pSelectedEntry );
516 
517     m_apLB_SERIES->SetUpdateMode( sal_True );
518 }
519 
520 void DataSourceTabPage::fillRoleListBox()
521 {
522     SeriesEntry * pSeriesEntry = dynamic_cast< SeriesEntry * >( m_apLB_SERIES->FirstSelected());
523     bool bHasSelectedEntry = (pSeriesEntry != 0);
524 
525     SvLBoxEntry * pRoleEntry =  m_aLB_ROLE.FirstSelected();
526     sal_uLong nRoleIndex = SAL_MAX_UINT32;
527     if( pRoleEntry )
528         nRoleIndex = m_aLB_ROLE.GetModel()->GetAbsPos( pRoleEntry );
529 
530     if( bHasSelectedEntry )
531     {
532         DialogModel::tRolesWithRanges aRoles(
533             m_rDialogModel.getRolesWithRanges(
534                 pSeriesEntry->m_xDataSeries,
535                 lcl_GetSequenceNameForLabel( pSeriesEntry ),
536                 pSeriesEntry->m_xChartType ));
537 
538         // fill role list
539         m_aLB_ROLE.SetUpdateMode( sal_False );
540         m_aLB_ROLE.Clear();
541         m_aLB_ROLE.RemoveSelection();
542 
543         for( DialogModel::tRolesWithRanges::const_iterator aIt( aRoles.begin());
544              aIt != aRoles.end(); ++ aIt )
545         {
546             m_aLB_ROLE.InsertEntry( lcl_GetRoleLBEntry( aIt->first, aIt->second ));
547         }
548 
549         // series may contain no roles, check listbox size before selecting entries
550         if( m_aLB_ROLE.GetEntryCount() > 0 )
551         {
552             if( nRoleIndex >= m_aLB_ROLE.GetEntryCount())
553                 nRoleIndex = 0;
554             m_aLB_ROLE.Select( m_aLB_ROLE.GetEntry( nRoleIndex ));
555         }
556 
557         m_aLB_ROLE.SetUpdateMode( sal_True );
558     }
559 }
560 
561 void DataSourceTabPage::updateControlState()
562 {
563     SvLBoxEntry * pSeriesEntry = m_apLB_SERIES->FirstSelected();
564     bool bHasSelectedSeries = (pSeriesEntry != 0);
565     bool bHasValidRole = false;
566     bool bHasRangeChooser = m_rDialogModel.getRangeSelectionHelper()->hasRangeSelection();
567 
568     if( bHasSelectedSeries )
569     {
570         SvLBoxEntry * pRoleEntry =  m_aLB_ROLE.FirstSelected();
571         bHasValidRole = (pRoleEntry != 0);
572     }
573 
574     m_aBTN_ADD.Enable( true );
575     m_aBTN_REMOVE.Enable( bHasSelectedSeries );
576 
577     m_aBTN_UP.Enable( bHasSelectedSeries && (pSeriesEntry != m_apLB_SERIES->First()));
578     m_aBTN_DOWN.Enable( bHasSelectedSeries && (pSeriesEntry != m_apLB_SERIES->Last()));
579 
580     bool bHasCategories = m_rDialogModel.isCategoryDiagram();
581 
582     m_aFT_DATALABELS.Show(!bHasCategories);
583     m_aFT_CATEGORIES.Show( bHasCategories);
584     sal_Bool bShowIB = bHasRangeChooser;
585     lcl_ShowChooserButton( m_aIMB_RANGE_CAT, m_aEDT_CATEGORIES, bShowIB );
586 
587     m_aFT_SERIES.Enable();
588     m_apLB_SERIES->Enable();
589 
590     m_aFT_ROLE.Enable( bHasSelectedSeries );
591     m_aLB_ROLE.Enable( bHasSelectedSeries );
592 
593     m_aFT_RANGE.Enable( bHasValidRole );
594     m_aEDT_RANGE.Enable( bHasValidRole );
595     lcl_ShowChooserButton( m_aIMB_RANGE_MAIN, m_aEDT_RANGE, bShowIB );
596     isValid();
597 }
598 
599 IMPL_LINK( DataSourceTabPage, SeriesSelectionChangedHdl, void *, EMPTYARG )
600 {
601     m_rDialogModel.startControllerLockTimer();
602     if( m_apLB_SERIES->FirstSelected())
603     {
604         fillRoleListBox();
605         RoleSelectionChangedHdl( 0 );
606     }
607     updateControlState();
608 
609     return 0;
610 }
611 
612 IMPL_LINK( DataSourceTabPage, RoleSelectionChangedHdl, void *, EMPTYARG )
613 {
614     m_rDialogModel.startControllerLockTimer();
615     SvLBoxEntry * pEntry = m_aLB_ROLE.FirstSelected();
616     if( pEntry )
617     {
618         OUString aRange( m_aEDT_RANGE.GetText());
619         OUString aSelectedRoleUI = lcl_GetSelectedRole( m_aLB_ROLE, true );
620         OUString aSelectedRange = lcl_GetSelectedRolesRange( m_aLB_ROLE );
621 
622         // replace role in fixed text label
623         const OUString aReplacementStr( RTL_CONSTASCII_USTRINGPARAM( "%VALUETYPE" ));
624         sal_Int32 nIndex = m_aFixedTextRange.indexOf( aReplacementStr );
625         if( nIndex != -1 )
626         {
627             m_aFT_RANGE.SetText(
628                 String( m_aFixedTextRange.replaceAt(
629                             nIndex, aReplacementStr.getLength(), aSelectedRoleUI )));
630         }
631 
632         m_aEDT_RANGE.SetText( String( aSelectedRange ));
633         isValid();
634     }
635 
636     return 0;
637 }
638 
639 IMPL_LINK( DataSourceTabPage, MainRangeButtonClickedHdl, void *, EMPTYARG )
640 {
641     OSL_ASSERT( m_pCurrentRangeChoosingField == 0 );
642     m_pCurrentRangeChoosingField = & m_aEDT_RANGE;
643     if( m_aEDT_RANGE.GetText().Len() > 0 &&
644         ! updateModelFromControl( m_pCurrentRangeChoosingField ))
645         return 0;
646 
647     SeriesEntry * pEntry = dynamic_cast< SeriesEntry * >( m_apLB_SERIES->FirstSelected());
648     bool bHasSelectedEntry = (pEntry != 0);
649 
650     OUString aSelectedRolesRange = lcl_GetSelectedRolesRange( m_aLB_ROLE );
651 
652     if( bHasSelectedEntry && (m_aLB_ROLE.FirstSelected() != 0))
653     {
654         String aStr( SchResId( STR_DATA_SELECT_RANGE_FOR_SERIES ));
655         OUString aUIStr( aStr );
656 
657         // replace role
658         OUString aReplacement( RTL_CONSTASCII_USTRINGPARAM( "%VALUETYPE" ));
659         sal_Int32 nIndex = aUIStr.indexOf( aReplacement );
660         if( nIndex != -1 )
661         {
662             aUIStr = aUIStr.replaceAt( nIndex, aReplacement.getLength(),
663                                        lcl_GetSelectedRole( m_aLB_ROLE, true ));
664         }
665         // replace series name
666         aReplacement = C2U( "%SERIESNAME" );
667         nIndex = aUIStr.indexOf( aReplacement );
668         if( nIndex != -1 )
669         {
670             aUIStr = aUIStr.replaceAt( nIndex, aReplacement.getLength(),
671                                        OUString( m_apLB_SERIES->GetEntryText( pEntry )));
672         }
673 
674         lcl_enableRangeChoosing( true, m_pParentDialog );
675         m_rDialogModel.getRangeSelectionHelper()->chooseRange( aSelectedRolesRange, aUIStr, *this );
676     }
677     else
678         m_pCurrentRangeChoosingField = 0;
679 
680     return 0;
681 }
682 
683 IMPL_LINK( DataSourceTabPage, CategoriesRangeButtonClickedHdl, void *, EMPTYARG )
684 {
685     OSL_ASSERT( m_pCurrentRangeChoosingField == 0 );
686     m_pCurrentRangeChoosingField = & m_aEDT_CATEGORIES;
687     if( m_aEDT_CATEGORIES.GetText().Len() > 0 &&
688         ! updateModelFromControl( m_pCurrentRangeChoosingField ))
689         return 0;
690 
691     String aStr( SchResId( m_aFT_CATEGORIES.IsVisible() ? STR_DATA_SELECT_RANGE_FOR_CATEGORIES : STR_DATA_SELECT_RANGE_FOR_DATALABELS ));
692     lcl_enableRangeChoosing( true, m_pParentDialog );
693     m_rDialogModel.getRangeSelectionHelper()->chooseRange(
694         m_rDialogModel.getCategoriesRange(), OUString( aStr ), *this );
695     return 0;
696 }
697 
698 IMPL_LINK( DataSourceTabPage, AddButtonClickedHdl, void *, EMPTYARG )
699 {
700     m_rDialogModel.startControllerLockTimer();
701     SeriesEntry * pEntry = dynamic_cast< SeriesEntry * >( m_apLB_SERIES->FirstSelected());
702     Reference< XDataSeries > xSeriesToInsertAfter;
703     Reference< XChartType > xChartTypeForNewSeries;
704     if( m_pTemplateProvider )
705             m_rDialogModel.setTemplate( m_pTemplateProvider->getCurrentTemplate());
706 
707     if( pEntry )
708     {
709         xSeriesToInsertAfter.set( pEntry->m_xDataSeries );
710         xChartTypeForNewSeries.set( pEntry->m_xChartType );
711     }
712     else
713     {
714         ::std::vector< Reference< XDataSeriesContainer > > aCntVec(
715             m_rDialogModel.getAllDataSeriesContainers());
716         if( ! aCntVec.empty())
717             xChartTypeForNewSeries.set( aCntVec.front(), uno::UNO_QUERY );
718     }
719     OSL_ENSURE( xChartTypeForNewSeries.is(), "Cannot insert new series" );
720 
721     m_rDialogModel.insertSeriesAfter( xSeriesToInsertAfter, xChartTypeForNewSeries );
722     setDirty();
723 
724     fillSeriesListBox();
725     // note the box was cleared and refilled, so pEntry is invalid now
726     SvLBoxEntry * pSelEntry = m_apLB_SERIES->FirstSelected();
727     if( pSelEntry )
728     {
729         SvLBoxEntry * pNextEntry = m_apLB_SERIES->Next( pSelEntry );
730         if( pNextEntry )
731             m_apLB_SERIES->Select( pNextEntry );
732     }
733     SeriesSelectionChangedHdl( 0 );
734 
735     return 0;
736 }
737 
738 IMPL_LINK( DataSourceTabPage, RemoveButtonClickedHdl, void *, EMPTYARG )
739 {
740     m_rDialogModel.startControllerLockTimer();
741     SeriesEntry * pEntry = dynamic_cast< SeriesEntry * >( m_apLB_SERIES->FirstSelected());
742     if( pEntry )
743     {
744         Reference< XDataSeries > xNewSelSeries;
745         SeriesEntry * pNewSelEntry = dynamic_cast< SeriesEntry * >(
746             m_apLB_SERIES->Next( pEntry ));
747         if( pNewSelEntry )
748             xNewSelSeries.set( pNewSelEntry->m_xDataSeries );
749         else
750         {
751             pNewSelEntry = dynamic_cast< SeriesEntry * >( m_apLB_SERIES->Prev( pEntry ));
752             if( pNewSelEntry )
753                 xNewSelSeries.set( pNewSelEntry->m_xDataSeries );
754         }
755 
756         m_rDialogModel.deleteSeries( pEntry->m_xDataSeries, pEntry->m_xChartType );
757         setDirty();
758 
759         m_apLB_SERIES->RemoveSelection();
760         fillSeriesListBox();
761 
762         // select previous or next series
763         //@improve: see methods GetModel()->GetAbsPos()/GetEntry() for absoulte list positions
764         if( xNewSelSeries.is())
765         {
766             pEntry = dynamic_cast< SeriesEntry * >( m_apLB_SERIES->First());
767             while( pEntry )
768             {
769                 if( pEntry->m_xDataSeries == xNewSelSeries )
770                 {
771                     m_apLB_SERIES->Select( pEntry );
772                     break;
773                 }
774                 pEntry = dynamic_cast< SeriesEntry * >( m_apLB_SERIES->Next( pEntry ));
775             }
776         }
777         SeriesSelectionChangedHdl( 0 );
778     }
779 
780     return 0;
781 }
782 
783 IMPL_LINK( DataSourceTabPage, UpButtonClickedHdl, void *, EMPTYARG )
784 {
785     m_rDialogModel.startControllerLockTimer();
786     SeriesEntry * pEntry = dynamic_cast< SeriesEntry * >( m_apLB_SERIES->FirstSelected());
787     bool bHasSelectedEntry = (pEntry != 0);
788 
789     if( bHasSelectedEntry )
790     {
791         m_rDialogModel.moveSeries( pEntry->m_xDataSeries, DialogModel::MOVE_UP );
792         setDirty();
793         fillSeriesListBox();
794         SeriesSelectionChangedHdl(0);
795     }
796 
797     return 0;
798 }
799 
800 IMPL_LINK( DataSourceTabPage, DownButtonClickedHdl, void *, EMPTYARG )
801 {
802     m_rDialogModel.startControllerLockTimer();
803     SeriesEntry * pEntry = dynamic_cast< SeriesEntry * >( m_apLB_SERIES->FirstSelected());
804     bool bHasSelectedEntry = (pEntry != 0);
805 
806     if( bHasSelectedEntry )
807     {
808         m_rDialogModel.moveSeries( pEntry->m_xDataSeries, DialogModel::MOVE_DOWN );
809         setDirty();
810         fillSeriesListBox();
811         SeriesSelectionChangedHdl(0);
812     }
813 
814     return 0;
815 }
816 
817 IMPL_LINK( DataSourceTabPage, RangeModifiedHdl, Edit*, pEdit )
818 {
819     if( isRangeFieldContentValid( *pEdit ))
820         setDirty();
821 
822     // enable/disable OK button
823     isValid();
824 
825     return 0;
826 }
827 
828 IMPL_LINK( DataSourceTabPage, RangeUpdateDataHdl, Edit*, pEdit )
829 {
830     // note: isValid sets the color of the edit field
831     if( isRangeFieldContentValid( *pEdit ))
832     {
833         setDirty();
834         updateModelFromControl( pEdit );
835         if( pEdit== &m_aEDT_RANGE )
836         {
837             if( ! lcl_UpdateCurrentSeriesName( *m_apLB_SERIES ))
838                 fillSeriesListBox();
839         }
840     }
841     // enable/disable OK button
842     isValid();
843 
844     return 0;
845 }
846 
847 void DataSourceTabPage::listeningFinished(
848     const ::rtl::OUString & rNewRange )
849 {
850     // rNewRange becomes invalid after removing the listener
851     OUString aRange( rNewRange );
852 
853     m_rDialogModel.startControllerLockTimer();
854 
855     // stop listening
856     m_rDialogModel.getRangeSelectionHelper()->stopRangeListening();
857 
858     // change edit field
859     ToTop();
860     GrabFocus();
861     if( m_pCurrentRangeChoosingField )
862     {
863         m_pCurrentRangeChoosingField->SetText( String( aRange ));
864         m_pCurrentRangeChoosingField->GrabFocus();
865     }
866 
867     if( m_pCurrentRangeChoosingField == & m_aEDT_RANGE )
868     {
869         m_aEDT_RANGE.SetText( String( aRange ));
870         setDirty();
871     }
872     else if( m_pCurrentRangeChoosingField == & m_aEDT_CATEGORIES )
873     {
874         m_aEDT_CATEGORIES.SetText( String( aRange ));
875         setDirty();
876     }
877 
878     updateModelFromControl( m_pCurrentRangeChoosingField );
879     if( ! lcl_UpdateCurrentSeriesName( *m_apLB_SERIES ))
880         fillSeriesListBox();
881 
882     m_pCurrentRangeChoosingField = 0;
883 
884     updateControlState();
885     lcl_enableRangeChoosing( false, m_pParentDialog );
886 }
887 
888 void DataSourceTabPage::disposingRangeSelection()
889 {
890     m_rDialogModel.getRangeSelectionHelper()->stopRangeListening( false );
891 }
892 
893 bool DataSourceTabPage::updateModelFromControl( Edit * pField )
894 {
895     if( !m_bIsDirty )
896         return true;
897 
898     ControllerLockGuard aLockedControllers( m_rDialogModel.getChartModel() );
899 
900     // @todo: validity check of field content
901     bool bResult = true;
902     bool bAll = (pField == 0);
903     Reference< data::XDataProvider > xDataProvider( m_rDialogModel.getDataProvider());
904 
905     if( bAll || (pField == & m_aEDT_CATEGORIES) )
906     {
907         Reference< data::XLabeledDataSequence > xLabeledSeq( m_rDialogModel.getCategories() );
908         if( xDataProvider.is())
909         {
910             OUString aRange( m_aEDT_CATEGORIES.GetText());
911             if( aRange.getLength())
912             {
913                 // create or change categories
914                 if( !xLabeledSeq.is())
915                 {
916                     xLabeledSeq.set( DataSourceHelper::createLabeledDataSequence( Reference< uno::XComponentContext >(0)));
917                     m_rDialogModel.setCategories( xLabeledSeq );
918                 }
919                 try
920                 {
921                     xLabeledSeq->setValues( xDataProvider->createDataSequenceByRangeRepresentation( aRange ));
922                 }
923                 catch( const uno::Exception & ex )
924                 {
925                     // should work as validation should have happened before
926                     ASSERT_EXCEPTION( ex );
927                 }
928             }
929             else if( xLabeledSeq.is())
930             {
931                 // clear existing categories
932                 xLabeledSeq.set(0);
933                 m_rDialogModel.setCategories( xLabeledSeq );
934             }
935         }
936     }
937 
938     SeriesEntry * pSeriesEntry = dynamic_cast< SeriesEntry * >( m_apLB_SERIES->FirstSelected());
939     bool bHasSelectedEntry = (pSeriesEntry != 0);
940 
941     if( bHasSelectedEntry )
942     {
943         if( bAll || (pField == & m_aEDT_RANGE) )
944         {
945             try
946             {
947                 OUString aSelectedRole = lcl_GetSelectedRole( m_aLB_ROLE );
948                 OUString aRange( m_aEDT_RANGE.GetText());
949                 OUString aSequenceRole( aSelectedRole );
950                 bool bIsLabel = aSequenceRole.equals( lcl_aLabelRole );
951                 OUString aSequenceNameForLabel( lcl_GetSequenceNameForLabel( pSeriesEntry ));
952 
953                 if( bIsLabel )
954                     aSequenceRole = aSequenceNameForLabel;
955 
956                 Reference< data::XDataSource > xSource( pSeriesEntry->m_xDataSeries, uno::UNO_QUERY_THROW );
957                 Reference< data::XLabeledDataSequence > xLabeledSeq(
958                     DataSeriesHelper::getDataSequenceByRole( xSource, aSequenceRole ));
959 
960                 if( xDataProvider.is())
961                 {
962                     if( bIsLabel )
963                     {
964                         if( ! xLabeledSeq.is())
965                         {
966                             // check if there is already an "orphan" label sequence
967                             xLabeledSeq.set( lcl_findLSequenceWithOnlyLabel( xSource ));
968                             if( ! xLabeledSeq.is())
969                             {
970                                 // no corresponding labeled data sequence for label found
971                                 xLabeledSeq.set( DataSourceHelper::createLabeledDataSequence( Reference< uno::XComponentContext >(0)));
972                                 lcl_addLSequenceToDataSource( xLabeledSeq, xSource );
973                             }
974                         }
975                         if( xLabeledSeq.is())
976                         {
977                             if( aRange.getLength())
978                             {
979                                 Reference< data::XDataSequence > xNewSeq;
980                                 try
981                                 {
982                                     xNewSeq.set( xDataProvider->createDataSequenceByRangeRepresentation( aRange ));
983                                 }
984                                 catch( const uno::Exception & ex )
985                                 {
986                                     // should work as validation should have happened before
987                                     ASSERT_EXCEPTION( ex );
988                                 }
989                                 if( xNewSeq.is())
990                                 {
991                                     // update range name by the full string provided
992                                     // by the data provider. E.g. "a1" might become
993                                     // "$Sheet1.$A$1"
994                                     aRange = xNewSeq->getSourceRangeRepresentation();
995                                     Reference< beans::XPropertySet > xProp( xNewSeq, uno::UNO_QUERY_THROW );
996                                     xProp->setPropertyValue( C2U("Role"), uno::makeAny( lcl_aLabelRole ));
997                                     xLabeledSeq->setLabel( xNewSeq );
998                                 }
999                             }
1000                             else
1001                             {
1002                                 xLabeledSeq->setLabel( Reference< data::XDataSequence >());
1003                             }
1004                         }
1005                     }
1006                     else
1007                     {
1008                         if( aRange.getLength())
1009                         {
1010                             Reference< data::XDataSequence > xNewSeq;
1011                             try
1012                             {
1013                                 xNewSeq.set( xDataProvider->createDataSequenceByRangeRepresentation( aRange ));
1014                             }
1015                             catch( const uno::Exception & ex )
1016                             {
1017                                 // should work as validation should have happened before
1018                                 ASSERT_EXCEPTION( ex );
1019                             }
1020                             if( xNewSeq.is())
1021                             {
1022                                 // update range name by the full string provided
1023                                 // by the data provider. E.g. "a1:e1" might become
1024                                 // "$Sheet1.$A$1:$E$1"
1025                                 aRange = xNewSeq->getSourceRangeRepresentation();
1026 
1027                                 Reference< beans::XPropertySet > xProp( xNewSeq, uno::UNO_QUERY_THROW );
1028                                 xProp->setPropertyValue( C2U("Role"), uno::makeAny( aSelectedRole ));
1029                                 if( !xLabeledSeq.is())
1030                                 {
1031                                     if( aSelectedRole.equals( aSequenceNameForLabel ))
1032                                         xLabeledSeq.set( lcl_findLSequenceWithOnlyLabel( xSource ));
1033                                     if( ! xLabeledSeq.is())
1034                                     {
1035                                         xLabeledSeq.set( DataSourceHelper::createLabeledDataSequence( Reference< uno::XComponentContext >(0)));
1036                                         lcl_addLSequenceToDataSource( xLabeledSeq, xSource );
1037                                     }
1038                                 }
1039                                 xLabeledSeq->setValues( xNewSeq );
1040                             }
1041                         }
1042                         else if( xLabeledSeq.is())
1043                         {
1044                             // values cannot be deleted. This would also delete the Role (for labels)
1045 //                             xLabeledSeq->setValues( Reference< data::XDataSequence >());
1046                         }
1047                     }
1048                 }
1049 
1050                 lcl_UpdateCurrentRange( m_aLB_ROLE, aSelectedRole, aRange );
1051             }
1052             catch( uno::Exception & ex )
1053             {
1054                 bResult = false;
1055                 ASSERT_EXCEPTION( ex );
1056             }
1057         }
1058     }
1059 
1060     // update View
1061     // @todo remove this when automatic view updates from calc, writer and own data sequences are available
1062     if( bResult )
1063     {
1064         try
1065         {
1066             Reference< util::XModifiable > xModifiable( m_rDialogModel.getChartModel(), uno::UNO_QUERY );
1067             if( xModifiable.is() )
1068                 xModifiable->setModified( sal_True );
1069         }
1070         catch( uno::Exception & ex )
1071         {
1072             ASSERT_EXCEPTION( ex );
1073         }
1074     }
1075 
1076     return bResult;
1077 }
1078 
1079 } //  namespace chart
1080