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