Coverage Report

Created: 2026-05-16 09:25

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