1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <svtools/controldims.hxx>
22 #include <dp_shared.hxx>
23 #include <strings.hrc>
25 #include "dp_gui_extlistbox.hxx"
26 #include "dp_gui_theextmgr.hxx"
27 #include "dp_gui_dialog2.hxx"
28 #include <dp_dependencies.hxx>
29 #include <bitmaps.hlst>
31 #include <comphelper/processfactory.hxx>
32 #include <com/sun/star/i18n/CollatorOptions.hpp>
33 #include <com/sun/star/deployment/DependencyException.hpp>
34 #include <com/sun/star/deployment/DeploymentException.hpp>
35 #include <com/sun/star/deployment/ExtensionRemovedException.hpp>
36 #include <com/sun/star/system/XSystemShellExecute.hpp>
37 #include <com/sun/star/system/SystemShellExecuteFlags.hpp>
38 #include <com/sun/star/system/SystemShellExecute.hpp>
39 #include <cppuhelper/weakref.hxx>
40 #include <i18nlangtag/languagetag.hxx>
41 #include <vcl/commandevent.hxx>
42 #include <vcl/event.hxx>
43 #include <vcl/ptrstyle.hxx>
44 #include <vcl/svapp.hxx>
45 #include <vcl/settings.hxx>
48 #define USER_PACKAGE_MANAGER "user"
49 #define SHARED_PACKAGE_MANAGER "shared"
51 using namespace ::com::sun::star
;
59 const uno::Reference
<deployment::XPackage
> m_extension
;
61 explicit FindWeakRef( uno::Reference
<deployment::XPackage
> const & ext
): m_extension(ext
) {}
62 bool operator () (uno::WeakReference
< deployment::XPackage
> const & ref
);
65 bool FindWeakRef::operator () (uno::WeakReference
< deployment::XPackage
> const & ref
)
67 const uno::Reference
<deployment::XPackage
> ext(ref
);
68 return ext
== m_extension
;
75 Entry_Impl::Entry_Impl( const uno::Reference
< deployment::XPackage
> &xPackage
,
76 const PackageState eState
, const bool bReadOnly
) :
78 m_bLocked( bReadOnly
),
79 m_bHasOptions( false ),
84 m_bMissingDeps( false ),
85 m_bHasButtons( false ),
86 m_bMissingLic( false ),
88 m_xPackage( xPackage
)
92 m_sTitle
= xPackage
->getDisplayName();
93 m_sVersion
= xPackage
->getVersion();
94 m_sDescription
= xPackage
->getDescription();
95 m_sLicenseText
= xPackage
->getLicenseText();
97 beans::StringPair
aInfo( m_xPackage
->getPublisherInfo() );
98 m_sPublisher
= aInfo
.First
;
99 m_sPublisherURL
= aInfo
.Second
;
101 // get the icons for the package if there are any
102 uno::Reference
< graphic::XGraphic
> xGraphic
= xPackage
->getIcon( false );
104 m_aIcon
= Image( xGraphic
);
106 if ( eState
== AMBIGUOUS
)
107 m_sErrorText
= DpResId( RID_STR_ERROR_UNKNOWN_STATUS
);
108 else if ( eState
== NOT_REGISTERED
)
111 catch (const deployment::ExtensionRemovedException
&) {}
112 catch (const uno::RuntimeException
&) {}
116 Entry_Impl::~Entry_Impl()
120 sal_Int32
Entry_Impl::CompareTo( const CollatorWrapper
*pCollator
, const TEntry_Impl
& rEntry
) const
122 sal_Int32 eCompare
= pCollator
->compareString( m_sTitle
, rEntry
->m_sTitle
);
125 eCompare
= m_sVersion
.compareTo( rEntry
->m_sVersion
);
128 sal_Int32 nCompare
= m_xPackage
->getRepositoryName().compareTo( rEntry
->m_xPackage
->getRepositoryName() );
131 else if ( nCompare
> 0 )
139 void Entry_Impl::checkDependencies()
142 m_xPackage
->checkDependencies( uno::Reference
< ucb::XCommandEnvironment
>() );
144 catch ( const deployment::DeploymentException
&e
)
146 deployment::DependencyException depExc
;
147 if ( e
.Cause
>>= depExc
)
149 OUStringBuffer
aMissingDep( DpResId( RID_STR_ERROR_MISSING_DEPENDENCIES
) );
150 for ( sal_Int32 i
= 0; i
< depExc
.UnsatisfiedDependencies
.getLength(); ++i
)
152 aMissingDep
.append("\n");
153 aMissingDep
.append(dp_misc::Dependencies::getErrorText( depExc
.UnsatisfiedDependencies
[i
]));
155 aMissingDep
.append("\n");
156 m_sErrorText
= aMissingDep
.makeStringAndClear();
157 m_bMissingDeps
= true;
162 // ExtensionRemovedListener
164 void ExtensionRemovedListener::disposing( lang::EventObject
const & rEvt
)
166 uno::Reference
< deployment::XPackage
> xPackage( rEvt
.Source
, uno::UNO_QUERY
);
170 m_pParent
->removeEntry( xPackage
);
175 ExtensionRemovedListener::~ExtensionRemovedListener()
181 ExtensionBox_Impl::ExtensionBox_Impl(std::unique_ptr
<weld::ScrolledWindow
> xScroll
)
182 : m_bHasScrollBar( false )
183 , m_bHasActive( false )
184 , m_bNeedsRecalc( true )
185 , m_bInCheckMode( false )
186 , m_bAdjustActive( false )
187 , m_bInDelete( false )
191 , m_nActiveHeight( 0 )
192 , m_aSharedImage(StockImage::Yes
, RID_BMP_SHARED
)
193 , m_aLockedImage(StockImage::Yes
, RID_BMP_LOCKED
)
194 , m_aWarningImage(StockImage::Yes
, RID_BMP_WARNING
)
195 , m_aDefaultImage(StockImage::Yes
, RID_BMP_EXTENSION
)
196 , m_pManager( nullptr )
197 , m_xScrollBar(std::move(xScroll
))
201 void ExtensionBox_Impl::Init()
203 m_xScrollBar
->set_user_managed_scrolling();
204 m_xScrollBar
->connect_vadjustment_changed( LINK( this, ExtensionBox_Impl
, ScrollHdl
) );
206 auto nIconHeight
= 2*TOP_OFFSET
+ SMALL_ICON_SIZE
;
207 auto nTitleHeight
= 2*TOP_OFFSET
+ GetTextHeight();
208 if ( nIconHeight
< nTitleHeight
)
209 m_nStdHeight
= nTitleHeight
;
211 m_nStdHeight
= nIconHeight
;
212 m_nStdHeight
+= GetTextHeight() + TOP_OFFSET
;
214 nIconHeight
= ICON_HEIGHT
+ 2*TOP_OFFSET
+ 1;
215 if ( m_nStdHeight
< nIconHeight
)
216 m_nStdHeight
= nIconHeight
;
218 m_nActiveHeight
= m_nStdHeight
;
220 m_xRemoveListener
= new ExtensionRemovedListener( this );
222 m_pLocale
.reset( new lang::Locale( Application::GetSettings().GetLanguageTag().getLocale() ) );
223 m_pCollator
.reset( new CollatorWrapper( ::comphelper::getProcessComponentContext() ) );
224 m_pCollator
->loadDefaultCollator( *m_pLocale
, i18n::CollatorOptions::CollatorOptions_IGNORE_CASE
);
227 ExtensionBox_Impl::~ExtensionBox_Impl()
234 for (auto const& entry
: m_vEntries
)
236 entry
->m_xPackage
->removeEventListener( m_xRemoveListener
.get() );
241 m_xRemoveListener
.clear();
247 sal_Int32
ExtensionBox_Impl::getItemCount() const
249 return static_cast< sal_Int32
>( m_vEntries
.size() );
253 sal_Int32
ExtensionBox_Impl::getSelIndex() const
257 OSL_ASSERT( m_nActive
>= -1);
258 return static_cast< sal_Int32
>( m_nActive
);
261 return ENTRY_NOTFOUND
;
265 // Title + description
266 void ExtensionBox_Impl::CalcActiveHeight( const long nPos
)
268 const ::osl::MutexGuard
aGuard( m_entriesMutex
);
272 long nIconHeight
= 2*TOP_OFFSET
+ SMALL_ICON_SIZE
;
273 long nTitleHeight
= 2*TOP_OFFSET
+ GetTextHeight();
274 if ( nIconHeight
< nTitleHeight
)
275 aTextHeight
= nTitleHeight
;
277 aTextHeight
= nIconHeight
;
279 // calc description height
280 Size aSize
= GetOutputSizePixel();
282 aSize
.AdjustWidth( -(ICON_OFFSET
) );
283 aSize
.setHeight( 10000 );
285 OUString
aText( m_vEntries
[ nPos
]->m_sErrorText
);
286 if ( !aText
.isEmpty() )
288 aText
+= m_vEntries
[ nPos
]->m_sDescription
;
290 tools::Rectangle aRect
= GetDrawingArea()->get_ref_device().GetTextRect(tools::Rectangle( Point(), aSize
), aText
,
291 DrawTextFlags::MultiLine
| DrawTextFlags::WordBreak
);
292 aTextHeight
+= aRect
.GetHeight();
294 if ( aTextHeight
< m_nStdHeight
)
295 aTextHeight
= m_nStdHeight
;
297 m_nActiveHeight
= aTextHeight
;
299 if ( m_vEntries
[ nPos
]->m_bHasButtons
)
300 m_nActiveHeight
+= 2;
303 tools::Rectangle
ExtensionBox_Impl::GetEntryRect( const long nPos
) const
305 const ::osl::MutexGuard
aGuard( m_entriesMutex
);
307 Size
aSize( GetOutputSizePixel() );
309 if ( m_vEntries
[ nPos
]->m_bActive
)
310 aSize
.setHeight( m_nActiveHeight
);
312 aSize
.setHeight( m_nStdHeight
);
314 Point
aPos( 0, -m_nTopIndex
+ nPos
* m_nStdHeight
);
315 if ( m_bHasActive
&& ( nPos
< m_nActive
) )
316 aPos
.AdjustY(m_nActiveHeight
- m_nStdHeight
);
318 return tools::Rectangle( aPos
, aSize
);
322 void ExtensionBox_Impl::DeleteRemoved()
324 const ::osl::MutexGuard
aGuard( m_entriesMutex
);
328 m_vRemovedEntries
.clear();
334 //This function may be called with nPos < 0
335 void ExtensionBox_Impl::selectEntry( const long nPos
)
337 bool invalidate
= false;
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
);
347 if ( m_bInCheckMode
)
352 if ( nPos
== m_nActive
)
355 m_bHasActive
= false;
356 m_vEntries
[ m_nActive
]->m_bActive
= false;
359 if ( ( nPos
>= 0 ) && ( nPos
< static_cast<long>(m_vEntries
.size()) ) )
363 m_vEntries
[ nPos
]->m_bActive
= true;
365 if ( IsReallyVisible() )
367 m_bAdjustActive
= true;
371 if ( IsReallyVisible() )
373 m_bNeedsRecalc
= true;
386 void ExtensionBox_Impl::DrawRow(vcl::RenderContext
& rRenderContext
, const tools::Rectangle
& rRect
, const TEntry_Impl
& rEntry
)
388 const StyleSettings
& rStyleSettings
= rRenderContext
.GetSettings().GetStyleSettings();
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());
395 rRenderContext
.SetTextColor(rStyleSettings
.GetFieldTextColor());
397 if (rEntry
->m_bActive
)
399 rRenderContext
.SetLineColor();
400 rRenderContext
.SetFillColor(rStyleSettings
.GetHighlightColor());
401 rRenderContext
.DrawRect(rRect
);
405 rRenderContext
.SetBackground(rStyleSettings
.GetFieldColor());
406 rRenderContext
.SetTextFillColor();
407 rRenderContext
.Erase(rRect
);
410 // Draw extension icon
411 Point
aPos( rRect
.TopLeft() );
412 aPos
+= Point(TOP_OFFSET
, TOP_OFFSET
);
414 if (!rEntry
->m_aIcon
)
415 aImage
= m_aDefaultImage
;
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)),
424 rRenderContext
.DrawImage(aPos
, Size(ICON_WIDTH
, ICON_HEIGHT
), aImage
);
427 // expand the point size of the desired font to the equivalent pixel size
428 if (vcl::Window
* pDefaultDevice
= dynamic_cast<vcl::Window
*>(Application::GetDefaultDevice()))
429 pDefaultDevice
->SetPointFont(rRenderContext
, GetDrawingArea()->get_font());
430 vcl::Font
aStdFont(rRenderContext
.GetFont());
431 vcl::Font
aBoldFont(aStdFont
);
432 aBoldFont
.SetWeight(WEIGHT_BOLD
);
433 rRenderContext
.SetFont(aBoldFont
);
434 auto aTextHeight
= rRenderContext
.GetTextHeight();
436 // Get max title width
437 auto nMaxTitleWidth
= rRect
.GetWidth() - ICON_OFFSET
;
438 nMaxTitleWidth
-= (2 * SMALL_ICON_SIZE
) + (4 * SPACE_BETWEEN
);
439 rRenderContext
.SetFont(aStdFont
);
441 if (!rEntry
->m_sPublisher
.isEmpty())
443 nLinkWidth
= rRenderContext
.GetTextWidth(rEntry
->m_sPublisher
);
444 nMaxTitleWidth
-= nLinkWidth
+ (2 * SPACE_BETWEEN
);
446 long aVersionWidth
= rRenderContext
.GetTextWidth(rEntry
->m_sVersion
);
448 aPos
= rRect
.TopLeft() + Point(ICON_OFFSET
, TOP_OFFSET
);
450 rRenderContext
.SetFont(aBoldFont
);
451 long aTitleWidth
= rRenderContext
.GetTextWidth(rEntry
->m_sTitle
) + (aTextHeight
/ 3);
452 if (aTitleWidth
> nMaxTitleWidth
- aVersionWidth
)
454 aTitleWidth
= nMaxTitleWidth
- aVersionWidth
- (aTextHeight
/ 3);
455 OUString aShortTitle
= rRenderContext
.GetEllipsisString(rEntry
->m_sTitle
, aTitleWidth
);
456 rRenderContext
.DrawText(aPos
, aShortTitle
);
457 aTitleWidth
+= (aTextHeight
/ 3);
460 rRenderContext
.DrawText(aPos
, rEntry
->m_sTitle
);
462 rRenderContext
.SetFont(aStdFont
);
463 rRenderContext
.DrawText(Point(aPos
.X() + aTitleWidth
, aPos
.Y()), rEntry
->m_sVersion
);
465 long nIconHeight
= TOP_OFFSET
+ SMALL_ICON_SIZE
;
466 long nTitleHeight
= TOP_OFFSET
+ GetTextHeight();
467 if ( nIconHeight
< nTitleHeight
)
468 aTextHeight
= nTitleHeight
;
470 aTextHeight
= nIconHeight
;
473 OUString sDescription
;
474 if (!rEntry
->m_sErrorText
.isEmpty())
476 if (rEntry
->m_bActive
)
477 sDescription
= rEntry
->m_sErrorText
+ "\n" + rEntry
->m_sDescription
;
479 sDescription
= rEntry
->m_sErrorText
;
482 sDescription
= rEntry
->m_sDescription
;
484 aPos
.AdjustY(aTextHeight
);
485 if (rEntry
->m_bActive
)
487 long nExtraHeight
= 0;
489 if (rEntry
->m_bHasButtons
)
492 rRenderContext
.DrawText(tools::Rectangle(aPos
.X(), aPos
.Y(), rRect
.Right(), rRect
.Bottom() - nExtraHeight
),
493 sDescription
, DrawTextFlags::MultiLine
| DrawTextFlags::WordBreak
);
497 //replace LF to space, so words do not stick together in one line view
498 sDescription
= sDescription
.replace(0x000A, ' ');
499 const long nWidth
= rRenderContext
.GetTextWidth( sDescription
);
500 if (nWidth
> rRect
.GetWidth() - aPos
.X())
501 sDescription
= rRenderContext
.GetEllipsisString(sDescription
, rRect
.GetWidth() - aPos
.X());
502 rRenderContext
.DrawText(aPos
, sDescription
);
505 // Draw publisher link
506 if (!rEntry
->m_sPublisher
.isEmpty())
508 aPos
= rRect
.TopLeft() + Point( ICON_OFFSET
+ nMaxTitleWidth
+ (2*SPACE_BETWEEN
), TOP_OFFSET
);
510 rRenderContext
.Push(PushFlags::FONT
| PushFlags::TEXTCOLOR
| PushFlags::TEXTFILLCOLOR
);
511 rRenderContext
.SetTextColor(rStyleSettings
.GetLinkColor());
512 rRenderContext
.SetTextFillColor(rStyleSettings
.GetFieldColor());
513 vcl::Font aFont
= rRenderContext
.GetFont();
515 aFont
.SetUnderline(LINESTYLE_SINGLE
);
516 rRenderContext
.SetFont(aFont
);
517 rRenderContext
.DrawText(aPos
, rEntry
->m_sPublisher
);
518 rEntry
->m_aLinkRect
= tools::Rectangle(aPos
, Size(nLinkWidth
, aTextHeight
));
519 rRenderContext
.Pop();
523 if (!rEntry
->m_bUser
)
525 aPos
= rRect
.TopRight() + Point( -(RIGHT_ICON_OFFSET
+ SMALL_ICON_SIZE
), TOP_OFFSET
);
526 if (rEntry
->m_bLocked
)
527 rRenderContext
.DrawImage(aPos
, Size(SMALL_ICON_SIZE
, SMALL_ICON_SIZE
), m_aLockedImage
);
529 rRenderContext
.DrawImage(aPos
, Size(SMALL_ICON_SIZE
, SMALL_ICON_SIZE
), m_aSharedImage
);
531 if ((rEntry
->m_eState
== AMBIGUOUS
) || rEntry
->m_bMissingDeps
|| rEntry
->m_bMissingLic
)
533 aPos
= rRect
.TopRight() + Point(-(RIGHT_ICON_OFFSET
+ SPACE_BETWEEN
+ 2 * SMALL_ICON_SIZE
), TOP_OFFSET
);
534 rRenderContext
.DrawImage(aPos
, Size(SMALL_ICON_SIZE
, SMALL_ICON_SIZE
), m_aWarningImage
);
537 rRenderContext
.SetLineColor(COL_LIGHTGRAY
);
538 rRenderContext
.DrawLine(rRect
.BottomLeft(), rRect
.BottomRight());
542 void ExtensionBox_Impl::RecalcAll()
545 CalcActiveHeight( m_nActive
);
551 tools::Rectangle aEntryRect
= GetEntryRect( m_nActive
);
553 if ( m_bAdjustActive
)
555 m_bAdjustActive
= false;
557 // If the top of the selected entry isn't visible, make it visible
558 if ( aEntryRect
.Top() < 0 )
560 m_nTopIndex
+= aEntryRect
.Top();
561 aEntryRect
.Move( 0, -aEntryRect
.Top() );
564 // If the bottom of the selected entry isn't visible, make it visible even if now the top
565 // isn't visible any longer ( the buttons are more important )
566 Size aOutputSize
= GetOutputSizePixel();
567 if ( aEntryRect
.Bottom() > aOutputSize
.Height() )
569 m_nTopIndex
+= ( aEntryRect
.Bottom() - aOutputSize
.Height() );
570 aEntryRect
.Move( 0, -( aEntryRect
.Bottom() - aOutputSize
.Height() ) );
573 // If there is unused space below the last entry but all entries don't fit into the box,
574 // move the content down to use the whole space
575 const long nTotalHeight
= GetTotalHeight();
576 if ( m_bHasScrollBar
&& ( aOutputSize
.Height() + m_nTopIndex
> nTotalHeight
) )
578 long nOffset
= m_nTopIndex
;
579 m_nTopIndex
= nTotalHeight
- aOutputSize
.Height();
580 nOffset
-= m_nTopIndex
;
581 aEntryRect
.Move( 0, nOffset
);
584 if ( m_bHasScrollBar
)
585 m_xScrollBar
->vadjustment_set_value( m_nTopIndex
);
589 m_bNeedsRecalc
= false;
593 bool ExtensionBox_Impl::HandleCursorKey( sal_uInt16 nKeyCode
)
595 if ( m_vEntries
.empty() )
602 long nPageSize
= GetOutputSizePixel().Height() / m_nStdHeight
;
606 if ( ( nKeyCode
== KEY_DOWN
) || ( nKeyCode
== KEY_RIGHT
) )
607 nSelect
= m_nActive
+ 1;
608 else if ( ( nKeyCode
== KEY_UP
) || ( nKeyCode
== KEY_LEFT
) )
609 nSelect
= m_nActive
- 1;
610 else if ( nKeyCode
== KEY_HOME
)
612 else if ( nKeyCode
== KEY_END
)
613 nSelect
= m_vEntries
.size() - 1;
614 else if ( nKeyCode
== KEY_PAGEUP
)
615 nSelect
= m_nActive
- nPageSize
+ 1;
616 else if ( nKeyCode
== KEY_PAGEDOWN
)
617 nSelect
= m_nActive
+ nPageSize
- 1;
619 else // when there is no selected entry, we will select the first or the last.
621 if ( ( nKeyCode
== KEY_DOWN
) || ( nKeyCode
== KEY_PAGEDOWN
) || ( nKeyCode
== KEY_HOME
) )
623 else if ( ( nKeyCode
== KEY_UP
) || ( nKeyCode
== KEY_PAGEUP
) || ( nKeyCode
== KEY_END
) )
624 nSelect
= m_vEntries
.size() - 1;
629 if ( nSelect
>= static_cast<long>(m_vEntries
.size()) )
630 nSelect
= m_vEntries
.size() - 1;
632 selectEntry( nSelect
);
638 void ExtensionBox_Impl::Paint(vcl::RenderContext
& rRenderContext
, const tools::Rectangle
& /*rPaintRect*/)
643 if ( m_bNeedsRecalc
)
646 Point
aStart( 0, -m_nTopIndex
);
647 Size
aSize(GetOutputSizePixel());
649 const ::osl::MutexGuard
aGuard( m_entriesMutex
);
651 for (auto const& entry
: m_vEntries
)
653 aSize
.setHeight( entry
->m_bActive
? m_nActiveHeight
: m_nStdHeight
);
654 tools::Rectangle
aEntryRect( aStart
, aSize
);
655 DrawRow(rRenderContext
, aEntryRect
, entry
);
656 aStart
.AdjustY(aSize
.Height() );
661 long ExtensionBox_Impl::GetTotalHeight() const
663 long nHeight
= m_vEntries
.size() * m_nStdHeight
;
667 nHeight
+= m_nActiveHeight
- m_nStdHeight
;
674 void ExtensionBox_Impl::SetupScrollBar()
676 const Size aSize
= GetOutputSizePixel();
677 const auto nTotalHeight
= GetTotalHeight();
678 const bool bNeedsScrollBar
= ( nTotalHeight
> aSize
.Height() );
680 if ( bNeedsScrollBar
)
682 if ( m_nTopIndex
+ aSize
.Height() > nTotalHeight
)
683 m_nTopIndex
= nTotalHeight
- aSize
.Height();
685 m_xScrollBar
->vadjustment_configure(m_nTopIndex
, 0, nTotalHeight
,
686 m_nStdHeight
, ( aSize
.Height() * 4 ) / 5,
689 if (!m_bHasScrollBar
)
690 m_xScrollBar
->set_vpolicy(VclPolicyType::ALWAYS
);
692 else if ( m_bHasScrollBar
)
694 m_xScrollBar
->set_vpolicy(VclPolicyType::NEVER
);
698 m_bHasScrollBar
= bNeedsScrollBar
;
702 void ExtensionBox_Impl::Resize()
708 void ExtensionBox_Impl::SetDrawingArea(weld::DrawingArea
* pDrawingArea
)
710 Size aSize
= pDrawingArea
->get_ref_device().LogicToPixel(Size(250, 150), MapMode(MapUnit::MapAppFont
));
711 pDrawingArea
->set_size_request(aSize
.Width(), aSize
.Height());
712 CustomWidgetController::SetDrawingArea(pDrawingArea
);
713 SetOutputSizePixel(aSize
);
718 long ExtensionBox_Impl::PointToPos( const Point
& rPos
)
720 long nPos
= ( rPos
.Y() + m_nTopIndex
) / m_nStdHeight
;
722 if ( m_bHasActive
&& ( nPos
> m_nActive
) )
724 if ( rPos
.Y() + m_nTopIndex
<= m_nActive
*m_nStdHeight
+ m_nActiveHeight
)
727 nPos
= ( rPos
.Y() + m_nTopIndex
- (m_nActiveHeight
- m_nStdHeight
) ) / m_nStdHeight
;
733 bool ExtensionBox_Impl::MouseMove( const MouseEvent
& rMEvt
)
735 bool bOverHyperlink
= false;
737 auto nPos
= PointToPos( rMEvt
.GetPosPixel() );
738 if ( ( nPos
>= 0 ) && ( nPos
< static_cast<long>(m_vEntries
.size()) ) )
740 const auto& rEntry
= m_vEntries
[nPos
];
741 bOverHyperlink
= !rEntry
->m_sPublisher
.isEmpty() && rEntry
->m_aLinkRect
.IsInside(rMEvt
.GetPosPixel());
745 SetPointer(PointerStyle::RefHand
);
747 SetPointer(PointerStyle::Arrow
);
752 OUString
ExtensionBox_Impl::RequestHelp(tools::Rectangle
& rRect
)
754 auto nPos
= PointToPos( rRect
.TopLeft() );
755 if ( ( nPos
>= 0 ) && ( nPos
< static_cast<long>(m_vEntries
.size()) ) )
757 const auto& rEntry
= m_vEntries
[nPos
];
758 bool bOverHyperlink
= !rEntry
->m_sPublisher
.isEmpty() && rEntry
->m_aLinkRect
.IsInside(rRect
);
761 rRect
= rEntry
->m_aLinkRect
;
762 return rEntry
->m_sPublisherURL
;
769 bool ExtensionBox_Impl::MouseButtonDown( const MouseEvent
& rMEvt
)
771 if ( rMEvt
.IsLeft() )
773 if (rMEvt
.IsMod1() && m_bHasActive
)
774 selectEntry(ExtensionBox_Impl::ENTRY_NOTFOUND
); // Selecting a not existing entry will deselect the current one
777 auto nPos
= PointToPos( rMEvt
.GetPosPixel() );
779 if ( ( nPos
>= 0 ) && ( nPos
< static_cast<long>(m_vEntries
.size()) ) )
781 const auto& rEntry
= m_vEntries
[nPos
];
782 if (!rEntry
->m_sPublisher
.isEmpty() && rEntry
->m_aLinkRect
.IsInside(rMEvt
.GetPosPixel()))
786 css::uno::Reference
<css::system::XSystemShellExecute
> xSystemShellExecute(
787 css::system::SystemShellExecute::create(comphelper::getProcessComponentContext()));
788 //throws css::lang::IllegalArgumentException, css::system::SystemShellExecuteException
789 xSystemShellExecute
->execute(rEntry
->m_sPublisherURL
, OUString(), css::system::SystemShellExecuteFlags::URIS_ONLY
);
806 bool ExtensionBox_Impl::KeyInput(const KeyEvent
& rKEvt
)
811 vcl::KeyCode aKeyCode
= rKEvt
.GetKeyCode();
812 sal_uInt16 nKeyCode
= aKeyCode
.GetCode();
814 bool bHandled
= false;
815 if (nKeyCode
!= KEY_TAB
&& aKeyCode
.GetGroup() == KEYGROUP_CURSOR
)
816 bHandled
= HandleCursorKey(nKeyCode
);
821 bool ExtensionBox_Impl::FindEntryPos( const TEntry_Impl
& rEntry
, const long nStart
,
822 const long nEnd
, long &nPos
)
830 if ( nStart
== nEnd
)
832 eCompare
= rEntry
->CompareTo( m_pCollator
.get(), m_vEntries
[ nStart
] );
835 else if ( eCompare
== 0 )
837 //Workaround. See i86963.
838 if (rEntry
->m_xPackage
!= m_vEntries
[nStart
]->m_xPackage
)
841 if ( m_bInCheckMode
)
842 m_vEntries
[ nStart
]->m_bChecked
= true;
852 const long nMid
= nStart
+ ( ( nEnd
- nStart
) / 2 );
853 eCompare
= rEntry
->CompareTo( m_pCollator
.get(), m_vEntries
[ nMid
] );
856 return FindEntryPos( rEntry
, nStart
, nMid
-1, nPos
);
857 else if ( eCompare
> 0 )
858 return FindEntryPos( rEntry
, nMid
+1, nEnd
, nPos
);
861 //Workaround.See i86963.
862 if (rEntry
->m_xPackage
!= m_vEntries
[nMid
]->m_xPackage
)
865 if ( m_bInCheckMode
)
866 m_vEntries
[ nMid
]->m_bChecked
= true;
872 void ExtensionBox_Impl::cleanVecListenerAdded()
874 m_vListenerAdded
.erase(std::remove_if(m_vListenerAdded
.begin(), m_vListenerAdded
.end(),
875 [](const uno::WeakReference
<deployment::XPackage
>& rxListener
) {
876 const uno::Reference
<deployment::XPackage
> hardRef(rxListener
);
877 return !hardRef
.is();
879 m_vListenerAdded
.end());
882 void ExtensionBox_Impl::addEventListenerOnce(
883 uno::Reference
<deployment::XPackage
> const & extension
)
885 //make sure to only add the listener once
886 cleanVecListenerAdded();
887 if ( std::none_of(m_vListenerAdded
.begin(), m_vListenerAdded
.end(),
888 FindWeakRef(extension
)) )
890 extension
->addEventListener( m_xRemoveListener
.get() );
891 m_vListenerAdded
.emplace_back(extension
);
896 void ExtensionBox_Impl::addEntry( const uno::Reference
< deployment::XPackage
> &xPackage
,
897 bool bLicenseMissing
)
900 PackageState eState
= TheExtensionManager::getPackageState( xPackage
);
901 bool bLocked
= m_pManager
->isReadOnly( xPackage
);
903 TEntry_Impl
pEntry( new Entry_Impl( xPackage
, eState
, bLocked
) );
905 // Don't add empty entries
906 if ( pEntry
->m_sTitle
.isEmpty() )
910 osl::MutexGuard
guard(m_entriesMutex
);
911 if (m_vEntries
.empty())
913 addEventListenerOnce(xPackage
);
914 m_vEntries
.push_back(pEntry
);
918 if (!FindEntryPos(pEntry
, 0, m_vEntries
.size() - 1, nPos
))
920 addEventListenerOnce(xPackage
);
921 m_vEntries
.insert(m_vEntries
.begin() + nPos
, pEntry
);
923 else if (!m_bInCheckMode
)
925 OSL_FAIL("ExtensionBox_Impl::addEntry(): Will not add duplicate entries");
929 pEntry
->m_bHasOptions
= m_pManager
->supportsOptions(xPackage
);
930 pEntry
->m_bUser
= (xPackage
->getRepositoryName() == USER_PACKAGE_MANAGER
);
931 pEntry
->m_bShared
= (xPackage
->getRepositoryName() == SHARED_PACKAGE_MANAGER
);
932 pEntry
->m_bNew
= m_bInCheckMode
;
933 pEntry
->m_bMissingLic
= bLicenseMissing
;
936 pEntry
->m_sErrorText
= DpResId(RID_STR_ERROR_MISSING_LICENSE
);
938 //access to m_nActive must be guarded
939 if (!m_bInCheckMode
&& m_bHasActive
&& (m_nActive
>= nPos
))
943 if ( IsReallyVisible() )
946 m_bNeedsRecalc
= true;
949 void ExtensionBox_Impl::updateEntry( const uno::Reference
< deployment::XPackage
> &xPackage
)
951 for (auto const& entry
: m_vEntries
)
953 if ( entry
->m_xPackage
== xPackage
)
955 PackageState eState
= TheExtensionManager::getPackageState( xPackage
);
956 entry
->m_bHasOptions
= m_pManager
->supportsOptions( xPackage
);
957 entry
->m_eState
= eState
;
958 entry
->m_sTitle
= xPackage
->getDisplayName();
959 entry
->m_sVersion
= xPackage
->getVersion();
960 entry
->m_sDescription
= xPackage
->getDescription();
962 if ( eState
== REGISTERED
)
963 entry
->m_bMissingLic
= false;
965 if ( eState
== AMBIGUOUS
)
966 entry
->m_sErrorText
= DpResId( RID_STR_ERROR_UNKNOWN_STATUS
);
967 else if ( ! entry
->m_bMissingLic
)
968 entry
->m_sErrorText
.clear();
970 if ( IsReallyVisible() )
977 //This function is also called as a result of removing an extension.
978 //see PackageManagerImpl::removePackage
979 //The gui is a registered as listener on the package. Removing it will cause the
980 //listeners to be notified and then this function is called. At this moment xPackage
981 //is in the disposing state and all calls on it may result in a DisposedException.
982 void ExtensionBox_Impl::removeEntry( const uno::Reference
< deployment::XPackage
> &xPackage
)
986 bool invalidate
= false;
988 ::osl::ClearableMutexGuard
aGuard( m_entriesMutex
);
990 auto iIndex
= std::find_if(m_vEntries
.begin(), m_vEntries
.end(),
991 [&xPackage
](const TEntry_Impl
& rxEntry
) { return rxEntry
->m_xPackage
== xPackage
; });
992 if (iIndex
!= m_vEntries
.end())
994 long nPos
= iIndex
- m_vEntries
.begin();
996 // Entries mustn't be removed here, because they contain a hyperlink control
997 // which can only be deleted when the thread has the solar mutex. Therefore
998 // the entry will be moved into the m_vRemovedEntries list which will be
999 // cleared on the next paint event
1000 m_vRemovedEntries
.push_back( *iIndex
);
1001 (*iIndex
)->m_xPackage
->removeEventListener(m_xRemoveListener
.get());
1002 m_vEntries
.erase( iIndex
);
1004 m_bNeedsRecalc
= true;
1006 if ( IsReallyVisible() )
1011 if ( nPos
< m_nActive
)
1013 else if ( ( nPos
== m_nActive
) &&
1014 ( nPos
== static_cast<long>(m_vEntries
.size()) ) )
1017 m_bHasActive
= false;
1018 //clear before calling out of this method
1020 selectEntry( m_nActive
);
1034 void ExtensionBox_Impl::RemoveUnlocked()
1036 bool bAllRemoved
= false;
1038 while ( ! bAllRemoved
)
1042 ::osl::ClearableMutexGuard
aGuard( m_entriesMutex
);
1044 for (auto const& entry
: m_vEntries
)
1046 if ( !entry
->m_bLocked
)
1048 bAllRemoved
= false;
1049 uno::Reference
< deployment::XPackage
> xPackage
= entry
->m_xPackage
;
1051 removeEntry( xPackage
);
1059 void ExtensionBox_Impl::prepareChecking()
1061 m_bInCheckMode
= true;
1062 for (auto const& entry
: m_vEntries
)
1064 entry
->m_bChecked
= false;
1065 entry
->m_bNew
= false;
1070 void ExtensionBox_Impl::checkEntries()
1073 long nChangedActivePos
= -1;
1075 bool bNeedsUpdate
= false;
1078 osl::MutexGuard
guard(m_entriesMutex
);
1079 auto iIndex
= m_vEntries
.begin();
1080 while (iIndex
!= m_vEntries
.end())
1082 if (!(*iIndex
)->m_bChecked
)
1084 (*iIndex
)->m_bChecked
= true;
1085 bNeedsUpdate
= true;
1086 nPos
= iIndex
- m_vEntries
.begin();
1087 if ((*iIndex
)->m_bNew
)
1088 { // add entry to list and correct active pos
1091 if (nPos
<= m_nActive
)
1096 { // remove entry from list
1101 if (nPos
< nChangedActivePos
)
1103 --nChangedActivePos
;
1105 if (nPos
< m_nActive
)
1107 else if (nPos
== m_nActive
)
1109 nChangedActivePos
= nPos
;
1111 m_bHasActive
= false;
1113 m_vRemovedEntries
.push_back(*iIndex
);
1114 iIndex
= m_vEntries
.erase(iIndex
);
1122 m_bInCheckMode
= false;
1124 if ( nNewPos
!= - 1)
1125 selectEntry( nNewPos
);
1126 else if (nChangedActivePos
!= -1) {
1127 selectEntry(nChangedActivePos
);
1132 m_bNeedsRecalc
= true;
1133 if ( IsReallyVisible() )
1138 IMPL_LINK(ExtensionBox_Impl
, ScrollHdl
, weld::ScrolledWindow
&, rScrBar
, void)
1140 m_nTopIndex
= rScrBar
.vadjustment_get_value();
1144 } //namespace dp_gui
1146 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */