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_sc.hxx"
26 
27 //------------------------------------------------------------------
28 
29 #include "solveroptions.hxx"
30 #include "solveroptions.hrc"
31 #include "scresid.hxx"
32 #include "global.hxx"
33 #include "miscuno.hxx"
34 #include "solverutil.hxx"
35 
36 #include <rtl/math.hxx>
37 #include <vcl/msgbox.hxx>
38 #include <unotools/collatorwrapper.hxx>
39 #include <unotools/localedatawrapper.hxx>
40 
41 #include <algorithm>
42 
43 #include <com/sun/star/sheet/Solver.hpp>
44 #include <com/sun/star/sheet/XSolverDescription.hpp>
45 #include <com/sun/star/beans/PropertyValue.hpp>
46 #include <com/sun/star/beans/XPropertySet.hpp>
47 
48 using namespace com::sun::star;
49 
50 //==================================================================
51 
52 /// Helper for sorting properties
53 struct ScSolverOptionsEntry
54 {
55     sal_Int32       nPosition;
56     rtl::OUString   aDescription;
57 
ScSolverOptionsEntryScSolverOptionsEntry58     ScSolverOptionsEntry() : nPosition(0) {}
59 
operator <ScSolverOptionsEntry60     bool operator< (const ScSolverOptionsEntry& rOther) const
61     {
62         return ( ScGlobal::GetCollator()->compareString( aDescription, rOther.aDescription ) == COMPARE_LESS );
63     }
64 };
65 
66 //------------------------------------------------------------------
67 
68 class ScSolverOptionsString : public SvLBoxString
69 {
70     bool        mbIsDouble;
71     double      mfDoubleValue;
72     sal_Int32   mnIntValue;
73 
74 public:
ScSolverOptionsString(SvLBoxEntry * pEntry,sal_uInt16 nFlags,const String & rStr)75     ScSolverOptionsString( SvLBoxEntry* pEntry, sal_uInt16 nFlags, const String& rStr ) :
76         SvLBoxString( pEntry, nFlags, rStr ),
77         mbIsDouble( false ),
78         mfDoubleValue( 0.0 ),
79         mnIntValue( 0 ) {}
80 
IsDouble() const81     bool      IsDouble() const        { return mbIsDouble; }
GetDoubleValue() const82     double    GetDoubleValue() const  { return mfDoubleValue; }
GetIntValue() const83     sal_Int32 GetIntValue() const     { return mnIntValue; }
84 
SetDoubleValue(double fNew)85     void      SetDoubleValue( double fNew ) { mbIsDouble = true; mfDoubleValue = fNew; }
SetIntValue(sal_Int32 nNew)86     void      SetIntValue( sal_Int32 nNew ) { mbIsDouble = false; mnIntValue = nNew; }
87 // MT: Commented out SV_ITEM_ID_EXTENDRLBOXSTRING and GetExtendText() in svlbitem.hxx - needed?
88 //	virtual USHORT IsA() {return SV_ITEM_ID_EXTENDRLBOXSTRING;}
89 //	virtual XubString GetExtendText() const;
90     virtual void Paint( const Point& rPos, SvLBox& rDev, sal_uInt16 nFlags, SvLBoxEntry* pEntry );
91 };
92 
93 // MT: Commented out SV_ITEM_ID_EXTENDRLBOXSTRING and GetExtendText() in svlbitem.hxx - needed?
94 /*
95 XubString ScSolverOptionsString::GetExtendText() const
96 {
97 	String aNormalStr( GetText() );
98 	aNormalStr.Append( (sal_Unicode) ':' );
99 	String sTxt( ' ' );
100     if ( mbIsDouble )
101         sTxt += (String)rtl::math::doubleToUString( mfDoubleValue,
102             rtl_math_StringFormat_Automatic, rtl_math_DecimalPlaces_Max,
103             ScGlobal::GetpLocaleData()->getNumDecimalSep().GetChar(0), true );
104     else
105         sTxt += String::CreateFromInt32( mnIntValue );
106 	aNormalStr.Append(sTxt);
107 	return aNormalStr;
108 }
109 */
110 
Paint(const Point & rPos,SvLBox & rDev,sal_uInt16,SvLBoxEntry *)111 void ScSolverOptionsString::Paint( const Point& rPos, SvLBox& rDev, sal_uInt16, SvLBoxEntry* /* pEntry */ )
112 {
113     //! move position? (SvxLinguTabPage: aPos.X() += 20)
114     String aNormalStr( GetText() );
115     aNormalStr.Append( (sal_Unicode) ':' );
116     rDev.DrawText( rPos, aNormalStr );
117 
118     Point aNewPos( rPos );
119     aNewPos.X() += rDev.GetTextWidth( aNormalStr );
120     Font aOldFont( rDev.GetFont() );
121     Font aFont( aOldFont );
122     aFont.SetWeight( WEIGHT_BOLD );
123 
124     String sTxt( ' ' );
125     if ( mbIsDouble )
126         sTxt += (String)rtl::math::doubleToUString( mfDoubleValue,
127             rtl_math_StringFormat_Automatic, rtl_math_DecimalPlaces_Max,
128             ScGlobal::GetpLocaleData()->getNumDecimalSep().GetChar(0), true );
129     else
130         sTxt += String::CreateFromInt32( mnIntValue );
131     rDev.SetFont( aFont );
132     rDev.DrawText( aNewPos, sTxt );
133 
134     rDev.SetFont( aOldFont );
135 }
136 
137 //------------------------------------------------------------------
138 
ScSolverOptionsDialog(Window * pParent,const uno::Sequence<rtl::OUString> & rImplNames,const uno::Sequence<rtl::OUString> & rDescriptions,const String & rEngine,const uno::Sequence<beans::PropertyValue> & rProperties)139 ScSolverOptionsDialog::ScSolverOptionsDialog( Window* pParent,
140                         const uno::Sequence<rtl::OUString>& rImplNames,
141                         const uno::Sequence<rtl::OUString>& rDescriptions,
142                         const String& rEngine,
143                         const uno::Sequence<beans::PropertyValue>& rProperties )
144     : ModalDialog( pParent, ScResId( RID_SCDLG_SOLVEROPTIONS ) ),
145     maFtEngine      ( this, ScResId( FT_ENGINE ) ),
146     maLbEngine      ( this, ScResId( LB_ENGINE ) ),
147     maFtSettings    ( this, ScResId( FT_SETTINGS ) ),
148     maLbSettings    ( this, ScResId( LB_SETTINGS ) ),
149     maBtnEdit       ( this, ScResId( BTN_EDIT ) ),
150     maFlButtons     ( this, ScResId( FL_BUTTONS ) ),
151     maBtnHelp       ( this, ScResId( BTN_HELP ) ),
152     maBtnOk         ( this, ScResId( BTN_OK ) ),
153     maBtnCancel     ( this, ScResId( BTN_CANCEL ) ),
154     mpCheckButtonData( NULL ),
155     maImplNames( rImplNames ),
156     maDescriptions( rDescriptions ),
157     maEngine( rEngine ),
158     maProperties( rProperties )
159 {
160     maLbEngine.SetSelectHdl( LINK( this, ScSolverOptionsDialog, EngineSelectHdl ) );
161 
162 	maBtnEdit.SetClickHdl( LINK( this, ScSolverOptionsDialog, ButtonHdl ) );
163 
164     maLbSettings.SetStyle( maLbSettings.GetStyle()|WB_CLIPCHILDREN|WB_FORCE_MAKEVISIBLE );
165     maLbSettings.SetHelpId( HID_SC_SOLVEROPTIONS_LB );
166     maLbSettings.SetHighlightRange();
167 
168     maLbSettings.SetSelectHdl( LINK( this, ScSolverOptionsDialog, SettingsSelHdl ) );
169     maLbSettings.SetDoubleClickHdl( LINK( this, ScSolverOptionsDialog, SettingsDoubleClickHdl ) );
170 
171     sal_Int32 nSelect = -1;
172     sal_Int32 nImplCount = maImplNames.getLength();
173     for (sal_Int32 nImpl=0; nImpl<nImplCount; ++nImpl)
174     {
175         String aImplName( maImplNames[nImpl] );
176         String aDescription( maDescriptions[nImpl] );   // user-visible descriptions in list box
177         maLbEngine.InsertEntry( aDescription );
178         if ( aImplName == maEngine )
179             nSelect = nImpl;
180     }
181     if ( nSelect < 0 )                  // no (valid) engine given
182     {
183         if ( nImplCount > 0 )
184         {
185             maEngine = maImplNames[0];  // use first implementation
186             nSelect = 0;
187         }
188         else
189             maEngine.Erase();
190         maProperties.realloc(0);        // don't use options from different engine
191     }
192     if ( nSelect >= 0 )                 // select in list box
193         maLbEngine.SelectEntryPos( static_cast<sal_uInt16>(nSelect) );
194 
195     if ( !maProperties.getLength() )
196         ReadFromComponent();            // fill maProperties from component (using maEngine)
197     FillListBox();                      // using maProperties
198 
199     FreeResource();
200 }
201 
~ScSolverOptionsDialog()202 ScSolverOptionsDialog::~ScSolverOptionsDialog()
203 {
204     delete mpCheckButtonData;
205 }
206 
GetEngine() const207 const String& ScSolverOptionsDialog::GetEngine() const
208 {
209     return maEngine;    // already updated in selection handler
210 }
211 
GetProperties()212 const uno::Sequence<beans::PropertyValue>& ScSolverOptionsDialog::GetProperties()
213 {
214     // update maProperties from list box content
215     // order of entries in list box and maProperties is the same
216     sal_Int32 nEntryCount = maProperties.getLength();
217     SvLBoxTreeList* pModel = maLbSettings.GetModel();
218     if ( nEntryCount == (sal_Int32)pModel->GetEntryCount() )
219     {
220         for (sal_Int32 nEntryPos=0; nEntryPos<nEntryCount; ++nEntryPos)
221         {
222             uno::Any& rValue = maProperties[nEntryPos].Value;
223             SvLBoxEntry* pEntry = pModel->GetEntry(nEntryPos);
224 
225             bool bHasData = false;
226             sal_uInt16 nItemCount = pEntry->ItemCount();
227             for (sal_uInt16 nItemPos=0; nItemPos<nItemCount && !bHasData; ++nItemPos)
228             {
229                 SvLBoxItem*	pItem = pEntry->GetItem( nItemPos );
230                 ScSolverOptionsString* pStringItem = dynamic_cast<ScSolverOptionsString*>(pItem);
231                 if ( pStringItem )
232                 {
233                     if ( pStringItem->IsDouble() )
234                         rValue <<= pStringItem->GetDoubleValue();
235                     else
236                         rValue <<= pStringItem->GetIntValue();
237                     bHasData = true;
238                 }
239             }
240             if ( !bHasData )
241                 ScUnoHelpFunctions::SetBoolInAny( rValue,
242                                     maLbSettings.GetCheckButtonState( pEntry ) == SV_BUTTON_CHECKED );
243         }
244     }
245     else
246     {
247         DBG_ERRORFILE( "wrong count" );
248     }
249 
250     return maProperties;
251 }
252 
FillListBox()253 void ScSolverOptionsDialog::FillListBox()
254 {
255     // get property descriptions, sort by them
256 
257     uno::Reference<sheet::XSolverDescription> xDesc( ScSolverUtil::GetSolver( maEngine ), uno::UNO_QUERY );
258     sal_Int32 nCount = maProperties.getLength();
259     std::vector<ScSolverOptionsEntry> aDescriptions( nCount );
260     for (sal_Int32 nPos=0; nPos<nCount; nPos++)
261     {
262         rtl::OUString aPropName( maProperties[nPos].Name );
263         rtl::OUString aVisName;
264         if ( xDesc.is() )
265             aVisName = xDesc->getPropertyDescription( aPropName );
266         if ( !aVisName.getLength() )
267             aVisName = aPropName;
268         aDescriptions[nPos].nPosition = nPos;
269         aDescriptions[nPos].aDescription = aVisName;
270     }
271     std::sort( aDescriptions.begin(), aDescriptions.end() );
272 
273     // also update maProperties to the order of descriptions
274 
275     uno::Sequence<beans::PropertyValue> aNewSeq;
276     aNewSeq.realloc( nCount );
277     for (sal_Int32 nPos=0; nPos<nCount; nPos++)
278         aNewSeq[nPos] = maProperties[ aDescriptions[nPos].nPosition ];
279     maProperties = aNewSeq;
280 
281     // fill the list box
282 
283     maLbSettings.SetUpdateMode(sal_False);
284     maLbSettings.Clear();
285 
286     String sEmpty;
287     if (!mpCheckButtonData)
288         mpCheckButtonData = new SvLBoxButtonData( &maLbSettings );
289 
290     SvLBoxTreeList* pModel = maLbSettings.GetModel();
291     SvLBoxEntry* pEntry = NULL;
292 
293     for (sal_Int32 nPos=0; nPos<nCount; nPos++)
294     {
295         rtl::OUString aVisName = aDescriptions[nPos].aDescription;
296 
297         uno::Any aValue = maProperties[nPos].Value;
298         uno::TypeClass eClass = aValue.getValueTypeClass();
299         if ( eClass == uno::TypeClass_BOOLEAN )
300         {
301             // check box entry
302             pEntry = new SvLBoxEntry;
303             SvLBoxButton* pButton = new SvLBoxButton( pEntry, SvLBoxButtonKind_enabledCheckbox, 0, mpCheckButtonData );
304             if ( ScUnoHelpFunctions::GetBoolFromAny( aValue ) )
305                 pButton->SetStateChecked();
306             else
307                 pButton->SetStateUnchecked();
308             pEntry->AddItem( pButton );
309             pEntry->AddItem( new SvLBoxContextBmp( pEntry, 0, Image(), Image(), 0 ) );
310             pEntry->AddItem( new SvLBoxString( pEntry, 0, aVisName ) );
311         }
312         else
313         {
314             // value entry
315             pEntry = new SvLBoxEntry;
316             pEntry->AddItem( new SvLBoxString( pEntry, 0, sEmpty ) );                   // empty column
317             pEntry->AddItem( new SvLBoxContextBmp( pEntry, 0, Image(), Image(), 0 ) );
318             ScSolverOptionsString* pItem = new ScSolverOptionsString( pEntry, 0, aVisName );
319             if ( eClass == uno::TypeClass_DOUBLE )
320             {
321                 double fDoubleValue = 0.0;
322                 if ( aValue >>= fDoubleValue )
323                     pItem->SetDoubleValue( fDoubleValue );
324             }
325             else
326             {
327                 sal_Int32 nIntValue = 0;
328                 if ( aValue >>= nIntValue )
329                     pItem->SetIntValue( nIntValue );
330             }
331             pEntry->AddItem( pItem );
332         }
333         pModel->Insert( pEntry );
334     }
335 
336     maLbSettings.SetUpdateMode(sal_True);
337 }
338 
ReadFromComponent()339 void ScSolverOptionsDialog::ReadFromComponent()
340 {
341     maProperties = ScSolverUtil::GetDefaults( maEngine );
342 }
343 
EditOption()344 void ScSolverOptionsDialog::EditOption()
345 {
346     SvLBoxEntry* pEntry = maLbSettings.GetCurEntry();
347     if (pEntry)
348     {
349         sal_uInt16 nItemCount = pEntry->ItemCount();
350         for (sal_uInt16 nPos=0; nPos<nItemCount; ++nPos)
351         {
352             SvLBoxItem*	pItem = pEntry->GetItem( nPos );
353             ScSolverOptionsString* pStringItem = dynamic_cast<ScSolverOptionsString*>(pItem);
354             if ( pStringItem )
355             {
356                 if ( pStringItem->IsDouble() )
357                 {
358                     ScSolverValueDialog aValDialog( this );
359                     aValDialog.SetOptionName( pStringItem->GetText() );
360                     aValDialog.SetValue( pStringItem->GetDoubleValue() );
361                     if ( aValDialog.Execute() == RET_OK )
362                     {
363                         pStringItem->SetDoubleValue( aValDialog.GetValue() );
364                         maLbSettings.InvalidateEntry( pEntry );
365                     }
366                 }
367                 else
368                 {
369                     ScSolverIntegerDialog aIntDialog( this );
370                     aIntDialog.SetOptionName( pStringItem->GetText() );
371                     aIntDialog.SetValue( pStringItem->GetIntValue() );
372                     if ( aIntDialog.Execute() == RET_OK )
373                     {
374                         pStringItem->SetIntValue( aIntDialog.GetValue() );
375                         maLbSettings.InvalidateEntry( pEntry );
376                     }
377                 }
378             }
379         }
380     }
381 }
382 
IMPL_LINK(ScSolverOptionsDialog,ButtonHdl,PushButton *,pBtn)383 IMPL_LINK( ScSolverOptionsDialog, ButtonHdl, PushButton*, pBtn )
384 {
385     if ( pBtn == &maBtnEdit )
386         EditOption();
387 
388 	return 0;
389 }
390 
IMPL_LINK(ScSolverOptionsDialog,SettingsDoubleClickHdl,SvTreeListBox *,EMPTYARG)391 IMPL_LINK( ScSolverOptionsDialog, SettingsDoubleClickHdl, SvTreeListBox*, EMPTYARG )
392 {
393     EditOption();
394 	return 0;
395 }
396 
IMPL_LINK(ScSolverOptionsDialog,EngineSelectHdl,ListBox *,EMPTYARG)397 IMPL_LINK( ScSolverOptionsDialog, EngineSelectHdl, ListBox*, EMPTYARG )
398 {
399     sal_uInt16 nSelectPos = maLbEngine.GetSelectEntryPos();
400     if ( nSelectPos < maImplNames.getLength() )
401     {
402         String aNewEngine( maImplNames[nSelectPos] );
403         if ( aNewEngine != maEngine )
404         {
405             maEngine = aNewEngine;
406             ReadFromComponent();            // fill maProperties from component (using maEngine)
407             FillListBox();                  // using maProperties
408         }
409     }
410 	return 0;
411 }
412 
IMPL_LINK(ScSolverOptionsDialog,SettingsSelHdl,SvxCheckListBox *,EMPTYARG)413 IMPL_LINK( ScSolverOptionsDialog, SettingsSelHdl, SvxCheckListBox*, EMPTYARG )
414 {
415     sal_Bool bCheckbox = sal_False;
416 
417     SvLBoxEntry* pEntry = maLbSettings.GetCurEntry();
418     if (pEntry)
419     {
420         SvLBoxItem* pItem = pEntry->GetFirstItem(SV_ITEM_ID_LBOXBUTTON);
421         if ( pItem && pItem->IsA() == SV_ITEM_ID_LBOXBUTTON )
422             bCheckbox = sal_True;
423     }
424 
425     maBtnEdit.Enable( !bCheckbox );
426 
427     return 0;
428 }
429 
430 //------------------------------------------------------------------
431 
ScSolverIntegerDialog(Window * pParent)432 ScSolverIntegerDialog::ScSolverIntegerDialog( Window * pParent )
433     : ModalDialog( pParent, ScResId( RID_SCDLG_SOLVER_INTEGER ) ),
434     maFtName        ( this, ScResId( FT_OPTIONNAME ) ),
435     maNfValue       ( this, ScResId( NF_VALUE ) ),
436     maFlButtons     ( this, ScResId( FL_BUTTONS ) ),
437     maBtnOk         ( this, ScResId( BTN_OK ) ),
438     maBtnCancel     ( this, ScResId( BTN_CANCEL ) )
439 {
440     FreeResource();
441 }
442 
~ScSolverIntegerDialog()443 ScSolverIntegerDialog::~ScSolverIntegerDialog()
444 {
445 }
446 
SetOptionName(const String & rName)447 void ScSolverIntegerDialog::SetOptionName( const String& rName )
448 {
449     maFtName.SetText( rName );
450 }
451 
SetValue(sal_Int32 nValue)452 void ScSolverIntegerDialog::SetValue( sal_Int32 nValue )
453 {
454     maNfValue.SetValue( nValue );
455 }
456 
GetValue() const457 sal_Int32 ScSolverIntegerDialog::GetValue() const
458 {
459     sal_Int64 nValue = maNfValue.GetValue();
460     if ( nValue < SAL_MIN_INT32 )
461         return SAL_MIN_INT32;
462     if ( nValue > SAL_MAX_INT32 )
463         return SAL_MAX_INT32;
464     return (sal_Int32) nValue;
465 }
466 
467 //------------------------------------------------------------------
468 
ScSolverValueDialog(Window * pParent)469 ScSolverValueDialog::ScSolverValueDialog( Window * pParent )
470     : ModalDialog( pParent, ScResId( RID_SCDLG_SOLVER_DOUBLE ) ),
471     maFtName        ( this, ScResId( FT_OPTIONNAME ) ),
472     maEdValue       ( this, ScResId( ED_VALUE ) ),
473     maFlButtons     ( this, ScResId( FL_BUTTONS ) ),
474     maBtnOk         ( this, ScResId( BTN_OK ) ),
475     maBtnCancel     ( this, ScResId( BTN_CANCEL ) )
476 {
477     FreeResource();
478 }
479 
~ScSolverValueDialog()480 ScSolverValueDialog::~ScSolverValueDialog()
481 {
482 }
483 
SetOptionName(const String & rName)484 void ScSolverValueDialog::SetOptionName( const String& rName )
485 {
486     maFtName.SetText( rName );
487 }
488 
SetValue(double fValue)489 void ScSolverValueDialog::SetValue( double fValue )
490 {
491     maEdValue.SetText( rtl::math::doubleToUString( fValue,
492             rtl_math_StringFormat_Automatic, rtl_math_DecimalPlaces_Max,
493             ScGlobal::GetpLocaleData()->getNumDecimalSep().GetChar(0), true ) );
494 }
495 
GetValue() const496 double ScSolverValueDialog::GetValue() const
497 {
498     String aInput = maEdValue.GetText();
499 
500     const LocaleDataWrapper* pLocaleData = ScGlobal::GetpLocaleData();
501     rtl_math_ConversionStatus eStatus = rtl_math_ConversionStatus_Ok;
502     double fValue = rtl::math::stringToDouble( aInput,
503                             pLocaleData->getNumDecimalSep().GetChar(0),
504                             pLocaleData->getNumThousandSep().GetChar(0),
505                             &eStatus, NULL );
506     return fValue;
507 }
508 
509