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