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 .
22 #include <svtools/controldims.hxx>
24 #include "RemoteDialogClientBox.hxx"
25 #include <RemoteServer.hxx>
27 #include <com/sun/star/i18n/CollatorOptions.hpp>
28 #include <com/sun/star/deployment/DependencyException.hpp>
29 #include <com/sun/star/deployment/DeploymentException.hpp>
30 #include <vcl/settings.hxx>
31 #include <vcl/builderfactory.hxx>
33 #include <sdresid.hxx>
34 #include <strings.hrc>
38 using namespace ::com::sun::star
;
42 // struct ClientBoxEntry
44 ClientBoxEntry::ClientBoxEntry(const std::shared_ptr
<ClientInfo
>& pClientInfo
)
46 , m_pClientInfo(pClientInfo
)
50 ClientBoxEntry::~ClientBoxEntry()
53 void ClientRemovedListener::disposing( lang::EventObject
const & )
56 ClientRemovedListener::~ClientRemovedListener()
62 ClientBox::ClientBox( vcl::Window
* pParent
, WinBits nStyle
) :
63 Control( pParent
, nStyle
),
64 m_bHasScrollBar( false ),
65 m_bHasActive( false ),
66 m_bNeedsRecalc( true ),
67 m_bAdjustActive( false ),
72 m_aPinBox( VclPtr
<NumericBox
>::Create( this, 0 ) ),
73 m_aDeauthoriseButton( VclPtr
<PushButton
>::Create( this ) ),
74 m_aScrollBar( VclPtr
<ScrollBar
>::Create( this, WB_VERT
) )
76 m_aScrollBar
->SetScrollHdl( LINK( this, ClientBox
, ScrollHdl
) );
77 m_aScrollBar
->EnableDrag();
79 m_aPinBox
->SetUseThousandSep(false);
80 m_aDeauthoriseButton
->SetText( SdResId(STR_DEAUTHORISE_CLIENT
) );
81 m_aDeauthoriseButton
->SetClickHdl( LINK( this, ClientBox
, DeauthoriseHdl
) );
83 SetPosPixel( Point( RSC_SP_DLG_INNERBORDER_LEFT
, RSC_SP_DLG_INNERBORDER_TOP
) );
84 long nIconHeight
= 2*TOP_OFFSET
+ SMALL_ICON_SIZE
;
85 long nTitleHeight
= 2*TOP_OFFSET
+ GetTextHeight();
86 if ( nIconHeight
< nTitleHeight
)
87 m_nStdHeight
= nTitleHeight
;
89 m_nStdHeight
= nIconHeight
;
90 m_nStdHeight
+= GetTextHeight() + TOP_OFFSET
;
92 m_nActiveHeight
= m_nStdHeight
;
94 const StyleSettings
& rStyleSettings
= GetSettings().GetStyleSettings();
95 if( IsControlBackground() )
96 SetBackground( GetControlBackground() );
98 SetBackground( rStyleSettings
.GetFieldColor() );
100 m_xRemoveListener
= new ClientRemovedListener( this );
107 VCL_BUILDER_FACTORY_CONSTRUCTOR(ClientBox
, WB_TABSTOP
)
109 Size
ClientBox::GetOptimalSize() const
111 return LogicToPixel(Size(200, 140), MapMode(MapUnit::MapAppFont
));
114 ClientBox::~ClientBox()
119 void ClientBox::dispose()
128 m_xRemoveListener
.clear();
130 m_aPinBox
.disposeAndClear();
131 m_aDeauthoriseButton
.disposeAndClear();
132 m_aScrollBar
.disposeAndClear();
136 // Title + description
137 void ClientBox::CalcActiveHeight()
139 const ::osl::MutexGuard
aGuard( m_entriesMutex
);
143 long nIconHeight
= 2*TOP_OFFSET
+ SMALL_ICON_SIZE
;
144 long nTitleHeight
= 2*TOP_OFFSET
+ GetTextHeight();
145 if ( nIconHeight
< nTitleHeight
)
146 aTextHeight
= nTitleHeight
;
148 aTextHeight
= nIconHeight
;
151 Size aSize
= GetOutputSizePixel();
152 if ( m_bHasScrollBar
)
153 aSize
.AdjustWidth( -(m_aScrollBar
->GetSizePixel().Width()) );
155 aSize
.AdjustWidth( -ICON_OFFSET
);
157 aSize
= LogicToPixel( Size( RSC_CD_PUSHBUTTON_WIDTH
, RSC_CD_PUSHBUTTON_HEIGHT
),
158 MapMode( MapUnit::MapAppFont
) );
159 aTextHeight
+= aSize
.Height();
161 if ( aTextHeight
< m_nStdHeight
)
162 aTextHeight
= m_nStdHeight
;
164 m_nActiveHeight
= aTextHeight
+ 2;
167 ::tools::Rectangle
ClientBox::GetEntryRect( const long nPos
) const
169 const ::osl::MutexGuard
aGuard( m_entriesMutex
);
171 Size
aSize( GetOutputSizePixel() );
173 if ( m_bHasScrollBar
)
174 aSize
.AdjustWidth( -(m_aScrollBar
->GetSizePixel().Width()) );
176 if ( m_vEntries
[ nPos
]->m_bActive
)
177 aSize
.setHeight( m_nActiveHeight
);
179 aSize
.setHeight( m_nStdHeight
);
181 Point
aPos( 0, -m_nTopIndex
+ nPos
* m_nStdHeight
);
182 if ( m_bHasActive
&& ( nPos
< m_nActive
) )
183 aPos
.AdjustY(m_nActiveHeight
- m_nStdHeight
);
185 return ::tools::Rectangle( aPos
, aSize
);
188 void ClientBox::DeleteRemoved()
190 const ::osl::MutexGuard
aGuard( m_entriesMutex
);
194 if ( ! m_vRemovedEntries
.empty() )
196 m_vRemovedEntries
.clear();
202 long ClientBox::GetActiveEntryIndex()
210 //This function may be called with nPos < 0
211 void ClientBox::selectEntry( const long nPos
)
213 //ToDo we should not use the guard at such a big scope here.
214 //Currently it is used to guard m_vEntries and m_nActive. m_nActive will be
215 //modified in this function.
216 //It would be probably best to always use a copy of m_vEntries
217 //and some other state variables from ClientBox for
218 //the whole painting operation. See issue i86993
219 ::osl::ClearableMutexGuard
guard(m_entriesMutex
);
223 if ( nPos
== m_nActive
)
226 m_bHasActive
= false;
227 m_vEntries
[ m_nActive
]->m_bActive
= false;
230 if ( ( nPos
>= 0 ) && ( nPos
< static_cast<long>(m_vEntries
.size()) ) )
234 m_vEntries
[ nPos
]->m_bActive
= true;
236 if ( IsReallyVisible() )
238 m_bAdjustActive
= true;
242 // We empty the pin box now too, just in case the user previously
243 // entered a pin, but then changed their selected device.
244 m_aPinBox
->SetText( "" );
247 bool bAlreadyAuthorised
=
248 m_vEntries
[ m_nActive
]->m_pClientInfo
->mbIsAlreadyAuthorised
;
250 if ( bAlreadyAuthorised
)
252 m_aDeauthoriseButton
->GetFocus();
256 m_aPinBox
->GetFocus();
260 if ( IsReallyVisible() )
262 m_bNeedsRecalc
= true;
269 void ClientBox::DrawRow(vcl::RenderContext
& rRenderContext
, const ::tools::Rectangle
& rRect
, const TClientBoxEntry
& rEntry
)
271 const StyleSettings
& rStyleSettings
= rRenderContext
.GetSettings().GetStyleSettings();
273 if (rEntry
->m_bActive
)
274 SetTextColor(rStyleSettings
.GetHighlightTextColor());
276 SetTextColor(rStyleSettings
.GetFieldTextColor());
278 if (rEntry
->m_bActive
)
280 rRenderContext
.SetLineColor();
281 rRenderContext
.SetFillColor(rStyleSettings
.GetHighlightColor());
282 rRenderContext
.DrawRect(rRect
);
286 if(IsControlBackground())
287 SetBackground(GetControlBackground());
289 SetBackground(rStyleSettings
.GetFieldColor());
291 rRenderContext
.SetTextFillColor();
292 rRenderContext
.Erase(rRect
);
295 // FIXME: draw bluetooth or wifi icon
296 Point
aPos(rRect
.TopLeft());
299 vcl::Font
aStdFont(rRenderContext
.GetFont());
300 vcl::Font
aBoldFont(aStdFont
);
301 aBoldFont
.SetWeight(WEIGHT_BOLD
);
302 rRenderContext
.SetFont(aBoldFont
);
303 long aTextHeight
= rRenderContext
.GetTextHeight();
305 // Get max title width
306 long nMaxTitleWidth
= rRect
.GetWidth() - ICON_OFFSET
;
307 nMaxTitleWidth
-= ( 2 * SMALL_ICON_SIZE
) + ( 4 * SPACE_BETWEEN
);
309 long aTitleWidth
= rRenderContext
.GetTextWidth(rEntry
->m_pClientInfo
->mName
) + (aTextHeight
/ 3);
311 aPos
= rRect
.TopLeft() + Point(ICON_OFFSET
, TOP_OFFSET
);
313 if (aTitleWidth
> nMaxTitleWidth
)
315 aTitleWidth
= nMaxTitleWidth
- (aTextHeight
/ 3);
316 OUString aShortTitle
= rRenderContext
.GetEllipsisString(rEntry
->m_pClientInfo
->mName
, aTitleWidth
);
317 rRenderContext
.DrawText(aPos
, aShortTitle
);
320 rRenderContext
.DrawText(aPos
, rEntry
->m_pClientInfo
->mName
);
324 aPos
.AdjustY(aTextHeight
);
325 if (rEntry
->m_bActive
)
327 OUString
sPinText(SdResId(STR_ENTER_PIN
));
328 DrawText(m_sPinTextRect
, sPinText
);
331 rRenderContext
.SetLineColor(COL_LIGHTGRAY
);
332 rRenderContext
.DrawLine(rRect
.BottomLeft(), rRect
.BottomRight());
335 void ClientBox::RecalcAll()
342 Size aPBSize
= LogicToPixel(
343 Size( RSC_CD_PUSHBUTTON_WIDTH
, RSC_CD_PUSHBUTTON_HEIGHT
),
344 MapMode( MapUnit::MapAppFont
) );
345 m_aPinBox
->SetSizePixel( aPBSize
);
346 m_aDeauthoriseButton
->SetSizePixel( m_aDeauthoriseButton
->GetOptimalSize() );
350 m_aPinBox
->Show( false );
351 m_aDeauthoriseButton
->Show( false );
355 ::tools::Rectangle aEntryRect
= GetEntryRect( m_nActive
);
357 Size
aPinBoxSize( m_aPinBox
->GetSizePixel() );
358 Point
aPos( aEntryRect
.Left(),
359 aEntryRect
.Bottom() - TOP_OFFSET
- aPinBoxSize
.Height() );
361 bool bAlreadyAuthorised
= m_vEntries
[ m_nActive
]->m_pClientInfo
->mbIsAlreadyAuthorised
;
363 if ( !bAlreadyAuthorised
)
365 m_sPinTextRect
= ::tools::Rectangle( aPos
.X(), aPos
.Y(),
367 aEntryRect
.Bottom() - TOP_OFFSET
);
369 OUString
sPinText(SdResId(STR_ENTER_PIN
));
371 aPos
= Point( aEntryRect
.Left() + GetTextWidth( sPinText
),
372 aEntryRect
.Bottom() - TOP_OFFSET
- aPinBoxSize
.Height() );
373 m_aPinBox
->SetPosPixel( aPos
);
374 // The text would have it's TOP aligned with the top of
375 // the pin box -- hence we push it down to align baselines.
376 m_sPinTextRect
+= Point( 0, 4 );
380 aPos
+= Point( 20, 0 );
381 m_aDeauthoriseButton
->SetPosPixel( aPos
);
384 m_aPinBox
->Show( !bAlreadyAuthorised
);
385 m_aDeauthoriseButton
->Show( bAlreadyAuthorised
);
387 if ( m_bAdjustActive
)
389 m_bAdjustActive
= false;
391 // If the top of the selected entry isn't visible, make it visible
392 if ( aEntryRect
.Top() < 0 )
394 m_nTopIndex
+= aEntryRect
.Top();
395 aEntryRect
.Move( 0, -aEntryRect
.Top() );
398 // If the bottom of the selected entry isn't visible, make it visible even if now the top
399 // isn't visible any longer ( the buttons are more important )
400 Size aOutputSize
= GetOutputSizePixel();
401 if ( aEntryRect
.Bottom() > aOutputSize
.Height() )
403 m_nTopIndex
+= ( aEntryRect
.Bottom() - aOutputSize
.Height() );
404 aEntryRect
.Move( 0, -( aEntryRect
.Bottom() - aOutputSize
.Height() ) );
407 // If there is unused space below the last entry but all entries don't fit into the box,
408 // move the content down to use the whole space
409 const long nTotalHeight
= GetTotalHeight();
410 if ( m_bHasScrollBar
&& ( aOutputSize
.Height() + m_nTopIndex
> nTotalHeight
) )
412 long nOffset
= m_nTopIndex
;
413 m_nTopIndex
= nTotalHeight
- aOutputSize
.Height();
414 nOffset
-= m_nTopIndex
;
415 aEntryRect
.Move( 0, nOffset
);
418 if ( m_bHasScrollBar
)
419 m_aScrollBar
->SetThumbPos( m_nTopIndex
);
423 m_bNeedsRecalc
= false;
426 bool ClientBox::HandleCursorKey( sal_uInt16 nKeyCode
)
428 if ( m_vEntries
.empty() )
435 long nPageSize
= GetOutputSizePixel().Height() / m_nStdHeight
;
439 if ( ( nKeyCode
== KEY_DOWN
) || ( nKeyCode
== KEY_RIGHT
) )
440 nSelect
= m_nActive
+ 1;
441 else if ( ( nKeyCode
== KEY_UP
) || ( nKeyCode
== KEY_LEFT
) )
442 nSelect
= m_nActive
- 1;
443 else if ( nKeyCode
== KEY_HOME
)
445 else if ( nKeyCode
== KEY_END
)
446 nSelect
= m_vEntries
.size() - 1;
447 else if ( nKeyCode
== KEY_PAGEUP
)
448 nSelect
= m_nActive
- nPageSize
+ 1;
449 else if ( nKeyCode
== KEY_PAGEDOWN
)
450 nSelect
= m_nActive
+ nPageSize
- 1;
452 else // when there is no selected entry, we will select the first or the last.
454 if ( ( nKeyCode
== KEY_DOWN
) || ( nKeyCode
== KEY_PAGEDOWN
) || ( nKeyCode
== KEY_HOME
) )
456 else if ( ( nKeyCode
== KEY_UP
) || ( nKeyCode
== KEY_PAGEUP
) || ( nKeyCode
== KEY_END
) )
457 nSelect
= m_vEntries
.size() - 1;
462 if ( nSelect
>= static_cast<long>(m_vEntries
.size()) )
463 nSelect
= m_vEntries
.size() - 1;
465 selectEntry( nSelect
);
470 void ClientBox::Paint(vcl::RenderContext
& rRenderContext
, const ::tools::Rectangle
&/*rPaintRect*/)
478 Point
aStart(0, -m_nTopIndex
);
479 Size
aSize(GetOutputSizePixel());
482 aSize
.AdjustWidth( -(m_aScrollBar
->GetSizePixel().Width()) );
484 const ::osl::MutexGuard
aGuard(m_entriesMutex
);
486 typedef std::vector
< TClientBoxEntry
>::iterator ITER
;
487 for (ITER iIndex
= m_vEntries
.begin(); iIndex
< m_vEntries
.end(); ++iIndex
)
489 aSize
.setHeight( (*iIndex
)->m_bActive
? m_nActiveHeight
: m_nStdHeight
);
490 ::tools::Rectangle
aEntryRect(aStart
, aSize
);
491 DrawRow(rRenderContext
, aEntryRect
, *iIndex
);
492 aStart
.AdjustY(aSize
.Height() );
496 long ClientBox::GetTotalHeight() const
498 long nHeight
= m_vEntries
.size() * m_nStdHeight
;
502 nHeight
+= m_nActiveHeight
- m_nStdHeight
;
508 void ClientBox::SetupScrollBar()
510 const Size aSize
= GetOutputSizePixel();
511 const long nScrBarSize
= GetSettings().GetStyleSettings().GetScrollBarSize();
512 const long nTotalHeight
= GetTotalHeight();
513 const bool bNeedsScrollBar
= ( nTotalHeight
> aSize
.Height() );
515 if ( bNeedsScrollBar
)
517 if ( m_nTopIndex
+ aSize
.Height() > nTotalHeight
)
518 m_nTopIndex
= nTotalHeight
- aSize
.Height();
520 m_aScrollBar
->SetPosSizePixel( Point( aSize
.Width() - nScrBarSize
, 0 ),
521 Size( nScrBarSize
, aSize
.Height() ) );
522 m_aScrollBar
->SetRangeMax( nTotalHeight
);
523 m_aScrollBar
->SetVisibleSize( aSize
.Height() );
524 m_aScrollBar
->SetPageSize( ( aSize
.Height() * 4 ) / 5 );
525 m_aScrollBar
->SetLineSize( m_nStdHeight
);
526 m_aScrollBar
->SetThumbPos( m_nTopIndex
);
528 if ( !m_bHasScrollBar
)
529 m_aScrollBar
->Show();
531 else if ( m_bHasScrollBar
)
533 m_aScrollBar
->Hide();
537 m_bHasScrollBar
= bNeedsScrollBar
;
540 void ClientBox::Resize()
545 long ClientBox::PointToPos( const Point
& rPos
)
547 long nPos
= ( rPos
.Y() + m_nTopIndex
) / m_nStdHeight
;
549 if ( m_bHasActive
&& ( nPos
> m_nActive
) )
551 if ( rPos
.Y() + m_nTopIndex
<= m_nActive
*m_nStdHeight
+ m_nActiveHeight
)
554 nPos
= ( rPos
.Y() + m_nTopIndex
- (m_nActiveHeight
- m_nStdHeight
) ) / m_nStdHeight
;
560 OUString
ClientBox::getPin()
562 return OUString::number( m_aPinBox
->GetValue() );
565 void ClientBox::MouseButtonDown( const MouseEvent
& rMEvt
)
567 long nPos
= PointToPos( rMEvt
.GetPosPixel() );
569 if ( rMEvt
.IsLeft() )
571 if ( rMEvt
.IsMod1() && m_bHasActive
)
572 selectEntry( m_vEntries
.size() ); // Selecting an not existing entry will deselect the current one
578 bool ClientBox::EventNotify( NotifyEvent
& rNEvt
)
583 bool bHandled
= false;
585 if ( rNEvt
.GetType() == MouseNotifyEvent::KEYINPUT
)
587 const KeyEvent
* pKEvt
= rNEvt
.GetKeyEvent();
588 vcl::KeyCode aKeyCode
= pKEvt
->GetKeyCode();
589 sal_uInt16 nKeyCode
= aKeyCode
.GetCode();
591 if ( aKeyCode
.GetGroup() == KEYGROUP_CURSOR
)
592 bHandled
= HandleCursorKey( nKeyCode
);
595 if ( rNEvt
.GetType() == MouseNotifyEvent::COMMAND
)
597 if ( m_bHasScrollBar
&&
598 ( rNEvt
.GetCommandEvent()->GetCommand() == CommandEventId::Wheel
) )
600 const CommandWheelData
* pData
= rNEvt
.GetCommandEvent()->GetWheelData();
601 if ( pData
->GetMode() == CommandWheelMode::SCROLL
)
603 long nThumbPos
= m_aScrollBar
->GetThumbPos();
604 if ( pData
->GetDelta() < 0 )
605 m_aScrollBar
->DoScroll( nThumbPos
+ m_nStdHeight
);
607 m_aScrollBar
->DoScroll( nThumbPos
- m_nStdHeight
);
614 return Control::EventNotify(rNEvt
);
619 void ClientBox::addEntry( const std::shared_ptr
<ClientInfo
>& pClientInfo
)
623 TClientBoxEntry
xEntry( new ClientBoxEntry( pClientInfo
) );
625 ::osl::ClearableMutexGuard
guard(m_entriesMutex
);
626 if ( m_vEntries
.empty() )
628 m_vEntries
.push_back( xEntry
);
632 m_vEntries
.insert( m_vEntries
.begin()+nPos
, xEntry
);
635 //access to m_nActive must be guarded
636 if ( m_bHasActive
&& ( m_nActive
>= nPos
) )
641 if ( IsReallyVisible() )
644 m_bNeedsRecalc
= true;
647 void ClientBox::clearEntries()
650 m_bHasActive
= false;
652 const ::osl::MutexGuard
aGuard( m_entriesMutex
);
655 if ( IsReallyVisible() )
657 m_bNeedsRecalc
= true;
660 void ClientBox::populateEntries()
662 const ::osl::MutexGuard
aGuard( m_entriesMutex
);
666 #ifdef ENABLE_SDREMOTE
667 RemoteServer::ensureDiscoverable();
669 vector
< std::shared_ptr
< ClientInfo
> > aClients( RemoteServer::getClients() );
671 const vector
< std::shared_ptr
<ClientInfo
> >::const_iterator
aEnd( aClients
.end() );
673 for ( vector
< std::shared_ptr
< ClientInfo
> >::const_iterator
aIt( aClients
.begin() );
680 if ( IsReallyVisible() )
682 m_bNeedsRecalc
= true;
685 void ClientBox::DoScroll( long nDelta
)
687 m_nTopIndex
+= nDelta
;
688 Point
aNewSBPt( m_aScrollBar
->GetPosPixel() );
690 ::tools::Rectangle
aScrRect( Point(), GetOutputSizePixel() );
691 aScrRect
.AdjustRight( -(m_aScrollBar
->GetSizePixel().Width()) );
692 Scroll( 0, -nDelta
, aScrRect
);
694 m_aScrollBar
->SetPosPixel( aNewSBPt
);
697 IMPL_LINK( ClientBox
, ScrollHdl
, ScrollBar
*, pScrBar
, void )
699 DoScroll( pScrBar
->GetDelta() );
702 IMPL_LINK_NOARG( ClientBox
, DeauthoriseHdl
, Button
*, void )
704 long aSelected
= GetActiveEntryIndex();
707 TClientBoxEntry aEntry
= GetEntryData(aSelected
);
709 #ifdef ENABLE_SDREMOTE
710 RemoteServer::deauthoriseClient( aEntry
->m_pClientInfo
);
717 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */