bump product version to 6.3.0.0.beta1
[LibreOffice.git] / sd / source / ui / dlg / RemoteDialogClientBox.cxx
blobad1355644a231b48a1f9996db605d80ad425dcef
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 <vcl/settings.hxx>
28 #include <vcl/builderfactory.hxx>
29 #include <vcl/commandevent.hxx>
30 #include <vcl/event.hxx>
32 #include <sdresid.hxx>
33 #include <strings.hrc>
35 using namespace std;
37 using namespace ::com::sun::star;
39 namespace sd {
41 // struct ClientBoxEntry
43 ClientBoxEntry::ClientBoxEntry(const std::shared_ptr<ClientInfo>& pClientInfo)
44 : m_bActive(false)
45 , m_pClientInfo(pClientInfo)
49 ClientBoxEntry::~ClientBoxEntry()
52 // ClientBox
54 ClientBox::ClientBox( vcl::Window* pParent, WinBits nStyle ) :
55 Control( pParent, nStyle ),
56 m_bHasScrollBar( false ),
57 m_bHasActive( false ),
58 m_bNeedsRecalc( true ),
59 m_bAdjustActive( false ),
60 m_nActive( 0 ),
61 m_nTopIndex( 0 ),
62 m_nActiveHeight( 0 ),
63 m_aPinBox( VclPtr<NumericBox>::Create( this, 0 ) ),
64 m_aDeauthoriseButton( VclPtr<PushButton>::Create( this ) ),
65 m_aScrollBar( VclPtr<ScrollBar>::Create( this, WB_VERT ) )
67 m_aScrollBar->SetScrollHdl( LINK( this, ClientBox, ScrollHdl ) );
68 m_aScrollBar->EnableDrag();
70 m_aPinBox->SetUseThousandSep(false);
71 m_aDeauthoriseButton->SetText( SdResId(STR_DEAUTHORISE_CLIENT) );
72 m_aDeauthoriseButton->SetClickHdl( LINK( this, ClientBox, DeauthoriseHdl ) );
74 SetPosPixel( Point( RSC_SP_DLG_INNERBORDER_LEFT, RSC_SP_DLG_INNERBORDER_TOP ) );
75 long nIconHeight = 2*TOP_OFFSET + SMALL_ICON_SIZE;
76 long nTitleHeight = 2*TOP_OFFSET + GetTextHeight();
77 if ( nIconHeight < nTitleHeight )
78 m_nStdHeight = nTitleHeight;
79 else
80 m_nStdHeight = nIconHeight;
81 m_nStdHeight += GetTextHeight() + TOP_OFFSET;
83 m_nActiveHeight = m_nStdHeight;
85 const StyleSettings& rStyleSettings = GetSettings().GetStyleSettings();
86 if( IsControlBackground() )
87 SetBackground( GetControlBackground() );
88 else
89 SetBackground( rStyleSettings.GetFieldColor() );
91 populateEntries();
93 Show();
96 VCL_BUILDER_FACTORY_CONSTRUCTOR(ClientBox, WB_TABSTOP)
98 Size ClientBox::GetOptimalSize() const
100 return LogicToPixel(Size(200, 140), MapMode(MapUnit::MapAppFont));
103 ClientBox::~ClientBox()
105 disposeOnce();
108 void ClientBox::dispose()
110 m_vEntries.clear();
112 m_aPinBox.disposeAndClear();
113 m_aDeauthoriseButton.disposeAndClear();
114 m_aScrollBar.disposeAndClear();
115 Control::dispose();
118 // Title + description
119 void ClientBox::CalcActiveHeight()
121 const ::osl::MutexGuard aGuard( m_entriesMutex );
123 // get title height
124 long aTextHeight;
125 long nIconHeight = 2*TOP_OFFSET + SMALL_ICON_SIZE;
126 long nTitleHeight = 2*TOP_OFFSET + GetTextHeight();
127 if ( nIconHeight < nTitleHeight )
128 aTextHeight = nTitleHeight;
129 else
130 aTextHeight = nIconHeight;
132 // Text entry height
133 Size aSize = GetOutputSizePixel();
134 if ( m_bHasScrollBar )
135 aSize.AdjustWidth( -(m_aScrollBar->GetSizePixel().Width()) );
137 aSize.AdjustWidth( -ICON_OFFSET );
139 aSize = LogicToPixel( Size( RSC_CD_PUSHBUTTON_WIDTH, RSC_CD_PUSHBUTTON_HEIGHT ),
140 MapMode( MapUnit::MapAppFont ) );
141 aTextHeight += aSize.Height();
143 if ( aTextHeight < m_nStdHeight )
144 aTextHeight = m_nStdHeight;
146 m_nActiveHeight = aTextHeight + 2;
149 ::tools::Rectangle ClientBox::GetEntryRect( const long nPos ) const
151 const ::osl::MutexGuard aGuard( m_entriesMutex );
153 Size aSize( GetOutputSizePixel() );
155 if ( m_bHasScrollBar )
156 aSize.AdjustWidth( -(m_aScrollBar->GetSizePixel().Width()) );
158 if ( m_vEntries[ nPos ]->m_bActive )
159 aSize.setHeight( m_nActiveHeight );
160 else
161 aSize.setHeight( m_nStdHeight );
163 Point aPos( 0, -m_nTopIndex + nPos * m_nStdHeight );
164 if ( m_bHasActive && ( nPos < m_nActive ) )
165 aPos.AdjustY(m_nActiveHeight - m_nStdHeight );
167 return ::tools::Rectangle( aPos, aSize );
170 long ClientBox::GetActiveEntryIndex()
172 if ( m_bHasActive )
173 return m_nActive;
174 else
175 return -1;
178 //This function may be called with nPos < 0
179 void ClientBox::selectEntry( const long nPos )
181 //ToDo we should not use the guard at such a big scope here.
182 //Currently it is used to guard m_vEntries and m_nActive. m_nActive will be
183 //modified in this function.
184 //It would be probably best to always use a copy of m_vEntries
185 //and some other state variables from ClientBox for
186 //the whole painting operation. See issue i86993
187 osl::MutexGuard guard(m_entriesMutex);
189 if ( m_bHasActive )
191 if ( nPos == m_nActive )
192 return;
194 m_bHasActive = false;
195 m_vEntries[ m_nActive ]->m_bActive = false;
198 if ( ( nPos >= 0 ) && ( nPos < static_cast<long>(m_vEntries.size()) ) )
200 m_bHasActive = true;
201 m_nActive = nPos;
202 m_vEntries[ nPos ]->m_bActive = true;
204 if ( IsReallyVisible() )
206 m_bAdjustActive = true;
210 // We empty the pin box now too, just in case the user previously
211 // entered a pin, but then changed their selected device.
212 m_aPinBox->SetText( "" );
213 if ( m_bHasActive )
215 bool bAlreadyAuthorised =
216 m_vEntries[ m_nActive ]->m_pClientInfo->mbIsAlreadyAuthorised;
218 if ( bAlreadyAuthorised )
220 m_aDeauthoriseButton->GetFocus();
222 else
224 m_aPinBox->GetFocus();
228 if ( IsReallyVisible() )
230 m_bNeedsRecalc = true;
231 Invalidate();
235 void ClientBox::DrawRow(vcl::RenderContext& rRenderContext, const ::tools::Rectangle& rRect, const TClientBoxEntry& rEntry)
237 const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings();
239 if (rEntry->m_bActive)
240 SetTextColor(rStyleSettings.GetHighlightTextColor());
241 else
242 SetTextColor(rStyleSettings.GetFieldTextColor());
244 if (rEntry->m_bActive)
246 rRenderContext.SetLineColor();
247 rRenderContext.SetFillColor(rStyleSettings.GetHighlightColor());
248 rRenderContext.DrawRect(rRect);
250 else
252 if(IsControlBackground())
253 SetBackground(GetControlBackground());
254 else
255 SetBackground(rStyleSettings.GetFieldColor());
257 rRenderContext.SetTextFillColor();
258 rRenderContext.Erase(rRect);
261 // FIXME: draw bluetooth or wifi icon
262 Point aPos(rRect.TopLeft());
264 // Setup fonts
265 vcl::Font aStdFont(rRenderContext.GetFont());
266 vcl::Font aBoldFont(aStdFont);
267 aBoldFont.SetWeight(WEIGHT_BOLD);
268 rRenderContext.SetFont(aBoldFont);
269 long aTextHeight = rRenderContext.GetTextHeight();
271 // Get max title width
272 long nMaxTitleWidth = rRect.GetWidth() - ICON_OFFSET;
273 nMaxTitleWidth -= ( 2 * SMALL_ICON_SIZE ) + ( 4 * SPACE_BETWEEN );
275 long aTitleWidth = rRenderContext.GetTextWidth(rEntry->m_pClientInfo->mName) + (aTextHeight / 3);
277 aPos = rRect.TopLeft() + Point(ICON_OFFSET, TOP_OFFSET);
279 if (aTitleWidth > nMaxTitleWidth)
281 aTitleWidth = nMaxTitleWidth - (aTextHeight / 3);
282 OUString aShortTitle = rRenderContext.GetEllipsisString(rEntry->m_pClientInfo->mName, aTitleWidth );
283 rRenderContext.DrawText(aPos, aShortTitle);
285 else
286 rRenderContext.DrawText(aPos, rEntry->m_pClientInfo->mName);
288 SetFont(aStdFont);
290 aPos.AdjustY(aTextHeight );
291 if (rEntry->m_bActive)
293 OUString sPinText(SdResId(STR_ENTER_PIN));
294 DrawText(m_sPinTextRect, sPinText);
297 rRenderContext.SetLineColor(COL_LIGHTGRAY);
298 rRenderContext.DrawLine(rRect.BottomLeft(), rRect.BottomRight());
301 void ClientBox::RecalcAll()
303 if ( m_bHasActive )
304 CalcActiveHeight();
306 SetupScrollBar();
308 Size aPBSize = LogicToPixel(
309 Size( RSC_CD_PUSHBUTTON_WIDTH, RSC_CD_PUSHBUTTON_HEIGHT ),
310 MapMode( MapUnit::MapAppFont ) );
311 m_aPinBox->SetSizePixel( aPBSize );
312 m_aDeauthoriseButton->SetSizePixel( m_aDeauthoriseButton->GetOptimalSize() );
314 if ( !m_bHasActive )
316 m_aPinBox->Show( false );
317 m_aDeauthoriseButton->Show( false );
319 else
321 ::tools::Rectangle aEntryRect = GetEntryRect( m_nActive );
323 Size aPinBoxSize( m_aPinBox->GetSizePixel() );
324 Point aPos( aEntryRect.Left(),
325 aEntryRect.Bottom() - TOP_OFFSET - aPinBoxSize.Height() );
327 bool bAlreadyAuthorised = m_vEntries[ m_nActive ]->m_pClientInfo->mbIsAlreadyAuthorised;
329 if ( !bAlreadyAuthorised )
331 m_sPinTextRect = ::tools::Rectangle( aPos.X(), aPos.Y(),
332 aEntryRect.Right(),
333 aEntryRect.Bottom() - TOP_OFFSET);
335 OUString sPinText(SdResId(STR_ENTER_PIN));
337 aPos = Point( aEntryRect.Left() + GetTextWidth( sPinText ),
338 aEntryRect.Bottom() - TOP_OFFSET - aPinBoxSize.Height() );
339 m_aPinBox->SetPosPixel( aPos );
340 // The text would have it's TOP aligned with the top of
341 // the pin box -- hence we push it down to align baselines.
342 m_sPinTextRect += Point( 0, 4 );
344 else
346 aPos += Point( 20, 0 );
347 m_aDeauthoriseButton->SetPosPixel( aPos );
350 m_aPinBox->Show( !bAlreadyAuthorised );
351 m_aDeauthoriseButton->Show( bAlreadyAuthorised );
353 if ( m_bAdjustActive )
355 m_bAdjustActive = false;
357 // If the top of the selected entry isn't visible, make it visible
358 if ( aEntryRect.Top() < 0 )
360 m_nTopIndex += aEntryRect.Top();
361 aEntryRect.Move( 0, -aEntryRect.Top() );
364 // If the bottom of the selected entry isn't visible, make it visible even if now the top
365 // isn't visible any longer ( the buttons are more important )
366 Size aOutputSize = GetOutputSizePixel();
367 if ( aEntryRect.Bottom() > aOutputSize.Height() )
369 m_nTopIndex += ( aEntryRect.Bottom() - aOutputSize.Height() );
370 aEntryRect.Move( 0, -( aEntryRect.Bottom() - aOutputSize.Height() ) );
373 // If there is unused space below the last entry but all entries don't fit into the box,
374 // move the content down to use the whole space
375 const long nTotalHeight = GetTotalHeight();
376 if ( m_bHasScrollBar && ( aOutputSize.Height() + m_nTopIndex > nTotalHeight ) )
378 long nOffset = m_nTopIndex;
379 m_nTopIndex = nTotalHeight - aOutputSize.Height();
380 nOffset -= m_nTopIndex;
381 aEntryRect.Move( 0, nOffset );
384 if ( m_bHasScrollBar )
385 m_aScrollBar->SetThumbPos( m_nTopIndex );
389 m_bNeedsRecalc = false;
392 bool ClientBox::HandleCursorKey( sal_uInt16 nKeyCode )
394 if ( m_vEntries.empty() )
395 return true;
397 long nSelect = 0;
399 if ( m_bHasActive )
401 long nPageSize = GetOutputSizePixel().Height() / m_nStdHeight;
402 if ( nPageSize < 2 )
403 nPageSize = 2;
405 if ( ( nKeyCode == KEY_DOWN ) || ( nKeyCode == KEY_RIGHT ) )
406 nSelect = m_nActive + 1;
407 else if ( ( nKeyCode == KEY_UP ) || ( nKeyCode == KEY_LEFT ) )
408 nSelect = m_nActive - 1;
409 else if ( nKeyCode == KEY_HOME )
410 nSelect = 0;
411 else if ( nKeyCode == KEY_END )
412 nSelect = m_vEntries.size() - 1;
413 else if ( nKeyCode == KEY_PAGEUP )
414 nSelect = m_nActive - nPageSize + 1;
415 else if ( nKeyCode == KEY_PAGEDOWN )
416 nSelect = m_nActive + nPageSize - 1;
418 else // when there is no selected entry, we will select the first or the last.
420 if ( ( nKeyCode == KEY_DOWN ) || ( nKeyCode == KEY_PAGEDOWN ) || ( nKeyCode == KEY_HOME ) )
421 nSelect = 0;
422 else if ( ( nKeyCode == KEY_UP ) || ( nKeyCode == KEY_PAGEUP ) || ( nKeyCode == KEY_END ) )
423 nSelect = m_vEntries.size() - 1;
426 if ( nSelect < 0 )
427 nSelect = 0;
428 if ( nSelect >= static_cast<long>(m_vEntries.size()) )
429 nSelect = m_vEntries.size() - 1;
431 selectEntry( nSelect );
433 return true;
436 void ClientBox::Paint(vcl::RenderContext& rRenderContext, const ::tools::Rectangle &/*rPaintRect*/)
438 if (m_bNeedsRecalc)
439 RecalcAll();
441 Point aStart(0, -m_nTopIndex);
442 Size aSize(GetOutputSizePixel());
444 if (m_bHasScrollBar)
445 aSize.AdjustWidth( -(m_aScrollBar->GetSizePixel().Width()) );
447 const ::osl::MutexGuard aGuard(m_entriesMutex);
449 for (auto& vEntry : m_vEntries)
451 aSize.setHeight( vEntry->m_bActive ? m_nActiveHeight : m_nStdHeight );
452 ::tools::Rectangle aEntryRect(aStart, aSize);
453 DrawRow(rRenderContext, aEntryRect, vEntry);
454 aStart.AdjustY(aSize.Height() );
458 long ClientBox::GetTotalHeight() const
460 long nHeight = m_vEntries.size() * m_nStdHeight;
462 if ( m_bHasActive )
464 nHeight += m_nActiveHeight - m_nStdHeight;
467 return nHeight;
470 void ClientBox::SetupScrollBar()
472 const Size aSize = GetOutputSizePixel();
473 const long nScrBarSize = GetSettings().GetStyleSettings().GetScrollBarSize();
474 const long nTotalHeight = GetTotalHeight();
475 const bool bNeedsScrollBar = ( nTotalHeight > aSize.Height() );
477 if ( bNeedsScrollBar )
479 if ( m_nTopIndex + aSize.Height() > nTotalHeight )
480 m_nTopIndex = nTotalHeight - aSize.Height();
482 m_aScrollBar->SetPosSizePixel( Point( aSize.Width() - nScrBarSize, 0 ),
483 Size( nScrBarSize, aSize.Height() ) );
484 m_aScrollBar->SetRangeMax( nTotalHeight );
485 m_aScrollBar->SetVisibleSize( aSize.Height() );
486 m_aScrollBar->SetPageSize( ( aSize.Height() * 4 ) / 5 );
487 m_aScrollBar->SetLineSize( m_nStdHeight );
488 m_aScrollBar->SetThumbPos( m_nTopIndex );
490 if ( !m_bHasScrollBar )
491 m_aScrollBar->Show();
493 else if ( m_bHasScrollBar )
495 m_aScrollBar->Hide();
496 m_nTopIndex = 0;
499 m_bHasScrollBar = bNeedsScrollBar;
502 void ClientBox::Resize()
504 RecalcAll();
507 long ClientBox::PointToPos( const Point& rPos )
509 long nPos = ( rPos.Y() + m_nTopIndex ) / m_nStdHeight;
511 if ( m_bHasActive && ( nPos > m_nActive ) )
513 if ( rPos.Y() + m_nTopIndex <= m_nActive*m_nStdHeight + m_nActiveHeight )
514 nPos = m_nActive;
515 else
516 nPos = ( rPos.Y() + m_nTopIndex - (m_nActiveHeight - m_nStdHeight) ) / m_nStdHeight;
519 return nPos;
522 OUString ClientBox::getPin()
524 return OUString::number( m_aPinBox->GetValue() );
527 void ClientBox::MouseButtonDown( const MouseEvent& rMEvt )
529 long nPos = PointToPos( rMEvt.GetPosPixel() );
531 if ( rMEvt.IsLeft() )
533 if ( rMEvt.IsMod1() && m_bHasActive )
534 selectEntry( m_vEntries.size() ); // Selecting an not existing entry will deselect the current one
535 else
536 selectEntry( nPos );
540 bool ClientBox::EventNotify( NotifyEvent& rNEvt )
542 bool bHandled = false;
544 if ( rNEvt.GetType() == MouseNotifyEvent::KEYINPUT )
546 const KeyEvent* pKEvt = rNEvt.GetKeyEvent();
547 vcl::KeyCode aKeyCode = pKEvt->GetKeyCode();
548 sal_uInt16 nKeyCode = aKeyCode.GetCode();
550 if ( aKeyCode.GetGroup() == KEYGROUP_CURSOR )
551 bHandled = HandleCursorKey( nKeyCode );
554 if ( rNEvt.GetType() == MouseNotifyEvent::COMMAND )
556 if ( m_bHasScrollBar &&
557 ( rNEvt.GetCommandEvent()->GetCommand() == CommandEventId::Wheel ) )
559 const CommandWheelData* pData = rNEvt.GetCommandEvent()->GetWheelData();
560 if ( pData->GetMode() == CommandWheelMode::SCROLL )
562 long nThumbPos = m_aScrollBar->GetThumbPos();
563 if ( pData->GetDelta() < 0 )
564 m_aScrollBar->DoScroll( nThumbPos + m_nStdHeight );
565 else
566 m_aScrollBar->DoScroll( nThumbPos - m_nStdHeight );
567 bHandled = true;
572 if ( !bHandled )
573 return Control::EventNotify(rNEvt);
574 else
575 return true;
578 void ClientBox::addEntry( const std::shared_ptr<ClientInfo>& pClientInfo )
580 long nPos = 0;
582 TClientBoxEntry xEntry( new ClientBoxEntry( pClientInfo ) );
585 osl::MutexGuard guard(m_entriesMutex);
586 if (m_vEntries.empty())
588 m_vEntries.push_back(xEntry);
590 else
592 m_vEntries.insert(m_vEntries.begin() + nPos, xEntry);
595 //access to m_nActive must be guarded
596 if (m_bHasActive && (m_nActive >= nPos))
597 m_nActive += 1;
600 if ( IsReallyVisible() )
601 Invalidate();
603 m_bNeedsRecalc = true;
606 void ClientBox::clearEntries()
608 selectEntry( -1 );
609 m_bHasActive = false;
611 const ::osl::MutexGuard aGuard( m_entriesMutex );
613 m_vEntries.clear();
614 if ( IsReallyVisible() )
615 Invalidate();
616 m_bNeedsRecalc = true;
619 void ClientBox::populateEntries()
621 const ::osl::MutexGuard aGuard( m_entriesMutex );
623 clearEntries();
625 #ifdef ENABLE_SDREMOTE
626 RemoteServer::ensureDiscoverable();
628 vector< std::shared_ptr< ClientInfo > > aClients( RemoteServer::getClients() );
630 for ( const auto& rxClient : aClients )
632 addEntry( rxClient );
634 #endif
636 if ( IsReallyVisible() )
637 Invalidate();
638 m_bNeedsRecalc = true;
641 void ClientBox::DoScroll( long nDelta )
643 m_nTopIndex += nDelta;
644 Point aNewSBPt( m_aScrollBar->GetPosPixel() );
646 ::tools::Rectangle aScrRect( Point(), GetOutputSizePixel() );
647 aScrRect.AdjustRight( -(m_aScrollBar->GetSizePixel().Width()) );
648 Scroll( 0, -nDelta, aScrRect );
650 m_aScrollBar->SetPosPixel( aNewSBPt );
653 IMPL_LINK( ClientBox, ScrollHdl, ScrollBar*, pScrBar, void )
655 DoScroll( pScrBar->GetDelta() );
658 IMPL_LINK_NOARG( ClientBox, DeauthoriseHdl, Button*, void )
660 long aSelected = GetActiveEntryIndex();
661 if ( aSelected < 0 )
662 return;
663 TClientBoxEntry aEntry = GetEntryData(aSelected);
665 #ifdef ENABLE_SDREMOTE
666 RemoteServer::deauthoriseClient( aEntry->m_pClientInfo );
667 #endif
668 populateEntries();
671 } //namespace dp_gui
673 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */