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