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 <rtl/ustrbuf.hxx>
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>
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
43 , maBubbleTitle(std::move( aTitle
))
44 , maBubbleText(std::move( aText
))
45 , maBubbleImage(std::move( aImage
))
46 , maMaxTextSize( TEXT_MAX_WIDTH
, TEXT_MAX_HEIGHT
)
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 ) )
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
;
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
= std::move(aRegion
);
77 SetWindowRegionPixel( maBounds
);
80 void BubbleWindow::SetTitleAndText( const OUString
& rTitle
,
81 const OUString
& rText
,
84 maBubbleTitle
= rTitle
;
86 maBubbleImage
= rImage
;
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
),
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
);
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
& )
132 void BubbleWindow::Show( bool bVisible
)
136 FloatingWindow::Show( bVisible
);
140 // don't show bubbles without a text
141 if ( ( maBubbleTitle
.isEmpty() ) && ( maBubbleText
.isEmpty() ) )
144 Size aWindowSize
= GetSizePixel();
146 Size aImgSize
= maBubbleImage
.GetSizePixel();
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() );
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
);
164 aPos
.setX( maTipPos
.X() - aWindowSize
.Width() + TIP_RIGHT_OFFSET
);
165 aPos
.setY( maTipPos
.Y() );
166 AbsoluteScreenPixelPoint 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()
180 bool bFinished
= false;
181 vcl::Font aOldFont
= GetFont();
182 vcl::Font aBoldFont
= aOldFont
;
184 aBoldFont
.SetWeight( WEIGHT_BOLD
);
188 SetFont( aBoldFont
);
190 maTitleRect
= GetTextRect( tools::Rectangle( Point( 0, 0 ), maMaxTextSize
),
192 DrawTextFlags::MultiLine
| DrawTextFlags::WordBreak
);
195 maTextRect
= GetTextRect( tools::Rectangle( Point( 0, 0 ), maMaxTextSize
),
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 );
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())
242 return maIconIDs
[std::distance(maIconMBars
.begin(), aI
)];
245 VclPtr
<BubbleWindow
> MenuBarUpdateIconManager::GetBubbleWindow()
250 tools::Rectangle aIconRect
= mpActiveMBar
->GetMenuBarButtonRectPixel(GetIconID(mpActiveMBar
));
251 if( aIconRect
.IsEmpty() )
254 auto pBubbleWin
= mpBubbleWin
;
257 pBubbleWin
= VclPtr
<BubbleWindow
>::Create( mpActiveSysWin
, maBubbleTitle
,
258 maBubbleText
, maBubbleImage
);
259 mbBubbleChanged
= false;
261 else if ( mbBubbleChanged
) {
262 pBubbleWin
->SetTitleAndText( maBubbleTitle
, maBubbleText
,
264 mbBubbleChanged
= false;
267 Point aWinPos
= aIconRect
.BottomCenter();
269 pBubbleWin
->SetTipPosPixel( aWinPos
);
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();
297 SystemWindow
*pSysWin
= pWindow
->GetSystemWindow();
299 AddMenuBarIcon(*pSysWin
, false);
302 else if ( VclEventId::WindowMenubarRemoved
== nEventID
)
304 MenuBar
*pMBar
= static_cast<MenuBar
*>(rEvent
.GetData());
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;
344 AddMenuBarIcon(*pSysWin
, true);
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;
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
);
377 AddMenuBarIcon(*pActiveSysWin
, true);
380 IMPL_LINK_NOARG(MenuBarUpdateIconManager
, ClickHdl
, MenuBarButtonCallbackArg
&, bool)
384 mpBubbleWin
->Show( false );
386 maClickHdl
.Call(nullptr);
391 IMPL_LINK(MenuBarUpdateIconManager
, HighlightHdl
, MenuBarButtonCallbackArg
&, rData
, bool)
393 if ( rData
.bHighlight
)
396 RemoveBubbleWindow();
401 IMPL_LINK_NOARG(MenuBarUpdateIconManager
, WaitTimeOutHdl
, Timer
*, void)
403 mpBubbleWin
= GetBubbleWindow();
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
;
431 Application::PostUserEvent(LINK(this, MenuBarUpdateIconManager
, UserEventHdl
));
434 RemoveBubbleWindow();
435 RemoveMenuBarIcons();
440 void MenuBarUpdateIconManager::SetShowBubble(bool bShowBubble
)
442 mbShowBubble
= bShowBubble
;
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
;
462 void MenuBarUpdateIconManager::SetBubbleTitle(const OUString
& rTitle
)
464 if (rTitle
!= maBubbleTitle
)
466 maBubbleTitle
= rTitle
;
471 void MenuBarUpdateIconManager::SetBubbleText(const OUString
& rText
)
473 if (rText
!= maBubbleText
)
475 maBubbleText
= rText
;
481 Image
GetMenuBarIcon( MenuBar
const * pMBar
)
484 vcl::Window
*pMBarWin
= pMBar
->GetWindow();
485 sal_uInt32 nMBarHeight
= 20;
488 nMBarHeight
= pMBarWin
->GetOutputSizePixel().getHeight();
490 if (nMBarHeight
>= 35)
491 sResID
= RID_UPDATE_AVAILABLE_26
;
493 sResID
= RID_UPDATE_AVAILABLE_16
;
495 return Image(StockImage::Yes
, sResID
);
499 void MenuBarUpdateIconManager::AddMenuBarIcon(SystemWindow
& rSysWin
, bool bAddEventHdl
)
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())
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
);
532 rSysWin
.AddEventListener( maWindowEventHdl
);
535 if (mpActiveMBar
!= pActiveMBar
)
539 mpActiveMBar
->SetMenuBarButtonHighlightHdl(GetIconID(mpActiveMBar
),
540 Link
<MenuBarButtonCallbackArg
&,bool>());
542 mpActiveMBar
= pActiveMBar
;
545 mpActiveMBar
->SetMenuBarButtonHighlightHdl(GetIconID(mpActiveMBar
),
546 LINK(this, MenuBarUpdateIconManager
, HighlightHdl
));
550 mpActiveSysWin
= &rSysWin
;
552 if (mbShowBubble
&& pActiveMBar
)
554 mpBubbleWin
= GetBubbleWindow();
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())
570 auto aIconI
= maIconIDs
.begin() + std::distance(maIconMBars
.begin(), aI
);
574 pMenuBar
->RemoveMenuBarButton(*aIconI
);
580 maIconMBars
.erase(aI
);
581 maIconIDs
.erase(aIconI
);
584 void MenuBarUpdateIconManager::RemoveBubbleWindow()
587 maTimeoutTimer
.Stop();
588 mpBubbleWin
.disposeAndClear();
591 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */