1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 2 /* 3 * This file is part of the LibreOffice project. 4 * 5 * This Source Code Form is subject to the terms of the Mozilla Public 6 * License, v. 2.0. If a copy of the MPL was not distributed with this 7 * file, You can obtain one at http://mozilla.org/MPL/2.0/. 8 * 9 * This file incorporates work covered by the following license notice: 10 * 11 * Licensed to the Apache Software Foundation (ASF) under one or more 12 * contributor license agreements. See the NOTICE file distributed 13 * with this work for additional information regarding copyright 14 * ownership. The ASF licenses this file to you under the Apache 15 * License, Version 2.0 (the "License"); you may not use this file 16 * except in compliance with the License. You may obtain a copy of 17 * the License at http://www.apache.org/licenses/LICENSE-2.0 . 18 */ 19 20 21 #include <limits.h> 22 #include <stdlib.h> 23 #include <algorithm> 24 25 #include <appdata.hxx> 26 #include <sfxtypes.hxx> 27 #include <sfx2/tabdlg.hxx> 28 #include <sfx2/viewfrm.hxx> 29 #include <sfx2/app.hxx> 30 #include <sfx2/sfxresid.hxx> 31 #include <sfx2/sfxhelp.hxx> 32 #include <sfx2/ctrlitem.hxx> 33 #include <sfx2/bindings.hxx> 34 #include <sfx2/sfxdlg.hxx> 35 #include <sfx2/viewsh.hxx> 36 #include <unotools/viewoptions.hxx> 37 #include <vcl/IDialogRenderable.hxx> 38 #include <vcl/virdev.hxx> 39 #include <sal/log.hxx> 40 #include <osl/diagnose.h> 41 42 #include <sfx2/strings.hrc> 43 #include <helpids.h> 44 45 using namespace ::com::sun::star::uno; 46 47 #define USERITEM_NAME "UserItem" 48 49 50 struct TabPageImpl 51 { 52 bool mbStandard; 53 SfxOkDialogController* mpSfxDialogController; 54 css::uno::Reference< css::frame::XFrame > mxFrame; 55 56 TabPageImpl() : mbStandard(false), mpSfxDialogController(nullptr) {} 57 }; 58 59 struct Data_Impl 60 { 61 OString const sId; // The ID 62 CreateTabPage fnCreatePage; // Pointer to Factory 63 GetTabPageRanges fnGetRanges; // Pointer to Ranges-Function 64 std::unique_ptr<SfxTabPage> xTabPage; // The TabPage itself 65 bool bRefresh; // Flag: Page must be re-initialized 66 67 // Constructor 68 Data_Impl( const OString& rId, CreateTabPage fnPage, 69 GetTabPageRanges fnRanges ) : 70 71 sId ( rId ), 72 fnCreatePage( fnPage ), 73 fnGetRanges ( fnRanges ), 74 bRefresh ( false ) 75 { 76 } 77 }; 78 79 SfxTabDialogItem::SfxTabDialogItem( const SfxTabDialogItem& rAttr, SfxItemPool* pItemPool ) 80 : SfxSetItem( rAttr, pItemPool ) 81 { 82 } 83 84 SfxTabDialogItem::SfxTabDialogItem( sal_uInt16 nId, const SfxItemSet& rItemSet ) 85 : SfxSetItem( nId, rItemSet ) 86 { 87 } 88 89 SfxPoolItem* SfxTabDialogItem::Clone(SfxItemPool* pToPool) const 90 { 91 return new SfxTabDialogItem( *this, pToPool ); 92 } 93 94 typedef std::vector<Data_Impl*> SfxTabDlgData_Impl; 95 96 struct TabDlg_Impl 97 { 98 bool bHideResetBtn : 1; 99 bool bStarted : 1; 100 SfxTabDlgData_Impl aData; 101 102 explicit TabDlg_Impl(sal_uInt8 nCnt) 103 : bHideResetBtn(false) 104 , bStarted(false) 105 { 106 aData.reserve( nCnt ); 107 } 108 }; 109 110 static Data_Impl* Find( const SfxTabDlgData_Impl& rArr, const OString& rId, sal_uInt16* pPos = nullptr) 111 { 112 const sal_uInt16 nCount = rArr.size(); 113 114 for ( sal_uInt16 i = 0; i < nCount; ++i ) 115 { 116 Data_Impl* pObj = rArr[i]; 117 118 if ( pObj->sId == rId ) 119 { 120 if ( pPos ) 121 *pPos = i; 122 return pObj; 123 } 124 } 125 return nullptr; 126 } 127 128 void SfxTabPage::SetFrame(const css::uno::Reference< css::frame::XFrame >& xFrame) 129 { 130 if (pImpl) 131 pImpl->mxFrame = xFrame; 132 } 133 134 css::uno::Reference< css::frame::XFrame > SfxTabPage::GetFrame() const 135 { 136 if (pImpl) 137 return pImpl->mxFrame; 138 return css::uno::Reference< css::frame::XFrame >(); 139 } 140 141 SfxTabPage::SfxTabPage(weld::Container* pPage, weld::DialogController* pController, const OUString& rUIXMLDescription, const OString& rID, const SfxItemSet *rAttrSet) 142 : BuilderPage(pPage, pController, rUIXMLDescription, rID) 143 , pSet ( rAttrSet ) 144 , bHasExchangeSupport ( false ) 145 , pImpl ( new TabPageImpl ) 146 { 147 pImpl->mpSfxDialogController = dynamic_cast<SfxOkDialogController*>(m_pDialogController); 148 } 149 150 SfxTabPage::~SfxTabPage() 151 { 152 if (m_xContainer) 153 { 154 std::unique_ptr<weld::Container> xParent(m_xContainer->weld_parent()); 155 if (xParent) 156 xParent->move(m_xContainer.get(), nullptr); 157 } 158 m_xContainer.reset(); 159 pImpl.reset(); 160 m_xBuilder.reset(); 161 } 162 163 bool SfxTabPage::FillItemSet( SfxItemSet* ) 164 { 165 return false; 166 } 167 168 void SfxTabPage::Reset( const SfxItemSet* ) 169 { 170 } 171 172 void SfxTabPage::ActivatePage( const SfxItemSet& ) 173 /* [Description] 174 175 Default implementation of the virtual ActivatePage method. This method is 176 called when a page of dialogue supports the exchange of data between pages. 177 <SfxTabPage::DeactivatePage(SfxItemSet *)> 178 */ 179 { 180 } 181 182 DeactivateRC SfxTabPage::DeactivatePage( SfxItemSet* ) 183 184 /* [Description] 185 186 Default implementation of the virtual DeactivatePage method. This method is 187 called by Sfx when leaving a page; the application can, through the return 188 value, control whether to leave the page. If the page is displayed through 189 bHasExchangeSupport which supports data exchange between pages, then a 190 pointer to the exchange set is passed as parameter. This takes on data for 191 the exchange, then the set is available as a parameter in 192 <SfxTabPage::ActivatePage(const SfxItemSet &)>. 193 194 [Return value] 195 196 DeactivateRC::LeavePage; Allow leaving the page 197 */ 198 199 { 200 return DeactivateRC::LeavePage; 201 } 202 203 204 void SfxTabPage::FillUserData() 205 206 /* [Description] 207 208 Virtual method is called by the base class in the destructor to save 209 specific information of the TabPage in the ini-file. When overriding a 210 string must be compiled, which is then flushed with the <SetUserData()>. 211 */ 212 213 { 214 } 215 216 217 bool SfxTabPage::IsReadOnly() const 218 { 219 return false; 220 } 221 222 223 const SfxPoolItem* SfxTabPage::GetItem( const SfxItemSet& rSet, sal_uInt16 nSlot, bool bDeep ) 224 225 /* [Description] 226 227 static Method: hereby are the implementations of the TabPage code 228 being simplified. 229 */ 230 231 { 232 const SfxItemPool* pPool = rSet.GetPool(); 233 sal_uInt16 nWh = pPool->GetWhich( nSlot, bDeep ); 234 const SfxPoolItem* pItem = nullptr; 235 rSet.GetItemState( nWh, true, &pItem ); 236 237 if ( !pItem && nWh != nSlot ) 238 pItem = &pPool->GetDefaultItem( nWh ); 239 return pItem; 240 } 241 242 243 const SfxPoolItem* SfxTabPage::GetOldItem( const SfxItemSet& rSet, 244 sal_uInt16 nSlot, bool bDeep ) 245 246 /* [Description] 247 248 This method returns an attribute for comparison of the old value. 249 */ 250 251 { 252 const SfxItemSet& rOldSet = GetItemSet(); 253 sal_uInt16 nWh = GetWhich( nSlot, bDeep ); 254 const SfxPoolItem* pItem = nullptr; 255 256 if ( pImpl->mbStandard && rOldSet.GetParent() ) 257 pItem = GetItem( *rOldSet.GetParent(), nSlot ); 258 else if ( rSet.GetParent() && 259 SfxItemState::DONTCARE == rSet.GetItemState( nWh ) ) 260 pItem = GetItem( *rSet.GetParent(), nSlot ); 261 else 262 pItem = GetItem( rOldSet, nSlot ); 263 return pItem; 264 } 265 266 void SfxTabPage::PageCreated( const SfxAllItemSet& /*aSet*/ ) 267 { 268 SAL_WARN( "sfx.dialog", "SfxTabPage::PageCreated should not be called"); 269 } 270 271 void SfxTabPage::ChangesApplied() 272 { 273 } 274 275 void SfxTabPage::SetDialogController(SfxOkDialogController* pDialog) 276 { 277 pImpl->mpSfxDialogController = pDialog; 278 m_pDialogController = pImpl->mpSfxDialogController; 279 } 280 281 SfxOkDialogController* SfxTabPage::GetDialogController() const 282 { 283 return pImpl->mpSfxDialogController; 284 } 285 286 OString SfxTabPage::GetHelpId() const 287 { 288 if (m_xContainer) 289 return m_xContainer->get_help_id(); 290 return OString(); 291 } 292 293 weld::Window* SfxTabPage::GetFrameWeld() const 294 { 295 if (m_pDialogController) 296 return m_pDialogController->getDialog(); 297 return nullptr; 298 } 299 300 const SfxItemSet* SfxTabPage::GetDialogExampleSet() const 301 { 302 if (pImpl->mpSfxDialogController) 303 return pImpl->mpSfxDialogController->GetExampleSet(); 304 return nullptr; 305 } 306 307 SfxTabDialogController::SfxTabDialogController 308 ( 309 weld::Window* pParent, // Parent Window 310 const OUString& rUIXMLDescription, const OString& rID, // Dialog .ui path, Dialog Name 311 const SfxItemSet* pItemSet, // Itemset with the data; 312 // can be NULL, when Pages are onDemand 313 bool bEditFmt // when yes -> additional Button for standard 314 ) 315 : SfxOkDialogController(pParent, rUIXMLDescription, rID) 316 , m_xTabCtrl(m_xBuilder->weld_notebook("tabcontrol")) 317 , m_xOKBtn(m_xBuilder->weld_button("ok")) 318 , m_xApplyBtn(m_xBuilder->weld_button("apply")) 319 , m_xUserBtn(m_xBuilder->weld_button("user")) 320 , m_xCancelBtn(m_xBuilder->weld_button("cancel")) 321 , m_xResetBtn(m_xBuilder->weld_button("reset")) 322 , m_xBaseFmtBtn(m_xBuilder->weld_button("standard")) 323 , m_pSet(pItemSet ? new SfxItemSet(*pItemSet) : nullptr) 324 , m_bStandardPushed(false) 325 { 326 m_pImpl.reset(new TabDlg_Impl(m_xTabCtrl->get_n_pages())); 327 m_pImpl->bHideResetBtn = !m_xResetBtn->get_visible(); 328 m_xOKBtn->connect_clicked(LINK(this, SfxTabDialogController, OkHdl)); 329 m_xCancelBtn->connect_clicked(LINK(this, SfxTabDialogController, CancelHdl)); 330 m_xResetBtn->connect_clicked(LINK(this, SfxTabDialogController, ResetHdl)); 331 m_xResetBtn->set_label(SfxResId(STR_RESET)); 332 m_xTabCtrl->connect_enter_page(LINK(this, SfxTabDialogController, ActivatePageHdl)); 333 m_xTabCtrl->connect_leave_page(LINK(this, SfxTabDialogController, DeactivatePageHdl)); 334 m_xResetBtn->set_help_id(HID_TABDLG_RESET_BTN); 335 336 if (bEditFmt) 337 { 338 m_xBaseFmtBtn->set_label(SfxResId(STR_STANDARD_SHORTCUT)); 339 m_xBaseFmtBtn->connect_clicked(LINK(this, SfxTabDialogController, BaseFmtHdl)); 340 m_xBaseFmtBtn->set_help_id(HID_TABDLG_STANDARD_BTN); 341 m_xBaseFmtBtn->show(); 342 } 343 344 if (m_xUserBtn) 345 m_xUserBtn->connect_clicked(LINK(this, SfxTabDialogController, UserHdl)); 346 347 if (m_pSet) 348 { 349 m_xExampleSet.reset(new SfxItemSet(*m_pSet)); 350 m_pOutSet.reset(new SfxItemSet(*m_pSet->GetPool(), m_pSet->GetRanges())); 351 } 352 } 353 354 IMPL_LINK_NOARG(SfxTabDialogController, OkHdl, weld::Button&, void) 355 356 /* [Description] 357 358 Handler of the Ok-Buttons 359 This calls the current page <SfxTabPage::DeactivatePage(SfxItemSet *)>. 360 Returns <DeactivateRC::LeavePage>, <SfxTabDialog::Ok()> is called 361 and the Dialog is ended. 362 */ 363 364 { 365 if (PrepareLeaveCurrentPage()) 366 m_xDialog->response(Ok()); 367 } 368 369 IMPL_LINK_NOARG(SfxTabDialogController, UserHdl, weld::Button&, void) 370 371 /* [Description] 372 373 Handler of the User-Buttons 374 This calls the current page <SfxTabPage::DeactivatePage(SfxItemSet *)>. 375 returns this <DeactivateRC::LeavePage> and <SfxTabDialog::Ok()> is called. 376 Then the Dialog is ended with the Return value <SfxTabDialog::Ok()> 377 */ 378 379 { 380 if (PrepareLeaveCurrentPage()) 381 { 382 short nRet = Ok(); 383 if (RET_OK == nRet) 384 nRet = RET_USER; 385 else 386 nRet = RET_CANCEL; 387 m_xDialog->response(nRet); 388 } 389 } 390 391 IMPL_LINK_NOARG(SfxTabDialogController, CancelHdl, weld::Button&, void) 392 { 393 m_xDialog->response(RET_CANCEL); 394 } 395 396 IMPL_LINK_NOARG(SfxTabDialogController, ResetHdl, weld::Button&, void) 397 398 /* [Description] 399 400 Handler behind the reset button. 401 The Current Page is new initialized with their initial data, all the 402 settings that the user has made on this page are repealed. 403 */ 404 405 { 406 Data_Impl* pDataObject = Find(m_pImpl->aData, m_xTabCtrl->get_current_page_ident()); 407 assert(pDataObject && "Id not known"); 408 409 pDataObject->xTabPage->Reset(m_pSet.get()); 410 // Also reset relevant items of ExampleSet and OutSet to initial state 411 if (!pDataObject->fnGetRanges) 412 return; 413 414 if (!m_xExampleSet) 415 m_xExampleSet.reset(new SfxItemSet(*m_pSet)); 416 417 const SfxItemPool* pPool = m_pSet->GetPool(); 418 const sal_uInt16* pTmpRanges = (pDataObject->fnGetRanges)(); 419 420 while (*pTmpRanges) 421 { 422 const sal_uInt16* pU = pTmpRanges + 1; 423 424 // Correct Range with multiple values 425 sal_uInt16 nTmp = *pTmpRanges, nTmpEnd = *pU; 426 DBG_ASSERT(nTmp <= nTmpEnd, "Range is sorted the wrong way"); 427 428 if (nTmp > nTmpEnd) 429 { 430 // If really sorted wrongly, then set new 431 std::swap(nTmp, nTmpEnd); 432 } 433 434 while (nTmp && nTmp <= nTmpEnd) 435 { 436 // Iterate over the Range and set the Items 437 sal_uInt16 nWh = pPool->GetWhich(nTmp); 438 const SfxPoolItem* pItem; 439 if (SfxItemState::SET == m_pSet->GetItemState(nWh, false, &pItem)) 440 { 441 m_xExampleSet->Put(*pItem); 442 m_pOutSet->Put(*pItem); 443 } 444 else 445 { 446 m_xExampleSet->ClearItem(nWh); 447 m_pOutSet->ClearItem(nWh); 448 } 449 nTmp++; 450 } 451 // Go to the next pair 452 pTmpRanges += 2; 453 } 454 } 455 456 /* [Description] 457 458 Handler behind the Standard-Button. 459 This button is available when editing style sheets. All the set attributes 460 in the edited stylesheet are deleted. 461 */ 462 IMPL_LINK_NOARG(SfxTabDialogController, BaseFmtHdl, weld::Button&, void) 463 { 464 m_bStandardPushed = true; 465 466 Data_Impl* pDataObject = Find(m_pImpl->aData, m_xTabCtrl->get_current_page_ident()); 467 assert(pDataObject && "Id not known"); 468 469 if (!pDataObject->fnGetRanges) 470 return; 471 472 if (!m_xExampleSet) 473 m_xExampleSet.reset(new SfxItemSet(*m_pSet)); 474 475 const SfxItemPool* pPool = m_pSet->GetPool(); 476 const sal_uInt16* pTmpRanges = (pDataObject->fnGetRanges)(); 477 SfxItemSet aTmpSet(*m_xExampleSet); 478 479 while (*pTmpRanges) 480 { 481 const sal_uInt16* pU = pTmpRanges + 1; 482 483 // Correct Range with multiple values 484 sal_uInt16 nTmp = *pTmpRanges, nTmpEnd = *pU; 485 DBG_ASSERT( nTmp <= nTmpEnd, "Range is sorted the wrong way" ); 486 487 if ( nTmp > nTmpEnd ) 488 { 489 // If really sorted wrongly, then set new 490 std::swap(nTmp, nTmpEnd); 491 } 492 493 while ( nTmp && nTmp <= nTmpEnd ) // guard against overflow 494 { 495 // Iterate over the Range and set the Items 496 sal_uInt16 nWh = pPool->GetWhich(nTmp); 497 m_xExampleSet->ClearItem(nWh); 498 aTmpSet.ClearItem(nWh); 499 // At the Outset of InvalidateItem, 500 // so that the change takes effect 501 m_pOutSet->InvalidateItem(nWh); 502 nTmp++; 503 } 504 // Go to the next pair 505 pTmpRanges += 2; 506 } 507 // Set all Items as new -> the call the current Page Reset() 508 assert(pDataObject->xTabPage && "the Page is gone"); 509 pDataObject->xTabPage->Reset( &aTmpSet ); 510 pDataObject->xTabPage->pImpl->mbStandard = true; 511 } 512 513 IMPL_LINK(SfxTabDialogController, ActivatePageHdl, const OString&, rPage, void) 514 515 /* [Description] 516 517 Handler that is called by StarView for switching to a different page. 518 If possible the <SfxTabPage::Reset(const SfxItemSet &)> or 519 <SfxTabPage::ActivatePage(const SfxItemSet &)> is called on the new page 520 */ 521 522 { 523 assert(!m_pImpl->aData.empty() && "no Pages registered"); 524 Data_Impl* pDataObject = Find(m_pImpl->aData, rPage); 525 if (!pDataObject) 526 { 527 SAL_WARN("sfx.dialog", "Tab Page ID not known, this is pretty serious and needs investigation"); 528 return; 529 } 530 531 SfxTabPage* pTabPage = pDataObject->xTabPage.get(); 532 if (!pTabPage) 533 return; 534 535 if (pDataObject->bRefresh) 536 pTabPage->Reset(m_pSet.get()); 537 pDataObject->bRefresh = false; 538 539 if (m_xExampleSet) 540 pTabPage->ActivatePage(*m_xExampleSet); 541 542 if (pTabPage->IsReadOnly() || m_pImpl->bHideResetBtn) 543 m_xResetBtn->hide(); 544 else 545 m_xResetBtn->show(); 546 } 547 548 IMPL_LINK(SfxTabDialogController, DeactivatePageHdl, const OString&, rPage, bool) 549 550 /* [Description] 551 552 Handler that is called by StarView before leaving a page. 553 554 [Cross-reference] 555 556 <SfxTabPage::DeactivatePage(SfxItemSet *)> 557 */ 558 559 { 560 assert(!m_pImpl->aData.empty() && "no Pages registered"); 561 Data_Impl* pDataObject = Find(m_pImpl->aData, rPage); 562 if (!pDataObject) 563 { 564 SAL_WARN("sfx.dialog", "Tab Page ID not known, this is pretty serious and needs investigation"); 565 return false; 566 } 567 568 SfxTabPage* pPage = pDataObject->xTabPage.get(); 569 if (!pPage) 570 return true; 571 572 DeactivateRC nRet = DeactivateRC::LeavePage; 573 574 if (!m_xExampleSet && pPage->HasExchangeSupport() && m_pSet) 575 m_xExampleSet.reset(new SfxItemSet(*m_pSet->GetPool(), m_pSet->GetRanges())); 576 577 if (m_pSet) 578 { 579 SfxItemSet aTmp( *m_pSet->GetPool(), m_pSet->GetRanges() ); 580 581 if (pPage->HasExchangeSupport()) 582 nRet = pPage->DeactivatePage(&aTmp); 583 else 584 nRet = pPage->DeactivatePage(nullptr); 585 if ( ( DeactivateRC::LeavePage & nRet ) == DeactivateRC::LeavePage && 586 aTmp.Count() && m_xExampleSet) 587 { 588 m_xExampleSet->Put( aTmp ); 589 m_pOutSet->Put( aTmp ); 590 } 591 } 592 else 593 { 594 if ( pPage->HasExchangeSupport() ) //!!! 595 { 596 if (!m_xExampleSet) 597 { 598 SfxItemPool* pPool = pPage->GetItemSet().GetPool(); 599 m_xExampleSet.reset(new SfxItemSet(*pPool, GetInputRanges(*pPool))); 600 } 601 nRet = pPage->DeactivatePage(m_xExampleSet.get()); 602 } 603 else 604 nRet = pPage->DeactivatePage( nullptr ); 605 } 606 607 if ( nRet & DeactivateRC::RefreshSet ) 608 { 609 RefreshInputSet(); 610 // Flag all Pages as to be initialized as new 611 612 for (auto const& elem : m_pImpl->aData) 613 { 614 elem->bRefresh = ( elem->xTabPage.get() != pPage ); // Do not refresh own Page anymore 615 } 616 } 617 return static_cast<bool>(nRet & DeactivateRC::LeavePage); 618 } 619 620 bool SfxTabDialogController::PrepareLeaveCurrentPage() 621 { 622 const OString sId = m_xTabCtrl->get_current_page_ident(); 623 Data_Impl* pDataObject = Find(m_pImpl->aData, sId); 624 DBG_ASSERT( pDataObject, "Id not known" ); 625 SfxTabPage* pPage = pDataObject ? pDataObject->xTabPage.get() : nullptr; 626 627 bool bEnd = !pPage; 628 629 if ( pPage ) 630 { 631 DeactivateRC nRet = DeactivateRC::LeavePage; 632 if ( m_pSet ) 633 { 634 SfxItemSet aTmp( *m_pSet->GetPool(), m_pSet->GetRanges() ); 635 636 if ( pPage->HasExchangeSupport() ) 637 nRet = pPage->DeactivatePage( &aTmp ); 638 else 639 nRet = pPage->DeactivatePage( nullptr ); 640 641 if ( ( DeactivateRC::LeavePage & nRet ) == DeactivateRC::LeavePage 642 && aTmp.Count() ) 643 { 644 m_xExampleSet->Put( aTmp ); 645 m_pOutSet->Put( aTmp ); 646 } 647 } 648 else 649 nRet = pPage->DeactivatePage( nullptr ); 650 bEnd = nRet != DeactivateRC::KeepPage; 651 } 652 653 return bEnd; 654 } 655 656 const sal_uInt16* SfxTabDialogController::GetInputRanges(const SfxItemPool& rPool) 657 658 /* [Description] 659 660 Makes the set over the range of all pages of the dialogue. Pages have the 661 static method for querying their range in AddTabPage, ie deliver their 662 sets onDemand. 663 664 [Return value] 665 666 Pointer to a null-terminated array of sal_uInt16. This array belongs to the 667 dialog and is deleted when the dialogue is destroy. 668 669 [Cross-reference] 670 671 <SfxTabDialog::AddTabPage(sal_uInt16, CreateTabPage, GetTabPageRanges, bool)> 672 <SfxTabDialog::AddTabPage(sal_uInt16, const String &, CreateTabPage, GetTabPageRanges, bool, sal_uInt16)> 673 <SfxTabDialog::AddTabPage(sal_uInt16, const Bitmap &, CreateTabPage, GetTabPageRanges, bool, sal_uInt16)> 674 */ 675 676 { 677 if ( m_pSet ) 678 { 679 SAL_WARN( "sfx.dialog", "Set already exists!" ); 680 return m_pSet->GetRanges(); 681 } 682 683 if ( m_pRanges ) 684 return m_pRanges.get(); 685 std::vector<sal_uInt16> aUS; 686 687 for (auto const& elem : m_pImpl->aData) 688 { 689 690 if ( elem->fnGetRanges ) 691 { 692 const sal_uInt16* pTmpRanges = (elem->fnGetRanges)(); 693 const sal_uInt16* pIter = pTmpRanges; 694 695 sal_uInt16 nLen; 696 for( nLen = 0; *pIter; ++nLen, ++pIter ) 697 ; 698 aUS.insert( aUS.end(), pTmpRanges, pTmpRanges + nLen ); 699 } 700 } 701 702 //! Remove duplicated Ids? 703 { 704 for (auto & elem : aUS) 705 elem = rPool.GetWhich(elem); 706 } 707 708 // sort 709 if ( aUS.size() > 1 ) 710 { 711 std::sort( aUS.begin(), aUS.end() ); 712 } 713 714 m_pRanges.reset(new sal_uInt16[aUS.size() + 1]); 715 std::copy( aUS.begin(), aUS.end(), m_pRanges.get() ); 716 m_pRanges[aUS.size()] = 0; 717 return m_pRanges.get(); 718 } 719 720 SfxTabDialogController::~SfxTabDialogController() 721 { 722 SavePosAndId(); 723 724 for (auto & elem : m_pImpl->aData) 725 { 726 if ( elem->xTabPage ) 727 { 728 // save settings of all pages (user data) 729 elem->xTabPage->FillUserData(); 730 OUString aPageData( elem->xTabPage->GetUserData() ); 731 if ( !aPageData.isEmpty() ) 732 { 733 // save settings of all pages (user data) 734 OUString sConfigId = OStringToOUString(elem->xTabPage->GetConfigId(), 735 RTL_TEXTENCODING_UTF8); 736 SvtViewOptions aPageOpt(EViewType::TabPage, sConfigId); 737 aPageOpt.SetUserItem( USERITEM_NAME, makeAny( aPageData ) ); 738 } 739 740 elem->xTabPage.reset(); 741 } 742 delete elem; 743 elem = nullptr; 744 } 745 } 746 747 short SfxTabDialogController::Ok() 748 749 /* [Description] 750 751 Ok handler for the Dialogue. 752 753 Dialog's current location and current page are saved for the next time 754 the dialog is shown. 755 756 The OutputSet is created and for each page this or the special OutputSet 757 is set by calling the method <SfxTabPage::FillItemSet(SfxItemSet &)>, to 758 insert the entered data by the user into the set. 759 760 [Return value] 761 762 RET_OK: if at least one page has returned from FillItemSet, 763 otherwise RET_CANCEL. 764 */ 765 { 766 SavePosAndId(); //See fdo#38828 "Apply" resetting window position 767 768 if ( !m_pOutSet ) 769 { 770 if ( m_xExampleSet ) 771 m_pOutSet.reset(new SfxItemSet( *m_xExampleSet )); 772 else if ( m_pSet ) 773 m_pOutSet = m_pSet->Clone( false ); // without Items 774 } 775 bool bModified = false; 776 777 for (auto const& elem : m_pImpl->aData) 778 { 779 SfxTabPage* pTabPage = elem->xTabPage.get(); 780 781 if ( pTabPage ) 782 { 783 if ( m_pSet && !pTabPage->HasExchangeSupport() ) 784 { 785 SfxItemSet aTmp( *m_pSet->GetPool(), m_pSet->GetRanges() ); 786 787 if ( pTabPage->FillItemSet( &aTmp ) ) 788 { 789 bModified = true; 790 if (m_xExampleSet) 791 m_xExampleSet->Put( aTmp ); 792 m_pOutSet->Put( aTmp ); 793 } 794 } 795 } 796 } 797 798 if (m_pOutSet && m_pOutSet->Count() > 0) 799 bModified = true; 800 801 if (m_bStandardPushed) 802 bModified = true; 803 804 return bModified ? RET_OK : RET_CANCEL; 805 } 806 807 void SfxTabDialogController::RefreshInputSet() 808 809 /* [Description] 810 811 Default implementation of the virtual Method. 812 This is called, when <SfxTabPage::DeactivatePage(SfxItemSet *)> 813 returns <DeactivateRC::RefreshSet>. 814 */ 815 816 { 817 SAL_INFO ( "sfx.dialog", "RefreshInputSet not implemented" ); 818 } 819 820 void SfxTabDialogController::PageCreated 821 822 /* [Description] 823 824 Default implementation of the virtual method. This is called immediately 825 after creating a page. Here the dialogue can call the TabPage Method 826 directly. 827 */ 828 829 ( 830 const OString&, // Id of the created page 831 SfxTabPage& // Reference to the created page 832 ) 833 { 834 } 835 836 void SfxTabDialogController::SavePosAndId() 837 { 838 // save settings (screen position and current page) 839 SvtViewOptions aDlgOpt(EViewType::TabDialog, OStringToOUString(m_xDialog->get_help_id(), RTL_TEXTENCODING_UTF8)); 840 aDlgOpt.SetPageID(m_xTabCtrl->get_current_page_ident()); 841 } 842 843 /* 844 Adds a page to the dialog. The Name must correspond to an entry in the 845 TabControl in the dialog .ui 846 */ 847 void SfxTabDialogController::AddTabPage(const OString &rName /* Page ID */, 848 CreateTabPage pCreateFunc /* Pointer to the Factory Method */, 849 GetTabPageRanges pRangesFunc /* Pointer to the Method for querying Ranges onDemand */) 850 { 851 m_pImpl->aData.push_back(new Data_Impl(rName, pCreateFunc, pRangesFunc)); 852 } 853 854 void SfxTabDialogController::AddTabPage(const OString &rName /* Page ID */, 855 sal_uInt16 nPageCreateId /* Identifier of the Factory Method to create the page */) 856 { 857 SfxAbstractDialogFactory* pFact = SfxAbstractDialogFactory::Create(); 858 CreateTabPage pCreateFunc = pFact->GetTabPageCreatorFunc(nPageCreateId); 859 GetTabPageRanges pRangesFunc = pFact->GetTabPageRangesFunc(nPageCreateId); 860 AddTabPage(rName, pCreateFunc, pRangesFunc); 861 } 862 863 /* [Description] 864 865 Add a page to the dialog. The Rider text is passed on, the page has no 866 counterpart in the TabControl in the resource of the dialogue. 867 */ 868 869 void SfxTabDialogController::AddTabPage(const OString &rName, /* Page ID */ 870 const OUString& rRiderText, 871 CreateTabPage pCreateFunc /* Pointer to the Factory Method */) 872 { 873 assert(!m_xTabCtrl->get_page(rName) && "Double Page-Ids in the Tabpage"); 874 m_xTabCtrl->append_page(rName, rRiderText); 875 AddTabPage(rName, pCreateFunc, nullptr); 876 } 877 878 void SfxTabDialogController::AddTabPage(const OString &rName, const OUString& rRiderText, 879 sal_uInt16 nPageCreateId /* Identifier of the Factory Method to create the page */) 880 { 881 assert(!m_xTabCtrl->get_page(rName) && "Double Page-Ids in the Tabpage"); 882 m_xTabCtrl->append_page(rName, rRiderText); 883 AddTabPage(rName, nPageCreateId); 884 } 885 886 /* [Description] 887 888 Default implementation of the virtual Method. 889 This is called when pages create their sets onDemand. 890 */ 891 SfxItemSet* SfxTabDialogController::CreateInputItemSet(const OString&) 892 { 893 SAL_WARN( "sfx.dialog", "CreateInputItemSet not implemented" ); 894 return new SfxAllItemSet(SfxGetpApp()->GetPool()); 895 } 896 897 void SfxTabDialogController::CreatePages() 898 { 899 for (auto pDataObject : m_pImpl->aData) 900 { 901 if (pDataObject->xTabPage) 902 continue; 903 weld::Container* pPage = m_xTabCtrl->get_page(pDataObject->sId); 904 if (m_pSet) 905 pDataObject->xTabPage = (pDataObject->fnCreatePage)(pPage, this, m_pSet.get()); 906 else 907 pDataObject->xTabPage = (pDataObject->fnCreatePage)(pPage, this, CreateInputItemSet(pDataObject->sId)); 908 pDataObject->xTabPage->SetDialogController(this); 909 OUString sConfigId = OStringToOUString(pDataObject->xTabPage->GetConfigId(), RTL_TEXTENCODING_UTF8); 910 SvtViewOptions aPageOpt(EViewType::TabPage, sConfigId); 911 OUString sUserData; 912 Any aUserItem = aPageOpt.GetUserItem(USERITEM_NAME); 913 OUString aTemp; 914 if ( aUserItem >>= aTemp ) 915 sUserData = aTemp; 916 pDataObject->xTabPage->SetUserData(sUserData); 917 918 PageCreated(pDataObject->sId, *pDataObject->xTabPage); 919 pDataObject->xTabPage->Reset(m_pSet.get()); 920 } 921 } 922 923 void SfxTabDialogController::setPreviewsToSamePlace() 924 { 925 //where tab pages have the same basic layout with a preview on the right, 926 //get both of their non-preview areas to request the same size so that the 927 //preview appears in the same place in each one so flipping between tabs 928 //isn't distracting as it jumps around 929 std::vector<std::unique_ptr<weld::Widget>> aGrids; 930 for (auto pDataObject : m_pImpl->aData) 931 { 932 if (!pDataObject->xTabPage) 933 continue; 934 if (!pDataObject->xTabPage->m_xBuilder) 935 continue; 936 std::unique_ptr<weld::Widget> pGrid = pDataObject->xTabPage->m_xBuilder->weld_widget("maingrid"); 937 if (!pGrid) 938 continue; 939 aGrids.emplace_back(std::move(pGrid)); 940 } 941 942 m_xSizeGroup.reset(); 943 944 if (aGrids.size() <= 1) 945 return; 946 947 m_xSizeGroup = m_xBuilder->create_size_group(); 948 for (auto& rGrid : aGrids) 949 m_xSizeGroup->add_widget(rGrid.get()); 950 } 951 952 void SfxTabDialogController::RemoveTabPage(const OString& rId) 953 954 /* [Description] 955 956 Delete the TabPage with ID nId 957 */ 958 959 { 960 sal_uInt16 nPos = 0; 961 m_xTabCtrl->remove_page(rId); 962 Data_Impl* pDataObject = Find( m_pImpl->aData, rId, &nPos ); 963 964 if ( pDataObject ) 965 { 966 if ( pDataObject->xTabPage ) 967 { 968 pDataObject->xTabPage->FillUserData(); 969 OUString aPageData( pDataObject->xTabPage->GetUserData() ); 970 if ( !aPageData.isEmpty() ) 971 { 972 // save settings of this page (user data) 973 OUString sConfigId = OStringToOUString(pDataObject->xTabPage->GetConfigId(), 974 RTL_TEXTENCODING_UTF8); 975 SvtViewOptions aPageOpt(EViewType::TabPage, sConfigId); 976 aPageOpt.SetUserItem( USERITEM_NAME, makeAny( aPageData ) ); 977 } 978 979 pDataObject->xTabPage.reset(); 980 } 981 982 delete pDataObject; 983 m_pImpl->aData.erase( m_pImpl->aData.begin() + nPos ); 984 } 985 else 986 { 987 SAL_INFO( "sfx.dialog", "TabPage-Id not known" ); 988 } 989 } 990 991 void SfxTabDialogController::Start_Impl() 992 { 993 CreatePages(); 994 995 setPreviewsToSamePlace(); 996 997 assert(m_pImpl->aData.size() == static_cast<size_t>(m_xTabCtrl->get_n_pages()) 998 && "not all pages registered"); 999 1000 // load old settings, when exists, setting SetCurPageId will override the settings, 1001 // something that the sort dialog in calc depends on 1002 if (m_sAppPageId.isEmpty()) 1003 { 1004 SvtViewOptions aDlgOpt(EViewType::TabDialog, OStringToOUString(m_xDialog->get_help_id(), RTL_TEXTENCODING_UTF8)); 1005 if (aDlgOpt.Exists()) 1006 m_xTabCtrl->set_current_page(aDlgOpt.GetPageID()); 1007 } 1008 1009 ActivatePageHdl(m_xTabCtrl->get_current_page_ident()); 1010 1011 m_pImpl->bStarted = true; 1012 } 1013 1014 void SfxTabDialogController::SetCurPageId(const OString& rIdent) 1015 { 1016 m_sAppPageId = rIdent; 1017 m_xTabCtrl->set_current_page(m_sAppPageId); 1018 } 1019 1020 /* [Description] 1021 1022 The TabPage is activated with the specified Id. 1023 */ 1024 void SfxTabDialogController::ShowPage(const OString& rIdent) 1025 { 1026 SetCurPageId(rIdent); 1027 ActivatePageHdl(rIdent); 1028 } 1029 1030 OString SfxTabDialogController::GetCurPageId() const 1031 { 1032 return m_xTabCtrl->get_current_page_ident(); 1033 } 1034 1035 short SfxTabDialogController::run() 1036 { 1037 Start_Impl(); 1038 return SfxDialogController::run(); 1039 } 1040 1041 bool SfxTabDialogController::runAsync(const std::shared_ptr<SfxTabDialogController>& rController, 1042 const std::function<void(sal_Int32)>& rFunc) 1043 { 1044 rController->Start_Impl(); 1045 return weld::DialogController::runAsync(rController, rFunc); 1046 } 1047 1048 void SfxTabDialogController::SetInputSet( const SfxItemSet* pInSet ) 1049 1050 /* [Description] 1051 1052 With this method the Input-Set can subsequently be set initially or re-set. 1053 */ 1054 1055 { 1056 bool bSet = ( m_pSet != nullptr ); 1057 m_pSet.reset(pInSet ? new SfxItemSet(*pInSet) : nullptr); 1058 1059 if (!bSet && !m_xExampleSet && !m_pOutSet && m_pSet) 1060 { 1061 m_xExampleSet.reset(new SfxItemSet(*m_pSet)); 1062 m_pOutSet.reset(new SfxItemSet( *m_pSet->GetPool(), m_pSet->GetRanges() )); 1063 } 1064 } 1065 1066 SfxItemSet* SfxTabDialogController::GetInputSetImpl() 1067 1068 /* [Description] 1069 1070 Derived classes may create new storage for the InputSet. This has to be 1071 released in the Destructor. To do this, this method must be called. 1072 */ 1073 1074 { 1075 return m_pSet.get(); 1076 } 1077 1078 void SfxTabDialogController::RemoveResetButton() 1079 { 1080 m_xResetBtn->hide(); 1081 m_pImpl->bHideResetBtn = true; 1082 } 1083 1084 void SfxTabDialogController::RemoveStandardButton() 1085 { 1086 m_xBaseFmtBtn->hide(); 1087 } 1088 1089 SfxTabPage* SfxTabDialogController::GetTabPage(const OString& rPageId) const 1090 1091 /* [Description] 1092 1093 Return TabPage with the specified Id. 1094 */ 1095 1096 { 1097 Data_Impl* pDataObject = Find(m_pImpl->aData, rPageId); 1098 if (pDataObject) 1099 return pDataObject->xTabPage.get(); 1100 return nullptr; 1101 } 1102 1103 void SfxTabDialogController::SetApplyHandler(const Link<weld::Button&, void>& _rHdl) 1104 { 1105 DBG_ASSERT( m_xApplyBtn, "SfxTabDialog::GetApplyHandler: no apply button enabled!" ); 1106 if (m_xApplyBtn) 1107 m_xApplyBtn->connect_clicked(_rHdl); 1108 } 1109 1110 bool SfxTabDialogController::Apply() 1111 { 1112 bool bApplied = false; 1113 if (PrepareLeaveCurrentPage()) 1114 { 1115 bApplied = (Ok() == RET_OK); 1116 //let the pages update their saved values 1117 GetInputSetImpl()->Put(*GetOutputItemSet()); 1118 for (auto pDataObject : m_pImpl->aData) 1119 { 1120 if (!pDataObject->xTabPage) 1121 continue; 1122 pDataObject->xTabPage->ChangesApplied(); 1123 } 1124 } 1125 return bApplied; 1126 } 1127 1128 std::vector<OString> SfxTabDialogController::getAllPageUIXMLDescriptions() const 1129 { 1130 int nPages = m_xTabCtrl->get_n_pages(); 1131 std::vector<OString> aRet; 1132 aRet.reserve(nPages); 1133 for (int i = 0; i < nPages; ++i) 1134 aRet.push_back(m_xTabCtrl->get_page_ident(i)); 1135 return aRet; 1136 } 1137 1138 bool SfxTabDialogController::selectPageByUIXMLDescription(const OString& rUIXMLDescription) 1139 { 1140 ShowPage(rUIXMLDescription); 1141 return m_xTabCtrl->get_current_page_ident() == rUIXMLDescription; 1142 } 1143 1144 BitmapEx SfxTabDialogController::createScreenshot() const 1145 { 1146 // if we haven't run Start_Impl yet, do so now to create the initial pages 1147 if (!m_pImpl->bStarted) 1148 { 1149 const_cast<SfxTabDialogController*>(this)->Start_Impl(); 1150 } 1151 1152 VclPtr<VirtualDevice> xDialogSurface(VclPtr<VirtualDevice>::Create(DeviceFormat::DEFAULT)); 1153 m_xDialog->draw(*xDialogSurface); 1154 return xDialogSurface->GetBitmapEx(Point(), xDialogSurface->GetOutputSizePixel()); 1155 } 1156 1157 OString SfxTabDialogController::GetScreenshotId() const 1158 { 1159 const OString sId = m_xTabCtrl->get_current_page_ident(); 1160 Data_Impl* pDataObject = Find(m_pImpl->aData, sId); 1161 SfxTabPage* pPage = pDataObject ? pDataObject->xTabPage.get() : nullptr; 1162 if (pPage) 1163 { 1164 OString sHelpId(pPage->GetHelpId()); 1165 if (!sHelpId.isEmpty()) 1166 return sHelpId; 1167 } 1168 return m_xDialog->get_help_id(); 1169 } 1170 1171 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ 1172
