/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   Licensed to the Apache Software Foundation (ASF) under one or more
 *   contributor license agreements. See the NOTICE file distributed
 *   with this work for additional information regarding copyright
 *   ownership. The ASF licenses this file to you under the Apache
 *   License, Version 2.0 (the "License"); you may not use this file
 *   except in compliance with the License. You may obtain a copy of
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
 */

#include <config_features.h>
#include <config_version.h>

#include <osl/diagnose.h>
#include <osl/file.hxx>
#include <osl/thread.hxx>
#include <osl/module.hxx>
#include <rtl/ustrbuf.hxx>

#include <sal/log.hxx>

#include <tools/debug.hxx>
#include <tools/time.hxx>
#include <tools/stream.hxx>
#include <tools/json_writer.hxx>

#include <comphelper/configuration.hxx>
#include <unotools/resmgr.hxx>
#include <unotools/syslocale.hxx>
#include <unotools/syslocaleoptions.hxx>

#include <vcl/toolkit/dialog.hxx>
#include <vcl/dialoghelper.hxx>
#include <vcl/lok.hxx>
#include <vcl/toolkit/floatwin.hxx>
#include <vcl/settings.hxx>
#include <vcl/keycod.hxx>
#include <vcl/event.hxx>
#include <vcl/vclevent.hxx>
#include <vcl/virdev.hxx>
#include <vcl/wrkwin.hxx>
#include <vcl/svapp.hxx>
#include <vcl/cvtgrf.hxx>
#include <vcl/toolkit/unowrap.hxx>
#include <vcl/timer.hxx>
#include <vcl/scheduler.hxx>
#include <vcl/skia/SkiaHelper.hxx>

#include <dbggui.hxx>
#include <salinst.hxx>
#include <graphic/Manager.hxx>
#include <salframe.hxx>
#include <salsys.hxx>
#include <svdata.hxx>
#include <displayconnectiondispatch.hxx>
#include <window.h>
#include <accmgr.hxx>
#include <strings.hrc>
#include <strings.hxx>
#if OSL_DEBUG_LEVEL > 0
#include <schedulerimpl.hxx>
#endif

#include <com/sun/star/uno/Reference.h>
#include <com/sun/star/awt/XToolkit.hpp>
#include <comphelper/lok.hxx>
#include <comphelper/threadpool.hxx>
#include <comphelper/solarmutex.hxx>
#include <osl/process.h>

#ifdef DBG_UTIL
#include <svl/poolitem.hxx>
#include <svl/itemset.hxx>
#include <svl/itempool.hxx>
#endif

#include <cassert>
#include <limits>
#include <string_view>
#include <utility>
#include <thread>

using namespace ::com::sun::star;
using namespace ::com::sun::star::uno;

namespace {
void InitSettings(ImplSVData* pSVData);
}

// keycodes handled internally by VCL
vcl::KeyCode const ReservedKeys[]
{
                vcl::KeyCode(KEY_F1,0)                  , // Help
                vcl::KeyCode(KEY_F1,KEY_SHIFT)          , // Context help
                vcl::KeyCode(KEY_F2,KEY_SHIFT)          , // Activate extended tooltips
                vcl::KeyCode(KEY_F4,KEY_MOD1)           , // Close document
                vcl::KeyCode(KEY_F4,KEY_MOD2)           , // Close document
                vcl::KeyCode(KEY_F6,0)                  , // Set focus to next visible subwindow
                vcl::KeyCode(KEY_F6,KEY_MOD1)           , // Set focus to the document canvas/data source
                vcl::KeyCode(KEY_F6,KEY_SHIFT)          , // Set focus to previous subwindow
                vcl::KeyCode(KEY_F10,0)                   // Activate the first menu
};

extern "C" {
    typedef UnoWrapperBase* (*FN_TkCreateUnoWrapper)();
}

struct ImplPostEventData
{
    VclPtr<vcl::Window> mpWin;
    ImplSVEvent *   mnEventId;
    MouseEvent      maMouseEvent;
    VclEventId      mnEvent;
    KeyEvent        maKeyEvent;
    GestureEventPan maGestureEvent;

    ImplPostEventData(VclEventId nEvent, vcl::Window* pWin, const KeyEvent& rKeyEvent)
        : mpWin(pWin)
        , mnEventId(nullptr)
        , mnEvent(nEvent)
        , maKeyEvent(rKeyEvent)
    {}
    ImplPostEventData(VclEventId nEvent, vcl::Window* pWin, const MouseEvent& rMouseEvent)
        : mpWin(pWin)
        , mnEventId(nullptr)
        , maMouseEvent(rMouseEvent)
        , mnEvent(nEvent)
    {}
    ImplPostEventData(VclEventId nEvent, vcl::Window* pWin, const GestureEventPan& rGestureEvent)
        : mpWin(pWin)
        , mnEventId(nullptr)
        , mnEvent(nEvent)
        , maGestureEvent(rGestureEvent)
    {}
};

Application* GetpApp()
{
    ImplSVData* pSVData = ImplGetSVData();
    if ( !pSVData )
        return nullptr;
    return pSVData->mpApp;
}

Application::Application()
{
    // useful for themes at least, perhaps extensions too
    OUString aVar(u"LIBO_VERSION"_ustr), aValue(u"" LIBO_VERSION_DOTTED ""_ustr);
    osl_setEnvironment(aVar.pData, aValue.pData);

    ImplGetSVData()->mpApp = this;
    m_pCallbackData = nullptr;
    m_pCallback = nullptr;
}

Application::~Application()
{
    ImplDeInitSVData();
    ImplGetSVData()->mpApp = nullptr;
#ifdef DBG_UTIL
    // Due to
    //   svx/source/dialog/framelinkarray.cxx
    //   class Cell final : public SfxPoolItem
    //   const Cell OBJ_CELL_NONE;
    // being a static held SfxPoolItem which is not yet de-initialized here
    // number often is (1), even higher with other modules loaded (like 5).
    // These get de-allocated reliably in module-deinitializations, so this
    // is no memory loss. These counters are more to be able to have an eye
    // on amounts of SfxPoolItems used during office usage and to be able to
    // detect if an error in future changes may lead to memory losses - these
    // would show in dramatically higher numbers then immediately
    SAL_INFO("vcl.items", "ITEM: " << getAllocatedSfxPoolItemCount() << " SfxPoolItems still allocated at shutdown");
    SAL_INFO("vcl.items", "ITEM: " << getUsedSfxPoolItemCount() << " SfxPoolItems were incarnated during runtime");

    // Same mechanism for SfxItemSet(s)
    SAL_INFO("vcl.items", "ITEM: " << getAllocatedSfxItemSetCount() << " SfxItemSets still allocated at shutdown");
    SAL_INFO("vcl.items", "ITEM: " << getUsedSfxItemSetCount() << " SfxItemSets were incarnated during runtime");

    // Same mechanism for PoolItemHolder(s)
    SAL_INFO("vcl.items", "ITEM: " << getAllocatedSfxPoolItemHolderCount() << " SfxPoolItemHolders still allocated at shutdown");
    SAL_INFO("vcl.items", "ITEM: " << getUsedSfxPoolItemHolderCount() << " SfxPoolItemHolders were incarnated during runtime");

    // Additional call to list still incarnated SfxPoolItems (under 'svl.items')
    listAllocatedSfxPoolItems();

    // List SfxPoolItems with highest RefCounts, these are the best
    // candidates to add a ItemInstanceManager mechanism
    listSfxPoolItemsWithHighestUsage(20);
    listSfxItemSetUsage();
#endif
}

int Application::Main()
{
    SAL_WARN("vcl", "Application is a base class and should be overridden.");
    return EXIT_SUCCESS;
}

bool Application::QueryExit()
{
    WorkWindow* pAppWin = ImplGetSVData()->maFrameData.mpAppWin;

    // call the close handler of the application window
    if ( pAppWin )
        return pAppWin->Close();
    else
        return true;
}

void Application::Shutdown()
{
}

void Application::Init()
{
}

void Application::InitFinished()
{
}

void Application::DeInit()
{
}

sal_uInt16 Application::GetCommandLineParamCount()
{
    return static_cast<sal_uInt16>(osl_getCommandArgCount());
}

OUString Application::GetCommandLineParam( sal_uInt16 nParam )
{
    OUString aParam;
    osl_getCommandArg( nParam, &aParam.pData );
    return aParam;
}

OUString Application::GetAppFileName()
{
    ImplSVData* pSVData = ImplGetSVData();
    SAL_WARN_IF( !pSVData->maAppData.mxAppFileName, "vcl", "AppFileName should be set to something after SVMain!" );
    if ( pSVData->maAppData.mxAppFileName )
        return *pSVData->maAppData.mxAppFileName;

    /*
     *  provide a fallback for people without initialized vcl here (like setup
     *  in responsefile mode)
     */
    OUString aAppFileName;
    OUString aExeFileName;
    osl_getExecutableFile(&aExeFileName.pData);

    // convert path to native file format
    osl::FileBase::getSystemPathFromFileURL(aExeFileName, aAppFileName);

    return aAppFileName;
}

void Application::Exception( ExceptionCategory nCategory )
{
    switch ( nCategory )
    {
        // System has precedence (so do nothing)
        case ExceptionCategory::System:
        case ExceptionCategory::UserInterface:
            break;
        default:
            Abort(u"Unknown Error"_ustr);
            break;
    }
}

void Application::Abort( const OUString& rErrorText )
{
    //HACK: Dump core iff --norestore command line argument is given (assuming
    // this process is run by developers who are interested in cores, vs. end
    // users who are not):
#if OSL_DEBUG_LEVEL > 0
    bool dumpCore = true;
#else
    bool dumpCore = false;
    sal_uInt16 n = GetCommandLineParamCount();
    for (sal_uInt16 i = 0; i != n; ++i) {
        if (GetCommandLineParam(i) == "--norestore") {
            dumpCore = true;
            break;
        }
    }
#endif

    SalAbort( rErrorText, dumpCore );
}

size_t Application::GetReservedKeyCodeCount()
{
    return SAL_N_ELEMENTS(ReservedKeys);
}

const vcl::KeyCode* Application::GetReservedKeyCode( size_t i )
{
    if( i >= GetReservedKeyCodeCount() )
        return nullptr;
    else
        return &ReservedKeys[i];
}

void Application::notifyWindow(vcl::LOKWindowId /*nLOKWindowId*/,
                               const OUString& /*rAction*/,
                               const std::vector<vcl::LOKPayloadItem>& /*rPayload = std::vector<LOKPayloadItem>()*/) const
{
    SAL_WARN("vcl", "Invoked not implemented method: Application::notifyWindow");
}

OString Application::dumpNotifyState() const
{
    SAL_WARN("vcl", "Invoked not implemented method: Application::dumpNotifyState");
    return "notimpl"_ostr;
}

void Application::libreOfficeKitViewCallback(int nType, const OString& pPayload) const
{
    if (!comphelper::LibreOfficeKit::isActive())
        return;

    if (m_pCallback)
    {
        m_pCallback(nType, pPayload.getStr(), m_pCallbackData);
    }
}

void Application::notifyInvalidation(tools::Rectangle const* /*pRect*/) const
{
}

void Application::Execute()
{
    ImplSVData* pSVData = ImplGetSVData();
    pSVData->maAppData.mbInAppExecute = true;
    pSVData->maAppData.mbAppQuit = false;

    int nExitCode = 0;
    if (!pSVData->mpDefInst->DoExecute(nExitCode))
    {
        if (Application::IsOnSystemEventLoop())
        {
            SAL_WARN("vcl.schedule", "Can't omit DoExecute when running on system event loop!");
            std::abort();
        }
        while (!pSVData->maAppData.mbAppQuit)
        {
            Application::Yield();
            SolarMutexReleaser releaser; // Give a chance for the waiting threads to lock the mutex
            pSVData->m_inExecuteCondtion.set();
        }
    }

    pSVData->maAppData.mbInAppExecute = false;

    GetpApp()->Shutdown();
}

static bool ImplYield(bool i_bWait, bool i_bAllEvents)
{
    ImplSVData* pSVData = ImplGetSVData();

    SAL_INFO("vcl.schedule", "Enter ImplYield: " << (i_bWait ? "wait" : "no wait") <<
             ": " << (i_bAllEvents ? "all events" : "one event"));

    // there's a data race here on WNT only because ImplYield may be
    // called without SolarMutex; but the only remaining use of mnDispatchLevel
    // is in OSX specific code
    pSVData->maAppData.mnDispatchLevel++;

    // do not wait for events if application was already quit; in that
    // case only dispatch events already available
    bool bProcessedEvent = pSVData->mpDefInst->DoYield(
            i_bWait && !pSVData->maAppData.mbAppQuit, i_bAllEvents );

    pSVData->maAppData.mnDispatchLevel--;

    DBG_TESTSOLARMUTEX(); // must be locked on return from Yield

    SAL_INFO("vcl.schedule", "Leave ImplYield with return " << bProcessedEvent );
    return bProcessedEvent;
}

bool Application::Reschedule( bool i_bAllEvents )
{
    static const bool bAbort = Application::IsOnSystemEventLoop();
    if (bAbort)
    {
        SAL_WARN("vcl.schedule", "Application::Reschedule(" << i_bAllEvents << ")");
        return false;
    }
    return ImplYield(false, i_bAllEvents);
}

bool Application::IsOnSystemEventLoop()
{
    return ImplGetSVData()->maAppData.m_bUseSystemLoop;
}

void Scheduler::ProcessEventsToIdle()
{
#if OSL_DEBUG_LEVEL > 0
    const ImplSVData* pSVData = ImplGetSVData();
    if (pSVData->mpDefInst->IsMainThread())
        assert(pSVData->maSchedCtx.mnIdlesLockCount == 0);
#endif
    int nSanity = 1;
    while (ImplYield(false, true))
    {
        if (0 == ++nSanity % 1000)
        {
            SAL_WARN("vcl.schedule", "ProcessEventsToIdle: " << nSanity);
        }
    }
#if OSL_DEBUG_LEVEL > 0
    // If we yield from a non-main thread we just can guarantee that all idle
    // events were processed at some point, but our check can't prevent further
    // processing in the main thread, which may add new events, so skip it.
    if ( !pSVData->mpDefInst->IsMainThread() )
        return;
    for (int nTaskPriority = 0; nTaskPriority < PRIO_COUNT; ++nTaskPriority)
    {
        const ImplSchedulerData* pSchedulerData = pSVData->maSchedCtx.mpFirstSchedulerData[nTaskPriority];
        while (pSchedulerData)
        {
            assert(!pSchedulerData->mbInScheduler);
            if (pSchedulerData->mpTask)
            {
                Idle *pIdle = dynamic_cast<Idle*>(pSchedulerData->mpTask);
                if (pIdle && pIdle->IsActive())
                {
                    SAL_WARN("vcl.schedule",
                             "Unprocessed Idle: "
                                 << pIdle << " "
                                 << (pIdle->GetDebugName() ? pIdle->GetDebugName() : "(nullptr)"));
                }
            }
            pSchedulerData = pSchedulerData->mpNext;
        }
    }
#endif
}

extern "C" {
/// used by unit tests that test only via the LOK API
SAL_DLLPUBLIC_EXPORT void unit_lok_process_events_to_idle()
{
    const SolarMutexGuard aGuard;
    Scheduler::ProcessEventsToIdle();
}
}

void Application::Yield()
{
    static const bool bAbort = Application::IsOnSystemEventLoop();
    if (bAbort)
    {
        SAL_WARN("vcl.schedule", "Application::Yield()");
        std::abort();
    }
    ImplYield(true, false);
}

IMPL_STATIC_LINK_NOARG( ImplSVAppData, ImplQuitMsg, void*, void )
{
    assert(ImplGetSVData()->maAppData.mbAppQuit);
    ImplGetSVData()->mpDefInst->DoQuit();
}

void Application::Quit()
{
    ImplGetSVData()->maAppData.mbAppQuit = true;
    Application::PostUserEvent( LINK( nullptr, ImplSVAppData, ImplQuitMsg ) );
}

comphelper::SolarMutex& Application::GetSolarMutex()
{
    ImplSVData* pSVData = ImplGetSVData();
    return *(pSVData->mpDefInst->GetYieldMutex());
}

bool Application::IsMainThread()
{
    return ImplGetSVData()->mnMainThreadId == osl::Thread::getCurrentIdentifier();
}

sal_uInt32 Application::ReleaseSolarMutex()
{
    ImplSVData* pSVData = ImplGetSVData();
    return pSVData->mpDefInst->ReleaseYieldMutex(true);
}

void Application::AcquireSolarMutex( sal_uInt32 nCount )
{
    ImplSVData* pSVData = ImplGetSVData();
    pSVData->mpDefInst->AcquireYieldMutex( nCount );
}

bool Application::IsInMain()
{
    ImplSVData* pSVData = ImplGetSVData();
    return pSVData && pSVData->maAppData.mbInAppMain;
}

bool Application::IsInExecute()
{
    return ImplGetSVData()->maAppData.mbInAppExecute;
}

bool Application::IsQuit()
{
    return ImplGetSVData()->maAppData.mbAppQuit;
}

bool Application::IsInModalMode()
{
    return (ImplGetSVData()->maAppData.mnModalMode != 0);
}

sal_uInt16 Application::GetDispatchLevel()
{
    return ImplGetSVData()->maAppData.mnDispatchLevel;
}

bool Application::AnyInput( VclInputFlags nType )
{
    return ImplGetSVData()->mpDefInst->AnyInput( nType );
}

sal_uInt64 Application::GetLastInputInterval()
{
    return (tools::Time::GetSystemTicks()-ImplGetSVData()->maAppData.mnLastInputTime);
}

bool Application::IsUICaptured()
{
    ImplSVData* pSVData = ImplGetSVData();

    // If mouse was captured, or if in tracking- or in select-mode of a floatingwindow (e.g. menus
    // or pulldown toolboxes) another window should be created
    // D&D active !!!
    return pSVData->mpWinData->mpCaptureWin || pSVData->mpWinData->mpTrackWin
           || pSVData->mpWinData->mpFirstFloat || nImplSysDialog;
}

void Application::OverrideSystemSettings( AllSettings& /*rSettings*/ )
{
}

void Application::MergeSystemSettings( AllSettings& rSettings )
{
    vcl::Window* pWindow = ImplGetSVData()->maFrameData.mpFirstFrame;
    if( ! pWindow )
        pWindow = ImplGetDefaultWindow();
    if( pWindow )
    {
        ImplSVData* pSVData = ImplGetSVData();
        if ( !pSVData->maAppData.mbSettingsInit )
        {
            // side effect: ImplUpdateGlobalSettings does an ImplGetFrame()->UpdateSettings
            pWindow->ImplUpdateGlobalSettings( *pSVData->maAppData.mxSettings );
            pSVData->maAppData.mbSettingsInit = true;
        }
        // side effect: ImplUpdateGlobalSettings does an ImplGetFrame()->UpdateSettings
        pWindow->ImplUpdateGlobalSettings( rSettings, false );
    }
}

void Application::SetSettings(const AllSettings& rSettings, bool bTemporary)
{
    const SolarMutexGuard aGuard;

    ImplSVData* pSVData = ImplGetSVData();
    if ( !pSVData->maAppData.mxSettings )
    {
        InitSettings(pSVData);
        *pSVData->maAppData.mxSettings = rSettings;
    }
    else
    {
        AllSettings aOldSettings = *pSVData->maAppData.mxSettings;
        if (aOldSettings.GetUILanguageTag().getLanguageType() != rSettings.GetUILanguageTag().getLanguageType() &&
                pSVData->mbResLocaleSet)
        {
            pSVData->mbResLocaleSet = false;
        }
        *pSVData->maAppData.mxSettings = rSettings;
        // Don't broadcast temporary changes
        AllSettingsFlags nChangeFlags = bTemporary ? AllSettingsFlags::NONE
                                                   : aOldSettings.GetChangeFlags(*pSVData->maAppData.mxSettings);
        if (nChangeFlags != AllSettingsFlags::NONE)
        {
            DataChangedEvent aDCEvt( DataChangedEventType::SETTINGS, &aOldSettings, nChangeFlags );

            // notify data change handler
            ImplCallEventListenersApplicationDataChanged( &aDCEvt);

            // Update all windows
            vcl::Window* pFirstFrame = pSVData->maFrameData.mpFirstFrame;
            // Reset data that needs to be re-calculated
            tools::Long nOldDPIX = 0;
            tools::Long nOldDPIY = 0;
            if ( pFirstFrame )
            {
                nOldDPIX = pFirstFrame->GetOutDev()->GetDPIX();
                nOldDPIY = pFirstFrame->GetOutDev()->GetDPIY();
                vcl::Window::ImplInitAppFontData(pFirstFrame);
            }
            vcl::Window* pFrame = pFirstFrame;
            while ( pFrame )
            {
                // call UpdateSettings from ClientWindow in order to prevent updating data twice
                vcl::Window* pClientWin = pFrame;
                while ( pClientWin->ImplGetClientWindow() )
                    pClientWin = pClientWin->ImplGetClientWindow();
                pClientWin->UpdateSettings( rSettings, true );

                vcl::Window* pTempWin = pFrame->mpWindowImpl->mpFrameData->mpFirstOverlap;
                while ( pTempWin )
                {
                    // call UpdateSettings from ClientWindow in order to prevent updating data twice
                    pClientWin = pTempWin;
                    while ( pClientWin->ImplGetClientWindow() )
                        pClientWin = pClientWin->ImplGetClientWindow();
                    pClientWin->UpdateSettings( rSettings, true );
                    pTempWin = pTempWin->mpWindowImpl->mpNextOverlap;
                }

                pFrame = pFrame->mpWindowImpl->mpFrameData->mpNextFrame;
            }

            // if DPI resolution for screen output was changed set the new resolution for all
            // screen compatible VirDev's
            pFirstFrame = pSVData->maFrameData.mpFirstFrame;
            if ( pFirstFrame )
            {
                if ( (pFirstFrame->GetOutDev()->GetDPIX() != nOldDPIX) ||
                     (pFirstFrame->GetOutDev()->GetDPIY() != nOldDPIY) )
                {
                    VirtualDevice* pVirDev = pSVData->maGDIData.mpFirstVirDev;
                    while ( pVirDev )
                    {
                        if ( pVirDev->mbScreenComp &&
                             (pVirDev->GetDPIX() == nOldDPIX) &&
                             (pVirDev->GetDPIY() == nOldDPIY) )
                        {
                            pVirDev->SetDPIX( pFirstFrame->GetOutDev()->GetDPIX() );
                            pVirDev->SetDPIY( pFirstFrame->GetOutDev()->GetDPIY() );
                            if ( pVirDev->IsMapModeEnabled() )
                            {
                                MapMode aMapMode = pVirDev->GetMapMode();
                                pVirDev->SetMapMode();
                                pVirDev->SetMapMode( aMapMode );
                            }
                        }

                        pVirDev = pVirDev->mpNext;
                    }
                }
            }
        }
    }
}

const AllSettings& Application::GetSettings()
{
    ImplSVData* pSVData = ImplGetSVData();
    if ( !pSVData->maAppData.mxSettings )
    {
        InitSettings(pSVData);
    }

    return *(pSVData->maAppData.mxSettings);
}

namespace {

void InitSettings(ImplSVData* pSVData)
{
    assert(!pSVData->maAppData.mxSettings && "initialization should not happen twice!");

    pSVData->maAppData.mxSettings.emplace();
    if (!comphelper::IsFuzzing())
    {
        pSVData->maAppData.mpCfgListener = new LocaleConfigurationListener;
        pSVData->maAppData.mxSettings->GetSysLocale().GetOptions().AddListener( pSVData->maAppData.mpCfgListener );
    }
}

}

void Application::NotifyAllWindows( DataChangedEvent& rDCEvt )
{
    ImplSVData* pSVData = ImplGetSVData();
    vcl::Window* pFrame = pSVData->maFrameData.mpFirstFrame;
    while ( pFrame )
    {
        pFrame->NotifyAllChildren( rDCEvt );

        vcl::Window* pSysWin = pFrame->mpWindowImpl->mpFrameData->mpFirstOverlap;
        while ( pSysWin )
        {
            pSysWin->NotifyAllChildren( rDCEvt );
            pSysWin = pSysWin->mpWindowImpl->mpNextOverlap;
        }

        pFrame = pFrame->mpWindowImpl->mpFrameData->mpNextFrame;
    }
}

void Application::ImplCallEventListenersApplicationDataChanged( void* pData )
{
    ImplSVData* pSVData = ImplGetSVData();
    VclWindowEvent aEvent( nullptr, VclEventId::ApplicationDataChanged, pData );

    pSVData->maAppData.maEventListeners.Call( aEvent );
}

void Application::ImplCallEventListeners( VclSimpleEvent& rEvent )
{
    ImplSVData* pSVData = ImplGetSVData();
    pSVData->maAppData.maEventListeners.Call( rEvent );
}

void Application::AddEventListener( const Link<VclSimpleEvent&,void>& rEventListener )
{
    ImplSVData* pSVData = ImplGetSVData();
    pSVData->maAppData.maEventListeners.addListener( rEventListener );
}

void Application::RemoveEventListener( const Link<VclSimpleEvent&,void>& rEventListener )
{
    ImplSVData* pSVData = ImplGetSVData();
    pSVData->maAppData.maEventListeners.removeListener( rEventListener );
}

void Application::AddKeyListener( const Link<VclWindowEvent&,bool>& rKeyListener )
{
    ImplSVData* pSVData = ImplGetSVData();
    pSVData->maAppData.maKeyListeners.push_back( rKeyListener );
}

void Application::RemoveKeyListener( const Link<VclWindowEvent&,bool>& rKeyListener )
{
    ImplSVData* pSVData = ImplGetSVData();
    auto & rVec = pSVData->maAppData.maKeyListeners;
    std::erase(rVec, rKeyListener);
}

bool Application::HandleKey( VclEventId nEvent, vcl::Window *pWin, KeyEvent* pKeyEvent )
{
    // let listeners process the key event
    VclWindowEvent aEvent( pWin, nEvent, static_cast<void *>(pKeyEvent) );

    ImplSVData* pSVData = ImplGetSVData();

    if ( pSVData->maAppData.maKeyListeners.empty() )
        return false;

    bool bProcessed = false;
    // Copy the list, because this can be destroyed when calling a Link...
    std::vector<Link<VclWindowEvent&,bool>> aCopy( pSVData->maAppData.maKeyListeners );
    for ( const Link<VclWindowEvent&,bool>& rLink : aCopy )
    {
        if( rLink.Call( aEvent ) )
        {
            bProcessed = true;
            break;
        }
    }
    return bProcessed;
}

ImplSVEvent * Application::PostKeyEvent( VclEventId nEvent, vcl::Window *pWin, KeyEvent const * pKeyEvent )
{
    const SolarMutexGuard aGuard;
    ImplSVEvent * nEventId = nullptr;

    if( pWin && pKeyEvent )
    {
        std::unique_ptr<ImplPostEventData> pPostEventData(new ImplPostEventData( nEvent, pWin, *pKeyEvent ));

        nEventId = PostUserEvent(
                       LINK( nullptr, Application, PostEventHandler ),
                       pPostEventData.get() );

        if( nEventId )
        {
            pPostEventData->mnEventId = nEventId;
            ImplGetSVData()->maAppData.maPostedEventList.emplace_back( pWin, pPostEventData.release() );
        }
    }

    return nEventId;
}

ImplSVEvent* Application::PostGestureEvent(VclEventId nEvent, vcl::Window* pWin,
                                           GestureEventPan const * pGestureEvent)
{
    const SolarMutexGuard aGuard;
    ImplSVEvent * nEventId = nullptr;

    if (pWin && pGestureEvent)
    {
        Point aTransformedPosition(pGestureEvent->mnX, pGestureEvent->mnY);

        aTransformedPosition.AdjustX(pWin->GetOutOffXPixel());
        aTransformedPosition.AdjustY(pWin->GetOutOffYPixel());

        const GestureEventPan aGestureEvent(
            sal_Int32(aTransformedPosition.X()),
            sal_Int32(aTransformedPosition.Y()),
            pGestureEvent->meEventType,
            pGestureEvent->mnOffset,
            pGestureEvent->meOrientation
        );

        std::unique_ptr<ImplPostEventData> pPostEventData(new ImplPostEventData(nEvent, pWin, aGestureEvent));

        nEventId = PostUserEvent(
                       LINK( nullptr, Application, PostEventHandler ),
                       pPostEventData.get());

        if (nEventId)
        {
            pPostEventData->mnEventId = nEventId;
            ImplGetSVData()->maAppData.maPostedEventList.emplace_back(pWin, pPostEventData.release());
        }
    }

    return nEventId;
}

bool Application::LOKHandleMouseEvent(VclEventId nEvent, vcl::Window* pWindow, const MouseEvent* pEvent)
{
    bool bSuccess = false;
    SalMouseEvent aMouseEvent;

    if (!pWindow)
        return false;

    if (!pEvent)
        return false;

    aMouseEvent.mnTime = tools::Time::GetSystemTicks();
    aMouseEvent.mnX = pEvent->GetPosPixel().X();
    aMouseEvent.mnY = pEvent->GetPosPixel().Y();
    aMouseEvent.mnCode = pEvent->GetButtons() | pEvent->GetModifier();

    switch (nEvent)
    {
        case VclEventId::WindowMouseMove:
            aMouseEvent.mnButton = 0;
            bSuccess = ImplLOKHandleMouseEvent(pWindow, NotifyEventType::MOUSEMOVE, false,
                                               aMouseEvent.mnX, aMouseEvent.mnY,
                                               aMouseEvent.mnTime, aMouseEvent.mnCode,
                                               ImplGetMouseMoveMode(&aMouseEvent),
                                               pEvent->GetClicks());
        break;

        case VclEventId::WindowMouseButtonDown:
            aMouseEvent.mnButton = pEvent->GetButtons();
            bSuccess = ImplLOKHandleMouseEvent(pWindow, NotifyEventType::MOUSEBUTTONDOWN, false,
                                               aMouseEvent.mnX, aMouseEvent.mnY,
                                               aMouseEvent.mnTime,
#ifdef MACOSX
                                               aMouseEvent.mnButton |
                                               (aMouseEvent.mnCode & (KEY_SHIFT | KEY_MOD1 | KEY_MOD2 | KEY_MOD3)),
#else
                                               aMouseEvent.mnButton |
                                               (aMouseEvent.mnCode & (KEY_SHIFT | KEY_MOD1 | KEY_MOD2)),
#endif
                                               ImplGetMouseButtonMode(&aMouseEvent),
                                               pEvent->GetClicks());
            break;

        case VclEventId::WindowMouseButtonUp:
            aMouseEvent.mnButton = pEvent->GetButtons();
            bSuccess = ImplLOKHandleMouseEvent(pWindow, NotifyEventType::MOUSEBUTTONUP, false,
                                               aMouseEvent.mnX, aMouseEvent.mnY,
                                               aMouseEvent.mnTime,
#ifdef MACOSX
                                               aMouseEvent.mnButton |
                                               (aMouseEvent.mnCode & (KEY_SHIFT | KEY_MOD1 | KEY_MOD2 | KEY_MOD3)),
#else
                                               aMouseEvent.mnButton |
                                               (aMouseEvent.mnCode & (KEY_SHIFT | KEY_MOD1 | KEY_MOD2)),
#endif
                                               ImplGetMouseButtonMode(&aMouseEvent),
                                               pEvent->GetClicks());
            break;

        default:
            SAL_WARN( "vcl.layout", "Application::HandleMouseEvent unknown event (" << static_cast<int>(nEvent) << ")" );
            break;
    }

    return bSuccess;
}


ImplSVEvent* Application::PostMouseEvent( VclEventId nEvent, vcl::Window *pWin, MouseEvent const * pMouseEvent )
{
    const SolarMutexGuard aGuard;
    ImplSVEvent * nEventId = nullptr;

    if( pWin && pMouseEvent )
    {
        Point aTransformedPos( pMouseEvent->GetPosPixel() );

        // LOK uses (0, 0) as the origin of all windows; don't offset.
        if (!comphelper::LibreOfficeKit::isActive())
        {
            aTransformedPos.AdjustX(pWin->GetOutOffXPixel());
            aTransformedPos.AdjustY(pWin->GetOutOffYPixel());
        }

        const MouseEvent aTransformedEvent( aTransformedPos, pMouseEvent->GetClicks(), pMouseEvent->GetMode(),
                                            pMouseEvent->GetButtons(), pMouseEvent->GetModifier() );

        std::unique_ptr<ImplPostEventData> pPostEventData(new ImplPostEventData( nEvent, pWin, aTransformedEvent ));

        nEventId = PostUserEvent(
                       LINK( nullptr, Application, PostEventHandler ),
                       pPostEventData.get() );

        if( nEventId )
        {
            pPostEventData->mnEventId = nEventId;
            ImplGetSVData()->maAppData.maPostedEventList.emplace_back( pWin, pPostEventData.release() );
        }
    }

    return nEventId;
}


IMPL_STATIC_LINK( Application, PostEventHandler, void*, pCallData, void )
{
    const SolarMutexGuard aGuard;
    ImplPostEventData*  pData = static_cast< ImplPostEventData * >( pCallData );
    const void*         pEventData;
    SalEvent            nEvent;
    ImplSVEvent * const nEventId = pData->mnEventId;

    switch( pData->mnEvent )
    {
        case VclEventId::WindowMouseMove:
            nEvent = SalEvent::ExternalMouseMove;
            pEventData = &pData->maMouseEvent;
        break;

        case VclEventId::WindowMouseButtonDown:
            nEvent = SalEvent::ExternalMouseButtonDown;
            pEventData = &pData->maMouseEvent;
        break;

        case VclEventId::WindowMouseButtonUp:
            nEvent = SalEvent::ExternalMouseButtonUp;
            pEventData = &pData->maMouseEvent;
        break;

        case VclEventId::WindowKeyInput:
            nEvent = SalEvent::ExternalKeyInput;
            pEventData = &pData->maKeyEvent;
        break;

        case VclEventId::WindowKeyUp:
            nEvent = SalEvent::ExternalKeyUp;
            pEventData = &pData->maKeyEvent;
        break;

        case VclEventId::WindowGestureEvent:
            nEvent = SalEvent::ExternalGesture;
            pEventData = &pData->maGestureEvent;
        break;

        default:
            nEvent = SalEvent::NONE;
            pEventData = nullptr;
        break;
    }

    if( pData->mpWin && pData->mpWin->mpWindowImpl->mpFrameWindow && pEventData )
        ImplWindowFrameProc( pData->mpWin->mpWindowImpl->mpFrameWindow.get(), nEvent, pEventData );

    // remove this event from list of posted events, watch for destruction of internal data
    auto svdata = ImplGetSVData();
    ::std::vector< ImplPostEventPair >::iterator aIter( svdata->maAppData.maPostedEventList.begin() );

    while( aIter != svdata->maAppData.maPostedEventList.end() )
    {
        if( nEventId == (*aIter).second->mnEventId )
        {
            delete (*aIter).second;
            aIter = svdata->maAppData.maPostedEventList.erase( aIter );
        }
        else
            ++aIter;
    }
}

void Application::RemoveMouseAndKeyEvents( vcl::Window* pWin )
{
    const SolarMutexGuard aGuard;

    // remove all events for specific window, watch for destruction of internal data
    auto svdata = ImplGetSVData();
    ::std::vector< ImplPostEventPair >::iterator aIter( svdata->maAppData.maPostedEventList.begin() );

    while( aIter != svdata->maAppData.maPostedEventList.end() )
    {
        if( pWin == (*aIter).first )
        {
            if( (*aIter).second->mnEventId )
                RemoveUserEvent( (*aIter).second->mnEventId );

            delete (*aIter).second;
            aIter = svdata->maAppData.maPostedEventList.erase( aIter );
        }
        else
            ++aIter;
    }
}

ImplSVEvent * Application::PostUserEvent( const Link<void*,void>& rLink, void* pCaller,
                                          bool bReferenceLink )
{
    vcl::Window* pDefWindow = ImplGetDefaultWindow();
    if ( pDefWindow == nullptr )
        return nullptr;

    std::unique_ptr<ImplSVEvent> pSVEvent(new ImplSVEvent);
    pSVEvent->mpData    = pCaller;
    pSVEvent->maLink    = rLink;
    pSVEvent->mpWindow  = nullptr;
    pSVEvent->mbCall    = true;
    if (bReferenceLink)
    {
        SolarMutexGuard aGuard;
        pSVEvent->mpInstanceRef = static_cast<vcl::Window *>(rLink.GetInstance());
    }

    auto pTmpEvent = pSVEvent.get();
    if (!pDefWindow->ImplGetFrame()->PostEvent( std::move(pSVEvent) ))
        return nullptr;
    return pTmpEvent;
}

void Application::RemoveUserEvent( ImplSVEvent * nUserEvent )
{
    if(nUserEvent)
    {
        SAL_WARN_IF( nUserEvent->mpWindow, "vcl",
                    "Application::RemoveUserEvent(): Event is send to a window" );
        SAL_WARN_IF( !nUserEvent->mbCall, "vcl",
                    "Application::RemoveUserEvent(): Event is already removed" );

        nUserEvent->mpWindow.clear();
        nUserEvent->mpInstanceRef.clear();
        nUserEvent->mbCall = false;
    }
}

vcl::Window* Application::GetFocusWindow()
{
    return ImplGetSVData()->mpWinData->mpFocusWin;
}

OutputDevice* Application::GetDefaultDevice()
{
    vcl::Window* pWindow = ImplGetDefaultWindow();
    if (pWindow != nullptr)
    {
        return pWindow->GetOutDev();
    }
    else
    {
        return nullptr;
    }
}

basegfx::SystemDependentDataManager& Application::GetSystemDependentDataManager()
{
    return ImplGetSystemDependentDataManager();
}

vcl::Window* Application::GetFirstTopLevelWindow()
{
    ImplSVData* pSVData = ImplGetSVData();
    return pSVData->maFrameData.mpFirstFrame;
}

vcl::Window* Application::GetNextTopLevelWindow( vcl::Window const * pWindow )
{
    return pWindow->mpWindowImpl->mpFrameData->mpNextFrame;
}

tools::Long    Application::GetTopWindowCount()
{
    tools::Long nRet = 0;
    ImplSVData* pSVData = ImplGetSVData();
    vcl::Window *pWin = pSVData ? pSVData->maFrameData.mpFirstFrame.get() : nullptr;
    while( pWin )
    {
        if( pWin->ImplGetWindow()->IsTopWindow() )
            nRet++;
        pWin = pWin->mpWindowImpl->mpFrameData->mpNextFrame;
    }
    return nRet;
}

vcl::Window* Application::GetTopWindow( tools::Long nIndex )
{
    tools::Long nIdx = 0;
    ImplSVData* pSVData = ImplGetSVData();
    vcl::Window *pWin = pSVData ? pSVData->maFrameData.mpFirstFrame.get() : nullptr;
    while( pWin )
    {
        if( pWin->ImplGetWindow()->IsTopWindow() )
        {
            if( nIdx == nIndex )
                return pWin->ImplGetWindow();
            else
                nIdx++;
        }
        pWin = pWin->mpWindowImpl->mpFrameData->mpNextFrame;
    }
    return nullptr;
}

vcl::Window* Application::GetActiveTopWindow()
{
    vcl::Window *pWin = ImplGetSVData()->mpWinData->mpFocusWin;
    while( pWin )
    {
        if( pWin->IsTopWindow() )
            return pWin;
        pWin = pWin->mpWindowImpl->mpParent;
    }
    return nullptr;
}

void Application::SetAppName( const OUString& rUniqueName )
{
    ImplSVData* pSVData = ImplGetSVData();
    pSVData->maAppData.mxAppName = rUniqueName;
}

const OUString & Application::GetAppName()
{
    ImplSVData* pSVData = ImplGetSVData();
    if ( pSVData->maAppData.mxAppName )
        return *(pSVData->maAppData.mxAppName);
    else
        return EMPTY_OUSTRING;
}

enum {hwAll=0, hwEnv=1, hwUI=2};

static OUString Localize(TranslateId aId, const bool bLocalize)
{
    if (bLocalize)
        return VclResId(aId);
    else
        return Translate::get(aId, Translate::Create("vcl", LanguageTag(u"en-US"_ustr)));
}

OUString Application::GetOSVersion()
{
    ImplSVData* pSVData = ImplGetSVData();
    OUString aVersion;
    if (pSVData && pSVData->mpDefInst)
        aVersion = pSVData->mpDefInst->getOSVersion();
    else
        aVersion = "-";
    return aVersion;
}

OUString Application::GetHWOSConfInfo(const int bSelection, const bool bLocalize)
{
    OUStringBuffer aDetails;

    const auto appendDetails = [&aDetails](std::u16string_view sep, auto&& val) {
        if (!aDetails.isEmpty() && !sep.empty())
            aDetails.append(sep);
        aDetails.append(std::move(val));
    };

    if (bSelection != hwUI) {
        appendDetails(u"; ", Localize(SV_APP_CPUTHREADS, bLocalize)
                                + OUString::number(std::thread::hardware_concurrency()));

        OUString aVersion = GetOSVersion();

        appendDetails(u"; ", Localize(SV_APP_OSVERSION, bLocalize) + aVersion);
    }

    if (bSelection != hwEnv) {
        appendDetails(u"; ", Localize(SV_APP_UIRENDER, bLocalize));
#if HAVE_FEATURE_SKIA
        if ( SkiaHelper::isVCLSkiaEnabled() )
        {
            switch(SkiaHelper::renderMethodToUse())
            {
                case SkiaHelper::RenderVulkan:
                    appendDetails(u"", Localize(SV_APP_SKIA_VULKAN, bLocalize));
                    break;
                case SkiaHelper::RenderMetal:
                    appendDetails(u"", Localize(SV_APP_SKIA_METAL, bLocalize));
                    break;
                case SkiaHelper::RenderRaster:
                    appendDetails(u"", Localize(SV_APP_SKIA_RASTER, bLocalize));
                    break;
            }
        }
        else
#endif
            appendDetails(u"", Localize(SV_APP_DEFAULT, bLocalize));

#if (defined LINUX || defined _WIN32 || defined MACOSX || defined __FreeBSD__ || defined EMSCRIPTEN)
        appendDetails(u"; ", SV_APP_VCLBACKEND + GetToolkitName());
#endif
    }

    return aDetails.makeStringAndClear();
}

void Application::SetDisplayName( const OUString& rName )
{
    ImplSVData* pSVData = ImplGetSVData();
    pSVData->maAppData.mxDisplayName = rName;
}

OUString Application::GetDisplayName()
{
    ImplSVData* pSVData = ImplGetSVData();
    if ( pSVData->maAppData.mxDisplayName )
        return *(pSVData->maAppData.mxDisplayName);
    else if (pSVData->maFrameData.mpAppWin)
        return pSVData->maFrameData.mpAppWin->GetText();
    else
        return OUString();
}

unsigned int Application::GetScreenCount()
{
    SalSystem* pSys = ImplGetSalSystem();
    return pSys ? pSys->GetDisplayScreenCount() : 0;
}

unsigned int Application::GetDisplayBuiltInScreen()
{
    SalSystem* pSys = ImplGetSalSystem();
    return pSys ? pSys->GetDisplayBuiltInScreen() : 0;
}

unsigned int Application::GetDisplayExternalScreen()
{
    // This is really unpleasant, in theory we could have multiple
    // external displays etc.
    int nExternal(0);
    switch (GetDisplayBuiltInScreen())
    {
    case 0:
        nExternal = 1;
        break;
    case 1:
        nExternal = 0;
        break;
    default:
        // When the built-in display is neither 0 nor 1
        // then place the full-screen presentation on the
        // first available screen.
        nExternal = 0;
        break;
    }
    return nExternal;
}

AbsoluteScreenPixelRectangle Application::GetScreenPosSizePixel( unsigned int nScreen )
{
    SalSystem* pSys = ImplGetSalSystem();
    if (!pSys)
    {
        SAL_WARN("vcl", "Requesting screen size/pos for screen #" << nScreen << " failed");
        assert(false);
        return AbsoluteScreenPixelRectangle();
    }
    AbsoluteScreenPixelRectangle aRect = pSys->GetDisplayScreenPosSizePixel(nScreen);
    if (aRect.GetHeight() == 0)
        SAL_WARN("vcl", "Requesting screen size/pos for screen #" << nScreen << " returned 0 height.");
    return aRect;
}

namespace {
tools::Long calcDistSquare( const AbsoluteScreenPixelPoint& i_rPoint, const AbsoluteScreenPixelRectangle& i_rRect )
{
    const AbsoluteScreenPixelPoint aRectCenter( (i_rRect.Left() + i_rRect.Right())/2,
                       (i_rRect.Top() + i_rRect.Bottom())/ 2 );
    const tools::Long nDX = aRectCenter.X() - i_rPoint.X();
    const tools::Long nDY = aRectCenter.Y() - i_rPoint.Y();
    return nDX*nDX + nDY*nDY;
}
}

unsigned int Application::GetBestScreen( const AbsoluteScreenPixelRectangle& i_rRect )
{
    const unsigned int nScreens = GetScreenCount();
    unsigned int nBestMatchScreen = 0;
    unsigned long nOverlap = 0;
    for( unsigned int i = 0; i < nScreens; i++ )
    {
        const AbsoluteScreenPixelRectangle aCurScreenRect( GetScreenPosSizePixel( i ) );
        // if a screen contains the rectangle completely it is obviously the best screen
        if( aCurScreenRect.Contains( i_rRect ) )
            return i;
        // next the screen which contains most of the area of the rect is the best
        AbsoluteScreenPixelRectangle aIntersection( aCurScreenRect.GetIntersection( i_rRect ) );
        if( ! aIntersection.IsEmpty() )
        {
            const unsigned long nCurOverlap( aIntersection.GetWidth() * aIntersection.GetHeight() );
            if( nCurOverlap > nOverlap )
            {
                nOverlap = nCurOverlap;
                nBestMatchScreen = i;
            }
        }
    }
    if( nOverlap > 0 )
        return nBestMatchScreen;

    // finally the screen which center is nearest to the rect is the best
    const AbsoluteScreenPixelPoint aCenter( (i_rRect.Left() + i_rRect.Right())/2,
                         (i_rRect.Top() + i_rRect.Bottom())/2 );
    tools::Long nDist = std::numeric_limits<tools::Long>::max();
    for( unsigned int i = 0; i < nScreens; i++ )
    {
        const AbsoluteScreenPixelRectangle aCurScreenRect( GetScreenPosSizePixel( i ) );
        const tools::Long nCurDist( calcDistSquare( aCenter, aCurScreenRect ) );
        if( nCurDist < nDist )
        {
            nBestMatchScreen = i;
            nDist = nCurDist;
        }
    }
    return nBestMatchScreen;
}

bool Application::InsertAccel( Accelerator* pAccel )
{
    ImplSVData* pSVData = ImplGetSVData();

    if ( !pSVData->maAppData.mpAccelMgr )
        pSVData->maAppData.mpAccelMgr = new ImplAccelManager();
    return pSVData->maAppData.mpAccelMgr->InsertAccel( pAccel );
}

void Application::RemoveAccel( Accelerator const * pAccel )
{
    ImplSVData* pSVData = ImplGetSVData();

    if ( pSVData->maAppData.mpAccelMgr )
        pSVData->maAppData.mpAccelMgr->RemoveAccel( pAccel );
}

void Application::SetHelp( Help* pHelp )
{
    ImplGetSVData()->maAppData.mpHelp = pHelp;
}

void Application::UpdateMainThread()
{
    ImplSVData* pSVData = ImplGetSVData();
    if (pSVData && pSVData->mpDefInst)
        pSVData->mpDefInst->updateMainThread();
}

Help* Application::GetHelp()
{
    return ImplGetSVData()->maAppData.mpHelp;
}

const OUString & Application::GetToolkitName()
{
    ImplSVData* pSVData = ImplGetSVData();
    if ( pSVData->maAppData.mxToolkitName )
        return *(pSVData->maAppData.mxToolkitName);
    else
        return EMPTY_OUSTRING;
}

vcl::Window* Dialog::GetDefDialogParent()
{
    ImplSVData* pSVData = ImplGetSVData();
    // find some useful dialog parent

    // always use the topmost parent of the candidate
    // window to avoid using dialogs or floaters
    // as DefDialogParent

    // current focus frame
    vcl::Window *pWin = pSVData->mpWinData->mpFocusWin;
    if (pWin && !pWin->IsMenuFloatingWindow())
    {
        while (pWin->mpWindowImpl && pWin->mpWindowImpl->mpParent)
            pWin = pWin->mpWindowImpl->mpParent;

        // check for corrupted window hierarchy, #122232#, may be we now crash somewhere else
        if (!pWin->mpWindowImpl)
        {
            OSL_FAIL( "Window hierarchy corrupted!" );
            pSVData->mpWinData->mpFocusWin = nullptr;   // avoid further access
            return nullptr;
        }

        if ((pWin->mpWindowImpl->mnStyle & WB_INTROWIN) == 0)
        {
            return pWin->mpWindowImpl->mpFrameWindow->ImplGetWindow();
        }
    }

    // last active application frame
    pWin = pSVData->maFrameData.mpActiveApplicationFrame;
    if (pWin)
    {
        return pWin->mpWindowImpl->mpFrameWindow->ImplGetWindow();
    }

    // first visible top window (may be totally wrong...)
    pWin = pSVData->maFrameData.mpFirstFrame;
    while (pWin)
    {
        if( pWin->ImplGetWindow()->IsTopWindow() &&
            pWin->mpWindowImpl->mbReallyVisible &&
            (pWin->mpWindowImpl->mnStyle & WB_INTROWIN) == 0
        )
        {
            while( pWin->mpWindowImpl->mpParent )
                pWin = pWin->mpWindowImpl->mpParent;
            return pWin->mpWindowImpl->mpFrameWindow->ImplGetWindow();
        }
        pWin = pWin->mpWindowImpl->mpFrameData->mpNextFrame;
    }

    // use the desktop
    return nullptr;
}

weld::Window* Application::GetDefDialogParent()
{
    vcl::Window* pWindow = Dialog::GetDefDialogParent();
    return pWindow ? pWindow->GetFrameWeld() : nullptr;
}

DialogCancelMode Application::GetDialogCancelMode()
{
    return ImplGetSVData()->maAppData.meDialogCancel;
}

void Application::SetDialogCancelMode( DialogCancelMode mode )
{
    ImplGetSVData()->maAppData.meDialogCancel = mode;
}

bool Application::IsDialogCancelEnabled()
{
    return ImplGetSVData()->maAppData.meDialogCancel != DialogCancelMode::Off;
}

void Application::SetSystemWindowMode( SystemWindowFlags nMode )
{
    ImplGetSVData()->maAppData.mnSysWinMode = nMode;
}

SystemWindowFlags Application::GetSystemWindowMode()
{
    return ImplGetSVData()->maAppData.mnSysWinMode;
}

css::uno::Reference< css::awt::XToolkit > Application::GetVCLToolkit()
{
    css::uno::Reference< css::awt::XToolkit > xT;
    UnoWrapperBase* pWrapper = UnoWrapperBase::GetUnoWrapper();
    if ( pWrapper )
        xT = pWrapper->GetVCLToolkit();
    return xT;
}

#ifdef DISABLE_DYNLOADING

extern "C" { UnoWrapperBase* CreateUnoWrapper(); }

#else

extern "C" { static void thisModule() {} }

#endif

UnoWrapperBase* UnoWrapperBase::GetUnoWrapper( bool bCreateIfNotExist )
{
    ImplSVData* pSVData = ImplGetSVData();
    static bool bAlreadyTriedToCreate = false;
    if ( !pSVData->mpUnoWrapper && bCreateIfNotExist && !bAlreadyTriedToCreate )
    {
#ifndef DISABLE_DYNLOADING
        osl::Module aTkLib;
        aTkLib.loadRelative(&thisModule, TK_DLL_NAME);
        if (aTkLib.is())
        {
            FN_TkCreateUnoWrapper fnCreateWrapper = reinterpret_cast<FN_TkCreateUnoWrapper>(aTkLib.getFunctionSymbol("CreateUnoWrapper"));
            if ( fnCreateWrapper )
            {
                pSVData->mpUnoWrapper = fnCreateWrapper();
            }
            aTkLib.release();
        }
        SAL_WARN_IF( !pSVData->mpUnoWrapper, "vcl", "UnoWrapper could not be created!" );
#else
        pSVData->mpUnoWrapper = CreateUnoWrapper();
#endif
        bAlreadyTriedToCreate = true;
    }
    return pSVData->mpUnoWrapper;
}

void UnoWrapperBase::SetUnoWrapper( UnoWrapperBase* pWrapper )
{
    ImplSVData* pSVData = ImplGetSVData();
    SAL_WARN_IF( pSVData->mpUnoWrapper, "vcl", "SetUnoWrapper: Wrapper already exists" );
    pSVData->mpUnoWrapper = pWrapper;
}

css::uno::Reference< css::awt::XDisplayConnection > Application::GetDisplayConnection()
{
    ImplSVData* pSVData = ImplGetSVData();

    if( !pSVData->mxDisplayConnection.is() )
    {
        pSVData->mxDisplayConnection.set( new vcl::DisplayConnectionDispatch );
        pSVData->mxDisplayConnection->start();
    }

    return pSVData->mxDisplayConnection;
}

void Application::SetFilterHdl( const Link<ConvertData&,bool>& rLink )
{
    ImplGetSVData()->maGDIData.mxGrfConverter->SetFilterHdl( rLink );
}

const LocaleDataWrapper& Application::GetAppLocaleDataWrapper()
{
    return GetSettings().GetLocaleDataWrapper();
}

void Application::EnableHeadlessMode( bool dialogsAreFatal )
{
    DialogCancelMode eNewMode = dialogsAreFatal ? DialogCancelMode::Fatal : DialogCancelMode::Silent;
    DialogCancelMode eOldMode = GetDialogCancelMode();
    assert(eOldMode == DialogCancelMode::Off || GetDialogCancelMode() == eNewMode);
    if (eOldMode != eNewMode)
        SetDialogCancelMode( eNewMode );
}

bool Application::IsHeadlessModeEnabled()
{
    return IsDialogCancelEnabled() || comphelper::LibreOfficeKit::isActive();
}

void Application::EnableBitmapRendering()
{
    ImplGetSVData()->maAppData.mbRenderToBitmaps = true;
}

bool Application::IsBitmapRendering()
{
    return ImplGetSVData()->maAppData.mbRenderToBitmaps;
}

void Application::EnableConsoleOnly()
{
    EnableHeadlessMode(true);
    EnableBitmapRendering();
}

static bool bSafeMode = false;

bool Application::IsSafeModeEnabled()
{
    return bSafeMode;
}

void Application::EnableSafeMode()
{
    bSafeMode = true;
}

void Application::ShowNativeErrorBox(const OUString& sTitle  ,
                                     const OUString& sMessage)
{
    int btn = ImplGetSalSystem()->ShowNativeMessageBox(
            sTitle,
            sMessage);
    if (btn != SALSYSTEM_SHOWNATIVEMSGBOX_BTN_OK) {
        SAL_WARN( "vcl", "ShowNativeMessageBox returned " << btn);
    }
}

const OUString& Application::GetDesktopEnvironment()
{
    if (IsHeadlessModeEnabled())
    {
        static constexpr OUString aNone(u"none"_ustr);
        return aNone;
    }
    else
        return SalGetDesktopEnvironment();
}

void Application::AddToRecentDocumentList(const OUString& rFileUrl, const OUString& rMimeType, const OUString& rDocumentService)
{
    ImplSVData* pSVData = ImplGetSVData();
    pSVData->mpDefInst->AddToRecentDocumentList(rFileUrl, rMimeType, rDocumentService);
}

// MT: AppEvent was in oldsv.cxx, but is still needed...
void Application::AppEvent( const ApplicationEvent& /*rAppEvent*/ )
{
}

bool Application::hasNativeFileSelection()
{
    ImplSVData* pSVData = ImplGetSVData();
    return pSVData->mpDefInst->hasNativeFileSelection();
}

Reference< ui::dialogs::XFilePicker2 >
Application::createFilePicker( const Reference< uno::XComponentContext >& xSM )
{
    ImplSVData* pSVData = ImplGetSVData();
    return pSVData->mpDefInst->createFilePicker( xSM );
}

Reference< ui::dialogs::XFolderPicker2 >
Application::createFolderPicker( const Reference< uno::XComponentContext >& xSM )
{
    ImplSVData* pSVData = ImplGetSVData();
    return pSVData->mpDefInst->createFolderPicker( xSM );
}

void Application::setDeInitHook(Link<LinkParamNone*,void> const & hook) {
    ImplSVData * pSVData = ImplGetSVData();
    assert(!pSVData->maDeInitHook.IsSet());
    pSVData->maDeInitHook = hook;
    // Fake this for VCLXToolkit ctor instantiated from
    // postprocess/CppunitTest_services.mk:
    pSVData->maAppData.mbInAppMain = true;
}

namespace vcl::lok {

void registerPollCallbacks(
    LibreOfficeKitPollCallback pPollCallback,
    LibreOfficeKitWakeCallback pWakeCallback,
    void *pData) {

    ImplSVData * pSVData = ImplGetSVData();
    if (pSVData)
    {
        pSVData->mpPollCallback = pPollCallback;
        pSVData->mpWakeCallback = pWakeCallback;
        pSVData->mpPollClosure = pData;
    }
}

void unregisterPollCallbacks()
{
    ImplSVData * pSVData = ImplGetSVData();
    if (!pSVData)
        return;

    // Not hyper-elegant - but in the case of Android & unipoll we need to detach
    // this thread from the JVM's clutches to avoid a crash closing document
    if (pSVData->mpPollClosure && pSVData->mpDefInst)
        pSVData->mpDefInst->releaseMainThread();

    // Just set mpPollClosure to null as that is what calling this means, that the callback data
    // points to an object that no longer exists. In particular, don't set
    // pSVData->mpPollCallback to nullptr as that is used to detect whether Unipoll is in use in
    // isUnipoll().
    pSVData->mpPollClosure = nullptr;
}

bool isUnipoll()
{
    ImplSVData * pSVData = ImplGetSVData();
    return pSVData && pSVData->mpPollCallback != nullptr;
}

void numberOfViewsChanged(int count)
{
    if (count == 0)
        return;
    ImplSVData * pSVData = ImplGetSVData();
    auto& rCache = pSVData->maGDIData.maScaleCache;
    // Normally the cache size is set to 10, scale according to the number of users.
    rCache.setMaxSize(count * 10);
}

void dumpState(rtl::OStringBuffer &rState)
{
    ImplSVData* pSVData = ImplGetSVData();
    if (!pSVData)
        return;

#ifndef NDEBUG
    // lo_dumpState deliberately doesn't take SolarMutexGuard
    // so disable these checks during dumpState
    DbgGUIDeInitSolarMutexCheck();
#endif

    rState.append("\nWindows:\t");
    rState.append(static_cast<sal_Int32>(Application::GetTopWindowCount()));

    vcl::Window *pWin = Application::GetFirstTopLevelWindow();
    while (pWin)
    {
        tools::JsonWriter aProps;
        pWin->DumpAsPropertyTree(aProps);

        rState.append("\n\tWindow: ");

        auto notifier = pWin->GetLOKNotifier();
        if (notifier)
        {
            rState.append(notifier->dumpNotifyState());
            rState.append(" ");
        }
        else
            rState.append("no notifier ");

        OString aPropStr = aProps.finishAndGetAsOString();
        if (aPropStr.getLength() > 256)
        {
            rState.append(aPropStr.subView(0, 256));
            rState.append("...");
        } else
            rState.append(aPropStr);

        pWin = Application::GetNextTopLevelWindow( pWin );
    }

    vcl::graphic::MemoryManager::get().dumpState(rState);

    pSVData->dumpState(rState);

#ifndef NDEBUG
    DbgGUIInitSolarMutexCheck();
#endif
}

void trimMemory(int nTarget)
{
    if (nTarget >= 1000)
    {
        ImplSVData* pSVData = ImplGetSVData();
        if (!pSVData) // shutting down
            return;
        pSVData->dropCaches();
        vcl::graphic::MemoryManager::get().reduceAllAndNow();
        // TODO: ideally - free up any deeper dirtied thread stacks.
        // comphelper::ThreadPool::getSharedOptimalPool().shutdown();
    }
    // else for now caches re-fill themselves as/when used.
}

} // namespace lok, namespace vcl

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
