1792 lines
54 KiB
C++
1792 lines
54 KiB
C++
/* -*- 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: */
|