/* -*- 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 <sal/config.h>
#include <limits>
#include <string_view>
#include <com/sun/star/accessibility/AccessibleRelationType.hpp>
#include <com/sun/star/awt/XWindow.hpp>
#include <com/sun/star/awt/XVclWindowPeer.hpp>
#include <o3tl/safeint.hxx>
#include <o3tl/sorted_vector.hxx>
#include <o3tl/string_view.hxx>
#include <officecfg/Office/Common.hxx>
#include <salframe.hxx>
#include <salinst.hxx>
#include <salvd.hxx>
#include <salprn.hxx>
#include <saltimer.hxx>
#include <salsession.hxx>
#include <salsys.hxx>
#include <salbmp.hxx>
#include <salobj.hxx>
#include <salmenu.hxx>
#include <strings.hrc>
#include <svdata.hxx>
#include <svimpbox.hxx>
#include <messagedialog.hxx>
#include <treeglue.hxx>
#include <unotools/accessiblerelationsethelper.hxx>
#include <unotools/configmgr.hxx>
#include <utility>
#include <tools/helpers.hxx>
#include <vcl/abstdlg.hxx>
#include <vcl/builder.hxx>
#include <vcl/toolkit/combobox.hxx>
#include <vcl/toolkit/dialog.hxx>
#include <vcl/toolkit/fixed.hxx>
#include <vcl/toolkit/fmtfield.hxx>
#include <vcl/headbar.hxx>
#include <vcl/toolkit/ivctrl.hxx>
#include <vcl/layout.hxx>
#include <vcl/toolkit/MenuButton.hxx>
#include <vcl/ptrstyle.hxx>
#include <slider.hxx>
#include <vcl/sysdata.hxx>
#include <vcl/toolkit/svlbitm.hxx>
#include <vcl/toolkit/svtabbx.hxx>
#include <vcl/tabctrl.hxx>
#include <vcl/tabpage.hxx>
#include <vcl/toolbox.hxx>
#include <vcl/toolkit/treelistentry.hxx>
#include <vcl/toolkit/throbber.hxx>
#include <vcl/toolkit/unowrap.hxx>
#include <vcl/weld.hxx>
#include <vcl/weldutils.hxx>
#include <vcl/toolkit/vclmedit.hxx>
#include <vcl/toolkit/viewdataentry.hxx>
#include <vcl/virdev.hxx>
#include <bitmaps.hlst>
#include <listbox.hxx>
#include <menutogglebutton.hxx>
#include <window.h>
#include <wizdlg.hxx>
#include <salvtables.hxx>
#include <comphelper/lok.hxx>
SalFrame::SalFrame()
: m_pWindow(nullptr)
, m_pProc(nullptr)
{
}
// this file contains the virtual destructors of the sal interface
// compilers usually put their vtables where the destructor is
SalFrame::~SalFrame() {}
void SalFrame::SetCallback(vcl::Window* pWindow, SALFRAMEPROC pProc)
{
m_pWindow = pWindow;
m_pProc = pProc;
}
// default to full-frame flushes
// on ports where partial-flushes are much cheaper this method should be overridden
void SalFrame::Flush(
const tools::Rectangle&) { Flush(); }
void SalFrame::SetRepresentedURL(
const OUString&)
{
// currently this is Mac only functionality
}
OUString SalFrame::DumpSetPosSize(tools::
Long nX, tools::
Long nY, tools::
Long nWidth,
tools::
Long nHeight, sal_uInt16 nFlags)
{
// assuming the 4 integers normally don't have more than 4 digits, but might be negative
OUStringBuffer aBuffer(4 * 5 + 5);
if (nFlags & SAL_FRAME_POSSIZE_WIDTH)
aBuffer << nWidth <<
"x" ;
else
aBuffer <<
"?x" ;
if (nFlags & SAL_FRAME_POSSIZE_HEIGHT)
aBuffer << nHeight <<
"@(" ;
else
aBuffer <<
"?@(" ;
if (nFlags & SAL_FRAME_POSSIZE_X)
aBuffer << nX <<
"," ;
else
aBuffer <<
"?," ;
if (nFlags & SAL_FRAME_POSSIZE_Y)
aBuffer << nY <<
")" ;
else
aBuffer <<
"?)" ;
return aBuffer.makeStringAndClear();
}
SalInstance::SalInstance(std::unique_ptr<comphelper::SolarMutex> pMutex)
: m_pYieldMutex(std::move(pMutex))
{
}
SalInstance::~SalInstance() {}
comphelper::SolarMutex* SalInstance::GetYieldMutex() {
return m_pYieldMutex.get(); }
sal_uInt32 SalInstance::ReleaseYieldMutex(
bool all) {
return m_pYieldMutex->release(al
l); }
void SalInstance::AcquireYieldMutex(sal_uInt32 nCount) { m_pYieldMutex->acquire(nCount); }
std::unique_ptr<SalSession> SalInstance::CreateSalSession() { return nullptr; }
OpenGLContext* SalInstance::CreateOpenGLContext()
{
assert(!m_bSupportsOpenGL);
std::abort();
}
std::unique_ptr<SalMenu> SalInstance::CreateMenu(bool , Menu*)
{
// default: no native menus
return nullptr;
}
std::unique_ptr<SalMenuItem> SalInstance::CreateMenuItem(const SalItemParams&) { return nullptr; }
bool SalInstance::CallEventCallback(void const * pEvent, int nBytes)
{
return m_pEventInst.is() && m_pEventInst->dispatchEvent(pEvent, nBytes);
}
bool SalInstance::DoExecute(int &)
{
// can't run on system event loop without implementing DoExecute and DoQuit
if (Application::IsUseSystemEventLoop())
std::abort();
return false ;
}
void SalInstance::DoQuit()
{
if (Application::IsUseSystemEventLoop())
std::abort();
}
SalTimer::~SalTimer() COVERITY_NOEXCEPT_FALSE {}
void SalBitmap::DropScaledCache()
{
if (ImplSVData* pSVData = ImplGetSVData())
{
auto & rCache = pSVData->maGDIData.maScaleCache;
rCache.remove_if([this ](const lru_scale_cache::key_value_pair_t& rKeyValuePair) {
return rKeyValuePair.first.mpBitmap == this ;
});
}
}
SalBitmap::~SalBitmap() { DropScaledCache(); }
SalSystem::~SalSystem() {}
SalPrinter::~SalPrinter() {}
bool SalPrinter::StartJob(const OUString*, const OUString&, const OUString&, ImplJobSetup*,
vcl::PrinterController&)
{
return false ;
}
SalInfoPrinter::~SalInfoPrinter() {}
SalVirtualDevice::~SalVirtualDevice() {}
SalObject::~SalObject() {}
SalMenu::~SalMenu() {}
void SalMenu::GetSystemMenuData(SystemMenuData&) {}
bool SalMenu::ShowNativePopupMenu(FloatingWindow*, const tools::Rectangle&, FloatWinPopupFlags)
{
return false ;
}
void SalMenu::ShowCloseButton(bool ) {}
bool SalMenu::AddMenuBarButton(const SalMenuButtonItem&) { return false ; }
void SalMenu::RemoveMenuBarButton(sal_uInt16) {}
tools::Rectangle SalMenu::GetMenuBarButtonRectPixel(sal_uInt16, SalFrame*)
{
return tools::Rectangle();
}
int SalMenu::GetMenuBarHeight() const { return 0; }
SalMenuItem::~SalMenuItem() {}
class SalFlashAttention
{
private :
VclPtr<vcl::Window> m_xWidget;
Timer m_aFlashTimer;
Color m_aOrigControlBackground;
Wallpaper m_aOrigBackground;
bool m_bOrigControlBackground;
int m_nFlashCount;
void SetFlash()
{
Color aColor(Application::GetSettings().GetStyleSettings().GetHighlightColor());
m_xWidget->SetControlBackground(aColor);
}
void ClearFlash()
{
if (m_bOrigControlBackground)
m_xWidget->SetControlBackground(m_aOrigControlBackground);
else
m_xWidget->SetControlBackground();
}
void Flash()
{
constexpr int FlashesWanted = 1;
if (m_nFlashCount % 2 == 0)
ClearFlash();
else
SetFlash();
if (m_nFlashCount == FlashesWanted * 2)
return ;
++m_nFlashCount;
m_aFlashTimer.Start();
}
DECL_LINK(FlashTimeout, Timer*, void );
public :
SalFlashAttention(VclPtr<vcl::Window> xWidget)
: m_xWidget(std::move(xWidget))
, m_aFlashTimer("SalFlashAttention" )
, m_bOrigControlBackground(false )
, m_nFlashCount(1)
{
m_aFlashTimer.SetTimeout(150);
m_aFlashTimer.SetInvokeHandler(LINK(this , SalFlashAttention, FlashTimeout));
}
void Start()
{
m_bOrigControlBackground = m_xWidget->IsControlBackground();
if (m_bOrigControlBackground)
m_aOrigControlBackground = m_xWidget->GetControlBackground();
m_aFlashTimer.Start();
}
~SalFlashAttention() { ClearFlash(); }
};
IMPL_LINK_NOARG(SalFlashAttention, FlashTimeout, Timer*, void ) { Flash(); }
void SalInstanceWidget::ensure_event_listener()
{
if (!m_bEventListener)
{
m_xWidget->AddEventListener(LINK(this , SalInstanceWidget, EventListener));
m_bEventListener = true ;
}
}
// we want the ability to mark key events as handled, so use this variant
// for those, we get all keystrokes in this case, so we will need to filter
// them later
void SalInstanceWidget::ensure_key_listener()
{
if (!m_bKeyEventListener)
{
Application::AddKeyListener(LINK(this , SalInstanceWidget, KeyEventListener));
m_bKeyEventListener = true ;
}
}
// we want the ability to know about mouse events that happen in our children
// so use this variant, we will need to filter them later
void SalInstanceWidget::ensure_mouse_listener()
{
if (!m_bMouseEventListener)
{
m_xWidget->AddChildEventListener(LINK(this , SalInstanceWidget, MouseEventListener));
m_bMouseEventListener = true ;
}
}
void SalInstanceWidget::set_background(const Color& rColor)
{
m_xWidget->SetControlBackground(rColor);
m_xWidget->SetBackground(m_xWidget->GetControlBackground());
if (m_xWidget->GetStyle() & WB_CLIPCHILDREN)
{
// turn off WB_CLIPCHILDREN otherwise the bg won't extend "under"
// transparent children of the widget e.g. expander in sidebar panel header
m_xWidget->SetStyle(m_xWidget->GetStyle() & ~WB_CLIPCHILDREN);
// and toggle mbClipChildren on instead otherwise the bg won't fill e.g.
// deck titlebar header when its width is stretched
WindowImpl* pImpl = m_xWidget->ImplGetWindowImpl();
pImpl->mbClipChildren = true ;
}
}
void SalInstanceWidget::set_background()
{
m_xWidget->SetControlBackground();
m_xWidget->SetBackground();
}
SalInstanceWidget::SalInstanceWidget(vcl::Window* pWidget, SalInstanceBuilder* pBuilder,
bool bTakeOwnership)
: m_xWidget(pWidget)
, m_pBuilder(pBuilder)
, m_bTakeOwnership(bTakeOwnership)
, m_bEventListener(false )
, m_bKeyEventListener(false )
, m_bMouseEventListener(false )
, m_nBlockNotify(0)
, m_nFreezeCount(0)
{
}
void SalInstanceWidget::set_sensitive(bool sensitive) { m_xWidget->Enable(sensitive); }
bool SalInstanceWidget::get_sensitive() const { return m_xWidget->IsEnabled(); }
bool SalInstanceWidget::get_visible() const { return m_xWidget->IsVisible(); }
bool SalInstanceWidget::is_visible() const { return m_xWidget->IsReallyVisible(); }
void SalInstanceWidget::set_can_focus(bool bCanFocus)
{
auto nStyle = m_xWidget->GetStyle() & ~(WB_TABSTOP | WB_NOTABSTOP);
if (bCanFocus)
nStyle |= WB_TABSTOP;
else
nStyle |= WB_NOTABSTOP;
m_xWidget->SetStyle(nStyle);
}
void SalInstanceWidget::grab_focus()
{
if (has_focus())
return ;
m_xWidget->GrabFocus();
}
bool SalInstanceWidget::has_focus() const { return m_xWidget->HasFocus(); }
bool SalInstanceWidget::is_active() const { return m_xWidget->IsActive(); }
bool SalInstanceWidget::has_child_focus() const { return m_xWidget->HasChildPathFocus(true ); }
void SalInstanceWidget::show() { m_xWidget->Show(); }
void SalInstanceWidget::hide() { m_xWidget->Hide(); }
void SalInstanceWidget::set_size_request(int nWidth, int nHeight)
{
m_xWidget->set_width_request(nWidth);
m_xWidget->set_height_request(nHeight);
}
Size SalInstanceWidget::get_size_request() const
{
return Size(m_xWidget->get_width_request(), m_xWidget->get_height_request());
}
Size SalInstanceWidget::get_preferred_size() const { return m_xWidget->get_preferred_size(); }
float SalInstanceWidget::get_approximate_digit_width() const
{
return m_xWidget->approximate_digit_width();
}
int SalInstanceWidget::get_text_height() const { return m_xWidget->GetTextHeight(); }
Size SalInstanceWidget::get_pixel_size(const OUString& rText) const
{
//TODO, or do I want GetTextBoundRect ?, just using width at the moment anyway
return Size(m_xWidget->GetTextWidth(rText), m_xWidget->GetTextHeight());
}
vcl::Font SalInstanceWidget::get_font() { return m_xWidget->GetPointFont(*m_xWidget->GetOutDev()); }
OUString SalInstanceWidget::get_buildable_name() const { return m_xWidget->get_id(); }
void SalInstanceWidget::set_buildable_name(const OUString& rId) { return m_xWidget->set_id(rId); }
void SalInstanceWidget::set_help_id(const OUString& rId) { return m_xWidget->SetHelpId(rId); }
OUString SalInstanceWidget::get_help_id() const { return m_xWidget->GetHelpId(); }
void SalInstanceWidget::set_hexpand(bool bExpand) { m_xWidget->set_hexpand(bExpand); }
bool SalInstanceWidget::get_hexpand() const { return m_xWidget->get_hexpand(); }
void SalInstanceWidget::set_vexpand(bool bExpand) { m_xWidget->set_vexpand(bExpand); }
bool SalInstanceWidget::get_vexpand() const { return m_xWidget->get_vexpand(); }
void SalInstanceWidget::set_margin_top(int nMargin) { m_xWidget->set_margin_top(nMargin); }
void SalInstanceWidget::set_margin_bottom(int nMargin) { m_xWidget->set_margin_bottom(nMargin); }
void SalInstanceWidget::set_margin_start(int nMargin) { m_xWidget->set_margin_start(nMargin); }
void SalInstanceWidget::set_margin_end(int nMargin) { m_xWidget->set_margin_end(nMargin); }
int SalInstanceWidget::get_margin_top() const { return m_xWidget->get_margin_top(); }
int SalInstanceWidget::get_margin_bottom() const { return m_xWidget->get_margin_bottom(); }
int SalInstanceWidget::get_margin_start() const { return m_xWidget->get_margin_start(); }
int SalInstanceWidget::get_margin_end() const { return m_xWidget->get_margin_end(); }
void SalInstanceWidget::set_accessible_name(const OUString& rName)
{
m_xWidget->SetAccessibleName(rName);
}
void SalInstanceWidget::set_accessible_description(const OUString& rDescription)
{
m_xWidget->SetAccessibleDescription(rDescription);
}
OUString SalInstanceWidget::get_accessible_name() const { return m_xWidget->GetAccessibleName(); }
OUString SalInstanceWidget::get_accessible_description() const
{
return m_xWidget->GetAccessibleDescription();
}
OUString SalInstanceWidget::get_accessible_id() const { return m_xWidget->get_id(); }
void SalInstanceWidget::set_accessible_relation_labeled_by(weld::Widget* pLabel)
{
if (vcl::Window* pOldLabel = m_xWidget->GetAccessibleRelationLabeledBy())
pOldLabel->SetAccessibleRelationLabelFor(nullptr);
vcl::Window* pA11yLabel
= pLabel ? dynamic_cast <SalInstanceWidget&>(*pLabel).getWidget() : nullptr;
m_xWidget->SetAccessibleRelationLabeledBy(pA11yLabel);
if (pA11yLabel)
pA11yLabel->SetAccessibleRelationLabelFor(m_xWidget);
}
void SalInstanceWidget::set_tooltip_text(const OUString& rTip)
{
m_xWidget->SetQuickHelpText(rTip);
}
OUString SalInstanceWidget::get_tooltip_text() const { return m_xWidget->GetQuickHelpText(); }
void SalInstanceWidget::set_cursor_data(void * pData)
{
vcl::Cursor* pCursor = static_cast <vcl::Cursor*>(pData);
if (!pCursor)
return ;
m_xWidget->SetCursor(pCursor);
}
void SalInstanceWidget::connect_focus_in(const Link<Widget&, void >& rLink)
{
ensure_event_listener();
weld::Widget::connect_focus_in(rLink);
}
void SalInstanceWidget::connect_mnemonic_activate(const Link<Widget&, bool >& rLink)
{
m_xWidget->SetMnemonicActivateHdl(LINK(this , SalInstanceWidget, MnemonicActivateHdl));
weld::Widget::connect_mnemonic_activate(rLink);
}
void SalInstanceWidget::connect_focus_out(const Link<Widget&, void >& rLink)
{
ensure_event_listener();
weld::Widget::connect_focus_out(rLink);
}
void SalInstanceWidget::connect_size_allocate(const Link<const Size&, void >& rLink)
{
ensure_event_listener();
weld::Widget::connect_size_allocate(rLink);
}
void SalInstanceWidget::connect_mouse_press(const Link<const MouseEvent&, bool >& rLink)
{
ensure_mouse_listener();
weld::Widget::connect_mouse_press(rLink);
}
void SalInstanceWidget::connect_mouse_move(const Link<const MouseEvent&, bool >& rLink)
{
ensure_mouse_listener();
weld::Widget::connect_mouse_move(rLink);
}
void SalInstanceWidget::connect_mouse_release(const Link<const MouseEvent&, bool >& rLink)
{
ensure_mouse_listener();
weld::Widget::connect_mouse_release(rLink);
}
void SalInstanceWidget::connect_key_press(const Link<const KeyEvent&, bool >& rLink)
{
ensure_key_listener();
weld::Widget::connect_key_press(rLink);
}
void SalInstanceWidget::connect_key_release(const Link<const KeyEvent&, bool >& rLink)
{
ensure_key_listener();
weld::Widget::connect_key_release(rLink);
}
IMPL_LINK(SalInstanceWidget, SettingsChangedHdl, VclWindowEvent&, rEvent, void )
{
if (rEvent.GetId() != VclEventId::WindowDataChanged)
return ;
DataChangedEvent* pData = static_cast <DataChangedEvent*>(rEvent.GetData());
if (pData->GetType() == DataChangedEventType::SETTINGS)
m_aStyleUpdatedHdl.Call(*this );
}
void SalInstanceWidget::connect_style_updated(const Link<Widget&, void >& rLink)
{
if (m_aStyleUpdatedHdl.IsSet())
m_xWidget->RemoveEventListener(LINK(this , SalInstanceWidget, SettingsChangedHdl));
weld::Widget::connect_style_updated(rLink);
if (m_aStyleUpdatedHdl.IsSet())
m_xWidget->AddEventListener(LINK(this , SalInstanceWidget, SettingsChangedHdl));
}
bool SalInstanceWidget::get_extents_relative_to(const Widget& rRelative, int & x, int & y, int & width,
int & height) const
{
tools::Rectangle aRect(m_xWidget->GetWindowExtentsRelative(
*dynamic_cast <const SalInstanceWidget&>(rRelative).getWidget()));
x = aRect.Left();
y = aRect.Top();
width = aRect.GetWidth();
height = aRect.GetHeight();
return true ;
}
void SalInstanceWidget::grab_mouse() { m_xWidget->CaptureMouse(); }
bool SalInstanceWidget::has_mouse_grab() const { return m_xWidget->IsMouseCaptured(); }
void SalInstanceWidget::release_mouse() { m_xWidget->ReleaseMouse(); }
bool SalInstanceWidget::get_direction() const { return m_xWidget->IsRTLEnabled(); }
void SalInstanceWidget::set_direction(bool bRTL) { m_xWidget->EnableRTL(bRTL); }
void SalInstanceWidget::freeze()
{
if (m_nFreezeCount == 0)
m_xWidget->SetUpdateMode(false );
++m_nFreezeCount;
}
void SalInstanceWidget::thaw()
{
--m_nFreezeCount;
if (m_nFreezeCount == 0)
m_xWidget->SetUpdateMode(true );
}
void SalInstanceWidget::set_busy_cursor(bool bBusy)
{
if (!m_xWidget)
{
return ;
}
if (bBusy)
m_xWidget->EnterWait();
else
m_xWidget->LeaveWait();
}
void SalInstanceWidget::queue_resize() { m_xWidget->queue_resize(); }
SalInstanceWidget::~SalInstanceWidget()
{
if (m_aStyleUpdatedHdl.IsSet())
m_xWidget->RemoveEventListener(LINK(this , SalInstanceWidget, SettingsChangedHdl));
if (m_aMnemonicActivateHdl.IsSet())
m_xWidget->SetMnemonicActivateHdl(Link<vcl::Window&, bool >());
if (m_bMouseEventListener)
m_xWidget->RemoveChildEventListener(LINK(this , SalInstanceWidget, MouseEventListener));
if (m_bKeyEventListener)
Application::RemoveKeyListener(LINK(this , SalInstanceWidget, KeyEventListener));
if (m_bEventListener)
m_xWidget->RemoveEventListener(LINK(this , SalInstanceWidget, EventListener));
if (m_bTakeOwnership)
m_xWidget.disposeAndClear();
}
vcl::Window* SalInstanceWidget::getWidget() const { return m_xWidget; }
void SalInstanceWidget::disable_notify_events() { ++m_nBlockNotify; }
bool SalInstanceWidget::notify_events_disabled() const { return m_nBlockNotify != 0; }
void SalInstanceWidget::enable_notify_events() { --m_nBlockNotify; }
OUString SalInstanceWidget::strip_mnemonic(const OUString& rLabel) const
{
return rLabel.replaceFirst("~" , "" );
}
OUString SalInstanceWidget::escape_ui_str(const OUString& rLabel) const
{
return rLabel.replaceAll("~" , "~~" );
}
VclPtr<VirtualDevice> SalInstanceWidget::create_virtual_device() const
{
// create with (annoying) separate alpha layer that LibreOffice itself uses
return VclPtr<VirtualDevice>::Create(*Application::GetDefaultDevice(),
DeviceFormat::WITH_ALPHA);
}
void SalInstanceWidget::call_attention_to()
{
m_xFlashAttention.reset(new SalFlashAttention(m_xWidget));
m_xFlashAttention->Start();
}
css::uno::Reference<css::datatransfer::dnd::XDropTarget> SalInstanceWidget::get_drop_target()
{
return m_xWidget->GetDropTarget();
}
css::uno::Reference<css::datatransfer::clipboard::XClipboard>
SalInstanceWidget::get_clipboard() const
{
return m_xWidget->GetClipboard();
}
void SalInstanceWidget::connect_get_property_tree(const Link<tools::JsonWriter&, void >& rLink)
{
m_xWidget->SetDumpAsPropertyTreeHdl(rLink);
}
void SalInstanceWidget::get_property_tree(tools::JsonWriter& rJsonWriter)
{
m_xWidget->DumpAsPropertyTree(rJsonWriter);
}
void SalInstanceWidget::set_stack_background()
{
set_background(m_xWidget->GetSettings().GetStyleSettings().GetFieldColor());
}
void SalInstanceWidget::set_title_background()
{
set_background(m_xWidget->GetSettings().GetStyleSettings().GetShadowColor());
}
void SalInstanceWidget::set_toolbar_background()
{
m_xWidget->SetBackground();
m_xWidget->SetPaintTransparent(true );
}
void SalInstanceWidget::set_highlight_background()
{
set_background(m_xWidget->GetSettings().GetStyleSettings().GetHighlightColor());
}
SystemWindow* SalInstanceWidget::getSystemWindow() { return m_xWidget->GetSystemWindow(); }
void SalInstanceWidget::HandleEventListener(VclWindowEvent& rEvent)
{
if (rEvent.GetId() == VclEventId::WindowGetFocus)
signal_focus_in();
else if (rEvent.GetId() == VclEventId::WindowLoseFocus)
signal_focus_out();
else if (rEvent.GetId() == VclEventId::WindowResize)
signal_size_allocate(m_xWidget->GetSizePixel());
}
namespace
{
MouseEvent TransformEvent(const MouseEvent& rEvent, const vcl::Window* pParent,
const vcl::Window* pChild)
{
return MouseEvent(
pParent->ScreenToOutputPixel(pChild->OutputToScreenPixel(rEvent.GetPosPixel())),
rEvent.GetClicks(), rEvent.GetMode(), rEvent.GetButtons(), rEvent.GetModifier());
}
}
void SalInstanceWidget::HandleMouseEventListener(VclWindowEvent& rWinEvent)
{
if (rWinEvent.GetId() == VclEventId::WindowMouseButtonDown)
{
if (m_xWidget == rWinEvent.GetWindow())
{
const MouseEvent* pMouseEvent = static_cast <const MouseEvent*>(rWinEvent.GetData());
signal_mouse_press(*pMouseEvent);
}
else if (m_xWidget->ImplIsChild(rWinEvent.GetWindow()))
{
const MouseEvent* pMouseEvent = static_cast <const MouseEvent*>(rWinEvent.GetData());
const MouseEvent aTransformedEvent(
TransformEvent(*pMouseEvent, m_xWidget, rWinEvent.GetWindow()));
signal_mouse_press(aTransformedEvent);
}
}
else if (rWinEvent.GetId() == VclEventId::WindowMouseButtonUp)
{
if (m_xWidget == rWinEvent.GetWindow())
{
const MouseEvent* pMouseEvent = static_cast <const MouseEvent*>(rWinEvent.GetData());
signal_mouse_release(*pMouseEvent);
}
else if (m_xWidget->ImplIsChild(rWinEvent.GetWindow()))
{
const MouseEvent* pMouseEvent = static_cast <const MouseEvent*>(rWinEvent.GetData());
const MouseEvent aTransformedEvent(
TransformEvent(*pMouseEvent, m_xWidget, rWinEvent.GetWindow()));
signal_mouse_release(aTransformedEvent);
}
}
else if (rWinEvent.GetId() == VclEventId::WindowMouseMove)
{
if (m_xWidget == rWinEvent.GetWindow())
{
const MouseEvent* pMouseEvent = static_cast <const MouseEvent*>(rWinEvent.GetData());
signal_mouse_motion(*pMouseEvent);
}
else if (m_xWidget->ImplIsChild(rWinEvent.GetWindow()))
{
const MouseEvent* pMouseEvent = static_cast <const MouseEvent*>(rWinEvent.GetData());
const MouseEvent aTransformedEvent(
TransformEvent(*pMouseEvent, m_xWidget, rWinEvent.GetWindow()));
signal_mouse_motion(aTransformedEvent);
}
}
}
bool SalInstanceWidget::HandleKeyEventListener(VclWindowEvent& rEvent)
{
// we get all key events here, ignore them unless we have focus
if (!m_xWidget->HasChildPathFocus())
return false ;
if (rEvent.GetId() == VclEventId::WindowKeyInput)
{
const KeyEvent* pKeyEvent = static_cast <const KeyEvent*>(rEvent.GetData());
return signal_key_press(*pKeyEvent);
}
else if (rEvent.GetId() == VclEventId::WindowKeyUp)
{
const KeyEvent* pKeyEvent = static_cast <const KeyEvent*>(rEvent.GetData());
return signal_key_release(*pKeyEvent);
}
return false ;
}
IMPL_LINK(SalInstanceWidget, EventListener, VclWindowEvent&, rEvent, void )
{
HandleEventListener(rEvent);
}
IMPL_LINK(SalInstanceWidget, KeyEventListener, VclWindowEvent&, rEvent, bool )
{
return HandleKeyEventListener(rEvent);
}
IMPL_LINK(SalInstanceWidget, MouseEventListener, VclWindowEvent&, rEvent, void )
{
HandleMouseEventListener(rEvent);
}
IMPL_LINK_NOARG(SalInstanceWidget, MnemonicActivateHdl, vcl::Window&, bool )
{
return signal_mnemonic_activate();
}
namespace
{
Image createImage(const OUString& rImage)
{
if (rImage.isEmpty())
return Image();
if (rImage.lastIndexOf('.' ) != rImage.getLength() - 4)
{
assert((rImage == "dialog-warning" || rImage == "dialog-error"
|| rImage == "dialog-information" )
&& "unknown stock image" );
if (rImage == "dialog-warning" )
return Image(StockImage::Yes, IMG_WARN);
else if (rImage == "dialog-error" )
return Image(StockImage::Yes, IMG_ERROR);
else if (rImage == "dialog-information" )
return Image(StockImage::Yes, IMG_INFO);
}
return Image(StockImage::Yes, rImage);
}
Image createImage(const VirtualDevice& rDevice)
{
return Image(rDevice.GetBitmapEx(Point(), rDevice.GetOutputSizePixel()));
}
sal_uInt16 insert_to_menu(sal_uInt16 nLastId, PopupMenu* pMenu, int pos, const OUString& rId,
const OUString& rStr, const OUString* pIconName,
const VirtualDevice* pImageSurface,
const css::uno::Reference<css::graphic::XGraphic>& rImage,
TriState eCheckRadioFalse)
{
const sal_uInt16 nNewid = nLastId + 1;
MenuItemBits nBits;
if (eCheckRadioFalse == TRISTATE_TRUE)
nBits = MenuItemBits::CHECKABLE;
else if (eCheckRadioFalse == TRISTATE_FALSE)
nBits = MenuItemBits::CHECKABLE | MenuItemBits::RADIOCHECK;
else
nBits = MenuItemBits::NONE;
pMenu->InsertItem(nNewid, rStr, nBits, rId, pos == -1 ? MENU_APPEND : pos);
if (pIconName)
{
pMenu->SetItemImage(nNewid, createImage(*pIconName));
}
else if (pImageSurface)
{
pMenu->SetItemImage(nNewid, createImage(*pImageSurface));
}
else if (rImage)
{
pMenu->SetItemImage(nNewid, Image(rImage));
}
return nNewid;
}
}
SalInstanceMenu::SalInstanceMenu(PopupMenu* pMenu, bool bTakeOwnership)
: m_xMenu(pMenu)
, m_bTakeOwnership(bTakeOwnership)
{
const auto nCount = m_xMenu->GetItemCount();
m_nLastId = nCount ? pMenu->GetItemId(nCount - 1) : 0;
m_xMenu->SetSelectHdl(LINK(this , SalInstanceMenu, SelectMenuHdl));
}
OUString SalInstanceMenu::popup_at_rect(weld::Widget* pParent, const tools::Rectangle& rRect,
weld::Placement ePlace)
{
SalInstanceWidget* pVclWidget = dynamic_cast <SalInstanceWidget*>(pParent);
assert(pVclWidget);
PopupMenuFlags eFlags = PopupMenuFlags::NoMouseUpClose;
if (ePlace == weld::Placement::Under)
eFlags = eFlags | PopupMenuFlags::ExecuteDown;
else
eFlags = eFlags | PopupMenuFlags::ExecuteRight;
m_xMenu->Execute(pVclWidget->getWidget(), rRect, eFlags);
return m_xMenu->GetCurItemIdent();
}
void SalInstanceMenu::set_sensitive(const OUString& rIdent, bool bSensitive)
{
m_xMenu->EnableItem(rIdent, bSensitive);
}
bool SalInstanceMenu::get_sensitive(const OUString& rIdent) const
{
return m_xMenu->IsItemEnabled(m_xMenu->GetItemId(rIdent));
}
void SalInstanceMenu::set_active(const OUString& rIdent, bool bActive)
{
m_xMenu->CheckItem(rIdent, bActive);
}
bool SalInstanceMenu::get_active(const OUString& rIdent) const
{
return m_xMenu->IsItemChecked(m_xMenu->GetItemId(rIdent));
}
void SalInstanceMenu::set_label(const OUString& rIdent, const OUString& rLabel)
{
m_xMenu->SetItemText(m_xMenu->GetItemId(rIdent), rLabel);
}
OUString SalInstanceMenu::get_label(const OUString& rIdent) const
{
return m_xMenu->GetItemText(m_xMenu->GetItemId(rIdent));
}
void SalInstanceMenu::set_visible(const OUString& rIdent, bool bShow)
{
m_xMenu->ShowItem(m_xMenu->GetItemId(rIdent), bShow);
}
void SalInstanceMenu::clear() { m_xMenu->Clear(); }
void SalInstanceMenu::insert(int pos, const OUString& rId, const OUString& rStr,
const OUString* pIconName, VirtualDevice* pImageSurface,
const css::uno::Reference<css::graphic::XGraphic>& rImage,
TriState eCheckRadioFalse)
{
m_nLastId = insert_to_menu(m_nLastId, m_xMenu, pos, rId, rStr, pIconName, pImageSurface, rImage,
eCheckRadioFalse);
}
void SalInstanceMenu::insert_separator(int pos, const OUString& rId)
{
auto nInsertPos = pos == -1 ? MENU_APPEND : pos;
m_xMenu->InsertSeparator(rId, nInsertPos);
}
// Defines the help id of the item in a given position
void SalInstanceMenu::set_item_help_id(const OUString& rIdent, const OUString& rHelpId)
{
m_xMenu->SetHelpId(m_xMenu->GetItemId(rIdent), rHelpId);
}
void SalInstanceMenu::remove(const OUString& rId)
{
m_xMenu->RemoveItem(m_xMenu->GetItemPos(m_xMenu->GetItemId(rId)));
}
int SalInstanceMenu::n_children() const { return m_xMenu->GetItemCount(); }
OUString SalInstanceMenu::get_id(int pos) const
{
return m_xMenu->GetItemIdent(m_xMenu->GetItemId(pos));
}
PopupMenu* SalInstanceMenu::getMenu() const { return m_xMenu.get(); }
SalInstanceMenu::~SalInstanceMenu()
{
m_xMenu->SetSelectHdl(Link<::Menu*, bool >());
if (m_bTakeOwnership)
m_xMenu.disposeAndClear();
}
IMPL_LINK_NOARG(SalInstanceMenu, SelectMenuHdl, ::Menu*, bool )
{
signal_activate(m_xMenu->GetCurItemIdent());
/* tdf#131333 Menu::Select depends on a false here to allow
propagating a submens's selected id to its parent menu to become its
selected id.
without this, while gen menus already have propagated this to its parent
in MenuFloatingWindow::EndExecute, SalMenus as used under kf5/macOS
won't propagate the selected id
*/
return false ;
}
SalInstanceToolbar::SalInstanceToolbar(ToolBox* pToolBox, SalInstanceBuilder* pBuilder,
bool bTakeOwnership)
: SalInstanceWidget(pToolBox, pBuilder, bTakeOwnership)
, m_xToolBox(pToolBox)
{
m_xToolBox->SetSelectHdl(LINK(this , SalInstanceToolbar, ClickHdl));
m_xToolBox->SetDropdownClickHdl(LINK(this , SalInstanceToolbar, DropdownClick));
}
void SalInstanceToolbar::set_item_sensitive(const OUString& rIdent, bool bSensitive)
{
m_xToolBox->EnableItem(m_xToolBox->GetItemId(rIdent), bSensitive);
}
bool SalInstanceToolbar::get_item_sensitive(const OUString& rIdent) const
{
return m_xToolBox->IsItemEnabled(m_xToolBox->GetItemId(rIdent));
}
void SalInstanceToolbar::set_item_visible(const OUString& rIdent, bool bVisible)
{
m_xToolBox->ShowItem(m_xToolBox->GetItemId(rIdent), bVisible);
}
void SalInstanceToolbar::set_item_help_id(const OUString& rIdent, const OUString& rHelpId)
{
m_xToolBox->SetHelpId(m_xToolBox->GetItemId(rIdent), rHelpId);
}
bool SalInstanceToolbar::get_item_visible(const OUString& rIdent) const
{
return m_xToolBox->IsItemVisible(m_xToolBox->GetItemId(rIdent));
}
void SalInstanceToolbar::set_item_active(const OUString& rIdent, bool bActive)
{
ToolBoxItemId nItemId = m_xToolBox->GetItemId(rIdent);
m_xToolBox->CheckItem(nItemId, bActive);
}
bool SalInstanceToolbar::get_item_active(const OUString& rIdent) const
{
return m_xToolBox->IsItemChecked(m_xToolBox->GetItemId(rIdent));
}
void SalInstanceToolbar::set_menu_item_active(const OUString& rIdent, bool bActive)
{
ToolBoxItemId nItemId = m_xToolBox->GetItemId(rIdent);
assert(m_xToolBox->GetItemBits(nItemId) & ToolBoxItemBits::DROPDOWN);
if (bActive)
{
m_sStartShowIdent = m_xToolBox->GetItemCommand(nItemId);
signal_toggle_menu(m_sStartShowIdent);
}
auto pFloat = m_aFloats[nItemId];
if (pFloat)
{
if (bActive)
vcl::Window::GetDockingManager()->StartPopupMode(m_xToolBox, pFloat,
FloatWinPopupFlags::GrabFocus);
else
vcl::Window::GetDockingManager()->EndPopupMode(pFloat);
}
auto pPopup = m_aMenus[nItemId];
if (pPopup)
{
if (bActive)
{
MenuFlags nMenuFlags = pPopup->GetMenuFlags();
if (officecfg::Office::Common::View::Menu::DontHideDisabledEntry::get())
nMenuFlags &= ~MenuFlags::HideDisabledEntries;
else
nMenuFlags |= MenuFlags::HideDisabledEntries;
pPopup->SetMenuFlags(nMenuFlags);
tools::Rectangle aRect = m_xToolBox->GetItemRect(nItemId);
pPopup->Execute(m_xToolBox, aRect, PopupMenuFlags::ExecuteDown);
}
else
pPopup->EndExecute();
}
m_sStartShowIdent.clear();
}
bool SalInstanceToolbar::get_menu_item_active(const OUString& rIdent) const
{
ToolBoxItemId nItemId = m_xToolBox->GetItemId(rIdent);
assert(m_xToolBox->GetItemBits(nItemId) & ToolBoxItemBits::DROPDOWN);
if (rIdent == m_sStartShowIdent)
return true ;
auto aFloat = m_aFloats.find(nItemId);
if (aFloat != m_aFloats.end())
{
return vcl::Window::GetDockingManager()->IsInPopupMode(aFloat->second);
}
auto aPopup = m_aMenus.find(nItemId);
if (aPopup != m_aMenus.end())
{
return PopupMenu::GetActivePopupMenu() == aPopup->second;
}
return false ;
}
void SalInstanceToolbar::set_item_popover(const OUString& rIdent, weld::Widget* pPopover)
{
SalInstanceWidget* pPopoverWidget = dynamic_cast <SalInstanceWidget*>(pPopover);
vcl::Window* pFloat = pPopoverWidget ? pPopoverWidget->getWidget() : nullptr;
if (pFloat)
{
pFloat->AddEventListener(LINK(this , SalInstanceToolbar, MenuToggleListener));
pFloat->EnableDocking();
}
ToolBoxItemId nId = m_xToolBox->GetItemId(rIdent);
auto xOldFloat = m_aFloats[nId];
if (xOldFloat)
{
xOldFloat->RemoveEventListener(LINK(this , SalInstanceToolbar, MenuToggleListener));
}
m_aFloats[nId] = pFloat;
m_aMenus[nId] = nullptr;
}
void SalInstanceToolbar::set_item_menu(const OUString& rIdent, weld::Menu* pMenu)
{
SalInstanceMenu* pInstanceMenu = dynamic_cast <SalInstanceMenu*>(pMenu);
PopupMenu* pPopup = pInstanceMenu ? pInstanceMenu->getMenu() : nullptr;
ToolBoxItemId nId = m_xToolBox->GetItemId(rIdent);
m_aMenus[nId] = pPopup;
m_aFloats[nId] = nullptr;
}
void SalInstanceToolbar::insert_item(int pos, const OUString& rId)
{
ToolBoxItemId nId(pos);
m_xToolBox->InsertItem(nId, OUString(), rId, ToolBoxItemBits::ICON_ONLY);
}
void SalInstanceToolbar::insert_separator(int pos, const OUString& /*rId*/)
{
auto nInsertPos = pos == -1 ? ToolBox::APPEND : pos;
m_xToolBox->InsertSeparator(nInsertPos, 5);
}
int SalInstanceToolbar::get_n_items() const { return m_xToolBox->GetItemCount(); }
OUString SalInstanceToolbar::get_item_ident(int nIndex) const
{
return m_xToolBox->GetItemCommand(m_xToolBox->GetItemId(nIndex));
}
void SalInstanceToolbar::set_item_ident(int nIndex, const OUString& rIdent)
{
return m_xToolBox->SetItemCommand(m_xToolBox->GetItemId(nIndex), rIdent);
}
void SalInstanceToolbar::set_item_label(int nIndex, const OUString& rLabel)
{
m_xToolBox->SetItemText(m_xToolBox->GetItemId(nIndex), rLabel);
}
OUString SalInstanceToolbar::get_item_label(const OUString& rIdent) const
{
return m_xToolBox->GetItemText(m_xToolBox->GetItemId(rIdent));
}
void SalInstanceToolbar::set_item_label(const OUString& rIdent, const OUString& rLabel)
{
m_xToolBox->SetItemText(m_xToolBox->GetItemId(rIdent), rLabel);
}
void SalInstanceToolbar::set_item_icon_name(const OUString& rIdent, const OUString& rIconName)
{
m_xToolBox->SetItemImage(m_xToolBox->GetItemId(rIdent), Image(StockImage::Yes, rIconName));
}
void SalInstanceToolbar::set_item_image_mirrored(const OUString& rIdent, bool bMirrored)
{
m_xToolBox->SetItemImageMirrorMode(m_xToolBox->GetItemId(rIdent), bMirrored);
}
void SalInstanceToolbar::set_item_image(const OUString& rIdent,
const css::uno::Reference<css::graphic::XGraphic>& rIcon)
{
m_xToolBox->SetItemImage(m_xToolBox->GetItemId(rIdent), Image(rIcon));
}
void SalInstanceToolbar::set_item_image(const OUString& rIdent, VirtualDevice* pDevice)
{
if (pDevice)
m_xToolBox->SetItemImage(m_xToolBox->GetItemId(rIdent), createImage(*pDevice));
else
m_xToolBox->SetItemImage(m_xToolBox->GetItemId(rIdent), Image());
}
void SalInstanceToolbar::set_item_image(int nIndex,
const css::uno::Reference<css::graphic::XGraphic>& rIcon)
{
m_xToolBox->SetItemImage(m_xToolBox->GetItemId(nIndex), Image(rIcon));
}
void SalInstanceToolbar::set_item_tooltip_text(int nIndex, const OUString& rTip)
{
m_xToolBox->SetQuickHelpText(m_xToolBox->GetItemId(nIndex), rTip);
}
void SalInstanceToolbar::set_item_tooltip_text(const OUString& rIdent, const OUString& rTip)
{
m_xToolBox->SetQuickHelpText(m_xToolBox->GetItemId(rIdent), rTip);
}
void SalInstanceToolbar::set_item_accessible_name(int nIndex, const OUString& rName)
{
m_xToolBox->SetAccessibleName(m_xToolBox->GetItemId(nIndex), rName);
}
void SalInstanceToolbar::set_item_accessible_name(const OUString& rIdent, const OUString& rName)
{
m_xToolBox->SetAccessibleName(m_xToolBox->GetItemId(rIdent), rName);
}
OUString SalInstanceToolbar::get_item_tooltip_text(const OUString& rIdent) const
{
return m_xToolBox->GetQuickHelpText(m_xToolBox->GetItemId(rIdent));
}
vcl::ImageType SalInstanceToolbar::get_icon_size() const { return m_xToolBox->GetImageSize(); }
void SalInstanceToolbar::set_icon_size(vcl::ImageType eType)
{
ToolBoxButtonSize eButtonSize = ToolBoxButtonSize::DontCare;
switch (eType)
{
case vcl::ImageType::Size16:
eButtonSize = ToolBoxButtonSize::Small;
break ;
case vcl::ImageType::Size26:
eButtonSize = ToolBoxButtonSize::Large;
break ;
case vcl::ImageType::Size32:
eButtonSize = ToolBoxButtonSize::Size32;
break ;
}
if (m_xToolBox->GetToolboxButtonSize() != eButtonSize)
{
m_xToolBox->SetToolboxButtonSize(eButtonSize);
m_xToolBox->queue_resize();
}
}
sal_uInt16 SalInstanceToolbar::get_modifier_state() const { return m_xToolBox->GetModifier(); }
int SalInstanceToolbar::get_drop_index(const Point& rPoint) const
{
auto nRet = m_xToolBox->GetItemPos(rPoint);
if (nRet == ToolBox::ITEM_NOTFOUND)
return 0;
return nRet;
}
SalInstanceToolbar::~SalInstanceToolbar()
{
m_xToolBox->SetDropdownClickHdl(Link<ToolBox*, void >());
m_xToolBox->SetSelectHdl(Link<ToolBox*, void >());
}
IMPL_LINK_NOARG(SalInstanceToolbar, ClickHdl, ToolBox*, void )
{
ToolBoxItemId nItemId = m_xToolBox->GetCurItemId();
signal_clicked(m_xToolBox->GetItemCommand(nItemId));
}
IMPL_LINK_NOARG(SalInstanceToolbar, DropdownClick, ToolBox*, void )
{
ToolBoxItemId nItemId = m_xToolBox->GetCurItemId();
set_menu_item_active(m_xToolBox->GetItemCommand(nItemId), true );
}
IMPL_LINK(SalInstanceToolbar, MenuToggleListener, VclWindowEvent&, rEvent, void )
{
if (rEvent.GetId() == VclEventId::WindowEndPopupMode)
{
for (const auto & rFloat : m_aFloats)
{
if (rEvent.GetWindow() == rFloat.second)
{
ToolBoxItemId nItemId = rFloat.first;
signal_toggle_menu(m_xToolBox->GetItemCommand(nItemId));
break ;
}
}
}
}
namespace
{
class SalInstanceSizeGroup : public weld::SizeGroup
{
private :
std::shared_ptr<VclSizeGroup> m_xGroup;
public :
SalInstanceSizeGroup()
: m_xGroup(std::make_shared<VclSizeGroup>())
{
}
virtual void add_widget(weld::Widget* pWidget) override
{
SalInstanceWidget* pVclWidget = dynamic_cast <SalInstanceWidget*>(pWidget);
assert(pVclWidget && pVclWidget->getWidget());
pVclWidget->getWidget()->add_to_size_group(m_xGroup);
}
virtual void set_mode(VclSizeGroupMode eMode) override { m_xGroup->set_mode(eMode); }
};
}
void SalInstanceContainer::connect_container_focus_changed(const Link<Container&, void >& rLink)
{
ensure_event_listener();
weld::Container::connect_container_focus_changed(rLink);
}
void SalInstanceContainer::HandleEventListener(VclWindowEvent& rEvent)
{
if (rEvent.GetId() == VclEventId::WindowActivate
|| rEvent.GetId() == VclEventId::WindowDeactivate)
{
signal_container_focus_changed();
return ;
}
SalInstanceWidget::HandleEventListener(rEvent);
}
SalInstanceContainer::SalInstanceContainer(vcl::Window* pContainer, SalInstanceBuilder* pBuilder,
bool bTakeOwnership)
: SalInstanceWidget(pContainer, pBuilder, bTakeOwnership)
, m_xContainer(pContainer)
{
}
void SalInstanceContainer::move(weld::Widget* pWidget, weld::Container* pNewParent)
{
SalInstanceWidget* pVclWidget = dynamic_cast <SalInstanceWidget*>(pWidget);
assert(pVclWidget);
SalInstanceContainer* pNewVclParent = dynamic_cast <SalInstanceContainer*>(pNewParent);
assert(!pNewParent || pNewVclParent);
vcl::Window* pVclWindow = pVclWidget->getWidget();
if (pNewVclParent)
{
vcl::Window* pNew = pNewVclParent->getWidget();
if (!pNew->isDisposed())
pVclWindow->SetParent(pNewVclParent->getWidget());
else
SAL_WARN("vcl" , "ignoring move because new parent is already disposed" );
}
else
{
pVclWindow->Hide();
pVclWindow->SetParent(ImplGetDefaultWindow());
}
}
void SalInstanceContainer::child_grab_focus()
{
m_xContainer->GrabFocus();
if (vcl::Window* pFirstChild = m_xContainer->ImplGetDlgWindow(0, GetDlgWindowType::First))
pFirstChild->ImplControlFocus();
}
css::uno::Reference<css::awt::XWindow> SalInstanceContainer::CreateChildFrame()
{
auto xPage = VclPtr<VclBin>::Create(m_xContainer.get());
xPage->set_expand(true );
xPage->Show();
return css::uno::Reference<css::awt::XWindow>(xPage->GetComponentInterface(),
css::uno::UNO_QUERY);
}
std::unique_ptr<weld::Container> SalInstanceWidget::weld_parent() const
{
vcl::Window* pParent = m_xWidget->GetParent();
if (!pParent)
return nullptr;
return std::make_unique<SalInstanceContainer>(pParent, m_pBuilder, false );
}
void SalInstanceWidget::DoRecursivePaint(vcl::Window* pWindow, const Point& rRenderLogicPos,
OutputDevice& rOutput)
{
rOutput.Push();
bool bOldMapModeEnabled = pWindow->IsMapModeEnabled();
if (pWindow->GetMapMode().GetMapUnit() != rOutput.GetMapMode().GetMapUnit())
{
// This is needed for e.g. the scrollbar in writer comments in margins that has its map unit in pixels
// as seen with bin/run gtktiledviewer --enable-tiled-annotations on a document containing a comment
// long enough to need a scrollbar
pWindow->EnableMapMode();
MapMode aMapMode = pWindow->GetMapMode();
aMapMode.SetMapUnit(rOutput.GetMapMode().GetMapUnit());
aMapMode.SetScaleX(rOutput.GetMapMode().GetScaleX());
aMapMode.SetScaleY(rOutput.GetMapMode().GetScaleY());
pWindow->SetMapMode(aMapMode);
}
VclPtr<VirtualDevice> xOutput(VclPtr<VirtualDevice>::Create(DeviceFormat::WITHOUT_ALPHA));
Size aChildSizePixel(pWindow->GetSizePixel());
xOutput->SetOutputSizePixel(aChildSizePixel);
MapMode aMapMode(xOutput->GetMapMode());
aMapMode.SetMapUnit(rOutput.GetMapMode().GetMapUnit());
aMapMode.SetScaleX(rOutput.GetMapMode().GetScaleX());
aMapMode.SetScaleY(rOutput.GetMapMode().GetScaleY());
xOutput->SetMapMode(aMapMode);
Size aTempLogicSize(xOutput->PixelToLogic(aChildSizePixel));
Size aRenderLogicSize(rOutput.PixelToLogic(aChildSizePixel));
switch (rOutput.GetOutDevType())
{
case OUTDEV_WINDOW:
case OUTDEV_VIRDEV:
xOutput->DrawOutDev(Point(), aTempLogicSize, rRenderLogicPos, aRenderLogicSize,
rOutput);
break ;
case OUTDEV_PRINTER:
case OUTDEV_PDF:
xOutput->SetBackground(rOutput.GetBackground());
xOutput->Erase();
break ;
}
//set ReallyVisible to match Visible, we restore the original state after Paint
WindowImpl* pImpl = pWindow->ImplGetWindowImpl();
bool bRVisible = pImpl->mbReallyVisible;
pImpl->mbReallyVisible = pWindow->IsVisible();
pWindow->ApplySettings(*xOutput);
pWindow->Paint(*xOutput, tools::Rectangle(Point(), pWindow->PixelToLogic(aChildSizePixel)));
pImpl->mbReallyVisible = bRVisible;
switch (rOutput.GetOutDevType())
{
case OUTDEV_WINDOW:
case OUTDEV_VIRDEV:
rOutput.DrawOutDev(rRenderLogicPos, aRenderLogicSize, Point(), aTempLogicSize,
*xOutput);
break ;
case OUTDEV_PRINTER:
case OUTDEV_PDF:
rOutput.DrawBitmapEx(rRenderLogicPos, aRenderLogicSize,
xOutput->GetBitmapEx(Point(), aTempLogicSize));
break ;
}
bool bHasMirroredGraphics = pWindow->GetOutDev()->HasMirroredGraphics();
xOutput.disposeAndClear();
pWindow->EnableMapMode(bOldMapModeEnabled);
rOutput.Pop();
for (vcl::Window* pChild = pWindow->GetWindow(GetWindowType::FirstChild); pChild;
pChild = pChild->GetWindow(GetWindowType::Next))
{
if (!pChild->IsVisible())
continue ;
tools::Long nDeltaX
= pChild->GetOutDev()->GetOutOffXPixel() - pWindow->GetOutDev()->GetOutOffXPixel();
if (bHasMirroredGraphics)
nDeltaX = pWindow->GetOutDev()->GetOutputWidthPixel() - nDeltaX
- pChild->GetOutDev()->GetOutputWidthPixel();
tools::Long nDeltaY
= pChild->GetOutDev()->GetOutOffYPixel() - pWindow->GetOutDev()->GetOutOffYPixel();
Point aPos(rRenderLogicPos);
aPos += Point(nDeltaX, nDeltaY);
DoRecursivePaint(pChild, aPos, rOutput);
}
}
void SalInstanceWidget::draw(OutputDevice& rOutput, const Point& rPos, const Size& rSizePixel)
{
Size aOrigSize(m_xWidget->GetSizePixel());
bool bChangeSize = aOrigSize != rSizePixel;
if (bChangeSize)
m_xWidget->SetSizePixel(rSizePixel);
DoRecursivePaint(m_xWidget, rPos, rOutput);
if (bChangeSize)
m_xWidget->SetSizePixel(aOrigSize);
}
SalInstanceBox::SalInstanceBox(VclBox* pContainer, SalInstanceBuilder* pBuilder,
bool bTakeOwnership)
: SalInstanceContainer(pContainer, pBuilder, bTakeOwnership)
, m_xBox(pContainer)
{
}
void SalInstanceBox::reorder_child(weld::Widget* pWidget, int nNewPosition)
{
SalInstanceWidget* pVclWidget = dynamic_cast <SalInstanceWidget*>(pWidget);
assert(pVclWidget);
pVclWidget->getWidget()->reorderWithinParent(nNewPosition);
}
void SalInstanceBox::sort_native_button_order() { ::sort_native_button_order(*m_xBox); }
SalInstanceGrid::SalInstanceGrid(VclGrid* pGrid, SalInstanceBuilder* pBuilder, bool bTakeOwnership)
: SalInstanceContainer(pGrid, pBuilder, bTakeOwnership)
{
}
void SalInstanceGrid::set_child_left_attach(weld::Widget& rWidget, int nAttach)
{
vcl::Window* pChild = dynamic_cast <SalInstanceWidget&>(rWidget).getWidget();
assert(pChild && pChild->GetParent() == getWidget() && "widget is not a grid child" );
pChild->set_grid_left_attach(nAttach);
}
int SalInstanceGrid::get_child_left_attach(weld::Widget& rWidget) const
{
vcl::Window* pChild = dynamic_cast <SalInstanceWidget&>(rWidget).getWidget();
assert(pChild && pChild->GetParent() == getWidget() && "widget is not a grid child" );
return pChild->get_grid_left_attach();
}
void SalInstanceGrid::set_child_column_span(weld::Widget& rWidget, int nCols)
{
vcl::Window* pChild = dynamic_cast <SalInstanceWidget&>(rWidget).getWidget();
assert(pChild && pChild->GetParent() == getWidget() && "widget is not a grid child" );
pChild->set_grid_width(nCols);
}
void SalInstanceGrid::set_child_top_attach(weld::Widget& rWidget, int nAttach)
{
vcl::Window* pChild = dynamic_cast <SalInstanceWidget&>(rWidget).getWidget();
assert(pChild && pChild->GetParent() == getWidget() && "widget is not a grid child" );
pChild->set_grid_top_attach(nAttach);
}
int SalInstanceGrid::get_child_top_attach(weld::Widget& rWidget) const
{
vcl::Window* pChild = dynamic_cast <SalInstanceWidget&>(rWidget).getWidget();
assert(pChild && pChild->GetParent() == getWidget() && "widget is not a grid child" );
return pChild->get_grid_top_attach();
}
namespace
{
void CollectChildren(const vcl::Window& rCurrent, const basegfx::B2IPoint& rTopLeft,
weld::ScreenShotCollection& rControlDataCollection)
{
if (!rCurrent.IsVisible())
return ;
const Point aCurrentPos(rCurrent.GetPosPixel());
const Size aCurrentSize(rCurrent.GetSizePixel());
const basegfx::B2IPoint aCurrentTopLeft(rTopLeft.getX() + aCurrentPos.X(),
rTopLeft.getY() + aCurrentPos.Y());
const basegfx::B2IRange aCurrentRange(
aCurrentTopLeft,
aCurrentTopLeft + basegfx::B2IPoint(aCurrentSize.Width(), aCurrentSize.Height()));
if (!aCurrentRange.isEmpty())
{
rControlDataCollection.emplace_back(rCurrent.GetHelpId(), aCurrentRange);
}
for (sal_uInt16 a(0); a < rCurrent.GetChildCount(); a++)
{
vcl::Window* pChild = rCurrent.GetChild(a);
if (nullptr != pChild)
{
CollectChildren(*pChild, aCurrentTopLeft, rControlDataCollection);
}
}
}
}
void SalInstanceWindow::override_child_help(vcl::Window* pParent)
{
for (vcl::Window* pChild = pParent->GetWindow(GetWindowType::FirstChild); pChild;
pChild = pChild->GetWindow(GetWindowType::Next))
override_child_help(pChild);
pParent->SetHelpHdl(LINK(this , SalInstanceWindow, HelpHdl));
}
void SalInstanceWindow::clear_child_help(vcl::Window* pParent)
{
for (vcl::Window* pChild = pParent->GetWindow(GetWindowType::FirstChild); pChild;
pChild = pChild->GetWindow(GetWindowType::Next))
clear_child_help(pChild);
pParent->SetHelpHdl(Link<vcl::Window&, bool >());
}
SalInstanceWindow::SalInstanceWindow(vcl::Window* pWindow, SalInstanceBuilder* pBuilder,
bool bTakeOwnership)
: SalInstanceContainer(pWindow, pBuilder, bTakeOwnership)
, m_xWindow(pWindow)
{
// tdf#129745 only override child help for the normal case, not for
// m_pBuilder of null which is the toplevel application frame case.
if (m_pBuilder)
override_child_help(m_xWindow);
}
void SalInstanceWindow::set_title(const OUString& rTitle) { m_xWindow->SetText(rTitle); }
OUString SalInstanceWindow::get_title() const { return m_xWindow->GetText(); }
css::uno::Reference<css::awt::XWindow> SalInstanceWindow::GetXWindow()
{
css::uno::Reference<css::awt::XWindow> xWindow(m_xWindow->GetComponentInterface(),
css::uno::UNO_QUERY);
return xWindow;
}
namespace
{
void resize_to_request(vcl::Window* pWindow)
{
if (SystemWindow* pSysWin = dynamic_cast <SystemWindow*>(pWindow))
{
pSysWin->setOptimalLayoutSize(true );
return ;
}
if (DockingWindow* pDockWin = dynamic_cast <DockingWindow*>(pWindow))
{
pDockWin->setOptimalLayoutSize();
return ;
}
assert(false && "must be system or docking window" );
}
}
void SalInstanceWindow::resize_to_request() { ::resize_to_request(m_xWindow.get()); }
void SalInstanceWindow::window_move(int x, int y) { m_xWindow->SetPosPixel(Point(x, y)); }
Size SalInstanceWindow::get_size() const { return m_xWindow->GetSizePixel(); }
Point SalInstanceWindow::get_position() const { return m_xWindow->GetPosPixel(); }
AbsoluteScreenPixelRectangle SalInstanceWindow::get_monitor_workarea() const
{
return m_xWindow->GetDesktopRectPixel();
}
bool SalInstanceWindow::get_resizable() const { return m_xWindow->GetStyle() & WB_SIZEABLE; }
bool SalInstanceWindow::has_toplevel_focus() const { return m_xWindow->HasChildPathFocus(); }
void SalInstanceWindow::present()
{
m_xWindow->ToTop(ToTopFlags::RestoreWhenMin | ToTopFlags::ForegroundTask);
}
void SalInstanceWindow::implResetDefault(const vcl::Window* _pWindow)
{
vcl::Window* pChildLoop = _pWindow->GetWindow(GetWindowType::FirstChild);
while (pChildLoop)
{
// does the window participate in the tabbing order?
if (pChildLoop->GetStyle() & WB_DIALOGCONTROL)
implResetDefault(pChildLoop);
// is it a button?
WindowType eType = pChildLoop->GetType();
if ((WindowType::PUSHBUTTON == eType) || (WindowType::OKBUTTON == eType)
|| (WindowType::CANCELBUTTON == eType) || (WindowType::HELPBUTTON == eType)
|| (WindowType::IMAGEBUTTON == eType) || (WindowType::MENUBUTTON == eType)
|| (WindowType::MOREBUTTON == eType))
{
pChildLoop->SetStyle(pChildLoop->GetStyle() & ~WB_DEFBUTTON);
}
// the next one ...
pChildLoop = pChildLoop->GetWindow(GetWindowType::Next);
}
}
void SalInstanceWindow::recursively_unset_default_buttons() { implResetDefault(m_xWindow.get()); }
void SalInstanceWindow::change_default_widget(weld::Widget* pOld, weld::Widget* pNew)
{
SalInstanceWidget* pVclNew = dynamic_cast <SalInstanceWidget*>(pNew);
vcl::Window* pWidgetNew = pVclNew ? pVclNew->getWidget() : nullptr;
SalInstanceWidget* pVclOld = dynamic_cast <SalInstanceWidget*>(pOld);
vcl::Window* pWidgetOld = pVclOld ? pVclOld->getWidget() : nullptr;
if (pWidgetOld)
pWidgetOld->set_property(u"has-default" _ustr, OUString::boolean(false ));
else
recursively_unset_default_buttons();
if (pWidgetNew)
pWidgetNew->set_property(u"has-default" _ustr, OUString::boolean(true ));
}
bool SalInstanceWindow::is_default_widget(const weld::Widget* pCandidate) const
{
const SalInstanceWidget* pVclCandidate = dynamic_cast <const SalInstanceWidget*>(pCandidate);
vcl::Window* pWidget = pVclCandidate ? pVclCandidate->getWidget() : nullptr;
return pWidget && pWidget->GetStyle() & WB_DEFBUTTON;
}
void SalInstanceWindow::set_window_state(const OUString& rStr)
{
SystemWindow* pSysWin = dynamic_cast <SystemWindow*>(m_xWindow.get());
assert(pSysWin);
pSysWin->SetWindowState(rStr);
}
OUString SalInstanceWindow::get_window_state(vcl::WindowDataMask nMask) const
{
SystemWindow* pSysWin = dynamic_cast <SystemWindow*>(m_xWindow.get());
assert(pSysWin);
return pSysWin->GetWindowState(nMask);
}
SystemEnvData SalInstanceWindow::get_system_data() const { return *m_xWindow->GetSystemData(); }
VclPtr<VirtualDevice> SalInstanceWindow::screenshot()
{
SystemWindow* pSysWin = dynamic_cast <SystemWindow*>(m_xWindow.get());
assert(pSysWin);
return pSysWin->createScreenshot();
}
weld::ScreenShotCollection SalInstanceWindow::collect_screenshot_data()
{
weld::ScreenShotCollection aRet;
// collect all children. Choose start pos to be negative
// of target dialog's position to get all positions relative to (0,0)
const Point aParentPos(m_xWindow->GetPosPixel());
const basegfx::B2IPoint aTopLeft(-aParentPos.X(), -aParentPos.Y());
CollectChildren(*m_xWindow, aTopLeft, aRet);
return aRet;
}
const vcl::ILibreOfficeKitNotifier* SalInstanceWindow::GetLOKNotifier()
{
return m_xWindow ? m_xWindow->GetLOKNotifier() : nullptr;
}
SalInstanceWindow::~SalInstanceWindow()
{
// tdf#129745 only undo overriding child help for the normal case, not for
// m_pBuilder of null which is the toplevel application frame case.
if (m_pBuilder)
clear_child_help(m_xWindow);
}
IMPL_LINK_NOARG(SalInstanceWindow, HelpHdl, vcl::Window&, bool )
{
help();
return false ;
}
typedef std::set<VclPtr<vcl::Window>> winset;
namespace
{
void hideUnless(const vcl::Window* pTop, const winset& rVisibleWidgets,
std::vector<VclPtr<vcl::Window>>& rWasVisibleWidgets)
{
for (vcl::Window* pChild = pTop->GetWindow(GetWindowType::FirstChild); pChild;
pChild = pChild->GetWindow(GetWindowType::Next))
{
if (!pChild->IsVisible())
continue ;
if (rVisibleWidgets.find(pChild) == rVisibleWidgets.end())
{
rWasVisibleWidgets.emplace_back(pChild);
pChild->Hide();
}
else if (isContainerWindow(pChild))
{
hideUnless(pChild, rVisibleWidgets, rWasVisibleWidgets);
}
}
}
}
SalInstanceDialog::SalInstanceDialog(::Dialog* pDialog, SalInstanceBuilder* pBuilder,
bool bTakeOwnership)
: SalInstanceWindow(pDialog, pBuilder, bTakeOwnership)
, m_xDialog(pDialog)
, m_nOldEditWidthReq(0)
, m_nOldBorderWidth(0)
{
const bool bScreenshotMode(officecfg::Office::Common::Misc::ScreenshotMode::get());
if (bScreenshotMode)
{
m_xDialog->SetPopupMenuHdl(LINK(this , SalInstanceDialog, PopupScreenShotMenuHdl));
}
}
bool SalInstanceDialog::runAsync(std::shared_ptr<weld::DialogController> const & rxOwner,
const std::function<void (sal_Int32)>& rEndDialogFn)
{
VclAbstractDialog::AsyncContext aCtx;
aCtx.mxOwnerDialogController = rxOwner;
aCtx.maEndDialogFn = rEndDialogFn;
VclButtonBox* pActionArea = m_xDialog->get_action_area();
if (pActionArea)
sort_native_button_order(*pActionArea);
return m_xDialog->StartExecuteAsync(aCtx);
}
bool SalInstanceDialog::runAsync(std::shared_ptr<Dialog> const & rxSelf,
const std::function<void (sal_Int32)>& rEndDialogFn)
{
assert(rxSelf.get() == this );
VclAbstractDialog::AsyncContext aCtx;
// In order to store a shared_ptr to ourself, we have to have been constructed by make_shared,
// which is that rxSelf enforces.
aCtx.mxOwnerSelf = rxSelf;
aCtx.maEndDialogFn = rEndDialogFn;
VclButtonBox* pActionArea = m_xDialog->get_action_area();
if (pActionArea)
sort_native_button_order(*pActionArea);
return m_xDialog->StartExecuteAsync(aCtx);
}
void SalInstanceDialog::collapse(weld::Widget* pEdit, weld::Widget* pButton)
{
SalInstanceWidget* pVclEdit = dynamic_cast <SalInstanceWidget*>(pEdit);
assert(pVclEdit);
SalInstanceWidget* pVclButton = dynamic_cast <SalInstanceWidget*>(pButton);
vcl::Window* pRefEdit = pVclEdit->getWidget();
vcl::Window* pRefBtn = pVclButton ? pVclButton->getWidget() : nullptr;
auto nOldEditWidth = pRefEdit->GetSizePixel().Width();
m_nOldEditWidthReq = pRefEdit->get_width_request();
//We want just pRefBtn and pRefEdit to be shown
//mark widgets we want to be visible, starting with pRefEdit
//and all its direct parents.
winset aVisibleWidgets;
vcl::Window* pContentArea = m_xDialog->get_content_area();
for (vcl::Window* pCandidate = pRefEdit;
pCandidate && (pCandidate != pContentArea && pCandidate->IsVisible());
pCandidate = pCandidate->GetWindow(GetWindowType::RealParent))
{
aVisibleWidgets.insert(pCandidate);
}
//same again with pRefBtn, except stop if there's a
//shared parent in the existing widgets
for (vcl::Window* pCandidate = pRefBtn;
pCandidate && (pCandidate != pContentArea && pCandidate->IsVisible());
pCandidate = pCandidate->GetWindow(GetWindowType::RealParent))
{
if (aVisibleWidgets.insert(pCandidate).second)
break ;
}
//hide everything except the aVisibleWidgets
hideUnless(pContentArea, aVisibleWidgets, m_aHiddenWidgets);
// the insert function case has an initially hidden edit widget, so it has
// not start size, so take larger of actual size and size request
pRefEdit->set_width_request(std::max(nOldEditWidth, m_nOldEditWidthReq));
m_nOldBorderWidth = m_xDialog->get_border_width();
m_xDialog->set_border_width(0);
if (vcl::Window* pActionArea = m_xDialog->get_action_area())
pActionArea->Hide();
m_xDialog->setOptimalLayoutSize(true );
m_xRefEdit = pRefEdit;
}
void SalInstanceDialog::undo_collapse()
{
// All others: Show();
for (VclPtr<vcl::Window> const & pWindow : m_aHiddenWidgets)
{
pWindow->Show();
}
m_aHiddenWidgets.clear();
m_xRefEdit->set_width_request(m_nOldEditWidthReq);
m_xRefEdit.reset();
m_xDialog->set_border_width(m_nOldBorderWidth);
if (vcl::Window* pActionArea = m_xDialog->get_action_area())
pActionArea->Show();
m_xDialog->setOptimalLayoutSize(true );
}
void SalInstanceDialog::SetInstallLOKNotifierHdl(
const Link<void *, vcl::ILibreOfficeKitNotifier*>& rLink)
{
m_xDialog->SetInstallLOKNotifierHdl(rLink);
}
int SalInstanceDialog::run()
{
VclButtonBox* pActionArea = m_xDialog->get_action_area();
if (pActionArea)
sort_native_button_order(*pActionArea);
return m_xDialog->Execute();
}
void SalInstanceDialog::response(int nResponse) { m_xDialog->EndDialog(nResponse); }
void SalInstanceDialog::add_button(const OUString& rText, int nResponse, const OUString& rHelpId)
{
VclButtonBox* pBox = m_xDialog->get_action_area();
VclPtr<PushButton> xButton(
VclPtr<PushButton>::Create(pBox, WB_CLIPCHILDREN | WB_CENTER | WB_VCENTER));
xButton->SetText(rText);
xButton->SetHelpId(rHelpId);
switch (nResponse)
{
case RET_OK:
xButton->set_id(u"ok" _ustr);
break ;
case RET_CLOSE:
xButton->set_id(u"close" _ustr);
break ;
case RET_CANCEL:
xButton->set_id(u"cancel" _ustr);
break ;
case RET_YES:
xButton->set_id(u"yes" _ustr);
break ;
case RET_NO:
xButton->set_id(u"no" _ustr);
break ;
}
xButton->Show();
m_xDialog->add_button(xButton, nResponse, true );
}
void SalInstanceDialog::set_modal(bool bModal)
{
if (get_modal() == bModal)
return ;
m_xDialog->SetModalInputMode(bModal);
}
bool SalInstanceDialog::get_modal() const { return m_xDialog->IsModalInputMode(); }
void SalInstanceDialog::set_centered_on_parent(bool /*bTrackGeometryRequests*/)
{
if (vcl::Window* pParent = m_xWidget->GetParent())
{
Size aParentGeometry(pParent->GetSizePixel());
Size aGeometry(m_xWidget->get_preferred_size());
auto nX = (aParentGeometry.Width() - aGeometry.Width()) / 2;
auto nY = (aParentGeometry.Height() - aGeometry.Height()) / 2;
m_xWidget->SetPosPixel(Point(nX, nY));
}
}
void SalInstanceDialog::set_default_response(int nResponse)
{
m_xDialog->set_default_response(nResponse);
}
std::unique_ptr<weld::Container> SalInstanceDialog::weld_content_area()
{
return std::make_unique<SalInstanceContainer>(m_xDialog->get_content_area(), m_pBuilder, false );
}
IMPL_LINK(SalInstanceDialog, PopupScreenShotMenuHdl, const CommandEvent&, rCEvt, bool )
{
if (CommandEventId::ContextMenu == rCEvt.GetCommand())
{
const Point aMenuPos(rCEvt.GetMousePosPixel());
ScopedVclPtrInstance<PopupMenu> aMenu;
sal_uInt16 nLocalID(1);
aMenu->InsertItem(nLocalID, VclResId(SV_BUTTONTEXT_SCREENSHOT));
aMenu->SetHelpText(nLocalID, VclResId(SV_HELPTEXT_SCREENSHOT));
aMenu->SetHelpId(nLocalID, u"InteractiveScreenshotMode" _ustr);
aMenu->EnableItem(nLocalID);
const sal_uInt16 nId(aMenu->Execute(m_xDialog, aMenuPos));
// 0 == no selection (so not usable as ID)
if (0 != nId)
{
// open screenshot annotation dialog
VclAbstractDialogFactory* pFact = VclAbstractDialogFactory::Create();
--> --------------------
--> maximum size reached
--> --------------------
Messung V0.5 C=94 H=98 G=95
¤ Dauer der Verarbeitung: 0.21 Sekunden
¤
*© Formatika GbR, Deutschland