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