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