1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
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/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
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 .
18  */
19 
20 #include <comphelper/string.hxx>
21 #include <sal/log.hxx>
22 
23 #include <tools/diagnose_ex.h>
24 #include <tools/time.hxx>
25 
26 #include <vcl/window.hxx>
27 #include <vcl/event.hxx>
28 #include <vcl/svapp.hxx>
29 #include <vcl/wrkwin.hxx>
30 #include <vcl/help.hxx>
31 #include <vcl/settings.hxx>
32 
33 #include <helpwin.hxx>
34 #include <salframe.hxx>
35 #include <svdata.hxx>
36 
37 #define HELPWINSTYLE_QUICK      0
38 #define HELPWINSTYLE_BALLOON    1
39 
40 #define HELPTEXTMARGIN_QUICK    3
41 #define HELPTEXTMARGIN_BALLOON  6
42 
43 #define HELPTEXTMAXLEN        150
44 
Help()45 Help::Help()
46 {
47 }
48 
~Help()49 Help::~Help()
50 {
51 }
52 
Start(const OUString &,const vcl::Window *)53 bool Help::Start( const OUString&, const vcl::Window* )
54 {
55     return false;
56 }
57 
Start(const OUString &,weld::Widget *)58 bool Help::Start(const OUString&, weld::Widget*)
59 {
60     return false;
61 }
62 
SearchKeyword(const OUString &)63 void Help::SearchKeyword( const OUString& )
64 {
65 }
66 
GetHelpText(const OUString &,const vcl::Window *)67 OUString Help::GetHelpText( const OUString&, const vcl::Window* )
68 {
69     return OUString();
70 }
71 
GetHelpText(const OUString &,const weld::Widget *)72 OUString Help::GetHelpText( const OUString&, const weld::Widget* )
73 {
74     return OUString();
75 }
76 
EnableContextHelp()77 void Help::EnableContextHelp()
78 {
79     ImplGetSVHelpData().mbContextHelp = true;
80 }
81 
DisableContextHelp()82 void Help::DisableContextHelp()
83 {
84     ImplGetSVHelpData().mbContextHelp = false;
85 }
86 
IsContextHelpEnabled()87 bool Help::IsContextHelpEnabled()
88 {
89     return ImplGetSVHelpData().mbContextHelp;
90 }
91 
EnableExtHelp()92 void Help::EnableExtHelp()
93 {
94     ImplGetSVHelpData().mbExtHelp = true;
95 }
96 
DisableExtHelp()97 void Help::DisableExtHelp()
98 {
99     ImplGetSVHelpData().mbExtHelp = false;
100 }
101 
IsExtHelpEnabled()102 bool Help::IsExtHelpEnabled()
103 {
104     return ImplGetSVHelpData().mbExtHelp;
105 }
106 
StartExtHelp()107 bool Help::StartExtHelp()
108 {
109     ImplSVData* pSVData = ImplGetSVData();
110     ImplSVHelpData& aHelpData = ImplGetSVHelpData();
111 
112     if ( aHelpData.mbExtHelp && !aHelpData.mbExtHelpMode )
113     {
114         aHelpData.mbExtHelpMode = true;
115         aHelpData.mbOldBalloonMode = aHelpData.mbBalloonHelp;
116         aHelpData.mbBalloonHelp = true;
117         if (pSVData->maFrameData.mpAppWin)
118             pSVData->maFrameData.mpAppWin->ImplGenerateMouseMove();
119         return true;
120     }
121 
122     return false;
123 }
124 
EndExtHelp()125 bool Help::EndExtHelp()
126 {
127     ImplSVData* pSVData = ImplGetSVData();
128     ImplSVHelpData& aHelpData = ImplGetSVHelpData();
129 
130     if ( aHelpData.mbExtHelp && aHelpData.mbExtHelpMode )
131     {
132         aHelpData.mbExtHelpMode = false;
133         aHelpData.mbBalloonHelp = aHelpData.mbOldBalloonMode;
134         if (pSVData->maFrameData.mpAppWin)
135             pSVData->maFrameData.mpAppWin->ImplGenerateMouseMove();
136         return true;
137     }
138 
139     return false;
140 }
141 
EnableBalloonHelp()142 void Help::EnableBalloonHelp()
143 {
144     ImplGetSVHelpData().mbBalloonHelp = true;
145 }
146 
DisableBalloonHelp()147 void Help::DisableBalloonHelp()
148 {
149     ImplGetSVHelpData().mbBalloonHelp = false;
150 }
151 
IsBalloonHelpEnabled()152 bool Help::IsBalloonHelpEnabled()
153 {
154     return ImplGetSVHelpData().mbBalloonHelp;
155 }
156 
ShowBalloon(vcl::Window * pParent,const Point & rScreenPos,const tools::Rectangle & rRect,const OUString & rHelpText)157 void Help::ShowBalloon( vcl::Window* pParent,
158                         const Point& rScreenPos, const tools::Rectangle& rRect,
159                         const OUString& rHelpText )
160 {
161     ImplShowHelpWindow( pParent, HELPWINSTYLE_BALLOON, QuickHelpFlags::NONE,
162                         rHelpText, rScreenPos, rRect );
163 }
164 
EnableQuickHelp()165 void Help::EnableQuickHelp()
166 {
167     ImplGetSVHelpData().mbQuickHelp = true;
168 }
169 
DisableQuickHelp()170 void Help::DisableQuickHelp()
171 {
172     ImplGetSVHelpData().mbQuickHelp = false;
173 }
174 
IsQuickHelpEnabled()175 bool Help::IsQuickHelpEnabled()
176 {
177     return ImplGetSVHelpData().mbQuickHelp;
178 }
179 
ShowQuickHelp(vcl::Window * pParent,const tools::Rectangle & rScreenRect,const OUString & rHelpText,QuickHelpFlags nStyle)180 void Help::ShowQuickHelp( vcl::Window* pParent,
181                           const tools::Rectangle& rScreenRect,
182                           const OUString& rHelpText,
183                           QuickHelpFlags nStyle )
184 {
185     sal_uInt16 nHelpWinStyle = ( nStyle & QuickHelpFlags::TipStyleBalloon ) ? HELPWINSTYLE_BALLOON : HELPWINSTYLE_QUICK;
186     ImplShowHelpWindow( pParent, nHelpWinStyle, nStyle,
187                         rHelpText,
188                         pParent->OutputToScreenPixel( pParent->GetPointerPosPixel() ), rScreenRect );
189 }
190 
HideBalloonAndQuickHelp()191 void Help::HideBalloonAndQuickHelp()
192 {
193     HelpTextWindow const * pHelpWin = ImplGetSVHelpData().mpHelpWin;
194     bool const bIsVisible = ( pHelpWin != nullptr ) && pHelpWin->IsVisible();
195     ImplDestroyHelpWindow( bIsVisible );
196 }
197 
ShowPopover(vcl::Window * pParent,const tools::Rectangle & rScreenRect,const OUString & rText,QuickHelpFlags nStyle)198 void* Help::ShowPopover(vcl::Window* pParent, const tools::Rectangle& rScreenRect,
199                               const OUString& rText, QuickHelpFlags nStyle)
200 {
201     void* nId = pParent->ImplGetFrame()->ShowPopover(rText, pParent, rScreenRect, nStyle);
202     if (nId)
203     {
204         //popovers are handled natively, return early
205         return nId;
206     }
207 
208     sal_uInt16 nHelpWinStyle = ( nStyle & QuickHelpFlags::TipStyleBalloon ) ? HELPWINSTYLE_BALLOON : HELPWINSTYLE_QUICK;
209     VclPtrInstance<HelpTextWindow> pHelpWin( pParent, rText, nHelpWinStyle, nStyle );
210 
211     nId = pHelpWin.get();
212     UpdatePopover(nId, pParent, rScreenRect, rText);
213 
214     pHelpWin->ShowHelp(true);
215     return nId;
216 }
217 
UpdatePopover(void * nId,vcl::Window * pParent,const tools::Rectangle & rScreenRect,const OUString & rText)218 void Help::UpdatePopover(void* nId, vcl::Window* pParent, const tools::Rectangle& rScreenRect,
219                          const OUString& rText)
220 {
221     if (pParent->ImplGetFrame()->UpdatePopover(nId, rText, pParent, rScreenRect))
222     {
223         //popovers are handled natively, return early
224         return;
225     }
226 
227     HelpTextWindow* pHelpWin = static_cast< HelpTextWindow* >( nId );
228     ENSURE_OR_RETURN_VOID( pHelpWin != nullptr, "Help::UpdatePopover: invalid ID!" );
229 
230     Size aSz = pHelpWin->CalcOutSize();
231     pHelpWin->SetOutputSizePixel( aSz );
232     ImplSetHelpWindowPos( pHelpWin, pHelpWin->GetWinStyle(), pHelpWin->GetStyle(),
233         pParent->OutputToScreenPixel( pParent->GetPointerPosPixel() ), rScreenRect );
234 
235     pHelpWin->SetHelpText( rText );
236     pHelpWin->Invalidate();
237 }
238 
HidePopover(vcl::Window const * pParent,void * nId)239 void Help::HidePopover(vcl::Window const * pParent, void* nId)
240 {
241     if (pParent->ImplGetFrame()->HidePopover(nId))
242     {
243         //popovers are handled natively, return early
244         return;
245     }
246 
247     VclPtr<HelpTextWindow> pHelpWin = static_cast<HelpTextWindow*>(nId);
248     vcl::Window* pFrameWindow = pHelpWin->ImplGetFrameWindow();
249     pHelpWin->Hide();
250     // trigger update, so that a Paint is instantly triggered since we do not save the background
251     pFrameWindow->ImplUpdateAll();
252     pHelpWin.disposeAndClear();
253     ImplGetSVHelpData().mnLastHelpHideTime = tools::Time::GetSystemTicks();
254 }
255 
HelpTextWindow(vcl::Window * pParent,const OUString & rText,sal_uInt16 nHelpWinStyle,QuickHelpFlags nStyle)256 HelpTextWindow::HelpTextWindow( vcl::Window* pParent, const OUString& rText, sal_uInt16 nHelpWinStyle, QuickHelpFlags nStyle ) :
257     FloatingWindow( pParent, WB_SYSTEMWINDOW|WB_TOOLTIPWIN ), // #105827# if we change the parent, mirroring will not work correctly when positioning this window
258     maHelpText( rText )
259 {
260     SetType( WindowType::HELPTEXTWINDOW );
261     ImplSetMouseTransparent( true );
262     mnHelpWinStyle = nHelpWinStyle;
263     mnStyle = nStyle;
264 
265     if( mnStyle & QuickHelpFlags::BiDiRtl )
266     {
267         ComplexTextLayoutFlags nLayoutMode = GetOutDev()->GetLayoutMode();
268         nLayoutMode |= ComplexTextLayoutFlags::BiDiRtl | ComplexTextLayoutFlags::TextOriginLeft;
269         GetOutDev()->SetLayoutMode( nLayoutMode );
270     }
271     SetHelpText( rText );
272     Window::SetHelpText( rText );
273 
274     if ( ImplGetSVHelpData().mbSetKeyboardHelp )
275         ImplGetSVHelpData().mbKeyboardHelp = true;
276 
277 
278     maShowTimer.SetInvokeHandler( LINK( this, HelpTextWindow, TimerHdl ) );
279     maShowTimer.SetDebugName( "vcl::HelpTextWindow maShowTimer" );
280 
281     const HelpSettings& rHelpSettings = pParent->GetSettings().GetHelpSettings();
282     maHideTimer.SetTimeout( rHelpSettings.GetTipTimeout() );
283     maHideTimer.SetInvokeHandler( LINK( this, HelpTextWindow, TimerHdl ) );
284     maHideTimer.SetDebugName( "vcl::HelpTextWindow maHideTimer" );
285 }
286 
ApplySettings(vcl::RenderContext & rRenderContext)287 void HelpTextWindow::ApplySettings(vcl::RenderContext& rRenderContext)
288 {
289     const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings();
290     SetPointFont(rRenderContext, rStyleSettings.GetHelpFont());
291     rRenderContext.SetTextColor(rStyleSettings.GetHelpTextColor());
292     rRenderContext.SetTextAlign(ALIGN_TOP);
293 
294     if (rRenderContext.IsNativeControlSupported(ControlType::Tooltip, ControlPart::Entire))
295     {
296         EnableChildTransparentMode();
297         SetParentClipMode(ParentClipMode::NoClip);
298         SetPaintTransparent(true);
299         rRenderContext.SetBackground();
300     }
301     else
302         rRenderContext.SetBackground(Wallpaper(rStyleSettings.GetHelpColor()));
303 
304     if (rStyleSettings.GetHelpColor().IsDark())
305         rRenderContext.SetLineColor(COL_WHITE);
306     else
307         rRenderContext.SetLineColor(COL_BLACK);
308     rRenderContext.SetFillColor();
309 }
310 
~HelpTextWindow()311 HelpTextWindow::~HelpTextWindow()
312 {
313     disposeOnce();
314 }
315 
dispose()316 void HelpTextWindow::dispose()
317 {
318     maShowTimer.Stop();
319     maHideTimer.Stop();
320 
321     if( this == ImplGetSVHelpData().mpHelpWin )
322         ImplGetSVHelpData().mpHelpWin = nullptr;
323     FloatingWindow::dispose();
324 }
325 
SetHelpText(const OUString & rHelpText)326 void HelpTextWindow::SetHelpText( const OUString& rHelpText )
327 {
328     maHelpText = rHelpText;
329     ApplySettings(*GetOutDev());
330     if ( mnHelpWinStyle == HELPWINSTYLE_QUICK && maHelpText.getLength() < HELPTEXTMAXLEN && maHelpText.indexOf('\n') < 0)
331     {
332         Size aSize;
333         aSize.setHeight( GetTextHeight() );
334         if ( mnStyle & QuickHelpFlags::CtrlText )
335             aSize.setWidth( GetOutDev()->GetCtrlTextWidth( maHelpText ) );
336         else
337             aSize.setWidth( GetTextWidth( maHelpText ) );
338         maTextRect = tools::Rectangle( Point( HELPTEXTMARGIN_QUICK, HELPTEXTMARGIN_QUICK ), aSize );
339     }
340     else // HELPWINSTYLE_BALLOON
341     {
342         sal_Int32 nCharsInLine = 35 + ((maHelpText.getLength()/100)*5);
343         // average width to have all windows consistent
344         OUStringBuffer aBuf;
345         comphelper::string::padToLength(aBuf, nCharsInLine, 'x');
346         OUString aXXX = aBuf.makeStringAndClear();
347         tools::Long nWidth = GetTextWidth( aXXX );
348         Size aTmpSize( nWidth, 0x7FFFFFFF );
349         tools::Rectangle aTry1( Point(), aTmpSize );
350         DrawTextFlags nDrawFlags = DrawTextFlags::MultiLine | DrawTextFlags::WordBreak |
351                             DrawTextFlags::Left | DrawTextFlags::Top;
352         if ( mnStyle & QuickHelpFlags::CtrlText )
353             nDrawFlags |= DrawTextFlags::Mnemonic;
354         tools::Rectangle aTextRect = GetTextRect( aTry1, maHelpText, nDrawFlags );
355 
356         // get a better width later...
357         maTextRect = aTextRect;
358 
359         // safety distance...
360         maTextRect.SetPos( Point( HELPTEXTMARGIN_BALLOON, HELPTEXTMARGIN_BALLOON ) );
361     }
362 
363     Size aSize( CalcOutSize() );
364     SetOutputSizePixel( aSize );
365 }
366 
ImplShow()367 void HelpTextWindow::ImplShow()
368 {
369     VclPtr<HelpTextWindow> xWindow( this );
370     Show( true, ShowFlags::NoActivate );
371     if( !xWindow->isDisposed() )
372         PaintImmediately();
373 }
374 
Paint(vcl::RenderContext & rRenderContext,const tools::Rectangle &)375 void HelpTextWindow::Paint( vcl::RenderContext& rRenderContext, const tools::Rectangle& )
376 {
377     // paint native background
378     bool bNativeOK = false;
379     if (rRenderContext.IsNativeControlSupported(ControlType::Tooltip, ControlPart::Entire))
380     {
381         tools::Rectangle aCtrlRegion(Point(0, 0), GetOutputSizePixel());
382         ImplControlValue aControlValue;
383         bNativeOK = rRenderContext.DrawNativeControl(ControlType::Tooltip, ControlPart::Entire, aCtrlRegion,
384                                                      ControlState::NONE, aControlValue, OUString());
385     }
386 
387     // paint text
388     if (mnHelpWinStyle == HELPWINSTYLE_QUICK && maHelpText.getLength() < HELPTEXTMAXLEN && maHelpText.indexOf('\n') < 0)
389     {
390         if ( mnStyle & QuickHelpFlags::CtrlText )
391             rRenderContext.DrawCtrlText(maTextRect.TopLeft(), maHelpText);
392         else
393             rRenderContext.DrawText(maTextRect.TopLeft(), maHelpText);
394     }
395     else // HELPWINSTYLE_BALLOON
396     {
397         DrawTextFlags nDrawFlags = DrawTextFlags::MultiLine|DrawTextFlags::WordBreak|
398                                 DrawTextFlags::Left|DrawTextFlags::Top;
399         if (mnStyle & QuickHelpFlags::CtrlText)
400             nDrawFlags |= DrawTextFlags::Mnemonic;
401         rRenderContext.DrawText(maTextRect, maHelpText, nDrawFlags);
402     }
403 
404     // border
405     if (bNativeOK)
406         return;
407 
408     Size aSz = GetOutputSizePixel();
409     rRenderContext.DrawRect(tools::Rectangle(Point(), aSz));
410     if (mnHelpWinStyle == HELPWINSTYLE_BALLOON)
411     {
412         aSz.AdjustWidth( -2 );
413         aSz.AdjustHeight( -2 );
414         Color aColor(rRenderContext.GetLineColor());
415         rRenderContext.SetLineColor(COL_GRAY);
416         rRenderContext.DrawRect(tools::Rectangle(Point(1, 1), aSz));
417         rRenderContext.SetLineColor(aColor);
418     }
419 }
420 
ShowHelp(bool bNoDelay)421 void HelpTextWindow::ShowHelp(bool bNoDelay)
422 {
423     sal_uLong nTimeout = 0;
424     if (!bNoDelay)
425     {
426         // In case of ExtendedHelp display help sooner
427         if ( ImplGetSVHelpData().mbExtHelpMode )
428             nTimeout = 15;
429         else
430         {
431             if ( mnHelpWinStyle == HELPWINSTYLE_QUICK )
432                 nTimeout = HelpSettings::GetTipDelay();
433             else
434                 nTimeout = HelpSettings::GetBalloonDelay();
435         }
436     }
437 
438     maShowTimer.SetTimeout( nTimeout );
439     maShowTimer.Start();
440 }
441 
IMPL_LINK(HelpTextWindow,TimerHdl,Timer *,pTimer,void)442 IMPL_LINK( HelpTextWindow, TimerHdl, Timer*, pTimer, void)
443 {
444     if ( pTimer == &maShowTimer )
445     {
446         if ( mnHelpWinStyle == HELPWINSTYLE_QUICK )
447         {
448             // start auto-hide-timer for non-ShowTip windows
449             if ( this == ImplGetSVHelpData().mpHelpWin )
450                 maHideTimer.Start();
451         }
452         ImplShow();
453     }
454     else
455     {
456         SAL_WARN_IF( pTimer != &maHideTimer, "vcl", "HelpTextWindow::TimerHdl with bad Timer" );
457         ImplDestroyHelpWindow( true );
458     }
459 }
460 
CalcOutSize() const461 Size HelpTextWindow::CalcOutSize() const
462 {
463     Size aSz = maTextRect.GetSize();
464     aSz.AdjustWidth(2*maTextRect.Left() );
465     aSz.AdjustHeight(2*maTextRect.Top() );
466     return aSz;
467 }
468 
RequestHelp(const HelpEvent &)469 void HelpTextWindow::RequestHelp( const HelpEvent& /*rHEvt*/ )
470 {
471     // Just to assure that Window::RequestHelp() is not called by
472     // ShowQuickHelp/ShowBalloonHelp in the HelpTextWindow.
473 }
474 
GetText() const475 OUString HelpTextWindow::GetText() const
476 {
477     return maHelpText;
478 }
479 
ImplShowHelpWindow(vcl::Window * pParent,sal_uInt16 nHelpWinStyle,QuickHelpFlags nStyle,const OUString & rHelpText,const Point & rScreenPos,const tools::Rectangle & rHelpArea)480 void ImplShowHelpWindow( vcl::Window* pParent, sal_uInt16 nHelpWinStyle, QuickHelpFlags nStyle,
481                          const OUString& rHelpText,
482                          const Point& rScreenPos, const tools::Rectangle& rHelpArea )
483 {
484     if (pParent->ImplGetFrame()->ShowTooltip(rHelpText, rHelpArea))
485     {
486         //tooltips are handled natively, return early
487         return;
488     }
489 
490     ImplSVHelpData& aHelpData = ImplGetSVHelpData();
491 
492     if (rHelpText.isEmpty() && !aHelpData.mbRequestingHelp)
493         return;
494 
495     VclPtr<HelpTextWindow> pHelpWin = aHelpData.mpHelpWin;
496     bool bNoDelay = false;
497     if ( pHelpWin )
498     {
499         SAL_WARN_IF( pHelpWin == pParent, "vcl", "HelpInHelp ?!" );
500 
501         bool bRemoveHelp = (rHelpText.isEmpty() || (pHelpWin->GetWinStyle() != nHelpWinStyle))
502                             && aHelpData.mbRequestingHelp;
503 
504         if (!bRemoveHelp && pHelpWin->GetParent() == pParent)
505         {
506             bool const bUpdate = (pHelpWin->GetHelpText() != rHelpText) ||
507                 ((pHelpWin->GetHelpArea() != rHelpArea) && aHelpData.mbRequestingHelp);
508             if (bUpdate)
509             {
510                 pHelpWin->SetHelpText( rHelpText );
511                 // approach mouse position
512                 ImplSetHelpWindowPos( pHelpWin, nHelpWinStyle, nStyle, rScreenPos, rHelpArea );
513                 if( pHelpWin->IsVisible() )
514                     pHelpWin->Invalidate();
515             }
516         }
517         else
518         {
519             // remove help window if no HelpText or
520             // other help mode. but keep it if we are scrolling, ie not requesting help
521             bool bWasVisible = pHelpWin->IsVisible();
522             if ( bWasVisible )
523                 bNoDelay = true; // display it quickly if we were already in quick help mode
524             pHelpWin = nullptr;
525             ImplDestroyHelpWindow( bWasVisible );
526         }
527     }
528 
529     if (pHelpWin || rHelpText.isEmpty())
530         return;
531 
532     sal_uInt64 nCurTime = tools::Time::GetSystemTicks();
533     if ( ( nCurTime - aHelpData.mnLastHelpHideTime ) < o3tl::make_unsigned(HelpSettings::GetTipDelay()) )
534         bNoDelay = true;
535 
536     pHelpWin = VclPtr<HelpTextWindow>::Create( pParent, rHelpText, nHelpWinStyle, nStyle );
537     aHelpData.mpHelpWin = pHelpWin;
538     pHelpWin->SetHelpArea( rHelpArea );
539 
540     //  positioning
541     Size aSz = pHelpWin->CalcOutSize();
542     pHelpWin->SetOutputSizePixel( aSz );
543     ImplSetHelpWindowPos( pHelpWin, nHelpWinStyle, nStyle, rScreenPos, rHelpArea );
544     // if not called from Window::RequestHelp, then without delay...
545     if ( !aHelpData.mbRequestingHelp )
546         bNoDelay = true;
547     pHelpWin->ShowHelp(bNoDelay);
548 
549 }
550 
ImplDestroyHelpWindow(bool bUpdateHideTime)551 void ImplDestroyHelpWindow( bool bUpdateHideTime )
552 {
553     ImplDestroyHelpWindow(ImplGetSVHelpData(), bUpdateHideTime);
554 }
555 
ImplDestroyHelpWindow(ImplSVHelpData & rHelpData,bool bUpdateHideTime)556 void ImplDestroyHelpWindow(ImplSVHelpData& rHelpData, bool bUpdateHideTime)
557 {
558     VclPtr<HelpTextWindow> pHelpWin = rHelpData.mpHelpWin;
559     if( pHelpWin )
560     {
561         rHelpData.mpHelpWin = nullptr;
562         rHelpData.mbKeyboardHelp = false;
563         pHelpWin->Hide();
564         pHelpWin.disposeAndClear();
565         if( bUpdateHideTime )
566             rHelpData.mnLastHelpHideTime = tools::Time::GetSystemTicks();
567     }
568 }
569 
ImplSetHelpWindowPos(vcl::Window * pHelpWin,sal_uInt16 nHelpWinStyle,QuickHelpFlags nStyle,const Point & rPos,const tools::Rectangle & rHelpArea)570 void ImplSetHelpWindowPos( vcl::Window* pHelpWin, sal_uInt16 nHelpWinStyle, QuickHelpFlags nStyle,
571                            const Point& rPos, const tools::Rectangle& rHelpArea )
572 {
573     Point       aPos = rPos;
574     Size        aSz = pHelpWin->GetSizePixel();
575     tools::Rectangle   aScreenRect = pHelpWin->ImplGetFrameWindow()->GetDesktopRectPixel();
576     aPos = pHelpWin->GetParent()->ImplGetFrameWindow()->OutputToAbsoluteScreenPixel( aPos );
577     // get mouse screen coords
578     Point aMousePos( pHelpWin->GetParent()->ImplGetFrameWindow()->GetPointerPosPixel() );
579     aMousePos = pHelpWin->GetParent()->ImplGetFrameWindow()->OutputToAbsoluteScreenPixel( aMousePos );
580 
581     if ( nHelpWinStyle == HELPWINSTYLE_QUICK )
582     {
583         if ( !(nStyle & QuickHelpFlags::NoAutoPos) )
584         {
585             tools::Long nScreenHeight = aScreenRect.GetHeight();
586             aPos.AdjustX( -4 );
587             if ( aPos.Y() > aScreenRect.Top()+nScreenHeight-(nScreenHeight/4) )
588                 aPos.AdjustY( -(aSz.Height()+4) );
589             else
590                 aPos.AdjustY(21 );
591         }
592     }
593     else
594     {
595         // If it's the mouse position, move the window slightly
596         // so the mouse pointer does not cover it
597         if ( aPos == aMousePos )
598         {
599             aPos.AdjustX(12 );
600             aPos.AdjustY(16 );
601         }
602     }
603 
604     if ( nStyle & QuickHelpFlags::NoAutoPos )
605     {
606         // convert help area to screen coords
607         tools::Rectangle devHelpArea(
608             pHelpWin->GetParent()->ImplGetFrameWindow()->OutputToAbsoluteScreenPixel( rHelpArea.TopLeft() ),
609             pHelpWin->GetParent()->ImplGetFrameWindow()->OutputToAbsoluteScreenPixel( rHelpArea.BottomRight() ) );
610 
611         // which position of the rectangle?
612         aPos = devHelpArea.Center();
613 
614         if ( nStyle & QuickHelpFlags::Left )
615             aPos.setX( devHelpArea.Left() );
616         else if ( nStyle & QuickHelpFlags::Right )
617             aPos.setX( devHelpArea.Right() );
618 
619         if ( nStyle & QuickHelpFlags::Top )
620             aPos.setY( devHelpArea.Top() );
621         else if ( nStyle & QuickHelpFlags::Bottom )
622             aPos.setY( devHelpArea.Bottom() );
623 
624         // which direction?
625         if ( nStyle & QuickHelpFlags::Left )
626             ;
627         else if ( nStyle & QuickHelpFlags::Right )
628             aPos.AdjustX( -(aSz.Width()) );
629         else
630             aPos.AdjustX( -(aSz.Width()/2) );
631 
632         if ( nStyle & QuickHelpFlags::Top )
633             ;
634         else if ( nStyle & QuickHelpFlags::Bottom )
635             aPos.AdjustY( -(aSz.Height()) );
636         else
637             aPos.AdjustY( -(aSz.Height()/2) );
638     }
639 
640     if ( aPos.X() < aScreenRect.Left() )
641         aPos.setX( aScreenRect.Left() );
642     else if ( ( aPos.X() + aSz.Width() ) > aScreenRect.Right() )
643         aPos.setX( aScreenRect.Right() - aSz.Width() );
644     if ( aPos.Y() < aScreenRect.Top() )
645         aPos.setY( aScreenRect.Top() );
646     else if ( ( aPos.Y() + aSz.Height() ) > aScreenRect.Bottom() )
647         aPos.setY( aScreenRect.Bottom() - aSz.Height() );
648 
649     if( ! (nStyle & QuickHelpFlags::NoEvadePointer) )
650     {
651         /* the remark below should be obsolete by now as the helpwindow should
652         not be focusable, leaving it as a hint. However it is sensible in most
653         conditions to evade the mouse pointer so the content window is fully visible.
654 
655         // the popup must not appear under the mouse
656         // otherwise it would directly be closed due to a focus change...
657         */
658         tools::Rectangle aHelpRect( aPos, aSz );
659         if( aHelpRect.IsInside( aMousePos ) )
660         {
661             Point delta(2,2);
662             Point aSize( aSz.Width(), aSz.Height() );
663             Point aTest( aMousePos - aSize - delta );
664             if( aTest.X() > aScreenRect.Left() && aTest.Y() > aScreenRect.Top() )
665                 aPos = aTest;
666             else
667                 aPos = aMousePos + delta;
668         }
669     }
670 
671     vcl::Window* pWindow = pHelpWin->GetParent()->ImplGetFrameWindow();
672     aPos = pWindow->AbsoluteScreenToOutputPixel( aPos );
673     pHelpWin->SetPosPixel( aPos );
674 }
675 
676 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
677