bump product version to 7.6.3.2-android
[LibreOffice.git] / vcl / source / window / bubblewindow.cxx
blob9b8432a14a9a0b7cbfc33accd581034f5ab1cd2b
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 <rtl/ustrbuf.hxx>
21 #include <utility>
22 #include <vcl/menubarupdateicon.hxx>
23 #include <vcl/lineinfo.hxx>
24 #include <vcl/settings.hxx>
25 #include <vcl/svapp.hxx>
26 #include <unotools/resmgr.hxx>
27 #include <bubblewindow.hxx>
28 #include <bitmaps.hlst>
30 #define TIP_HEIGHT 15
31 #define TIP_WIDTH 7
32 #define TIP_RIGHT_OFFSET 18
33 #define BUBBLE_BORDER 10
34 #define TEXT_MAX_WIDTH 300
35 #define TEXT_MAX_HEIGHT 200
37 BubbleWindow::BubbleWindow( vcl::Window* pParent, OUString aTitle,
38 OUString aText, Image aImage )
39 : FloatingWindow( pParent, WB_SYSTEMWINDOW
40 | WB_OWNERDRAWDECORATION
41 | WB_NOBORDER
43 , maBubbleTitle(std::move( aTitle ))
44 , maBubbleText(std::move( aText ))
45 , maBubbleImage(std::move( aImage ))
46 , maMaxTextSize( TEXT_MAX_WIDTH, TEXT_MAX_HEIGHT )
47 , mnTipOffset( 0 )
49 SetBackground( Wallpaper( GetSettings().GetStyleSettings().GetHelpColor() ) );
52 void BubbleWindow::Resize()
54 FloatingWindow::Resize();
56 Size aSize = GetSizePixel();
58 if ( ( aSize.Height() < 20 ) || ( aSize.Width() < 60 ) )
59 return;
61 tools::Rectangle aRect( 0, TIP_HEIGHT, aSize.Width(), aSize.Height() - TIP_HEIGHT );
62 maRectPoly = tools::Polygon( aRect, 6, 6 );
63 vcl::Region aRegion( maRectPoly );
64 tools::Long nTipOffset = aSize.Width() - TIP_RIGHT_OFFSET + mnTipOffset;
66 Point aPointArr[4];
67 aPointArr[0] = Point( nTipOffset, TIP_HEIGHT );
68 aPointArr[1] = Point( nTipOffset, 0 );
69 aPointArr[2] = Point( nTipOffset + TIP_WIDTH , TIP_HEIGHT );
70 aPointArr[3] = Point( nTipOffset, TIP_HEIGHT );
71 maTriPoly = tools::Polygon( 4, aPointArr );
72 vcl::Region aTriRegion( maTriPoly );
74 aRegion.Union( aTriRegion);
75 maBounds = aRegion;
77 SetWindowRegionPixel( maBounds );
80 void BubbleWindow::SetTitleAndText( const OUString& rTitle,
81 const OUString& rText,
82 const Image& rImage )
84 maBubbleTitle = rTitle;
85 maBubbleText = rText;
86 maBubbleImage = rImage;
88 Resize();
91 void BubbleWindow::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& /*rRect*/)
93 LineInfo aThickLine( LineStyle::Solid, 2 );
95 rRenderContext.DrawPolyLine( maRectPoly, aThickLine );
96 rRenderContext.DrawPolyLine( maTriPoly );
98 Color aOldLine = rRenderContext.GetLineColor();
99 Size aSize = GetSizePixel();
100 tools::Long nTipOffset = aSize.Width() - TIP_RIGHT_OFFSET + mnTipOffset;
102 rRenderContext.SetLineColor( GetSettings().GetStyleSettings().GetHelpColor() );
103 rRenderContext.DrawLine( Point( nTipOffset+2, TIP_HEIGHT ),
104 Point( nTipOffset + TIP_WIDTH -1 , TIP_HEIGHT ),
105 aThickLine );
106 rRenderContext.SetLineColor( aOldLine );
108 Size aImgSize = maBubbleImage.GetSizePixel();
110 rRenderContext.DrawImage( Point( BUBBLE_BORDER, BUBBLE_BORDER + TIP_HEIGHT ), maBubbleImage );
112 vcl::Font aOldFont = GetFont();
113 vcl::Font aBoldFont = aOldFont;
114 aBoldFont.SetWeight( WEIGHT_BOLD );
116 SetFont( aBoldFont );
117 tools::Rectangle aTitleRect = maTitleRect;
118 aTitleRect.Move( aImgSize.Width(), 0 );
119 rRenderContext.DrawText( aTitleRect, maBubbleTitle, DrawTextFlags::MultiLine | DrawTextFlags::WordBreak );
121 SetFont( aOldFont );
122 tools::Rectangle aTextRect = maTextRect;
123 aTextRect.Move( aImgSize.Width(), 0 );
124 rRenderContext.DrawText( aTextRect, maBubbleText, DrawTextFlags::MultiLine | DrawTextFlags::WordBreak );
127 void BubbleWindow::MouseButtonDown( const MouseEvent& )
129 Show( false );
132 void BubbleWindow::Show( bool bVisible )
134 if ( !bVisible )
136 FloatingWindow::Show( bVisible );
137 return;
140 // don't show bubbles without a text
141 if ( ( maBubbleTitle.isEmpty() ) && ( maBubbleText.isEmpty() ) )
142 return;
144 Size aWindowSize = GetSizePixel();
146 Size aImgSize = maBubbleImage.GetSizePixel();
148 RecalcTextRects();
150 aWindowSize.setHeight( maTitleRect.GetHeight() * 7 / 4+ maTextRect.GetHeight() +
151 3 * BUBBLE_BORDER + TIP_HEIGHT );
153 if ( maTitleRect.GetWidth() > maTextRect.GetWidth() )
154 aWindowSize.setWidth( maTitleRect.GetWidth() );
155 else
156 aWindowSize.setWidth( maTextRect.GetWidth() );
158 aWindowSize.setWidth( aWindowSize.Width() + 3 * BUBBLE_BORDER + aImgSize.Width() );
160 if ( aWindowSize.Height() < aImgSize.Height() + TIP_HEIGHT + 2 * BUBBLE_BORDER )
161 aWindowSize.setHeight( aImgSize.Height() + TIP_HEIGHT + 2 * BUBBLE_BORDER );
163 Point aPos;
164 aPos.setX( maTipPos.X() - aWindowSize.Width() + TIP_RIGHT_OFFSET );
165 aPos.setY( maTipPos.Y() );
166 Point aScreenPos = GetParent()->OutputToAbsoluteScreenPixel( aPos );
167 if ( aScreenPos.X() < 0 )
169 mnTipOffset = aScreenPos.X();
170 aPos.AdjustX( -mnTipOffset );
172 SetPosSizePixel( aPos, aWindowSize );
174 FloatingWindow::Show( bVisible, ShowFlags::NoActivate );
177 void BubbleWindow::RecalcTextRects()
179 Size aTotalSize;
180 bool bFinished = false;
181 vcl::Font aOldFont = GetFont();
182 vcl::Font aBoldFont = aOldFont;
184 aBoldFont.SetWeight( WEIGHT_BOLD );
186 while ( !bFinished )
188 SetFont( aBoldFont );
190 maTitleRect = GetTextRect( tools::Rectangle( Point( 0, 0 ), maMaxTextSize ),
191 maBubbleTitle,
192 DrawTextFlags::MultiLine | DrawTextFlags::WordBreak );
194 SetFont( aOldFont );
195 maTextRect = GetTextRect( tools::Rectangle( Point( 0, 0 ), maMaxTextSize ),
196 maBubbleText,
197 DrawTextFlags::MultiLine | DrawTextFlags::WordBreak );
199 if ( maTextRect.GetHeight() < 10 )
200 maTextRect.setHeight( 10 );
202 aTotalSize.setHeight( maTitleRect.GetHeight() +
203 aBoldFont.GetFontHeight() * 3 / 4 +
204 maTextRect.GetHeight() +
205 3 * BUBBLE_BORDER + TIP_HEIGHT );
206 if ( aTotalSize.Height() > maMaxTextSize.Height() )
208 maMaxTextSize.setWidth( maMaxTextSize.Width() * 3 / 2 );
209 maMaxTextSize.setHeight( maMaxTextSize.Height() * 3 / 2 );
211 else
212 bFinished = true;
214 maTitleRect.Move( 2*BUBBLE_BORDER, BUBBLE_BORDER + TIP_HEIGHT );
215 maTextRect.Move( 2*BUBBLE_BORDER, BUBBLE_BORDER + TIP_HEIGHT + maTitleRect.GetHeight() + aBoldFont.GetFontHeight() * 3 / 4 );
218 MenuBarUpdateIconManager::MenuBarUpdateIconManager()
219 : maTimeoutTimer("MenuBarUpdateIconManager")
220 , maWaitIdle("vcl MenuBarUpdateIconManager maWaitIdle")
221 , mbShowMenuIcon(false)
222 , mbShowBubble(false)
223 , mbBubbleChanged( false )
225 maTimeoutTimer.SetTimeout( 10000 );
226 maTimeoutTimer.SetInvokeHandler(LINK(this, MenuBarUpdateIconManager, TimeOutHdl));
228 maWaitIdle.SetPriority( TaskPriority::LOWEST );
229 maWaitIdle.SetInvokeHandler(LINK(this, MenuBarUpdateIconManager, WaitTimeOutHdl));
231 maApplicationEventHdl = LINK(this, MenuBarUpdateIconManager, ApplicationEventHdl);
232 Application::AddEventListener( maApplicationEventHdl );
234 maWindowEventHdl = LINK(this, MenuBarUpdateIconManager, WindowEventHdl);
237 sal_uInt16 MenuBarUpdateIconManager::GetIconID(MenuBar* pMenuBar) const
239 auto aI = std::find(maIconMBars.begin(), maIconMBars.end(), pMenuBar);
240 if (aI == maIconMBars.end())
241 return 0;
242 return maIconIDs[std::distance(maIconMBars.begin(), aI)];
245 VclPtr<BubbleWindow> MenuBarUpdateIconManager::GetBubbleWindow()
247 if (!mpActiveSysWin)
248 return nullptr;
250 tools::Rectangle aIconRect = mpActiveMBar->GetMenuBarButtonRectPixel(GetIconID(mpActiveMBar));
251 if( aIconRect.IsEmpty() )
252 return nullptr;
254 auto pBubbleWin = mpBubbleWin;
256 if ( !pBubbleWin ) {
257 pBubbleWin = VclPtr<BubbleWindow>::Create( mpActiveSysWin, maBubbleTitle,
258 maBubbleText, maBubbleImage );
259 mbBubbleChanged = false;
261 else if ( mbBubbleChanged ) {
262 pBubbleWin->SetTitleAndText( maBubbleTitle, maBubbleText,
263 maBubbleImage );
264 mbBubbleChanged = false;
267 Point aWinPos = aIconRect.BottomCenter();
269 pBubbleWin->SetTipPosPixel( aWinPos );
271 return pBubbleWin;
274 IMPL_LINK_NOARG(MenuBarUpdateIconManager, TimeOutHdl, Timer *, void)
276 RemoveBubbleWindow();
279 IMPL_LINK(MenuBarUpdateIconManager, WindowEventHdl, VclWindowEvent&, rEvent, void)
281 VclEventId nEventID = rEvent.GetId();
283 if ( VclEventId::ObjectDying == nEventID )
285 if (mpActiveSysWin == rEvent.GetWindow())
287 RemoveBubbleWindow();
288 mpActiveSysWin = nullptr;
289 mpActiveMBar = nullptr;
292 else if ( VclEventId::WindowMenubarAdded == nEventID )
294 vcl::Window *pWindow = rEvent.GetWindow();
295 if ( pWindow )
297 SystemWindow *pSysWin = pWindow->GetSystemWindow();
298 if (pSysWin)
299 AddMenuBarIcon(*pSysWin, false);
302 else if ( VclEventId::WindowMenubarRemoved == nEventID )
304 MenuBar *pMBar = static_cast<MenuBar*>(rEvent.GetData());
305 if (pMBar)
307 if (pMBar == mpActiveMBar)
309 RemoveBubbleWindow();
310 mpActiveMBar = nullptr;
312 RemoveMenuBarIcon(pMBar);
315 else if ( ( nEventID == VclEventId::WindowMove ) ||
316 ( nEventID == VclEventId::WindowResize ) )
318 if ( mpActiveSysWin == rEvent.GetWindow() &&
319 mpBubbleWin && mpActiveMBar )
321 tools::Rectangle aIconRect = mpActiveMBar->GetMenuBarButtonRectPixel(GetIconID(mpActiveMBar));
322 Point aWinPos = aIconRect.BottomCenter();
323 mpBubbleWin->SetTipPosPixel( aWinPos );
324 if ( mpBubbleWin->IsVisible() )
325 mpBubbleWin->Show(); // This will recalc the screen position of the bubble
330 IMPL_LINK(MenuBarUpdateIconManager, ApplicationEventHdl, VclSimpleEvent&, rEvent, void)
332 switch (rEvent.GetId())
334 case VclEventId::WindowShow:
335 case VclEventId::WindowActivate:
336 case VclEventId::WindowGetFocus: {
338 vcl::Window *pWindow = static_cast< VclWindowEvent * >(&rEvent)->GetWindow();
339 if ( pWindow && pWindow->IsTopWindow() )
341 SystemWindow *pSysWin = pWindow->GetSystemWindow();
342 MenuBar *pMBar = pSysWin ? pSysWin->GetMenuBar() : nullptr;
343 if (pMBar)
344 AddMenuBarIcon(*pSysWin, true);
346 break;
348 default: break;
352 IMPL_LINK_NOARG(MenuBarUpdateIconManager, UserEventHdl, void*, void)
354 vcl::Window *pTopWin = Application::GetFirstTopLevelWindow();
355 vcl::Window *pActiveWin = Application::GetActiveTopWindow();
356 SystemWindow *pActiveSysWin = nullptr;
358 vcl::Window *pBubbleWin = nullptr;
359 if ( mpBubbleWin )
360 pBubbleWin = mpBubbleWin;
362 if ( pActiveWin && ( pActiveWin != pBubbleWin ) && pActiveWin->IsTopWindow() )
363 pActiveSysWin = pActiveWin->GetSystemWindow();
365 if ( pActiveWin == pBubbleWin )
366 pActiveSysWin = nullptr;
368 while ( !pActiveSysWin && pTopWin )
370 if ( ( pTopWin != pBubbleWin ) && pTopWin->IsTopWindow() )
371 pActiveSysWin = pTopWin->GetSystemWindow();
372 if ( !pActiveSysWin )
373 pTopWin = Application::GetNextTopLevelWindow( pTopWin );
376 if ( pActiveSysWin )
377 AddMenuBarIcon(*pActiveSysWin, true);
380 IMPL_LINK_NOARG(MenuBarUpdateIconManager, ClickHdl, MenuBarButtonCallbackArg&, bool)
382 maWaitIdle.Stop();
383 if ( mpBubbleWin )
384 mpBubbleWin->Show( false );
386 maClickHdl.Call(nullptr);
388 return false;
391 IMPL_LINK(MenuBarUpdateIconManager, HighlightHdl, MenuBarButtonCallbackArg&, rData, bool)
393 if ( rData.bHighlight )
394 maWaitIdle.Start();
395 else
396 RemoveBubbleWindow();
398 return false;
401 IMPL_LINK_NOARG(MenuBarUpdateIconManager, WaitTimeOutHdl, Timer *, void)
403 mpBubbleWin = GetBubbleWindow();
405 if ( mpBubbleWin )
407 mpBubbleWin->Show();
411 MenuBarUpdateIconManager::~MenuBarUpdateIconManager()
413 Application::RemoveEventListener( maApplicationEventHdl );
415 RemoveBubbleWindow();
416 RemoveMenuBarIcons();
419 void MenuBarUpdateIconManager::RemoveMenuBarIcons()
421 while (!maIconMBars.empty())
422 RemoveMenuBarIcon(maIconMBars[0]);
425 void MenuBarUpdateIconManager::SetShowMenuIcon(bool bShowMenuIcon)
427 if ( bShowMenuIcon != mbShowMenuIcon )
429 mbShowMenuIcon = bShowMenuIcon;
430 if ( bShowMenuIcon )
431 Application::PostUserEvent(LINK(this, MenuBarUpdateIconManager, UserEventHdl));
432 else
434 RemoveBubbleWindow();
435 RemoveMenuBarIcons();
440 void MenuBarUpdateIconManager::SetShowBubble(bool bShowBubble)
442 mbShowBubble = bShowBubble;
443 if ( mbShowBubble )
444 Application::PostUserEvent(LINK(this, MenuBarUpdateIconManager, UserEventHdl));
445 else if ( mpBubbleWin )
446 mpBubbleWin->Show( false );
449 void MenuBarUpdateIconManager::SetBubbleChanged()
451 mbBubbleChanged = true;
452 if (mbBubbleChanged && mpBubbleWin)
453 mpBubbleWin->Show( false );
456 void MenuBarUpdateIconManager::SetBubbleImage(const Image& rImage)
458 maBubbleImage = rImage;
459 SetBubbleChanged();
462 void MenuBarUpdateIconManager::SetBubbleTitle(const OUString& rTitle)
464 if (rTitle != maBubbleTitle)
466 maBubbleTitle = rTitle;
467 SetBubbleChanged();
471 void MenuBarUpdateIconManager::SetBubbleText(const OUString& rText)
473 if (rText != maBubbleText)
475 maBubbleText = rText;
476 SetBubbleChanged();
480 namespace {
481 Image GetMenuBarIcon( MenuBar const * pMBar )
483 OUString sResID;
484 vcl::Window *pMBarWin = pMBar->GetWindow();
485 sal_uInt32 nMBarHeight = 20;
487 if ( pMBarWin )
488 nMBarHeight = pMBarWin->GetOutputSizePixel().getHeight();
490 if (nMBarHeight >= 35)
491 sResID = RID_UPDATE_AVAILABLE_26;
492 else
493 sResID = RID_UPDATE_AVAILABLE_16;
495 return Image(StockImage::Yes, sResID);
499 void MenuBarUpdateIconManager::AddMenuBarIcon(SystemWindow& rSysWin, bool bAddEventHdl)
501 if (!mbShowMenuIcon)
502 return;
504 MenuBar *pActiveMBar = rSysWin.GetMenuBar();
505 if (&rSysWin != mpActiveSysWin || pActiveMBar != mpActiveMBar)
506 RemoveBubbleWindow();
508 auto aI = std::find(maIconMBars.begin(), maIconMBars.end(), pActiveMBar);
509 if (aI == maIconMBars.end())
511 if (pActiveMBar)
513 OUStringBuffer aBuf;
514 if( !maBubbleTitle.isEmpty() )
515 aBuf.append( maBubbleTitle );
516 if( !maBubbleText.isEmpty() )
518 if( !maBubbleTitle.isEmpty() )
519 aBuf.append( "\n\n" );
520 aBuf.append( maBubbleText );
523 Image aImage = GetMenuBarIcon( pActiveMBar );
524 sal_uInt16 nIconID = pActiveMBar->AddMenuBarButton( aImage,
525 LINK( this, MenuBarUpdateIconManager, ClickHdl ),
526 aBuf.makeStringAndClear() );
527 maIconMBars.push_back(pActiveMBar);
528 maIconIDs.push_back(nIconID);
531 if (bAddEventHdl)
532 rSysWin.AddEventListener( maWindowEventHdl );
535 if (mpActiveMBar != pActiveMBar)
537 if (mpActiveMBar)
539 mpActiveMBar->SetMenuBarButtonHighlightHdl(GetIconID(mpActiveMBar),
540 Link<MenuBarButtonCallbackArg&,bool>());
542 mpActiveMBar = pActiveMBar;
543 if (mpActiveMBar)
545 mpActiveMBar->SetMenuBarButtonHighlightHdl(GetIconID(mpActiveMBar),
546 LINK(this, MenuBarUpdateIconManager, HighlightHdl));
550 mpActiveSysWin = &rSysWin;
552 if (mbShowBubble && pActiveMBar)
554 mpBubbleWin = GetBubbleWindow();
555 if ( mpBubbleWin )
557 mpBubbleWin->Show();
558 maTimeoutTimer.Start();
560 mbShowBubble = false;
564 void MenuBarUpdateIconManager::RemoveMenuBarIcon(MenuBar* pMenuBar)
566 auto aI = std::find(maIconMBars.begin(), maIconMBars.end(), pMenuBar);
567 if (aI == maIconMBars.end())
568 return;
570 auto aIconI = maIconIDs.begin() + std::distance(maIconMBars.begin(), aI);
574 pMenuBar->RemoveMenuBarButton(*aIconI);
576 catch (...)
580 maIconMBars.erase(aI);
581 maIconIDs.erase(aIconI);
584 void MenuBarUpdateIconManager::RemoveBubbleWindow()
586 maWaitIdle.Stop();
587 maTimeoutTimer.Stop();
588 mpBubbleWin.disposeAndClear();
591 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */