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