LibreOffice Module desktop (master) 1
dp_gui_extlistbox.cxx
Go to the documentation of this file.
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 <dp_shared.hxx>
21#include <strings.hrc>
22#include "dp_gui.h"
23#include "dp_gui_extlistbox.hxx"
24#include "dp_gui_theextmgr.hxx"
25#include <dp_dependencies.hxx>
26#include <bitmaps.hlst>
27
29#include <com/sun/star/i18n/CollatorOptions.hpp>
30#include <com/sun/star/deployment/DependencyException.hpp>
31#include <com/sun/star/deployment/DeploymentException.hpp>
32#include <com/sun/star/deployment/ExtensionRemovedException.hpp>
33#include <com/sun/star/system/XSystemShellExecute.hpp>
34#include <com/sun/star/system/SystemShellExecuteFlags.hpp>
35#include <com/sun/star/system/SystemShellExecute.hpp>
38#include <o3tl/safeint.hxx>
39#include <osl/diagnose.h>
40#include <rtl/ustrbuf.hxx>
41#include <utility>
42#include <vcl/event.hxx>
43#include <vcl/ptrstyle.hxx>
44#include <vcl/svapp.hxx>
45#include <vcl/settings.hxx>
46#include <vcl/weldutils.hxx>
47#include <algorithm>
48
49constexpr OUStringLiteral USER_PACKAGE_MANAGER = u"user";
50constexpr OUStringLiteral SHARED_PACKAGE_MANAGER = u"shared";
51
52using namespace ::com::sun::star;
53
54namespace dp_gui {
55
56namespace {
57
58struct FindWeakRef
59{
61
62 explicit FindWeakRef( uno::Reference<deployment::XPackage> ext): m_extension(std::move(ext)) {}
63 bool operator () (uno::WeakReference< deployment::XPackage > const & ref);
64};
65
66bool FindWeakRef::operator () (uno::WeakReference< deployment::XPackage > const & ref)
67{
69 return ext == m_extension;
70}
71
72} // end namespace
73
74// struct Entry_Impl
75
77 const PackageState eState, const bool bReadOnly ) :
78 m_bActive( false ),
79 m_bLocked( bReadOnly ),
80 m_bHasOptions( false ),
81 m_bUser( false ),
82 m_bShared( false ),
83 m_bNew( false ),
84 m_bChecked( false ),
85 m_bMissingDeps( false ),
86 m_bHasButtons( false ),
87 m_bMissingLic( false ),
88 m_eState( eState ),
89 m_xPackage( xPackage )
90{
91 try
92 {
93 m_sTitle = xPackage->getDisplayName();
94 m_sVersion = xPackage->getVersion();
95 m_sDescription = xPackage->getDescription();
96 m_sLicenseText = xPackage->getLicenseText();
97
98 beans::StringPair aInfo( m_xPackage->getPublisherInfo() );
99 m_sPublisher = aInfo.First;
100 m_sPublisherURL = aInfo.Second;
101
102 // get the icons for the package if there are any
103 uno::Reference< graphic::XGraphic > xGraphic = xPackage->getIcon( false );
104 if ( xGraphic.is() )
105 m_aIcon = Image( xGraphic );
106
107 if ( eState == AMBIGUOUS )
108 m_sErrorText = DpResId( RID_STR_ERROR_UNKNOWN_STATUS );
109 else if ( eState == NOT_REGISTERED )
111 }
112 catch (const deployment::ExtensionRemovedException &) {}
113 catch (const uno::RuntimeException &) {}
114}
115
116
118{}
119
120
121sal_Int32 Entry_Impl::CompareTo( const CollatorWrapper *pCollator, const TEntry_Impl& rEntry ) const
122{
123 sal_Int32 eCompare = pCollator->compareString( m_sTitle, rEntry->m_sTitle );
124 if ( eCompare == 0 )
125 {
126 eCompare = m_sVersion.compareTo( rEntry->m_sVersion );
127 if ( eCompare == 0 )
128 {
129 sal_Int32 nCompare = m_xPackage->getRepositoryName().compareTo( rEntry->m_xPackage->getRepositoryName() );
130 if ( nCompare < 0 )
131 eCompare = -1;
132 else if ( nCompare > 0 )
133 eCompare = 1;
134 }
135 }
136 return eCompare;
137}
138
139
141{
142 try {
144 }
145 catch ( const deployment::DeploymentException &e )
146 {
147 deployment::DependencyException depExc;
148 if ( e.Cause >>= depExc )
149 {
150 OUStringBuffer aMissingDep( DpResId( RID_STR_ERROR_MISSING_DEPENDENCIES ) );
151 for ( const auto& i : std::as_const(depExc.UnsatisfiedDependencies) )
152 {
153 aMissingDep.append("\n"
155 }
156 aMissingDep.append("\n");
157 m_sErrorText = aMissingDep.makeStringAndClear();
158 m_bMissingDeps = true;
159 }
160 }
161}
162
163// ExtensionRemovedListener
164
165void ExtensionRemovedListener::disposing( lang::EventObject const & rEvt )
166{
167 uno::Reference< deployment::XPackage > xPackage( rEvt.Source, uno::UNO_QUERY );
168
169 if ( xPackage.is() )
170 {
171 m_pParent->removeEntry( xPackage );
172 }
173}
174
175
177{
178}
179
180
181// ExtensionBox_Impl
182ExtensionBox_Impl::ExtensionBox_Impl(std::unique_ptr<weld::ScrolledWindow> xScroll)
183 : m_bHasScrollBar( false )
184 , m_bHasActive( false )
185 , m_bNeedsRecalc( true )
186 , m_bInCheckMode( false )
187 , m_bAdjustActive( false )
188 , m_bInDelete( false )
189 , m_nActive( 0 )
190 , m_nTopIndex( 0 )
191 , m_nStdHeight( 0 )
192 , m_nActiveHeight( 0 )
193 , m_aSharedImage(StockImage::Yes, RID_BMP_SHARED)
194 , m_aLockedImage(StockImage::Yes, RID_BMP_LOCKED)
195 , m_aWarningImage(StockImage::Yes, RID_BMP_WARNING)
196 , m_aDefaultImage(StockImage::Yes, RID_BMP_EXTENSION)
197 , m_pManager( nullptr )
198 , m_xScrollBar(std::move(xScroll))
199{
200}
201
203{
204 m_xScrollBar->connect_vadjustment_changed( LINK( this, ExtensionBox_Impl, ScrollHdl ) );
205
206 auto nIconHeight = 2*TOP_OFFSET + SMALL_ICON_SIZE;
207 auto nTitleHeight = 2*TOP_OFFSET + GetTextHeight();
208 if ( nIconHeight < nTitleHeight )
209 m_nStdHeight = nTitleHeight;
210 else
211 m_nStdHeight = nIconHeight;
213
214 nIconHeight = ICON_HEIGHT + 2*TOP_OFFSET + 1;
215 if ( m_nStdHeight < nIconHeight )
216 m_nStdHeight = nIconHeight;
217
219
221
222 m_pLocale.reset( new lang::Locale( Application::GetSettings().GetLanguageTag().getLocale() ) );
223 m_oCollator.emplace( ::comphelper::getProcessComponentContext() );
224 m_oCollator->loadDefaultCollator( *m_pLocale, i18n::CollatorOptions::CollatorOptions_IGNORE_CASE );
225}
226
228{
229 if ( ! m_bInDelete )
231
232 m_bInDelete = true;
233
234 for (auto const& entry : m_vEntries)
235 {
236 entry->m_xPackage->removeEventListener( m_xRemoveListener );
237 }
238
239 m_vEntries.clear();
240
241 m_xRemoveListener.clear();
242
243 m_pLocale.reset();
244 m_oCollator.reset();
245}
246
248{
249 return static_cast< sal_Int32 >( m_vEntries.size() );
250}
251
252
254{
255 if ( m_bHasActive )
256 {
257 OSL_ASSERT( m_nActive >= -1);
258 return static_cast< sal_Int32 >( m_nActive );
259 }
260 else
261 return ENTRY_NOTFOUND;
262}
263
264
265// Title + description
267{
268 const ::osl::MutexGuard aGuard( m_entriesMutex );
269
270 // get title height
271 tools::Long aTextHeight;
272 tools::Long nIconHeight = 2*TOP_OFFSET + SMALL_ICON_SIZE;
273 tools::Long nTitleHeight = 2*TOP_OFFSET + GetTextHeight();
274 if ( nIconHeight < nTitleHeight )
275 aTextHeight = nTitleHeight;
276 else
277 aTextHeight = nIconHeight;
278
279 // calc description height
280 Size aSize = GetOutputSizePixel();
281
282 aSize.AdjustWidth( -(ICON_OFFSET) );
283 aSize.setHeight( 10000 );
284
285 OUString aText( m_vEntries[ nPos ]->m_sErrorText );
286 if ( !aText.isEmpty() )
287 aText += "\n";
288 aText += m_vEntries[ nPos ]->m_sDescription;
289
291 DrawTextFlags::MultiLine | DrawTextFlags::WordBreak);
292 aTextHeight += aRect.GetHeight();
293
294 if ( aTextHeight < m_nStdHeight )
295 aTextHeight = m_nStdHeight;
296
297 m_nActiveHeight = aTextHeight;
298
299 if ( m_vEntries[ nPos ]->m_bHasButtons )
300 m_nActiveHeight += 2;
301}
302
304{
305 const ::osl::MutexGuard aGuard( m_entriesMutex );
306
307 Size aSize( GetOutputSizePixel() );
308
309 if ( m_vEntries[ nPos ]->m_bActive )
310 aSize.setHeight( m_nActiveHeight );
311 else
312 aSize.setHeight( m_nStdHeight );
313
314 Point aPos( 0, -m_nTopIndex + nPos * m_nStdHeight );
315 if ( m_bHasActive && ( nPos < m_nActive ) )
317
318 return tools::Rectangle( aPos, aSize );
319}
320
321
323{
324 const ::osl::MutexGuard aGuard( m_entriesMutex );
325
326 m_bInDelete = true;
327
328 m_vRemovedEntries.clear();
329
330 m_bInDelete = false;
331}
332
333
334//This function may be called with nPos < 0
336{
337 bool invalidate = false;
338 {
339 //ToDo we should not use the guard at such a big scope here.
340 //Currently it is used to guard m_vEntries and m_nActive. m_nActive will be
341 //modified in this function.
342 //It would be probably best to always use a copy of m_vEntries
343 //and some other state variables from ExtensionBox_Impl for
344 //the whole painting operation. See issue i86993
345 ::osl::MutexGuard guard(m_entriesMutex);
346
347 if ( m_bInCheckMode )
348 return;
349
350 if ( m_bHasActive )
351 {
352 if ( nPos == m_nActive )
353 return;
354
355 m_bHasActive = false;
356 m_vEntries[ m_nActive ]->m_bActive = false;
357 }
358
359 if ( ( nPos >= 0 ) && ( o3tl::make_unsigned(nPos) < m_vEntries.size() ) )
360 {
361 m_bHasActive = true;
362 m_nActive = nPos;
363 m_vEntries[ nPos ]->m_bActive = true;
364
365 if ( IsReallyVisible() )
366 {
367 m_bAdjustActive = true;
368 }
369 }
370
371 if ( IsReallyVisible() )
372 {
373 m_bNeedsRecalc = true;
374 invalidate = true;
375 }
376 }
377
378 if (invalidate)
379 {
381 Invalidate();
382 }
383}
384
385
386void ExtensionBox_Impl::DrawRow(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect, const TEntry_Impl& rEntry)
387{
388 const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings();
389
390 if (rEntry->m_bActive)
391 rRenderContext.SetTextColor(rStyleSettings.GetHighlightTextColor());
392 else if ((rEntry->m_eState != REGISTERED) && (rEntry->m_eState != NOT_AVAILABLE))
393 rRenderContext.SetTextColor(rStyleSettings.GetDisableColor());
394 else
395 rRenderContext.SetTextColor(rStyleSettings.GetFieldTextColor());
396
397 if (rEntry->m_bActive)
398 {
399 rRenderContext.SetLineColor();
400 rRenderContext.SetFillColor(rStyleSettings.GetHighlightColor());
401 rRenderContext.DrawRect(rRect);
402 }
403 else
404 {
405 rRenderContext.SetBackground(rStyleSettings.GetFieldColor());
406 rRenderContext.SetTextFillColor();
407 rRenderContext.Erase(rRect);
408 }
409
410 // Draw extension icon
411 Point aPos( rRect.TopLeft() );
412 aPos += Point(TOP_OFFSET, TOP_OFFSET);
413 Image aImage;
414 if (!rEntry->m_aIcon)
415 aImage = m_aDefaultImage;
416 else
417 aImage = rEntry->m_aIcon;
418 Size aImageSize = aImage.GetSizePixel();
419 if ((aImageSize.Width() <= ICON_WIDTH ) && ( aImageSize.Height() <= ICON_HEIGHT ) )
420 rRenderContext.DrawImage(Point(aPos.X() + ((ICON_WIDTH - aImageSize.Width()) / 2),
421 aPos.Y() + ((ICON_HEIGHT - aImageSize.Height()) / 2)),
422 aImage);
423 else
424 rRenderContext.DrawImage(aPos, Size(ICON_WIDTH, ICON_HEIGHT), aImage);
425
426 // Setup fonts
427 // expand the point size of the desired font to the equivalent pixel size
428 weld::SetPointFont(rRenderContext, GetDrawingArea()->get_font());
429 vcl::Font aStdFont(rRenderContext.GetFont());
430 vcl::Font aBoldFont(aStdFont);
431 aBoldFont.SetWeight(WEIGHT_BOLD);
432 rRenderContext.SetFont(aBoldFont);
433 auto aTextHeight = rRenderContext.GetTextHeight();
434
435 // Get max title width
436 auto nMaxTitleWidth = rRect.GetWidth() - ICON_OFFSET;
437 nMaxTitleWidth -= (2 * SMALL_ICON_SIZE) + (4 * SPACE_BETWEEN);
438 rRenderContext.SetFont(aStdFont);
439 tools::Long nLinkWidth = 0;
440 if (!rEntry->m_sPublisher.isEmpty())
441 {
442 nLinkWidth = rRenderContext.GetTextWidth(rEntry->m_sPublisher);
443 nMaxTitleWidth -= nLinkWidth + (2 * SPACE_BETWEEN);
444 }
445 tools::Long aVersionWidth = rRenderContext.GetTextWidth(rEntry->m_sVersion);
446
447 aPos = rRect.TopLeft() + Point(ICON_OFFSET, TOP_OFFSET);
448
449 rRenderContext.SetFont(aBoldFont);
450 tools::Long aTitleWidth = rRenderContext.GetTextWidth(rEntry->m_sTitle) + (aTextHeight / 3);
451 if (aTitleWidth > nMaxTitleWidth - aVersionWidth)
452 {
453 aTitleWidth = nMaxTitleWidth - aVersionWidth - (aTextHeight / 3);
454 OUString aShortTitle = rRenderContext.GetEllipsisString(rEntry->m_sTitle, aTitleWidth);
455 rRenderContext.DrawText(aPos, aShortTitle);
456 aTitleWidth += (aTextHeight / 3);
457 }
458 else
459 rRenderContext.DrawText(aPos, rEntry->m_sTitle);
460
461 rRenderContext.SetFont(aStdFont);
462 rRenderContext.DrawText(Point(aPos.X() + aTitleWidth, aPos.Y()), rEntry->m_sVersion);
463
464 tools::Long nIconHeight = TOP_OFFSET + SMALL_ICON_SIZE;
465 tools::Long nTitleHeight = TOP_OFFSET + GetTextHeight();
466 if ( nIconHeight < nTitleHeight )
467 aTextHeight = nTitleHeight;
468 else
469 aTextHeight = nIconHeight;
470
471 // draw description
472 OUString sDescription;
473 if (!rEntry->m_sErrorText.isEmpty())
474 {
475 if (rEntry->m_bActive)
476 sDescription = rEntry->m_sErrorText + "\n" + rEntry->m_sDescription;
477 else
478 sDescription = rEntry->m_sErrorText;
479 }
480 else
481 sDescription = rEntry->m_sDescription;
482
483 aPos.AdjustY(aTextHeight );
484 if (rEntry->m_bActive)
485 {
486 tools::Long nExtraHeight = 0;
487
488 if (rEntry->m_bHasButtons)
489 nExtraHeight = 2;
490
491 rRenderContext.DrawText(tools::Rectangle(aPos.X(), aPos.Y(), rRect.Right(), rRect.Bottom() - nExtraHeight),
492 sDescription, DrawTextFlags::MultiLine | DrawTextFlags::WordBreak );
493 }
494 else
495 {
496 //replace LF to space, so words do not stick together in one line view
497 sDescription = sDescription.replace(0x000A, ' ');
498 const tools::Long nWidth = rRenderContext.GetTextWidth( sDescription );
499 if (nWidth > rRect.GetWidth() - aPos.X())
500 sDescription = rRenderContext.GetEllipsisString(sDescription, rRect.GetWidth() - aPos.X());
501 rRenderContext.DrawText(aPos, sDescription);
502 }
503
504 // Draw publisher link
505 if (!rEntry->m_sPublisher.isEmpty())
506 {
507 aPos = rRect.TopLeft() + Point( ICON_OFFSET + nMaxTitleWidth + (2*SPACE_BETWEEN), TOP_OFFSET );
508
510 rRenderContext.SetTextColor(rStyleSettings.GetLinkColor());
511 rRenderContext.SetTextFillColor(rStyleSettings.GetFieldColor());
512 vcl::Font aFont = rRenderContext.GetFont();
513 // to underline
515 rRenderContext.SetFont(aFont);
516 rRenderContext.DrawText(aPos, rEntry->m_sPublisher);
517 rEntry->m_aLinkRect = tools::Rectangle(aPos, Size(nLinkWidth, aTextHeight));
518 rRenderContext.Pop();
519 }
520
521 // Draw status icons
522 if (!rEntry->m_bUser)
523 {
525 if (rEntry->m_bLocked)
527 else
529 }
530 if ((rEntry->m_eState == AMBIGUOUS ) || rEntry->m_bMissingDeps || rEntry->m_bMissingLic)
531 {
534 }
535
536 rRenderContext.SetLineColor(COL_LIGHTGRAY);
537 rRenderContext.DrawLine(rRect.BottomLeft(), rRect.BottomRight());
538}
539
540
542{
543 if ( m_bHasActive )
545
547
548 if ( m_bHasActive )
549 {
551
552 if ( m_bAdjustActive )
553 {
554 m_bAdjustActive = false;
555
556 // If the top of the selected entry isn't visible, make it visible
557 if ( aEntryRect.Top() < 0 )
558 {
559 m_nTopIndex += aEntryRect.Top();
560 aEntryRect.Move( 0, -aEntryRect.Top() );
561 }
562
563 // If the bottom of the selected entry isn't visible, make it visible even if now the top
564 // isn't visible any longer ( the buttons are more important )
565 Size aOutputSize = GetOutputSizePixel();
566 if ( aEntryRect.Bottom() > aOutputSize.Height() )
567 {
568 m_nTopIndex += ( aEntryRect.Bottom() - aOutputSize.Height() );
569 aEntryRect.Move( 0, -( aEntryRect.Bottom() - aOutputSize.Height() ) );
570 }
571
572 // If there is unused space below the last entry but all entries don't fit into the box,
573 // move the content down to use the whole space
574 const tools::Long nTotalHeight = GetTotalHeight();
575 if ( m_bHasScrollBar && ( aOutputSize.Height() + m_nTopIndex > nTotalHeight ) )
576 {
577 tools::Long nOffset = m_nTopIndex;
578 m_nTopIndex = nTotalHeight - aOutputSize.Height();
579 nOffset -= m_nTopIndex;
580 aEntryRect.Move( 0, nOffset );
581 }
582
583 if ( m_bHasScrollBar )
584 m_xScrollBar->vadjustment_set_value( m_nTopIndex );
585 }
586 }
587
588 m_bNeedsRecalc = false;
589}
590
591
592bool ExtensionBox_Impl::HandleCursorKey( sal_uInt16 nKeyCode )
593{
594 if ( m_vEntries.empty() )
595 return true;
596
597 tools::Long nSelect = 0;
598
599 if ( m_bHasActive )
600 {
602 if ( nPageSize < 2 )
603 nPageSize = 2;
604
605 if ( ( nKeyCode == KEY_DOWN ) || ( nKeyCode == KEY_RIGHT ) )
606 nSelect = m_nActive + 1;
607 else if ( ( nKeyCode == KEY_UP ) || ( nKeyCode == KEY_LEFT ) )
608 nSelect = m_nActive - 1;
609 else if ( nKeyCode == KEY_HOME )
610 nSelect = 0;
611 else if ( nKeyCode == KEY_END )
612 nSelect = m_vEntries.size() - 1;
613 else if ( nKeyCode == KEY_PAGEUP )
614 nSelect = m_nActive - nPageSize + 1;
615 else if ( nKeyCode == KEY_PAGEDOWN )
616 nSelect = m_nActive + nPageSize - 1;
617 }
618 else // when there is no selected entry, we will select the first or the last.
619 {
620 if ( ( nKeyCode == KEY_DOWN ) || ( nKeyCode == KEY_PAGEDOWN ) || ( nKeyCode == KEY_HOME ) )
621 nSelect = 0;
622 else if ( ( nKeyCode == KEY_UP ) || ( nKeyCode == KEY_PAGEUP ) || ( nKeyCode == KEY_END ) )
623 nSelect = m_vEntries.size() - 1;
624 }
625
626 if ( nSelect < 0 )
627 nSelect = 0;
628 if ( o3tl::make_unsigned(nSelect) >= m_vEntries.size() )
629 nSelect = m_vEntries.size() - 1;
630
631 selectEntry( nSelect );
632
633 return true;
634}
635
636
637void ExtensionBox_Impl::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& /*rPaintRect*/)
638{
639 if ( !m_bInDelete )
641
642 if ( m_bNeedsRecalc )
643 RecalcAll();
644
645 Point aStart( 0, -m_nTopIndex );
646 Size aSize(GetOutputSizePixel());
647
648 const ::osl::MutexGuard aGuard( m_entriesMutex );
649
650 for (auto const& entry : m_vEntries)
651 {
652 aSize.setHeight( entry->m_bActive ? m_nActiveHeight : m_nStdHeight );
653 tools::Rectangle aEntryRect( aStart, aSize );
654 DrawRow(rRenderContext, aEntryRect, entry);
655 aStart.AdjustY(aSize.Height() );
656 }
657}
658
659
661{
662 tools::Long nHeight = m_vEntries.size() * m_nStdHeight;
663
664 if ( m_bHasActive )
665 {
666 nHeight += m_nActiveHeight - m_nStdHeight;
667 }
668
669 return nHeight;
670}
671
672
674{
675 const Size aSize = GetOutputSizePixel();
676 const auto nTotalHeight = GetTotalHeight();
677 const bool bNeedsScrollBar = ( nTotalHeight > aSize.Height() );
678
679 if ( bNeedsScrollBar )
680 {
681 if ( m_nTopIndex + aSize.Height() > nTotalHeight )
682 m_nTopIndex = nTotalHeight - aSize.Height();
683
684 m_xScrollBar->vadjustment_configure(m_nTopIndex, 0, nTotalHeight,
685 m_nStdHeight, ( aSize.Height() * 4 ) / 5,
686 aSize.Height());
687
688 if (!m_bHasScrollBar)
689 m_xScrollBar->set_vpolicy(VclPolicyType::ALWAYS);
690 }
691 else if ( m_bHasScrollBar )
692 {
693 m_xScrollBar->set_vpolicy(VclPolicyType::NEVER);
694 m_nTopIndex = 0;
695 }
696
697 m_bHasScrollBar = bNeedsScrollBar;
698}
699
700
702{
703 RecalcAll();
704 Invalidate();
705}
706
708{
709 Size aSize = pDrawingArea->get_ref_device().LogicToPixel(Size(250, 150), MapMode(MapUnit::MapAppFont));
710 pDrawingArea->set_size_request(aSize.Width(), aSize.Height());
711 CustomWidgetController::SetDrawingArea(pDrawingArea);
712 SetOutputSizePixel(aSize);
713
714 Init();
715}
716
718{
719 tools::Long nPos = ( rPos.Y() + m_nTopIndex ) / m_nStdHeight;
720
721 if ( m_bHasActive && ( nPos > m_nActive ) )
722 {
724 nPos = m_nActive;
725 else
727 }
728
729 return nPos;
730}
731
733{
734 bool bOverHyperlink = false;
735
736 auto nPos = PointToPos( rMEvt.GetPosPixel() );
737 if ( ( nPos >= 0 ) && ( o3tl::make_unsigned(nPos) < m_vEntries.size() ) )
738 {
739 const auto& rEntry = m_vEntries[nPos];
740 bOverHyperlink = !rEntry->m_sPublisher.isEmpty() && rEntry->m_aLinkRect.Contains(rMEvt.GetPosPixel());
741 }
742
743 if (bOverHyperlink)
744 SetPointer(PointerStyle::RefHand);
745 else
746 SetPointer(PointerStyle::Arrow);
747
748 return false;
749}
750
752{
753 auto nPos = PointToPos( rRect.TopLeft() );
754 if ( ( nPos >= 0 ) && ( o3tl::make_unsigned(nPos) < m_vEntries.size() ) )
755 {
756 const auto& rEntry = m_vEntries[nPos];
757 bool bOverHyperlink = !rEntry->m_sPublisher.isEmpty() && rEntry->m_aLinkRect.Contains(rRect);
758 if (bOverHyperlink)
759 {
760 rRect = rEntry->m_aLinkRect;
761 return rEntry->m_sPublisherURL;
762 }
763 }
764
765 return OUString();
766}
767
769{
770 if ( !rMEvt.IsLeft() )
771 return false;
772
773 if (rMEvt.IsMod1() && m_bHasActive)
774 selectEntry(ExtensionBox_Impl::ENTRY_NOTFOUND); // Selecting a not existing entry will deselect the current one
775 else
776 {
777 auto nPos = PointToPos( rMEvt.GetPosPixel() );
778
779 if ( ( nPos >= 0 ) && ( o3tl::make_unsigned(nPos) < m_vEntries.size() ) )
780 {
781 const auto& rEntry = m_vEntries[nPos];
782 if (!rEntry->m_sPublisher.isEmpty() && rEntry->m_aLinkRect.Contains(rMEvt.GetPosPixel()))
783 {
784 try
785 {
786 css::uno::Reference<css::system::XSystemShellExecute> xSystemShellExecute(
788 //throws css::lang::IllegalArgumentException, css::system::SystemShellExecuteException
789 xSystemShellExecute->execute(rEntry->m_sPublisherURL, OUString(), css::system::SystemShellExecuteFlags::URIS_ONLY);
790 }
791 catch (...)
792 {
793 }
794 return true;
795 }
796 }
797
798 selectEntry( nPos );
799 }
800 return true;
801}
802
804{
805 if ( !m_bInDelete )
807
808 vcl::KeyCode aKeyCode = rKEvt.GetKeyCode();
809 sal_uInt16 nKeyCode = aKeyCode.GetCode();
810
811 bool bHandled = false;
812 if (nKeyCode != KEY_TAB && aKeyCode.GetGroup() == KEYGROUP_CURSOR)
813 bHandled = HandleCursorKey(nKeyCode);
814
815 return bHandled;
816}
817
819 const tools::Long nEnd, tools::Long &nPos )
820{
821 nPos = nStart;
822 if ( nStart > nEnd )
823 return false;
824
825 sal_Int32 eCompare;
826
827 if ( nStart == nEnd )
828 {
829 eCompare = rEntry->CompareTo( &*m_oCollator, m_vEntries[ nStart ] );
830 if ( eCompare < 0 )
831 return false;
832 else if ( eCompare == 0 )
833 {
834 //Workaround. See i86963.
835 if (rEntry->m_xPackage != m_vEntries[nStart]->m_xPackage)
836 return false;
837
838 if ( m_bInCheckMode )
839 m_vEntries[ nStart ]->m_bChecked = true;
840 return true;
841 }
842 else
843 {
844 nPos = nStart + 1;
845 return false;
846 }
847 }
848
849 const tools::Long nMid = nStart + ( ( nEnd - nStart ) / 2 );
850 eCompare = rEntry->CompareTo( &*m_oCollator, m_vEntries[ nMid ] );
851
852 if ( eCompare < 0 )
853 return FindEntryPos( rEntry, nStart, nMid-1, nPos );
854 else if ( eCompare > 0 )
855 return FindEntryPos( rEntry, nMid+1, nEnd, nPos );
856 else
857 {
858 //Workaround.See i86963.
859 if (rEntry->m_xPackage != m_vEntries[nMid]->m_xPackage)
860 return false;
861
862 if ( m_bInCheckMode )
863 m_vEntries[ nMid ]->m_bChecked = true;
864 nPos = nMid;
865 return true;
866 }
867}
868
870{
871 m_vListenerAdded.erase(std::remove_if(m_vListenerAdded.begin(), m_vListenerAdded.end(),
872 [](const uno::WeakReference<deployment::XPackage>& rxListener) {
873 const uno::Reference<deployment::XPackage> hardRef(rxListener);
874 return !hardRef.is();
875 }),
876 m_vListenerAdded.end());
877}
878
881{
882 //make sure to only add the listener once
884 if ( std::none_of(m_vListenerAdded.begin(), m_vListenerAdded.end(),
885 FindWeakRef(extension)) )
886 {
887 extension->addEventListener( m_xRemoveListener );
888 m_vListenerAdded.emplace_back(extension);
889 }
890}
891
892
894 bool bLicenseMissing )
895{
897 bool bLocked = m_pManager->isReadOnly( xPackage );
898
899 TEntry_Impl pEntry = std::make_shared<Entry_Impl>( xPackage, eState, bLocked );
900
901 // Don't add empty entries
902 if ( pEntry->m_sTitle.isEmpty() )
903 return;
904
905 {
906 osl::MutexGuard guard(m_entriesMutex);
907 tools::Long nPos = 0;
908 if (m_vEntries.empty())
909 {
910 addEventListenerOnce(xPackage);
911 m_vEntries.push_back(pEntry);
912 }
913 else
914 {
915 if (!FindEntryPos(pEntry, 0, m_vEntries.size() - 1, nPos))
916 {
917 addEventListenerOnce(xPackage);
918 m_vEntries.insert(m_vEntries.begin() + nPos, pEntry);
919 }
920 else if (!m_bInCheckMode)
921 {
922 OSL_FAIL("ExtensionBox_Impl::addEntry(): Will not add duplicate entries");
923 }
924 }
925
926 pEntry->m_bHasOptions = m_pManager->supportsOptions(xPackage);
927 pEntry->m_bUser = (xPackage->getRepositoryName() == USER_PACKAGE_MANAGER);
928 pEntry->m_bShared = (xPackage->getRepositoryName() == SHARED_PACKAGE_MANAGER);
929 pEntry->m_bNew = m_bInCheckMode;
930 pEntry->m_bMissingLic = bLicenseMissing;
931
932 if (bLicenseMissing)
933 pEntry->m_sErrorText = DpResId(RID_STR_ERROR_MISSING_LICENSE);
934
935 //access to m_nActive must be guarded
937 m_nActive += 1;
938 }
939
940 if ( IsReallyVisible() )
941 Invalidate();
942
943 m_bNeedsRecalc = true;
944}
945
947{
948 for (auto const& entry : m_vEntries)
949 {
950 if ( entry->m_xPackage == xPackage )
951 {
953 entry->m_bHasOptions = m_pManager->supportsOptions( xPackage );
954 entry->m_eState = eState;
955 entry->m_sTitle = xPackage->getDisplayName();
956 entry->m_sVersion = xPackage->getVersion();
957 entry->m_sDescription = xPackage->getDescription();
958
959 if ( eState == REGISTERED )
960 entry->m_bMissingLic = false;
961
962 if ( eState == AMBIGUOUS )
963 entry->m_sErrorText = DpResId( RID_STR_ERROR_UNKNOWN_STATUS );
964 else if ( ! entry->m_bMissingLic )
965 entry->m_sErrorText.clear();
966
967 if ( IsReallyVisible() )
968 Invalidate();
969 break;
970 }
971 }
972}
973
974//This function is also called as a result of removing an extension.
975//see PackageManagerImpl::removePackage
976//The gui is a registered as listener on the package. Removing it will cause the
977//listeners to be notified and then this function is called. At this moment xPackage
978//is in the disposing state and all calls on it may result in a DisposedException.
980{
981 if ( m_bInDelete )
982 return;
983
984 bool invalidate = false;
985 {
986 ::osl::ClearableMutexGuard aGuard( m_entriesMutex );
987
988 auto iIndex = std::find_if(m_vEntries.begin(), m_vEntries.end(),
989 [&xPackage](const TEntry_Impl& rxEntry) { return rxEntry->m_xPackage == xPackage; });
990 if (iIndex != m_vEntries.end())
991 {
992 tools::Long nPos = iIndex - m_vEntries.begin();
993
994 // Entries mustn't be removed here, because they contain a hyperlink control
995 // which can only be deleted when the thread has the solar mutex. Therefore
996 // the entry will be moved into the m_vRemovedEntries list which will be
997 // cleared on the next paint event
998 m_vRemovedEntries.push_back( *iIndex );
999 (*iIndex)->m_xPackage->removeEventListener(m_xRemoveListener);
1000 m_vEntries.erase( iIndex );
1001
1002 m_bNeedsRecalc = true;
1003
1004 if ( IsReallyVisible() )
1005 invalidate = true;
1006
1007 if ( m_bHasActive )
1008 {
1009 if ( nPos < m_nActive )
1010 m_nActive -= 1;
1011 else if ( ( nPos == m_nActive ) &&
1012 ( nPos == static_cast<tools::Long>(m_vEntries.size()) ) )
1013 m_nActive -= 1;
1014
1015 m_bHasActive = false;
1016 //clear before calling out of this method
1017 aGuard.clear();
1019 }
1020 }
1021 }
1022
1023 if (invalidate)
1024 {
1026 Invalidate();
1027 }
1028}
1029
1030
1032{
1033 bool bAllRemoved = false;
1034
1035 while ( ! bAllRemoved )
1036 {
1037 bAllRemoved = true;
1038
1039 ::osl::ClearableMutexGuard aGuard( m_entriesMutex );
1040
1041 for (auto const& entry : m_vEntries)
1042 {
1043 if ( !entry->m_bLocked )
1044 {
1045 bAllRemoved = false;
1046 uno::Reference< deployment::XPackage> xPackage = entry->m_xPackage;
1047 aGuard.clear();
1048 removeEntry( xPackage );
1049 break;
1050 }
1051 }
1052 }
1053}
1054
1055
1057{
1058 m_bInCheckMode = true;
1059 for (auto const& entry : m_vEntries)
1060 {
1061 entry->m_bChecked = false;
1062 entry->m_bNew = false;
1063 }
1064}
1065
1066
1068{
1069 tools::Long nNewPos = -1;
1070 tools::Long nChangedActivePos = -1;
1071 tools::Long nPos = 0;
1072 bool bNeedsUpdate = false;
1073
1074 {
1075 osl::MutexGuard guard(m_entriesMutex);
1076 auto iIndex = m_vEntries.begin();
1077 while (iIndex != m_vEntries.end())
1078 {
1079 if (!(*iIndex)->m_bChecked)
1080 {
1081 (*iIndex)->m_bChecked = true;
1082 bNeedsUpdate = true;
1083 nPos = iIndex - m_vEntries.begin();
1084 if ((*iIndex)->m_bNew)
1085 { // add entry to list and correct active pos
1086 if (nNewPos == -1)
1087 nNewPos = nPos;
1088 if (nPos <= m_nActive)
1089 m_nActive += 1;
1090 ++iIndex;
1091 }
1092 else
1093 { // remove entry from list
1094 if (nPos < nNewPos)
1095 {
1096 --nNewPos;
1097 }
1098 if (nPos < nChangedActivePos)
1099 {
1100 --nChangedActivePos;
1101 }
1102 if (nPos < m_nActive)
1103 m_nActive -= 1;
1104 else if (nPos == m_nActive)
1105 {
1106 nChangedActivePos = nPos;
1107 m_nActive = -1;
1108 m_bHasActive = false;
1109 }
1110 m_vRemovedEntries.push_back(*iIndex);
1111 (*iIndex)->m_xPackage->removeEventListener(m_xRemoveListener);
1112 iIndex = m_vEntries.erase(iIndex);
1113 }
1114 }
1115 else
1116 ++iIndex;
1117 }
1118 }
1119
1120 m_bInCheckMode = false;
1121
1122 if ( nNewPos != - 1)
1123 selectEntry( nNewPos );
1124 else if (nChangedActivePos != -1) {
1125 selectEntry(nChangedActivePos);
1126 }
1127
1128 if ( bNeedsUpdate )
1129 {
1130 m_bNeedsRecalc = true;
1131 if ( IsReallyVisible() )
1132 Invalidate();
1133 }
1134}
1135
1137{
1138 m_nTopIndex = rScrBar.vadjustment_get_value();
1139 Invalidate();
1140}
1141
1142} //namespace dp_gui
1143
1144/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
const StyleSettings & GetStyleSettings() const
static const AllSettings & GetSettings()
sal_Int32 compareString(const OUString &s1, const OUString &s2) const
Size GetSizePixel() const
const vcl::KeyCode & GetKeyCode() const
bool IsMod1() const
const Point & GetPosPixel() const
bool IsLeft() const
const vcl::Font & GetFont() const
void SetFont(const vcl::Font &rNewFont)
void DrawRect(const tools::Rectangle &rRect)
void DrawLine(const Point &rStartPt, const Point &rEndPt)
void SetLineColor()
OUString GetEllipsisString(const OUString &rStr, tools::Long nMaxWidth, DrawTextFlags nStyle=DrawTextFlags::EndEllipsis) const
tools::Long GetTextWidth(const OUString &rStr, sal_Int32 nIndex=0, sal_Int32 nLen=-1, vcl::text::TextLayoutCache const *=nullptr, SalLayoutGlyphs const *const pLayoutCache=nullptr) const
void SetTextColor(const Color &rColor)
void DrawImage(const Point &rPos, const Image &rImage, DrawImageFlags nStyle=DrawImageFlags::NONE)
void SetFillColor()
SAL_WARN_UNUSED_RESULT Point LogicToPixel(const Point &rLogicPt) const
tools::Rectangle GetTextRect(const tools::Rectangle &rRect, const OUString &rStr, DrawTextFlags nStyle=DrawTextFlags::WordBreak, TextRectInfo *pInfo=nullptr, const vcl::ITextLayout *_pTextLayout=nullptr) const
void SetTextFillColor()
void Push(vcl::PushFlags nFlags=vcl::PushFlags::ALL)
tools::Long GetTextHeight() const
void SetBackground()
void DrawText(const Point &rStartPt, const OUString &rStr, sal_Int32 nIndex=0, sal_Int32 nLen=-1, std::vector< tools::Rectangle > *pVector=nullptr, OUString *pDisplayText=nullptr, const SalLayoutGlyphs *pLayoutCache=nullptr)
const AllSettings & GetSettings() const
constexpr tools::Long Y() const
tools::Long AdjustY(tools::Long nVertMove)
constexpr tools::Long X() const
constexpr tools::Long Height() const
tools::Long AdjustWidth(tools::Long n)
void setHeight(tools::Long nHeight)
constexpr tools::Long Width() const
const Color & GetFieldTextColor() const
const Color & GetFieldColor() const
const Color & GetLinkColor() const
const Color & GetHighlightColor() const
const Color & GetHighlightTextColor() const
const Color & GetDisableColor() const
virtual void selectEntry(const tools::Long nPos)
std::vector< TEntry_Impl > m_vEntries
tools::Long PointToPos(const Point &rPos)
virtual ~ExtensionBox_Impl() override
bool FindEntryPos(const TEntry_Impl &rEntry, tools::Long nStart, tools::Long nEnd, tools::Long &nFound)
virtual void SetDrawingArea(weld::DrawingArea *pDrawingArea) override
void updateEntry(const css::uno::Reference< css::deployment::XPackage > &xPackage)
tools::Long GetTotalHeight() const
tools::Rectangle GetEntryRect(const tools::Long nPos) const
void DrawRow(vcl::RenderContext &rRenderContext, const tools::Rectangle &rRect, const TEntry_Impl &rEntry)
std::vector< TEntry_Impl > m_vRemovedEntries
void addEventListenerOnce(css::uno::Reference< css::deployment::XPackage > const &extension)
std::optional< CollatorWrapper > m_oCollator
virtual void Resize() override
virtual void Paint(vcl::RenderContext &rRenderContext, const tools::Rectangle &rPaintRect) override
mutable::osl::Mutex m_entriesMutex
std::unique_ptr< css::lang::Locale > m_pLocale
rtl::Reference< ExtensionRemovedListener > m_xRemoveListener
std::vector< css::uno::WeakReference< css::deployment::XPackage > > m_vListenerAdded
virtual bool MouseButtonDown(const MouseEvent &rMEvt) override
void removeEntry(const css::uno::Reference< css::deployment::XPackage > &xPackage)
ExtensionBox_Impl(std::unique_ptr< weld::ScrolledWindow > xScroll)
std::unique_ptr< weld::ScrolledWindow > m_xScrollBar
virtual OUString RequestHelp(tools::Rectangle &rRect) override
TheExtensionManager * m_pManager
bool HandleCursorKey(sal_uInt16 nKeyCode)
virtual bool KeyInput(const KeyEvent &rKEvt) override
void CalcActiveHeight(const tools::Long nPos)
void addEntry(const css::uno::Reference< css::deployment::XPackage > &xPackage, bool bLicenseMissing=false)
virtual bool MouseMove(const MouseEvent &rMEvt) override
virtual void SAL_CALL disposing(css::lang::EventObject const &evt) override
virtual ~ExtensionRemovedListener() override
static PackageState getPackageState(const css::uno::Reference< css::deployment::XPackage > &xPackage)
bool isReadOnly(const css::uno::Reference< css::deployment::XPackage > &xPackage) const
bool supportsOptions(const css::uno::Reference< css::deployment::XPackage > &xPackage) const
constexpr tools::Long GetWidth() const
constexpr tools::Long Top() const
constexpr Point TopLeft() const
void Move(tools::Long nHorzMoveDelta, tools::Long nVertMoveDelta)
constexpr tools::Long Right() const
constexpr Point BottomRight() const
constexpr Point TopRight() const
constexpr tools::Long GetHeight() const
constexpr tools::Long Bottom() const
constexpr Point BottomLeft() const
void SetWeight(FontWeight)
void SetUnderline(FontLineStyle)
sal_uInt16 GetGroup() const
sal_uInt16 GetCode() const
void SetPointer(PointerStyle ePointerStyle)
weld::DrawingArea * GetDrawingArea() const
void SetOutputSizePixel(const Size &rSize)
Size const & GetOutputSizePixel() const
virtual OutputDevice & get_ref_device()=0
virtual void set_size_request(int nWidth, int nHeight)=0
constexpr ::Color COL_LIGHTGRAY(0xC0, 0xC0, 0xC0)
uno::Reference< deployment::XPackage > m_xPackage
constexpr OUStringLiteral SHARED_PACKAGE_MANAGER
const uno::Reference< deployment::XPackage > m_extension
constexpr OUStringLiteral USER_PACKAGE_MANAGER
#define SPACE_BETWEEN
#define SMALL_ICON_SIZE
#define ICON_OFFSET
#define ICON_WIDTH
#define ICON_HEIGHT
#define TOP_OFFSET
#define RIGHT_ICON_OFFSET
OUString DpResId(TranslateId aId)
Definition: dp_misc.cxx:555
float u
LINESTYLE_SINGLE
WEIGHT_BOLD
bool bReadOnly
StockImage
constexpr sal_uInt16 KEY_HOME
constexpr sal_uInt16 KEY_LEFT
constexpr sal_uInt16 KEY_PAGEDOWN
constexpr sal_uInt16 KEY_TAB
constexpr sal_uInt16 KEY_UP
constexpr sal_uInt16 KEY_RIGHT
constexpr sal_uInt16 KEY_DOWN
constexpr sal_uInt16 KEY_PAGEUP
constexpr sal_uInt16 KEY_END
constexpr sal_uInt16 KEYGROUP_CURSOR
sal_uInt16 nPos
Yes
const LanguageTag & getLocale()
Reference< XComponentContext > getProcessComponentContext()
Definition: dp_gui.h:22
PackageState
Definition: dp_gui.h:24
@ AMBIGUOUS
Definition: dp_gui.h:24
@ REGISTERED
Definition: dp_gui.h:24
@ NOT_REGISTERED
Definition: dp_gui.h:24
@ NOT_AVAILABLE
Definition: dp_gui.h:24
constexpr OUStringLiteral SHARED_PACKAGE_MANAGER
IMPL_LINK(ExtMgrDialog, startProgress, void *, _bLockInterface, void)
constexpr OUStringLiteral USER_PACKAGE_MANAGER
std::shared_ptr< Entry_Impl > TEntry_Impl
DESKTOP_DEPLOYMENTMISC_DLLPUBLIC OUString getErrorText(css::uno::Reference< css::xml::dom::XElement > const &dependency)
Obtain the (human-readable) error message of a failed dependency.
css::uno::Reference< css::deployment::XPackageRegistry > create(css::uno::Reference< css::deployment::XPackageRegistry > const &xRootRegistry, OUString const &context, OUString const &cachePath, css::uno::Reference< css::uno::XComponentContext > const &xComponentContext)
int i
constexpr std::enable_if_t< std::is_signed_v< T >, std::make_unsigned_t< T > > make_unsigned(T value)
long Long
void SetPointFont(OutputDevice &rDevice, const vcl::Font &rFont)
sal_Int32 CompareTo(const CollatorWrapper *pCollator, const TEntry_Impl &rEntry) const
Entry_Impl(const css::uno::Reference< css::deployment::XPackage > &xPackage, const PackageState eState, const bool bReadOnly)
css::uno::Reference< css::deployment::XPackage > m_xPackage
ToolBarManager * m_pManager