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