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 <stdlib.h> 22 #include <algorithm> 23 24 #include <sfx2/tabdlg.hxx> 25 #include <sfx2/app.hxx> 26 #include <sfx2/sfxresid.hxx> 27 #include <sfx2/sfxdlg.hxx> 28 #include <unotools/viewoptions.hxx> 29 #include <vcl/virdev.hxx> 30 #include <sal/log.hxx> 31 #include <tools/debug.hxx> 32 #include <comphelper/lok.hxx> 33 34 #include <sfx2/strings.hrc> 35 #include <helpids.h> 36 37 using namespace ::com::sun::star::uno; 38 39 #define USERITEM_NAME "UserItem" 40 41 42 struct TabPageImpl 43 { 44 bool mbStandard; 45 SfxOkDialogController* mpSfxDialogController; 46 css::uno::Reference< css::frame::XFrame > mxFrame; 47 48 TabPageImpl() : mbStandard(false), mpSfxDialogController(nullptr) {} 49 }; 50 51 namespace { 52 53 struct Data_Impl 54 { 55 OString sId; // The ID 56 CreateTabPage fnCreatePage; // Pointer to Factory 57 GetTabPageRanges fnGetRanges; // Pointer to Ranges-Function 58 std::unique_ptr<SfxTabPage> xTabPage; // The TabPage itself 59 bool bRefresh; // Flag: Page must be re-initialized 60 61 // Constructor 62 Data_Impl( const OString& rId, CreateTabPage fnPage, 63 GetTabPageRanges fnRanges ) : 64 65 sId ( rId ), 66 fnCreatePage( fnPage ), 67 fnGetRanges ( fnRanges ), 68 bRefresh ( false ) 69 { 70 } 71 }; 72 73 } 74 75 SfxTabDialogItem::SfxTabDialogItem( const SfxTabDialogItem& rAttr, SfxItemPool* pItemPool ) 76 : SfxSetItem( rAttr, pItemPool ) 77 { 78 } 79 80 SfxTabDialogItem::SfxTabDialogItem( sal_uInt16 nId, const SfxItemSet& rItemSet ) 81 : SfxSetItem( nId, rItemSet ) 82 { 83 } 84 85 SfxTabDialogItem* SfxTabDialogItem::Clone(SfxItemPool* pToPool) const 86 { 87 return new SfxTabDialogItem( *this, pToPool ); 88 } 89 90 typedef std::vector<Data_Impl*> SfxTabDlgData_Impl; 91 92 struct TabDlg_Impl 93 { 94 bool bHideResetBtn : 1; 95 bool bStarted : 1; 96 SfxTabDlgData_Impl aData; 97 98 explicit TabDlg_Impl(sal_uInt8 nCnt) 99 : bHideResetBtn(false) 100 , bStarted(false) 101 { 102 aData.reserve( nCnt ); 103 } 104 }; 105 106 static Data_Impl* Find( const SfxTabDlgData_Impl& rArr, const OString& rId, sal_uInt16* pPos = nullptr) 107 { 108 const sal_uInt16 nCount = rArr.size(); 109 110 for ( sal_uInt16 i = 0; i < nCount; ++i ) 111 { 112 Data_Impl* pObj = rArr[i]; 113 114 if ( pObj->sId == rId ) 115 { 116 if ( pPos ) 117 *pPos = i; 118 return pObj; 119 } 120 } 121 return nullptr; 122 } 123 124 void SfxTabPage::SetFrame(const css::uno::Reference< css::frame::XFrame >& xFrame) 125 { 126 if (pImpl) 127 pImpl->mxFrame = xFrame; 128 } 129 130 css::uno::Reference< css::frame::XFrame > SfxTabPage::GetFrame() const 131 { 132 if (pImpl) 133 return pImpl->mxFrame; 134 return css::uno::Reference< css::frame::XFrame >(); 135 } 136 137 SfxTabPage::SfxTabPage(weld::Container* pPage, weld::DialogController* pController, const OUString& rUIXMLDescription, const OString& rID, const SfxItemSet *rAttrSet) 138 : BuilderPage(pPage, pController, rUIXMLDescription, rID) 139 , pSet ( rAttrSet ) 140 , bHasExchangeSupport ( false ) 141 , pImpl ( new TabPageImpl ) 142 { 143 pImpl->mpSfxDialogController = dynamic_cast<SfxOkDialogController*>(m_pDialogController); 144 } 145 146 SfxTabPage::~SfxTabPage() 147 { 148 if (m_xContainer) 149 { 150 std::unique_ptr<weld::Container> xParent(m_xContainer->weld_parent()); 151 if (xParent) 152 xParent->move(m_xContainer.get(), nullptr); 153 } 154 m_xContainer.reset(); 155 pImpl.reset(); 156 m_xBuilder.reset(); 157 } 158 159 bool SfxTabPage::FillItemSet( SfxItemSet* ) 160 { 161 return false; 162 } 163 164 void SfxTabPage::Reset( const SfxItemSet* ) 165 { 166 } 167 168 void SfxTabPage::ActivatePage( const SfxItemSet& ) 169 /* [Description] 170 171 Default implementation of the virtual ActivatePage method. This method is 172 called when a page of dialogue supports the exchange of data between pages. 173 <SfxTabPage::DeactivatePage(SfxItemSet *)> 174 */ 175 { 176 } 177 178 DeactivateRC SfxTabPage::DeactivatePage( SfxItemSet* ) 179 180 /* [Description] 181 182 Default implementation of the virtual DeactivatePage method. This method is 183 called by Sfx when leaving a page; the application can, through the return 184 value, control whether to leave the page. If the page is displayed through 185 bHasExchangeSupport which supports data exchange between pages, then a 186 pointer to the exchange set is passed as parameter. This takes on data for 187 the exchange, then the set is available as a parameter in 188 <SfxTabPage::ActivatePage(const SfxItemSet &)>. 189 190 [Return value] 191 192 DeactivateRC::LeavePage; Allow leaving the page 193 */ 194 195 { 196 return DeactivateRC::LeavePage; 197 } 198 199 200 void SfxTabPage::FillUserData() 201 202 /* [Description] 203 204 Virtual method is called by the base class in the destructor to save 205 specific information of the TabPage in the ini-file. When overriding a 206 string must be compiled, which is then flushed with the <SetUserData()>. 207 */ 208 209 { 210 } 211 212 213 bool SfxTabPage::IsReadOnly() const 214 { 215 return false; 216 } 217 218 219 const SfxPoolItem* SfxTabPage::GetItem( const SfxItemSet& rSet, sal_uInt16 nSlot, bool bDeep ) 220 221 /* [Description] 222 223 static Method: hereby are the implementations of the TabPage code 224 being simplified. 225 */ 226 227 { 228 const SfxItemPool* pPool = rSet.GetPool(); 229 sal_uInt16 nWh = pPool->GetWhich( nSlot, bDeep ); 230 const SfxPoolItem* pItem = nullptr; 231 rSet.GetItemState( nWh, true, &pItem ); 232 233 if ( !pItem && nWh != nSlot ) 234 pItem = &pPool->GetDefaultItem( nWh ); 235 return pItem; 236 } 237 238 239 const SfxPoolItem* SfxTabPage::GetOldItem( const SfxItemSet& rSet, 240 sal_uInt16 nSlot, bool bDeep ) 241 242 /* [Description] 243 244 This method returns an attribute for comparison of the old value. 245 */ 246 247 { 248 const SfxItemSet& rOldSet = GetItemSet(); 249 sal_uInt16 nWh = GetWhich( nSlot, bDeep ); 250 const SfxPoolItem* pItem = nullptr; 251 252 if ( pImpl->mbStandard && rOldSet.GetParent() ) 253 pItem = GetItem( *rOldSet.GetParent(), nSlot ); 254 else if ( rSet.GetParent() && 255 SfxItemState::DONTCARE == rSet.GetItemState( nWh ) ) 256 pItem = GetItem( *rSet.GetParent(), nSlot ); 257 else 258 pItem = GetItem( rOldSet, nSlot ); 259 return pItem; 260 } 261 262 void SfxTabPage::PageCreated( const SfxAllItemSet& /*aSet*/ ) 263 { 264 SAL_WARN( "sfx.dialog", "SfxTabPage::PageCreated should not be called"); 265 } 266 267 void SfxTabPage::ChangesApplied() 268 { 269 } 270 271 void SfxTabPage::SetDialogController(SfxOkDialogController* pDialog) 272 { 273 pImpl->mpSfxDialogController = pDialog; 274 m_pDialogController = pImpl->mpSfxDialogController; 275 } 276 277 SfxOkDialogController* SfxTabPage::GetDialogController() const 278 { 279 return pImpl->mpSfxDialogController; 280 } 281 282 OString SfxTabPage::GetHelpId() const 283 { 284 if (m_xContainer) 285 return m_xContainer->get_help_id(); 286 return OString(); 287 } 288 289 weld::Window* SfxTabPage::GetFrameWeld() const 290 { 291 if (m_pDialogController) 292 return m_pDialogController->getDialog(); 293 return nullptr; 294 } 295 296 const SfxItemSet* SfxTabPage::GetDialogExampleSet() const 297 { 298 if (pImpl->mpSfxDialogController) 299 return pImpl->mpSfxDialogController->GetExampleSet(); 300 return nullptr; 301 } 302 303 SfxTabDialogController::SfxTabDialogController 304 ( 305 weld::Widget* pParent, // Parent Window 306 const OUString& rUIXMLDescription, const OString& rID, // Dialog .ui path, Dialog Name 307 const SfxItemSet* pItemSet, // Itemset with the data; 308 // can be NULL, when Pages are onDemand 309 bool bEditFmt // when yes -> additional Button for standard 310 ) 311 : SfxOkDialogController(pParent, rUIXMLDescription, rID) 312 , m_xTabCtrl(m_xBuilder->weld_notebook("tabcontrol")) 313 , m_xOKBtn(m_xBuilder->weld_button("ok")) 314 , m_xApplyBtn(m_xBuilder->weld_button("apply")) 315 , m_xUserBtn(m_xBuilder->weld_button("user")) 316 , m_xCancelBtn(m_xBuilder->weld_button("cancel")) 317 , m_xResetBtn(m_xBuilder->weld_button("reset")) 318 , m_xBaseFmtBtn(m_xBuilder->weld_button("standard")) 319 , m_pSet(pItemSet ? new SfxItemSet(*pItemSet) : nullptr) 320 , m_bStandardPushed(false) 321 { 322 m_pImpl.reset(new TabDlg_Impl(m_xTabCtrl->get_n_pages())); 323 m_pImpl->bHideResetBtn = !m_xResetBtn->get_visible(); 324 m_xOKBtn->connect_clicked(LINK(this, SfxTabDialogController, OkHdl)); 325 m_xCancelBtn->connect_clicked(LINK(this, SfxTabDialogController, CancelHdl)); 326 m_xResetBtn->connect_clicked(LINK(this, SfxTabDialogController, ResetHdl)); 327 m_xResetBtn->set_label(SfxResId(STR_RESET)); 328 m_xTabCtrl->connect_enter_page(LINK(this, SfxTabDialogController, ActivatePageHdl)); 329 m_xTabCtrl->connect_leave_page(LINK(this, SfxTabDialogController, DeactivatePageHdl)); 330 m_xResetBtn->set_help_id(HID_TABDLG_RESET_BTN); 331 332 if (bEditFmt) 333 { 334 m_xBaseFmtBtn->set_label(SfxResId(STR_STANDARD_SHORTCUT)); 335 m_xBaseFmtBtn->connect_clicked(LINK(this, SfxTabDialogController, BaseFmtHdl)); 336 m_xBaseFmtBtn->set_help_id(HID_TABDLG_STANDARD_BTN); 337 m_xBaseFmtBtn->show(); 338 } 339 340 if (m_xUserBtn) 341 m_xUserBtn->connect_clicked(LINK(this, SfxTabDialogController, UserHdl)); 342 343 if (m_pSet) 344 { 345 m_xExampleSet.reset(new SfxItemSet(*m_pSet)); 346 m_pOutSet.reset(new SfxItemSet(*m_pSet->GetPool(), m_pSet->GetRanges())); 347 } 348 349 // The reset functionality seems to be confusing to many; disable in LOK. 350 if (comphelper::LibreOfficeKit::isActive()) 351 RemoveResetButton(); 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 m_xSizeGroup->set_mode(VclSizeGroupMode::Both); 949 for (auto& rGrid : aGrids) 950 m_xSizeGroup->add_widget(rGrid.get()); 951 } 952 953 void SfxTabDialogController::RemoveTabPage(const OString& rId) 954 955 /* [Description] 956 957 Delete the TabPage with ID nId 958 */ 959 960 { 961 sal_uInt16 nPos = 0; 962 m_xTabCtrl->remove_page(rId); 963 Data_Impl* pDataObject = Find( m_pImpl->aData, rId, &nPos ); 964 965 if ( pDataObject ) 966 { 967 if ( pDataObject->xTabPage ) 968 { 969 pDataObject->xTabPage->FillUserData(); 970 OUString aPageData( pDataObject->xTabPage->GetUserData() ); 971 if ( !aPageData.isEmpty() ) 972 { 973 // save settings of this page (user data) 974 OUString sConfigId = OStringToOUString(pDataObject->xTabPage->GetConfigId(), 975 RTL_TEXTENCODING_UTF8); 976 SvtViewOptions aPageOpt(EViewType::TabPage, sConfigId); 977 aPageOpt.SetUserItem( USERITEM_NAME, makeAny( aPageData ) ); 978 } 979 980 pDataObject->xTabPage.reset(); 981 } 982 983 delete pDataObject; 984 m_pImpl->aData.erase( m_pImpl->aData.begin() + nPos ); 985 } 986 else 987 { 988 SAL_INFO( "sfx.dialog", "TabPage-Id not known" ); 989 } 990 } 991 992 void SfxTabDialogController::Start_Impl() 993 { 994 CreatePages(); 995 996 setPreviewsToSamePlace(); 997 998 assert(m_pImpl->aData.size() == static_cast<size_t>(m_xTabCtrl->get_n_pages()) 999 && "not all pages registered"); 1000 1001 // load old settings, when exists, setting SetCurPageId will override the settings, 1002 // something that the sort dialog in calc depends on 1003 if (m_sAppPageId.isEmpty()) 1004 { 1005 SvtViewOptions aDlgOpt(EViewType::TabDialog, OStringToOUString(m_xDialog->get_help_id(), RTL_TEXTENCODING_UTF8)); 1006 if (aDlgOpt.Exists()) 1007 m_xTabCtrl->set_current_page(aDlgOpt.GetPageID()); 1008 } 1009 1010 ActivatePageHdl(m_xTabCtrl->get_current_page_ident()); 1011 1012 m_pImpl->bStarted = true; 1013 } 1014 1015 void SfxTabDialogController::SetCurPageId(const OString& rIdent) 1016 { 1017 m_sAppPageId = rIdent; 1018 m_xTabCtrl->set_current_page(m_sAppPageId); 1019 } 1020 1021 /* [Description] 1022 1023 The TabPage is activated with the specified Id. 1024 */ 1025 void SfxTabDialogController::ShowPage(const OString& rIdent) 1026 { 1027 SetCurPageId(rIdent); 1028 ActivatePageHdl(rIdent); 1029 } 1030 1031 OString SfxTabDialogController::GetCurPageId() const 1032 { 1033 return m_xTabCtrl->get_current_page_ident(); 1034 } 1035 1036 short SfxTabDialogController::run() 1037 { 1038 Start_Impl(); 1039 return SfxDialogController::run(); 1040 } 1041 1042 bool SfxTabDialogController::runAsync(const std::shared_ptr<SfxTabDialogController>& rController, 1043 const std::function<void(sal_Int32)>& rFunc) 1044 { 1045 rController->Start_Impl(); 1046 return weld::DialogController::runAsync(rController, rFunc); 1047 } 1048 1049 void SfxTabDialogController::SetInputSet( const SfxItemSet* pInSet ) 1050 1051 /* [Description] 1052 1053 With this method the Input-Set can subsequently be set initially or re-set. 1054 */ 1055 1056 { 1057 bool bSet = ( m_pSet != nullptr ); 1058 m_pSet.reset(pInSet ? new SfxItemSet(*pInSet) : nullptr); 1059 1060 if (!bSet && !m_xExampleSet && !m_pOutSet && m_pSet) 1061 { 1062 m_xExampleSet.reset(new SfxItemSet(*m_pSet)); 1063 m_pOutSet.reset(new SfxItemSet( *m_pSet->GetPool(), m_pSet->GetRanges() )); 1064 } 1065 } 1066 1067 SfxItemSet* SfxTabDialogController::GetInputSetImpl() 1068 1069 /* [Description] 1070 1071 Derived classes may create new storage for the InputSet. This has to be 1072 released in the Destructor. To do this, this method must be called. 1073 */ 1074 1075 { 1076 return m_pSet.get(); 1077 } 1078 1079 void SfxTabDialogController::RemoveResetButton() 1080 { 1081 m_xResetBtn->hide(); 1082 m_pImpl->bHideResetBtn = true; 1083 } 1084 1085 void SfxTabDialogController::RemoveStandardButton() 1086 { 1087 m_xBaseFmtBtn->hide(); 1088 } 1089 1090 SfxTabPage* SfxTabDialogController::GetTabPage(const OString& rPageId) const 1091 1092 /* [Description] 1093 1094 Return TabPage with the specified Id. 1095 */ 1096 1097 { 1098 Data_Impl* pDataObject = Find(m_pImpl->aData, rPageId); 1099 if (pDataObject) 1100 return pDataObject->xTabPage.get(); 1101 return nullptr; 1102 } 1103 1104 void SfxTabDialogController::SetApplyHandler(const Link<weld::Button&, void>& _rHdl) 1105 { 1106 DBG_ASSERT( m_xApplyBtn, "SfxTabDialog::GetApplyHandler: no apply button enabled!" ); 1107 if (m_xApplyBtn) 1108 m_xApplyBtn->connect_clicked(_rHdl); 1109 } 1110 1111 bool SfxTabDialogController::Apply() 1112 { 1113 bool bApplied = false; 1114 if (PrepareLeaveCurrentPage()) 1115 { 1116 bApplied = (Ok() == RET_OK); 1117 //let the pages update their saved values 1118 GetInputSetImpl()->Put(*GetOutputItemSet()); 1119 for (auto pDataObject : m_pImpl->aData) 1120 { 1121 if (!pDataObject->xTabPage) 1122 continue; 1123 pDataObject->xTabPage->ChangesApplied(); 1124 } 1125 } 1126 return bApplied; 1127 } 1128 1129 std::vector<OString> SfxTabDialogController::getAllPageUIXMLDescriptions() const 1130 { 1131 int nPages = m_xTabCtrl->get_n_pages(); 1132 std::vector<OString> aRet; 1133 aRet.reserve(nPages); 1134 for (int i = 0; i < nPages; ++i) 1135 aRet.push_back(m_xTabCtrl->get_page_ident(i)); 1136 return aRet; 1137 } 1138 1139 bool SfxTabDialogController::selectPageByUIXMLDescription(const OString& rUIXMLDescription) 1140 { 1141 ShowPage(rUIXMLDescription); 1142 return m_xTabCtrl->get_current_page_ident() == rUIXMLDescription; 1143 } 1144 1145 BitmapEx SfxTabDialogController::createScreenshot() const 1146 { 1147 // if we haven't run Start_Impl yet, do so now to create the initial pages 1148 if (!m_pImpl->bStarted) 1149 { 1150 const_cast<SfxTabDialogController*>(this)->Start_Impl(); 1151 } 1152 1153 VclPtr<VirtualDevice> xDialogSurface(VclPtr<VirtualDevice>::Create(DeviceFormat::DEFAULT)); 1154 m_xDialog->draw(*xDialogSurface); 1155 return xDialogSurface->GetBitmapEx(Point(), xDialogSurface->GetOutputSizePixel()); 1156 } 1157 1158 OString SfxTabDialogController::GetScreenshotId() const 1159 { 1160 const OString sId = m_xTabCtrl->get_current_page_ident(); 1161 Data_Impl* pDataObject = Find(m_pImpl->aData, sId); 1162 SfxTabPage* pPage = pDataObject ? pDataObject->xTabPage.get() : nullptr; 1163 if (pPage) 1164 { 1165 OString sHelpId(pPage->GetHelpId()); 1166 if (!sHelpId.isEmpty()) 1167 return sHelpId; 1168 } 1169 return m_xDialog->get_help_id(); 1170 } 1171 1172 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ 1173
