Coverage Report

Created: 2025-12-31 10:39

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/vcl/source/window/floatwin.cxx
Line
Count
Source
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 <svdata.hxx>
21
#include <brdwin.hxx>
22
#include <window.h>
23
#include <salframe.hxx>
24
#include <helpwin.hxx>
25
26
#include <comphelper/lok.hxx>
27
#include <sal/log.hxx>
28
#include <vcl/layout.hxx>
29
#include <vcl/svapp.hxx>
30
#include <vcl/wrkwin.hxx>
31
#include <vcl/event.hxx>
32
#include <vcl/toolbox.hxx>
33
#include <vcl/toolkit/floatwin.hxx>
34
#include <vcl/settings.hxx>
35
#include <vcl/IDialogRenderable.hxx>
36
37
class FloatingWindow::ImplData
38
{
39
public:
40
    ImplData();
41
42
    VclPtr<ToolBox> mpBox;
43
    AbsoluteScreenPixelRectangle maItemEdgeClipRect; // used to clip the common edge between a toolbar item and the border of this window
44
    Point maPos; // position of the floating window wrt. parent
45
    Point maLOKTwipsPos; ///< absolute position of the floating window in the document - in twips (for toplevel floating windows).
46
};
47
48
FloatingWindow::ImplData::ImplData()
49
0
{
50
0
    mpBox = nullptr;
51
0
}
52
53
const AbsoluteScreenPixelRectangle & FloatingWindow::ImplGetItemEdgeClipRect()
54
0
{
55
0
    return mpImplData->maItemEdgeClipRect;
56
0
}
57
58
void FloatingWindow::ImplInitFloating( vcl::Window* pParent, WinBits nStyle )
59
0
{
60
0
    mpImplData.reset(new ImplData);
61
62
0
    mpWindowImpl->mbFloatWin = true;
63
0
    mbInCleanUp = false;
64
0
    mbGrabFocus = false;
65
66
0
    SAL_WARN_IF(!pParent, "vcl", "FloatWindow::FloatingWindow(): - pParent == NULL!");
67
68
0
    if (!pParent)
69
0
        pParent = ImplGetSVData()->maFrameData.mpAppWin;
70
71
0
    SAL_WARN_IF(!pParent, "vcl", "FloatWindow::FloatingWindow(): - pParent == NULL and no AppWindow exists");
72
73
    // no Border, then we don't need a border window
74
0
    if (!nStyle)
75
0
    {
76
0
        mpWindowImpl->mbOverlapWin = true;
77
0
        nStyle |= WB_DIALOGCONTROL;
78
0
        ImplInit(pParent, nStyle, nullptr);
79
0
    }
80
0
    else
81
0
    {
82
0
        if (!(nStyle & WB_NODIALOGCONTROL))
83
0
            nStyle |= WB_DIALOGCONTROL;
84
85
0
        if (nStyle & (WB_MOVEABLE | WB_SIZEABLE | WB_CLOSEABLE | WB_STANDALONE)
86
0
            && !(nStyle & WB_OWNERDRAWDECORATION))
87
0
        {
88
0
            WinBits nFloatWinStyle = nStyle;
89
            // #99154# floaters are not closeable by default anymore, eg fullscreen floater
90
            // nFloatWinStyle |= WB_CLOSEABLE;
91
0
            mpWindowImpl->mbFrame = true;
92
0
            mpWindowImpl->mbOverlapWin = true;
93
0
            ImplInit(pParent, nFloatWinStyle & ~WB_BORDER, nullptr);
94
0
        }
95
0
        else
96
0
        {
97
0
            VclPtr<ImplBorderWindow> pBorderWin;
98
0
            BorderWindowStyle nBorderStyle = BorderWindowStyle::Float;
99
100
0
            if (nStyle & WB_OWNERDRAWDECORATION)
101
0
                nBorderStyle |= BorderWindowStyle::Frame;
102
0
            else
103
0
                nBorderStyle |= BorderWindowStyle::Overlap;
104
105
0
            if ((nStyle & WB_SYSTEMWINDOW) && !(nStyle & (WB_MOVEABLE | WB_SIZEABLE)))
106
0
            {
107
0
                nBorderStyle |= BorderWindowStyle::Frame;
108
0
                nStyle |= WB_CLOSEABLE; // make undecorated floaters closeable
109
0
            }
110
0
            pBorderWin  = VclPtr<ImplBorderWindow>::Create(pParent, nStyle, nBorderStyle);
111
0
            ImplInit(pBorderWin, nStyle & ~WB_BORDER, nullptr);
112
0
            pBorderWin->mpWindowImpl->mpClientWindow = this;
113
0
            pBorderWin->GetBorder(mpWindowImpl->mnLeftBorder, mpWindowImpl->mnTopBorder,
114
0
                                  mpWindowImpl->mnRightBorder, mpWindowImpl->mnBottomBorder);
115
0
            pBorderWin->SetDisplayActive(true);
116
0
            mpWindowImpl->mpBorderWindow = pBorderWin;
117
0
            mpWindowImpl->mpRealParent = pParent;
118
0
        }
119
0
    }
120
0
    SetActivateMode( ActivateModeFlags::NONE );
121
122
0
    mpNextFloat             = nullptr;
123
0
    mpFirstPopupModeWin     = nullptr;
124
0
    mnPostId                = nullptr;
125
0
    mnTitle                 = (nStyle & (WB_MOVEABLE | WB_POPUP)) ? FloatWinTitleType::Normal : FloatWinTitleType::NONE;
126
0
    mnOldTitle              = mnTitle;
127
0
    mnPopupModeFlags        = FloatWinPopupFlags::NONE;
128
0
    mbInPopupMode           = false;
129
0
    mbPopupMode             = false;
130
0
    mbPopupModeCanceled     = false;
131
0
    mbPopupModeTearOff      = false;
132
0
    mbMouseDown             = false;
133
134
0
    ImplInitSettings();
135
0
}
136
137
void FloatingWindow::ImplInitSettings()
138
0
{
139
0
    const StyleSettings& rStyleSettings = GetSettings().GetStyleSettings();
140
141
0
    Color aColor;
142
0
    if (IsControlBackground())
143
0
        aColor = GetControlBackground();
144
0
    else if (Window::GetStyle() & WB_3DLOOK)
145
0
        aColor = rStyleSettings.GetFaceColor();
146
0
    else
147
0
        aColor = rStyleSettings.GetWindowColor();
148
0
    SetBackground(aColor);
149
0
}
150
151
FloatingWindow::FloatingWindow(vcl::Window* pParent, WinBits nStyle) :
152
0
    SystemWindow(WindowType::FLOATINGWINDOW, "vcl::FloatingWindow maLayoutIdle")
153
0
{
154
0
    ImplInitFloating(pParent, nStyle);
155
0
}
Unexecuted instantiation: FloatingWindow::FloatingWindow(vcl::Window*, long)
Unexecuted instantiation: FloatingWindow::FloatingWindow(vcl::Window*, long)
156
157
FloatingWindow::FloatingWindow(vcl::Window* pParent, const OUString& rID, const OUString& rUIXMLDescription, const css::uno::Reference<css::frame::XFrame> &rFrame)
158
0
    : SystemWindow(WindowType::FLOATINGWINDOW, "vcl::FloatingWindow maLayoutIdle")
159
0
    , mpNextFloat(nullptr)
160
0
    , mpFirstPopupModeWin(nullptr)
161
0
    , mnPostId(nullptr)
162
0
    , mnPopupModeFlags(FloatWinPopupFlags::NONE)
163
0
    , mnTitle(FloatWinTitleType::Unknown)
164
0
    , mnOldTitle(FloatWinTitleType::Unknown)
165
0
    , mbInPopupMode(false)
166
0
    , mbPopupMode(false)
167
0
    , mbPopupModeCanceled(false)
168
0
    , mbPopupModeTearOff(false)
169
0
    , mbMouseDown(false)
170
0
    , mbGrabFocus(false)
171
0
    , mbInCleanUp(false)
172
0
{
173
0
    loadUI(pParent, rID, rUIXMLDescription, rFrame);
174
0
}
Unexecuted instantiation: FloatingWindow::FloatingWindow(vcl::Window*, rtl::OUString const&, rtl::OUString const&, com::sun::star::uno::Reference<com::sun::star::frame::XFrame> const&)
Unexecuted instantiation: FloatingWindow::FloatingWindow(vcl::Window*, rtl::OUString const&, rtl::OUString const&, com::sun::star::uno::Reference<com::sun::star::frame::XFrame> const&)
175
176
void FloatingWindow::ImplDeferredInit(vcl::Window* pParent, WinBits nBits)
177
0
{
178
0
    ImplInitFloating(pParent, nBits);
179
0
}
180
181
void FloatingWindow::ApplySettings(vcl::RenderContext& rRenderContext)
182
0
{
183
0
    const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings();
184
185
0
    Color aColor;
186
0
    if (Window::GetStyle() & WB_3DLOOK)
187
0
        aColor = rStyleSettings.GetFaceColor();
188
0
    else
189
0
        aColor = rStyleSettings.GetWindowColor();
190
191
0
    ApplyControlBackground(rRenderContext, aColor);
192
0
}
193
194
FloatingWindow::~FloatingWindow()
195
0
{
196
0
    disposeOnce();
197
0
    assert (!mnPostId);
198
0
}
199
200
void FloatingWindow::dispose()
201
0
{
202
0
    ReleaseLOKNotifier();
203
204
0
    if (mpImplData)
205
0
    {
206
0
        if( mbPopupModeCanceled )
207
            // indicates that ESC key was pressed
208
            // will be handled in Window::ImplGrabFocus()
209
0
            SetDialogControlFlags( GetDialogControlFlags() | DialogControlFlags::FloatWinPopupModeEndCancel );
210
211
0
        if ( IsInPopupMode() )
212
0
            EndPopupMode( FloatWinPopupEndFlags::Cancel | FloatWinPopupEndFlags::CloseAll | FloatWinPopupEndFlags::DontCallHdl );
213
214
0
        if ( mnPostId )
215
0
            Application::RemoveUserEvent( mnPostId );
216
0
        mnPostId = nullptr;
217
0
    }
218
219
0
    mpImplData.reset();
220
221
0
    mpNextFloat.reset();
222
0
    mpFirstPopupModeWin.reset();
223
0
    mxPrevFocusWin.reset();
224
0
    SystemWindow::dispose();
225
0
}
226
227
Point FloatingWindow::ImplCalcPos(vcl::Window* pWindow,
228
                                  const tools::Rectangle& rRect, FloatWinPopupFlags nFlags,
229
                                  sal_uInt16& rArrangeIndex, Point* pLOKTwipsPos)
230
0
{
231
    // get window position
232
0
    AbsoluteScreenPixelPoint aPos;
233
0
    Size        aSize = ::isLayoutEnabled(pWindow) ? pWindow->get_preferred_size() : pWindow->GetSizePixel();
234
0
    AbsoluteScreenPixelRectangle aScreenRect = pWindow->ImplGetFrameWindow()->GetDesktopRectPixel();
235
0
    FloatingWindow *pFloatingWindow = dynamic_cast<FloatingWindow*>( pWindow );
236
237
    // convert...
238
0
    vcl::Window* pW = pWindow;
239
0
    if ( pW->mpWindowImpl->mpRealParent )
240
0
        pW = pW->mpWindowImpl->mpRealParent;
241
242
0
    tools::Rectangle normRect( rRect );  // rRect is already relative to top-level window
243
0
    normRect.SetPos( pW->ScreenToOutputPixel( normRect.TopLeft() ) );
244
245
0
    bool bRTL = AllSettings::GetLayoutRTL();
246
247
0
    AbsoluteScreenPixelRectangle devRect(  pW->OutputToAbsoluteScreenPixel( normRect.TopLeft() ),
248
0
                        pW->OutputToAbsoluteScreenPixel( normRect.BottomRight() ) );
249
250
0
    AbsoluteScreenPixelRectangle devRectRTL( devRect );
251
0
    if( bRTL )
252
        // create a rect that can be compared to desktop coordinates
253
0
        devRectRTL = pW->ImplOutputToUnmirroredAbsoluteScreenPixel( normRect );
254
0
    if( Application::GetScreenCount() > 1 )
255
0
        aScreenRect = Application::GetScreenPosSizePixel(
256
0
            Application::GetBestScreen( bRTL ? devRectRTL : devRect ) );
257
258
0
    FloatWinPopupFlags nArrangeAry[5];
259
0
    sal_uInt16 nArrangeAttempts = 5;
260
0
    AbsoluteScreenPixelPoint e1,e2;  // the common edge between the item rect and the floating window
261
262
0
    if ( nFlags & FloatWinPopupFlags::Left )
263
0
    {
264
0
        nArrangeAry[0]  = FloatWinPopupFlags::Left;
265
0
        nArrangeAry[1]  = FloatWinPopupFlags::Right;
266
0
        nArrangeAry[2]  = FloatWinPopupFlags::Up;
267
0
        nArrangeAry[3]  = FloatWinPopupFlags::Down;
268
0
        nArrangeAry[4]  = FloatWinPopupFlags::Left;
269
0
    }
270
0
    else if ( nFlags & FloatWinPopupFlags::Right )
271
0
    {
272
0
        nArrangeAry[0]  = FloatWinPopupFlags::Right;
273
0
        nArrangeAry[1]  = FloatWinPopupFlags::Left;
274
0
        nArrangeAry[2]  = FloatWinPopupFlags::Up;
275
0
        nArrangeAry[3]  = FloatWinPopupFlags::Down;
276
0
        nArrangeAry[4]  = FloatWinPopupFlags::Right;
277
0
    }
278
0
    else if ( nFlags & FloatWinPopupFlags::Up )
279
0
    {
280
0
        nArrangeAry[0]  = FloatWinPopupFlags::Up;
281
0
        nArrangeAry[1]  = FloatWinPopupFlags::Down;
282
0
        if (nFlags & FloatWinPopupFlags::NoHorzPlacement)
283
0
        {
284
0
            nArrangeAry[2]  = FloatWinPopupFlags::Up;
285
0
            nArrangeAttempts = 3;
286
0
        }
287
0
        else
288
0
        {
289
0
            nArrangeAry[2]  = FloatWinPopupFlags::Right;
290
0
            nArrangeAry[3]  = FloatWinPopupFlags::Left;
291
0
            nArrangeAry[4]  = FloatWinPopupFlags::Up;
292
0
        }
293
0
    }
294
0
    else
295
0
    {
296
0
        nArrangeAry[0]  = FloatWinPopupFlags::Down;
297
0
        nArrangeAry[1]  = FloatWinPopupFlags::Up;
298
0
        if (nFlags & FloatWinPopupFlags::NoHorzPlacement)
299
0
        {
300
0
            nArrangeAry[2]  = FloatWinPopupFlags::Down;
301
0
            nArrangeAttempts = 3;
302
0
        }
303
0
        else
304
0
        {
305
0
            nArrangeAry[2]  = FloatWinPopupFlags::Right;
306
0
            nArrangeAry[3]  = FloatWinPopupFlags::Left;
307
0
            nArrangeAry[4]  = FloatWinPopupFlags::Down;
308
0
        }
309
0
    }
310
311
0
    sal_uInt16 nArrangeIndex = 0;
312
0
    const bool bLOKActive = comphelper::LibreOfficeKit::isActive();
313
314
0
    for ( ; nArrangeIndex < nArrangeAttempts; nArrangeIndex++ )
315
0
    {
316
0
        bool bBreak = true;
317
0
        switch ( nArrangeAry[nArrangeIndex] )
318
0
        {
319
320
0
            case FloatWinPopupFlags::Left:
321
0
                aPos.setX( devRect.Left()-aSize.Width()+1 );
322
0
                aPos.setY( devRect.Top() );
323
0
                aPos.AdjustY( -(pWindow->mpWindowImpl->mnTopBorder) );
324
0
                if( bRTL )
325
0
                {
326
0
                    if( (devRectRTL.Right()+aSize.Width()) > aScreenRect.Right() )
327
0
                        bBreak = false;
328
0
                }
329
0
                else
330
0
                {
331
0
                    if ( aPos.X() < aScreenRect.Left() )
332
0
                        bBreak = false;
333
0
                }
334
0
                if (bBreak || bLOKActive)
335
0
                {
336
0
                    e1 = devRect.TopLeft();
337
0
                    e2 = devRect.BottomLeft();
338
                    // set non-zero width
339
0
                    e2.AdjustX( 1 );
340
                    // don't clip corners
341
0
                    e1.AdjustY( 1 );
342
0
                    e2.AdjustY( -1 );
343
0
                }
344
0
                break;
345
0
            case FloatWinPopupFlags::Right:
346
0
                aPos     = devRect.TopRight();
347
0
                aPos.AdjustY( -(pWindow->mpWindowImpl->mnTopBorder) );
348
0
                if( bRTL )
349
0
                {
350
0
                    if( (devRectRTL.Left() - aSize.Width()) < aScreenRect.Left() )
351
0
                        bBreak = false;
352
0
                }
353
0
                else
354
0
                {
355
0
                    if ( aPos.X()+aSize.Width() > aScreenRect.Right() )
356
0
                        bBreak = false;
357
0
                }
358
0
                if (bBreak || bLOKActive)
359
0
                {
360
0
                    e1 = devRect.TopRight();
361
0
                    e2 = devRect.BottomRight();
362
                    // set non-zero width
363
0
                    e2.AdjustX( 1 );
364
                    // don't clip corners
365
0
                    e1.AdjustY( 1 );
366
0
                    e2.AdjustY( -1 );
367
0
                }
368
0
                break;
369
0
            case FloatWinPopupFlags::Up:
370
0
                aPos.setX( devRect.Left() );
371
0
                aPos.setY( devRect.Top()-aSize.Height()+1 );
372
0
                if ( aPos.Y() < aScreenRect.Top() )
373
0
                    bBreak = false;
374
0
                if (bBreak || bLOKActive)
375
0
                {
376
0
                    e1 = devRect.TopLeft();
377
0
                    e2 = devRect.TopRight();
378
                    // set non-zero height
379
0
                    e2.AdjustY( 1 );
380
                    // don't clip corners
381
0
                    e1.AdjustX( 1 );
382
0
                    e2.AdjustX( -1 );
383
0
                }
384
0
                break;
385
0
            case FloatWinPopupFlags::Down:
386
0
                aPos = devRect.BottomLeft();
387
0
                if ( aPos.Y()+aSize.Height() > aScreenRect.Bottom() )
388
0
                    bBreak = false;
389
0
                if (bBreak || bLOKActive)
390
0
                {
391
0
                    e1 = devRect.BottomLeft();
392
0
                    e2 = devRect.BottomRight();
393
                    // set non-zero height
394
0
                    e2.AdjustY( 1 );
395
                    // don't clip corners
396
0
                    e1.AdjustX( 1 );
397
0
                    e2.AdjustX( -1 );
398
0
                }
399
0
                break;
400
0
            default: break;
401
0
        }
402
403
        // no further adjustment for LibreOfficeKit
404
0
        if (bLOKActive)
405
0
            break;
406
407
        // set bBreak true on last attempt
408
0
        if (nArrangeIndex + 1 == nArrangeAttempts)
409
0
            bBreak = true;
410
411
        // adjust if necessary
412
0
        if (bBreak)
413
0
        {
414
0
            if (aPos.Y() + aSize.Height() > aScreenRect.Bottom())
415
0
            {
416
0
                aPos.setY(devRect.Bottom() - aSize.Height() + 1);
417
0
                if (aPos.Y() < aScreenRect.Top())
418
0
                    aPos.setY(aScreenRect.Top());
419
                // move to the right or left of the parent if possible
420
0
                if (devRect.Right() + 4 + aSize.Width() < aScreenRect.Right())
421
0
                    aPos.setX(devRect.Right() + 4);
422
0
                else if (devRect.Left() - 4 - aSize.Width() > aScreenRect.Left())
423
0
                    aPos.setX(devRect.Left() - 4 - aSize.Width());
424
0
            }
425
0
            else
426
0
            {
427
0
                if( bRTL )
428
0
                {
429
0
                    if( devRectRTL.Right()-aSize.Width()+1 < aScreenRect.Left() )
430
0
                        aPos.AdjustX( -(aScreenRect.Left() - devRectRTL.Right() + aSize.Width() - 1) );
431
0
                }
432
0
                else if ( aPos.X()+aSize.Width() > aScreenRect.Right() )
433
0
                {
434
0
                    aPos.setX( devRect.Right()-aSize.Width()+1 );
435
0
                    if ( aPos.X() < aScreenRect.Left() )
436
0
                        aPos.setX( aScreenRect.Left() );
437
0
                }
438
0
            }
439
0
        }
440
441
0
        if ( bBreak )
442
0
            break;
443
0
    }
444
445
0
    rArrangeIndex = nArrangeIndex;
446
447
0
    Point aPosOut = pW->AbsoluteScreenToOutputPixel( aPos );
448
449
    // store a cliprect that can be used to clip the common edge of the itemrect and the floating window
450
0
    if( pFloatingWindow && pFloatingWindow->mpImplData->mpBox )
451
0
    {
452
0
        pFloatingWindow->mpImplData->maItemEdgeClipRect =
453
0
            AbsoluteScreenPixelRectangle( e1, e2 );
454
0
    }
455
456
0
    if (bLOKActive && pLOKTwipsPos)
457
0
    {
458
0
        if (pW->IsMapModeEnabled() || pW->GetMapMode().GetMapUnit() == MapUnit::MapPixel)
459
0
        {
460
            // if we use pW->LogicToLogic(aPos, pW->GetMapMode(), MapMode(MapUnit::MapTwip)),
461
            // for pixel conversions when map mode is not enabled, we get
462
            // a 20 twips per pixel conversion since LogicToLogic uses
463
            // a fixed 72 dpi value, instead of a correctly computed output
464
            // device dpi or at least the most commonly used 96 dpi value;
465
            // and anyway the following is what we already do in
466
            // ScGridWindow::LogicInvalidate when map mode is not enabled.
467
468
0
            *pLOKTwipsPos = pW->PixelToLogic(aPosOut, MapMode(MapUnit::MapTwip));
469
0
        }
470
0
        else
471
0
        {
472
0
            *pLOKTwipsPos = OutputDevice::LogicToLogic(aPosOut, pW->GetMapMode(), MapMode(MapUnit::MapTwip));
473
0
        }
474
0
    }
475
476
    // caller expects coordinates relative to top-level win
477
0
    return pW->OutputToScreenPixel( aPosOut );
478
0
}
479
480
AbsoluteScreenPixelPoint FloatingWindow::ImplConvertToAbsPos(vcl::Window* pReference, const Point& rPos)
481
0
{
482
0
    const OutputDevice *pWindowOutDev = pReference->GetOutDev();
483
484
    // compare coordinates in absolute screen coordinates
485
0
    if ( pWindowOutDev->HasMirroredGraphics() && !comphelper::LibreOfficeKit::isActive() )
486
0
    {
487
0
        Point aTmp(rPos);
488
0
        if(!pReference->IsRTLEnabled() )
489
0
            pWindowOutDev->ReMirror( aTmp );
490
491
0
        tools::Rectangle aRect( pReference->ScreenToOutputPixel(aTmp), Size(1,1) ) ;
492
0
        aRect = tools::Rectangle(pReference->ImplOutputToUnmirroredAbsoluteScreenPixel( aRect ));
493
0
        return AbsoluteScreenPixelPoint(aRect.TopLeft());
494
0
    }
495
0
    else
496
0
        return pReference->OutputToAbsoluteScreenPixel(
497
0
            pReference->ScreenToOutputPixel(rPos) );
498
0
}
499
500
AbsoluteScreenPixelRectangle FloatingWindow::ImplConvertToAbsPos(vcl::Window* pReference, const tools::Rectangle& rRect)
501
0
{
502
0
    AbsoluteScreenPixelRectangle aFloatRect;
503
504
0
    const OutputDevice *pParentWinOutDev = pReference->GetOutDev();
505
506
    // compare coordinates in absolute screen coordinates
507
    // Keep in sync with FloatingWindow::ImplFloatHitTest, e.g. fdo#33509
508
0
    if( pParentWinOutDev->HasMirroredGraphics() && !comphelper::LibreOfficeKit::isActive() )
509
0
    {
510
0
        tools::Rectangle aScreenRect(rRect);
511
0
        if(!pReference->IsRTLEnabled() )
512
0
            pParentWinOutDev->ReMirror(aScreenRect);
513
514
0
        tools::Rectangle aOutRect(pReference->ScreenToOutputPixel(aScreenRect.TopLeft()), aScreenRect.GetSize());
515
0
        aFloatRect = pReference->ImplOutputToUnmirroredAbsoluteScreenPixel(aOutRect);
516
0
    }
517
0
    else
518
0
        aFloatRect = AbsoluteScreenPixelRectangle(
519
0
            pReference->OutputToAbsoluteScreenPixel(pReference->ScreenToOutputPixel(rRect.TopLeft())),
520
0
            rRect.GetSize());
521
522
0
    return aFloatRect;
523
0
}
524
525
tools::Rectangle FloatingWindow::ImplConvertToRelPos(vcl::Window* pReference, const AbsoluteScreenPixelRectangle& rRect)
526
0
{
527
0
    tools::Rectangle aFloatRect;
528
529
0
    const OutputDevice *pParentWinOutDev = pReference->GetOutDev();
530
531
    // compare coordinates in absolute screen coordinates
532
    // Keep in sync with FloatingWindow::ImplFloatHitTest, e.g. fdo#33509
533
0
    if( pParentWinOutDev->HasMirroredGraphics()  )
534
0
    {
535
0
        aFloatRect = pReference->ImplUnmirroredAbsoluteScreenToOutputPixel(rRect);
536
0
        aFloatRect.SetPos(pReference->OutputToScreenPixel(aFloatRect.TopLeft()));
537
538
0
        if(!pReference->IsRTLEnabled() )
539
0
            pParentWinOutDev->ReMirror(aFloatRect);
540
0
    }
541
0
    else
542
0
        aFloatRect = tools::Rectangle(pReference->OutputToScreenPixel(pReference->AbsoluteScreenToOutputPixel(rRect.TopLeft())),
543
0
                                      rRect.GetSize());
544
545
0
    return aFloatRect;
546
0
}
547
548
FloatingWindow* FloatingWindow::ImplFloatHitTest( vcl::Window* pReference, const Point& rPos, bool& rbHitTestInsideRect )
549
0
{
550
0
    FloatingWindow* pWin = this;
551
0
    rbHitTestInsideRect = false;
552
553
0
    AbsoluteScreenPixelPoint aAbsolute(FloatingWindow::ImplConvertToAbsPos(pReference, rPos));
554
555
0
    do
556
0
    {
557
        // compute the floating window's size in absolute screen coordinates
558
559
        // use the border window to have the exact position
560
0
        vcl::Window *pBorderWin = pWin->GetWindow( GetWindowType::Border );
561
0
        if (!pBorderWin)
562
0
            break;
563
564
        // the top-left corner in output coordinates ie (0,0)
565
0
        AbsoluteScreenPixelRectangle devRect( pBorderWin->ImplOutputToUnmirroredAbsoluteScreenPixel( tools::Rectangle( Point(), pBorderWin->GetSizePixel()) ) ) ;
566
0
        if ( devRect.Contains( aAbsolute ) )
567
0
        {
568
            // inside the window
569
0
            return pWin;
570
0
        }
571
572
        // test, if mouse is in rectangle, (this is typically the rect of the active
573
        // toolbox item or similar)
574
        // note: maFloatRect is set in FloatingWindow::StartPopupMode() and
575
        //       is already in absolute device coordinates
576
0
        if ( pWin->maFloatRect.Contains( aAbsolute ) )
577
0
        {
578
0
            rbHitTestInsideRect = true;
579
0
            return pWin;
580
0
        }
581
582
0
        pWin = pWin->mpNextFloat;
583
0
    }
584
0
    while ( pWin );
585
586
0
    return nullptr;
587
0
}
588
589
FloatingWindow* FloatingWindow::ImplFindLastLevelFloat()
590
0
{
591
0
    FloatingWindow* pWin = this;
592
0
    FloatingWindow* pLastFoundWin = pWin;
593
594
0
    do
595
0
    {
596
0
        if ( pWin->GetPopupModeFlags() & FloatWinPopupFlags::NewLevel )
597
0
            pLastFoundWin = pWin;
598
599
0
        pWin = pWin->mpNextFloat;
600
0
    }
601
0
    while ( pWin );
602
603
0
    return pLastFoundWin;
604
0
}
605
606
bool FloatingWindow::ImplIsFloatPopupModeWindow( const vcl::Window* pWindow )
607
0
{
608
0
    FloatingWindow* pWin = this;
609
610
0
    do
611
0
    {
612
0
        if ( pWin->mpFirstPopupModeWin == pWindow )
613
0
            return true;
614
615
0
        pWin = pWin->mpNextFloat;
616
0
    }
617
0
    while ( pWin );
618
619
0
    return false;
620
0
}
621
622
IMPL_LINK_NOARG(FloatingWindow, ImplEndPopupModeHdl, void*, void)
623
0
{
624
0
    VclPtr<FloatingWindow> pThis(this);
625
0
    mnPostId            = nullptr;
626
0
    mnPopupModeFlags    = FloatWinPopupFlags::NONE;
627
0
    mbPopupMode         = false;
628
0
    PopupModeEnd();
629
0
}
630
631
bool FloatingWindow::EventNotify( NotifyEvent& rNEvt )
632
0
{
633
    // call Base Class first for tab control
634
0
    bool bRet = SystemWindow::EventNotify( rNEvt );
635
0
    if ( !bRet )
636
0
    {
637
0
        if ( rNEvt.GetType() == NotifyEventType::KEYINPUT )
638
0
        {
639
0
            const KeyEvent* pKEvt = rNEvt.GetKeyEvent();
640
0
            vcl::KeyCode    aKeyCode = pKEvt->GetKeyCode();
641
0
            sal_uInt16      nKeyCode = aKeyCode.GetCode();
642
643
0
            if ( (nKeyCode == KEY_ESCAPE) && (GetStyle() & WB_CLOSEABLE) )
644
0
            {
645
0
                Close();
646
0
                return true;
647
0
            }
648
0
        }
649
0
    }
650
651
0
    return bRet;
652
0
}
653
654
void FloatingWindow::PixelInvalidate(const tools::Rectangle* /*pRectangle*/)
655
0
{
656
0
    if (VclPtr<vcl::Window> pParent = GetParentWithLOKNotifier())
657
0
    {
658
0
        const tools::Rectangle aRect(Point(0,0), Size(GetSizePixel().Width()+1, GetSizePixel().Height()+1));
659
0
        std::vector<vcl::LOKPayloadItem> aPayload
660
0
        {
661
0
            std::make_pair("rectangle"_ostr, aRect.toString())
662
0
        };
663
0
        const vcl::ILibreOfficeKitNotifier* pNotifier = pParent->GetLOKNotifier();
664
0
        pNotifier->notifyWindow(GetLOKWindowId(), u"invalidate"_ustr, aPayload);
665
0
    }
666
0
}
667
668
void FloatingWindow::StateChanged( StateChangedType nType )
669
0
{
670
0
    if (nType == StateChangedType::InitShow)
671
0
    {
672
0
        DoInitialLayout();
673
0
    }
674
675
0
    SystemWindow::StateChanged( nType );
676
677
0
    VclPtr<vcl::Window> pParent = GetParentWithLOKNotifier();
678
0
    if (pParent)
679
0
    {
680
0
        if (nType == StateChangedType::InitShow)
681
0
        {
682
0
            std::vector<vcl::LOKPayloadItem> aItems;
683
0
            if (pParent == this)
684
0
            {
685
                // we are a toplevel window, let's so far pretend to be a
686
                // dialog - but maybe we'll need a separate type for this
687
                // later
688
0
                if (mbInPopupMode)
689
0
                    aItems.emplace_back("type", "dropdown");
690
0
                else
691
0
                    aItems.emplace_back("type", "dialog");
692
0
                aItems.emplace_back("position", mpImplData->maLOKTwipsPos.toString()); // twips
693
0
            }
694
0
            else
695
0
            {
696
0
                SetLOKNotifier(pParent->GetLOKNotifier());
697
0
                if (dynamic_cast<HelpTextWindow*>(this))
698
0
                    aItems.emplace_back("type", "tooltip");
699
0
                else
700
0
                    aItems.emplace_back("type", "child");
701
702
0
                aItems.emplace_back("parentId", OString::number(pParent->GetLOKWindowId()));
703
0
                if (mbInPopupMode)
704
0
                    aItems.emplace_back("position", mpImplData->maPos.toString()); // pixels
705
0
                else // mpImplData->maPos is not set
706
0
                    aItems.emplace_back("position", GetPosPixel().toString());
707
708
0
            }
709
0
            aItems.emplace_back("size", GetSizePixel().toString());
710
0
            GetLOKNotifier()->notifyWindow(GetLOKWindowId(), u"created"_ustr, aItems);
711
0
        }
712
0
        else if (!IsVisible() && nType == StateChangedType::Visible)
713
0
        {
714
0
            if (const vcl::ILibreOfficeKitNotifier* pNotifier = GetLOKNotifier())
715
0
            {
716
0
                pNotifier->notifyWindow(GetLOKWindowId(), u"close"_ustr);
717
0
                ReleaseLOKNotifier();
718
0
            }
719
0
        }
720
0
    }
721
722
0
    if ( nType == StateChangedType::ControlBackground )
723
0
    {
724
0
        ImplInitSettings();
725
0
        Invalidate();
726
0
    }
727
0
}
728
729
void FloatingWindow::DataChanged( const DataChangedEvent& rDCEvt )
730
0
{
731
0
    SystemWindow::DataChanged( rDCEvt );
732
733
0
    if ( (rDCEvt.GetType() == DataChangedEventType::SETTINGS) &&
734
0
         (rDCEvt.GetFlags() & AllSettingsFlags::STYLE) )
735
0
    {
736
0
        ImplInitSettings();
737
0
        Invalidate();
738
0
    }
739
0
}
740
741
void FloatingWindow::ImplCallPopupModeEnd()
742
0
{
743
    // PopupMode is finished
744
0
    mbInPopupMode = false;
745
746
    // call Handler asynchronously.
747
0
    if ( mpImplData && !mnPostId )
748
0
        mnPostId = Application::PostUserEvent(LINK(this, FloatingWindow, ImplEndPopupModeHdl));
749
0
}
750
751
void FloatingWindow::PopupModeEnd()
752
0
{
753
0
    maPopupModeEndHdl.Call( this );
754
0
}
755
756
void FloatingWindow::SetTitleType( FloatWinTitleType nTitle )
757
0
{
758
0
    if ( (mnTitle == nTitle) || !mpWindowImpl->mpBorderWindow )
759
0
        return;
760
761
0
    mnTitle = nTitle;
762
0
    Size aOutSize = GetOutputSizePixel();
763
0
    BorderWindowTitleType nTitleStyle;
764
0
    if ( nTitle == FloatWinTitleType::Normal )
765
0
        nTitleStyle = BorderWindowTitleType::Small;
766
0
    else if ( nTitle == FloatWinTitleType::TearOff )
767
0
        nTitleStyle = BorderWindowTitleType::Tearoff;
768
0
    else if ( nTitle == FloatWinTitleType::Popup )
769
0
        nTitleStyle = BorderWindowTitleType::Popup;
770
0
    else // nTitle == FloatWinTitleType::NONE
771
0
        nTitleStyle = BorderWindowTitleType::NONE;
772
0
    static_cast<ImplBorderWindow*>(mpWindowImpl->mpBorderWindow.get())->SetTitleType( nTitleStyle, aOutSize );
773
0
    static_cast<ImplBorderWindow*>(mpWindowImpl->mpBorderWindow.get())->GetBorder( mpWindowImpl->mnLeftBorder, mpWindowImpl->mnTopBorder, mpWindowImpl->mnRightBorder, mpWindowImpl->mnBottomBorder );
774
0
}
775
776
void FloatingWindow::StartPopupMode( const tools::Rectangle& rRect, FloatWinPopupFlags nFlags )
777
0
{
778
    // remove title
779
0
    mnOldTitle = mnTitle;
780
0
    if ( ( mpWindowImpl->mnStyle & WB_POPUP ) && !GetText().isEmpty() )
781
0
        SetTitleType( FloatWinTitleType::Popup );
782
0
    else if ( nFlags & FloatWinPopupFlags::AllowTearOff )
783
0
        SetTitleType( FloatWinTitleType::TearOff );
784
0
    else
785
0
        SetTitleType( FloatWinTitleType::NONE );
786
787
    // avoid close on focus change for decorated floating windows only
788
0
    if( mpWindowImpl->mbFrame && (GetStyle() & WB_MOVEABLE) )
789
0
        nFlags |= FloatWinPopupFlags::NoAppFocusClose;
790
791
    // compute window position according to flags and arrangement
792
0
    sal_uInt16 nArrangeIndex;
793
0
    DoInitialLayout();
794
0
    mpImplData->maPos = ImplCalcPos(this, rRect, nFlags, nArrangeIndex, &mpImplData->maLOKTwipsPos);
795
0
    SetPosPixel( mpImplData->maPos );
796
0
    ImplGetFrame()->PositionByToolkit(rRect, nFlags);
797
798
    // set data and display window
799
    // convert maFloatRect to absolute device coordinates
800
    // so they can be compared across different frames
801
    // !!! rRect is expected to be in screen coordinates of the parent frame window !!!
802
0
    maFloatRect = FloatingWindow::ImplConvertToAbsPos(GetParent(), rRect);
803
804
0
    maFloatRect.AdjustLeft( -2 );
805
0
    maFloatRect.AdjustTop( -2 );
806
0
    maFloatRect.AdjustRight(2 );
807
0
    maFloatRect.AdjustBottom(2 );
808
0
    mnPopupModeFlags        = nFlags;
809
0
    mbInPopupMode           = true;
810
0
    mbPopupMode             = true;
811
0
    mbPopupModeCanceled     = false;
812
0
    mbPopupModeTearOff      = false;
813
0
    mbMouseDown             = false;
814
815
    // add FloatingWindow to list of windows that are in popup mode
816
0
    ImplSVData* pSVData = ImplGetSVData();
817
0
    mpNextFloat = pSVData->mpWinData->mpFirstFloat;
818
0
    pSVData->mpWinData->mpFirstFloat = this;
819
0
    bool bGrabFocus(nFlags & FloatWinPopupFlags::GrabFocus);
820
0
    if (bGrabFocus)
821
0
    {
822
        // force key input even without focus (useful for menus)
823
0
        mbGrabFocus = true;
824
0
        mxPrevFocusWin = Window::SaveFocus();
825
0
        mpWindowImpl->mpFrameData->mbHasFocus = true;
826
0
    }
827
0
    Show( true, ShowFlags::NoActivate );
828
0
    if (bGrabFocus)
829
0
        GrabFocus();
830
0
}
831
832
void FloatingWindow::StartPopupMode( ToolBox* pBox, FloatWinPopupFlags nFlags )
833
0
{
834
0
    mpImplData->mpBox = pBox;
835
836
    // get selected button
837
0
    ToolBoxItemId nItemId = pBox->GetDownItemId();
838
839
0
    if ( nItemId )
840
0
        pBox->ImplFloatControl( true, this );
841
842
    // retrieve some data from the ToolBox
843
0
    tools::Rectangle aRect = nItemId ? pBox->GetItemRect( nItemId ) : pBox->GetOverflowRect();
844
845
    // convert to parent's screen coordinates
846
0
    mpImplData->maPos = GetParent()->OutputToScreenPixel( GetParent()->AbsoluteScreenToOutputPixel( pBox->OutputToAbsoluteScreenPixel( aRect.TopLeft() ) ) );
847
0
    aRect.SetPos( mpImplData->maPos );
848
849
0
    nFlags |=
850
0
        FloatWinPopupFlags::AllMouseButtonClose |
851
0
        FloatWinPopupFlags::NoMouseUpClose;
852
853
    // set Flags for positioning
854
0
    if ( !(nFlags & (FloatWinPopupFlags::Down | FloatWinPopupFlags::Up |
855
0
                     FloatWinPopupFlags::Left | FloatWinPopupFlags::Right)) )
856
0
    {
857
0
         if ( pBox->IsHorizontal() )
858
0
             nFlags |= FloatWinPopupFlags::Down;
859
0
         else
860
0
             nFlags |= FloatWinPopupFlags::Right;
861
0
    }
862
863
    // start FloatingMode
864
0
    StartPopupMode( aRect, nFlags );
865
0
}
866
867
void FloatingWindow::ImplEndPopupMode( FloatWinPopupEndFlags nFlags, const VclPtr<vcl::Window>& xFocusId )
868
0
{
869
0
    if ( !mbInPopupMode )
870
0
        return;
871
872
0
    ImplSVData* pSVData = ImplGetSVData();
873
874
0
    mbInCleanUp = true; // prevent killing this window due to focus change while working with it
875
876
0
    if (!(nFlags & FloatWinPopupEndFlags::NoCloseChildren))
877
0
    {
878
        // stop the PopupMode also for all PopupMode windows created after us
879
0
        std::vector<VclPtr<FloatingWindow>> aCancelFloats;
880
        // stop the PopupMode also for all following PopupMode windows
881
0
        for (auto pFloat = pSVData->mpWinData->mpFirstFloat;
882
0
             pFloat != nullptr && pFloat != this;
883
0
             pFloat = pFloat->mpNextFloat)
884
0
            aCancelFloats.push_back(pFloat);
885
0
        for (auto & it : aCancelFloats)
886
0
            it->EndPopupMode(FloatWinPopupEndFlags::Cancel | FloatWinPopupEndFlags::NoCloseChildren);
887
0
    }
888
889
    // delete window from the list
890
0
    pSVData->mpWinData->mpFirstFloat = mpNextFloat;
891
0
    mpNextFloat = nullptr;
892
893
0
    FloatWinPopupFlags nPopupModeFlags = mnPopupModeFlags;
894
0
    mbPopupModeTearOff = nFlags & FloatWinPopupEndFlags::TearOff &&
895
0
                         nPopupModeFlags & FloatWinPopupFlags::AllowTearOff;
896
897
    // hide window again if it was not deleted
898
0
    if (!mbPopupModeTearOff)
899
0
        Show( false, ShowFlags::NoFocusChange );
900
901
0
    if (HasChildPathFocus() && xFocusId != nullptr)
902
0
    {
903
        // restore focus to previous focus window if we still have the focus
904
0
        Window::EndSaveFocus(xFocusId);
905
0
    }
906
0
    else if ( pSVData->mpWinData->mpFocusWin && pSVData->mpWinData->mpFirstFloat &&
907
0
              ImplIsWindowOrChild( pSVData->mpWinData->mpFocusWin ) )
908
0
    {
909
        // maybe pass focus on to a suitable FloatingWindow
910
0
        pSVData->mpWinData->mpFirstFloat->GrabFocus();
911
0
    }
912
913
0
    mbPopupModeCanceled = bool(nFlags & FloatWinPopupEndFlags::Cancel);
914
915
    // redo title
916
0
    SetTitleType( mnOldTitle );
917
918
    // set ToolBox again to normal
919
0
    if (mpImplData && mpImplData->mpBox)
920
0
    {
921
0
        mpImplData->mpBox->ImplFloatControl( false, this );
922
        // if the parent ToolBox is in popup mode, it should be closed too.
923
0
        if ( GetDockingManager()->IsInPopupMode( mpImplData->mpBox ) )
924
0
            nFlags |= FloatWinPopupEndFlags::CloseAll;
925
926
0
        mpImplData->mpBox = nullptr;
927
0
    }
928
929
    // call PopupModeEnd-Handler depending on parameter
930
0
    if ( !(nFlags & FloatWinPopupEndFlags::DontCallHdl) )
931
0
        ImplCallPopupModeEnd();
932
933
    // close all other windows depending on parameter
934
0
    if ( nFlags & FloatWinPopupEndFlags::CloseAll )
935
0
    {
936
0
        if ( !(nPopupModeFlags & FloatWinPopupFlags::NewLevel) )
937
0
        {
938
0
            if (pSVData->mpWinData->mpFirstFloat)
939
0
            {
940
0
                FloatingWindow* pLastLevelFloat = pSVData->mpWinData->mpFirstFloat->ImplFindLastLevelFloat();
941
0
                pLastLevelFloat->EndPopupMode( FloatWinPopupEndFlags::Cancel | FloatWinPopupEndFlags::CloseAll );
942
0
            }
943
0
        }
944
0
    }
945
946
0
    mbInCleanUp = false;
947
0
}
948
949
void FloatingWindow::EndPopupMode( FloatWinPopupEndFlags nFlags )
950
0
{
951
0
    ImplEndPopupMode(nFlags, mxPrevFocusWin);
952
0
}
953
954
void FloatingWindow::AddPopupModeWindow(vcl::Window* pWindow)
955
0
{
956
    // !!! up-to-now only 1 window and not yet a list
957
0
    mpFirstPopupModeWin = pWindow;
958
0
}
959
960
bool SystemWindow::UpdatePositionData()
961
0
{
962
    // tdf#164337 don't update position data when waiting for a system resize
963
    // When entering and exiting LibreOffice's internal full screen mode,
964
    // updating position data causes the "exit full screen" floating
965
    // toolbar to migrate after cycle.
966
0
    if (mpWindowImpl->mbWaitSystemResize)
967
0
        return false;
968
969
0
    auto pWin = ImplGetParent();
970
0
    if (pWin)
971
0
    {
972
        // Simulate Move, so the relative position of the floating window will be recalculated
973
0
        pWin->ImplCallMove();
974
0
        return true;
975
0
    }
976
977
0
    return false;
978
0
}
979
980
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */