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