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 // 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 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 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 202 ScSolverOptionsDialog::~ScSolverOptionsDialog() 203 { 204 delete mpCheckButtonData; 205 } 206 207 const String& ScSolverOptionsDialog::GetEngine() const 208 { 209 return maEngine; // already updated in selection handler 210 } 211 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 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 339 void ScSolverOptionsDialog::ReadFromComponent() 340 { 341 maProperties = ScSolverUtil::GetDefaults( maEngine ); 342 } 343 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 383 IMPL_LINK( ScSolverOptionsDialog, ButtonHdl, PushButton*, pBtn ) 384 { 385 if ( pBtn == &maBtnEdit ) 386 EditOption(); 387 388 return 0; 389 } 390 391 IMPL_LINK( ScSolverOptionsDialog, SettingsDoubleClickHdl, SvTreeListBox*, EMPTYARG ) 392 { 393 EditOption(); 394 return 0; 395 } 396 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 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 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 443 ScSolverIntegerDialog::~ScSolverIntegerDialog() 444 { 445 } 446 447 void ScSolverIntegerDialog::SetOptionName( const String& rName ) 448 { 449 maFtName.SetText( rName ); 450 } 451 452 void ScSolverIntegerDialog::SetValue( sal_Int32 nValue ) 453 { 454 maNfValue.SetValue( nValue ); 455 } 456 457 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 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 480 ScSolverValueDialog::~ScSolverValueDialog() 481 { 482 } 483 484 void ScSolverValueDialog::SetOptionName( const String& rName ) 485 { 486 maFtName.SetText( rName ); 487 } 488 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 496 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