Version 6.1.0.2, tag libreoffice-6.1.0.2
[LibreOffice.git] / sd / source / ui / dlg / RemoteDialogClientBox.cxx
blobd159eea1b16ea0591b129d2d4db1dd2ae7c4a39f
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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 <vector>
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>
36 using namespace std;
38 using namespace ::com::sun::star;
40 namespace sd {
42 // struct ClientBoxEntry
44 ClientBoxEntry::ClientBoxEntry(const std::shared_ptr<ClientInfo>& pClientInfo)
45 : m_bActive(false)
46 , m_pClientInfo(pClientInfo)
50 ClientBoxEntry::~ClientBoxEntry()
53 void ClientRemovedListener::disposing( lang::EventObject const & )
56 ClientRemovedListener::~ClientRemovedListener()
60 // ClientBox
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 ),
68 m_bInDelete( false ),
69 m_nActive( 0 ),
70 m_nTopIndex( 0 ),
71 m_nActiveHeight( 0 ),
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;
88 else
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() );
97 else
98 SetBackground( rStyleSettings.GetFieldColor() );
100 m_xRemoveListener = new ClientRemovedListener( this );
102 populateEntries();
104 Show();
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()
116 disposeOnce();
119 void ClientBox::dispose()
121 if ( ! m_bInDelete )
122 DeleteRemoved();
124 m_bInDelete = true;
126 m_vEntries.clear();
128 m_xRemoveListener.clear();
130 m_aPinBox.disposeAndClear();
131 m_aDeauthoriseButton.disposeAndClear();
132 m_aScrollBar.disposeAndClear();
133 Control::dispose();
136 // Title + description
137 void ClientBox::CalcActiveHeight()
139 const ::osl::MutexGuard aGuard( m_entriesMutex );
141 // get title height
142 long aTextHeight;
143 long nIconHeight = 2*TOP_OFFSET + SMALL_ICON_SIZE;
144 long nTitleHeight = 2*TOP_OFFSET + GetTextHeight();
145 if ( nIconHeight < nTitleHeight )
146 aTextHeight = nTitleHeight;
147 else
148 aTextHeight = nIconHeight;
150 // Text entry height
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 );
178 else
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 );
192 m_bInDelete = true;
194 if ( ! m_vRemovedEntries.empty() )
196 m_vRemovedEntries.clear();
199 m_bInDelete = false;
202 long ClientBox::GetActiveEntryIndex()
204 if ( m_bHasActive )
205 return m_nActive;
206 else
207 return -1;
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);
221 if ( m_bHasActive )
223 if ( nPos == m_nActive )
224 return;
226 m_bHasActive = false;
227 m_vEntries[ m_nActive ]->m_bActive = false;
230 if ( ( nPos >= 0 ) && ( nPos < static_cast<long>(m_vEntries.size()) ) )
232 m_bHasActive = true;
233 m_nActive = nPos;
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( "" );
245 if ( m_bHasActive )
247 bool bAlreadyAuthorised =
248 m_vEntries[ m_nActive ]->m_pClientInfo->mbIsAlreadyAuthorised;
250 if ( bAlreadyAuthorised )
252 m_aDeauthoriseButton->GetFocus();
254 else
256 m_aPinBox->GetFocus();
260 if ( IsReallyVisible() )
262 m_bNeedsRecalc = true;
263 Invalidate();
266 guard.clear();
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());
275 else
276 SetTextColor(rStyleSettings.GetFieldTextColor());
278 if (rEntry->m_bActive)
280 rRenderContext.SetLineColor();
281 rRenderContext.SetFillColor(rStyleSettings.GetHighlightColor());
282 rRenderContext.DrawRect(rRect);
284 else
286 if(IsControlBackground())
287 SetBackground(GetControlBackground());
288 else
289 SetBackground(rStyleSettings.GetFieldColor());
291 rRenderContext.SetTextFillColor();
292 rRenderContext.Erase(rRect);
295 // FIXME: draw bluetooth or wifi icon
296 Point aPos(rRect.TopLeft());
298 // Setup fonts
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);
319 else
320 rRenderContext.DrawText(aPos, rEntry->m_pClientInfo->mName);
322 SetFont(aStdFont);
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()
337 if ( m_bHasActive )
338 CalcActiveHeight();
340 SetupScrollBar();
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() );
348 if ( !m_bHasActive )
350 m_aPinBox->Show( false );
351 m_aDeauthoriseButton->Show( false );
353 else
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(),
366 aEntryRect.Right(),
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 );
378 else
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() )
429 return true;
431 long nSelect = 0;
433 if ( m_bHasActive )
435 long nPageSize = GetOutputSizePixel().Height() / m_nStdHeight;
436 if ( nPageSize < 2 )
437 nPageSize = 2;
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 )
444 nSelect = 0;
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 ) )
455 nSelect = 0;
456 else if ( ( nKeyCode == KEY_UP ) || ( nKeyCode == KEY_PAGEUP ) || ( nKeyCode == KEY_END ) )
457 nSelect = m_vEntries.size() - 1;
460 if ( nSelect < 0 )
461 nSelect = 0;
462 if ( nSelect >= static_cast<long>(m_vEntries.size()) )
463 nSelect = m_vEntries.size() - 1;
465 selectEntry( nSelect );
467 return true;
470 void ClientBox::Paint(vcl::RenderContext& rRenderContext, const ::tools::Rectangle &/*rPaintRect*/)
472 if (!m_bInDelete)
473 DeleteRemoved();
475 if (m_bNeedsRecalc)
476 RecalcAll();
478 Point aStart(0, -m_nTopIndex);
479 Size aSize(GetOutputSizePixel());
481 if (m_bHasScrollBar)
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;
500 if ( m_bHasActive )
502 nHeight += m_nActiveHeight - m_nStdHeight;
505 return nHeight;
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();
534 m_nTopIndex = 0;
537 m_bHasScrollBar = bNeedsScrollBar;
540 void ClientBox::Resize()
542 RecalcAll();
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 )
552 nPos = m_nActive;
553 else
554 nPos = ( rPos.Y() + m_nTopIndex - (m_nActiveHeight - m_nStdHeight) ) / m_nStdHeight;
557 return nPos;
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
573 else
574 selectEntry( nPos );
578 bool ClientBox::EventNotify( NotifyEvent& rNEvt )
580 if ( !m_bInDelete )
581 DeleteRemoved();
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 );
606 else
607 m_aScrollBar->DoScroll( nThumbPos - m_nStdHeight );
608 bHandled = true;
613 if ( !bHandled )
614 return Control::EventNotify(rNEvt);
615 else
616 return true;
619 void ClientBox::addEntry( const std::shared_ptr<ClientInfo>& pClientInfo )
621 long nPos = 0;
623 TClientBoxEntry xEntry( new ClientBoxEntry( pClientInfo ) );
625 ::osl::ClearableMutexGuard guard(m_entriesMutex);
626 if ( m_vEntries.empty() )
628 m_vEntries.push_back( xEntry );
630 else
632 m_vEntries.insert( m_vEntries.begin()+nPos, xEntry );
635 //access to m_nActive must be guarded
636 if ( m_bHasActive && ( m_nActive >= nPos ) )
637 m_nActive += 1;
639 guard.clear();
641 if ( IsReallyVisible() )
642 Invalidate();
644 m_bNeedsRecalc = true;
647 void ClientBox::clearEntries()
649 selectEntry( -1 );
650 m_bHasActive = false;
652 const ::osl::MutexGuard aGuard( m_entriesMutex );
654 m_vEntries.clear();
655 if ( IsReallyVisible() )
656 Invalidate();
657 m_bNeedsRecalc = true;
660 void ClientBox::populateEntries()
662 const ::osl::MutexGuard aGuard( m_entriesMutex );
664 clearEntries();
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() );
674 aIt != aEnd; ++aIt )
676 addEntry( *aIt );
678 #endif
680 if ( IsReallyVisible() )
681 Invalidate();
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();
705 if ( aSelected < 0 )
706 return;
707 TClientBoxEntry aEntry = GetEntryData(aSelected);
709 #ifdef ENABLE_SDREMOTE
710 RemoteServer::deauthoriseClient( aEntry->m_pClientInfo );
711 #endif
712 populateEntries();
715 } //namespace dp_gui
717 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */