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 #include <sal/config.h> 21 22 #include <o3tl/safeint.hxx> 23 #include <tools/debug.hxx> 24 #include <tools/stream.hxx> 25 #include <comphelper/base64.hxx> 26 #include <vcl/decoview.hxx> 27 #include <vcl/event.hxx> 28 #include <vcl/graph.hxx> 29 #include <vcl/svapp.hxx> 30 #include <vcl/scrbar.hxx> 31 #include <vcl/cvtgrf.hxx> 32 #include <vcl/help.hxx> 33 #include <vcl/settings.hxx> 34 #include <vcl/commandevent.hxx> 35 #include <vcl/virdev.hxx> 36 37 #include <com/sun/star/accessibility/AccessibleEventId.hpp> 38 #include <com/sun/star/lang/XComponent.hpp> 39 #include <rtl/ustring.hxx> 40 #include <sal/log.hxx> 41 #include "valueimp.hxx" 42 43 #include <svtools/valueset.hxx> 44 #include <boost/property_tree/ptree.hpp> 45 46 using namespace css::uno; 47 using namespace css::lang; 48 using namespace css::accessibility; 49 50 namespace 51 { 52 53 enum 54 { 55 ITEM_OFFSET = 4, 56 ITEM_OFFSET_DOUBLE = 6, 57 NAME_LINE_OFF_X = 2, 58 NAME_LINE_OFF_Y = 2, 59 NAME_LINE_HEIGHT = 2, 60 NAME_OFFSET = 2, 61 SCRBAR_OFFSET = 1 62 }; 63 64 } 65 66 ValueSet::ValueSet(std::unique_ptr<weld::ScrolledWindow> pScrolledWindow) 67 : maVirDev( VclPtr<VirtualDevice>::Create()) 68 , mxScrolledWindow(std::move(pScrolledWindow)) 69 , mnHighItemId(0) 70 , maColor(COL_TRANSPARENT) 71 , mnStyle(0) 72 , mbFormat(true) 73 , mbHighlight(false) 74 { 75 maVirDev->SetBackground(Application::GetSettings().GetStyleSettings().GetFaceColor()); 76 77 mnItemWidth = 0; 78 mnItemHeight = 0; 79 mnTextOffset = 0; 80 mnVisLines = 0; 81 mnLines = 0; 82 mnUserItemWidth = 0; 83 mnUserItemHeight = 0; 84 mnFirstLine = 0; 85 mnSelItemId = 0; 86 mnSavedItemId = -1; 87 mnCols = 0; 88 mnCurCol = 0; 89 mnUserCols = 0; 90 mnUserVisLines = 0; 91 mnSpacing = 0; 92 mnFrameStyle = DrawFrameStyle::NONE; 93 mbNoSelection = true; 94 mbDrawSelection = true; 95 mbBlackSel = false; 96 mbDoubleSel = false; 97 mbScroll = false; 98 mbFullMode = true; 99 mbEdgeBlending = false; 100 mbHasVisibleItems = false; 101 102 if (mxScrolledWindow) 103 { 104 mxScrolledWindow->set_user_managed_scrolling(); 105 mxScrolledWindow->connect_vadjustment_changed(LINK(this, ValueSet, ImplScrollHdl)); 106 } 107 } 108 109 void ValueSet::SetDrawingArea(weld::DrawingArea* pDrawingArea) 110 { 111 CustomWidgetController::SetDrawingArea(pDrawingArea); 112 // #106446#, #106601# force mirroring of virtual device 113 maVirDev->EnableRTL(pDrawingArea->get_direction()); 114 } 115 116 Reference<XAccessible> ValueSet::CreateAccessible() 117 { 118 if (!mxAccessible) 119 mxAccessible.set(new ValueSetAcc(this)); 120 return mxAccessible; 121 } 122 123 ValueSet::~ValueSet() 124 { 125 Reference<XComponent> xComponent(mxAccessible, UNO_QUERY); 126 if (xComponent.is()) 127 xComponent->dispose(); 128 129 ImplDeleteItems(); 130 } 131 132 void ValueSet::ImplDeleteItems() 133 { 134 const size_t n = mItemList.size(); 135 136 for ( size_t i = 0; i < n; ++i ) 137 { 138 ValueSetItem* pItem = mItemList[i].get(); 139 if ( pItem->mbVisible && ImplHasAccessibleListeners() ) 140 { 141 Any aOldAny; 142 Any aNewAny; 143 144 aOldAny <<= pItem->GetAccessible( false/*bIsTransientChildrenDisabled*/ ); 145 ImplFireAccessibleEvent(AccessibleEventId::CHILD, aOldAny, aNewAny); 146 } 147 148 mItemList[i].reset(); 149 } 150 151 mItemList.clear(); 152 } 153 154 void ValueSet::Select() 155 { 156 maSelectHdl.Call( this ); 157 } 158 159 void ValueSet::UserDraw( const UserDrawEvent& ) 160 { 161 } 162 163 size_t ValueSet::ImplGetItem( const Point& rPos ) const 164 { 165 if (!mbHasVisibleItems) 166 { 167 return VALUESET_ITEM_NOTFOUND; 168 } 169 170 if (mpNoneItem && maNoneItemRect.IsInside(rPos)) 171 { 172 return VALUESET_ITEM_NONEITEM; 173 } 174 175 if (maItemListRect.IsInside(rPos)) 176 { 177 const int xc = rPos.X() - maItemListRect.Left(); 178 const int yc = rPos.Y() - maItemListRect.Top(); 179 // The point is inside the area of item list, 180 // let's find the containing item. 181 const int col = xc / (mnItemWidth + mnSpacing); 182 const int x = xc % (mnItemWidth + mnSpacing); 183 const int row = yc / (mnItemHeight + mnSpacing); 184 const int y = yc % (mnItemHeight + mnSpacing); 185 186 if (x < mnItemWidth && y < mnItemHeight) 187 { 188 // the point is inside item rect and not inside spacing 189 const size_t item = (mnFirstLine + row) * static_cast<size_t>(mnCols) + col; 190 if (item < mItemList.size()) 191 { 192 return item; 193 } 194 } 195 } 196 197 return VALUESET_ITEM_NOTFOUND; 198 } 199 200 ValueSetItem* ValueSet::ImplGetItem( size_t nPos ) 201 { 202 if (nPos == VALUESET_ITEM_NONEITEM) 203 return mpNoneItem.get(); 204 else 205 return (nPos < mItemList.size()) ? mItemList[nPos].get() : nullptr; 206 } 207 208 ValueSetItem* ValueSet::ImplGetFirstItem() 209 { 210 return !mItemList.empty() ? mItemList[0].get() : nullptr; 211 } 212 213 sal_uInt16 ValueSet::ImplGetVisibleItemCount() const 214 { 215 sal_uInt16 nRet = 0; 216 const size_t nItemCount = mItemList.size(); 217 218 for ( size_t n = 0; n < nItemCount; ++n ) 219 { 220 if ( mItemList[n]->mbVisible ) 221 ++nRet; 222 } 223 224 return nRet; 225 } 226 227 void ValueSet::ImplFireAccessibleEvent( short nEventId, const Any& rOldValue, const Any& rNewValue ) 228 { 229 ValueSetAcc* pAcc = ValueSetAcc::getImplementation(mxAccessible); 230 231 if( pAcc ) 232 pAcc->FireAccessibleEvent( nEventId, rOldValue, rNewValue ); 233 } 234 235 bool ValueSet::ImplHasAccessibleListeners() 236 { 237 ValueSetAcc* pAcc = ValueSetAcc::getImplementation(mxAccessible); 238 return( pAcc && pAcc->HasAccessibleListeners() ); 239 } 240 241 IMPL_LINK(ValueSet, ImplScrollHdl, weld::ScrolledWindow&, rScrollWin, void) 242 { 243 auto nNewFirstLine = rScrollWin.vadjustment_get_value(); 244 if ( nNewFirstLine != mnFirstLine ) 245 { 246 mnFirstLine = nNewFirstLine; 247 mbFormat = true; 248 Invalidate(); 249 } 250 } 251 252 void ValueSet::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle&) 253 { 254 if (GetStyle() & WB_FLATVALUESET) 255 { 256 const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings(); 257 rRenderContext.SetLineColor(); 258 rRenderContext.SetFillColor(rStyleSettings.GetFaceColor()); 259 long nOffY = maVirDev->GetOutputSizePixel().Height(); 260 Size aWinSize(GetOutputSizePixel()); 261 rRenderContext.DrawRect(tools::Rectangle(Point(0, nOffY ), Point( aWinSize.Width(), aWinSize.Height()))); 262 } 263 264 ImplDraw(rRenderContext); 265 } 266 267 void ValueSet::GetFocus() 268 { 269 SAL_INFO("svtools", "value set getting focus"); 270 Invalidate(); 271 CustomWidgetController::GetFocus(); 272 273 // Tell the accessible object that we got the focus. 274 ValueSetAcc* pAcc = ValueSetAcc::getImplementation(mxAccessible); 275 if (pAcc) 276 pAcc->GetFocus(); 277 } 278 279 void ValueSet::LoseFocus() 280 { 281 SAL_INFO("svtools", "value set losing focus"); 282 Invalidate(); 283 CustomWidgetController::LoseFocus(); 284 285 // Tell the accessible object that we lost the focus. 286 ValueSetAcc* pAcc = ValueSetAcc::getImplementation(mxAccessible); 287 if( pAcc ) 288 pAcc->LoseFocus(); 289 } 290 291 void ValueSet::Resize() 292 { 293 mbFormat = true; 294 if ( IsReallyVisible() && IsUpdateMode() ) 295 Invalidate(); 296 CustomWidgetController::Resize(); 297 } 298 299 bool ValueSet::KeyInput( const KeyEvent& rKeyEvent ) 300 { 301 size_t nLastItem = mItemList.size(); 302 303 if ( !nLastItem || !ImplGetFirstItem() ) 304 return CustomWidgetController::KeyInput(rKeyEvent); 305 306 if (mbFormat) 307 Invalidate(); 308 309 --nLastItem; 310 311 const size_t nCurPos 312 = mnSelItemId ? GetItemPos(mnSelItemId) : (mpNoneItem ? VALUESET_ITEM_NONEITEM : 0); 313 size_t nItemPos = VALUESET_ITEM_NOTFOUND; 314 size_t nVStep = mnCols; 315 316 switch (rKeyEvent.GetKeyCode().GetCode()) 317 { 318 case KEY_HOME: 319 nItemPos = mpNoneItem ? VALUESET_ITEM_NONEITEM : 0; 320 break; 321 322 case KEY_END: 323 nItemPos = nLastItem; 324 break; 325 326 case KEY_LEFT: 327 if (nCurPos != VALUESET_ITEM_NONEITEM) 328 { 329 if (nCurPos) 330 { 331 nItemPos = nCurPos-1; 332 } 333 else if (mpNoneItem) 334 { 335 nItemPos = VALUESET_ITEM_NONEITEM; 336 } 337 } 338 break; 339 340 case KEY_RIGHT: 341 if (nCurPos < nLastItem) 342 { 343 if (nCurPos == VALUESET_ITEM_NONEITEM) 344 { 345 nItemPos = 0; 346 } 347 else 348 { 349 nItemPos = nCurPos+1; 350 } 351 } 352 break; 353 354 case KEY_PAGEUP: 355 if (rKeyEvent.GetKeyCode().IsShift() || rKeyEvent.GetKeyCode().IsMod1() || rKeyEvent.GetKeyCode().IsMod2()) 356 { 357 return CustomWidgetController::KeyInput(rKeyEvent); 358 } 359 nVStep *= mnVisLines; 360 [[fallthrough]]; 361 case KEY_UP: 362 if (nCurPos != VALUESET_ITEM_NONEITEM) 363 { 364 if (nCurPos == nLastItem) 365 { 366 const size_t nCol = mnCols ? nLastItem % mnCols : 0; 367 if (nCol < mnCurCol) 368 { 369 // Move to previous row/page, keeping the old column 370 nVStep -= mnCurCol - nCol; 371 } 372 } 373 if (nCurPos >= nVStep) 374 { 375 // Go up of a whole page 376 nItemPos = nCurPos-nVStep; 377 } 378 else if (mpNoneItem) 379 { 380 nItemPos = VALUESET_ITEM_NONEITEM; 381 } 382 else if (nCurPos > mnCols) 383 { 384 // Go to same column in first row 385 nItemPos = nCurPos % mnCols; 386 } 387 } 388 break; 389 390 case KEY_PAGEDOWN: 391 if (rKeyEvent.GetKeyCode().IsShift() || rKeyEvent.GetKeyCode().IsMod1() || rKeyEvent.GetKeyCode().IsMod2()) 392 { 393 return CustomWidgetController::KeyInput(rKeyEvent); 394 } 395 nVStep *= mnVisLines; 396 [[fallthrough]]; 397 case KEY_DOWN: 398 if (nCurPos != nLastItem) 399 { 400 if (nCurPos == VALUESET_ITEM_NONEITEM) 401 { 402 nItemPos = nVStep-mnCols+mnCurCol; 403 } 404 else 405 { 406 nItemPos = nCurPos+nVStep; 407 } 408 if (nItemPos > nLastItem) 409 { 410 nItemPos = nLastItem; 411 } 412 } 413 break; 414 415 case KEY_RETURN: 416 if (GetStyle() & WB_NO_DIRECTSELECT) 417 { 418 Select(); 419 break; 420 } 421 [[fallthrough]]; 422 default: 423 return CustomWidgetController::KeyInput(rKeyEvent); 424 } 425 426 if ( nItemPos == VALUESET_ITEM_NOTFOUND ) 427 return true; 428 429 if ( nItemPos!=VALUESET_ITEM_NONEITEM && nItemPos<nLastItem ) 430 { 431 // update current column only in case of a new position 432 // which is also not a "specially" handled one. 433 mnCurCol = mnCols ? nItemPos % mnCols : 0; 434 } 435 const sal_uInt16 nItemId = (nItemPos != VALUESET_ITEM_NONEITEM) ? GetItemId( nItemPos ) : 0; 436 if ( nItemId != mnSelItemId ) 437 { 438 SelectItem( nItemId ); 439 if (!(GetStyle() & WB_NO_DIRECTSELECT)) 440 { 441 // select only if WB_NO_DIRECTSELECT is not set 442 Select(); 443 } 444 } 445 446 return true; 447 } 448 449 void ValueSet::ImplTracking(const Point& rPos) 450 { 451 ValueSetItem* pItem = ImplGetItem( ImplGetItem( rPos ) ); 452 if ( pItem ) 453 { 454 if( GetStyle() & WB_MENUSTYLEVALUESET || GetStyle() & WB_FLATVALUESET ) 455 mbHighlight = true; 456 457 ImplHighlightItem( pItem->mnId ); 458 } 459 else 460 { 461 if( GetStyle() & WB_MENUSTYLEVALUESET || GetStyle() & WB_FLATVALUESET ) 462 mbHighlight = true; 463 464 ImplHighlightItem( mnSelItemId, false ); 465 } 466 } 467 468 bool ValueSet::MouseButtonDown( const MouseEvent& rMouseEvent ) 469 { 470 if ( rMouseEvent.IsLeft() ) 471 { 472 ValueSetItem* pItem = ImplGetItem( ImplGetItem( rMouseEvent.GetPosPixel() ) ); 473 if (pItem && !rMouseEvent.IsMod2()) 474 { 475 if (rMouseEvent.GetClicks() == 1) 476 { 477 SelectItem( pItem->mnId ); 478 if (!(GetStyle() & WB_NOPOINTERFOCUS)) 479 GrabFocus(); 480 } 481 else if ( rMouseEvent.GetClicks() == 2 ) 482 maDoubleClickHdl.Call( this ); 483 484 return true; 485 } 486 } 487 488 return CustomWidgetController::MouseButtonDown( rMouseEvent ); 489 } 490 491 bool ValueSet::MouseButtonUp( const MouseEvent& rMouseEvent ) 492 { 493 if (rMouseEvent.IsLeft() && !rMouseEvent.IsMod2()) 494 { 495 Select(); 496 return true; 497 } 498 499 return CustomWidgetController::MouseButtonUp( rMouseEvent ); 500 } 501 502 bool ValueSet::MouseMove(const MouseEvent& rMouseEvent) 503 { 504 // because of SelectionMode 505 if ((GetStyle() & WB_MENUSTYLEVALUESET) || (GetStyle() & WB_FLATVALUESET)) 506 ImplTracking(rMouseEvent.GetPosPixel()); 507 return CustomWidgetController::MouseMove(rMouseEvent); 508 } 509 510 void ValueSet::RemoveItem( sal_uInt16 nItemId ) 511 { 512 size_t nPos = GetItemPos( nItemId ); 513 514 if ( nPos == VALUESET_ITEM_NOTFOUND ) 515 return; 516 517 if ( nPos < mItemList.size() ) { 518 mItemList.erase( mItemList.begin() + nPos ); 519 } 520 521 // reset variables 522 if (mnHighItemId == nItemId || mnSelItemId == nItemId) 523 { 524 mnCurCol = 0; 525 mnHighItemId = 0; 526 mnSelItemId = 0; 527 mbNoSelection = true; 528 } 529 530 queue_resize(); 531 532 mbFormat = true; 533 if ( IsReallyVisible() && IsUpdateMode() ) 534 Invalidate(); 535 } 536 537 void ValueSet::RecalcScrollBar() 538 { 539 // reset scrolled window state to initial value 540 // so it will get configured to the right adjustment 541 WinBits nStyle = GetStyle(); 542 if (mxScrolledWindow && (nStyle & WB_VSCROLL)) 543 mxScrolledWindow->set_vpolicy(VclPolicyType::NEVER); 544 } 545 546 void ValueSet::Clear() 547 { 548 ImplDeleteItems(); 549 550 // reset variables 551 mnFirstLine = 0; 552 mnCurCol = 0; 553 mnHighItemId = 0; 554 mnSelItemId = 0; 555 mbNoSelection = true; 556 557 RecalcScrollBar(); 558 559 mbFormat = true; 560 if ( IsReallyVisible() && IsUpdateMode() ) 561 Invalidate(); 562 } 563 564 size_t ValueSet::GetItemCount() const 565 { 566 return mItemList.size(); 567 } 568 569 size_t ValueSet::GetItemPos( sal_uInt16 nItemId ) const 570 { 571 for ( size_t i = 0, n = mItemList.size(); i < n; ++i ) { 572 if ( mItemList[i]->mnId == nItemId ) { 573 return i; 574 } 575 } 576 return VALUESET_ITEM_NOTFOUND; 577 } 578 579 sal_uInt16 ValueSet::GetItemId( size_t nPos ) const 580 { 581 return ( nPos < mItemList.size() ) ? mItemList[nPos]->mnId : 0 ; 582 } 583 584 sal_uInt16 ValueSet::GetItemId( const Point& rPos ) const 585 { 586 size_t nItemPos = ImplGetItem( rPos ); 587 if ( nItemPos != VALUESET_ITEM_NOTFOUND ) 588 return GetItemId( nItemPos ); 589 590 return 0; 591 } 592 593 tools::Rectangle ValueSet::GetItemRect( sal_uInt16 nItemId ) const 594 { 595 const size_t nPos = GetItemPos( nItemId ); 596 597 if ( nPos!=VALUESET_ITEM_NOTFOUND && mItemList[nPos]->mbVisible ) 598 return ImplGetItemRect( nPos ); 599 600 return tools::Rectangle(); 601 } 602 603 tools::Rectangle ValueSet::ImplGetItemRect( size_t nPos ) const 604 { 605 const size_t nVisibleBegin = static_cast<size_t>(mnFirstLine)*mnCols; 606 const size_t nVisibleEnd = nVisibleBegin + static_cast<size_t>(mnVisLines)*mnCols; 607 608 // Check if the item is inside the range of the displayed ones, 609 // taking into account that last row could be incomplete 610 if ( nPos<nVisibleBegin || nPos>=nVisibleEnd || nPos>=mItemList.size() ) 611 return tools::Rectangle(); 612 613 nPos -= nVisibleBegin; 614 615 const size_t row = mnCols ? nPos/mnCols : 0; 616 const size_t col = mnCols ? nPos%mnCols : 0; 617 const long x = maItemListRect.Left()+col*(mnItemWidth+mnSpacing); 618 const long y = maItemListRect.Top()+row*(mnItemHeight+mnSpacing); 619 620 return tools::Rectangle( Point(x, y), Size(mnItemWidth, mnItemHeight) ); 621 } 622 623 void ValueSet::ImplHighlightItem( sal_uInt16 nItemId, bool bIsSelection ) 624 { 625 if ( mnHighItemId == nItemId ) 626 return; 627 628 // remember the old item to delete the previous selection 629 mnHighItemId = nItemId; 630 631 // don't draw the selection if nothing is selected 632 if ( !bIsSelection && mbNoSelection ) 633 mbDrawSelection = false; 634 635 // remove the old selection and draw the new one 636 Invalidate(); 637 mbDrawSelection = true; 638 } 639 640 void ValueSet::ImplDraw(vcl::RenderContext& rRenderContext) 641 { 642 if (mbFormat) 643 Format(rRenderContext); 644 645 Point aDefPos; 646 Size aSize = maVirDev->GetOutputSizePixel(); 647 648 rRenderContext.DrawOutDev(aDefPos, aSize, aDefPos, aSize, *maVirDev); 649 650 // draw parting line to the Namefield 651 if (GetStyle() & WB_NAMEFIELD) 652 { 653 if (!(GetStyle() & WB_FLATVALUESET)) 654 { 655 const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings(); 656 Size aWinSize(GetOutputSizePixel()); 657 Point aPos1(NAME_LINE_OFF_X, mnTextOffset + NAME_LINE_OFF_Y); 658 Point aPos2(aWinSize.Width() - (NAME_LINE_OFF_X * 2), mnTextOffset + NAME_LINE_OFF_Y); 659 if (!(rStyleSettings.GetOptions() & StyleSettingsOptions::Mono)) 660 { 661 rRenderContext.SetLineColor(rStyleSettings.GetShadowColor()); 662 rRenderContext.DrawLine(aPos1, aPos2); 663 aPos1.AdjustY( 1 ); 664 aPos2.AdjustY( 1 ); 665 rRenderContext.SetLineColor(rStyleSettings.GetLightColor()); 666 } 667 else 668 rRenderContext.SetLineColor(rStyleSettings.GetWindowTextColor()); 669 rRenderContext.DrawLine(aPos1, aPos2); 670 } 671 } 672 673 ImplDrawSelect(rRenderContext); 674 } 675 676 /** 677 * An inelegant method; sets the item width & height such that 678 * all of the included items and their labels fit; if we can 679 * calculate that. 680 */ 681 void ValueSet::RecalculateItemSizes() 682 { 683 Size aLargestItem = GetLargestItemSize(); 684 685 if ( mnUserItemWidth != aLargestItem.Width() || 686 mnUserItemHeight != aLargestItem.Height() ) 687 { 688 mnUserItemWidth = aLargestItem.Width(); 689 mnUserItemHeight = aLargestItem.Height(); 690 mbFormat = true; 691 queue_resize(); 692 if ( IsReallyVisible() && IsUpdateMode() ) 693 Invalidate(); 694 } 695 } 696 697 void ValueSet::SelectItem( sal_uInt16 nItemId ) 698 { 699 size_t nItemPos = 0; 700 701 if ( nItemId ) 702 { 703 nItemPos = GetItemPos( nItemId ); 704 if ( nItemPos == VALUESET_ITEM_NOTFOUND ) 705 return; 706 } 707 708 if ( !((mnSelItemId != nItemId) || mbNoSelection) ) 709 return; 710 711 const sal_uInt16 nOldItem = mnSelItemId; 712 mnSelItemId = nItemId; 713 mbNoSelection = false; 714 715 bool bNewOut = !mbFormat && IsReallyVisible() && IsUpdateMode(); 716 bool bNewLine = false; 717 718 if (weld::DrawingArea* pNeedsFormatToScroll = !mnCols ? GetDrawingArea() : nullptr) 719 { 720 Format(pNeedsFormatToScroll->get_ref_device()); 721 // reset scrollbar so its set to the later calculated mnFirstLine on 722 // the next Format 723 RecalcScrollBar(); // reset scrollbar so its set to the later calculated 724 } 725 726 // if necessary scroll to the visible area 727 if (mbScroll && nItemId && mnCols) 728 { 729 sal_uInt16 nNewLine = static_cast<sal_uInt16>(nItemPos / mnCols); 730 if ( nNewLine < mnFirstLine ) 731 { 732 mnFirstLine = nNewLine; 733 bNewLine = true; 734 } 735 else if ( nNewLine > o3tl::make_unsigned(mnFirstLine+mnVisLines-1) ) 736 { 737 mnFirstLine = static_cast<sal_uInt16>(nNewLine-mnVisLines+1); 738 bNewLine = true; 739 } 740 } 741 742 if ( bNewOut ) 743 { 744 if ( bNewLine ) 745 { 746 // redraw everything if the visible area has changed 747 mbFormat = true; 748 } 749 Invalidate(); 750 } 751 752 if( ImplHasAccessibleListeners() ) 753 { 754 // focus event (deselect) 755 if( nOldItem ) 756 { 757 const size_t nPos = GetItemPos( nItemId ); 758 759 if( nPos != VALUESET_ITEM_NOTFOUND ) 760 { 761 ValueItemAcc* pItemAcc = ValueItemAcc::getImplementation( 762 mItemList[nPos]->GetAccessible( false/*bIsTransientChildrenDisabled*/ ) ); 763 764 if( pItemAcc ) 765 { 766 Any aOldAny; 767 Any aNewAny; 768 aOldAny <<= Reference<XInterface>(static_cast<cppu::OWeakObject*>(pItemAcc)); 769 ImplFireAccessibleEvent(AccessibleEventId::ACTIVE_DESCENDANT_CHANGED, aOldAny, aNewAny ); 770 } 771 } 772 } 773 774 // focus event (select) 775 const size_t nPos = GetItemPos( mnSelItemId ); 776 777 ValueSetItem* pItem; 778 if( nPos != VALUESET_ITEM_NOTFOUND ) 779 pItem = mItemList[nPos].get(); 780 else 781 pItem = mpNoneItem.get(); 782 783 ValueItemAcc* pItemAcc = nullptr; 784 if (pItem != nullptr) 785 pItemAcc = ValueItemAcc::getImplementation( pItem->GetAccessible( false/*bIsTransientChildrenDisabled*/ ) ); 786 787 if( pItemAcc ) 788 { 789 Any aOldAny; 790 Any aNewAny; 791 aNewAny <<= Reference<XInterface>(static_cast<cppu::OWeakObject*>(pItemAcc)); 792 ImplFireAccessibleEvent(AccessibleEventId::ACTIVE_DESCENDANT_CHANGED, aOldAny, aNewAny); 793 } 794 795 // selection event 796 Any aOldAny; 797 Any aNewAny; 798 ImplFireAccessibleEvent(AccessibleEventId::SELECTION_CHANGED, aOldAny, aNewAny); 799 } 800 } 801 802 void ValueSet::SetNoSelection() 803 { 804 mbNoSelection = true; 805 mbHighlight = false; 806 807 if (IsReallyVisible() && IsUpdateMode()) 808 Invalidate(); 809 } 810 811 void ValueSet::SetStyle(WinBits nStyle) 812 { 813 if (nStyle != mnStyle) 814 { 815 mnStyle = nStyle; 816 mbFormat = true; 817 Invalidate(); 818 } 819 } 820 821 void ValueSet::Format(vcl::RenderContext const & rRenderContext) 822 { 823 Size aWinSize(GetOutputSizePixel()); 824 size_t nItemCount = mItemList.size(); 825 WinBits nStyle = GetStyle(); 826 long nTxtHeight = rRenderContext.GetTextHeight(); 827 long nOff; 828 long nNoneHeight; 829 long nNoneSpace; 830 831 if (mxScrolledWindow && !(nStyle & WB_VSCROLL) && mxScrolledWindow->get_vpolicy() != VclPolicyType::NEVER) 832 { 833 mxScrolledWindow->set_vpolicy(VclPolicyType::NEVER); 834 Size aPrefSize(GetDrawingArea()->get_preferred_size()); 835 GetDrawingArea()->set_size_request(aPrefSize.Width() + GetScrollWidth(), aPrefSize.Height()); 836 } 837 838 // calculate item offset 839 if (nStyle & WB_ITEMBORDER) 840 { 841 if (nStyle & WB_DOUBLEBORDER) 842 nOff = ITEM_OFFSET_DOUBLE; 843 else 844 nOff = ITEM_OFFSET; 845 } 846 else 847 nOff = 0; 848 849 // consider size, if NameField does exist 850 if (nStyle & WB_NAMEFIELD) 851 { 852 mnTextOffset = aWinSize.Height() - nTxtHeight - NAME_OFFSET; 853 aWinSize.AdjustHeight( -(nTxtHeight + NAME_OFFSET) ); 854 855 if (!(nStyle & WB_FLATVALUESET)) 856 { 857 mnTextOffset -= NAME_LINE_HEIGHT + NAME_LINE_OFF_Y; 858 aWinSize.AdjustHeight( -(NAME_LINE_HEIGHT + NAME_LINE_OFF_Y) ); 859 } 860 } 861 else 862 mnTextOffset = 0; 863 864 // consider offset and size, if NoneField does exist 865 if (nStyle & WB_NONEFIELD) 866 { 867 nNoneHeight = nTxtHeight + nOff; 868 nNoneSpace = mnSpacing; 869 } 870 else 871 { 872 nNoneHeight = 0; 873 nNoneSpace = 0; 874 mpNoneItem.reset(); 875 } 876 877 // calculate number of columns 878 if (!mnUserCols) 879 { 880 if (mnUserItemWidth) 881 { 882 mnCols = static_cast<sal_uInt16>((aWinSize.Width() - mnSpacing) / (mnUserItemWidth + mnSpacing)); 883 if (mnCols <= 0) 884 mnCols = 1; 885 } 886 else 887 { 888 mnCols = 1; 889 } 890 } 891 else 892 { 893 mnCols = mnUserCols; 894 } 895 896 // calculate number of rows 897 mbScroll = false; 898 899 // Floor( (M+N-1)/N )==Ceiling( M/N ) 900 mnLines = (static_cast<long>(nItemCount) + mnCols - 1) / mnCols; 901 if (mnLines <= 0) 902 mnLines = 1; 903 904 long nCalcHeight = aWinSize.Height() - nNoneHeight; 905 if (mnUserVisLines) 906 { 907 mnVisLines = mnUserVisLines; 908 } 909 else if (mnUserItemHeight) 910 { 911 mnVisLines = (nCalcHeight - nNoneSpace + mnSpacing) / (mnUserItemHeight + mnSpacing); 912 if (!mnVisLines) 913 mnVisLines = 1; 914 } 915 else 916 { 917 mnVisLines = mnLines; 918 } 919 920 if (mnLines > mnVisLines) 921 mbScroll = true; 922 923 if (mnLines <= mnVisLines) 924 { 925 mnFirstLine = 0; 926 } 927 else 928 { 929 if (mnFirstLine > o3tl::make_unsigned(mnLines - mnVisLines)) 930 mnFirstLine = static_cast<sal_uInt16>(mnLines - mnVisLines); 931 } 932 933 // calculate item size 934 const long nColSpace = (mnCols - 1) * static_cast<long>(mnSpacing); 935 const long nLineSpace = ((mnVisLines - 1) * mnSpacing) + nNoneSpace; 936 if (mnUserItemWidth && !mnUserCols) 937 { 938 mnItemWidth = mnUserItemWidth; 939 if (mnItemWidth > aWinSize.Width() - nColSpace) 940 mnItemWidth = aWinSize.Width() - nColSpace; 941 } 942 else 943 mnItemWidth = (aWinSize.Width() - nColSpace) / mnCols; 944 if (mnUserItemHeight && !mnUserVisLines) 945 { 946 mnItemHeight = mnUserItemHeight; 947 if (mnItemHeight > nCalcHeight - nNoneSpace) 948 mnItemHeight = nCalcHeight - nNoneSpace; 949 } 950 else 951 { 952 nCalcHeight -= nLineSpace; 953 mnItemHeight = nCalcHeight / mnVisLines; 954 } 955 956 // Init VirDev 957 maVirDev->SetSettings(rRenderContext.GetSettings()); 958 maVirDev->SetOutputSizePixel(aWinSize); 959 960 // nothing is changed in case of too small items 961 if ((mnItemWidth <= 0) || 962 (mnItemHeight <= ((nStyle & WB_ITEMBORDER) ? 4 : 2)) || 963 !nItemCount) 964 { 965 mbHasVisibleItems = false; 966 967 if ((nStyle & WB_NONEFIELD) && mpNoneItem) 968 { 969 mpNoneItem->mbVisible = false; 970 mpNoneItem->maText = GetText(); 971 } 972 973 for (size_t i = 0; i < nItemCount; i++) 974 { 975 mItemList[i]->mbVisible = false; 976 } 977 978 if (mxScrolledWindow && mxScrolledWindow->get_vpolicy() != VclPolicyType::NEVER) 979 { 980 mxScrolledWindow->set_vpolicy(VclPolicyType::NEVER); 981 Size aPrefSize(GetDrawingArea()->get_preferred_size()); 982 GetDrawingArea()->set_size_request(aPrefSize.Width() + GetScrollWidth(), aPrefSize.Height()); 983 } 984 } 985 else 986 { 987 mbHasVisibleItems = true; 988 989 // determine Frame-Style 990 if (nStyle & WB_DOUBLEBORDER) 991 mnFrameStyle = DrawFrameStyle::DoubleIn; 992 else 993 mnFrameStyle = DrawFrameStyle::In; 994 995 // determine selected color and width 996 // if necessary change the colors, to make the selection 997 // better detectable 998 const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings(); 999 Color aHighColor(rStyleSettings.GetHighlightColor()); 1000 if (((aHighColor.GetRed() > 0x80) || (aHighColor.GetGreen() > 0x80) || 1001 (aHighColor.GetBlue() > 0x80)) || 1002 ((aHighColor.GetRed() == 0x80) && (aHighColor.GetGreen() == 0x80) && 1003 (aHighColor.GetBlue() == 0x80))) 1004 { 1005 mbBlackSel = true; 1006 } 1007 else 1008 { 1009 mbBlackSel = false; 1010 } 1011 // draw the selection with double width if the items are bigger 1012 if ((nStyle & WB_DOUBLEBORDER) && 1013 ((mnItemWidth >= 25) && (mnItemHeight >= 20))) 1014 { 1015 mbDoubleSel = true; 1016 } 1017 else 1018 { 1019 mbDoubleSel = false; 1020 } 1021 1022 // calculate offsets 1023 long nStartX; 1024 long nStartY; 1025 if (mbFullMode) 1026 { 1027 long nAllItemWidth = (mnItemWidth * mnCols) + nColSpace; 1028 long nAllItemHeight = (mnItemHeight * mnVisLines) + nNoneHeight + nLineSpace; 1029 nStartX = (aWinSize.Width() - nAllItemWidth) / 2; 1030 nStartY = (aWinSize.Height() - nAllItemHeight) / 2; 1031 } 1032 else 1033 { 1034 nStartX = 0; 1035 nStartY = 0; 1036 } 1037 1038 // calculate and draw items 1039 maVirDev->SetLineColor(); 1040 long x = nStartX; 1041 long y = nStartY; 1042 1043 // create NoSelection field and show it 1044 if (nStyle & WB_NONEFIELD) 1045 { 1046 if (!mpNoneItem) 1047 mpNoneItem.reset(new ValueSetItem(*this)); 1048 1049 mpNoneItem->mnId = 0; 1050 mpNoneItem->meType = VALUESETITEM_NONE; 1051 mpNoneItem->mbVisible = true; 1052 maNoneItemRect.SetLeft( x ); 1053 maNoneItemRect.SetTop( y ); 1054 maNoneItemRect.SetRight( maNoneItemRect.Left() + aWinSize.Width() - x - 1 ); 1055 maNoneItemRect.SetBottom( y + nNoneHeight - 1 ); 1056 1057 ImplFormatItem(rRenderContext, mpNoneItem.get(), maNoneItemRect); 1058 1059 y += nNoneHeight + nNoneSpace; 1060 } 1061 1062 // draw items 1063 sal_uLong nFirstItem = static_cast<sal_uLong>(mnFirstLine) * mnCols; 1064 sal_uLong nLastItem = nFirstItem + (mnVisLines * mnCols); 1065 1066 maItemListRect.SetLeft( x ); 1067 maItemListRect.SetTop( y ); 1068 maItemListRect.SetRight( x + mnCols * (mnItemWidth + mnSpacing) - mnSpacing - 1 ); 1069 maItemListRect.SetBottom( y + mnVisLines * (mnItemHeight + mnSpacing) - mnSpacing - 1 ); 1070 1071 if (!mbFullMode) 1072 { 1073 // If want also draw parts of items in the last line, 1074 // then we add one more line if parts of these line are 1075 // visible 1076 if (y + (mnVisLines * (mnItemHeight + mnSpacing)) < aWinSize.Height()) 1077 nLastItem += mnCols; 1078 maItemListRect.SetBottom( aWinSize.Height() - y ); 1079 } 1080 for (size_t i = 0; i < nItemCount; i++) 1081 { 1082 ValueSetItem* pItem = mItemList[i].get(); 1083 1084 if (i >= nFirstItem && i < nLastItem) 1085 { 1086 if (!pItem->mbVisible && ImplHasAccessibleListeners()) 1087 { 1088 Any aOldAny; 1089 Any aNewAny; 1090 1091 aNewAny <<= pItem->GetAccessible(false/*bIsTransientChildrenDisabled*/); 1092 ImplFireAccessibleEvent(AccessibleEventId::CHILD, aOldAny, aNewAny); 1093 } 1094 1095 pItem->mbVisible = true; 1096 ImplFormatItem(rRenderContext, pItem, tools::Rectangle(Point(x, y), Size(mnItemWidth, mnItemHeight))); 1097 1098 if (!((i + 1) % mnCols)) 1099 { 1100 x = nStartX; 1101 y += mnItemHeight + mnSpacing; 1102 } 1103 else 1104 x += mnItemWidth + mnSpacing; 1105 } 1106 else 1107 { 1108 if (pItem->mbVisible && ImplHasAccessibleListeners()) 1109 { 1110 Any aOldAny; 1111 Any aNewAny; 1112 1113 aOldAny <<= pItem->GetAccessible(false/*bIsTransientChildrenDisabled*/); 1114 ImplFireAccessibleEvent(AccessibleEventId::CHILD, aOldAny, aNewAny); 1115 } 1116 1117 pItem->mbVisible = false; 1118 } 1119 } 1120 1121 // arrange ScrollBar, set values and show it 1122 if (mxScrolledWindow && (nStyle & WB_VSCROLL) && mxScrolledWindow->get_vpolicy() != VclPolicyType::ALWAYS) 1123 { 1124 long nPageSize = mnVisLines; 1125 if (nPageSize < 1) 1126 nPageSize = 1; 1127 mxScrolledWindow->vadjustment_configure(mnFirstLine, 0, mnLines, 1, 1128 mnVisLines, nPageSize); 1129 mxScrolledWindow->set_vpolicy(VclPolicyType::ALWAYS); 1130 Size aPrefSize(GetDrawingArea()->get_preferred_size()); 1131 GetDrawingArea()->set_size_request(aPrefSize.Width() - GetScrollWidth(), aPrefSize.Height()); 1132 } 1133 } 1134 1135 // waiting for the next since the formatting is finished 1136 mbFormat = false; 1137 } 1138 1139 void ValueSet::ImplDrawSelect(vcl::RenderContext& rRenderContext) 1140 { 1141 if (!IsReallyVisible()) 1142 return; 1143 1144 const bool bFocus = HasFocus(); 1145 const bool bDrawSel = !((mbNoSelection && !mbHighlight) || (!mbDrawSelection && mbHighlight)); 1146 1147 if (!bFocus && !bDrawSel) 1148 { 1149 ImplDrawItemText(rRenderContext, OUString()); 1150 return; 1151 } 1152 1153 ImplDrawSelect(rRenderContext, mnSelItemId, bFocus, bDrawSel); 1154 if (mbHighlight) 1155 { 1156 ImplDrawSelect(rRenderContext, mnHighItemId, bFocus, bDrawSel); 1157 } 1158 } 1159 1160 void ValueSet::ImplDrawSelect(vcl::RenderContext& rRenderContext, sal_uInt16 nItemId, const bool bFocus, const bool bDrawSel ) 1161 { 1162 ValueSetItem* pItem; 1163 tools::Rectangle aRect; 1164 if (nItemId) 1165 { 1166 const size_t nPos = GetItemPos( nItemId ); 1167 pItem = mItemList[ nPos ].get(); 1168 aRect = ImplGetItemRect( nPos ); 1169 } 1170 else if (mpNoneItem) 1171 { 1172 pItem = mpNoneItem.get(); 1173 aRect = maNoneItemRect; 1174 } 1175 else if (bFocus && (pItem = ImplGetFirstItem())) 1176 { 1177 aRect = ImplGetItemRect(0); 1178 } 1179 else 1180 { 1181 return; 1182 } 1183 1184 if (!pItem->mbVisible) 1185 return; 1186 1187 // draw selection 1188 const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings(); 1189 rRenderContext.SetFillColor(); 1190 1191 Color aDoubleColor(rStyleSettings.GetHighlightColor()); 1192 Color aSingleColor(rStyleSettings.GetHighlightTextColor()); 1193 if (!mbDoubleSel) 1194 { 1195 /* 1196 * #99777# contrast enhancement for thin mode 1197 */ 1198 const Wallpaper& rWall = maVirDev->GetBackground(); 1199 if (!rWall.IsBitmap() && ! rWall.IsGradient()) 1200 { 1201 const Color& rBack = rWall.GetColor(); 1202 if (rBack.IsDark() && ! aDoubleColor.IsBright()) 1203 { 1204 aDoubleColor = COL_WHITE; 1205 aSingleColor = COL_BLACK; 1206 } 1207 else if (rBack.IsBright() && !aDoubleColor.IsDark()) 1208 { 1209 aDoubleColor = COL_BLACK; 1210 aSingleColor = COL_WHITE; 1211 } 1212 } 1213 } 1214 1215 // specify selection output 1216 WinBits nStyle = GetStyle(); 1217 if (nStyle & WB_MENUSTYLEVALUESET) 1218 { 1219 if (bFocus) 1220 DrawFocusRect(rRenderContext, aRect); 1221 if (bDrawSel) 1222 { 1223 rRenderContext.SetLineColor(mbBlackSel ? COL_BLACK : aDoubleColor); 1224 rRenderContext.DrawRect(aRect); 1225 } 1226 } 1227 else 1228 { 1229 if (bDrawSel) 1230 { 1231 rRenderContext.SetLineColor(mbBlackSel ? COL_BLACK : aDoubleColor); 1232 rRenderContext.DrawRect(aRect); 1233 } 1234 if (mbDoubleSel) 1235 { 1236 aRect.AdjustLeft( 1 ); 1237 aRect.AdjustTop( 1 ); 1238 aRect.AdjustRight( -1 ); 1239 aRect.AdjustBottom( -1 ); 1240 if (bDrawSel) 1241 rRenderContext.DrawRect(aRect); 1242 } 1243 aRect.AdjustLeft( 1 ); 1244 aRect.AdjustTop( 1 ); 1245 aRect.AdjustRight( -1 ); 1246 aRect.AdjustBottom( -1 ); 1247 tools::Rectangle aRect2 = aRect; 1248 aRect.AdjustLeft( 1 ); 1249 aRect.AdjustTop( 1 ); 1250 aRect.AdjustRight( -1 ); 1251 aRect.AdjustBottom( -1 ); 1252 if (bDrawSel) 1253 rRenderContext.DrawRect(aRect); 1254 if (mbDoubleSel) 1255 { 1256 aRect.AdjustLeft( 1 ); 1257 aRect.AdjustTop( 1 ); 1258 aRect.AdjustRight( -1 ); 1259 aRect.AdjustBottom( -1 ); 1260 if (bDrawSel) 1261 rRenderContext.DrawRect(aRect); 1262 } 1263 1264 if (bDrawSel) 1265 { 1266 rRenderContext.SetLineColor(mbBlackSel ? COL_WHITE : aSingleColor); 1267 } 1268 else 1269 { 1270 rRenderContext.SetLineColor(COL_LIGHTGRAY); 1271 } 1272 rRenderContext.DrawRect(aRect2); 1273 if (bFocus) 1274 DrawFocusRect(rRenderContext, aRect2); 1275 } 1276 1277 ImplDrawItemText(rRenderContext, pItem->maText); 1278 } 1279 1280 void ValueSet::ImplFormatItem(vcl::RenderContext const & rRenderContext, ValueSetItem* pItem, tools::Rectangle aRect) 1281 { 1282 WinBits nStyle = GetStyle(); 1283 if (nStyle & WB_ITEMBORDER) 1284 { 1285 aRect.AdjustLeft(1 ); 1286 aRect.AdjustTop(1 ); 1287 aRect.AdjustRight( -1 ); 1288 aRect.AdjustBottom( -1 ); 1289 1290 if (nStyle & WB_FLATVALUESET) 1291 { 1292 sal_Int32 nBorder = (nStyle & WB_DOUBLEBORDER) ? 2 : 1; 1293 1294 aRect.AdjustLeft(nBorder ); 1295 aRect.AdjustTop(nBorder ); 1296 aRect.AdjustRight( -nBorder ); 1297 aRect.AdjustBottom( -nBorder ); 1298 } 1299 else 1300 { 1301 DecorationView aView(maVirDev.get()); 1302 aRect = aView.DrawFrame(aRect, mnFrameStyle); 1303 } 1304 } 1305 1306 if (pItem == mpNoneItem.get()) 1307 pItem->maText = GetText(); 1308 1309 if ((aRect.GetHeight() <= 0) || (aRect.GetWidth() <= 0)) 1310 return; 1311 1312 const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings(); 1313 1314 if (pItem == mpNoneItem.get()) 1315 { 1316 maVirDev->SetFont(rRenderContext.GetFont()); 1317 maVirDev->SetTextColor((nStyle & WB_MENUSTYLEVALUESET) ? rStyleSettings.GetMenuTextColor() : rStyleSettings.GetWindowTextColor()); 1318 maVirDev->SetTextFillColor(); 1319 maVirDev->SetFillColor((nStyle & WB_MENUSTYLEVALUESET) ? rStyleSettings.GetMenuColor() : rStyleSettings.GetWindowColor()); 1320 maVirDev->DrawRect(aRect); 1321 Point aTxtPos(aRect.Left() + 2, aRect.Top()); 1322 long nTxtWidth = rRenderContext.GetTextWidth(pItem->maText); 1323 if ((aTxtPos.X() + nTxtWidth) > aRect.Right()) 1324 { 1325 maVirDev->SetClipRegion(vcl::Region(aRect)); 1326 maVirDev->DrawText(aTxtPos, pItem->maText); 1327 maVirDev->SetClipRegion(); 1328 } 1329 else 1330 maVirDev->DrawText(aTxtPos, pItem->maText); 1331 } 1332 else if (pItem->meType == VALUESETITEM_COLOR) 1333 { 1334 maVirDev->SetFillColor(pItem->maColor); 1335 maVirDev->DrawRect(aRect); 1336 } 1337 else 1338 { 1339 if (IsColor()) 1340 maVirDev->SetFillColor(maColor); 1341 else if (nStyle & WB_MENUSTYLEVALUESET) 1342 maVirDev->SetFillColor(rStyleSettings.GetMenuColor()); 1343 else if (IsEnabled()) 1344 maVirDev->SetFillColor(rStyleSettings.GetWindowColor()); 1345 else 1346 maVirDev->SetFillColor(rStyleSettings.GetFaceColor()); 1347 maVirDev->DrawRect(aRect); 1348 1349 if (pItem->meType == VALUESETITEM_USERDRAW) 1350 { 1351 UserDrawEvent aUDEvt(nullptr, maVirDev.get(), aRect, pItem->mnId); 1352 UserDraw(aUDEvt); 1353 } 1354 else 1355 { 1356 Size aImageSize = pItem->maImage.GetSizePixel(); 1357 Size aRectSize = aRect.GetSize(); 1358 Point aPos(aRect.Left(), aRect.Top()); 1359 aPos.AdjustX((aRectSize.Width() - aImageSize.Width()) / 2 ); 1360 1361 if (pItem->meType != VALUESETITEM_IMAGE_AND_TEXT) 1362 aPos.AdjustY((aRectSize.Height() - aImageSize.Height()) / 2 ); 1363 1364 DrawImageFlags nImageStyle = DrawImageFlags::NONE; 1365 if (!IsEnabled()) 1366 nImageStyle |= DrawImageFlags::Disable; 1367 1368 if (aImageSize.Width() > aRectSize.Width() || 1369 aImageSize.Height() > aRectSize.Height()) 1370 { 1371 maVirDev->SetClipRegion(vcl::Region(aRect)); 1372 maVirDev->DrawImage(aPos, pItem->maImage, nImageStyle); 1373 maVirDev->SetClipRegion(); 1374 } 1375 else 1376 maVirDev->DrawImage(aPos, pItem->maImage, nImageStyle); 1377 1378 if (pItem->meType == VALUESETITEM_IMAGE_AND_TEXT) 1379 { 1380 maVirDev->SetFont(rRenderContext.GetFont()); 1381 maVirDev->SetTextColor((nStyle & WB_MENUSTYLEVALUESET) ? rStyleSettings.GetMenuTextColor() : rStyleSettings.GetWindowTextColor()); 1382 maVirDev->SetTextFillColor(); 1383 1384 long nTxtWidth = maVirDev->GetTextWidth(pItem->maText); 1385 1386 if (nTxtWidth > aRect.GetWidth()) 1387 maVirDev->SetClipRegion(vcl::Region(aRect)); 1388 1389 maVirDev->DrawText(Point(aRect.Left() + 1390 (aRect.GetWidth() - nTxtWidth) / 2, 1391 aRect.Bottom() - maVirDev->GetTextHeight()), 1392 pItem->maText); 1393 1394 if (nTxtWidth > aRect.GetWidth()) 1395 maVirDev->SetClipRegion(); 1396 } 1397 } 1398 } 1399 1400 const sal_uInt16 nEdgeBlendingPercent(GetEdgeBlending() ? rStyleSettings.GetEdgeBlending() : 0); 1401 1402 if (nEdgeBlendingPercent) 1403 { 1404 const Color& rTopLeft(rStyleSettings.GetEdgeBlendingTopLeftColor()); 1405 const Color& rBottomRight(rStyleSettings.GetEdgeBlendingBottomRightColor()); 1406 const sal_uInt8 nAlpha((nEdgeBlendingPercent * 255) / 100); 1407 const BitmapEx aBlendFrame(createBlendFrame(aRect.GetSize(), nAlpha, rTopLeft, rBottomRight)); 1408 1409 if (!aBlendFrame.IsEmpty()) 1410 { 1411 maVirDev->DrawBitmapEx(aRect.TopLeft(), aBlendFrame); 1412 } 1413 } 1414 } 1415 1416 void ValueSet::ImplDrawItemText(vcl::RenderContext& rRenderContext, const OUString& rText) 1417 { 1418 if (!(GetStyle() & WB_NAMEFIELD)) 1419 return; 1420 1421 Size aWinSize(GetOutputSizePixel()); 1422 long nTxtWidth = rRenderContext.GetTextWidth(rText); 1423 long nTxtOffset = mnTextOffset; 1424 1425 // delete rectangle and show text 1426 if (GetStyle() & WB_FLATVALUESET) 1427 { 1428 const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings(); 1429 rRenderContext.SetLineColor(); 1430 rRenderContext.SetFillColor(rStyleSettings.GetFaceColor()); 1431 rRenderContext.DrawRect(tools::Rectangle(Point(0, nTxtOffset), Point(aWinSize.Width(), aWinSize.Height()))); 1432 rRenderContext.SetTextColor(rStyleSettings.GetButtonTextColor()); 1433 } 1434 else 1435 { 1436 nTxtOffset += NAME_LINE_HEIGHT+NAME_LINE_OFF_Y; 1437 rRenderContext.SetBackground(Application::GetSettings().GetStyleSettings().GetFaceColor()); 1438 rRenderContext.Erase(tools::Rectangle(Point(0, nTxtOffset), Point(aWinSize.Width(), aWinSize.Height()))); 1439 } 1440 rRenderContext.DrawText(Point((aWinSize.Width() - nTxtWidth) / 2, nTxtOffset + (NAME_OFFSET / 2)), rText); 1441 } 1442 1443 void ValueSet::StyleUpdated() 1444 { 1445 mbFormat = true; 1446 CustomWidgetController::StyleUpdated(); 1447 } 1448 1449 void ValueSet::EnableFullItemMode( bool bFullMode ) 1450 { 1451 mbFullMode = bFullMode; 1452 } 1453 1454 void ValueSet::SetColCount( sal_uInt16 nNewCols ) 1455 { 1456 if ( mnUserCols != nNewCols ) 1457 { 1458 mnUserCols = nNewCols; 1459 mbFormat = true; 1460 queue_resize(); 1461 if (IsReallyVisible() && IsUpdateMode()) 1462 Invalidate(); 1463 } 1464 } 1465 1466 void ValueSet::SetItemImage( sal_uInt16 nItemId, const Image& rImage ) 1467 { 1468 size_t nPos = GetItemPos( nItemId ); 1469 1470 if ( nPos == VALUESET_ITEM_NOTFOUND ) 1471 return; 1472 1473 ValueSetItem* pItem = mItemList[nPos].get(); 1474 pItem->meType = VALUESETITEM_IMAGE; 1475 pItem->maImage = rImage; 1476 1477 if ( !mbFormat && IsReallyVisible() && IsUpdateMode() ) 1478 { 1479 const tools::Rectangle aRect = ImplGetItemRect(nPos); 1480 Invalidate(aRect); 1481 } 1482 else 1483 mbFormat = true; 1484 } 1485 1486 void ValueSet::SetItemColor( sal_uInt16 nItemId, const Color& rColor ) 1487 { 1488 size_t nPos = GetItemPos( nItemId ); 1489 1490 if ( nPos == VALUESET_ITEM_NOTFOUND ) 1491 return; 1492 1493 ValueSetItem* pItem = mItemList[nPos].get(); 1494 pItem->meType = VALUESETITEM_COLOR; 1495 pItem->maColor = rColor; 1496 1497 if ( !mbFormat && IsReallyVisible() && IsUpdateMode() ) 1498 { 1499 const tools::Rectangle aRect = ImplGetItemRect(nPos); 1500 Invalidate( aRect ); 1501 } 1502 else 1503 mbFormat = true; 1504 } 1505 1506 Color ValueSet::GetItemColor( sal_uInt16 nItemId ) const 1507 { 1508 size_t nPos = GetItemPos( nItemId ); 1509 1510 if ( nPos != VALUESET_ITEM_NOTFOUND ) 1511 return mItemList[nPos]->maColor; 1512 else 1513 return Color(); 1514 } 1515 1516 Size ValueSet::CalcWindowSizePixel( const Size& rItemSize, sal_uInt16 nDesireCols, 1517 sal_uInt16 nDesireLines ) const 1518 { 1519 size_t nCalcCols = nDesireCols; 1520 size_t nCalcLines = nDesireLines; 1521 1522 if ( !nCalcCols ) 1523 { 1524 if ( mnUserCols ) 1525 nCalcCols = mnUserCols; 1526 else 1527 nCalcCols = 1; 1528 } 1529 1530 if ( !nCalcLines ) 1531 { 1532 nCalcLines = mnVisLines; 1533 1534 if ( mbFormat ) 1535 { 1536 if ( mnUserVisLines ) 1537 nCalcLines = mnUserVisLines; 1538 else 1539 { 1540 // Floor( (M+N-1)/N )==Ceiling( M/N ) 1541 nCalcLines = (mItemList.size()+nCalcCols-1) / nCalcCols; 1542 if ( !nCalcLines ) 1543 nCalcLines = 1; 1544 } 1545 } 1546 } 1547 1548 Size aSize( rItemSize.Width() * nCalcCols, rItemSize.Height() * nCalcLines ); 1549 WinBits nStyle = GetStyle(); 1550 long nTxtHeight = GetTextHeight(); 1551 long n; 1552 1553 if ( nStyle & WB_ITEMBORDER ) 1554 { 1555 if ( nStyle & WB_DOUBLEBORDER ) 1556 n = ITEM_OFFSET_DOUBLE; 1557 else 1558 n = ITEM_OFFSET; 1559 1560 aSize.AdjustWidth(n * nCalcCols ); 1561 aSize.AdjustHeight(n * nCalcLines ); 1562 } 1563 else 1564 n = 0; 1565 1566 if ( mnSpacing ) 1567 { 1568 aSize.AdjustWidth(mnSpacing * (nCalcCols - 1) ); 1569 aSize.AdjustHeight(mnSpacing * (nCalcLines - 1) ); 1570 } 1571 1572 if ( nStyle & WB_NAMEFIELD ) 1573 { 1574 aSize.AdjustHeight(nTxtHeight + NAME_OFFSET ); 1575 if ( !(nStyle & WB_FLATVALUESET) ) 1576 aSize.AdjustHeight(NAME_LINE_HEIGHT + NAME_LINE_OFF_Y ); 1577 } 1578 1579 if ( nStyle & WB_NONEFIELD ) 1580 { 1581 aSize.AdjustHeight(nTxtHeight + n + mnSpacing ); 1582 } 1583 1584 return aSize; 1585 } 1586 1587 void ValueSet::InsertItem( sal_uInt16 nItemId, const Image& rImage ) 1588 { 1589 std::unique_ptr<ValueSetItem> pItem(new ValueSetItem( *this )); 1590 pItem->mnId = nItemId; 1591 pItem->meType = VALUESETITEM_IMAGE; 1592 pItem->maImage = rImage; 1593 ImplInsertItem( std::move(pItem), VALUESET_APPEND ); 1594 } 1595 1596 void ValueSet::InsertItem( sal_uInt16 nItemId, const Image& rImage, 1597 const OUString& rText, size_t nPos, 1598 bool bShowLegend ) 1599 { 1600 std::unique_ptr<ValueSetItem> pItem(new ValueSetItem( *this )); 1601 pItem->mnId = nItemId; 1602 pItem->meType = bShowLegend ? VALUESETITEM_IMAGE_AND_TEXT : VALUESETITEM_IMAGE; 1603 pItem->maImage = rImage; 1604 pItem->maText = rText; 1605 ImplInsertItem( std::move(pItem), nPos ); 1606 } 1607 1608 void ValueSet::InsertItem( sal_uInt16 nItemId, size_t nPos ) 1609 { 1610 std::unique_ptr<ValueSetItem> pItem(new ValueSetItem( *this )); 1611 pItem->mnId = nItemId; 1612 pItem->meType = VALUESETITEM_USERDRAW; 1613 ImplInsertItem( std::move(pItem), nPos ); 1614 } 1615 1616 void ValueSet::InsertItem( sal_uInt16 nItemId, const Color& rColor, 1617 const OUString& rText ) 1618 { 1619 std::unique_ptr<ValueSetItem> pItem(new ValueSetItem( *this )); 1620 pItem->mnId = nItemId; 1621 pItem->meType = VALUESETITEM_COLOR; 1622 pItem->maColor = rColor; 1623 pItem->maText = rText; 1624 ImplInsertItem( std::move(pItem), VALUESET_APPEND ); 1625 } 1626 1627 void ValueSet::ImplInsertItem( std::unique_ptr<ValueSetItem> pItem, const size_t nPos ) 1628 { 1629 DBG_ASSERT( pItem->mnId, "ValueSet::InsertItem(): ItemId == 0" ); 1630 DBG_ASSERT( GetItemPos( pItem->mnId ) == VALUESET_ITEM_NOTFOUND, 1631 "ValueSet::InsertItem(): ItemId already exists" ); 1632 1633 if ( nPos < mItemList.size() ) { 1634 mItemList.insert( mItemList.begin() + nPos, std::move(pItem) ); 1635 } else { 1636 mItemList.push_back( std::move(pItem) ); 1637 } 1638 1639 queue_resize(); 1640 1641 mbFormat = true; 1642 if ( IsReallyVisible() && IsUpdateMode() ) 1643 Invalidate(); 1644 } 1645 1646 int ValueSet::GetScrollWidth() const 1647 { 1648 if (mxScrolledWindow) 1649 return mxScrolledWindow->get_vscroll_width(); 1650 return 0; 1651 } 1652 1653 void ValueSet::SetEdgeBlending(bool bNew) 1654 { 1655 if(mbEdgeBlending != bNew) 1656 { 1657 mbEdgeBlending = bNew; 1658 mbFormat = true; 1659 1660 if (GetDrawingArea() && IsReallyVisible() && IsUpdateMode()) 1661 { 1662 Invalidate(); 1663 } 1664 } 1665 } 1666 1667 Size ValueSet::CalcItemSizePixel( const Size& rItemSize) const 1668 { 1669 Size aSize = rItemSize; 1670 1671 WinBits nStyle = GetStyle(); 1672 if ( nStyle & WB_ITEMBORDER ) 1673 { 1674 long n; 1675 1676 if ( nStyle & WB_DOUBLEBORDER ) 1677 n = ITEM_OFFSET_DOUBLE; 1678 else 1679 n = ITEM_OFFSET; 1680 1681 aSize.AdjustWidth(n ); 1682 aSize.AdjustHeight(n ); 1683 } 1684 1685 return aSize; 1686 } 1687 1688 void ValueSet::SetLineCount( sal_uInt16 nNewLines ) 1689 { 1690 if ( mnUserVisLines != nNewLines ) 1691 { 1692 mnUserVisLines = nNewLines; 1693 mbFormat = true; 1694 queue_resize(); 1695 if ( IsReallyVisible() && IsUpdateMode() ) 1696 Invalidate(); 1697 } 1698 } 1699 1700 void ValueSet::SetItemWidth( long nNewItemWidth ) 1701 { 1702 if ( mnUserItemWidth != nNewItemWidth ) 1703 { 1704 mnUserItemWidth = nNewItemWidth; 1705 mbFormat = true; 1706 queue_resize(); 1707 if ( IsReallyVisible() && IsUpdateMode() ) 1708 Invalidate(); 1709 } 1710 } 1711 1712 //method to set accessible when the style is user draw. 1713 void ValueSet::InsertItem( sal_uInt16 nItemId, const OUString& rText, size_t nPos ) 1714 { 1715 DBG_ASSERT( nItemId, "ValueSet::InsertItem(): ItemId == 0" ); 1716 DBG_ASSERT( GetItemPos( nItemId ) == VALUESET_ITEM_NOTFOUND, 1717 "ValueSet::InsertItem(): ItemId already exists" ); 1718 std::unique_ptr<ValueSetItem> pItem(new ValueSetItem( *this )); 1719 pItem->mnId = nItemId; 1720 pItem->meType = VALUESETITEM_USERDRAW; 1721 pItem->maText = rText; 1722 ImplInsertItem( std::move(pItem), nPos ); 1723 } 1724 1725 void ValueSet::SetItemHeight( long nNewItemHeight ) 1726 { 1727 if ( mnUserItemHeight != nNewItemHeight ) 1728 { 1729 mnUserItemHeight = nNewItemHeight; 1730 mbFormat = true; 1731 queue_resize(); 1732 if ( IsReallyVisible() && IsUpdateMode() ) 1733 Invalidate(); 1734 } 1735 } 1736 1737 OUString ValueSet::RequestHelp(tools::Rectangle& rHelpRect) 1738 { 1739 Point aPos = rHelpRect.TopLeft(); 1740 const size_t nItemPos = ImplGetItem( aPos ); 1741 OUString sRet; 1742 if (nItemPos != VALUESET_ITEM_NOTFOUND) 1743 { 1744 rHelpRect = ImplGetItemRect(nItemPos); 1745 sRet = GetItemText(ImplGetItem(nItemPos)->mnId); 1746 } 1747 return sRet; 1748 } 1749 1750 OUString ValueSet::GetItemText(sal_uInt16 nItemId) const 1751 { 1752 const size_t nPos = GetItemPos(nItemId); 1753 1754 if ( nPos != VALUESET_ITEM_NOTFOUND ) 1755 return mItemList[nPos]->maText; 1756 1757 return OUString(); 1758 } 1759 1760 void ValueSet::SetExtraSpacing( sal_uInt16 nNewSpacing ) 1761 { 1762 if ( GetStyle() & WB_ITEMBORDER ) 1763 { 1764 mnSpacing = nNewSpacing; 1765 1766 mbFormat = true; 1767 queue_resize(); 1768 if ( IsReallyVisible() && IsUpdateMode() ) 1769 Invalidate(); 1770 } 1771 } 1772 1773 void ValueSet::SetFormat() 1774 { 1775 mbFormat = true; 1776 } 1777 1778 void ValueSet::SetItemData( sal_uInt16 nItemId, void* pData ) 1779 { 1780 size_t nPos = GetItemPos( nItemId ); 1781 1782 if ( nPos == VALUESET_ITEM_NOTFOUND ) 1783 return; 1784 1785 ValueSetItem* pItem = mItemList[nPos].get(); 1786 pItem->mpData = pData; 1787 1788 if ( pItem->meType == VALUESETITEM_USERDRAW ) 1789 { 1790 if ( !mbFormat && IsReallyVisible() && IsUpdateMode() ) 1791 { 1792 const tools::Rectangle aRect = ImplGetItemRect(nPos); 1793 Invalidate(aRect); 1794 } 1795 else 1796 mbFormat = true; 1797 } 1798 } 1799 1800 void* ValueSet::GetItemData( sal_uInt16 nItemId ) const 1801 { 1802 size_t nPos = GetItemPos( nItemId ); 1803 1804 if ( nPos != VALUESET_ITEM_NOTFOUND ) 1805 return mItemList[nPos]->mpData; 1806 else 1807 return nullptr; 1808 } 1809 1810 void ValueSet::SetItemText(sal_uInt16 nItemId, const OUString& rText) 1811 { 1812 size_t nPos = GetItemPos( nItemId ); 1813 1814 if ( nPos == VALUESET_ITEM_NOTFOUND ) 1815 return; 1816 1817 ValueSetItem* pItem = mItemList[nPos].get(); 1818 1819 // Remember old and new name for accessibility event. 1820 Any aOldName; 1821 Any aNewName; 1822 OUString sString (pItem->maText); 1823 aOldName <<= sString; 1824 sString = rText; 1825 aNewName <<= sString; 1826 1827 pItem->maText = rText; 1828 1829 if (!mbFormat && IsReallyVisible() && IsUpdateMode()) 1830 { 1831 sal_uInt16 nTempId = mnSelItemId; 1832 1833 if (mbHighlight) 1834 nTempId = mnHighItemId; 1835 1836 if (nTempId == nItemId) 1837 Invalidate(); 1838 } 1839 1840 if (ImplHasAccessibleListeners()) 1841 { 1842 Reference<XAccessible> xAccessible(pItem->GetAccessible( false/*bIsTransientChildrenDisabled*/)); 1843 ValueItemAcc* pValueItemAcc = static_cast<ValueItemAcc*>(xAccessible.get()); 1844 pValueItemAcc->FireAccessibleEvent(AccessibleEventId::NAME_CHANGED, aOldName, aNewName); 1845 } 1846 } 1847 1848 Size ValueSet::GetLargestItemSize() 1849 { 1850 Size aLargestItem; 1851 1852 for (const std::unique_ptr<ValueSetItem>& pItem : mItemList) 1853 { 1854 if (!pItem->mbVisible) 1855 continue; 1856 1857 if (pItem->meType != VALUESETITEM_IMAGE && 1858 pItem->meType != VALUESETITEM_IMAGE_AND_TEXT) 1859 { 1860 // handle determining an optimal size for this case 1861 continue; 1862 } 1863 1864 Size aSize = pItem->maImage.GetSizePixel(); 1865 if (pItem->meType == VALUESETITEM_IMAGE_AND_TEXT) 1866 { 1867 aSize.AdjustHeight(3 * NAME_LINE_HEIGHT + 1868 maVirDev->GetTextHeight() ); 1869 aSize.setWidth( std::max(aSize.Width(), 1870 maVirDev->GetTextWidth(pItem->maText) + NAME_OFFSET) ); 1871 } 1872 1873 aLargestItem.setWidth( std::max(aLargestItem.Width(), aSize.Width()) ); 1874 aLargestItem.setHeight( std::max(aLargestItem.Height(), aSize.Height()) ); 1875 } 1876 1877 return aLargestItem; 1878 } 1879 1880 void ValueSet::SetOptimalSize() 1881 { 1882 Size aLargestSize(GetLargestItemSize()); 1883 aLargestSize.setWidth(std::max(aLargestSize.Width(), mnUserItemWidth)); 1884 aLargestSize.setHeight(std::max(aLargestSize.Height(), mnUserItemHeight)); 1885 Size aPrefSize(CalcWindowSizePixel(aLargestSize)); 1886 GetDrawingArea()->set_size_request(aPrefSize.Width(), aPrefSize.Height()); 1887 } 1888 1889 Image ValueSet::GetItemImage(sal_uInt16 nItemId) const 1890 { 1891 size_t nPos = GetItemPos( nItemId ); 1892 1893 if ( nPos != VALUESET_ITEM_NOTFOUND ) 1894 return mItemList[nPos]->maImage; 1895 else 1896 return Image(); 1897 } 1898 1899 void ValueSet::SetColor(const Color& rColor) 1900 { 1901 maColor = rColor; 1902 mbFormat = true; 1903 if (IsReallyVisible() && IsUpdateMode()) 1904 Invalidate(); 1905 } 1906 1907 void ValueSet::Show() 1908 { 1909 if (mxScrolledWindow) 1910 mxScrolledWindow->show(); 1911 CustomWidgetController::Show(); 1912 } 1913 1914 void ValueSet::Hide() 1915 { 1916 CustomWidgetController::Hide(); 1917 if (mxScrolledWindow) 1918 mxScrolledWindow->hide(); 1919 } 1920 1921 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ 1922
