diff options
Diffstat (limited to 'vcl/source/app')
29 files changed, 17458 insertions, 0 deletions
diff --git a/vcl/source/app/ITiledRenderable.cxx b/vcl/source/app/ITiledRenderable.cxx new file mode 100644 index 000000000..e2a639dec --- /dev/null +++ b/vcl/source/app/ITiledRenderable.cxx @@ -0,0 +1,81 @@ +/* -*- 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/. + */ + +#include <vcl/ITiledRenderable.hxx> + +namespace vcl +{ + + /* + * Map directly to css cursor styles to avoid further mapping in the client. + * Gtk (via gdk_cursor_new_from_name) also supports the same css cursor styles. + * + * This was created partially with help of the mappings in gtkdata.cxx. + * The list is incomplete as some cursor style simply aren't supported + * by css, it might turn out to be worth mapping some of these missing cursors + * to available cursors? + */ +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning( disable : 4592) +#endif + const std::map <PointerStyle, OString> gaLOKPointerMap { + { PointerStyle::Arrow, "default" }, + // PointerStyle::Null ? + { PointerStyle::Wait, "wait" }, + { PointerStyle::Text, "text" }, + { PointerStyle::Help, "help" }, + { PointerStyle::Cross, "crosshair" }, + { PointerStyle::Fill, "fill" }, + { PointerStyle::Move, "move" }, + { PointerStyle::NSize, "n-resize" }, + { PointerStyle::SSize, "s-resize" }, + { PointerStyle::WSize, "w-resize" }, + { PointerStyle::ESize, "e-resize" }, + { PointerStyle::NWSize, "ne-resize" }, + { PointerStyle::NESize, "ne-resize" }, + { PointerStyle::SWSize, "sw-resize" }, + { PointerStyle::SESize, "se-resize" }, + // WindowNSize through WindowSESize + { PointerStyle::HSplit, "col-resize" }, + { PointerStyle::VSplit, "row-resize" }, + { PointerStyle::HSizeBar, "col-resize" }, + { PointerStyle::VSizeBar, "row-resize" }, + { PointerStyle::Hand, "grab" }, + { PointerStyle::RefHand, "pointer" }, + // Pen, Magnify, Fill, Rotate + // HShear, VShear + // Mirror, Crook, Crop, MovePoint, MoveBezierWeight + // MoveData + { PointerStyle::CopyData, "copy" }, + { PointerStyle::LinkData, "alias" }, + // MoveDataLink, CopyDataLink + //MoveFile, CopyFile, LinkFile + // MoveFileLink, CopyFileLink, MoveFiless, CopyFiles + { PointerStyle::NotAllowed, "not-allowed" }, + // DrawLine through DrawCaption + // Chart, Detective, PivotCol, PivotRow, PivotField, Chain, ChainNotAllowed + // TimeEventMove, TimeEventSize + // AutoScrollN through AutoScrollNSWE + // Airbrush + { PointerStyle::TextVertical, "vertical-text" } + // Pivot Delete, TabSelectS through TabSelectSW + // PaintBrush, HideWhiteSpace, ShowWhiteSpace + }; +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +ITiledRenderable::~ITiledRenderable() +{ +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/source/app/IconThemeInfo.cxx b/vcl/source/app/IconThemeInfo.cxx new file mode 100644 index 000000000..84d85883b --- /dev/null +++ b/vcl/source/app/IconThemeInfo.cxx @@ -0,0 +1,188 @@ +/* -*- 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/. + */ + +#include <vcl/IconThemeInfo.hxx> +#include <rtl/character.hxx> + +#include <stdexcept> +#include <algorithm> + +// constants for theme ids and display names. Only the theme id for high contrast is used +// outside of this class and hence made public. + +const OUStringLiteral vcl::IconThemeInfo::HIGH_CONTRAST_ID("sifr"); + +namespace { + +static const OUStringLiteral KARASA_JAGA_ID("karasa_jaga"); +static const OUStringLiteral KARASA_JAGA_DISPLAY_NAME("Karasa Jaga"); +static const OUStringLiteral HELPIMG_FAKE_THEME("helpimg"); + +OUString +filename_from_url(const OUString& url) +{ + sal_Int32 slashPosition = url.lastIndexOf( '/' ); + if (slashPosition < 0) { + return OUString(); + } + OUString filename = url.copy( slashPosition+1 ); + return filename; +} + +} // end anonymous namespace + +namespace vcl { + +static const char ICON_THEME_PACKAGE_PREFIX[] = "images_"; + +static const char EXTENSION_FOR_ICON_PACKAGES[] = ".zip"; + +IconThemeInfo::IconThemeInfo() +{ +} + +IconThemeInfo::IconThemeInfo(const OUString& urlToFile) +: mUrlToFile(urlToFile) +{ + OUString filename = filename_from_url(urlToFile); + if (filename.isEmpty()) { + throw std::runtime_error("invalid URL passed to IconThemeInfo()"); + } + + mThemeId = FileNameToThemeId(filename); + mDisplayName = ThemeIdToDisplayName(mThemeId); + +} + +/*static*/ Size +IconThemeInfo::SizeByThemeName(const OUString& themeName) +{ + if (themeName == "galaxy") { //kept for compiler because of unused parameter 'themeName' + return Size( 26, 26 ); + } + else { + return Size( 24, 24 ); + } +} + +/*static*/ bool +IconThemeInfo::UrlCanBeParsed(const OUString& url) +{ + OUString fname = filename_from_url(url); + if (fname.isEmpty()) { + return false; + } + + if (!fname.startsWithIgnoreAsciiCase(ICON_THEME_PACKAGE_PREFIX)) { + return false; + } + + if (!fname.endsWithIgnoreAsciiCase(EXTENSION_FOR_ICON_PACKAGES)) { + return false; + } + + if (fname.indexOf(HELPIMG_FAKE_THEME) != -1 ) { + return false; + } + + return true; +} + +/*static*/ OUString +IconThemeInfo::FileNameToThemeId(const OUString& filename) +{ + OUString r; + sal_Int32 positionOfLastDot = filename.lastIndexOf(EXTENSION_FOR_ICON_PACKAGES); + if (positionOfLastDot < 0) { // -1 means index not found + throw std::runtime_error("IconThemeInfo::FileNameToThemeId() called with invalid filename."); + } + sal_Int32 positionOfFirstUnderscore = filename.indexOf(ICON_THEME_PACKAGE_PREFIX); + if (positionOfFirstUnderscore < 0) { // -1 means index not found. Use the whole name instead + throw std::runtime_error("IconThemeInfo::FileNameToThemeId() called with invalid filename."); + } + positionOfFirstUnderscore += RTL_CONSTASCII_LENGTH(ICON_THEME_PACKAGE_PREFIX); + r = filename.copy(positionOfFirstUnderscore, positionOfLastDot - positionOfFirstUnderscore); + return r; +} + +/*static*/ OUString +IconThemeInfo::ThemeIdToDisplayName(const OUString& themeId) +{ + if (themeId.isEmpty()) { + throw std::runtime_error("IconThemeInfo::ThemeIdToDisplayName() called with invalid id."); + } + + // Strip _svg and _dark filename "extensions" + OUString aDisplayName = themeId; + + bool bIsSvg = aDisplayName.endsWith("_svg", &aDisplayName); + bool bIsDark = aDisplayName.endsWith("_dark", &aDisplayName); + if (!bIsSvg && bIsDark) + bIsSvg = aDisplayName.endsWith("_svg", &aDisplayName); + + // special cases + if (aDisplayName.equalsIgnoreAsciiCase(KARASA_JAGA_ID)) { + aDisplayName = KARASA_JAGA_DISPLAY_NAME; + } + else + { + // make the first letter uppercase + sal_Unicode firstLetter = aDisplayName[0]; + if (rtl::isAsciiLowerCase(firstLetter)) + { + aDisplayName = OUStringChar(sal_Unicode(rtl::toAsciiUpperCase(firstLetter))) + aDisplayName.copy(1); + } + } + + if (bIsSvg && bIsDark) + aDisplayName += " (SVG + dark)"; + else if (bIsSvg) + aDisplayName += " (SVG)"; + else if (bIsDark) + aDisplayName += " (dark)"; + + return aDisplayName; +} + +namespace +{ + class SameTheme + { + private: + const OUString& m_rThemeId; + public: + explicit SameTheme(const OUString &rThemeId) : m_rThemeId(rThemeId) {} + bool operator()(const vcl::IconThemeInfo &rInfo) + { + return m_rThemeId == rInfo.GetThemeId(); + } + }; +} + +/*static*/ const vcl::IconThemeInfo& +IconThemeInfo::FindIconThemeById(const std::vector<vcl::IconThemeInfo>& themes, const OUString& themeId) +{ + std::vector<vcl::IconThemeInfo>::const_iterator it = std::find_if(themes.begin(), themes.end(), + SameTheme(themeId)); + if (it == themes.end()) + { + throw std::runtime_error("Could not find theme id in theme vector."); + } + return *it; +} + +/*static*/ bool +IconThemeInfo::IconThemeIsInVector(const std::vector<vcl::IconThemeInfo>& themes, const OUString& themeId) +{ + return std::any_of(themes.begin(), themes.end(), SameTheme(themeId)); +} + +} // end namespace vcl + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/source/app/IconThemeScanner.cxx b/vcl/source/app/IconThemeScanner.cxx new file mode 100644 index 000000000..a9163af3c --- /dev/null +++ b/vcl/source/app/IconThemeScanner.cxx @@ -0,0 +1,215 @@ +/* -*- 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/. + */ + +#include <sal/config.h> +#include <sal/log.hxx> + +#include <deque> + +#include <IconThemeScanner.hxx> + +#include <osl/file.hxx> +#include <salhelper/linkhelper.hxx> +#include <unotools/pathoptions.hxx> +#include <vcl/IconThemeInfo.hxx> + +namespace vcl { + +namespace { + +// set the status of a file. Returns false if the status could not be determined. +bool set_file_status(osl::FileStatus& status, const OUString& file) +{ + osl::DirectoryItem dirItem; + osl::FileBase::RC retvalGet = osl::DirectoryItem::get(file, dirItem); + if (retvalGet != osl::FileBase::E_None) { + SAL_WARN("vcl.app", "Could not determine status for file '" << file << "'."); + return false; + } + osl::FileBase::RC retvalStatus = dirItem.getFileStatus(status); + if (retvalStatus != osl::FileBase::E_None) { + SAL_WARN("vcl.app", "Could not determine status for file '" << file << "'."); + return false; + } + return true; +} + +OUString convert_to_absolute_path(const OUString& path) +{ + salhelper::LinkResolver resolver(0); + osl::FileBase::RC rc = resolver.fetchFileStatus(path); + if (rc != osl::FileBase::E_None) { + SAL_WARN("vcl.app", "Could not resolve path '" << path << "' to search for icon themes."); + if (rc == osl::FileBase::E_MULTIHOP) + { + throw std::runtime_error("Provided a recursive symlink to an icon theme directory that could not be resolved."); + } + } + return resolver.m_aStatus.getFileURL(); +} + +} + +IconThemeScanner::IconThemeScanner() +{} + +void IconThemeScanner::ScanDirectoryForIconThemes(const OUString& paths) +{ + mFoundIconThemes.clear(); + + std::deque<OUString> aPaths; + + sal_Int32 nIndex = 0; + do + { + aPaths.push_front(paths.getToken(0, ';', nIndex)); + } + while (nIndex >= 0); + + for (const auto& path : aPaths) + { + osl::FileStatus fileStatus(osl_FileStatus_Mask_Type); + bool couldSetFileStatus = set_file_status(fileStatus, path); + if (!couldSetFileStatus) { + continue; + } + + if (!fileStatus.isDirectory()) { + SAL_INFO("vcl.app", "Cannot search for icon themes in '"<< path << "'. It is not a directory."); + continue; + } + + std::vector<OUString> iconThemePaths = ReadIconThemesFromPath(path); + if (iconThemePaths.empty()) { + SAL_WARN("vcl.app", "Could not find any icon themes in the provided directory ('" <<path<<"'."); + continue; + } + for (auto const& iconThemePath : iconThemePaths) + { + AddIconThemeByPath(iconThemePath); + } + } +} + +bool +IconThemeScanner::AddIconThemeByPath(const OUString &url) +{ + if (!IconThemeInfo::UrlCanBeParsed(url)) { + return false; + } + SAL_INFO("vcl.app", "Found a file that seems to be an icon theme: '" << url << "'" ); + IconThemeInfo newTheme(url); + mFoundIconThemes.push_back(newTheme); + SAL_INFO("vcl.app", "Adding the file as '" << newTheme.GetDisplayName() << + "' with id '" << newTheme.GetThemeId() << "'."); + return true; +} + +/*static*/ std::vector<OUString> +IconThemeScanner::ReadIconThemesFromPath(const OUString& dir) +{ + std::vector<OUString> found; + SAL_INFO("vcl.app", "Scanning directory '" << dir << " for icon themes."); + + osl::Directory dirToScan(dir); + osl::FileBase::RC retvalOpen = dirToScan.open(); + if (retvalOpen != osl::FileBase::E_None) { + return found; + } + + osl::DirectoryItem directoryItem; + while (dirToScan.getNextItem(directoryItem) == osl::FileBase::E_None) { + osl::FileStatus status(osl_FileStatus_Mask_Type | osl_FileStatus_Mask_FileURL | osl_FileStatus_Mask_FileName); + osl::FileBase::RC retvalStatus = directoryItem.getFileStatus(status); + if (retvalStatus != osl::FileBase::E_None) { + continue; + } + + OUString filename = convert_to_absolute_path(status.getFileURL()); + if (!FileIsValidIconTheme(filename)) { + continue; + } + found.push_back(filename); + } + return found; +} + +/*static*/ bool +IconThemeScanner::FileIsValidIconTheme(const OUString& filename) +{ + // check whether we can construct an IconThemeInfo from it + if (!IconThemeInfo::UrlCanBeParsed(filename)) { + SAL_INFO("vcl.app", "File '" << filename << "' does not seem to be an icon theme."); + return false; + } + + osl::FileStatus fileStatus(osl_FileStatus_Mask_Type); + bool couldSetFileStatus = set_file_status(fileStatus, filename); + if (!couldSetFileStatus) { + return false; + } + + if (!fileStatus.isRegular()) { + return false; + } + return true; +} + +bool +IconThemeScanner::IconThemeIsInstalled(const OUString& themeId) const +{ + return IconThemeInfo::IconThemeIsInVector(mFoundIconThemes, themeId); +} + +/*static*/ std::shared_ptr<IconThemeScanner> +IconThemeScanner::Create(const OUString &path) +{ + std::shared_ptr<IconThemeScanner> retval(new IconThemeScanner); + retval->ScanDirectoryForIconThemes(path); + return retval; +} + +/*static*/ OUString +IconThemeScanner::GetStandardIconThemePath() +{ + SvtPathOptions aPathOptions; + return aPathOptions.GetIconsetPath(); +} + +namespace +{ + class SameTheme + { + private: + const OUString& m_rThemeId; + public: + explicit SameTheme(const OUString &rThemeId) : m_rThemeId(rThemeId) {} + bool operator()(const vcl::IconThemeInfo &rInfo) + { + return m_rThemeId == rInfo.GetThemeId(); + } + }; +} + +const vcl::IconThemeInfo& +IconThemeScanner::GetIconThemeInfo(const OUString& themeId) +{ + std::vector<IconThemeInfo>::iterator info = std::find_if(mFoundIconThemes.begin(), mFoundIconThemes.end(), + SameTheme(themeId)); + if (info == mFoundIconThemes.end()) { + SAL_WARN("vcl.app", "Requested information for icon theme with id '" << themeId + << "' which does not exist."); + throw std::runtime_error("Requested information on not-installed icon theme"); + } + return *info; +} + +} // end namespace vcl + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/source/app/IconThemeSelector.cxx b/vcl/source/app/IconThemeSelector.cxx new file mode 100644 index 000000000..e98d63657 --- /dev/null +++ b/vcl/source/app/IconThemeSelector.cxx @@ -0,0 +1,175 @@ +/* -*- 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/. + */ + +#include <comphelper/lok.hxx> + +#include <IconThemeSelector.hxx> + +#include <vcl/IconThemeInfo.hxx> +#include <config_mpl.h> + +#include <algorithm> + +namespace vcl { + +/*static*/ const OUStringLiteral IconThemeSelector::FALLBACK_ICON_THEME_ID("colibre"); + +namespace { + + class SameTheme + { + private: + const OUString& m_rThemeId; + public: + explicit SameTheme(const OUString &rThemeId) : m_rThemeId(rThemeId) {} + bool operator()(const vcl::IconThemeInfo &rInfo) + { + return m_rThemeId == rInfo.GetThemeId(); + } + }; + +bool icon_theme_is_in_installed_themes(const OUString& theme, + const std::vector<IconThemeInfo>& installedThemes) +{ + return std::any_of(installedThemes.begin(), installedThemes.end(), + SameTheme(theme)); +} + +} // end anonymous namespace + +IconThemeSelector::IconThemeSelector() + : mUseHighContrastTheme(false) + , mPreferDarkIconTheme(false) +{ +} + +/*static*/ OUString +IconThemeSelector::GetIconThemeForDesktopEnvironment(const OUString& desktopEnvironment) +{ + if (comphelper::LibreOfficeKit::isActive()) + return "colibre"; + +#ifdef _WIN32 + (void)desktopEnvironment; + return "colibre"; +#else + OUString r; + if ( desktopEnvironment.equalsIgnoreAsciiCase("plasma5") || + desktopEnvironment.equalsIgnoreAsciiCase("lxqt") ) { + r = "breeze"; + } + else if ( desktopEnvironment.equalsIgnoreAsciiCase("macosx") ) { + r = "sukapura"; + } + else if ( desktopEnvironment.equalsIgnoreAsciiCase("gnome") || + desktopEnvironment.equalsIgnoreAsciiCase("mate") || + desktopEnvironment.equalsIgnoreAsciiCase("unity") ) { + r = "elementary"; + } else + { + r = FALLBACK_ICON_THEME_ID; + } + return r; +#endif // _WIN32 +} + +OUString +IconThemeSelector::SelectIconThemeForDesktopEnvironment( + const std::vector<IconThemeInfo>& installedThemes, + const OUString& desktopEnvironment) const +{ + if (!mPreferredIconTheme.isEmpty()) { + if (icon_theme_is_in_installed_themes(mPreferredIconTheme, installedThemes)) { + return mPreferredIconTheme; + } + //if a dark variant is preferred, and we didn't have an exact match, then try our one and only dark theme + if (mPreferDarkIconTheme && icon_theme_is_in_installed_themes("breeze_dark", installedThemes)) { + return "breeze_dark"; + } + } + + OUString themeForDesktop = GetIconThemeForDesktopEnvironment(desktopEnvironment); + if (icon_theme_is_in_installed_themes(themeForDesktop, installedThemes)) { + return themeForDesktop; + } + + return ReturnFallback(installedThemes); +} + +OUString +IconThemeSelector::SelectIconTheme( + const std::vector<IconThemeInfo>& installedThemes, + const OUString& theme) const +{ + if (mUseHighContrastTheme) { + if (icon_theme_is_in_installed_themes(IconThemeInfo::HIGH_CONTRAST_ID, installedThemes)) { + return IconThemeInfo::HIGH_CONTRAST_ID; + } + } + + if (icon_theme_is_in_installed_themes(theme, installedThemes)) { + return theme; + } + + return ReturnFallback(installedThemes); +} + +void +IconThemeSelector::SetUseHighContrastTheme(bool v) +{ + mUseHighContrastTheme = v; +} + +void +IconThemeSelector::SetPreferredIconTheme(const OUString& theme, bool bDarkIconTheme) +{ + // lower case theme name, and (tdf#120175) replace - with _ + // see icon-themes/README + mPreferredIconTheme = theme.toAsciiLowerCase().replace('-','_'); + mPreferDarkIconTheme = bDarkIconTheme; +} + +bool +IconThemeSelector::operator==(const vcl::IconThemeSelector& other) const +{ + if (this == &other) { + return true; + } + if (mPreferredIconTheme != other.mPreferredIconTheme) { + return false; + } + if (mPreferDarkIconTheme != other.mPreferDarkIconTheme) { + return false; + } + if (mUseHighContrastTheme != other.mUseHighContrastTheme) { + return false; + } + return true; +} + +bool +IconThemeSelector::operator!=(const vcl::IconThemeSelector& other) const +{ + return !(*this == other); +} + +/*static*/ OUString +IconThemeSelector::ReturnFallback(const std::vector<IconThemeInfo>& installedThemes) +{ + if (!installedThemes.empty()) { + return installedThemes.front().GetThemeId(); + } + else { + return FALLBACK_ICON_THEME_ID; + } +} + +} /* namespace vcl */ + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/source/app/brand.cxx b/vcl/source/app/brand.cxx new file mode 100644 index 000000000..290606106 --- /dev/null +++ b/vcl/source/app/brand.cxx @@ -0,0 +1,73 @@ +/* -*- 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_folders.h> + +#include <rtl/ustring.hxx> +#include <rtl/bootstrap.hxx> +#include <osl/process.h> +#include <tools/urlobj.hxx> +#include <tools/stream.hxx> +#include <i18nlangtag/languagetag.hxx> +#include <vcl/pngread.hxx> +#include <vcl/svapp.hxx> + +namespace { + bool loadPng( const OUString & rPath, BitmapEx &rBitmap) + { + INetURLObject aObj( rPath ); + SvFileStream aStrm( aObj.PathToFileName(), StreamMode::STD_READ ); + if ( !aStrm.GetError() ) { + vcl::PNGReader aReader( aStrm ); + rBitmap = aReader.Read(); + return !rBitmap.IsEmpty(); + } + else + return false; + } + bool tryLoadPng( const OUString& rBaseDir, const OUString& rName, BitmapEx& rBitmap ) + { + return loadPng( rBaseDir + "/" LIBO_ETC_FOLDER + rName, rBitmap); + } +} + +bool Application::LoadBrandBitmap (const char* pName, BitmapEx &rBitmap) +{ + // TODO - if we want more flexibility we could add a branding path + // in an rc file perhaps fallback to "about.bmp" + OUString aBaseDir( "$BRAND_BASE_DIR"); + rtl::Bootstrap::expandMacros( aBaseDir ); + OUString aBaseName( "/" + OUString::createFromAscii( pName ) ); + OUString aPng( ".png" ); + + rtl_Locale *pLoc = nullptr; + osl_getProcessLocale (&pLoc); + LanguageTag aLanguageTag( *pLoc); + + ::std::vector< OUString > aFallbacks( aLanguageTag.getFallbackStrings( true)); + for (const OUString & aFallback : aFallbacks) + { + if (tryLoadPng( aBaseDir, aBaseName + "-" + aFallback + aPng, rBitmap)) + return true; + } + + return tryLoadPng( aBaseDir, aBaseName + aPng, rBitmap); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/source/app/customweld.cxx b/vcl/source/app/customweld.cxx new file mode 100644 index 000000000..c08a38208 --- /dev/null +++ b/vcl/source/app/customweld.cxx @@ -0,0 +1,105 @@ +/* -*- 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/. + */ + +#include <vcl/customweld.hxx> + +namespace weld +{ +CustomWidgetController::~CustomWidgetController() {} + +IMPL_LINK_NOARG(CustomWidgetController, DragBeginHdl, weld::DrawingArea&, bool) +{ + return StartDrag(); +} + +CustomWeld::CustomWeld(weld::Builder& rBuilder, const OString& rDrawingId, + CustomWidgetController& rWidgetController) + : m_rWidgetController(rWidgetController) + , m_xDrawingArea(rBuilder.weld_drawing_area(rDrawingId, rWidgetController.CreateAccessible(), + rWidgetController.GetUITestFactory(), + &rWidgetController)) +{ + m_xDrawingArea->connect_size_allocate(LINK(this, CustomWeld, DoResize)); + m_xDrawingArea->connect_draw(LINK(this, CustomWeld, DoPaint)); + m_xDrawingArea->connect_mouse_press(LINK(this, CustomWeld, DoMouseButtonDown)); + m_xDrawingArea->connect_mouse_move(LINK(this, CustomWeld, DoMouseMove)); + m_xDrawingArea->connect_mouse_release(LINK(this, CustomWeld, DoMouseButtonUp)); + m_xDrawingArea->connect_focus_in(LINK(this, CustomWeld, DoGetFocus)); + m_xDrawingArea->connect_focus_out(LINK(this, CustomWeld, DoLoseFocus)); + m_xDrawingArea->connect_key_press(LINK(this, CustomWeld, DoKeyPress)); + m_xDrawingArea->connect_focus_rect(LINK(this, CustomWeld, DoFocusRect)); + m_xDrawingArea->connect_style_updated(LINK(this, CustomWeld, DoStyleUpdated)); + m_xDrawingArea->connect_command(LINK(this, CustomWeld, DoCommand)); + m_xDrawingArea->connect_query_tooltip(LINK(this, CustomWeld, DoRequestHelp)); + m_xDrawingArea->connect_im_context_get_surrounding(LINK(this, CustomWeld, DoGetSurrounding)); + m_rWidgetController.SetDrawingArea(m_xDrawingArea.get()); +} + +IMPL_LINK(CustomWeld, DoResize, const Size&, rSize, void) +{ + m_rWidgetController.SetOutputSizePixel(rSize); + m_rWidgetController.Resize(); +} + +IMPL_LINK(CustomWeld, DoPaint, weld::DrawingArea::draw_args, aPayload, void) +{ + m_rWidgetController.Paint(aPayload.first, aPayload.second); +} + +IMPL_LINK(CustomWeld, DoMouseButtonDown, const MouseEvent&, rMEvt, bool) +{ + return m_rWidgetController.MouseButtonDown(rMEvt); +} + +IMPL_LINK(CustomWeld, DoMouseMove, const MouseEvent&, rMEvt, bool) +{ + return m_rWidgetController.MouseMove(rMEvt); +} + +IMPL_LINK(CustomWeld, DoMouseButtonUp, const MouseEvent&, rMEvt, bool) +{ + return m_rWidgetController.MouseButtonUp(rMEvt); +} + +IMPL_LINK_NOARG(CustomWeld, DoGetFocus, weld::Widget&, void) { m_rWidgetController.GetFocus(); } + +IMPL_LINK_NOARG(CustomWeld, DoLoseFocus, weld::Widget&, void) { m_rWidgetController.LoseFocus(); } + +IMPL_LINK(CustomWeld, DoKeyPress, const KeyEvent&, rKEvt, bool) +{ + return m_rWidgetController.KeyInput(rKEvt); +} + +IMPL_LINK_NOARG(CustomWeld, DoFocusRect, weld::Widget&, tools::Rectangle) +{ + return m_rWidgetController.GetFocusRect(); +} + +IMPL_LINK_NOARG(CustomWeld, DoStyleUpdated, weld::Widget&, void) +{ + m_rWidgetController.StyleUpdated(); +} + +IMPL_LINK(CustomWeld, DoCommand, const CommandEvent&, rPos, bool) +{ + return m_rWidgetController.Command(rPos); +} + +IMPL_LINK(CustomWeld, DoRequestHelp, tools::Rectangle&, rHelpArea, OUString) +{ + return m_rWidgetController.RequestHelp(rHelpArea); +} + +IMPL_LINK(CustomWeld, DoGetSurrounding, OUString&, rSurrounding, int) +{ + return m_rWidgetController.GetSurroundingText(rSurrounding); +} +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/source/app/dbggui.cxx b/vcl/source/app/dbggui.cxx new file mode 100644 index 000000000..cfeeeaf5d --- /dev/null +++ b/vcl/source/app/dbggui.cxx @@ -0,0 +1,50 @@ +/* -*- 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> + +#ifndef NDEBUG + +#include <tools/debug.hxx> + +#include <svdata.hxx> +#include <dbggui.hxx> + +#include <salinst.hxx> + +using namespace ::com::sun::star; + +static void ImplDbgTestSolarMutex() +{ + assert(ImplGetSVData()->mpDefInst->GetYieldMutex()->IsCurrentThread() && "SolarMutex not owned!"); +} + +void DbgGUIInitSolarMutexCheck() +{ + DbgSetTestSolarMutex( ImplDbgTestSolarMutex ); +} + +void DbgGUIDeInitSolarMutexCheck() +{ + DbgSetTestSolarMutex( nullptr ); +} + +#endif // NDEBUG + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/source/app/dndhelp.cxx b/vcl/source/app/dndhelp.cxx new file mode 100644 index 000000000..ff4beb789 --- /dev/null +++ b/vcl/source/app/dndhelp.cxx @@ -0,0 +1,139 @@ +/* -*- 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 <vcl/dndhelp.hxx> + +#include <cppuhelper/queryinterface.hxx> + +using namespace ::com::sun::star; + +vcl::unohelper::DragAndDropClient::~DragAndDropClient() COVERITY_NOEXCEPT_FALSE {} + +void vcl::unohelper::DragAndDropClient::dragGestureRecognized( const css::datatransfer::dnd::DragGestureEvent& /*dge*/ ) +{ +} + +void vcl::unohelper::DragAndDropClient::dragDropEnd( const css::datatransfer::dnd::DragSourceDropEvent& /*dsde*/ ) +{ +} + +void vcl::unohelper::DragAndDropClient::drop( const css::datatransfer::dnd::DropTargetDropEvent& /*dtde*/ ) +{ +} + +void vcl::unohelper::DragAndDropClient::dragEnter( const css::datatransfer::dnd::DropTargetDragEnterEvent& /*dtdee*/ ) +{ +} + +void vcl::unohelper::DragAndDropClient::dragExit( const css::datatransfer::dnd::DropTargetEvent& /*dte*/ ) +{ +} + +void vcl::unohelper::DragAndDropClient::dragOver( const css::datatransfer::dnd::DropTargetDragEvent& /*dtde*/ ) +{ +} + +vcl::unohelper::DragAndDropWrapper::DragAndDropWrapper( DragAndDropClient* pClient ) +{ + mpClient = pClient; +} + +vcl::unohelper::DragAndDropWrapper::~DragAndDropWrapper() +{ +} + +// uno::XInterface +uno::Any vcl::unohelper::DragAndDropWrapper::queryInterface( const uno::Type & rType ) +{ + uno::Any aRet = ::cppu::queryInterface( rType, + static_cast< css::lang::XEventListener* >( static_cast<css::datatransfer::dnd::XDragGestureListener*>(this) ), + static_cast< css::datatransfer::dnd::XDragGestureListener* >(this), + static_cast< css::datatransfer::dnd::XDragSourceListener* >(this), + static_cast< css::datatransfer::dnd::XDropTargetListener* >(this) ); + return (aRet.hasValue() ? aRet : OWeakObject::queryInterface( rType )); +} + +// css::lang::XEventListener +void vcl::unohelper::DragAndDropWrapper::disposing( const css::lang::EventObject& rEvent ) +{ + // Empty Source means it's the client, because the client is not a XInterface + if ( !rEvent.Source.is() ) + mpClient = nullptr; +} + +// css::datatransfer::dnd::XDragGestureListener +void vcl::unohelper::DragAndDropWrapper::dragGestureRecognized( const css::datatransfer::dnd::DragGestureEvent& rDGE ) +{ + if ( mpClient ) + mpClient->dragGestureRecognized( rDGE ); +} + +// css::datatransfer::dnd::XDragSourceListener +void vcl::unohelper::DragAndDropWrapper::dragDropEnd( const css::datatransfer::dnd::DragSourceDropEvent& rDSDE ) +{ + if ( mpClient ) + mpClient->dragDropEnd( rDSDE ); +} + +void vcl::unohelper::DragAndDropWrapper::dragEnter( const css::datatransfer::dnd::DragSourceDragEvent& ) +{ +} + +void vcl::unohelper::DragAndDropWrapper::dragExit( const css::datatransfer::dnd::DragSourceEvent& ) +{ +} + +void vcl::unohelper::DragAndDropWrapper::dragOver( const css::datatransfer::dnd::DragSourceDragEvent& ) +{ +} + +void vcl::unohelper::DragAndDropWrapper::dropActionChanged( const css::datatransfer::dnd::DragSourceDragEvent& ) +{ +} + +// css::datatransfer::dnd::XDropTargetListener +void vcl::unohelper::DragAndDropWrapper::drop( const css::datatransfer::dnd::DropTargetDropEvent& rDTDE ) +{ + if ( mpClient ) + mpClient->drop( rDTDE ); +} + +void vcl::unohelper::DragAndDropWrapper::dragEnter( const css::datatransfer::dnd::DropTargetDragEnterEvent& rDTDEE ) +{ + if ( mpClient ) + mpClient->dragEnter( rDTDEE ); +} + +void vcl::unohelper::DragAndDropWrapper::dragExit( const css::datatransfer::dnd::DropTargetEvent& dte ) +{ + if ( mpClient ) + mpClient->dragExit( dte ); +} + +void vcl::unohelper::DragAndDropWrapper::dragOver( const css::datatransfer::dnd::DropTargetDragEvent& rDTDE ) +{ + if ( mpClient ) + mpClient->dragOver( rDTDE ); +} + +void vcl::unohelper::DragAndDropWrapper::dropActionChanged( const css::datatransfer::dnd::DropTargetDragEvent& ) +{ +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/source/app/help.cxx b/vcl/source/app/help.cxx new file mode 100644 index 000000000..ebe7588c5 --- /dev/null +++ b/vcl/source/app/help.cxx @@ -0,0 +1,677 @@ +/* -*- 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 <comphelper/string.hxx> +#include <sal/log.hxx> + +#include <tools/diagnose_ex.h> +#include <tools/time.hxx> + +#include <vcl/window.hxx> +#include <vcl/event.hxx> +#include <vcl/svapp.hxx> +#include <vcl/wrkwin.hxx> +#include <vcl/help.hxx> +#include <vcl/settings.hxx> + +#include <helpwin.hxx> +#include <salframe.hxx> +#include <svdata.hxx> + +#define HELPWINSTYLE_QUICK 0 +#define HELPWINSTYLE_BALLOON 1 + +#define HELPTEXTMARGIN_QUICK 3 +#define HELPTEXTMARGIN_BALLOON 6 + +#define HELPTEXTMAXLEN 150 + +Help::Help() +{ +} + +Help::~Help() +{ +} + +bool Help::Start( const OUString&, const vcl::Window* ) +{ + return false; +} + +bool Help::Start(const OUString&, weld::Widget*) +{ + return false; +} + +void Help::SearchKeyword( const OUString& ) +{ +} + +OUString Help::GetHelpText( const OUString&, const vcl::Window* ) +{ + return OUString(); +} + +OUString Help::GetHelpText( const OUString&, const weld::Widget* ) +{ + return OUString(); +} + +void Help::EnableContextHelp() +{ + ImplGetSVHelpData().mbContextHelp = true; +} + +void Help::DisableContextHelp() +{ + ImplGetSVHelpData().mbContextHelp = false; +} + +bool Help::IsContextHelpEnabled() +{ + return ImplGetSVHelpData().mbContextHelp; +} + +void Help::EnableExtHelp() +{ + ImplGetSVHelpData().mbExtHelp = true; +} + +void Help::DisableExtHelp() +{ + ImplGetSVHelpData().mbExtHelp = false; +} + +bool Help::IsExtHelpEnabled() +{ + return ImplGetSVHelpData().mbExtHelp; +} + +bool Help::StartExtHelp() +{ + ImplSVData* pSVData = ImplGetSVData(); + ImplSVHelpData& aHelpData = ImplGetSVHelpData(); + + if ( aHelpData.mbExtHelp && !aHelpData.mbExtHelpMode ) + { + aHelpData.mbExtHelpMode = true; + aHelpData.mbOldBalloonMode = aHelpData.mbBalloonHelp; + aHelpData.mbBalloonHelp = true; + if (pSVData->maFrameData.mpAppWin) + pSVData->maFrameData.mpAppWin->ImplGenerateMouseMove(); + return true; + } + + return false; +} + +bool Help::EndExtHelp() +{ + ImplSVData* pSVData = ImplGetSVData(); + ImplSVHelpData& aHelpData = ImplGetSVHelpData(); + + if ( aHelpData.mbExtHelp && aHelpData.mbExtHelpMode ) + { + aHelpData.mbExtHelpMode = false; + aHelpData.mbBalloonHelp = aHelpData.mbOldBalloonMode; + if (pSVData->maFrameData.mpAppWin) + pSVData->maFrameData.mpAppWin->ImplGenerateMouseMove(); + return true; + } + + return false; +} + +void Help::EnableBalloonHelp() +{ + ImplGetSVHelpData().mbBalloonHelp = true; +} + +void Help::DisableBalloonHelp() +{ + ImplGetSVHelpData().mbBalloonHelp = false; +} + +bool Help::IsBalloonHelpEnabled() +{ + return ImplGetSVHelpData().mbBalloonHelp; +} + +void Help::ShowBalloon( vcl::Window* pParent, + const Point& rScreenPos, const tools::Rectangle& rRect, + const OUString& rHelpText ) +{ + ImplShowHelpWindow( pParent, HELPWINSTYLE_BALLOON, QuickHelpFlags::NONE, + rHelpText, rScreenPos, rRect ); +} + +void Help::EnableQuickHelp() +{ + ImplGetSVHelpData().mbQuickHelp = true; +} + +void Help::DisableQuickHelp() +{ + ImplGetSVHelpData().mbQuickHelp = false; +} + +bool Help::IsQuickHelpEnabled() +{ + return ImplGetSVHelpData().mbQuickHelp; +} + +void Help::ShowQuickHelp( vcl::Window* pParent, + const tools::Rectangle& rScreenRect, + const OUString& rHelpText, + QuickHelpFlags nStyle ) +{ + sal_uInt16 nHelpWinStyle = ( nStyle & QuickHelpFlags::TipStyleBalloon ) ? HELPWINSTYLE_BALLOON : HELPWINSTYLE_QUICK; + ImplShowHelpWindow( pParent, nHelpWinStyle, nStyle, + rHelpText, + pParent->OutputToScreenPixel( pParent->GetPointerPosPixel() ), rScreenRect ); +} + +void Help::HideBalloonAndQuickHelp() +{ + HelpTextWindow const * pHelpWin = ImplGetSVHelpData().mpHelpWin; + bool const bIsVisible = ( pHelpWin != nullptr ) && pHelpWin->IsVisible(); + ImplDestroyHelpWindow( bIsVisible ); +} + +void* Help::ShowPopover(vcl::Window* pParent, const tools::Rectangle& rScreenRect, + const OUString& rText, QuickHelpFlags nStyle) +{ + void* nId = pParent->ImplGetFrame()->ShowPopover(rText, pParent, rScreenRect, nStyle); + if (nId) + { + //popovers are handled natively, return early + return nId; + } + + sal_uInt16 nHelpWinStyle = ( nStyle & QuickHelpFlags::TipStyleBalloon ) ? HELPWINSTYLE_BALLOON : HELPWINSTYLE_QUICK; + VclPtrInstance<HelpTextWindow> pHelpWin( pParent, rText, nHelpWinStyle, nStyle ); + + nId = pHelpWin.get(); + UpdatePopover(nId, pParent, rScreenRect, rText); + + pHelpWin->ShowHelp(true); + return nId; +} + +void Help::UpdatePopover(void* nId, vcl::Window* pParent, const tools::Rectangle& rScreenRect, + const OUString& rText) +{ + if (pParent->ImplGetFrame()->UpdatePopover(nId, rText, pParent, rScreenRect)) + { + //popovers are handled natively, return early + return; + } + + HelpTextWindow* pHelpWin = static_cast< HelpTextWindow* >( nId ); + ENSURE_OR_RETURN_VOID( pHelpWin != nullptr, "Help::UpdatePopover: invalid ID!" ); + + Size aSz = pHelpWin->CalcOutSize(); + pHelpWin->SetOutputSizePixel( aSz ); + ImplSetHelpWindowPos( pHelpWin, pHelpWin->GetWinStyle(), pHelpWin->GetStyle(), + pParent->OutputToScreenPixel( pParent->GetPointerPosPixel() ), rScreenRect ); + + pHelpWin->SetHelpText( rText ); + pHelpWin->Invalidate(); +} + +void Help::HidePopover(vcl::Window const * pParent, void* nId) +{ + if (pParent->ImplGetFrame()->HidePopover(nId)) + { + //popovers are handled natively, return early + return; + } + + VclPtr<HelpTextWindow> pHelpWin = static_cast<HelpTextWindow*>(nId); + vcl::Window* pFrameWindow = pHelpWin->ImplGetFrameWindow(); + pHelpWin->Hide(); + // trigger update, so that a Paint is instantly triggered since we do not save the background + pFrameWindow->ImplUpdateAll(); + pHelpWin.disposeAndClear(); + ImplGetSVHelpData().mnLastHelpHideTime = tools::Time::GetSystemTicks(); +} + +HelpTextWindow::HelpTextWindow( vcl::Window* pParent, const OUString& rText, sal_uInt16 nHelpWinStyle, QuickHelpFlags nStyle ) : + FloatingWindow( pParent, WB_SYSTEMWINDOW|WB_TOOLTIPWIN ), // #105827# if we change the parent, mirroring will not work correctly when positioning this window + maHelpText( rText ) +{ + SetType( WindowType::HELPTEXTWINDOW ); + ImplSetMouseTransparent( true ); + mnHelpWinStyle = nHelpWinStyle; + mnStyle = nStyle; + + if( mnStyle & QuickHelpFlags::BiDiRtl ) + { + ComplexTextLayoutFlags nLayoutMode = GetLayoutMode(); + nLayoutMode |= ComplexTextLayoutFlags::BiDiRtl | ComplexTextLayoutFlags::TextOriginLeft; + SetLayoutMode( nLayoutMode ); + } + SetHelpText( rText ); + Window::SetHelpText( rText ); + + if ( ImplGetSVHelpData().mbSetKeyboardHelp ) + ImplGetSVHelpData().mbKeyboardHelp = true; + + + maShowTimer.SetInvokeHandler( LINK( this, HelpTextWindow, TimerHdl ) ); + maShowTimer.SetDebugName( "vcl::HelpTextWindow maShowTimer" ); + + const HelpSettings& rHelpSettings = pParent->GetSettings().GetHelpSettings(); + maHideTimer.SetTimeout( rHelpSettings.GetTipTimeout() ); + maHideTimer.SetInvokeHandler( LINK( this, HelpTextWindow, TimerHdl ) ); + maHideTimer.SetDebugName( "vcl::HelpTextWindow maHideTimer" ); +} + +void HelpTextWindow::ApplySettings(vcl::RenderContext& rRenderContext) +{ + const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings(); + SetPointFont(rRenderContext, rStyleSettings.GetHelpFont()); + rRenderContext.SetTextColor(rStyleSettings.GetHelpTextColor()); + rRenderContext.SetTextAlign(ALIGN_TOP); + + if (rRenderContext.IsNativeControlSupported(ControlType::Tooltip, ControlPart::Entire)) + { + EnableChildTransparentMode(); + SetParentClipMode(ParentClipMode::NoClip); + SetPaintTransparent(true); + rRenderContext.SetBackground(); + } + else + rRenderContext.SetBackground(Wallpaper(rStyleSettings.GetHelpColor())); + + if (rStyleSettings.GetHelpColor().IsDark()) + rRenderContext.SetLineColor(COL_WHITE); + else + rRenderContext.SetLineColor(COL_BLACK); + rRenderContext.SetFillColor(); +} + +HelpTextWindow::~HelpTextWindow() +{ + disposeOnce(); +} + +void HelpTextWindow::dispose() +{ + maShowTimer.Stop(); + maHideTimer.Stop(); + + if( this == ImplGetSVHelpData().mpHelpWin ) + ImplGetSVHelpData().mpHelpWin = nullptr; + FloatingWindow::dispose(); +} + +void HelpTextWindow::SetHelpText( const OUString& rHelpText ) +{ + maHelpText = rHelpText; + ApplySettings(*this); + if ( mnHelpWinStyle == HELPWINSTYLE_QUICK && maHelpText.getLength() < HELPTEXTMAXLEN && maHelpText.indexOf('\n') < 0) + { + Size aSize; + aSize.setHeight( GetTextHeight() ); + if ( mnStyle & QuickHelpFlags::CtrlText ) + aSize.setWidth( GetCtrlTextWidth( maHelpText ) ); + else + aSize.setWidth( GetTextWidth( maHelpText ) ); + maTextRect = tools::Rectangle( Point( HELPTEXTMARGIN_QUICK, HELPTEXTMARGIN_QUICK ), aSize ); + } + else // HELPWINSTYLE_BALLOON + { + sal_Int32 nCharsInLine = 35 + ((maHelpText.getLength()/100)*5); + // average width to have all windows consistent + OUStringBuffer aBuf; + comphelper::string::padToLength(aBuf, nCharsInLine, 'x'); + OUString aXXX = aBuf.makeStringAndClear(); + long nWidth = GetTextWidth( aXXX ); + Size aTmpSize( nWidth, 0x7FFFFFFF ); + tools::Rectangle aTry1( Point(), aTmpSize ); + DrawTextFlags nDrawFlags = DrawTextFlags::MultiLine | DrawTextFlags::WordBreak | + DrawTextFlags::Left | DrawTextFlags::Top; + if ( mnStyle & QuickHelpFlags::CtrlText ) + nDrawFlags |= DrawTextFlags::Mnemonic; + tools::Rectangle aTextRect = GetTextRect( aTry1, maHelpText, nDrawFlags ); + + // get a better width later... + maTextRect = aTextRect; + + // safety distance... + maTextRect.SetPos( Point( HELPTEXTMARGIN_BALLOON, HELPTEXTMARGIN_BALLOON ) ); + } + + Size aSize( CalcOutSize() ); + SetOutputSizePixel( aSize ); +} + +void HelpTextWindow::ImplShow() +{ + VclPtr<HelpTextWindow> xWindow( this ); + Show( true, ShowFlags::NoActivate ); + if( !xWindow->IsDisposed() ) + PaintImmediately(); +} + +void HelpTextWindow::Paint( vcl::RenderContext& rRenderContext, const tools::Rectangle& ) +{ + // paint native background + bool bNativeOK = false; + if (rRenderContext.IsNativeControlSupported(ControlType::Tooltip, ControlPart::Entire)) + { + tools::Rectangle aCtrlRegion(Point(0, 0), GetOutputSizePixel()); + ImplControlValue aControlValue; + bNativeOK = rRenderContext.DrawNativeControl(ControlType::Tooltip, ControlPart::Entire, aCtrlRegion, + ControlState::NONE, aControlValue, OUString()); + } + + // paint text + if (mnHelpWinStyle == HELPWINSTYLE_QUICK && maHelpText.getLength() < HELPTEXTMAXLEN && maHelpText.indexOf('\n') < 0) + { + if ( mnStyle & QuickHelpFlags::CtrlText ) + rRenderContext.DrawCtrlText(maTextRect.TopLeft(), maHelpText); + else + rRenderContext.DrawText(maTextRect.TopLeft(), maHelpText); + } + else // HELPWINSTYLE_BALLOON + { + DrawTextFlags nDrawFlags = DrawTextFlags::MultiLine|DrawTextFlags::WordBreak| + DrawTextFlags::Left|DrawTextFlags::Top; + if (mnStyle & QuickHelpFlags::CtrlText) + nDrawFlags |= DrawTextFlags::Mnemonic; + rRenderContext.DrawText(maTextRect, maHelpText, nDrawFlags); + } + + // border + if (!bNativeOK) + { + Size aSz = GetOutputSizePixel(); + rRenderContext.DrawRect(tools::Rectangle(Point(), aSz)); + if (mnHelpWinStyle == HELPWINSTYLE_BALLOON) + { + aSz.AdjustWidth( -2 ); + aSz.AdjustHeight( -2 ); + Color aColor(rRenderContext.GetLineColor()); + rRenderContext.SetLineColor(COL_GRAY); + rRenderContext.DrawRect(tools::Rectangle(Point(1, 1), aSz)); + rRenderContext.SetLineColor(aColor); + } + } +} + +void HelpTextWindow::ShowHelp(bool bNoDelay) +{ + sal_uLong nTimeout = 0; + if (!bNoDelay) + { + // In case of ExtendedHelp display help sooner + if ( ImplGetSVHelpData().mbExtHelpMode ) + nTimeout = 15; + else + { + if ( mnHelpWinStyle == HELPWINSTYLE_QUICK ) + nTimeout = HelpSettings::GetTipDelay(); + else + nTimeout = HelpSettings::GetBalloonDelay(); + } + } + + maShowTimer.SetTimeout( nTimeout ); + maShowTimer.Start(); +} + +IMPL_LINK( HelpTextWindow, TimerHdl, Timer*, pTimer, void) +{ + if ( pTimer == &maShowTimer ) + { + if ( mnHelpWinStyle == HELPWINSTYLE_QUICK ) + { + // start auto-hide-timer for non-ShowTip windows + if ( this == ImplGetSVHelpData().mpHelpWin ) + maHideTimer.Start(); + } + ImplShow(); + } + else + { + SAL_WARN_IF( pTimer != &maHideTimer, "vcl", "HelpTextWindow::TimerHdl with bad Timer" ); + ImplDestroyHelpWindow( true ); + } +} + +Size HelpTextWindow::CalcOutSize() const +{ + Size aSz = maTextRect.GetSize(); + aSz.AdjustWidth(2*maTextRect.Left() ); + aSz.AdjustHeight(2*maTextRect.Top() ); + return aSz; +} + +void HelpTextWindow::RequestHelp( const HelpEvent& /*rHEvt*/ ) +{ + // Just to assure that Window::RequestHelp() is not called by + // ShowQuickHelp/ShowBalloonHelp in the HelpTextWindow. +} + +OUString HelpTextWindow::GetText() const +{ + return maHelpText; +} + +void ImplShowHelpWindow( vcl::Window* pParent, sal_uInt16 nHelpWinStyle, QuickHelpFlags nStyle, + const OUString& rHelpText, + const Point& rScreenPos, const tools::Rectangle& rHelpArea ) +{ + if (pParent->ImplGetFrame()->ShowTooltip(rHelpText, rHelpArea)) + { + //tooltips are handled natively, return early + return; + } + + ImplSVHelpData& aHelpData = ImplGetSVHelpData(); + + if (rHelpText.isEmpty() && !aHelpData.mbRequestingHelp) + return; + + VclPtr<HelpTextWindow> pHelpWin = aHelpData.mpHelpWin; + bool bNoDelay = false; + if ( pHelpWin ) + { + SAL_WARN_IF( pHelpWin == pParent, "vcl", "HelpInHelp ?!" ); + + if ( ( rHelpText.isEmpty() + || ( pHelpWin->GetWinStyle() != nHelpWinStyle ) + ) + && aHelpData.mbRequestingHelp + ) + { + // remove help window if no HelpText or + // other help mode. but keep it if we are scrolling, ie not requesting help + bool bWasVisible = pHelpWin->IsVisible(); + if ( bWasVisible ) + bNoDelay = true; // display it quickly if we were already in quick help mode + pHelpWin = nullptr; + ImplDestroyHelpWindow( bWasVisible ); + } + else + { + bool const bUpdate = (pHelpWin->GetHelpText() != rHelpText) || + ((pHelpWin->GetHelpArea() != rHelpArea) && aHelpData.mbRequestingHelp); + if (bUpdate) + { + pHelpWin->SetHelpText( rHelpText ); + // approach mouse position + ImplSetHelpWindowPos( pHelpWin, nHelpWinStyle, nStyle, rScreenPos, rHelpArea ); + if( pHelpWin->IsVisible() ) + pHelpWin->Invalidate(); + } + } + } + + if (pHelpWin || rHelpText.isEmpty()) + return; + + sal_uInt64 nCurTime = tools::Time::GetSystemTicks(); + if ( ( nCurTime - aHelpData.mnLastHelpHideTime ) < HelpSettings::GetTipDelay() ) + bNoDelay = true; + + pHelpWin = VclPtr<HelpTextWindow>::Create( pParent, rHelpText, nHelpWinStyle, nStyle ); + aHelpData.mpHelpWin = pHelpWin; + pHelpWin->SetHelpArea( rHelpArea ); + + // positioning + Size aSz = pHelpWin->CalcOutSize(); + pHelpWin->SetOutputSizePixel( aSz ); + ImplSetHelpWindowPos( pHelpWin, nHelpWinStyle, nStyle, rScreenPos, rHelpArea ); + // if not called from Window::RequestHelp, then without delay... + if ( !aHelpData.mbRequestingHelp ) + bNoDelay = true; + pHelpWin->ShowHelp(bNoDelay); + +} + +void ImplDestroyHelpWindow( bool bUpdateHideTime ) +{ + ImplDestroyHelpWindow(ImplGetSVHelpData(), bUpdateHideTime); +} + +void ImplDestroyHelpWindow(ImplSVHelpData& rHelpData, bool bUpdateHideTime) +{ + VclPtr<HelpTextWindow> pHelpWin = rHelpData.mpHelpWin; + if( pHelpWin ) + { + rHelpData.mpHelpWin = nullptr; + rHelpData.mbKeyboardHelp = false; + pHelpWin->Hide(); + pHelpWin.disposeAndClear(); + if( bUpdateHideTime ) + rHelpData.mnLastHelpHideTime = tools::Time::GetSystemTicks(); + } +} + +void ImplSetHelpWindowPos( vcl::Window* pHelpWin, sal_uInt16 nHelpWinStyle, QuickHelpFlags nStyle, + const Point& rPos, const tools::Rectangle& rHelpArea ) +{ + Point aPos = rPos; + Size aSz = pHelpWin->GetSizePixel(); + tools::Rectangle aScreenRect = pHelpWin->ImplGetFrameWindow()->GetDesktopRectPixel(); + aPos = pHelpWin->GetParent()->ImplGetFrameWindow()->OutputToAbsoluteScreenPixel( aPos ); + // get mouse screen coords + Point aMousePos( pHelpWin->GetParent()->ImplGetFrameWindow()->GetPointerPosPixel() ); + aMousePos = pHelpWin->GetParent()->ImplGetFrameWindow()->OutputToAbsoluteScreenPixel( aMousePos ); + + if ( nHelpWinStyle == HELPWINSTYLE_QUICK ) + { + if ( !(nStyle & QuickHelpFlags::NoAutoPos) ) + { + long nScreenHeight = aScreenRect.GetHeight(); + aPos.AdjustX( -4 ); + if ( aPos.Y() > aScreenRect.Top()+nScreenHeight-(nScreenHeight/4) ) + aPos.AdjustY( -(aSz.Height()+4) ); + else + aPos.AdjustY(21 ); + } + } + else + { + // If it's the mouse position, move the window slightly + // so the mouse pointer does not cover it + if ( aPos == aMousePos ) + { + aPos.AdjustX(12 ); + aPos.AdjustY(16 ); + } + } + + if ( nStyle & QuickHelpFlags::NoAutoPos ) + { + // convert help area to screen coords + tools::Rectangle devHelpArea( + pHelpWin->GetParent()->ImplGetFrameWindow()->OutputToAbsoluteScreenPixel( rHelpArea.TopLeft() ), + pHelpWin->GetParent()->ImplGetFrameWindow()->OutputToAbsoluteScreenPixel( rHelpArea.BottomRight() ) ); + + // which position of the rectangle? + aPos = devHelpArea.Center(); + + if ( nStyle & QuickHelpFlags::Left ) + aPos.setX( devHelpArea.Left() ); + else if ( nStyle & QuickHelpFlags::Right ) + aPos.setX( devHelpArea.Right() ); + + if ( nStyle & QuickHelpFlags::Top ) + aPos.setY( devHelpArea.Top() ); + else if ( nStyle & QuickHelpFlags::Bottom ) + aPos.setY( devHelpArea.Bottom() ); + + // which direction? + if ( nStyle & QuickHelpFlags::Left ) + ; + else if ( nStyle & QuickHelpFlags::Right ) + aPos.AdjustX( -(aSz.Width()) ); + else + aPos.AdjustX( -(aSz.Width()/2) ); + + if ( nStyle & QuickHelpFlags::Top ) + ; + else if ( nStyle & QuickHelpFlags::Bottom ) + aPos.AdjustY( -(aSz.Height()) ); + else + aPos.AdjustY( -(aSz.Height()/2) ); + } + + if ( aPos.X() < aScreenRect.Left() ) + aPos.setX( aScreenRect.Left() ); + else if ( ( aPos.X() + aSz.Width() ) > aScreenRect.Right() ) + aPos.setX( aScreenRect.Right() - aSz.Width() ); + if ( aPos.Y() < aScreenRect.Top() ) + aPos.setY( aScreenRect.Top() ); + else if ( ( aPos.Y() + aSz.Height() ) > aScreenRect.Bottom() ) + aPos.setY( aScreenRect.Bottom() - aSz.Height() ); + + if( ! (nStyle & QuickHelpFlags::NoEvadePointer) ) + { + /* the remark below should be obsolete by now as the helpwindow should + not be focusable, leaving it as a hint. However it is sensible in most + conditions to evade the mouse pointer so the content window is fully visible. + + // the popup must not appear under the mouse + // otherwise it would directly be closed due to a focus change... + */ + tools::Rectangle aHelpRect( aPos, aSz ); + if( aHelpRect.IsInside( aMousePos ) ) + { + Point delta(2,2); + Point aSize( aSz.Width(), aSz.Height() ); + Point aTest( aMousePos - aSize - delta ); + if( aTest.X() > aScreenRect.Left() && aTest.Y() > aScreenRect.Top() ) + aPos = aTest; + else + aPos = aMousePos + delta; + } + } + + vcl::Window* pWindow = pHelpWin->GetParent()->ImplGetFrameWindow(); + aPos = pWindow->AbsoluteScreenToOutputPixel( aPos ); + pHelpWin->SetPosPixel( aPos ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/source/app/i18nhelp.cxx b/vcl/source/app/i18nhelp.cxx new file mode 100644 index 000000000..ae7eed0e2 --- /dev/null +++ b/vcl/source/app/i18nhelp.cxx @@ -0,0 +1,160 @@ +/* -*- 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 <unotools/localedatawrapper.hxx> +#include <unotools/transliterationwrapper.hxx> + +#include <i18nlangtag/languagetag.hxx> +#include <i18nutil/transliteration.hxx> + +#include <rtl/ustrbuf.hxx> + +#include <vcl/i18nhelp.hxx> + +using namespace ::com::sun::star; + +vcl::I18nHelper::I18nHelper( const css::uno::Reference< css::uno::XComponentContext >& rxContext, const LanguageTag& rLanguageTag ) + : + maLanguageTag( rLanguageTag) +{ + m_xContext = rxContext; + mpLocaleDataWrapper = nullptr; + mpTransliterationWrapper= nullptr; + mbTransliterateIgnoreCase = false; +} + +vcl::I18nHelper::~I18nHelper() +{ + ImplDestroyWrappers(); +} + +void vcl::I18nHelper::ImplDestroyWrappers() +{ + mpLocaleDataWrapper.reset(); + mpTransliterationWrapper.reset(); +} + +utl::TransliterationWrapper& vcl::I18nHelper::ImplGetTransliterationWrapper() const +{ + if ( !mpTransliterationWrapper ) + { + TransliterationFlags nModules = TransliterationFlags::IGNORE_WIDTH; + if ( mbTransliterateIgnoreCase ) + nModules |= TransliterationFlags::IGNORE_CASE; + + const_cast<vcl::I18nHelper*>(this)->mpTransliterationWrapper.reset(new utl::TransliterationWrapper( m_xContext, nModules )); + const_cast<vcl::I18nHelper*>(this)->mpTransliterationWrapper->loadModuleIfNeeded( maLanguageTag.getLanguageType() ); + } + return *mpTransliterationWrapper; +} + +LocaleDataWrapper& vcl::I18nHelper::ImplGetLocaleDataWrapper() const +{ + if ( !mpLocaleDataWrapper ) + { + const_cast<vcl::I18nHelper*>(this)->mpLocaleDataWrapper.reset(new LocaleDataWrapper( m_xContext, maLanguageTag )); + } + return *mpLocaleDataWrapper; +} + +static bool is_formatting_mark( sal_Unicode c ) +{ + if( (c >= 0x200B) && (c <= 0x200F) ) // BiDi and zero-width-markers + return true; + if( (c >= 0x2028) && (c <= 0x202E) ) // BiDi and paragraph-markers + return true; + return false; +} + +/* #i100057# filter formatting marks out of strings before passing them to + the transliteration. The real solution would have been an additional TransliterationModule + to ignore these marks during transliteration; however changing the code in i18npool that actually + implements this could produce unwanted side effects. + + Of course this copying around is not really good, but looking at i18npool, one more time + will not hurt. +*/ +OUString vcl::I18nHelper::filterFormattingChars( const OUString& rStr ) +{ + sal_Int32 nLength = rStr.getLength(); + OUStringBuffer aBuf( nLength ); + const sal_Unicode* pStr = rStr.getStr(); + while( nLength-- ) + { + if( ! is_formatting_mark( *pStr ) ) + aBuf.append( *pStr ); + pStr++; + } + return aBuf.makeStringAndClear(); +} + +sal_Int32 vcl::I18nHelper::CompareString( const OUString& rStr1, const OUString& rStr2 ) const +{ + ::osl::Guard< ::osl::Mutex > aGuard( const_cast<vcl::I18nHelper*>(this)->maMutex ); + + if ( mbTransliterateIgnoreCase ) + { + // Change mbTransliterateIgnoreCase and destroy the wrapper, next call to + // ImplGetTransliterationWrapper() will create a wrapper with the correct bIgnoreCase + const_cast<vcl::I18nHelper*>(this)->mbTransliterateIgnoreCase = false; + const_cast<vcl::I18nHelper*>(this)->mpTransliterationWrapper.reset(); + } + + OUString aStr1( filterFormattingChars(rStr1) ); + OUString aStr2( filterFormattingChars(rStr2) ); + return ImplGetTransliterationWrapper().compareString( aStr1, aStr2 ); +} + +bool vcl::I18nHelper::MatchString( const OUString& rStr1, const OUString& rStr2 ) const +{ + ::osl::Guard< ::osl::Mutex > aGuard( const_cast<vcl::I18nHelper*>(this)->maMutex ); + + if ( !mbTransliterateIgnoreCase ) + { + // Change mbTransliterateIgnoreCase and destroy the wrapper, next call to + // ImplGetTransliterationWrapper() will create a wrapper with the correct bIgnoreCase + const_cast<vcl::I18nHelper*>(this)->mbTransliterateIgnoreCase = true; + const_cast<vcl::I18nHelper*>(this)->mpTransliterationWrapper.reset(); + } + + OUString aStr1( filterFormattingChars(rStr1) ); + OUString aStr2( filterFormattingChars(rStr2) ); + return ImplGetTransliterationWrapper().isMatch( aStr1, aStr2 ); +} + +bool vcl::I18nHelper::MatchMnemonic( const OUString& rString, sal_Unicode cMnemonicChar ) const +{ + ::osl::Guard< ::osl::Mutex > aGuard( const_cast<vcl::I18nHelper*>(this)->maMutex ); + + bool bEqual = false; + sal_Int32 n = rString.indexOf( '~' ); + if ( n != -1 ) + { + OUString aMatchStr = rString.copy( n+1 ); // not only one char, because of transliteration... + bEqual = MatchString( OUString(cMnemonicChar), aMatchStr ); + } + return bEqual; +} + +OUString vcl::I18nHelper::GetNum( long nNumber, sal_uInt16 nDecimals, bool bUseThousandSep, bool bTrailingZeros ) const +{ + return ImplGetLocaleDataWrapper().getNum( nNumber, nDecimals, bUseThousandSep, bTrailingZeros ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/source/app/idle.cxx b/vcl/source/app/idle.cxx new file mode 100644 index 000000000..26e1e5f22 --- /dev/null +++ b/vcl/source/app/idle.cxx @@ -0,0 +1,65 @@ +/* -*- 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 <vcl/idle.hxx> +#include <vcl/scheduler.hxx> + +Idle::Idle( bool bAuto, const char *pDebugName ) + : Timer( bAuto, pDebugName ) +{ + SetPriority( TaskPriority::DEFAULT_IDLE ); +} + +Idle::Idle( const char *pDebugName ) + : Idle( false, pDebugName ) +{ +} + +void Idle::Start() +{ + Task::Start(); + + sal_uInt64 nPeriod = Scheduler::ImmediateTimeoutMs; + if (Scheduler::GetDeterministicMode()) + { + switch ( GetPriority() ) + { + case TaskPriority::DEFAULT_IDLE: + case TaskPriority::LOWEST: + nPeriod = Scheduler::InfiniteTimeoutMs; + break; + default: + break; + } + } + + Task::StartTimer(nPeriod); +} + +sal_uInt64 Idle::UpdateMinPeriod( sal_uInt64 /* nTimeNow */ ) const +{ + return Scheduler::ImmediateTimeoutMs; +} + +AutoIdle::AutoIdle( const char *pDebugName ) + : Idle( true, pDebugName ) +{ +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/source/app/salplug.cxx b/vcl/source/app/salplug.cxx new file mode 100644 index 000000000..1868853e9 --- /dev/null +++ b/vcl/source/app/salplug.cxx @@ -0,0 +1,355 @@ +/* -*- 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 <osl/module.hxx> + +#include <rtl/bootstrap.hxx> +#include <rtl/process.h> +#include <sal/log.hxx> + +#include <salframe.hxx> +#include <salinst.hxx> +#include <config_vclplug.h> +#include <desktop/crashreport.hxx> + +#ifndef _WIN32 +#include <headless/svpinst.hxx> +#include <printerinfomanager.hxx> +#include <unx/desktops.hxx> + +#include <unistd.h> +#else +#include <saldatabasic.hxx> +#include <Windows.h> +#endif + +#include <cstdio> + +#ifdef ANDROID +#error "Android has no plugin infrastructure!" +#endif + +#if !(defined _WIN32 || defined MACOSX) +#define DESKTOPDETECT +#define HEADLESS_VCLPLUG +#endif + +extern "C" { +typedef SalInstance*(*salFactoryProc)(); +} + +namespace { + +oslModule pCloseModule = nullptr; + +SalInstance* tryInstance( const OUString& rModuleBase, bool bForce = false ) +{ +#ifdef HEADLESS_VCLPLUG + if (rModuleBase == "svp") + return svp_create_SalInstance(); +#endif + + SalInstance* pInst = nullptr; + OUString aUsedModuleBase(rModuleBase); + if (aUsedModuleBase == "kde5") + aUsedModuleBase = "kf5"; + OUString aModule( +#ifdef SAL_DLLPREFIX + SAL_DLLPREFIX +#endif + "vclplug_" + aUsedModuleBase + "lo" SAL_DLLEXTENSION ); + + osl::Module aMod; + if (aMod.loadRelative(reinterpret_cast<oslGenericFunction>(&tryInstance), aModule, SAL_LOADMODULE_GLOBAL)) + { + salFactoryProc aProc = reinterpret_cast<salFactoryProc>(aMod.getFunctionSymbol("create_SalInstance")); + if (aProc) + { + pInst = aProc(); + SAL_INFO( + "vcl.plugadapt", + "sal plugin " << aModule << " produced instance " << pInst); + if (pInst) + { + pCloseModule = static_cast<oslModule>(aMod); + aMod.release(); + + /* + * Recent GTK+ versions load their modules with RTLD_LOCAL, so we can + * not access the 'gnome_accessibility_module_shutdown' anymore. + * So make sure libgtk+ & co are still mapped into memory when + * atk-bridge's atexit handler gets called. + */ + if( aUsedModuleBase == "gtk3" || aUsedModuleBase == "gtk3_kde5" || aUsedModuleBase == "win" ) + { + pCloseModule = nullptr; + } + } + } + else + { + SAL_WARN( + "vcl.plugadapt", + "could not load symbol create_SalInstance from shared object " + << aModule); + } + } + else if (bForce) + { + SAL_WARN("vcl.plugadapt", "could not load shared object " << aModule); + } + else + { + SAL_INFO("vcl.plugadapt", "could not load shared object " << aModule); + } + + // coverity[leaked_storage] - this is on purpose + return pInst; +} + +#ifdef DESKTOPDETECT +extern "C" typedef DesktopType Fn_get_desktop_environment(); + +DesktopType get_desktop_environment() +{ + OUString aModule(DESKTOP_DETECTOR_DLL_NAME); + oslModule aMod = osl_loadModuleRelative( + reinterpret_cast< oslGenericFunction >( &tryInstance ), aModule.pData, + SAL_LOADMODULE_DEFAULT ); + DesktopType ret = DESKTOP_UNKNOWN; + if( aMod ) + { + Fn_get_desktop_environment * pSym + = reinterpret_cast<Fn_get_desktop_environment *>( + osl_getAsciiFunctionSymbol(aMod, "get_desktop_environment")); + if( pSym ) + ret = pSym(); + } + osl_unloadModule( aMod ); + return ret; +} + +SalInstance* autodetect_plugin() +{ + static const char* const pKDEFallbackList[] = + { +#if ENABLE_KF5 + "kf5", +#endif +#if ENABLE_GTK3_KDE5 + "gtk3_kde5", +#endif + "gtk3", "gen", nullptr + }; + + static const char* const pStandardFallbackList[] = + { + "gtk3", "gen", nullptr + }; + +#ifdef HEADLESS_VCLPLUG + static const char* const pHeadlessFallbackList[] = + { + "svp", nullptr + }; +#endif + + DesktopType desktop = get_desktop_environment(); + const char * const * pList = pStandardFallbackList; + int nListEntry = 0; + +#ifdef HEADLESS_VCLPLUG + // no server at all: dummy plugin + if ( desktop == DESKTOP_NONE ) + pList = pHeadlessFallbackList; + else +#endif + if ( desktop == DESKTOP_GNOME || + desktop == DESKTOP_UNITY || + desktop == DESKTOP_XFCE || + desktop == DESKTOP_MATE ) + pList = pStandardFallbackList; + else if (desktop == DESKTOP_PLASMA5 || desktop == DESKTOP_LXQT) + pList = pKDEFallbackList; + + SalInstance* pInst = nullptr; + while( pList[nListEntry] && pInst == nullptr ) + { + OUString aTry( OUString::createFromAscii( pList[nListEntry] ) ); + pInst = tryInstance( aTry ); + SAL_INFO_IF( + pInst, "vcl.plugadapt", + "plugin autodetection: " << pList[nListEntry]); + nListEntry++; + } + + return pInst; +} +#endif // DESKTOPDETECT + +#ifdef HEADLESS_VCLPLUG +// HACK to obtain Application::IsHeadlessModeEnabled early on, before +// Application::EnableHeadlessMode has potentially been called: +bool IsHeadlessModeRequested() +{ + if (Application::IsHeadlessModeEnabled()) { + return true; + } + sal_uInt32 n = rtl_getAppCommandArgCount(); + for (sal_uInt32 i = 0; i < n; ++i) { + OUString arg; + rtl_getAppCommandArg(i, &arg.pData); + if ( arg == "--headless" || arg == "-headless" ) { + return true; + } + } + return false; +} +#endif + +} // anonymous namespace + +SalInstance *CreateSalInstance() +{ + SalInstance *pInst = nullptr; + + OUString aUsePlugin; + rtl::Bootstrap::get("SAL_USE_VCLPLUGIN", aUsePlugin); + SAL_INFO_IF(!aUsePlugin.isEmpty(), "vcl", "Requested VCL plugin: " << aUsePlugin); +#ifdef HEADLESS_VCLPLUG + if (Application::IsBitmapRendering() || (aUsePlugin.isEmpty() && IsHeadlessModeRequested())) + aUsePlugin = "svp"; +#endif + + if (aUsePlugin == "svp") + { + Application::EnableBitmapRendering(); +#ifndef HEADLESS_VCLPLUG + aUsePlugin.clear(); +#endif + } + if( !aUsePlugin.isEmpty() ) + pInst = tryInstance( aUsePlugin, true ); + +#ifdef DESKTOPDETECT + if( ! pInst ) + pInst = autodetect_plugin(); +#endif + + // fallback, try everything + static const char* const pPlugin[] = { +#ifdef _WIN32 + "win" +#else +#ifdef MACOSX + "osx" +#else + "gtk3", "kf5", "gen" +#endif +#endif + }; + + for ( int i = 0; !pInst && i != SAL_N_ELEMENTS(pPlugin); ++i ) + pInst = tryInstance( OUString::createFromAscii( pPlugin[ i ] ) ); + + if( ! pInst ) + { + std::fprintf( stderr, "no suitable windowing system found, exiting.\n" ); + _exit( 1 ); + } + + // acquire SolarMutex + pInst->AcquireYieldMutex(); + + return pInst; +} + +void DestroySalInstance( SalInstance *pInst ) +{ + // release SolarMutex + pInst->ReleaseYieldMutexAll(); + + delete pInst; + if( pCloseModule ) + osl_unloadModule( pCloseModule ); +} + +void SalAbort( const OUString& rErrorText, bool bDumpCore ) +{ + if( rErrorText.isEmpty() ) + std::fprintf( stderr, "Application Error\n" ); + else + { + CrashReporter::addKeyValue("AbortMessage", rErrorText, CrashReporter::Write); + std::fprintf( stderr, "%s\n", OUStringToOString(rErrorText, osl_getThreadTextEncoding()).getStr() ); + } + if( bDumpCore ) + abort(); + else + _exit(1); +} + +const OUString& SalGetDesktopEnvironment() +{ +#ifdef _WIN32 + static OUString aDesktopEnvironment( "Windows" ); + +#else +#ifdef MACOSX + static OUString aDesktopEnvironment( "MacOSX" ); +#else + // Order to match desktops.hxx' DesktopType + static const char * const desktop_strings[] = { + "none", "unknown", "GNOME", "UNITY", + "XFCE", "MATE", "PLASMA5", "LXQT" }; + static OUString aDesktopEnvironment; + if( aDesktopEnvironment.isEmpty()) + { + aDesktopEnvironment = OUString::createFromAscii( + desktop_strings[get_desktop_environment()]); + } +#endif +#endif + return aDesktopEnvironment; +} + +SalData::SalData() : + m_pInstance(nullptr), + m_pPIManager(nullptr) +{ +} + +SalData::~SalData() COVERITY_NOEXCEPT_FALSE +{ +#if (defined UNX && !defined MACOSX) + psp::PrinterInfoManager::release(); +#endif +} + +#ifdef _WIN32 +bool HasAtHook() +{ + BOOL bIsRunning = FALSE; + // pvParam must be BOOL + return SystemParametersInfoW(SPI_GETSCREENREADER, 0, &bIsRunning, 0) + && bIsRunning; +} +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/source/app/salusereventlist.cxx b/vcl/source/app/salusereventlist.cxx new file mode 100644 index 000000000..088bc141f --- /dev/null +++ b/vcl/source/app/salusereventlist.cxx @@ -0,0 +1,171 @@ +/* -*- 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 <salusereventlist.hxx> +#include <salwtype.hxx> + +#include <algorithm> +#include <cstdlib> +#include <exception> +#include <typeinfo> + +#include <com/sun/star/uno/Exception.hpp> +#include <tools/debug.hxx> +#include <tools/diagnose_ex.h> +#include <sal/log.hxx> +#include <sal/types.h> +#include <svdata.hxx> + +SalUserEventList::SalUserEventList() + : m_bAllUserEventProcessedSignaled( true ) + , m_aProcessingThread(0) +{ +} + +SalUserEventList::~SalUserEventList() COVERITY_NOEXCEPT_FALSE +{ +} + +void SalUserEventList::insertFrame( SalFrame* pFrame ) +{ + auto aPair = m_aFrames.insert( pFrame ); + assert( aPair.second ); (void) aPair; +} + +void SalUserEventList::eraseFrame( SalFrame* pFrame ) +{ + auto it = m_aFrames.find( pFrame ); + assert( it != m_aFrames.end() ); + if ( it != m_aFrames.end() ) + m_aFrames.erase( it ); +} + +bool SalUserEventList::DispatchUserEvents( bool bHandleAllCurrentEvents ) +{ + bool bWasEvent = false; + oslThreadIdentifier aCurId = osl::Thread::getCurrentIdentifier(); + + DBG_TESTSOLARMUTEX(); + osl::ResettableMutexGuard aResettableListGuard(m_aUserEventsMutex); + + if (!m_aUserEvents.empty()) + { + if (bHandleAllCurrentEvents) + { + if (m_aProcessingUserEvents.empty()) + m_aProcessingUserEvents.swap(m_aUserEvents); + else + m_aProcessingUserEvents.splice(m_aProcessingUserEvents.end(), m_aUserEvents); + } + else if (m_aProcessingUserEvents.empty()) + { + m_aProcessingUserEvents.push_back( m_aUserEvents.front() ); + m_aUserEvents.pop_front(); + } + } + + if (HasUserEvents()) + { + bWasEvent = true; + m_aProcessingThread = aCurId; + + SalUserEvent aEvent( nullptr, nullptr, SalEvent::NONE ); + do { + if (m_aProcessingUserEvents.empty() || aCurId != m_aProcessingThread) + break; + aEvent = m_aProcessingUserEvents.front(); + m_aProcessingUserEvents.pop_front(); + + // remember to reset the guard before break or continue the loop + aResettableListGuard.clear(); + + if ( !isFrameAlive( aEvent.m_pFrame ) ) + { + if ( aEvent.m_nEvent == SalEvent::UserEvent ) + delete static_cast< ImplSVEvent* >( aEvent.m_pData ); + aResettableListGuard.reset(); + continue; + } + +#ifndef IOS + try +#endif + { + ProcessEvent( aEvent ); + } +#ifndef IOS + catch (css::uno::Exception&) + { + TOOLS_WARN_EXCEPTION("vcl", "Uncaught"); + std::abort(); + } + catch (std::exception& e) + { + SAL_WARN("vcl", "Uncaught " << typeid(e).name() << " " << e.what()); + std::abort(); + } + catch (...) + { + SAL_WARN("vcl", "Uncaught exception during DispatchUserEvents!"); + std::abort(); + } +#endif + aResettableListGuard.reset(); + if (!bHandleAllCurrentEvents) + break; + } + while( true ); + } + + if ( !m_bAllUserEventProcessedSignaled && !HasUserEvents() ) + { + m_bAllUserEventProcessedSignaled = true; + TriggerAllUserEventsProcessed(); + } + + return bWasEvent; +} + +void SalUserEventList::RemoveEvent( SalFrame* pFrame, void* pData, SalEvent nEvent ) +{ + SalUserEvent aEvent( pFrame, pData, nEvent ); + + osl::MutexGuard aGuard( m_aUserEventsMutex ); + auto it = std::find( m_aUserEvents.begin(), m_aUserEvents.end(), aEvent ); + if ( it != m_aUserEvents.end() ) + { + m_aUserEvents.erase( it ); + } + else + { + it = std::find( m_aProcessingUserEvents.begin(), m_aProcessingUserEvents.end(), aEvent ); + if ( it != m_aProcessingUserEvents.end() ) + { + m_aProcessingUserEvents.erase( it ); + } + } + + if ( !m_bAllUserEventProcessedSignaled && !HasUserEvents() ) + { + m_bAllUserEventProcessedSignaled = true; + TriggerAllUserEventsProcessed(); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/source/app/salvtables.cxx b/vcl/source/app/salvtables.cxx new file mode 100644 index 000000000..d7c93164a --- /dev/null +++ b/vcl/source/app/salvtables.cxx @@ -0,0 +1,6777 @@ +/* -*- 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 <com/sun/star/accessibility/AccessibleRelationType.hpp> +#include <com/sun/star/awt/XWindow.hpp> +#include <com/sun/star/awt/XWindowPeer.hpp> +#include <o3tl/sorted_vector.hxx> +#include <officecfg/Office/Common.hxx> +#include <iconview.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/calendar.hxx> +#include <vcl/toolkit/combobox.hxx> +#include <vcl/toolkit/dialog.hxx> +#include <vcl/fixed.hxx> +#include <vcl/toolkit/fixedhyper.hxx> +#include <vcl/fmtfield.hxx> +#include <vcl/headbar.hxx> +#include <vcl/ivctrl.hxx> +#include <vcl/layout.hxx> +#include <vcl/menubtn.hxx> +#include <vcl/toolkit/prgsbar.hxx> +#include <vcl/ptrstyle.hxx> +#include <slider.hxx> +#include <vcl/sysdata.hxx> +#include <vcl/svlbitm.hxx> +#include <vcl/toolkit/svtabbx.hxx> +#include <vcl/tabctrl.hxx> +#include <vcl/tabpage.hxx> +#include <vcl/treelistentry.hxx> +#include <vcl/toolkit/throbber.hxx> +#include <vcl/toolkit/unowrap.hxx> +#include <vcl/weld.hxx> +#include <vcl/vclmedit.hxx> +#include <vcl/viewdataentry.hxx> +#include <vcl/virdev.hxx> +#include <bitmaps.hlst> +#include <wizdlg.hxx> +#include <salvtables.hxx> + +#include <boost/property_tree/ptree.hpp> + +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 +} + +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::ReleaseYieldMutexAll() { return m_pYieldMutex->release(true); } + +void SalInstance::AcquireYieldMutex(sal_uInt32 nCount) { m_pYieldMutex->acquire(nCount); } + +std::unique_ptr<SalSession> SalInstance::CreateSalSession() { return nullptr; } + +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); +} + +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() {} + +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; } + +void SalMenu::ApplyPersona() {} + +SalMenuItem::~SalMenuItem() {} + +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) + { + Application::AddEventListener(LINK(this, SalInstanceWidget, MouseEventListener)); + m_bMouseEventListener = true; + } +} + +void SalInstanceWidget::set_background(const Color& rColor) +{ + m_xWidget->SetControlBackground(rColor); + m_xWidget->SetBackground(m_xWidget->GetControlBackground()); + // turn off WB_CLIPCHILDREN otherwise the bg won't extend "under" + // transparent children of the widget + m_xWidget->SetStyle(m_xWidget->GetStyle() & ~WB_CLIPCHILDREN); +} + +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) +{ +} + +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() { m_xWidget->GrabFocus(); } + +bool SalInstanceWidget::has_focus() const { return m_xWidget->HasFocus(); } + +bool SalInstanceWidget::is_active() const { return m_xWidget->IsActive(); } + +void SalInstanceWidget::set_has_default(bool has_default) +{ + m_xWidget->set_property("has-default", OUString::boolean(has_default)); +} + +bool SalInstanceWidget::get_has_default() const { return m_xWidget->GetStyle() & WB_DEFBUTTON; } + +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); } + +OString SalInstanceWidget::get_buildable_name() const { return m_xWidget->get_id().toUtf8(); } + +void SalInstanceWidget::set_help_id(const OString& rId) { return m_xWidget->SetHelpId(rId); } + +OString SalInstanceWidget::get_help_id() const { return m_xWidget->GetHelpId(); } + +void SalInstanceWidget::set_grid_left_attach(int nAttach) +{ + m_xWidget->set_grid_left_attach(nAttach); +} + +int SalInstanceWidget::get_grid_left_attach() const { return m_xWidget->get_grid_left_attach(); } + +void SalInstanceWidget::set_grid_width(int nCols) { m_xWidget->set_grid_width(nCols); } + +void SalInstanceWidget::set_grid_top_attach(int nAttach) +{ + m_xWidget->set_grid_top_attach(nAttach); +} + +int SalInstanceWidget::get_grid_top_attach() const { return m_xWidget->get_grid_top_attach(); } + +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_secondary(bool bSecondary) { m_xWidget->set_secondary(bSecondary); } + +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_left(int nMargin) { m_xWidget->set_margin_left(nMargin); } + +void SalInstanceWidget::set_margin_right(int nMargin) { m_xWidget->set_margin_bottom(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_left() const { return m_xWidget->get_margin_left(); } + +int SalInstanceWidget::get_margin_right() const { return m_xWidget->get_margin_bottom(); } + +void SalInstanceWidget::set_accessible_name(const OUString& rName) +{ + m_xWidget->SetAccessibleName(rName); +} + +OUString SalInstanceWidget::get_accessible_name() const { return m_xWidget->GetAccessibleName(); } + +OUString SalInstanceWidget::get_accessible_description() const +{ + return m_xWidget->GetAccessibleDescription(); +} + +void SalInstanceWidget::set_accessible_relation_labeled_by(weld::Widget* pLabel) +{ + vcl::Window* pAtkLabel + = pLabel ? dynamic_cast<SalInstanceWidget&>(*pLabel).getWidget() : nullptr; + m_xWidget->SetAccessibleRelationLabeledBy(pAtkLabel); +} + +void SalInstanceWidget::set_accessible_relation_label_for(weld::Widget* pLabeled) +{ + vcl::Window* pAtkLabeled + = pLabeled ? dynamic_cast<SalInstanceWidget&>(*pLabeled).getWidget() : nullptr; + m_xWidget->SetAccessibleRelationLabelFor(pAtkLabeled); +} + +void SalInstanceWidget::set_tooltip_text(const OUString& rTip) +{ + m_xWidget->SetQuickHelpText(rTip); +} + +OUString SalInstanceWidget::get_tooltip_text() const { return m_xWidget->GetQuickHelpText(); } + +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); +} + +bool SalInstanceWidget::get_extents_relative_to(Widget& rRelative, int& x, int& y, int& width, + int& height) +{ + tools::Rectangle aRect(m_xWidget->GetWindowExtentsRelative( + dynamic_cast<SalInstanceWidget&>(rRelative).getWidget())); + x = aRect.Left(); + y = aRect.Top(); + width = aRect.GetWidth(); + height = aRect.GetHeight(); + return true; +} + +void SalInstanceWidget::grab_add() { m_xWidget->CaptureMouse(); } + +bool SalInstanceWidget::has_grab() const { return m_xWidget->IsMouseCaptured(); } + +void SalInstanceWidget::grab_remove() { 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() { m_xWidget->SetUpdateMode(false); } + +void SalInstanceWidget::thaw() { m_xWidget->SetUpdateMode(true); } + +SalInstanceWidget::~SalInstanceWidget() +{ + if (m_aMnemonicActivateHdl.IsSet()) + m_xWidget->SetMnemonicActivateHdl(Link<vcl::Window&, bool>()); + if (m_bMouseEventListener) + Application::RemoveEventListener(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() { return m_xWidget; } + +void SalInstanceWidget::disable_notify_events() { ++m_nBlockNotify; } + +bool SalInstanceWidget::notify_events_disabled() { return m_nBlockNotify != 0; } + +void SalInstanceWidget::enable_notify_events() { --m_nBlockNotify; } + +OUString SalInstanceWidget::strip_mnemonic(const OUString& rLabel) const +{ + return rLabel.replaceFirst("~", ""); +} + +VclPtr<VirtualDevice> SalInstanceWidget::create_virtual_device() const +{ + // create with (annoying) separate alpha layer that LibreOffice itself uses + return VclPtr<VirtualDevice>::Create(*Application::GetDefaultDevice(), DeviceFormat::DEFAULT, + DeviceFormat::DEFAULT); +} + +css::uno::Reference<css::datatransfer::dnd::XDropTarget> SalInstanceWidget::get_drop_target() +{ + return m_xWidget->GetDropTarget(); +} + +void SalInstanceWidget::connect_get_property_tree( + const Link<boost::property_tree::ptree&, void>& rLink) +{ + m_xWidget->SetDumpAsPropertyTreeHdl(rLink); +} + +void SalInstanceWidget::set_stack_background() +{ + set_background(m_xWidget->GetSettings().GetStyleSettings().GetWindowColor()); +} + +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) + m_aFocusInHdl.Call(*this); + else if (rEvent.GetId() == VclEventId::WindowLoseFocus) + m_aFocusOutHdl.Call(*this); + else if (rEvent.GetId() == VclEventId::WindowResize) + m_aSizeAllocateHdl.Call(m_xWidget->GetSizePixel()); +} + +void SalInstanceWidget::HandleMouseEventListener(VclSimpleEvent& rEvent) +{ + if (rEvent.GetId() == VclEventId::WindowMouseButtonDown) + { + auto& rWinEvent = static_cast<VclWindowEvent&>(rEvent); + if (m_xWidget->IsWindowOrChild(rWinEvent.GetWindow())) + { + const MouseEvent* pMouseEvent = static_cast<const MouseEvent*>(rWinEvent.GetData()); + m_aMousePressHdl.Call(*pMouseEvent); + } + } + else if (rEvent.GetId() == VclEventId::WindowMouseButtonUp) + { + auto& rWinEvent = static_cast<VclWindowEvent&>(rEvent); + if (m_xWidget->IsWindowOrChild(rWinEvent.GetWindow())) + { + const MouseEvent* pMouseEvent = static_cast<const MouseEvent*>(rWinEvent.GetData()); + m_aMouseReleaseHdl.Call(*pMouseEvent); + } + } + else if (rEvent.GetId() == VclEventId::WindowMouseMove) + { + auto& rWinEvent = static_cast<VclWindowEvent&>(rEvent); + if (m_xWidget->IsWindowOrChild(rWinEvent.GetWindow())) + { + const MouseEvent* pMouseEvent = static_cast<const MouseEvent*>(rWinEvent.GetData()); + m_aMouseMotionHdl.Call(*pMouseEvent); + } + } +} + +bool SalInstanceWidget::HandleKeyEventListener(VclWindowEvent& rEvent) +{ + // we get all key events here, ignore them unless we have focus + if (!has_focus()) + return false; + if (rEvent.GetId() == VclEventId::WindowKeyInput) + { + const KeyEvent* pKeyEvent = static_cast<const KeyEvent*>(rEvent.GetData()); + return m_aKeyPressHdl.Call(*pKeyEvent); + } + else if (rEvent.GetId() == VclEventId::WindowKeyUp) + { + const KeyEvent* pKeyEvent = static_cast<const KeyEvent*>(rEvent.GetData()); + return m_aKeyReleaseHdl.Call(*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, VclSimpleEvent&, rEvent, void) +{ + HandleMouseEventListener(rEvent); +} + +IMPL_LINK_NOARG(SalInstanceWidget, MnemonicActivateHdl, vcl::Window&, bool) +{ + return m_aMnemonicActivateHdl.Call(*this); +} + +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, 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, + OUStringToOString(rId, RTL_TEXTENCODING_UTF8), pos == -1 ? MENU_APPEND : pos); + if (pIconName) + { + pMenu->SetItemImage(nNewid, createImage(*pIconName)); + } + else if (pImageSurface) + { + pMenu->SetItemImage(nNewid, createImage(*pImageSurface)); + } + 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)); +} +OString SalInstanceMenu::popup_at_rect(weld::Widget* pParent, const tools::Rectangle& rRect) +{ + SalInstanceWidget* pVclWidget = dynamic_cast<SalInstanceWidget*>(pParent); + assert(pVclWidget); + m_xMenu->Execute(pVclWidget->getWidget(), rRect, + PopupMenuFlags::ExecuteDown | PopupMenuFlags::NoMouseUpClose); + return m_xMenu->GetCurItemIdent(); +} +void SalInstanceMenu::set_sensitive(const OString& rIdent, bool bSensitive) +{ + m_xMenu->EnableItem(rIdent, bSensitive); +} +void SalInstanceMenu::set_active(const OString& rIdent, bool bActive) +{ + m_xMenu->CheckItem(rIdent, bActive); +} +bool SalInstanceMenu::get_active(const OString& rIdent) const +{ + return m_xMenu->IsItemChecked(m_xMenu->GetItemId(rIdent)); +} +void SalInstanceMenu::set_label(const OString& rIdent, const OUString& rLabel) +{ + m_xMenu->SetItemText(m_xMenu->GetItemId(rIdent), rLabel); +} +OUString SalInstanceMenu::get_label(const OString& rIdent) const +{ + return m_xMenu->GetItemText(m_xMenu->GetItemId(rIdent)); +} +void SalInstanceMenu::set_visible(const OString& 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, + TriState eCheckRadioFalse) +{ + m_nLastId + = insert_to_menu(m_nLastId, m_xMenu, pos, rId, rStr, pIconName, pImageSurface, eCheckRadioFalse); +} +void SalInstanceMenu::insert_separator(int pos, const OUString& rId) +{ + auto nInsertPos = pos == -1 ? MENU_APPEND : pos; + m_xMenu->InsertSeparator(rId.toUtf8(), nInsertPos); +} +void SalInstanceMenu::remove(const OString& rId) +{ + m_xMenu->RemoveItem(m_xMenu->GetItemPos(m_xMenu->GetItemId(rId))); +} +int SalInstanceMenu::n_children() const { return m_xMenu->GetItemCount(); } +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; +} + +namespace +{ +class SalInstanceToolbar : public SalInstanceWidget, public virtual weld::Toolbar +{ +private: + VclPtr<ToolBox> m_xToolBox; + std::map<sal_uInt16, VclPtr<vcl::Window>> m_aFloats; + std::map<sal_uInt16, VclPtr<PopupMenu>> m_aMenus; + + OString m_sStartShowIdent; + + DECL_LINK(ClickHdl, ToolBox*, void); + DECL_LINK(DropdownClick, ToolBox*, void); + DECL_LINK(MenuToggleListener, VclWindowEvent&, void); + +public: + 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)); + } + + virtual void set_item_sensitive(const OString& rIdent, bool bSensitive) override + { + m_xToolBox->EnableItem(m_xToolBox->GetItemId(OUString::fromUtf8(rIdent)), bSensitive); + } + + virtual bool get_item_sensitive(const OString& rIdent) const override + { + return m_xToolBox->IsItemEnabled(m_xToolBox->GetItemId(OUString::fromUtf8(rIdent))); + } + + virtual void set_item_visible(const OString& rIdent, bool bVisible) override + { + m_xToolBox->ShowItem(m_xToolBox->GetItemId(OUString::fromUtf8(rIdent)), bVisible); + } + + virtual void set_item_help_id(const OString& rIdent, const OString& rHelpId) override + { + m_xToolBox->SetHelpId(m_xToolBox->GetItemId(OUString::fromUtf8(rIdent)), rHelpId); + } + + virtual bool get_item_visible(const OString& rIdent) const override + { + return m_xToolBox->IsItemVisible(m_xToolBox->GetItemId(OUString::fromUtf8(rIdent))); + } + + virtual void set_item_active(const OString& rIdent, bool bActive) override + { + sal_uInt16 nItemId = m_xToolBox->GetItemId(OUString::fromUtf8(rIdent)); + m_xToolBox->CheckItem(nItemId, bActive); + } + + virtual bool get_item_active(const OString& rIdent) const override + { + return m_xToolBox->IsItemChecked(m_xToolBox->GetItemId(OUString::fromUtf8(rIdent))); + } + + void set_menu_item_active(const OString& rIdent, bool bActive) override + { + sal_uInt16 nItemId = m_xToolBox->GetItemId(OUString::fromUtf8(rIdent)); + assert(m_xToolBox->GetItemBits(nItemId) & ToolBoxItemBits::DROPDOWN); + + if (bActive) + { + m_sStartShowIdent = m_xToolBox->GetItemCommand(nItemId).toUtf8(); + 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) + { + tools::Rectangle aRect = m_xToolBox->GetItemRect(nItemId); + pPopup->Execute(m_xToolBox, aRect, PopupMenuFlags::ExecuteDown); + } + else + pPopup->EndExecute(); + } + + m_sStartShowIdent.clear(); + } + + bool get_menu_item_active(const OString& rIdent) const override + { + sal_uInt16 nItemId = m_xToolBox->GetItemId(OUString::fromUtf8(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; + } + + virtual void set_item_popover(const OString& rIdent, weld::Widget* pPopover) override + { + SalInstanceWidget* pPopoverWidget = dynamic_cast<SalInstanceWidget*>(pPopover); + + vcl::Window* pFloat = pPopoverWidget ? pPopoverWidget->getWidget() : nullptr; + if (pFloat) + { + pFloat->AddEventListener(LINK(this, SalInstanceToolbar, MenuToggleListener)); + pFloat->EnableDocking(); + } + + sal_uInt16 nId = m_xToolBox->GetItemId(OUString::fromUtf8(rIdent)); + auto xOldFloat = m_aFloats[nId]; + if (xOldFloat) + { + xOldFloat->RemoveEventListener(LINK(this, SalInstanceToolbar, MenuToggleListener)); + } + m_aFloats[nId] = pFloat; + m_aMenus[nId] = nullptr; + } + + virtual void set_item_menu(const OString& rIdent, weld::Menu* pMenu) override + { + SalInstanceMenu* pInstanceMenu = dynamic_cast<SalInstanceMenu*>(pMenu); + + PopupMenu* pPopup = pInstanceMenu ? pInstanceMenu->getMenu() : nullptr; + + sal_uInt16 nId = m_xToolBox->GetItemId(OUString::fromUtf8(rIdent)); + m_aMenus[nId] = pPopup; + m_aFloats[nId] = nullptr; + } + + virtual void insert_separator(int pos, const OUString& /*rId*/) override + { + auto nInsertPos = pos == -1 ? ToolBox::APPEND : pos; + m_xToolBox->InsertSeparator(nInsertPos, 5); + } + + virtual int get_n_items() const override { return m_xToolBox->GetItemCount(); } + + virtual OString get_item_ident(int nIndex) const override + { + return m_xToolBox->GetItemCommand(m_xToolBox->GetItemId(nIndex)).toUtf8(); + } + + virtual void set_item_ident(int nIndex, const OString& rIdent) override + { + return m_xToolBox->SetItemCommand(m_xToolBox->GetItemId(nIndex), + OUString::fromUtf8(rIdent)); + } + + virtual void set_item_label(int nIndex, const OUString& rLabel) override + { + m_xToolBox->SetItemText(m_xToolBox->GetItemId(nIndex), rLabel); + } + + virtual OUString get_item_label(const OString& rIdent) const override + { + return m_xToolBox->GetItemText(m_xToolBox->GetItemId(OUString::fromUtf8(rIdent))); + } + + virtual void set_item_label(const OString& rIdent, const OUString& rLabel) override + { + m_xToolBox->SetItemText(m_xToolBox->GetItemId(OUString::fromUtf8(rIdent)), rLabel); + } + + virtual void set_item_icon_name(const OString& rIdent, const OUString& rIconName) override + { + m_xToolBox->SetItemImage(m_xToolBox->GetItemId(OUString::fromUtf8(rIdent)), + Image(StockImage::Yes, rIconName)); + } + + virtual void set_item_image(const OString& rIdent, + const css::uno::Reference<css::graphic::XGraphic>& rIcon) override + { + m_xToolBox->SetItemImage(m_xToolBox->GetItemId(OUString::fromUtf8(rIdent)), Image(rIcon)); + } + + virtual void set_item_image(const OString& rIdent, VirtualDevice* pDevice) override + { + if (pDevice) + m_xToolBox->SetItemImage(m_xToolBox->GetItemId(OUString::fromUtf8(rIdent)), + createImage(*pDevice)); + else + m_xToolBox->SetItemImage(m_xToolBox->GetItemId(OUString::fromUtf8(rIdent)), Image()); + } + + virtual void set_item_image(int nIndex, + const css::uno::Reference<css::graphic::XGraphic>& rIcon) override + { + m_xToolBox->SetItemImage(m_xToolBox->GetItemId(nIndex), Image(rIcon)); + } + + virtual void set_item_tooltip_text(int nIndex, const OUString& rTip) override + { + m_xToolBox->SetQuickHelpText(m_xToolBox->GetItemId(nIndex), rTip); + } + + virtual void set_item_tooltip_text(const OString& rIdent, const OUString& rTip) override + { + m_xToolBox->SetQuickHelpText(m_xToolBox->GetItemId(OUString::fromUtf8(rIdent)), rTip); + } + + virtual OUString get_item_tooltip_text(const OString& rIdent) const override + { + return m_xToolBox->GetQuickHelpText(m_xToolBox->GetItemId(OUString::fromUtf8(rIdent))); + } + + virtual vcl::ImageType get_icon_size() const override { return m_xToolBox->GetImageSize(); } + + virtual void set_icon_size(vcl::ImageType eType) override + { + 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(); + } + } + + virtual sal_uInt16 get_modifier_state() const override + { + return m_xToolBox->GetModifier(); + } + + int get_drop_index(const Point& rPoint) const override + { + auto nRet = m_xToolBox->GetItemPos(rPoint); + if (nRet == ToolBox::ITEM_NOTFOUND) + return 0; + return nRet; + } + + virtual ~SalInstanceToolbar() override + { + m_xToolBox->SetDropdownClickHdl(Link<ToolBox*, void>()); + m_xToolBox->SetSelectHdl(Link<ToolBox*, void>()); + } +}; + +} + +IMPL_LINK_NOARG(SalInstanceToolbar, ClickHdl, ToolBox*, void) +{ + sal_uInt16 nItemId = m_xToolBox->GetCurItemId(); + signal_clicked(m_xToolBox->GetItemCommand(nItemId).toUtf8()); +} + +IMPL_LINK_NOARG(SalInstanceToolbar, DropdownClick, ToolBox*, void) +{ + sal_uInt16 nItemId = m_xToolBox->GetCurItemId(); + set_menu_item_active(m_xToolBox->GetItemCommand(nItemId).toUtf8(), true); +} + +IMPL_LINK(SalInstanceToolbar, MenuToggleListener, VclWindowEvent&, rEvent, void) +{ + if (rEvent.GetId() == VclEventId::WindowEndPopupMode) + { + for (auto& rFloat : m_aFloats) + { + if (rEvent.GetWindow() == rFloat.second) + { + sal_uInt16 nItemId = rFloat.first; + signal_toggle_menu(m_xToolBox->GetItemCommand(nItemId).toUtf8()); + 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::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); + } +} + +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); + if (pNewVclParent) + pVclWidget->getWidget()->SetParent(pNewVclParent->getWidget()); + else + pVclWidget->getWidget()->SetParentToDefaultWindow(); +} + +void SalInstanceContainer::recursively_unset_default_buttons() +{ + implResetDefault(m_xContainer.get()); +} + +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::draw(VirtualDevice& rOutput) +{ + rOutput.SetOutputSizePixel(m_xWidget->GetSizePixel()); + m_xWidget->PaintToDevice(&rOutput, Point()); +} + +namespace +{ +class SalInstanceBox : public SalInstanceContainer, public virtual weld::Box +{ +public: + SalInstanceBox(vcl::Window* pContainer, SalInstanceBuilder* pBuilder, bool bTakeOwnership) + : SalInstanceContainer(pContainer, pBuilder, bTakeOwnership) + { + } + virtual void reorder_child(weld::Widget* pWidget, int nNewPosition) override + { + SalInstanceWidget* pVclWidget = dynamic_cast<SalInstanceWidget*>(pWidget); + assert(pVclWidget); + pVclWidget->getWidget()->reorderWithinParent(nNewPosition); + } +}; + +void CollectChildren(const vcl::Window& rCurrent, const basegfx::B2IPoint& rTopLeft, + weld::ScreenShotCollection& rControlDataCollection) +{ + if (rCurrent.IsVisible()) + { + 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) +{ + 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(); } + +void SalInstanceWindow::set_busy_cursor(bool bBusy) +{ + if (bBusy) + m_xWindow->EnterWait(); + else + m_xWindow->LeaveWait(); +} + +css::uno::Reference<css::awt::XWindow> SalInstanceWindow::GetXWindow() +{ + css::uno::Reference<css::awt::XWindow> xWindow(m_xWindow->GetComponentInterface(), + css::uno::UNO_QUERY); + return xWindow; +} + +void SalInstanceWindow::resize_to_request() +{ + if (SystemWindow* pSysWin = dynamic_cast<SystemWindow*>(m_xWindow.get())) + { + pSysWin->setOptimalLayoutSize(); + return; + } + if (DockingWindow* pDockWin = dynamic_cast<DockingWindow*>(m_xWindow.get())) + { + pDockWin->setOptimalLayoutSize(); + return; + } + assert(false && "must be system or docking window"); +} + +void SalInstanceWindow::set_modal(bool bModal) { m_xWindow->ImplGetFrame()->SetModal(bModal); } + +bool SalInstanceWindow::get_modal() const { return m_xWindow->ImplGetFrame()->GetModal(); } + +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(); } + +tools::Rectangle SalInstanceWindow::get_monitor_workarea() const +{ + return m_xWindow->GetDesktopRectPixel(); +} + +void SalInstanceWindow::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)); + } +} + +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::set_window_state(const OString& rStr) +{ + SystemWindow* pSysWin = dynamic_cast<SystemWindow*>(m_xWindow.get()); + assert(pSysWin); + pSysWin->SetWindowState(rStr); +} + +OString SalInstanceWindow::get_window_state(WindowStateMask 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(); } + +void SalInstanceWindow::connect_toplevel_focus_changed(const Link<weld::Widget&, void>& rLink) +{ + ensure_event_listener(); + weld::Window::connect_toplevel_focus_changed(rLink); +} + +void SalInstanceWindow::HandleEventListener(VclWindowEvent& rEvent) +{ + if (rEvent.GetId() == VclEventId::WindowActivate + || rEvent.GetId() == VclEventId::WindowDeactivate) + { + signal_toplevel_focus_changed(); + return; + } + SalInstanceContainer::HandleEventListener(rEvent); +} + +void SalInstanceWindow::draw(VirtualDevice& rOutput) +{ + SystemWindow* pSysWin = dynamic_cast<SystemWindow*>(m_xWindow.get()); + assert(pSysWin); + pSysWin->createScreenshot(rOutput); +} + +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; +} + +SalInstanceWindow::~SalInstanceWindow() { 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> aOwner, + const std::function<void(sal_Int32)>& rEndDialogFn) +{ + VclAbstractDialog::AsyncContext aCtx; + aCtx.mxOwnerDialogController = aOwner; + aCtx.maEndDialogFn = rEndDialogFn; + VclButtonBox* pActionArea = m_xDialog->get_action_area(); + if (pActionArea) + pActionArea->sort_native_button_order(); + 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) + pActionArea->sort_native_button_order(); + 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(); + 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.clear(); + m_xDialog->set_border_width(m_nOldBorderWidth); + if (vcl::Window* pActionArea = m_xDialog->get_action_area()) + pActionArea->Show(); + m_xDialog->setOptimalLayoutSize(); +} + +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) + pActionArea->sort_native_button_order(); + return m_xDialog->Execute(); +} + +void SalInstanceDialog::response(int nResponse) { m_xDialog->EndDialog(nResponse); } + +void SalInstanceDialog::add_button(const OUString& rText, int nResponse, const OString& 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("ok"); + break; + case RET_CLOSE: + xButton->set_id("close"); + break; + case RET_CANCEL: + xButton->set_id("cancel"); + break; + case RET_YES: + xButton->set_id("yes"); + break; + case RET_NO: + xButton->set_id("no"); + 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_default_response(int nResponse) +{ + m_xDialog->set_default_response(nResponse); +} + +weld::Container* SalInstanceDialog::weld_content_area() +{ + return new 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, "InteractiveScreenshotMode"); + 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(); + VclPtr<AbstractScreenshotAnnotationDlg> pTmp + = pFact->CreateScreenshotAnnotationDlg(*this); + ScopedVclPtr<AbstractScreenshotAnnotationDlg> pDialog(pTmp); + + if (pDialog) + { + // currently just execute the dialog, no need to do + // different things for ok/cancel. This may change later, + // for that case use 'if (pDlg->Execute() == RET_OK)' + pDialog->Execute(); + } + } + + // consume event when: + // - CommandEventId::ContextMenu + // - bScreenshotMode + return true; + } + + return false; +} + +SalInstanceMessageDialog::SalInstanceMessageDialog(::MessageDialog* pDialog, SalInstanceBuilder* pBuilder, + bool bTakeOwnership) + : SalInstanceDialog(pDialog, pBuilder, bTakeOwnership) + , m_xMessageDialog(pDialog) +{ +} + +void SalInstanceMessageDialog::set_primary_text(const OUString& rText) +{ + m_xMessageDialog->set_primary_text(rText); +} + +OUString SalInstanceMessageDialog::get_primary_text() const +{ + return m_xMessageDialog->get_primary_text(); +} + +void SalInstanceMessageDialog::set_secondary_text(const OUString& rText) +{ + m_xMessageDialog->set_secondary_text(rText); +} + +OUString SalInstanceMessageDialog::get_secondary_text() const +{ + return m_xMessageDialog->get_secondary_text(); +} + +weld::Container* SalInstanceMessageDialog::weld_message_area() +{ + return new SalInstanceContainer(m_xMessageDialog->get_message_area(), m_pBuilder, false); +} + +namespace +{ + +class SalInstanceAssistant : public SalInstanceDialog, public virtual weld::Assistant +{ +private: + VclPtr<vcl::RoadmapWizard> m_xWizard; + std::vector<std::unique_ptr<SalInstanceContainer>> m_aPages; + std::vector<VclPtr<TabPage>> m_aAddedPages; + std::vector<int> m_aIds; + std::vector<VclPtr<VclGrid>> m_aAddedGrids; + Idle m_aUpdateRoadmapIdle; + + int find_page(const OString& rIdent) const + { + for (size_t i = 0; i < m_aAddedPages.size(); ++i) + { + if (m_aAddedPages[i]->get_id().toUtf8() == rIdent) + return i; + } + return -1; + } + + int find_id(int nId) const + { + for (size_t i = 0; i < m_aIds.size(); ++i) + { + if (nId == m_aIds[i]) + return i; + } + return -1; + } + + DECL_LINK(OnRoadmapItemSelected, LinkParamNone*, void); + DECL_LINK(UpdateRoadmap_Hdl, Timer*, void); + +public: + SalInstanceAssistant(vcl::RoadmapWizard* pDialog, SalInstanceBuilder* pBuilder, + bool bTakeOwnership) + : SalInstanceDialog(pDialog, pBuilder, bTakeOwnership) + , m_xWizard(pDialog) + { + m_xWizard->SetItemSelectHdl(LINK(this, SalInstanceAssistant, OnRoadmapItemSelected)); + + m_aUpdateRoadmapIdle.SetInvokeHandler(LINK(this, SalInstanceAssistant, UpdateRoadmap_Hdl)); + m_aUpdateRoadmapIdle.SetPriority(TaskPriority::HIGHEST); + } + + virtual int get_current_page() const override { return find_id(m_xWizard->GetCurLevel()); } + + virtual int get_n_pages() const override { return m_aAddedPages.size(); } + + virtual OString get_page_ident(int nPage) const override + { + return m_aAddedPages[nPage]->get_id().toUtf8(); + } + + virtual OString get_current_page_ident() const override + { + return get_page_ident(get_current_page()); + } + + virtual void set_current_page(int nPage) override + { + disable_notify_events(); + + // take the first shown page as the size for all pages + if (m_xWizard->GetPageSizePixel().Width() == 0) + { + Size aFinalSize; + for (int i = 0, nPages = get_n_pages(); i < nPages; ++i) + { + TabPage* pPage = m_xWizard->GetPage(m_aIds[i]); + assert(pPage); + Size aPageSize(pPage->get_preferred_size()); + if (aPageSize.Width() > aFinalSize.Width()) + aFinalSize.setWidth(aPageSize.Width()); + if (aPageSize.Height() > aFinalSize.Height()) + aFinalSize.setHeight(aPageSize.Height()); + } + m_xWizard->SetPageSizePixel(aFinalSize); + } + + (void)m_xWizard->ShowPage(m_aIds[nPage]); + enable_notify_events(); + } + + virtual void set_current_page(const OString& rIdent) override + { + int nIndex = find_page(rIdent); + if (nIndex == -1) + return; + set_current_page(nIndex); + } + + virtual void set_page_index(const OString& rIdent, int nNewIndex) override + { + int nOldIndex = find_page(rIdent); + + if (nOldIndex == -1) + return; + + if (nOldIndex == nNewIndex) + return; + + disable_notify_events(); + + auto entry = std::move(m_aAddedPages[nOldIndex]); + m_aAddedPages.erase(m_aAddedPages.begin() + nOldIndex); + m_aAddedPages.insert(m_aAddedPages.begin() + nNewIndex, std::move(entry)); + + int nId = m_aIds[nOldIndex]; + m_aIds.erase(m_aIds.begin() + nOldIndex); + m_aIds.insert(m_aIds.begin() + nNewIndex, nId); + + m_aUpdateRoadmapIdle.Start(); + + enable_notify_events(); + } + + virtual weld::Container* append_page(const OString& rIdent) override + { + VclPtrInstance<TabPage> xPage(m_xWizard); + VclPtrInstance<VclGrid> xGrid(xPage); + xPage->set_id(OUString::fromUtf8(rIdent)); + xPage->Show(); + xGrid->set_hexpand(true); + xGrid->set_vexpand(true); + xGrid->Show(); + m_xWizard->AddPage(xPage); + m_aIds.push_back(m_aAddedPages.size()); + m_xWizard->SetPage(m_aIds.back(), xPage); + m_aAddedPages.push_back(xPage); + m_aAddedGrids.push_back(xGrid); + + m_aUpdateRoadmapIdle.Start(); + + m_aPages.emplace_back(new SalInstanceContainer(xGrid, m_pBuilder, false)); + return m_aPages.back().get(); + } + + virtual OUString get_page_title(const OString& rIdent) const override + { + int nIndex = find_page(rIdent); + if (nIndex == -1) + return OUString(); + return m_aAddedPages[nIndex]->GetText(); + } + + virtual void set_page_title(const OString& rIdent, const OUString& rTitle) override + { + int nIndex = find_page(rIdent); + if (nIndex == -1) + return; + if (m_aAddedPages[nIndex]->GetText() != rTitle) + { + disable_notify_events(); + m_aAddedPages[nIndex]->SetText(rTitle); + m_aUpdateRoadmapIdle.Start(); + enable_notify_events(); + } + } + + virtual void set_page_sensitive(const OString& rIdent, bool bSensitive) override + { + int nIndex = find_page(rIdent); + if (nIndex == -1) + return; + if (m_aAddedPages[nIndex]->IsEnabled() != bSensitive) + { + disable_notify_events(); + m_aAddedPages[nIndex]->Enable(bSensitive); + m_aUpdateRoadmapIdle.Start(); + enable_notify_events(); + } + } + + virtual void set_page_side_help_id(const OString& rHelpId) override + { + m_xWizard->SetRoadmapHelpId(rHelpId); + } + + weld::Button* weld_widget_for_response(int nResponse) override; + + virtual ~SalInstanceAssistant() override + { + for (auto& rGrid : m_aAddedGrids) + rGrid.disposeAndClear(); + for (auto& rPage : m_aAddedPages) + rPage.disposeAndClear(); + } +}; + +} + +IMPL_LINK_NOARG(SalInstanceAssistant, OnRoadmapItemSelected, LinkParamNone*, void) +{ + if (notify_events_disabled()) + return; + auto nCurItemId = m_xWizard->GetCurrentRoadmapItemID(); + int nPageIndex(find_id(nCurItemId)); + if (!signal_jump_page(get_page_ident(nPageIndex)) && nCurItemId != m_xWizard->GetCurLevel()) + m_xWizard->SelectRoadmapItemByID(m_xWizard->GetCurLevel()); +} + +IMPL_LINK_NOARG(SalInstanceAssistant, UpdateRoadmap_Hdl, Timer*, void) +{ + disable_notify_events(); + + m_xWizard->DeleteRoadmapItems(); + + int nPos = 0; + for (size_t i = 0; i < m_aAddedPages.size(); ++i) + { + const OUString& rLabel = m_aAddedPages[i]->GetText(); + bool bSensitive = m_aAddedPages[i]->IsEnabled(); + if (rLabel.isEmpty()) + continue; + m_xWizard->InsertRoadmapItem(nPos++, rLabel, m_aIds[i], bSensitive); + } + + m_xWizard->SelectRoadmapItemByID(m_aIds[get_current_page()]); + + m_xWizard->ShowRoadmap(nPos != 0); + + enable_notify_events(); +} + +namespace +{ +class SalInstanceFrame : public SalInstanceContainer, public virtual weld::Frame +{ +private: + VclPtr<VclFrame> m_xFrame; + +public: + SalInstanceFrame(VclFrame* pFrame, SalInstanceBuilder* pBuilder, bool bTakeOwnership) + : SalInstanceContainer(pFrame, pBuilder, bTakeOwnership) + , m_xFrame(pFrame) + { + } + + virtual void set_label(const OUString& rText) override { m_xFrame->set_label(rText); } + + virtual OUString get_label() const override { return m_xFrame->get_label(); } + + virtual std::unique_ptr<weld::Label> weld_label_widget() const override; +}; + +class SalInstancePaned : public SalInstanceContainer, public virtual weld::Paned +{ +private: + VclPtr<VclPaned> m_xPaned; + +public: + SalInstancePaned(VclPaned* pPaned, SalInstanceBuilder* pBuilder, bool bTakeOwnership) + : SalInstanceContainer(pPaned, pBuilder, bTakeOwnership) + , m_xPaned(pPaned) + { + } + + virtual void set_position(int nPos) override + { + m_xPaned->set_position(nPos); + } + + virtual int get_position() const override + { + return m_xPaned->get_position(); + } +}; + +class SalInstanceScrolledWindow : public SalInstanceContainer, public virtual weld::ScrolledWindow +{ +private: + VclPtr<VclScrolledWindow> m_xScrolledWindow; + Link<ScrollBar*, void> m_aOrigVScrollHdl; + Link<ScrollBar*, void> m_aOrigHScrollHdl; + bool m_bUserManagedScrolling; + + DECL_LINK(VscrollHdl, ScrollBar*, void); + DECL_LINK(HscrollHdl, ScrollBar*, void); + +public: + SalInstanceScrolledWindow(VclScrolledWindow* pScrolledWindow, SalInstanceBuilder* pBuilder, + bool bTakeOwnership) + : SalInstanceContainer(pScrolledWindow, pBuilder, bTakeOwnership) + , m_xScrolledWindow(pScrolledWindow) + , m_bUserManagedScrolling(false) + { + ScrollBar& rVertScrollBar = m_xScrolledWindow->getVertScrollBar(); + m_aOrigVScrollHdl = rVertScrollBar.GetScrollHdl(); + rVertScrollBar.SetScrollHdl(LINK(this, SalInstanceScrolledWindow, VscrollHdl)); + ScrollBar& rHorzScrollBar = m_xScrolledWindow->getHorzScrollBar(); + m_aOrigHScrollHdl = rHorzScrollBar.GetScrollHdl(); + rHorzScrollBar.SetScrollHdl(LINK(this, SalInstanceScrolledWindow, HscrollHdl)); + } + + virtual void hadjustment_configure(int value, int lower, int upper, int step_increment, + int page_increment, int page_size) override + { + ScrollBar& rHorzScrollBar = m_xScrolledWindow->getHorzScrollBar(); + rHorzScrollBar.SetRangeMin(lower); + rHorzScrollBar.SetRangeMax(upper); + rHorzScrollBar.SetLineSize(step_increment); + rHorzScrollBar.SetPageSize(page_increment); + rHorzScrollBar.SetThumbPos(value); + rHorzScrollBar.SetVisibleSize(page_size); + } + + virtual int hadjustment_get_value() const override + { + ScrollBar& rHorzScrollBar = m_xScrolledWindow->getHorzScrollBar(); + return rHorzScrollBar.GetThumbPos(); + } + + virtual void hadjustment_set_value(int value) override + { + ScrollBar& rHorzScrollBar = m_xScrolledWindow->getHorzScrollBar(); + rHorzScrollBar.SetThumbPos(value); + if (!m_bUserManagedScrolling) + m_aOrigHScrollHdl.Call(&rHorzScrollBar); + } + + virtual int hadjustment_get_upper() const override + { + ScrollBar& rHorzScrollBar = m_xScrolledWindow->getHorzScrollBar(); + return rHorzScrollBar.GetRangeMax(); + } + + virtual void hadjustment_set_upper(int upper) override + { + ScrollBar& rHorzScrollBar = m_xScrolledWindow->getHorzScrollBar(); + rHorzScrollBar.SetRangeMax(upper); + } + + virtual int hadjustment_get_page_size() const override + { + ScrollBar& rHorzScrollBar = m_xScrolledWindow->getHorzScrollBar(); + return rHorzScrollBar.GetVisibleSize(); + } + + virtual void hadjustment_set_page_size(int size) override + { + ScrollBar& rHorzScrollBar = m_xScrolledWindow->getHorzScrollBar(); + return rHorzScrollBar.SetVisibleSize(size); + } + + virtual void hadjustment_set_page_increment(int size) override + { + ScrollBar& rHorzScrollBar = m_xScrolledWindow->getHorzScrollBar(); + return rHorzScrollBar.SetPageSize(size); + } + + virtual void hadjustment_set_step_increment(int size) override + { + ScrollBar& rHorzScrollBar = m_xScrolledWindow->getHorzScrollBar(); + return rHorzScrollBar.SetLineSize(size); + } + + virtual void set_hpolicy(VclPolicyType eHPolicy) override + { + WinBits nWinBits = m_xScrolledWindow->GetStyle() & ~(WB_AUTOHSCROLL | WB_HSCROLL); + if (eHPolicy == VclPolicyType::ALWAYS) + nWinBits |= WB_HSCROLL; + else if (eHPolicy == VclPolicyType::AUTOMATIC) + nWinBits |= WB_AUTOHSCROLL; + m_xScrolledWindow->SetStyle(nWinBits); + m_xScrolledWindow->queue_resize(); + } + + virtual VclPolicyType get_hpolicy() const override + { + WinBits nWinBits = m_xScrolledWindow->GetStyle(); + if (nWinBits & WB_AUTOHSCROLL) + return VclPolicyType::AUTOMATIC; + else if (nWinBits & WB_HSCROLL) + return VclPolicyType::ALWAYS; + return VclPolicyType::NEVER; + } + + virtual int get_hscroll_height() const override + { + return m_xScrolledWindow->getHorzScrollBar().get_preferred_size().Height(); + } + + virtual void vadjustment_configure(int value, int lower, int upper, int step_increment, + int page_increment, int page_size) override + { + ScrollBar& rVertScrollBar = m_xScrolledWindow->getVertScrollBar(); + rVertScrollBar.SetRangeMin(lower); + rVertScrollBar.SetRangeMax(upper); + rVertScrollBar.SetLineSize(step_increment); + rVertScrollBar.SetPageSize(page_increment); + rVertScrollBar.SetThumbPos(value); + rVertScrollBar.SetVisibleSize(page_size); + } + + virtual int vadjustment_get_value() const override + { + ScrollBar& rVertScrollBar = m_xScrolledWindow->getVertScrollBar(); + return rVertScrollBar.GetThumbPos(); + } + + virtual void vadjustment_set_value(int value) override + { + ScrollBar& rVertScrollBar = m_xScrolledWindow->getVertScrollBar(); + rVertScrollBar.SetThumbPos(value); + if (!m_bUserManagedScrolling) + m_aOrigVScrollHdl.Call(&rVertScrollBar); + } + + virtual int vadjustment_get_upper() const override + { + ScrollBar& rVertScrollBar = m_xScrolledWindow->getVertScrollBar(); + return rVertScrollBar.GetRangeMax(); + } + + virtual void vadjustment_set_upper(int upper) override + { + ScrollBar& rVertScrollBar = m_xScrolledWindow->getVertScrollBar(); + rVertScrollBar.SetRangeMax(upper); + } + + virtual int vadjustment_get_lower() const override + { + ScrollBar& rVertScrollBar = m_xScrolledWindow->getVertScrollBar(); + return rVertScrollBar.GetRangeMin(); + } + + virtual void vadjustment_set_lower(int lower) override + { + ScrollBar& rVertScrollBar = m_xScrolledWindow->getVertScrollBar(); + rVertScrollBar.SetRangeMin(lower); + } + + virtual int vadjustment_get_page_size() const override + { + ScrollBar& rVertScrollBar = m_xScrolledWindow->getVertScrollBar(); + return rVertScrollBar.GetVisibleSize(); + } + + virtual void vadjustment_set_page_size(int size) override + { + ScrollBar& rVertScrollBar = m_xScrolledWindow->getVertScrollBar(); + return rVertScrollBar.SetVisibleSize(size); + } + + virtual void vadjustment_set_page_increment(int size) override + { + ScrollBar& rVertScrollBar = m_xScrolledWindow->getVertScrollBar(); + return rVertScrollBar.SetPageSize(size); + } + + virtual void vadjustment_set_step_increment(int size) override + { + ScrollBar& rVertScrollBar = m_xScrolledWindow->getVertScrollBar(); + return rVertScrollBar.SetLineSize(size); + } + + virtual void set_vpolicy(VclPolicyType eVPolicy) override + { + WinBits nWinBits = m_xScrolledWindow->GetStyle() & ~(WB_AUTOVSCROLL | WB_VSCROLL); + if (eVPolicy == VclPolicyType::ALWAYS) + nWinBits |= WB_VSCROLL; + else if (eVPolicy == VclPolicyType::AUTOMATIC) + nWinBits |= WB_AUTOVSCROLL; + m_xScrolledWindow->SetStyle(nWinBits); + m_xScrolledWindow->queue_resize(); + } + + virtual VclPolicyType get_vpolicy() const override + { + WinBits nWinBits = m_xScrolledWindow->GetStyle(); + if (nWinBits & WB_AUTOVSCROLL) + return VclPolicyType::AUTOMATIC; + else if (nWinBits & WB_VSCROLL) + return VclPolicyType::ALWAYS; + return VclPolicyType::NEVER; + } + + virtual int get_vscroll_width() const override + { + return m_xScrolledWindow->getVertScrollBar().get_preferred_size().Width(); + } + + virtual void set_user_managed_scrolling() override + { + m_bUserManagedScrolling = true; + m_xScrolledWindow->setUserManagedScrolling(true); + } + + virtual ~SalInstanceScrolledWindow() override + { + ScrollBar& rVertScrollBar = m_xScrolledWindow->getVertScrollBar(); + rVertScrollBar.SetScrollHdl(m_aOrigVScrollHdl); + } +}; + +} + +IMPL_LINK(SalInstanceScrolledWindow, VscrollHdl, ScrollBar*, pScrollBar, void) +{ + signal_vadjustment_changed(); + if (!m_bUserManagedScrolling) + m_aOrigVScrollHdl.Call(pScrollBar); +} + +IMPL_LINK_NOARG(SalInstanceScrolledWindow, HscrollHdl, ScrollBar*, void) +{ + signal_hadjustment_changed(); + if (!m_bUserManagedScrolling) + m_aOrigHScrollHdl.Call(&m_xScrolledWindow->getHorzScrollBar()); +} + +SalInstanceNotebook::SalInstanceNotebook(TabControl* pNotebook, SalInstanceBuilder* pBuilder, bool bTakeOwnership) + : SalInstanceContainer(pNotebook, pBuilder, bTakeOwnership) + , m_xNotebook(pNotebook) +{ + m_xNotebook->SetActivatePageHdl(LINK(this, SalInstanceNotebook, ActivatePageHdl)); + m_xNotebook->SetDeactivatePageHdl(LINK(this, SalInstanceNotebook, DeactivatePageHdl)); +} + +int SalInstanceNotebook::get_current_page() const +{ + return m_xNotebook->GetPagePos(m_xNotebook->GetCurPageId()); +} + +OString SalInstanceNotebook::get_page_ident(int nPage) const +{ + return m_xNotebook->GetPageName(m_xNotebook->GetPageId(nPage)); +} + +OString SalInstanceNotebook::get_current_page_ident() const +{ + return m_xNotebook->GetPageName(m_xNotebook->GetCurPageId()); +} + +weld::Container* SalInstanceNotebook::get_page(const OString& rIdent) const +{ + sal_uInt16 nPageId = m_xNotebook->GetPageId(rIdent); + sal_uInt16 nPageIndex = m_xNotebook->GetPagePos(nPageId); + if (nPageIndex == TAB_PAGE_NOTFOUND) + return nullptr; + TabPage* pPage = m_xNotebook->GetTabPage(nPageId); + vcl::Window* pChild = pPage->GetChild(0); + if (m_aPages.size() < nPageIndex + 1U) + m_aPages.resize(nPageIndex + 1U); + if (!m_aPages[nPageIndex]) + m_aPages[nPageIndex] = std::make_shared<SalInstanceContainer>(pChild, m_pBuilder, false); + return m_aPages[nPageIndex].get(); +} + +void SalInstanceNotebook::set_current_page(int nPage) +{ + m_xNotebook->SetCurPageId(m_xNotebook->GetPageId(nPage)); +} + +void SalInstanceNotebook::set_current_page(const OString& rIdent) +{ + m_xNotebook->SetCurPageId(m_xNotebook->GetPageId(rIdent)); +} + +void SalInstanceNotebook::remove_page(const OString& rIdent) +{ + sal_uInt16 nPageId = m_xNotebook->GetPageId(rIdent); + sal_uInt16 nPageIndex = m_xNotebook->GetPagePos(nPageId); + if (nPageIndex == TAB_PAGE_NOTFOUND) + return; + + m_xNotebook->RemovePage(nPageId); + if (nPageIndex < m_aPages.size()) + m_aPages.erase(m_aPages.begin() + nPageIndex); + + auto iter = m_aAddedPages.find(rIdent); + if (iter != m_aAddedPages.end()) + { + iter->second.second.disposeAndClear(); + iter->second.first.disposeAndClear(); + m_aAddedPages.erase(iter); + } +} + +void SalInstanceNotebook::insert_page(const OString& rIdent, const OUString& rLabel, int nPos) +{ + sal_uInt16 nPageCount = m_xNotebook->GetPageCount(); + sal_uInt16 nLastPageId = nPageCount ? m_xNotebook->GetPageId(nPageCount - 1) : 0; + sal_uInt16 nNewPageId = nLastPageId + 1; + m_xNotebook->InsertPage(nNewPageId, rLabel, nPos == -1 ? TAB_APPEND : nPos); + VclPtrInstance<TabPage> xPage(m_xNotebook); + VclPtrInstance<VclGrid> xGrid(xPage); + xPage->Show(); + xGrid->set_hexpand(true); + xGrid->set_vexpand(true); + xGrid->Show(); + m_xNotebook->SetTabPage(nNewPageId, xPage); + m_xNotebook->SetPageName(nNewPageId, rIdent); + m_aAddedPages.try_emplace(rIdent, xPage, xGrid); +} + +int SalInstanceNotebook::get_n_pages() const { return m_xNotebook->GetPageCount(); } + +OUString SalInstanceNotebook::get_tab_label_text(const OString& rIdent) const +{ + return m_xNotebook->GetPageText(m_xNotebook->GetPageId(rIdent)); +} + +void SalInstanceNotebook::set_tab_label_text(const OString& rIdent, const OUString& rText) +{ + return m_xNotebook->SetPageText(m_xNotebook->GetPageId(rIdent), rText); +} + +SalInstanceNotebook::~SalInstanceNotebook() +{ + for (auto& rItem : m_aAddedPages) + { + rItem.second.second.disposeAndClear(); + rItem.second.first.disposeAndClear(); + } + m_xNotebook->SetActivatePageHdl(Link<TabControl*, void>()); + m_xNotebook->SetDeactivatePageHdl(Link<TabControl*, bool>()); +} + +IMPL_LINK_NOARG(SalInstanceNotebook, DeactivatePageHdl, TabControl*, bool) +{ + return !m_aLeavePageHdl.IsSet() || m_aLeavePageHdl.Call(get_current_page_ident()); +} + +IMPL_LINK_NOARG(SalInstanceNotebook, ActivatePageHdl, TabControl*, void) +{ + m_aEnterPageHdl.Call(get_current_page_ident()); +} + +namespace +{ +class SalInstanceVerticalNotebook : public SalInstanceContainer, public virtual weld::Notebook +{ +private: + VclPtr<VerticalTabControl> m_xNotebook; + mutable std::vector<std::unique_ptr<SalInstanceContainer>> m_aPages; + + DECL_LINK(DeactivatePageHdl, VerticalTabControl*, bool); + DECL_LINK(ActivatePageHdl, VerticalTabControl*, void); + +public: + SalInstanceVerticalNotebook(VerticalTabControl* pNotebook, SalInstanceBuilder* pBuilder, + bool bTakeOwnership) + : SalInstanceContainer(pNotebook, pBuilder, bTakeOwnership) + , m_xNotebook(pNotebook) + { + m_xNotebook->SetActivatePageHdl(LINK(this, SalInstanceVerticalNotebook, ActivatePageHdl)); + m_xNotebook->SetDeactivatePageHdl( + LINK(this, SalInstanceVerticalNotebook, DeactivatePageHdl)); + } + + virtual int get_current_page() const override + { + return m_xNotebook->GetPagePos(m_xNotebook->GetCurPageId()); + } + + virtual OString get_page_ident(int nPage) const override + { + return m_xNotebook->GetPageId(nPage); + } + + virtual OString get_current_page_ident() const override { return m_xNotebook->GetCurPageId(); } + + virtual weld::Container* get_page(const OString& rIdent) const override + { + sal_uInt16 nPageIndex = m_xNotebook->GetPagePos(rIdent); + if (nPageIndex == TAB_PAGE_NOTFOUND) + return nullptr; + auto pChild = m_xNotebook->GetPage(rIdent); + if (m_aPages.size() < nPageIndex + 1U) + m_aPages.resize(nPageIndex + 1U); + if (!m_aPages[nPageIndex]) + m_aPages[nPageIndex].reset(new SalInstanceContainer(pChild, m_pBuilder, false)); + return m_aPages[nPageIndex].get(); + } + + virtual void set_current_page(int nPage) override + { + m_xNotebook->SetCurPageId(m_xNotebook->GetPageId(nPage)); + } + + virtual void set_current_page(const OString& rIdent) override + { + m_xNotebook->SetCurPageId(rIdent); + } + + virtual void remove_page(const OString& rIdent) override + { + sal_uInt16 nPageIndex = m_xNotebook->GetPagePos(rIdent); + if (nPageIndex == TAB_PAGE_NOTFOUND) + return; + m_xNotebook->RemovePage(rIdent); + if (nPageIndex < m_aPages.size()) + m_aPages.erase(m_aPages.begin() + nPageIndex); + } + + virtual void insert_page(const OString& rIdent, const OUString& rLabel, int nPos) override + { + VclPtrInstance<VclGrid> xGrid(m_xNotebook->GetPageParent()); + xGrid->set_hexpand(true); + xGrid->set_vexpand(true); + m_xNotebook->InsertPage(rIdent, rLabel, Image(), "", xGrid, nPos); + } + + virtual int get_n_pages() const override { return m_xNotebook->GetPageCount(); } + + virtual void set_tab_label_text(const OString& rIdent, const OUString& rText) override + { + return m_xNotebook->SetPageText(rIdent, rText); + } + + virtual OUString get_tab_label_text(const OString& rIdent) const override + { + return m_xNotebook->GetPageText(rIdent); + } + + virtual ~SalInstanceVerticalNotebook() override + { + m_xNotebook->SetActivatePageHdl(Link<VerticalTabControl*, void>()); + m_xNotebook->SetDeactivatePageHdl(Link<VerticalTabControl*, bool>()); + } +}; + +} + +IMPL_LINK_NOARG(SalInstanceVerticalNotebook, DeactivatePageHdl, VerticalTabControl*, bool) +{ + return !m_aLeavePageHdl.IsSet() || m_aLeavePageHdl.Call(get_current_page_ident()); +} + +IMPL_LINK_NOARG(SalInstanceVerticalNotebook, ActivatePageHdl, VerticalTabControl*, void) +{ + m_aEnterPageHdl.Call(get_current_page_ident()); +} + +SalInstanceButton::SalInstanceButton(::Button* pButton, SalInstanceBuilder* pBuilder, bool bTakeOwnership) + : SalInstanceContainer(pButton, pBuilder, bTakeOwnership) + , m_xButton(pButton) + , m_aOldClickHdl(pButton->GetClickHdl()) +{ + m_xButton->SetClickHdl(LINK(this, SalInstanceButton, ClickHdl)); +} + +void SalInstanceButton::set_label(const OUString& rText) { m_xButton->SetText(rText); } + +void SalInstanceButton::set_image(VirtualDevice* pDevice) +{ + m_xButton->SetImageAlign(ImageAlign::Left); + if (pDevice) + m_xButton->SetModeImage(createImage(*pDevice)); + else + m_xButton->SetModeImage(Image()); +} + +void SalInstanceButton::set_image(const css::uno::Reference<css::graphic::XGraphic>& rImage) +{ + m_xButton->SetImageAlign(ImageAlign::Left); + m_xButton->SetModeImage(Image(rImage)); +} + +void SalInstanceButton::set_from_icon_name(const OUString& rIconName) +{ + m_xButton->SetModeImage(Image(StockImage::Yes, rIconName)); +} + +void SalInstanceButton::set_label_line_wrap(bool wrap) +{ + WinBits nBits = m_xButton->GetStyle(); + nBits &= ~WB_WORDBREAK; + if (wrap) + nBits |= WB_WORDBREAK; + m_xButton->SetStyle(nBits); + m_xButton->queue_resize(); +} + +OUString SalInstanceButton::get_label() const { return m_xButton->GetText(); } + +SalInstanceButton::~SalInstanceButton() { m_xButton->SetClickHdl(Link<::Button*, void>()); } + +IMPL_LINK(SalInstanceButton, ClickHdl, ::Button*, pButton, void) +{ + //if there's no handler set, disengage our intercept and + //run the click again to get default behaviour for cancel/ok + //etc buttons. + if (!m_aClickHdl.IsSet()) + { + pButton->SetClickHdl(m_aOldClickHdl); + pButton->Click(); + pButton->SetClickHdl(LINK(this, SalInstanceButton, ClickHdl)); + return; + } + signal_clicked(); +} + +weld::Button* SalInstanceDialog::weld_widget_for_response(int nResponse) +{ + PushButton* pButton = dynamic_cast<PushButton*>(m_xDialog->get_widget_for_response(nResponse)); + return pButton ? new SalInstanceButton(pButton, nullptr, false) : nullptr; +} + +weld::Button* SalInstanceAssistant::weld_widget_for_response(int nResponse) +{ + PushButton* pButton = nullptr; + if (nResponse == RET_YES) + pButton = m_xWizard->m_pNextPage; + else if (nResponse == RET_NO) + pButton = m_xWizard->m_pPrevPage; + else if (nResponse == RET_OK) + pButton = m_xWizard->m_pFinish; + else if (nResponse == RET_CANCEL) + pButton = m_xWizard->m_pCancel; + else if (nResponse == RET_HELP) + pButton = m_xWizard->m_pHelp; + if (pButton) + return new SalInstanceButton(pButton, nullptr, false); + return nullptr; +} + +namespace +{ +class SalInstanceMenuButton : public SalInstanceButton, public virtual weld::MenuButton +{ +private: + VclPtr<::MenuButton> m_xMenuButton; + sal_uInt16 m_nLastId; + + DECL_LINK(MenuSelectHdl, ::MenuButton*, void); + DECL_LINK(ActivateHdl, ::MenuButton*, void); + +public: + SalInstanceMenuButton(::MenuButton* pButton, SalInstanceBuilder* pBuilder, bool bTakeOwnership) + : SalInstanceButton(pButton, pBuilder, bTakeOwnership) + , m_xMenuButton(pButton) + , m_nLastId(0) + { + m_xMenuButton->SetActivateHdl(LINK(this, SalInstanceMenuButton, ActivateHdl)); + m_xMenuButton->SetSelectHdl(LINK(this, SalInstanceMenuButton, MenuSelectHdl)); + if (PopupMenu* pMenu = m_xMenuButton->GetPopupMenu()) + { + pMenu->SetMenuFlags(MenuFlags::NoAutoMnemonics); + const auto nCount = pMenu->GetItemCount(); + m_nLastId = nCount ? pMenu->GetItemId(nCount - 1) : 0; + } + } + + virtual void set_active(bool active) override + { + if (active == get_active()) + return; + if (active) + m_xMenuButton->ExecuteMenu(); + else + m_xMenuButton->CancelMenu(); + } + + virtual bool get_active() const override { return m_xMenuButton->InPopupMode(); } + + virtual void set_inconsistent(bool /*inconsistent*/) override + { + //not available + } + + virtual bool get_inconsistent() const override { return false; } + + virtual void insert_item(int pos, const OUString& rId, const OUString& rStr, + const OUString* pIconName, VirtualDevice* pImageSurface, + TriState eCheckRadioFalse) override + { + m_nLastId = insert_to_menu(m_nLastId, m_xMenuButton->GetPopupMenu(), pos, rId, rStr, + pIconName, pImageSurface, eCheckRadioFalse); + } + + virtual void insert_separator(int pos, const OUString& rId) override + { + auto nInsertPos = pos == -1 ? MENU_APPEND : pos; + m_xMenuButton->GetPopupMenu()->InsertSeparator(rId.toUtf8(), nInsertPos); + } + + virtual void set_item_sensitive(const OString& rIdent, bool bSensitive) override + { + PopupMenu* pMenu = m_xMenuButton->GetPopupMenu(); + pMenu->EnableItem(rIdent, bSensitive); + } + + virtual void remove_item(const OString& rId) override + { + PopupMenu* pMenu = m_xMenuButton->GetPopupMenu(); + pMenu->RemoveItem(pMenu->GetItemPos(pMenu->GetItemId(rId))); + } + + virtual void clear() override + { + PopupMenu* pMenu = m_xMenuButton->GetPopupMenu(); + pMenu->Clear(); + } + + virtual void set_item_active(const OString& rIdent, bool bActive) override + { + PopupMenu* pMenu = m_xMenuButton->GetPopupMenu(); + pMenu->CheckItem(rIdent, bActive); + } + + virtual void set_item_label(const OString& rIdent, const OUString& rText) override + { + PopupMenu* pMenu = m_xMenuButton->GetPopupMenu(); + pMenu->SetItemText(pMenu->GetItemId(rIdent), rText); + } + + virtual OUString get_item_label(const OString& rIdent) const override + { + PopupMenu* pMenu = m_xMenuButton->GetPopupMenu(); + return pMenu->GetItemText(pMenu->GetItemId(rIdent)); + } + + virtual void set_item_visible(const OString& rIdent, bool bShow) override + { + PopupMenu* pMenu = m_xMenuButton->GetPopupMenu(); + pMenu->ShowItem(pMenu->GetItemId(rIdent), bShow); + } + + virtual void set_item_help_id(const OString& rIdent, const OString& rHelpId) override + { + PopupMenu* pMenu = m_xMenuButton->GetPopupMenu(); + pMenu->SetHelpId(pMenu->GetItemId(rIdent), rHelpId); + } + + virtual OString get_item_help_id(const OString& rIdent) const override + { + PopupMenu* pMenu = m_xMenuButton->GetPopupMenu(); + return pMenu->GetHelpId(pMenu->GetItemId(rIdent)); + } + + virtual void set_popover(weld::Widget* pPopover) override + { + SalInstanceWidget* pPopoverWidget = dynamic_cast<SalInstanceWidget*>(pPopover); + m_xMenuButton->SetPopover(pPopoverWidget ? pPopoverWidget->getWidget() : nullptr); + } + + virtual ~SalInstanceMenuButton() override + { + m_xMenuButton->SetSelectHdl(Link<::MenuButton*, void>()); + m_xMenuButton->SetActivateHdl(Link<::MenuButton*, void>()); + } +}; + +} + +IMPL_LINK_NOARG(SalInstanceMenuButton, MenuSelectHdl, ::MenuButton*, void) +{ + signal_selected(m_xMenuButton->GetCurItemIdent()); +} + +IMPL_LINK_NOARG(SalInstanceMenuButton, ActivateHdl, ::MenuButton*, void) +{ + if (notify_events_disabled()) + return; + signal_toggled(); +} + +namespace +{ +class SalInstanceLinkButton : public SalInstanceContainer, public virtual weld::LinkButton +{ +private: + VclPtr<FixedHyperlink> m_xButton; + Link<FixedHyperlink&, void> m_aOrigClickHdl; + + DECL_LINK(ClickHdl, FixedHyperlink&, void); + +public: + SalInstanceLinkButton(FixedHyperlink* pButton, SalInstanceBuilder* pBuilder, + bool bTakeOwnership) + : SalInstanceContainer(pButton, pBuilder, bTakeOwnership) + , m_xButton(pButton) + { + m_aOrigClickHdl = m_xButton->GetClickHdl(); + m_xButton->SetClickHdl(LINK(this, SalInstanceLinkButton, ClickHdl)); + } + + virtual void set_label(const OUString& rText) override { m_xButton->SetText(rText); } + + virtual OUString get_label() const override { return m_xButton->GetText(); } + + virtual void set_uri(const OUString& rUri) override { m_xButton->SetURL(rUri); } + + virtual OUString get_uri() const override { return m_xButton->GetURL(); } + + virtual ~SalInstanceLinkButton() override { m_xButton->SetClickHdl(m_aOrigClickHdl); } +}; + +} + +IMPL_LINK(SalInstanceLinkButton, ClickHdl, FixedHyperlink&, rButton, void) +{ + bool bConsumed = signal_activate_link(); + if (!bConsumed) + m_aOrigClickHdl.Call(rButton); +} + +namespace +{ +class SalInstanceRadioButton : public SalInstanceButton, public virtual weld::RadioButton +{ +private: + VclPtr<::RadioButton> m_xRadioButton; + + DECL_LINK(ToggleHdl, ::RadioButton&, void); + +public: + SalInstanceRadioButton(::RadioButton* pButton, SalInstanceBuilder* pBuilder, + bool bTakeOwnership) + : SalInstanceButton(pButton, pBuilder, bTakeOwnership) + , m_xRadioButton(pButton) + { + m_xRadioButton->SetToggleHdl(LINK(this, SalInstanceRadioButton, ToggleHdl)); + } + + virtual void set_active(bool active) override + { + disable_notify_events(); + m_xRadioButton->Check(active); + enable_notify_events(); + } + + virtual bool get_active() const override { return m_xRadioButton->IsChecked(); } + + virtual void set_image(VirtualDevice* pDevice) override + { + m_xRadioButton->SetImageAlign(ImageAlign::Center); + if (pDevice) + m_xRadioButton->SetModeImage(createImage(*pDevice)); + else + m_xRadioButton->SetModeImage(Image()); + } + + virtual void set_image(const css::uno::Reference<css::graphic::XGraphic>& rImage) override + { + m_xRadioButton->SetImageAlign(ImageAlign::Center); + m_xRadioButton->SetModeImage(Image(rImage)); + } + + virtual void set_from_icon_name(const OUString& rIconName) override + { + m_xRadioButton->SetModeRadioImage(Image(StockImage::Yes, rIconName)); + } + + virtual void set_inconsistent(bool /*inconsistent*/) override + { + //not available + } + + virtual bool get_inconsistent() const override { return false; } + + virtual ~SalInstanceRadioButton() override + { + m_xRadioButton->SetToggleHdl(Link<::RadioButton&, void>()); + } +}; + +} + +IMPL_LINK_NOARG(SalInstanceRadioButton, ToggleHdl, ::RadioButton&, void) +{ + if (notify_events_disabled()) + return; + signal_toggled(); +} + +namespace +{ +class SalInstanceToggleButton : public SalInstanceButton, public virtual weld::ToggleButton +{ +private: + VclPtr<PushButton> m_xToggleButton; + + DECL_LINK(ToggleListener, VclWindowEvent&, void); + +public: + SalInstanceToggleButton(PushButton* pButton, SalInstanceBuilder* pBuilder, bool bTakeOwnership) + : SalInstanceButton(pButton, pBuilder, bTakeOwnership) + , m_xToggleButton(pButton) + { + } + + virtual void connect_toggled(const Link<ToggleButton&, void>& rLink) override + { + assert(!m_aToggleHdl.IsSet()); + m_xToggleButton->AddEventListener(LINK(this, SalInstanceToggleButton, ToggleListener)); + weld::ToggleButton::connect_toggled(rLink); + } + + virtual void set_active(bool active) override + { + disable_notify_events(); + m_xToggleButton->Check(active); + enable_notify_events(); + } + + virtual bool get_active() const override { return m_xToggleButton->IsChecked(); } + + virtual void set_inconsistent(bool inconsistent) override + { + disable_notify_events(); + m_xToggleButton->SetState(inconsistent ? TRISTATE_INDET : TRISTATE_FALSE); + enable_notify_events(); + } + + virtual bool get_inconsistent() const override + { + return m_xToggleButton->GetState() == TRISTATE_INDET; + } + + virtual ~SalInstanceToggleButton() override + { + if (m_aToggleHdl.IsSet()) + m_xToggleButton->RemoveEventListener( + LINK(this, SalInstanceToggleButton, ToggleListener)); + } +}; + +} + +IMPL_LINK(SalInstanceToggleButton, ToggleListener, VclWindowEvent&, rEvent, void) +{ + if (notify_events_disabled()) + return; + if (rEvent.GetId() == VclEventId::PushbuttonToggle) + signal_toggled(); +} + +SalInstanceCheckButton::SalInstanceCheckButton(CheckBox* pButton, SalInstanceBuilder* pBuilder, bool bTakeOwnership) + : SalInstanceButton(pButton, pBuilder, bTakeOwnership) + , m_xCheckButton(pButton) +{ + m_xCheckButton->SetToggleHdl(LINK(this, SalInstanceCheckButton, ToggleHdl)); +} + +void SalInstanceCheckButton::set_active(bool active) +{ + disable_notify_events(); + m_xCheckButton->EnableTriState(false); + m_xCheckButton->Check(active); + enable_notify_events(); +} + +bool SalInstanceCheckButton::get_active() const { return m_xCheckButton->IsChecked(); } + +void SalInstanceCheckButton::set_inconsistent(bool inconsistent) +{ + disable_notify_events(); + m_xCheckButton->EnableTriState(true); + m_xCheckButton->SetState(inconsistent ? TRISTATE_INDET : TRISTATE_FALSE); + enable_notify_events(); +} + +bool SalInstanceCheckButton::get_inconsistent() const +{ + return m_xCheckButton->GetState() == TRISTATE_INDET; +} + +SalInstanceCheckButton::~SalInstanceCheckButton() +{ + m_xCheckButton->SetToggleHdl(Link<CheckBox&, void>()); +} + +IMPL_LINK_NOARG(SalInstanceCheckButton, ToggleHdl, CheckBox&, void) +{ + if (notify_events_disabled()) + return; + m_xCheckButton->EnableTriState(false); + signal_toggled(); +} + +namespace +{ +class SalInstanceScale : public SalInstanceWidget, public virtual weld::Scale +{ +private: + VclPtr<Slider> m_xScale; + + DECL_LINK(SlideHdl, Slider*, void); + +public: + SalInstanceScale(Slider* pScale, SalInstanceBuilder* pBuilder, bool bTakeOwnership) + : SalInstanceWidget(pScale, pBuilder, bTakeOwnership) + , m_xScale(pScale) + { + m_xScale->SetSlideHdl(LINK(this, SalInstanceScale, SlideHdl)); + } + + virtual void set_value(int value) override { m_xScale->SetThumbPos(value); } + + virtual void set_range(int min, int max) override + { + m_xScale->SetRangeMin(min); + m_xScale->SetRangeMax(max); + } + + virtual int get_value() const override { return m_xScale->GetThumbPos(); } + + virtual void set_increments(int step, int page) override + { + m_xScale->SetLineSize(step); + m_xScale->SetPageSize(page); + } + + virtual void get_increments(int& step, int& page) const override + { + step = m_xScale->GetLineSize(); + page = m_xScale->GetPageSize(); + } + + virtual ~SalInstanceScale() override { m_xScale->SetSlideHdl(Link<Slider*, void>()); } +}; + +} + +IMPL_LINK_NOARG(SalInstanceScale, SlideHdl, Slider*, void) { signal_value_changed(); } + +namespace +{ +class SalInstanceSpinner : public SalInstanceWidget, public virtual weld::Spinner +{ +private: + VclPtr<Throbber> m_xThrobber; + +public: + SalInstanceSpinner(Throbber* pThrobber, SalInstanceBuilder* pBuilder, bool bTakeOwnership) + : SalInstanceWidget(pThrobber, pBuilder, bTakeOwnership) + , m_xThrobber(pThrobber) + { + } + + virtual void start() override { m_xThrobber->start(); } + + virtual void stop() override { m_xThrobber->stop(); } +}; + +class SalInstanceProgressBar : public SalInstanceWidget, public virtual weld::ProgressBar +{ +private: + VclPtr<::ProgressBar> m_xProgressBar; + +public: + SalInstanceProgressBar(::ProgressBar* pProgressBar, SalInstanceBuilder* pBuilder, + bool bTakeOwnership) + : SalInstanceWidget(pProgressBar, pBuilder, bTakeOwnership) + , m_xProgressBar(pProgressBar) + { + } + + virtual void set_percentage(int value) override { m_xProgressBar->SetValue(value); } + + virtual OUString get_text() const override { return m_xProgressBar->GetText(); } + + virtual void set_text(const OUString& rText) override { m_xProgressBar->SetText(rText); } +}; + +class SalInstanceImage : public SalInstanceWidget, public virtual weld::Image +{ +private: + VclPtr<FixedImage> m_xImage; + +public: + SalInstanceImage(FixedImage* pImage, SalInstanceBuilder* pBuilder, bool bTakeOwnership) + : SalInstanceWidget(pImage, pBuilder, bTakeOwnership) + , m_xImage(pImage) + { + } + + virtual void set_from_icon_name(const OUString& rIconName) override + { + m_xImage->SetImage(::Image(StockImage::Yes, rIconName)); + } + + virtual void set_image(VirtualDevice* pDevice) override + { + if (pDevice) + m_xImage->SetImage(createImage(*pDevice)); + else + m_xImage->SetImage(::Image()); + } + + virtual void set_image(const css::uno::Reference<css::graphic::XGraphic>& rImage) override + { + m_xImage->SetImage(::Image(rImage)); + } +}; + +class SalInstanceCalendar : public SalInstanceWidget, public virtual weld::Calendar +{ +private: + VclPtr<::Calendar> m_xCalendar; + + DECL_LINK(SelectHdl, ::Calendar*, void); + DECL_LINK(ActivateHdl, ::Calendar*, void); + +public: + SalInstanceCalendar(::Calendar* pCalendar, SalInstanceBuilder* pBuilder, bool bTakeOwnership) + : SalInstanceWidget(pCalendar, pBuilder, bTakeOwnership) + , m_xCalendar(pCalendar) + { + m_xCalendar->SetSelectHdl(LINK(this, SalInstanceCalendar, SelectHdl)); + m_xCalendar->SetActivateHdl(LINK(this, SalInstanceCalendar, ActivateHdl)); + } + + virtual void set_date(const Date& rDate) override { m_xCalendar->SetCurDate(rDate); } + + virtual Date get_date() const override { return m_xCalendar->GetFirstSelectedDate(); } + + virtual ~SalInstanceCalendar() override + { + m_xCalendar->SetSelectHdl(Link<::Calendar*, void>()); + m_xCalendar->SetActivateHdl(Link<::Calendar*, void>()); + } +}; + +} + +IMPL_LINK_NOARG(SalInstanceCalendar, SelectHdl, ::Calendar*, void) +{ + if (notify_events_disabled()) + return; + signal_selected(); +} + +IMPL_LINK_NOARG(SalInstanceCalendar, ActivateHdl, ::Calendar*, void) +{ + if (notify_events_disabled()) + return; + signal_activated(); +} + +WeldTextFilter::WeldTextFilter(Link<OUString&, bool>& rInsertTextHdl) + : TextFilter(OUString()) + , m_rInsertTextHdl(rInsertTextHdl) +{ +} + +OUString WeldTextFilter::filter(const OUString& rText) +{ + if (!m_rInsertTextHdl.IsSet()) + return rText; + OUString sText(rText); + const bool bContinue = m_rInsertTextHdl.Call(sText); + if (!bContinue) + return OUString(); + return sText; +} + +SalInstanceEntry::SalInstanceEntry(Edit* pEntry, SalInstanceBuilder* pBuilder, bool bTakeOwnership) + : SalInstanceWidget(pEntry, pBuilder, bTakeOwnership) + , m_xEntry(pEntry) + , m_aTextFilter(m_aInsertTextHdl) +{ + m_xEntry->SetModifyHdl(LINK(this, SalInstanceEntry, ChangeHdl)); + m_xEntry->SetActivateHdl(LINK(this, SalInstanceEntry, ActivateHdl)); + m_xEntry->SetTextFilter(&m_aTextFilter); +} + +void SalInstanceEntry::set_text(const OUString& rText) +{ + disable_notify_events(); + m_xEntry->SetText(rText); + enable_notify_events(); +} + +OUString SalInstanceEntry::get_text() const +{ + return m_xEntry->GetText(); +} + +void SalInstanceEntry::set_width_chars(int nChars) +{ + m_xEntry->SetWidthInChars(nChars); +} + +int SalInstanceEntry::get_width_chars() const +{ + return m_xEntry->GetWidthInChars(); +} + +void SalInstanceEntry::set_max_length(int nChars) +{ + m_xEntry->SetMaxTextLen(nChars); +} + +void SalInstanceEntry::select_region(int nStartPos, int nEndPos) +{ + disable_notify_events(); + m_xEntry->SetSelection(Selection(nStartPos, nEndPos < 0 ? SELECTION_MAX : nEndPos)); + enable_notify_events(); +} + +bool SalInstanceEntry::get_selection_bounds(int& rStartPos, int& rEndPos) +{ + const Selection& rSelection = m_xEntry->GetSelection(); + rStartPos = rSelection.Min(); + rEndPos = rSelection.Max(); + return rSelection.Len(); +} + +void SalInstanceEntry::replace_selection(const OUString& rText) +{ + m_xEntry->ReplaceSelected(rText); +} + +void SalInstanceEntry::set_position(int nCursorPos) +{ + disable_notify_events(); + if (nCursorPos < 0) + m_xEntry->SetCursorAtLast(); + else + m_xEntry->SetSelection(Selection(nCursorPos, nCursorPos)); + enable_notify_events(); +} + +int SalInstanceEntry::get_position() const +{ + return m_xEntry->GetSelection().Max(); +} + +void SalInstanceEntry::set_editable(bool bEditable) +{ + m_xEntry->SetReadOnly(!bEditable); +} + +bool SalInstanceEntry::get_editable() const +{ + return !m_xEntry->IsReadOnly(); +} + +void SalInstanceEntry::set_message_type(weld::EntryMessageType eType) +{ + if (eType == weld::EntryMessageType::Error) + { + // tdf#114603: enable setting the background to a different color; + // relevant for GTK; see also #i75179# + m_xEntry->SetForceControlBackground(true); + m_xEntry->SetControlForeground(COL_WHITE); + m_xEntry->SetControlBackground(0xff6563); + } + else if (eType == weld::EntryMessageType::Warning) + { + // tdf#114603: enable setting the background to a different color; + // relevant for GTK; see also #i75179# + m_xEntry->SetForceControlBackground(true); + m_xEntry->SetControlForeground(); + m_xEntry->SetControlBackground(COL_YELLOW); + } + else + { + m_xEntry->SetForceControlBackground(false); + m_xEntry->SetControlForeground(); + m_xEntry->SetControlBackground(); + } +} + +void SalInstanceEntry::set_font(const vcl::Font& rFont) +{ + m_xEntry->SetPointFont(*m_xEntry, rFont); + m_xEntry->Invalidate(); +} + +void SalInstanceEntry::connect_cursor_position(const Link<Entry&, void>& rLink) +{ + assert(!m_aCursorPositionHdl.IsSet()); + m_xEntry->AddEventListener(LINK(this, SalInstanceEntry, CursorListener)); + weld::Entry::connect_cursor_position(rLink); +} + +void SalInstanceEntry::set_placeholder_text(const OUString& rText) +{ + m_xEntry->SetPlaceholderText(rText); +} + +Edit& SalInstanceEntry::getEntry() +{ + return *m_xEntry; +} + +void SalInstanceEntry::fire_signal_changed() +{ + signal_changed(); +} + +void SalInstanceEntry::cut_clipboard() +{ + m_xEntry->Cut(); +} + +void SalInstanceEntry::copy_clipboard() +{ + m_xEntry->Copy(); +} + +void SalInstanceEntry::paste_clipboard() +{ + m_xEntry->Paste(); +} + +SalInstanceEntry::~SalInstanceEntry() +{ + if (m_aCursorPositionHdl.IsSet()) + m_xEntry->RemoveEventListener(LINK(this, SalInstanceEntry, CursorListener)); + m_xEntry->SetTextFilter(nullptr); + m_xEntry->SetActivateHdl(Link<Edit&, bool>()); + m_xEntry->SetModifyHdl(Link<Edit&, void>()); +} + +IMPL_LINK_NOARG(SalInstanceEntry, ChangeHdl, Edit&, void) { signal_changed(); } + +IMPL_LINK(SalInstanceEntry, CursorListener, VclWindowEvent&, rEvent, void) +{ + if (notify_events_disabled()) + return; + if (rEvent.GetId() == VclEventId::EditSelectionChanged + || rEvent.GetId() == VclEventId::EditCaretChanged) + signal_cursor_position(); +} + +IMPL_LINK_NOARG(SalInstanceEntry, ActivateHdl, Edit&, bool) { return m_aActivateHdl.Call(*this); } + +namespace +{ +struct SalInstanceTreeIter : public weld::TreeIter +{ + SalInstanceTreeIter(const SalInstanceTreeIter* pOrig) + : iter(pOrig ? pOrig->iter : nullptr) + { + } + SalInstanceTreeIter(SvTreeListEntry* pIter) + : iter(pIter) + { + } + virtual bool equal(const TreeIter& rOther) const override + { + return iter == static_cast<const SalInstanceTreeIter&>(rOther).iter; + } + SvTreeListEntry* iter; +}; + +TriState get_toggle(SvTreeListEntry* pEntry, int col) +{ + ++col; //skip dummy/expander column + + if (static_cast<size_t>(col) == pEntry->ItemCount()) + return TRISTATE_FALSE; + + assert(col >= 0 && o3tl::make_unsigned(col) < pEntry->ItemCount()); + SvLBoxItem& rItem = pEntry->GetItem(col); + assert(dynamic_cast<SvLBoxButton*>(&rItem)); + SvLBoxButton& rToggle = static_cast<SvLBoxButton&>(rItem); + if (rToggle.IsStateTristate()) + return TRISTATE_INDET; + else if (rToggle.IsStateChecked()) + return TRISTATE_TRUE; + return TRISTATE_FALSE; +} + +bool get_text_emphasis(SvTreeListEntry* pEntry, int col) +{ + ++col; //skip dummy/expander column + + assert(col >= 0 && o3tl::make_unsigned(col) < pEntry->ItemCount()); + SvLBoxItem& rItem = pEntry->GetItem(col); + assert(dynamic_cast<SvLBoxString*>(&rItem)); + return static_cast<SvLBoxString&>(rItem).IsEmphasized(); +} +} + +class SalInstanceTreeView; + +static SalInstanceTreeView* g_DragSource; + +namespace +{ + // tdf#131581 if the TreeView is hidden then there are possibly additional + // optimizations available + class UpdateGuardIfHidden + { + private: + SvTabListBox& m_rTreeView; + bool m_bOrigUpdate; + bool m_bOrigEnableInvalidate; + + public: + UpdateGuardIfHidden(SvTabListBox& rTreeView) + : m_rTreeView(rTreeView) + // tdf#136962 only do SetUpdateMode(false) optimization if the widget is currently hidden + , m_bOrigUpdate(!m_rTreeView.IsVisible() && m_rTreeView.IsUpdateMode()) + // tdf#137432 only do EnableInvalidate(false) optimization if the widget is currently hidden + , m_bOrigEnableInvalidate(!m_rTreeView.IsVisible() && m_rTreeView.GetModel()->IsEnableInvalidate()) + { + if (m_bOrigUpdate) + m_rTreeView.SetUpdateMode(false); + if (m_bOrigEnableInvalidate) + m_rTreeView.GetModel()->EnableInvalidate(false); + } + + ~UpdateGuardIfHidden() + { + if (m_bOrigEnableInvalidate) + m_rTreeView.GetModel()->EnableInvalidate(true); + if (m_bOrigUpdate) + m_rTreeView.SetUpdateMode(true); + } + }; +} + +class SalInstanceTreeView : public SalInstanceContainer, public virtual weld::TreeView +{ +private: + // owner for UserData + std::vector<std::unique_ptr<OUString>> m_aUserData; + VclPtr<SvTabListBox> m_xTreeView; + SvLBoxButtonData m_aCheckButtonData; + SvLBoxButtonData m_aRadioButtonData; + // currently expanding parent that logically, but not currently physically, + // contain placeholders + o3tl::sorted_vector<SvTreeListEntry*> m_aExpandingPlaceHolderParents; + // which columns should be custom rendered + o3tl::sorted_vector<int> m_aCustomRenders; + bool m_bDisableCheckBoxAutoWidth; + int m_nSortColumn; + + DECL_LINK(SelectHdl, SvTreeListBox*, void); + DECL_LINK(DeSelectHdl, SvTreeListBox*, void); + DECL_LINK(DoubleClickHdl, SvTreeListBox*, bool); + DECL_LINK(ExpandingHdl, SvTreeListBox*, bool); + DECL_LINK(EndDragHdl, HeaderBar*, void); + DECL_LINK(HeaderBarClickedHdl, HeaderBar*, void); + DECL_LINK(ToggleHdl, SvLBoxButtonData*, void); + DECL_LINK(ModelChangedHdl, SvTreeListBox*, void); + DECL_LINK(StartDragHdl, SvTreeListBox*, bool); + DECL_STATIC_LINK(SalInstanceTreeView, FinishDragHdl, SvTreeListBox*, void); + DECL_LINK(EditingEntryHdl, SvTreeListEntry*, bool); + typedef std::pair<SvTreeListEntry*, OUString> IterString; + DECL_LINK(EditedEntryHdl, IterString, bool); + DECL_LINK(VisibleRangeChangedHdl, SvTreeListBox*, void); + DECL_LINK(CompareHdl, const SvSortData&, sal_Int32); + DECL_LINK(PopupMenuHdl, const CommandEvent&, bool); + DECL_LINK(TooltipHdl, const HelpEvent&, bool); + DECL_LINK(CustomRenderHdl, svtree_render_args, void); + DECL_LINK(CustomMeasureHdl, svtree_measure_args, Size); + + bool IsDummyEntry(SvTreeListEntry* pEntry) const + { + return m_xTreeView->GetEntryText(pEntry).trim() == "<dummy>"; + } + + SvTreeListEntry* GetPlaceHolderChild(SvTreeListEntry* pEntry) const + { + if (pEntry->HasChildren()) + { + auto pChild = m_xTreeView->FirstChild(pEntry); + assert(pChild); + if (IsDummyEntry(pChild)) + return pChild; + } + return nullptr; + } + + static void set_font_color(SvTreeListEntry* pEntry, const Color& rColor) + { + if (rColor == COL_AUTO) + pEntry->SetTextColor(std::optional<Color>()); + else + pEntry->SetTextColor(rColor); + } + + void AddStringItem(SvTreeListEntry* pEntry, const OUString& rStr, int nCol) + { + auto xCell = std::make_unique<SvLBoxString>(rStr); + if (m_aCustomRenders.count(nCol)) + xCell->SetCustomRender(); + pEntry->AddItem(std::move(xCell)); + } + + void InvalidateModelEntry(SvTreeListEntry* pEntry) + { + if (!m_xTreeView->GetModel()->IsEnableInvalidate()) + return; + m_xTreeView->ModelHasEntryInvalidated(pEntry); + } + +public: + SalInstanceTreeView(SvTabListBox* pTreeView, SalInstanceBuilder* pBuilder, bool bTakeOwnership) + : SalInstanceContainer(pTreeView, pBuilder, bTakeOwnership) + , m_xTreeView(pTreeView) + , m_aCheckButtonData(pTreeView, false) + , m_aRadioButtonData(pTreeView, true) + , m_bDisableCheckBoxAutoWidth(false) + , m_nSortColumn(-1) + { + m_xTreeView->SetNodeDefaultImages(); + m_xTreeView->SetForceMakeVisible(true); + m_xTreeView->SetSelectHdl(LINK(this, SalInstanceTreeView, SelectHdl)); + m_xTreeView->SetDeselectHdl(LINK(this, SalInstanceTreeView, DeSelectHdl)); + m_xTreeView->SetDoubleClickHdl(LINK(this, SalInstanceTreeView, DoubleClickHdl)); + m_xTreeView->SetExpandingHdl(LINK(this, SalInstanceTreeView, ExpandingHdl)); + m_xTreeView->SetPopupMenuHdl(LINK(this, SalInstanceTreeView, PopupMenuHdl)); + m_xTreeView->SetCustomRenderHdl(LINK(this, SalInstanceTreeView, CustomRenderHdl)); + m_xTreeView->SetCustomMeasureHdl(LINK(this, SalInstanceTreeView, CustomMeasureHdl)); + const long aTabPositions[] = { 0 }; + m_xTreeView->SetTabs(SAL_N_ELEMENTS(aTabPositions), aTabPositions); + LclHeaderTabListBox* pHeaderBox = dynamic_cast<LclHeaderTabListBox*>(m_xTreeView.get()); + + if (pHeaderBox) + { + if (HeaderBar* pHeaderBar = pHeaderBox->GetHeaderBar()) + { + //make the last entry fill available space + pHeaderBar->SetItemSize(pHeaderBar->GetItemId(pHeaderBar->GetItemCount() - 1), + HEADERBAR_FULLSIZE); + pHeaderBar->SetEndDragHdl(LINK(this, SalInstanceTreeView, EndDragHdl)); + pHeaderBar->SetSelectHdl(LINK(this, SalInstanceTreeView, HeaderBarClickedHdl)); + } + pHeaderBox->SetEditingEntryHdl(LINK(this, SalInstanceTreeView, EditingEntryHdl)); + pHeaderBox->SetEditedEntryHdl(LINK(this, SalInstanceTreeView, EditedEntryHdl)); + } + else + { + static_cast<LclTabListBox&>(*m_xTreeView) + .SetModelChangedHdl(LINK(this, SalInstanceTreeView, ModelChangedHdl)); + static_cast<LclTabListBox&>(*m_xTreeView) + .SetStartDragHdl(LINK(this, SalInstanceTreeView, StartDragHdl)); + static_cast<LclTabListBox&>(*m_xTreeView) + .SetEndDragHdl(LINK(this, SalInstanceTreeView, FinishDragHdl)); + static_cast<LclTabListBox&>(*m_xTreeView) + .SetEditingEntryHdl(LINK(this, SalInstanceTreeView, EditingEntryHdl)); + static_cast<LclTabListBox&>(*m_xTreeView) + .SetEditedEntryHdl(LINK(this, SalInstanceTreeView, EditedEntryHdl)); + } + m_aCheckButtonData.SetLink(LINK(this, SalInstanceTreeView, ToggleHdl)); + m_aRadioButtonData.SetLink(LINK(this, SalInstanceTreeView, ToggleHdl)); + } + + virtual void connect_query_tooltip(const Link<const weld::TreeIter&, OUString>& rLink) override + { + weld::TreeView::connect_query_tooltip(rLink); + m_xTreeView->SetTooltipHdl(LINK(this, SalInstanceTreeView, TooltipHdl)); + } + + virtual void columns_autosize() override + { + std::vector<long> aWidths; + m_xTreeView->getPreferredDimensions(aWidths); + if (aWidths.size() > 2) + { + std::vector<int> aColWidths; + for (size_t i = 1; i < aWidths.size() - 1; ++i) + aColWidths.push_back(aWidths[i] - aWidths[i - 1]); + set_column_fixed_widths(aColWidths); + } + } + + virtual void freeze() override + { + SalInstanceWidget::freeze(); + m_xTreeView->SetUpdateMode(false); + } + + virtual void thaw() override + { + m_xTreeView->SetUpdateMode(true); + SalInstanceWidget::thaw(); + } + + virtual void set_column_fixed_widths(const std::vector<int>& rWidths) override + { + m_bDisableCheckBoxAutoWidth = true; + std::vector<long> aTabPositions; + aTabPositions.push_back(0); + for (size_t i = 0; i < rWidths.size(); ++i) + aTabPositions.push_back(aTabPositions[i] + rWidths[i]); + m_xTreeView->SetTabs(aTabPositions.size(), aTabPositions.data(), MapUnit::MapPixel); + LclHeaderTabListBox* pHeaderBox = dynamic_cast<LclHeaderTabListBox*>(m_xTreeView.get()); + if (HeaderBar* pHeaderBar = pHeaderBox ? pHeaderBox->GetHeaderBar() : nullptr) + { + for (size_t i = 0; i < rWidths.size(); ++i) + pHeaderBar->SetItemSize(pHeaderBar->GetItemId(i), rWidths[i]); + } + // call Resize to recalculate based on the new tabs + m_xTreeView->Resize(); + } + + virtual void set_column_editables(const std::vector<bool>& rEditables) override + { + size_t nTabCount = rEditables.size(); + for (size_t i = 0 ; i < nTabCount; ++i) + m_xTreeView->SetTabEditable(i, rEditables[i]); + } + + virtual void set_centered_column(int nCol) override + { + m_xTreeView->SetTabJustify(nCol, SvTabJustify::AdjustCenter); + } + + virtual int get_column_width(int nColumn) const override + { + LclHeaderTabListBox* pHeaderBox = dynamic_cast<LclHeaderTabListBox*>(m_xTreeView.get()); + if (HeaderBar* pHeaderBar = pHeaderBox ? pHeaderBox->GetHeaderBar() : nullptr) + return pHeaderBar->GetItemSize(pHeaderBar->GetItemId(nColumn)); + // GetTab(0) gives the position of the bitmap which is automatically inserted by the TabListBox. + // So the first text column's width is Tab(2)-Tab(1). + auto nWidthPixel + = m_xTreeView->GetLogicTab(nColumn + 2) - m_xTreeView->GetLogicTab(nColumn + 1); + nWidthPixel -= SV_TAB_BORDER; + return nWidthPixel; + } + + virtual OUString get_column_title(int nColumn) const override + { + LclHeaderTabListBox* pHeaderBox = dynamic_cast<LclHeaderTabListBox*>(m_xTreeView.get()); + if (HeaderBar* pHeaderBar = pHeaderBox ? pHeaderBox->GetHeaderBar() : nullptr) + { + return pHeaderBar->GetItemText(pHeaderBar->GetItemId(nColumn)); + } + return OUString(); + } + + virtual void set_column_title(int nColumn, const OUString& rTitle) override + { + LclHeaderTabListBox* pHeaderBox = dynamic_cast<LclHeaderTabListBox*>(m_xTreeView.get()); + if (HeaderBar* pHeaderBar = pHeaderBox ? pHeaderBox->GetHeaderBar() : nullptr) + { + return pHeaderBar->SetItemText(pHeaderBar->GetItemId(nColumn), rTitle); + } + } + + virtual void set_column_custom_renderer(int nColumn, bool bEnable) override + { + assert(n_children() == 0 && "tree must be empty"); + if (bEnable) + m_aCustomRenders.insert(nColumn); + else + m_aCustomRenders.erase(nColumn); + } + + virtual void show() override + { + if (LclHeaderTabListBox* pHeaderBox = dynamic_cast<LclHeaderTabListBox*>(m_xTreeView.get())) + pHeaderBox->GetParent()->Show(); + SalInstanceContainer::show(); + } + + virtual void hide() override + { + if (LclHeaderTabListBox* pHeaderBox = dynamic_cast<LclHeaderTabListBox*>(m_xTreeView.get())) + pHeaderBox->GetParent()->Hide(); + SalInstanceContainer::hide(); + } + + virtual void insert(const weld::TreeIter* pParent, int pos, const OUString* pStr, + const OUString* pId, const OUString* pIconName, + VirtualDevice* pImageSurface, const OUString* pExpanderName, + bool bChildrenOnDemand, weld::TreeIter* pRet) override + { + disable_notify_events(); + const SalInstanceTreeIter* pVclIter = static_cast<const SalInstanceTreeIter*>(pParent); + SvTreeListEntry* iter = pVclIter ? pVclIter->iter : nullptr; + auto nInsertPos = pos == -1 ? TREELIST_APPEND : pos; + void* pUserData; + if (pId) + { + m_aUserData.emplace_back(std::make_unique<OUString>(*pId)); + pUserData = m_aUserData.back().get(); + } + else + pUserData = nullptr; + + SvTreeListEntry* pEntry = new SvTreeListEntry; + if (pIconName || pImageSurface) + { + Image aImage(pIconName ? createImage(*pIconName) : createImage(*pImageSurface)); + pEntry->AddItem(std::make_unique<SvLBoxContextBmp>(aImage, aImage, false)); + } + else + { + Image aDummy; + pEntry->AddItem(std::make_unique<SvLBoxContextBmp>(aDummy, aDummy, false)); + } + if (pStr) + AddStringItem(pEntry, *pStr, 0); + pEntry->SetUserData(pUserData); + m_xTreeView->Insert(pEntry, iter, nInsertPos); + + if (pExpanderName) + { + Image aImage(createImage(*pExpanderName)); + m_xTreeView->SetExpandedEntryBmp(pEntry, aImage); + m_xTreeView->SetCollapsedEntryBmp(pEntry, aImage); + } + + if (pRet) + { + SalInstanceTreeIter* pVclRetIter = static_cast<SalInstanceTreeIter*>(pRet); + pVclRetIter->iter = pEntry; + } + + if (bChildrenOnDemand) + { + SvTreeListEntry* pPlaceHolder = m_xTreeView->InsertEntry("<dummy>", pEntry, false, 0, nullptr); + SvViewDataEntry* pViewData = m_xTreeView->GetViewDataEntry(pPlaceHolder); + pViewData->SetSelectable(false); + } + enable_notify_events(); + } + + virtual void + bulk_insert_for_each(int nSourceCount, + const std::function<void(weld::TreeIter&, int nSourceIndex)>& func, + const std::vector<int>* pFixedWidths) override + { + freeze(); + clear(); + SalInstanceTreeIter aVclIter(static_cast<SvTreeListEntry*>(nullptr)); + + m_xTreeView->nTreeFlags |= SvTreeFlags::MANINS; + + if (pFixedWidths) + set_column_fixed_widths(*pFixedWidths); + + Image aDummy; + for (int i = 0; i < nSourceCount; ++i) + { + aVclIter.iter = new SvTreeListEntry; + aVclIter.iter->AddItem(std::make_unique<SvLBoxContextBmp>(aDummy, aDummy, false)); + m_xTreeView->Insert(aVclIter.iter, nullptr, TREELIST_APPEND); + func(aVclIter, i); + + if (!pFixedWidths) + continue; + + size_t nFixedWidths = std::min(pFixedWidths->size(), aVclIter.iter->ItemCount()); + for (size_t j = 0; j < nFixedWidths; ++j) + { + SvLBoxItem& rItem = aVclIter.iter->GetItem(j); + SvViewDataItem* pViewDataItem = m_xTreeView->GetViewDataItem(aVclIter.iter, &rItem); + pViewDataItem->mnWidth = (*pFixedWidths)[j]; + } + } + + m_xTreeView->nTreeFlags &= ~SvTreeFlags::MANINS; + + thaw(); + } + + virtual void set_font_color(int pos, const Color& rColor) override + { + SvTreeListEntry* pEntry = m_xTreeView->GetEntry(nullptr, pos); + set_font_color(pEntry, rColor); + } + + virtual void set_font_color(const weld::TreeIter& rIter, const Color& rColor) override + { + const SalInstanceTreeIter& rVclIter = static_cast<const SalInstanceTreeIter&>(rIter); + set_font_color(rVclIter.iter, rColor); + } + + virtual void remove(int pos) override + { + disable_notify_events(); + SvTreeListEntry* pEntry = m_xTreeView->GetEntry(nullptr, pos); + m_xTreeView->RemoveEntry(pEntry); + enable_notify_events(); + } + + virtual int find_text(const OUString& rText) const override + { + for (SvTreeListEntry* pEntry = m_xTreeView->First(); pEntry; + pEntry = m_xTreeView->Next(pEntry)) + { + if (SvTabListBox::GetEntryText(pEntry, 0) == rText) + return SvTreeList::GetRelPos(pEntry); + } + return -1; + } + + virtual int find_id(const OUString& rId) const override + { + for (SvTreeListEntry* pEntry = m_xTreeView->First(); pEntry; + pEntry = m_xTreeView->Next(pEntry)) + { + const OUString* pId = static_cast<const OUString*>(pEntry->GetUserData()); + if (!pId) + continue; + if (rId == *pId) + return SvTreeList::GetRelPos(pEntry); + } + return -1; + } + + virtual void swap(int pos1, int pos2) override + { + int min = std::min(pos1, pos2); + int max = std::max(pos1, pos2); + SvTreeList* pModel = m_xTreeView->GetModel(); + SvTreeListEntry* pEntry1 = pModel->GetEntry(nullptr, min); + SvTreeListEntry* pEntry2 = pModel->GetEntry(nullptr, max); + pModel->Move(pEntry1, pEntry2); + } + + virtual void clear() override + { + disable_notify_events(); + m_xTreeView->Clear(); + m_aUserData.clear(); + enable_notify_events(); + } + + virtual int n_children() const override + { + return m_xTreeView->GetModel()->GetChildList(nullptr).size(); + } + + virtual int iter_n_children(const weld::TreeIter& rIter) const override + { + const SalInstanceTreeIter& rVclIter = static_cast<const SalInstanceTreeIter&>(rIter); + return m_xTreeView->GetModel()->GetChildList(rVclIter.iter).size(); + } + + virtual void select(int pos) override + { + assert(m_xTreeView->IsUpdateMode() && "don't select when frozen, select after thaw. Note selection doesn't survive a freeze"); + disable_notify_events(); + if (pos == -1 || (pos == 0 && n_children() == 0)) + m_xTreeView->SelectAll(false); + else + { + SvTreeListEntry* pEntry = m_xTreeView->GetEntry(nullptr, pos); + m_xTreeView->Select(pEntry, true); + m_xTreeView->MakeVisible(pEntry); + } + enable_notify_events(); + } + + virtual int get_cursor_index() const override + { + SvTreeListEntry* pEntry = m_xTreeView->GetCurEntry(); + if (!pEntry) + return -1; + return SvTreeList::GetRelPos(pEntry); + } + + virtual void set_cursor(int pos) override + { + disable_notify_events(); + if (pos == -1) + m_xTreeView->SetCurEntry(nullptr); + else + { + SvTreeListEntry* pEntry = m_xTreeView->GetEntry(nullptr, pos); + m_xTreeView->SetCurEntry(pEntry); + } + enable_notify_events(); + } + + virtual void scroll_to_row(int pos) override + { + assert(m_xTreeView->IsUpdateMode() && "don't select when frozen, select after thaw. Note selection doesn't survive a freeze"); + disable_notify_events(); + SvTreeListEntry* pEntry = m_xTreeView->GetEntry(nullptr, pos); + m_xTreeView->MakeVisible(pEntry); + enable_notify_events(); + } + + virtual bool is_selected(int pos) const override + { + SvTreeListEntry* pEntry = m_xTreeView->GetEntry(nullptr, pos); + return m_xTreeView->IsSelected(pEntry); + } + + virtual void unselect(int pos) override + { + assert(m_xTreeView->IsUpdateMode() && "don't select when frozen, select after thaw. Note selection doesn't survive a freeze"); + disable_notify_events(); + if (pos == -1) + m_xTreeView->SelectAll(true); + else + { + SvTreeListEntry* pEntry = m_xTreeView->GetEntry(nullptr, pos); + m_xTreeView->Select(pEntry, false); + } + enable_notify_events(); + } + + virtual std::vector<int> get_selected_rows() const override + { + std::vector<int> aRows; + + aRows.reserve(m_xTreeView->GetSelectionCount()); + for (SvTreeListEntry* pEntry = m_xTreeView->FirstSelected(); pEntry; + pEntry = m_xTreeView->NextSelected(pEntry)) + aRows.push_back(SvTreeList::GetRelPos(pEntry)); + + return aRows; + } + + static OUString get_text(SvTreeListEntry* pEntry, int col) + { + if (col == -1) + return SvTabListBox::GetEntryText(pEntry, 0); + + ++col; //skip dummy/expander column + + if (static_cast<size_t>(col) == pEntry->ItemCount()) + return OUString(); + + assert(col >= 0 && o3tl::make_unsigned(col) < pEntry->ItemCount()); + SvLBoxItem& rItem = pEntry->GetItem(col); + assert(dynamic_cast<SvLBoxString*>(&rItem)); + return static_cast<SvLBoxString&>(rItem).GetText(); + } + + virtual OUString get_text(int pos, int col) const override + { + SvTreeListEntry* pEntry = m_xTreeView->GetEntry(nullptr, pos); + return get_text(pEntry, col); + } + + void set_text(SvTreeListEntry* pEntry, const OUString& rText, int col) + { + if (col == -1) + { + m_xTreeView->SetEntryText(pEntry, rText); + return; + } + + ++col; //skip dummy/expander column + + // blank out missing entries + for (int i = pEntry->ItemCount(); i < col; ++i) + AddStringItem(pEntry, "", i - 1); + + if (static_cast<size_t>(col) == pEntry->ItemCount()) + { + AddStringItem(pEntry, rText, col - 1); + SvViewDataEntry* pViewData = m_xTreeView->GetViewDataEntry(pEntry); + m_xTreeView->InitViewData(pViewData, pEntry); + } + else + { + assert(col >= 0 && o3tl::make_unsigned(col) < pEntry->ItemCount()); + SvLBoxItem& rItem = pEntry->GetItem(col); + assert(dynamic_cast<SvLBoxString*>(&rItem)); + static_cast<SvLBoxString&>(rItem).SetText(rText); + } + InvalidateModelEntry(pEntry); + } + + virtual void set_text(int pos, const OUString& rText, int col) override + { + SvTreeListEntry* pEntry = m_xTreeView->GetEntry(nullptr, pos); + set_text(pEntry, rText, col); + } + + void set_sensitive(SvTreeListEntry* pEntry, bool bSensitive, int col) + { + if (col == -1) + { + auto nFlags = pEntry->GetFlags() & ~SvTLEntryFlags::SEMITRANSPARENT; + if (!bSensitive) + nFlags = nFlags | SvTLEntryFlags::SEMITRANSPARENT; + pEntry->SetFlags(nFlags); + const sal_uInt16 nCount = pEntry->ItemCount(); + for (sal_uInt16 nCur = 0; nCur < nCount; ++nCur) + { + SvLBoxItem& rItem = pEntry->GetItem(nCur); + if (rItem.GetType() == SvLBoxItemType::String) + { + rItem.Enable(bSensitive); + InvalidateModelEntry(pEntry); + break; + } + } + return; + } + + ++col; //skip dummy/expander column + + assert(col >= 0 && o3tl::make_unsigned(col) < pEntry->ItemCount()); + SvLBoxItem& rItem = pEntry->GetItem(col); + rItem.Enable(bSensitive); + + InvalidateModelEntry(pEntry); + } + + using SalInstanceWidget::set_sensitive; + + virtual void set_sensitive(int pos, bool bSensitive, int col) override + { + SvTreeListEntry* pEntry = m_xTreeView->GetEntry(nullptr, pos); + set_sensitive(pEntry, bSensitive, col); + } + + virtual void set_sensitive(const weld::TreeIter& rIter, bool bSensitive, int col) override + { + const SalInstanceTreeIter& rVclIter = static_cast<const SalInstanceTreeIter&>(rIter); + set_sensitive(rVclIter.iter, bSensitive, col); + } + + virtual TriState get_toggle(int pos, int col) const override + { + SvTreeListEntry* pEntry = m_xTreeView->GetEntry(nullptr, pos); + return ::get_toggle(pEntry, col); + } + + virtual TriState get_toggle(const weld::TreeIter& rIter, int col) const override + { + const SalInstanceTreeIter& rVclIter = static_cast<const SalInstanceTreeIter&>(rIter); + return ::get_toggle(rVclIter.iter, col); + } + + void set_toggle(SvTreeListEntry* pEntry, TriState eState, int col) + { + bool bRadio = std::find(m_aRadioIndexes.begin(), m_aRadioIndexes.end(), col) + != m_aRadioIndexes.end(); + ++col; //skip dummy/expander column + + // blank out missing entries + for (int i = pEntry->ItemCount(); i < col; ++i) + AddStringItem(pEntry, "", i - 1); + + if (static_cast<size_t>(col) == pEntry->ItemCount()) + { + SvLBoxButtonData* pData = bRadio ? &m_aRadioButtonData : &m_aCheckButtonData; + + // if we want to have the implicit auto-sizing of the checkbox + // column we need to call EnableCheckButton and CheckBoxInserted to + // let it figure out that width. But we don't want to override any + // explicitly set column width, so disable this if we've set + // explicit column widths + if (!m_bDisableCheckBoxAutoWidth) + { + if (!(m_xTreeView->GetTreeFlags() & SvTreeFlags::CHKBTN)) + { + m_xTreeView->EnableCheckButton(pData); + // EnableCheckButton clobbered this, restore it + pData->SetLink(LINK(this, SalInstanceTreeView, ToggleHdl)); + } + } + + pEntry->AddItem(std::make_unique<SvLBoxButton>(pData)); + SvViewDataEntry* pViewData = m_xTreeView->GetViewDataEntry(pEntry); + m_xTreeView->InitViewData(pViewData, pEntry); + + if (!m_bDisableCheckBoxAutoWidth) + m_xTreeView->CheckBoxInserted(pEntry); + } + + assert(col >= 0 && o3tl::make_unsigned(col) < pEntry->ItemCount()); + SvLBoxItem& rItem = pEntry->GetItem(col); + assert(dynamic_cast<SvLBoxButton*>(&rItem)); + switch (eState) + { + case TRISTATE_TRUE: + static_cast<SvLBoxButton&>(rItem).SetStateChecked(); + break; + case TRISTATE_FALSE: + static_cast<SvLBoxButton&>(rItem).SetStateUnchecked(); + break; + case TRISTATE_INDET: + static_cast<SvLBoxButton&>(rItem).SetStateTristate(); + break; + } + + InvalidateModelEntry(pEntry); + } + + virtual void set_toggle(int pos, TriState eState, int col) override + { + SvTreeListEntry* pEntry = m_xTreeView->GetEntry(nullptr, pos); + set_toggle(pEntry, eState, col); + } + + virtual void set_toggle(const weld::TreeIter& rIter, TriState eState, int col) override + { + const SalInstanceTreeIter& rVclIter = static_cast<const SalInstanceTreeIter&>(rIter); + set_toggle(rVclIter.iter, eState, col); + } + + virtual void set_extra_row_indent(const weld::TreeIter& rIter, int nIndentLevel) override + { + weld::TreeIter& rNonConstIter = const_cast<weld::TreeIter&>(rIter); + SalInstanceTreeIter& rVclIter = static_cast<SalInstanceTreeIter&>(rNonConstIter); + rVclIter.iter->SetExtraIndent(nIndentLevel); + } + + void set_text_emphasis(SvTreeListEntry* pEntry, bool bOn, int col) + { + ++col; //skip dummy/expander column + + assert(col >= 0 && o3tl::make_unsigned(col) < pEntry->ItemCount()); + SvLBoxItem& rItem = pEntry->GetItem(col); + assert(dynamic_cast<SvLBoxString*>(&rItem)); + static_cast<SvLBoxString&>(rItem).Emphasize(bOn); + + InvalidateModelEntry(pEntry); + } + + virtual void set_text_emphasis(const weld::TreeIter& rIter, bool bOn, int col) override + { + const SalInstanceTreeIter& rVclIter = static_cast<const SalInstanceTreeIter&>(rIter); + set_text_emphasis(rVclIter.iter, bOn, col); + } + + virtual void set_text_emphasis(int pos, bool bOn, int col) override + { + SvTreeListEntry* pEntry = m_xTreeView->GetEntry(nullptr, pos); + set_text_emphasis(pEntry, bOn, col); + } + + virtual bool get_text_emphasis(const weld::TreeIter& rIter, int col) const override + { + const SalInstanceTreeIter& rVclIter = static_cast<const SalInstanceTreeIter&>(rIter); + return ::get_text_emphasis(rVclIter.iter, col); + } + + virtual bool get_text_emphasis(int pos, int col) const override + { + SvTreeListEntry* pEntry = m_xTreeView->GetEntry(nullptr, pos); + return ::get_text_emphasis(pEntry, col); + } + + void set_text_align(SvTreeListEntry* pEntry, double fAlign, int col) + { + ++col; //skip dummy/expander column + + assert(col >= 0 && o3tl::make_unsigned(col) < pEntry->ItemCount()); + SvLBoxItem& rItem = pEntry->GetItem(col); + assert(dynamic_cast<SvLBoxString*>(&rItem)); + static_cast<SvLBoxString&>(rItem).Align(fAlign); + + InvalidateModelEntry(pEntry); + } + + virtual void set_text_align(const weld::TreeIter& rIter, double fAlign, int col) override + { + const SalInstanceTreeIter& rVclIter = static_cast<const SalInstanceTreeIter&>(rIter); + set_text_align(rVclIter.iter, fAlign, col); + } + + virtual void set_text_align(int pos, double fAlign, int col) override + { + SvTreeListEntry* pEntry = m_xTreeView->GetEntry(nullptr, pos); + set_text_align(pEntry, fAlign, col); + } + + virtual void connect_editing( + const Link<const weld::TreeIter&, bool>& rStartLink, + const Link<const std::pair<const weld::TreeIter&, OUString>&, bool>& rEndLink) override + { + m_xTreeView->EnableInplaceEditing(rStartLink.IsSet() || rEndLink.IsSet()); + weld::TreeView::connect_editing(rStartLink, rEndLink); + } + + virtual void start_editing(const weld::TreeIter& rIter) override + { + const SalInstanceTreeIter& rVclIter = static_cast<const SalInstanceTreeIter&>(rIter); + m_xTreeView->EditEntry(rVclIter.iter); + } + + virtual void end_editing() override { m_xTreeView->EndEditing(); } + + void set_image(SvTreeListEntry* pEntry, const Image& rImage, int col) + { + if (col == -1) + { + m_xTreeView->SetExpandedEntryBmp(pEntry, rImage); + m_xTreeView->SetCollapsedEntryBmp(pEntry, rImage); + return; + } + + ++col; //skip dummy/expander column + + // blank out missing entries + for (int i = pEntry->ItemCount(); i < col; ++i) + AddStringItem(pEntry, "", i - 1); + + if (static_cast<size_t>(col) == pEntry->ItemCount()) + { + pEntry->AddItem(std::make_unique<SvLBoxContextBmp>(rImage, rImage, false)); + SvViewDataEntry* pViewData = m_xTreeView->GetViewDataEntry(pEntry); + m_xTreeView->InitViewData(pViewData, pEntry); + } + else + { + assert(col >= 0 && o3tl::make_unsigned(col) < pEntry->ItemCount()); + SvLBoxItem& rItem = pEntry->GetItem(col); + assert(dynamic_cast<SvLBoxContextBmp*>(&rItem)); + static_cast<SvLBoxContextBmp&>(rItem).SetBitmap1(rImage); + static_cast<SvLBoxContextBmp&>(rItem).SetBitmap2(rImage); + } + + m_xTreeView->SetEntryHeight(pEntry); + InvalidateModelEntry(pEntry); + } + + virtual void set_image(int pos, const OUString& rImage, int col) override + { + set_image(m_xTreeView->GetEntry(nullptr, pos), createImage(rImage), col); + } + + virtual void set_image(int pos, const css::uno::Reference<css::graphic::XGraphic>& rImage, + int col) override + { + set_image(m_xTreeView->GetEntry(nullptr, pos), Image(rImage), col); + } + + virtual void set_image(int pos, VirtualDevice& rImage, int col) override + { + set_image(m_xTreeView->GetEntry(nullptr, pos), createImage(rImage), col); + } + + virtual void set_image(const weld::TreeIter& rIter, const OUString& rImage, int col) override + { + const SalInstanceTreeIter& rVclIter = static_cast<const SalInstanceTreeIter&>(rIter); + set_image(rVclIter.iter, createImage(rImage), col); + } + + virtual void set_image(const weld::TreeIter& rIter, + const css::uno::Reference<css::graphic::XGraphic>& rImage, + int col) override + { + const SalInstanceTreeIter& rVclIter = static_cast<const SalInstanceTreeIter&>(rIter); + set_image(rVclIter.iter, Image(rImage), col); + } + + virtual void set_image(const weld::TreeIter& rIter, VirtualDevice& rImage, int col) override + { + const SalInstanceTreeIter& rVclIter = static_cast<const SalInstanceTreeIter&>(rIter); + set_image(rVclIter.iter, createImage(rImage), col); + } + + const OUString* getEntryData(int index) const + { + SvTreeListEntry* pEntry = m_xTreeView->GetEntry(nullptr, index); + return pEntry ? static_cast<const OUString*>(pEntry->GetUserData()) : nullptr; + } + + virtual OUString get_id(int pos) const override + { + const OUString* pRet = getEntryData(pos); + if (!pRet) + return OUString(); + return *pRet; + } + + void set_id(SvTreeListEntry* pEntry, const OUString& rId) + { + m_aUserData.emplace_back(std::make_unique<OUString>(rId)); + pEntry->SetUserData(m_aUserData.back().get()); + } + + virtual void set_id(int pos, const OUString& rId) override + { + SvTreeListEntry* pEntry = m_xTreeView->GetEntry(nullptr, pos); + set_id(pEntry, rId); + } + + virtual int get_selected_index() const override + { + assert(m_xTreeView->IsUpdateMode() && "don't request selection when frozen"); + SvTreeListEntry* pEntry = m_xTreeView->FirstSelected(); + if (!pEntry) + return -1; + return SvTreeList::GetRelPos(pEntry); + } + + virtual OUString get_selected_text() const override + { + assert(m_xTreeView->IsUpdateMode() && "don't request selection when frozen"); + if (SvTreeListEntry* pEntry = m_xTreeView->FirstSelected()) + return m_xTreeView->GetEntryText(pEntry); + return OUString(); + } + + virtual OUString get_selected_id() const override + { + assert(m_xTreeView->IsUpdateMode() && "don't request selection when frozen"); + if (SvTreeListEntry* pEntry = m_xTreeView->FirstSelected()) + { + if (const OUString* pStr = static_cast<const OUString*>(pEntry->GetUserData())) + return *pStr; + } + return OUString(); + } + + virtual std::unique_ptr<weld::TreeIter> + make_iterator(const weld::TreeIter* pOrig) const override + { + return std::unique_ptr<weld::TreeIter>( + new SalInstanceTreeIter(static_cast<const SalInstanceTreeIter*>(pOrig))); + } + + virtual void copy_iterator(const weld::TreeIter& rSource, weld::TreeIter& rDest) const override + { + const SalInstanceTreeIter& rVclSource(static_cast<const SalInstanceTreeIter&>(rSource)); + SalInstanceTreeIter& rVclDest(static_cast<SalInstanceTreeIter&>(rDest)); + rVclDest.iter = rVclSource.iter; + } + + virtual bool get_selected(weld::TreeIter* pIter) const override + { + SvTreeListEntry* pEntry = m_xTreeView->FirstSelected(); + auto pVclIter = static_cast<SalInstanceTreeIter*>(pIter); + if (pVclIter) + pVclIter->iter = pEntry; + return pEntry != nullptr; + } + + virtual bool get_cursor(weld::TreeIter* pIter) const override + { + SvTreeListEntry* pEntry = m_xTreeView->GetCurEntry(); + auto pVclIter = static_cast<SalInstanceTreeIter*>(pIter); + if (pVclIter) + pVclIter->iter = pEntry; + return pEntry != nullptr; + } + + virtual void set_cursor(const weld::TreeIter& rIter) override + { + const SalInstanceTreeIter& rVclIter = static_cast<const SalInstanceTreeIter&>(rIter); + disable_notify_events(); + m_xTreeView->SetCurEntry(rVclIter.iter); + enable_notify_events(); + } + + virtual bool get_iter_first(weld::TreeIter& rIter) const override + { + SalInstanceTreeIter& rVclIter = static_cast<SalInstanceTreeIter&>(rIter); + rVclIter.iter = m_xTreeView->GetEntry(0); + return rVclIter.iter != nullptr; + } + + virtual bool iter_next_sibling(weld::TreeIter& rIter) const override + { + SalInstanceTreeIter& rVclIter = static_cast<SalInstanceTreeIter&>(rIter); + rVclIter.iter = rVclIter.iter->NextSibling(); + return rVclIter.iter != nullptr; + } + + virtual bool iter_previous_sibling(weld::TreeIter& rIter) const override + { + SalInstanceTreeIter& rVclIter = static_cast<SalInstanceTreeIter&>(rIter); + rVclIter.iter = rVclIter.iter->PrevSibling(); + return rVclIter.iter != nullptr; + } + + virtual bool iter_next(weld::TreeIter& rIter) const override + { + SalInstanceTreeIter& rVclIter = static_cast<SalInstanceTreeIter&>(rIter); + rVclIter.iter = m_xTreeView->Next(rVclIter.iter); + if (rVclIter.iter && IsDummyEntry(rVclIter.iter)) + return iter_next(rVclIter); + return rVclIter.iter != nullptr; + } + + virtual bool iter_previous(weld::TreeIter& rIter) const override + { + SalInstanceTreeIter& rVclIter = static_cast<SalInstanceTreeIter&>(rIter); + rVclIter.iter = m_xTreeView->Prev(rVclIter.iter); + if (rVclIter.iter && IsDummyEntry(rVclIter.iter)) + return iter_previous(rVclIter); + return rVclIter.iter != nullptr; + } + + virtual bool iter_next_visible(weld::TreeIter& rIter) const override + { + SalInstanceTreeIter& rVclIter = static_cast<SalInstanceTreeIter&>(rIter); + rVclIter.iter = m_xTreeView->NextVisible(rVclIter.iter); + if (rVclIter.iter && IsDummyEntry(rVclIter.iter)) + return iter_next_visible(rVclIter); + return rVclIter.iter != nullptr; + } + + virtual bool iter_children(weld::TreeIter& rIter) const override + { + SalInstanceTreeIter& rVclIter = static_cast<SalInstanceTreeIter&>(rIter); + rVclIter.iter = m_xTreeView->FirstChild(rVclIter.iter); + bool bRet = rVclIter.iter != nullptr; + if (bRet) + { + //on-demand dummy entry doesn't count + return !IsDummyEntry(rVclIter.iter); + } + return bRet; + } + + virtual bool iter_parent(weld::TreeIter& rIter) const override + { + SalInstanceTreeIter& rVclIter = static_cast<SalInstanceTreeIter&>(rIter); + rVclIter.iter = m_xTreeView->GetParent(rVclIter.iter); + return rVclIter.iter != nullptr; + } + + virtual void remove(const weld::TreeIter& rIter) override + { + disable_notify_events(); + const SalInstanceTreeIter& rVclIter = static_cast<const SalInstanceTreeIter&>(rIter); + m_xTreeView->RemoveEntry(rVclIter.iter); + enable_notify_events(); + } + + virtual void select(const weld::TreeIter& rIter) override + { + assert(m_xTreeView->IsUpdateMode() && "don't select when frozen, select after thaw. Note selection doesn't survive a freeze"); + disable_notify_events(); + const SalInstanceTreeIter& rVclIter = static_cast<const SalInstanceTreeIter&>(rIter); + m_xTreeView->Select(rVclIter.iter, true); + enable_notify_events(); + } + + virtual void scroll_to_row(const weld::TreeIter& rIter) override + { + assert(m_xTreeView->IsUpdateMode() && "don't select when frozen, select after thaw. Note selection doesn't survive a freeze"); + disable_notify_events(); + const SalInstanceTreeIter& rVclIter = static_cast<const SalInstanceTreeIter&>(rIter); + m_xTreeView->MakeVisible(rVclIter.iter); + enable_notify_events(); + } + + virtual void unselect(const weld::TreeIter& rIter) override + { + assert(m_xTreeView->IsUpdateMode() && "don't unselect when frozen"); + disable_notify_events(); + const SalInstanceTreeIter& rVclIter = static_cast<const SalInstanceTreeIter&>(rIter); + m_xTreeView->Select(rVclIter.iter, false); + enable_notify_events(); + } + + virtual int get_iter_depth(const weld::TreeIter& rIter) const override + { + const SalInstanceTreeIter& rVclIter = static_cast<const SalInstanceTreeIter&>(rIter); + return m_xTreeView->GetModel()->GetDepth(rVclIter.iter); + } + + virtual bool iter_has_child(const weld::TreeIter& rIter) const override + { + weld::TreeIter& rNonConstIter = const_cast<weld::TreeIter&>(rIter); + SalInstanceTreeIter& rVclIter = static_cast<SalInstanceTreeIter&>(rNonConstIter); + SvTreeListEntry* restore(rVclIter.iter); + bool ret = iter_children(rNonConstIter); + rVclIter.iter = restore; + return ret; + } + + virtual bool get_row_expanded(const weld::TreeIter& rIter) const override + { + const SalInstanceTreeIter& rVclIter = static_cast<const SalInstanceTreeIter&>(rIter); + return m_xTreeView->IsExpanded(rVclIter.iter); + } + + virtual bool get_children_on_demand(const weld::TreeIter& rIter) const override + { + const SalInstanceTreeIter& rVclIter = static_cast<const SalInstanceTreeIter&>(rIter); + if (m_aExpandingPlaceHolderParents.count(rVclIter.iter)) + return true; + return GetPlaceHolderChild(rVclIter.iter) != nullptr; + } + + virtual void set_children_on_demand(const weld::TreeIter& rIter, bool bChildrenOnDemand) override + { + disable_notify_events(); + + const SalInstanceTreeIter& rVclIter = static_cast<const SalInstanceTreeIter&>(rIter); + + SvTreeListEntry* pPlaceHolder = GetPlaceHolderChild(rVclIter.iter); + + if (bChildrenOnDemand && !pPlaceHolder) + { + pPlaceHolder = m_xTreeView->InsertEntry("<dummy>", rVclIter.iter, false, 0, nullptr); + SvViewDataEntry* pViewData = m_xTreeView->GetViewDataEntry(pPlaceHolder); + pViewData->SetSelectable(false); + } + else if (!bChildrenOnDemand && pPlaceHolder) + m_xTreeView->RemoveEntry(pPlaceHolder); + + enable_notify_events(); + } + + virtual void expand_row(const weld::TreeIter& rIter) override + { + assert(m_xTreeView->IsUpdateMode() && "don't expand when frozen"); + const SalInstanceTreeIter& rVclIter = static_cast<const SalInstanceTreeIter&>(rIter); + if (!m_xTreeView->IsExpanded(rVclIter.iter) && signal_expanding(rIter)) + m_xTreeView->Expand(rVclIter.iter); + } + + virtual void collapse_row(const weld::TreeIter& rIter) override + { + const SalInstanceTreeIter& rVclIter = static_cast<const SalInstanceTreeIter&>(rIter); + if (m_xTreeView->IsExpanded(rVclIter.iter) && signal_collapsing(rIter)) + m_xTreeView->Collapse(rVclIter.iter); + } + + virtual OUString get_text(const weld::TreeIter& rIter, int col) const override + { + const SalInstanceTreeIter& rVclIter = static_cast<const SalInstanceTreeIter&>(rIter); + return get_text(rVclIter.iter, col); + } + + virtual void set_text(const weld::TreeIter& rIter, const OUString& rText, int col) override + { + const SalInstanceTreeIter& rVclIter = static_cast<const SalInstanceTreeIter&>(rIter); + set_text(rVclIter.iter, rText, col); + } + + virtual OUString get_id(const weld::TreeIter& rIter) const override + { + const SalInstanceTreeIter& rVclIter = static_cast<const SalInstanceTreeIter&>(rIter); + const OUString* pStr = static_cast<const OUString*>(rVclIter.iter->GetUserData()); + if (pStr) + return *pStr; + return OUString(); + } + + virtual void set_id(const weld::TreeIter& rIter, const OUString& rId) override + { + const SalInstanceTreeIter& rVclIter = static_cast<const SalInstanceTreeIter&>(rIter); + set_id(rVclIter.iter, rId); + } + + virtual void enable_drag_source(rtl::Reference<TransferDataContainer>& rHelper, + sal_uInt8 eDNDConstants) override + { + m_xTreeView->SetDragHelper(rHelper, eDNDConstants); + } + + virtual void set_selection_mode(SelectionMode eMode) override + { + m_xTreeView->SetSelectionMode(eMode); + } + + virtual void all_foreach(const std::function<bool(weld::TreeIter&)>& func) override + { + UpdateGuardIfHidden aGuard(*m_xTreeView); + + SalInstanceTreeIter aVclIter(m_xTreeView->First()); + while (aVclIter.iter) + { + if (func(aVclIter)) + return; + iter_next(aVclIter); + } + } + + virtual void selected_foreach(const std::function<bool(weld::TreeIter&)>& func) override + { + SalInstanceTreeIter aVclIter(m_xTreeView->FirstSelected()); + while (aVclIter.iter) + { + if (func(aVclIter)) + return; + aVclIter.iter = m_xTreeView->NextSelected(aVclIter.iter); + } + } + + virtual void visible_foreach(const std::function<bool(weld::TreeIter&)>& func) override + { + SalInstanceTreeIter aVclIter(m_xTreeView->GetFirstEntryInView()); + while (aVclIter.iter) + { + if (func(aVclIter)) + return; + aVclIter.iter = m_xTreeView->GetNextEntryInView(aVclIter.iter); + } + } + + virtual void connect_visible_range_changed(const Link<weld::TreeView&, void>& rLink) override + { + weld::TreeView::connect_visible_range_changed(rLink); + m_xTreeView->SetScrolledHdl(LINK(this, SalInstanceTreeView, VisibleRangeChangedHdl)); + } + + virtual void remove_selection() override + { + disable_notify_events(); + SvTreeListEntry* pSelected = m_xTreeView->FirstSelected(); + while (pSelected) + { + SvTreeListEntry* pNextSelected = m_xTreeView->NextSelected(pSelected); + m_xTreeView->RemoveEntry(pSelected); + pSelected = pNextSelected; + } + enable_notify_events(); + } + + virtual bool is_selected(const weld::TreeIter& rIter) const override + { + const SalInstanceTreeIter& rVclIter = static_cast<const SalInstanceTreeIter&>(rIter); + return m_xTreeView->IsSelected(rVclIter.iter); + } + + virtual int get_iter_index_in_parent(const weld::TreeIter& rIter) const override + { + const SalInstanceTreeIter& rVclIter = static_cast<const SalInstanceTreeIter&>(rIter); + return SvTreeList::GetRelPos(rVclIter.iter); + } + + virtual int iter_compare(const weld::TreeIter& a, const weld::TreeIter& b) const override + { + const SalInstanceTreeIter& rVclIterA = static_cast<const SalInstanceTreeIter&>(a); + const SalInstanceTreeIter& rVclIterB = static_cast<const SalInstanceTreeIter&>(b); + const SvTreeList* pModel = m_xTreeView->GetModel(); + auto nAbsPosA = pModel->GetAbsPos(rVclIterA.iter); + auto nAbsPosB = pModel->GetAbsPos(rVclIterB.iter); + if (nAbsPosA < nAbsPosB) + return -1; + if (nAbsPosA > nAbsPosB) + return 1; + return 0; + } + + virtual void move_subtree(weld::TreeIter& rNode, const weld::TreeIter* pNewParent, + int nIndexInNewParent) override + { + SalInstanceTreeIter& rVclIter = static_cast<SalInstanceTreeIter&>(rNode); + const SalInstanceTreeIter* pVclParentIter + = static_cast<const SalInstanceTreeIter*>(pNewParent); + m_xTreeView->GetModel()->Move( + rVclIter.iter, pVclParentIter ? pVclParentIter->iter : nullptr, nIndexInNewParent); + } + + virtual int count_selected_rows() const override { return m_xTreeView->GetSelectionCount(); } + + virtual int get_height_rows(int nRows) const override + { + return m_xTreeView->GetEntryHeight() * nRows; + } + + virtual void make_sorted() override + { + assert(m_xTreeView->IsUpdateMode() && "don't sort when frozen"); + m_xTreeView->SetStyle(m_xTreeView->GetStyle() | WB_SORT); + m_xTreeView->GetModel()->SetCompareHdl(LINK(this, SalInstanceTreeView, CompareHdl)); + set_sort_order(true); + } + + virtual void set_sort_func( + const std::function<int(const weld::TreeIter&, const weld::TreeIter&)>& func) override + { + weld::TreeView::set_sort_func(func); + SvTreeList* pListModel = m_xTreeView->GetModel(); + pListModel->Resort(); + } + + virtual void make_unsorted() override + { + m_xTreeView->SetStyle(m_xTreeView->GetStyle() & ~WB_SORT); + } + + virtual void set_sort_order(bool bAscending) override + { + SvTreeList* pListModel = m_xTreeView->GetModel(); + pListModel->SetSortMode(bAscending ? SortAscending : SortDescending); + pListModel->Resort(); + } + + virtual bool get_sort_order() const override + { + return m_xTreeView->GetModel()->GetSortMode() == SortAscending; + } + + virtual void set_sort_indicator(TriState eState, int col) override + { + if (col == -1) + col = 0; + + LclHeaderTabListBox* pHeaderBox = dynamic_cast<LclHeaderTabListBox*>(m_xTreeView.get()); + if (HeaderBar* pHeaderBar = pHeaderBox ? pHeaderBox->GetHeaderBar() : nullptr) + { + sal_uInt16 nTextId = pHeaderBar->GetItemId(col); + HeaderBarItemBits nBits = pHeaderBar->GetItemBits(nTextId); + nBits &= ~HeaderBarItemBits::UPARROW; + nBits &= ~HeaderBarItemBits::DOWNARROW; + if (eState != TRISTATE_INDET) + { + if (eState == TRISTATE_TRUE) + nBits |= HeaderBarItemBits::DOWNARROW; + else + nBits |= HeaderBarItemBits::UPARROW; + } + pHeaderBar->SetItemBits(nTextId, nBits); + } + } + + virtual TriState get_sort_indicator(int col) const override + { + if (col == -1) + col = 0; + + LclHeaderTabListBox* pHeaderBox = dynamic_cast<LclHeaderTabListBox*>(m_xTreeView.get()); + if (HeaderBar* pHeaderBar = pHeaderBox ? pHeaderBox->GetHeaderBar() : nullptr) + { + sal_uInt16 nTextId = pHeaderBar->GetItemId(col); + HeaderBarItemBits nBits = pHeaderBar->GetItemBits(nTextId); + if (nBits & HeaderBarItemBits::DOWNARROW) + return TRISTATE_TRUE; + if (nBits & HeaderBarItemBits::UPARROW) + return TRISTATE_FALSE; + } + + return TRISTATE_INDET; + } + + virtual int get_sort_column() const override { return m_nSortColumn; } + + virtual void set_sort_column(int nColumn) override + { + if (nColumn == -1) + { + make_unsorted(); + m_nSortColumn = -1; + return; + } + + if (nColumn != m_nSortColumn) + { + m_nSortColumn = nColumn; + m_xTreeView->GetModel()->Resort(); + } + } + + SvTabListBox& getTreeView() { return *m_xTreeView; } + + virtual bool get_dest_row_at_pos(const Point& rPos, weld::TreeIter* pResult, bool bHighLightTarget) override + { + LclTabListBox* pTreeView = !bHighLightTarget ? dynamic_cast<LclTabListBox*>(m_xTreeView.get()) : nullptr; + SvTreeListEntry* pTarget = pTreeView ? pTreeView->GetTargetAtPoint(rPos, false) : m_xTreeView->GetDropTarget(rPos); + + if (pTarget && pResult) + { + SalInstanceTreeIter& rSalIter = static_cast<SalInstanceTreeIter&>(*pResult); + rSalIter.iter = pTarget; + } + + return pTarget != nullptr; + } + + virtual void unset_drag_dest_row() override + { + m_xTreeView->UnsetDropTarget(); + } + + virtual tools::Rectangle get_row_area(const weld::TreeIter& rIter) const override + { + return m_xTreeView->GetBoundingRect(static_cast<const SalInstanceTreeIter&>(rIter).iter); + } + + virtual TreeView* get_drag_source() const override { return g_DragSource; } + + virtual int vadjustment_get_value() const override + { + int nValue = -1; + const SvTreeListEntry* pEntry = m_xTreeView->GetFirstEntryInView(); + if (pEntry) + nValue = m_xTreeView->GetAbsPos(pEntry); + return nValue; + } + + virtual void vadjustment_set_value(int nValue) override + { + if (nValue == -1) + return; + bool bUpdate = m_xTreeView->IsUpdateMode(); + if (bUpdate) + m_xTreeView->SetUpdateMode(false); + m_xTreeView->ScrollToAbsPos(nValue); + if (bUpdate) + m_xTreeView->SetUpdateMode(true); + } + + virtual ~SalInstanceTreeView() override + { + LclHeaderTabListBox* pHeaderBox = dynamic_cast<LclHeaderTabListBox*>(m_xTreeView.get()); + if (pHeaderBox) + { + if (HeaderBar* pHeaderBar = pHeaderBox->GetHeaderBar()) + { + pHeaderBar->SetSelectHdl(Link<HeaderBar*, void>()); + pHeaderBar->SetEndDragHdl(Link<HeaderBar*, void>()); + } + } + else + { + static_cast<LclTabListBox&>(*m_xTreeView).SetEndDragHdl(Link<SvTreeListBox*, void>()); + static_cast<LclTabListBox&>(*m_xTreeView).SetStartDragHdl(Link<SvTreeListBox*, bool>()); + static_cast<LclTabListBox&>(*m_xTreeView) + .SetModelChangedHdl(Link<SvTreeListBox*, void>()); + } + m_xTreeView->SetPopupMenuHdl(Link<const CommandEvent&, bool>()); + m_xTreeView->SetExpandingHdl(Link<SvTreeListBox*, bool>()); + m_xTreeView->SetDoubleClickHdl(Link<SvTreeListBox*, bool>()); + m_xTreeView->SetSelectHdl(Link<SvTreeListBox*, void>()); + m_xTreeView->SetDeselectHdl(Link<SvTreeListBox*, void>()); + m_xTreeView->SetScrolledHdl(Link<SvTreeListBox*, void>()); + m_xTreeView->SetTooltipHdl(Link<const HelpEvent&, bool>()); + m_xTreeView->SetCustomRenderHdl(Link<svtree_render_args, void>()); + m_xTreeView->SetCustomMeasureHdl(Link<svtree_measure_args, Size>()); + } +}; + +IMPL_LINK(SalInstanceTreeView, TooltipHdl, const HelpEvent&, rHEvt, bool) +{ + if (notify_events_disabled()) + return false; + Point aPos(m_xTreeView->ScreenToOutputPixel(rHEvt.GetMousePosPixel())); + SvTreeListEntry* pEntry = m_xTreeView->GetEntry(aPos); + if (pEntry) + { + SalInstanceTreeIter aIter(pEntry); + OUString aTooltip = signal_query_tooltip(aIter); + if (aTooltip.isEmpty()) + return false; + Size aSize(m_xTreeView->GetOutputSizePixel().Width(), m_xTreeView->GetEntryHeight()); + tools::Rectangle aScreenRect( + m_xTreeView->OutputToScreenPixel(m_xTreeView->GetEntryPosition(pEntry)), aSize); + Help::ShowQuickHelp(m_xTreeView, aScreenRect, aTooltip); + } + return true; +} + +IMPL_LINK(SalInstanceTreeView, CustomRenderHdl, svtree_render_args, payload, void) +{ + vcl::RenderContext& rRenderDevice = std::get<0>(payload); + const tools::Rectangle& rRect = std::get<1>(payload); + const SvTreeListEntry& rEntry = std::get<2>(payload); + const OUString* pId = static_cast<const OUString*>(rEntry.GetUserData()); + if (!pId) + return; + signal_custom_render(rRenderDevice, rRect, m_xTreeView->IsSelected(&rEntry), *pId); +} + +IMPL_LINK(SalInstanceTreeView, CustomMeasureHdl, svtree_measure_args, payload, Size) +{ + vcl::RenderContext& rRenderDevice = payload.first; + const SvTreeListEntry& rEntry = payload.second; + const OUString* pId = static_cast<const OUString*>(rEntry.GetUserData()); + if (!pId) + return Size(); + return signal_custom_get_size(rRenderDevice, *pId); +} + +IMPL_LINK(SalInstanceTreeView, CompareHdl, const SvSortData&, rSortData, sal_Int32) +{ + const SvTreeListEntry* pLHS = rSortData.pLeft; + const SvTreeListEntry* pRHS = rSortData.pRight; + assert(pLHS && pRHS); + + if (m_aCustomSort) + return m_aCustomSort(SalInstanceTreeIter(const_cast<SvTreeListEntry*>(pLHS)), + SalInstanceTreeIter(const_cast<SvTreeListEntry*>(pRHS))); + + const SvLBoxString* pLeftTextItem; + const SvLBoxString* pRightTextItem; + + if (m_nSortColumn != -1) + { + size_t col = m_nSortColumn; + + ++col; //skip dummy/expander column + + if (col < pLHS->ItemCount()) + { + const SvLBoxString& rLeftTextItem + = static_cast<const SvLBoxString&>(pLHS->GetItem(col)); + pLeftTextItem = &rLeftTextItem; + } + else + pLeftTextItem = nullptr; + if (col < pRHS->ItemCount()) + { + const SvLBoxString& rRightTextItem + = static_cast<const SvLBoxString&>(pRHS->GetItem(col)); + pRightTextItem = &rRightTextItem; + } + else + pRightTextItem = nullptr; + } + else + { + pLeftTextItem + = static_cast<const SvLBoxString*>(pLHS->GetFirstItem(SvLBoxItemType::String)); + pRightTextItem + = static_cast<const SvLBoxString*>(pRHS->GetFirstItem(SvLBoxItemType::String)); + } + + return m_xTreeView->DefaultCompare(pLeftTextItem, pRightTextItem); +} + +IMPL_LINK_NOARG(SalInstanceTreeView, VisibleRangeChangedHdl, SvTreeListBox*, void) +{ + if (notify_events_disabled()) + return; + signal_visible_range_changed(); +} + +IMPL_LINK_NOARG(SalInstanceTreeView, ModelChangedHdl, SvTreeListBox*, void) +{ + if (notify_events_disabled()) + return; + signal_model_changed(); +} + +IMPL_LINK_NOARG(SalInstanceTreeView, StartDragHdl, SvTreeListBox*, bool) +{ + bool bUnsetDragIcon(false); // ignored for vcl + if (m_aDragBeginHdl.Call(bUnsetDragIcon)) + return true; + g_DragSource = this; + return false; +} + +IMPL_STATIC_LINK_NOARG(SalInstanceTreeView, FinishDragHdl, SvTreeListBox*, void) +{ + g_DragSource = nullptr; +} + +IMPL_LINK(SalInstanceTreeView, ToggleHdl, SvLBoxButtonData*, pData, void) +{ + SvTreeListEntry* pEntry = pData->GetActEntry(); + SvLBoxButton* pBox = pData->GetActBox(); + + // tdf#122874 Select the row, calling SelectHdl, before handling + // the toggle + if (!m_xTreeView->IsSelected(pEntry)) + { + m_xTreeView->SelectAll(false); + m_xTreeView->Select(pEntry, true); + } + + // toggled signal handlers can query get_cursor to get which + // node was clicked + m_xTreeView->pImpl->m_pCursor = pEntry; + + for (int i = 1, nCount = pEntry->ItemCount(); i < nCount; ++i) + { + SvLBoxItem& rItem = pEntry->GetItem(i); + if (&rItem == pBox) + { + int nRow = SvTreeList::GetRelPos(pEntry); + int nCol = i - 1; // less dummy/expander column + signal_toggled(std::make_pair(nRow, nCol)); + break; + } + } +} + +IMPL_LINK_NOARG(SalInstanceTreeView, SelectHdl, SvTreeListBox*, void) +{ + if (notify_events_disabled()) + return; + signal_changed(); +} + +IMPL_LINK_NOARG(SalInstanceTreeView, DeSelectHdl, SvTreeListBox*, void) +{ + if (notify_events_disabled()) + return; + if (m_xTreeView->GetSelectionMode() == SelectionMode::Single) + return; + signal_changed(); +} + +IMPL_LINK_NOARG(SalInstanceTreeView, DoubleClickHdl, SvTreeListBox*, bool) +{ + if (notify_events_disabled()) + return false; + return !signal_row_activated(); +} + +IMPL_LINK(SalInstanceTreeView, EndDragHdl, HeaderBar*, pHeaderBar, void) +{ + std::vector<long> aTabPositions; + aTabPositions.push_back(0); + for (int i = 0; i < pHeaderBar->GetItemCount() - 1; ++i) + aTabPositions.push_back(aTabPositions[i] + + pHeaderBar->GetItemSize(pHeaderBar->GetItemId(i))); + m_xTreeView->SetTabs(aTabPositions.size(), aTabPositions.data(), MapUnit::MapPixel); +} + +IMPL_LINK(SalInstanceTreeView, HeaderBarClickedHdl, HeaderBar*, pHeaderBar, void) +{ + sal_uInt16 nId = pHeaderBar->GetCurItemId(); + if (!(pHeaderBar->GetItemBits(nId) & HeaderBarItemBits::CLICKABLE)) + return; + signal_column_clicked(pHeaderBar->GetItemPos(nId)); +} + +IMPL_LINK_NOARG(SalInstanceTreeView, ExpandingHdl, SvTreeListBox*, bool) +{ + SvTreeListEntry* pEntry = m_xTreeView->GetHdlEntry(); + SalInstanceTreeIter aIter(pEntry); + + if (m_xTreeView->IsExpanded(pEntry)) + { + //collapsing; + return signal_collapsing(aIter); + } + + // expanding + + // if there's a preexisting placeholder child, required to make this + // potentially expandable in the first place, now we remove it + SvTreeListEntry* pPlaceHolder = GetPlaceHolderChild(pEntry); + if (pPlaceHolder) + { + m_aExpandingPlaceHolderParents.insert(pEntry); + m_xTreeView->RemoveEntry(pPlaceHolder); + } + + bool bRet = signal_expanding(aIter); + + if (pPlaceHolder) + { + //expand disallowed, restore placeholder + if (!bRet) + { + pPlaceHolder = m_xTreeView->InsertEntry("<dummy>", pEntry, false, 0, nullptr); + SvViewDataEntry* pViewData = m_xTreeView->GetViewDataEntry(pPlaceHolder); + pViewData->SetSelectable(false); + } + m_aExpandingPlaceHolderParents.erase(pEntry); + } + + return bRet; +} + +IMPL_LINK(SalInstanceTreeView, PopupMenuHdl, const CommandEvent&, rEvent, bool) +{ + return m_aPopupMenuHdl.Call(rEvent); +} + +IMPL_LINK(SalInstanceTreeView, EditingEntryHdl, SvTreeListEntry*, pEntry, bool) +{ + return signal_editing_started(SalInstanceTreeIter(pEntry)); +} + +IMPL_LINK(SalInstanceTreeView, EditedEntryHdl, IterString, rIterString, bool) +{ + return signal_editing_done(std::pair<const weld::TreeIter&, OUString>( + SalInstanceTreeIter(rIterString.first), rIterString.second)); +} + +class SalInstanceIconView : public SalInstanceContainer, public virtual weld::IconView +{ +private: + // owner for UserData + std::vector<std::unique_ptr<OUString>> m_aUserData; + VclPtr<::IconView> m_xIconView; + + DECL_LINK(SelectHdl, SvTreeListBox*, void); + DECL_LINK(DeSelectHdl, SvTreeListBox*, void); + DECL_LINK(DoubleClickHdl, SvTreeListBox*, bool); + +public: + SalInstanceIconView(::IconView* pIconView, SalInstanceBuilder* pBuilder, bool bTakeOwnership) + : SalInstanceContainer(pIconView, pBuilder, bTakeOwnership) + , m_xIconView(pIconView) + { + m_xIconView->SetSelectHdl(LINK(this, SalInstanceIconView, SelectHdl)); + m_xIconView->SetDeselectHdl(LINK(this, SalInstanceIconView, DeSelectHdl)); + m_xIconView->SetDoubleClickHdl(LINK(this, SalInstanceIconView, DoubleClickHdl)); + } + + virtual void freeze() override + { + SalInstanceWidget::freeze(); + m_xIconView->SetUpdateMode(false); + } + + virtual void thaw() override + { + m_xIconView->SetUpdateMode(true); + SalInstanceWidget::thaw(); + } + + virtual void insert(int pos, const OUString* pStr, const OUString* pId, + const OUString* pIconName, weld::TreeIter* pRet) override + { + disable_notify_events(); + auto nInsertPos = pos == -1 ? TREELIST_APPEND : pos; + void* pUserData; + if (pId) + { + m_aUserData.emplace_back(std::make_unique<OUString>(*pId)); + pUserData = m_aUserData.back().get(); + } + else + pUserData = nullptr; + + SvTreeListEntry* pEntry = new SvTreeListEntry; + if (pIconName) + { + Image aImage(createImage(*pIconName)); + pEntry->AddItem(std::make_unique<SvLBoxContextBmp>(aImage, aImage, false)); + } + else + { + Image aDummy; + pEntry->AddItem(std::make_unique<SvLBoxContextBmp>(aDummy, aDummy, false)); + } + if (pStr) + pEntry->AddItem(std::make_unique<SvLBoxString>(*pStr)); + pEntry->SetUserData(pUserData); + m_xIconView->Insert(pEntry, nullptr, nInsertPos); + + if (pRet) + { + SalInstanceTreeIter* pVclRetIter = static_cast<SalInstanceTreeIter*>(pRet); + pVclRetIter->iter = pEntry; + } + + enable_notify_events(); + } + + virtual OUString get_selected_id() const override + { + assert(m_xIconView->IsUpdateMode() && "don't request selection when frozen"); + if (SvTreeListEntry* pEntry = m_xIconView->FirstSelected()) + { + if (const OUString* pStr = static_cast<const OUString*>(pEntry->GetUserData())) + return *pStr; + } + return OUString(); + } + + virtual OUString get_selected_text() const override + { + assert(m_xIconView->IsUpdateMode() && "don't request selection when frozen"); + if (SvTreeListEntry* pEntry = m_xIconView->FirstSelected()) + return m_xIconView->GetEntryText(pEntry); + return OUString(); + } + + virtual int count_selected_items() const override { return m_xIconView->GetSelectionCount(); } + + virtual void select(int pos) override + { + assert(m_xIconView->IsUpdateMode() && "don't select when frozen, select after thaw. Note selection doesn't survive a freeze"); + disable_notify_events(); + if (pos == -1 || (pos == 0 && n_children() == 0)) + m_xIconView->SelectAll(false); + else + { + SvTreeListEntry* pEntry = m_xIconView->GetEntry(nullptr, pos); + m_xIconView->Select(pEntry, true); + m_xIconView->MakeVisible(pEntry); + } + enable_notify_events(); + } + + virtual void unselect(int pos) override + { + assert(m_xIconView->IsUpdateMode() && "don't select when frozen, select after thaw. Note selection doesn't survive a freeze"); + disable_notify_events(); + if (pos == -1) + m_xIconView->SelectAll(true); + else + { + SvTreeListEntry* pEntry = m_xIconView->GetEntry(nullptr, pos); + m_xIconView->Select(pEntry, false); + } + enable_notify_events(); + } + + virtual int n_children() const override + { + return m_xIconView->GetModel()->GetChildList(nullptr).size(); + } + + virtual std::unique_ptr<weld::TreeIter> + make_iterator(const weld::TreeIter* pOrig) const override + { + return std::unique_ptr<weld::TreeIter>( + new SalInstanceTreeIter(static_cast<const SalInstanceTreeIter*>(pOrig))); + } + + virtual bool get_selected(weld::TreeIter* pIter) const override + { + SvTreeListEntry* pEntry = m_xIconView->FirstSelected(); + auto pVclIter = static_cast<SalInstanceTreeIter*>(pIter); + if (pVclIter) + pVclIter->iter = pEntry; + return pEntry != nullptr; + } + + virtual bool get_cursor(weld::TreeIter* pIter) const override + { + SvTreeListEntry* pEntry = m_xIconView->GetCurEntry(); + auto pVclIter = static_cast<SalInstanceTreeIter*>(pIter); + if (pVclIter) + pVclIter->iter = pEntry; + return pEntry != nullptr; + } + + virtual void set_cursor(const weld::TreeIter& rIter) override + { + const SalInstanceTreeIter& rVclIter = static_cast<const SalInstanceTreeIter&>(rIter); + disable_notify_events(); + m_xIconView->SetCurEntry(rVclIter.iter); + enable_notify_events(); + } + + virtual bool get_iter_first(weld::TreeIter& rIter) const override + { + SalInstanceTreeIter& rVclIter = static_cast<SalInstanceTreeIter&>(rIter); + rVclIter.iter = m_xIconView->GetEntry(0); + return rVclIter.iter != nullptr; + } + + virtual void scroll_to_item(const weld::TreeIter& rIter) override + { + assert(m_xIconView->IsUpdateMode() && "don't select when frozen, select after thaw. Note selection doesn't survive a freeze"); + disable_notify_events(); + const SalInstanceTreeIter& rVclIter = static_cast<const SalInstanceTreeIter&>(rIter); + m_xIconView->MakeVisible(rVclIter.iter); + enable_notify_events(); + } + + virtual void selected_foreach(const std::function<bool(weld::TreeIter&)>& func) override + { + SalInstanceTreeIter aVclIter(m_xIconView->FirstSelected()); + while (aVclIter.iter) + { + if (func(aVclIter)) + return; + aVclIter.iter = m_xIconView->NextSelected(aVclIter.iter); + } + } + + virtual OUString get_id(const weld::TreeIter& rIter) const override + { + const SalInstanceTreeIter& rVclIter = static_cast<const SalInstanceTreeIter&>(rIter); + const OUString* pStr = static_cast<const OUString*>(rVclIter.iter->GetUserData()); + if (pStr) + return *pStr; + return OUString(); + } + + virtual void clear() override + { + disable_notify_events(); + m_xIconView->Clear(); + m_aUserData.clear(); + enable_notify_events(); + } + + virtual ~SalInstanceIconView() override + { + m_xIconView->SetDoubleClickHdl(Link<SvTreeListBox*, bool>()); + m_xIconView->SetSelectHdl(Link<SvTreeListBox*, void>()); + m_xIconView->SetDeselectHdl(Link<SvTreeListBox*, void>()); + } +}; + +IMPL_LINK_NOARG(SalInstanceIconView, SelectHdl, SvTreeListBox*, void) +{ + if (notify_events_disabled()) + return; + signal_selection_changed(); +} + +IMPL_LINK_NOARG(SalInstanceIconView, DeSelectHdl, SvTreeListBox*, void) +{ + if (notify_events_disabled()) + return; + if (m_xIconView->GetSelectionMode() == SelectionMode::Single) + return; + signal_selection_changed(); +} + +IMPL_LINK_NOARG(SalInstanceIconView, DoubleClickHdl, SvTreeListBox*, bool) +{ + if (notify_events_disabled()) + return false; + return !signal_item_activated(); +} + +double SalInstanceSpinButton::toField(int nValue) const +{ + return static_cast<double>(nValue) / Power10(get_digits()); + } + +int SalInstanceSpinButton::fromField(double fValue) const +{ + return FRound(fValue * Power10(get_digits())); +} + +SalInstanceSpinButton::SalInstanceSpinButton(FormattedField* pButton, SalInstanceBuilder* pBuilder, + bool bTakeOwnership) + : SalInstanceEntry(pButton, pBuilder, bTakeOwnership) + , m_xButton(pButton) +{ + m_xButton->SetThousandsSep(false); //off by default, MetricSpinButton enables it + m_xButton->SetUpHdl(LINK(this, SalInstanceSpinButton, UpDownHdl)); + m_xButton->SetDownHdl(LINK(this, SalInstanceSpinButton, UpDownHdl)); + m_xButton->SetLoseFocusHdl(LINK(this, SalInstanceSpinButton, LoseFocusHdl)); + m_xButton->SetOutputHdl(LINK(this, SalInstanceSpinButton, OutputHdl)); + m_xButton->SetInputHdl(LINK(this, SalInstanceSpinButton, InputHdl)); + if (Edit* pEdit = m_xButton->GetSubEdit()) + pEdit->SetActivateHdl(LINK(this, SalInstanceSpinButton, ActivateHdl)); + else + m_xButton->SetActivateHdl(LINK(this, SalInstanceSpinButton, ActivateHdl)); +} + +int SalInstanceSpinButton::get_value() const +{ + return fromField(m_xButton->GetValue()); + } + +void SalInstanceSpinButton::set_value(int value) +{ + m_xButton->SetValue(toField(value)); + } + +void SalInstanceSpinButton::set_range(int min, int max) +{ + m_xButton->SetMinValue(toField(min)); + m_xButton->SetMaxValue(toField(max)); +} + +void SalInstanceSpinButton::get_range(int& min, int& max) const +{ + min = fromField(m_xButton->GetMinValue()); + max = fromField(m_xButton->GetMaxValue()); +} + +void SalInstanceSpinButton::set_increments(int step, int /*page*/) +{ + m_xButton->SetSpinSize(toField(step)); +} + +void SalInstanceSpinButton::get_increments(int& step, int& page) const +{ + step = fromField(m_xButton->GetSpinSize()); + page = fromField(m_xButton->GetSpinSize()); +} + +void SalInstanceSpinButton::set_digits(unsigned int digits) +{ + m_xButton->SetDecimalDigits(digits); +} + +// SpinButton may be comprised of multiple subwidgets, consider the lot as +// one thing for focus +bool SalInstanceSpinButton::has_focus() const +{ + return m_xWidget->HasChildPathFocus(); +} + +//so with hh::mm::ss, incrementing mm will not reset ss +void SalInstanceSpinButton::DisableRemainderFactor() +{ + m_xButton->DisableRemainderFactor(); +} + +//off by default for direct SpinButtons, MetricSpinButton enables it +void SalInstanceSpinButton::SetUseThousandSep() +{ + m_xButton->SetThousandsSep(true); +} + +unsigned int SalInstanceSpinButton::get_digits() const +{ + return m_xButton->GetDecimalDigits(); +} + +SalInstanceSpinButton::~SalInstanceSpinButton() +{ + if (Edit* pEdit = m_xButton->GetSubEdit()) + pEdit->SetActivateHdl(Link<Edit&, bool>()); + else + m_xButton->SetActivateHdl(Link<Edit&, bool>()); + m_xButton->SetInputHdl(Link<sal_Int64*, TriState>()); + m_xButton->SetOutputHdl(Link<Edit&, bool>()); + m_xButton->SetLoseFocusHdl(Link<Control&, void>()); + m_xButton->SetDownHdl(Link<SpinField&, void>()); + m_xButton->SetUpHdl(Link<SpinField&, void>()); +} + +IMPL_LINK_NOARG(SalInstanceSpinButton, ActivateHdl, Edit&, bool) +{ + // tdf#122348 return pressed to end dialog + signal_value_changed(); + return m_aActivateHdl.Call(*this); +} + +IMPL_LINK_NOARG(SalInstanceSpinButton, UpDownHdl, SpinField&, void) { signal_value_changed(); } + +IMPL_LINK_NOARG(SalInstanceSpinButton, LoseFocusHdl, Control&, void) { signal_value_changed(); } + +IMPL_LINK_NOARG(SalInstanceSpinButton, OutputHdl, Edit&, bool) { return signal_output(); } + +IMPL_LINK(SalInstanceSpinButton, InputHdl, sal_Int64*, pResult, TriState) +{ + int nResult; + TriState eRet = signal_input(&nResult); + if (eRet == TRISTATE_TRUE) + *pResult = nResult; + return eRet; +} + +namespace +{ +class SalInstanceFormattedSpinButton : public SalInstanceEntry, + public virtual weld::FormattedSpinButton +{ +private: + VclPtr<FormattedField> m_xButton; + +public: + SalInstanceFormattedSpinButton(FormattedField* pButton, SalInstanceBuilder* pBuilder, + bool bTakeOwnership) + : SalInstanceEntry(pButton, pBuilder, bTakeOwnership) + , m_xButton(pButton) + { + // #i6278# allow more decimal places than the output format. As + // the numbers shown in the edit fields are used for input, it makes more + // sense to display the values in the input format rather than the output + // format. + m_xButton->UseInputStringForFormatting(); + } + + virtual double get_value() const override { return m_xButton->GetValue(); } + + virtual void set_value(double value) override { m_xButton->SetValue(value); } + + virtual void set_range(double min, double max) override + { + m_xButton->SetMinValue(min); + m_xButton->SetMaxValue(max); + } + + virtual void get_range(double& min, double& max) const override + { + min = m_xButton->GetMinValue(); + max = m_xButton->GetMaxValue(); + } + + virtual void set_formatter(SvNumberFormatter* pFormatter) override + { + m_xButton->SetFormatter(pFormatter); + } + + virtual SvNumberFormatter* get_formatter() override { return m_xButton->GetFormatter(); } + + virtual sal_Int32 get_format_key() const override { return m_xButton->GetFormatKey(); } + + virtual void set_format_key(sal_Int32 nFormatKey) override + { + m_xButton->SetFormatKey(nFormatKey); + } + + virtual void treat_as_number(bool bSet) override { m_xButton->TreatAsNumber(bSet); } + + virtual void set_digits(unsigned int digits) override { m_xButton->SetDecimalDigits(digits); } +}; + +} + +SalInstanceLabel::SalInstanceLabel(Control* pLabel, SalInstanceBuilder* pBuilder, + bool bTakeOwnership) + : SalInstanceWidget(pLabel, pBuilder, bTakeOwnership) + , m_xLabel(pLabel) +{ +} + +void SalInstanceLabel::set_label(const OUString& rText) { m_xLabel->SetText(rText); } + +OUString SalInstanceLabel::get_label() const { return m_xLabel->GetText(); } + +void SalInstanceLabel::set_mnemonic_widget(Widget* pTarget) +{ + FixedText* pLabel = dynamic_cast<FixedText*>(m_xLabel.get()); + assert(pLabel && "can't use set_mnemonic_widget on SelectableFixedText"); + SalInstanceWidget* pTargetWidget = dynamic_cast<SalInstanceWidget*>(pTarget); + pLabel->set_mnemonic_widget(pTargetWidget ? pTargetWidget->getWidget() : nullptr); +} + +void SalInstanceLabel::set_message_type(weld::EntryMessageType eType) +{ + if (eType == weld::EntryMessageType::Error) + m_xLabel->SetControlBackground( + m_xLabel->GetSettings().GetStyleSettings().GetHighlightColor()); + else if (eType == weld::EntryMessageType::Warning) + m_xLabel->SetControlBackground(COL_YELLOW); + else + m_xLabel->SetControlBackground(); +} + +void SalInstanceLabel::set_font(const vcl::Font& rFont) +{ + m_xLabel->SetPointFont(*m_xLabel, rFont); + m_xLabel->Invalidate(); +} + +std::unique_ptr<weld::Label> SalInstanceFrame::weld_label_widget() const +{ + FixedText* pLabel = dynamic_cast<FixedText*>(m_xFrame->get_label_widget()); + if (!pLabel) + return nullptr; + return std::make_unique<SalInstanceLabel>(pLabel, m_pBuilder, false); +} + +namespace +{ +class SalInstanceTextView : public SalInstanceContainer, public virtual weld::TextView +{ +private: + VclPtr<VclMultiLineEdit> m_xTextView; + Link<ScrollBar*, void> m_aOrigVScrollHdl; + + DECL_LINK(ChangeHdl, Edit&, void); + DECL_LINK(VscrollHdl, ScrollBar*, void); + DECL_LINK(CursorListener, VclWindowEvent&, void); + +public: + SalInstanceTextView(VclMultiLineEdit* pTextView, SalInstanceBuilder* pBuilder, + bool bTakeOwnership) + : SalInstanceContainer(pTextView, pBuilder, bTakeOwnership) + , m_xTextView(pTextView) + { + m_xTextView->SetModifyHdl(LINK(this, SalInstanceTextView, ChangeHdl)); + ScrollBar& rVertScrollBar = m_xTextView->GetVScrollBar(); + m_aOrigVScrollHdl = rVertScrollBar.GetScrollHdl(); + rVertScrollBar.SetScrollHdl(LINK(this, SalInstanceTextView, VscrollHdl)); + } + + virtual void set_text(const OUString& rText) override + { + disable_notify_events(); + m_xTextView->SetText(rText); + enable_notify_events(); + } + + virtual void replace_selection(const OUString& rText) override + { + disable_notify_events(); + m_xTextView->ReplaceSelected(rText); + enable_notify_events(); + } + + virtual OUString get_text() const override { return m_xTextView->GetText(); } + + bool get_selection_bounds(int& rStartPos, int& rEndPos) override + { + const Selection& rSelection = m_xTextView->GetSelection(); + rStartPos = rSelection.Min(); + rEndPos = rSelection.Max(); + return rSelection.Len(); + } + + virtual void select_region(int nStartPos, int nEndPos) override + { + disable_notify_events(); + m_xTextView->SetSelection(Selection(nStartPos, nEndPos < 0 ? SELECTION_MAX : nEndPos)); + enable_notify_events(); + } + + virtual void set_editable(bool bEditable) override { m_xTextView->SetReadOnly(!bEditable); } + + virtual void set_monospace(bool bMonospace) override + { + vcl::Font aOrigFont = m_xTextView->GetControlFont(); + vcl::Font aFont; + if (bMonospace) + aFont = OutputDevice::GetDefaultFont(DefaultFontType::UI_FIXED, LANGUAGE_DONTKNOW, + GetDefaultFontFlags::OnlyOne, m_xTextView); + else + aFont = Application::GetSettings().GetStyleSettings().GetFieldFont(); + aFont.SetFontHeight(aOrigFont.GetFontHeight()); + m_xTextView->SetFont(aFont); + m_xTextView->SetControlFont(aFont); + } + + virtual void connect_cursor_position(const Link<TextView&, void>& rLink) override + { + assert(!m_aCursorPositionHdl.IsSet()); + m_xTextView->AddEventListener(LINK(this, SalInstanceTextView, CursorListener)); + weld::TextView::connect_cursor_position(rLink); + } + + virtual int vadjustment_get_value() const override + { + ScrollBar& rVertScrollBar = m_xTextView->GetVScrollBar(); + return rVertScrollBar.GetThumbPos(); + } + + virtual void vadjustment_set_value(int value) override + { + ScrollBar& rVertScrollBar = m_xTextView->GetVScrollBar(); + rVertScrollBar.SetThumbPos(value); + m_aOrigVScrollHdl.Call(&rVertScrollBar); + } + + virtual int vadjustment_get_upper() const override + { + ScrollBar& rVertScrollBar = m_xTextView->GetVScrollBar(); + return rVertScrollBar.GetRangeMax(); + } + + virtual int vadjustment_get_lower() const override + { + ScrollBar& rVertScrollBar = m_xTextView->GetVScrollBar(); + return rVertScrollBar.GetRangeMin(); + } + + virtual int vadjustment_get_page_size() const override + { + ScrollBar& rVertScrollBar = m_xTextView->GetVScrollBar(); + return rVertScrollBar.GetVisibleSize(); + } + + virtual ~SalInstanceTextView() override + { + if (!m_xTextView->IsDisposed()) + { + if (m_aCursorPositionHdl.IsSet()) + m_xTextView->RemoveEventListener(LINK(this, SalInstanceTextView, CursorListener)); + m_xTextView->SetModifyHdl(Link<Edit&, void>()); + ScrollBar& rVertScrollBar = m_xTextView->GetVScrollBar(); + rVertScrollBar.SetScrollHdl(m_aOrigVScrollHdl); + } + } +}; + +} + +IMPL_LINK(SalInstanceTextView, VscrollHdl, ScrollBar*, pScrollBar, void) +{ + signal_vadjustment_changed(); + m_aOrigVScrollHdl.Call(pScrollBar); +} + +IMPL_LINK_NOARG(SalInstanceTextView, ChangeHdl, Edit&, void) { signal_changed(); } + +IMPL_LINK(SalInstanceTextView, CursorListener, VclWindowEvent&, rEvent, void) +{ + if (notify_events_disabled()) + return; + if (rEvent.GetId() == VclEventId::EditSelectionChanged + || rEvent.GetId() == VclEventId::EditCaretChanged) + signal_cursor_position(); +} + +namespace +{ +class SalInstanceExpander : public SalInstanceContainer, public virtual weld::Expander +{ +private: + VclPtr<VclExpander> m_xExpander; + + DECL_LINK(ExpandedHdl, VclExpander&, void); + +public: + SalInstanceExpander(VclExpander* pExpander, SalInstanceBuilder* pBuilder, bool bTakeOwnership) + : SalInstanceContainer(pExpander, pBuilder, bTakeOwnership) + , m_xExpander(pExpander) + { + m_xExpander->SetExpandedHdl(LINK(this, SalInstanceExpander, ExpandedHdl)); + } + + virtual bool get_expanded() const override { return m_xExpander->get_expanded(); } + + virtual void set_expanded(bool bExpand) override { m_xExpander->set_expanded(bExpand); } + + virtual ~SalInstanceExpander() override + { + m_xExpander->SetExpandedHdl(Link<VclExpander&, void>()); + } +}; + +} + +IMPL_LINK_NOARG(SalInstanceExpander, ExpandedHdl, VclExpander&, void) { signal_expanded(); } + +namespace +{ +class SalInstanceDrawingArea : public SalInstanceWidget, public virtual weld::DrawingArea +{ +private: + VclPtr<VclDrawingArea> m_xDrawingArea; + + typedef std::pair<vcl::RenderContext&, const tools::Rectangle&> target_and_area; + DECL_LINK(PaintHdl, target_and_area, void); + DECL_LINK(ResizeHdl, const Size&, void); + DECL_LINK(MousePressHdl, const MouseEvent&, bool); + DECL_LINK(MouseMoveHdl, const MouseEvent&, bool); + DECL_LINK(MouseReleaseHdl, const MouseEvent&, bool); + DECL_LINK(KeyPressHdl, const KeyEvent&, bool); + DECL_LINK(KeyReleaseHdl, const KeyEvent&, bool); + DECL_LINK(StyleUpdatedHdl, VclDrawingArea&, void); + DECL_LINK(CommandHdl, const CommandEvent&, bool); + DECL_LINK(QueryTooltipHdl, tools::Rectangle&, OUString); + DECL_LINK(GetSurroundingHdl, OUString&, int); + DECL_LINK(StartDragHdl, VclDrawingArea*, bool); + + // SalInstanceWidget has a generic listener for all these + // events, ignore the ones we have specializations for + // in VclDrawingArea + virtual void HandleEventListener(VclWindowEvent& rEvent) override + { + if (rEvent.GetId() == VclEventId::WindowResize) + return; + SalInstanceWidget::HandleEventListener(rEvent); + } + + virtual void HandleMouseEventListener(VclSimpleEvent& rEvent) override + { + if (rEvent.GetId() == VclEventId::WindowMouseButtonDown + || rEvent.GetId() == VclEventId::WindowMouseButtonUp + || rEvent.GetId() == VclEventId::WindowMouseMove) + { + return; + } + SalInstanceWidget::HandleMouseEventListener(rEvent); + } + + virtual bool HandleKeyEventListener(VclWindowEvent& /*rEvent*/) override { return false; } + +public: + SalInstanceDrawingArea(VclDrawingArea* pDrawingArea, SalInstanceBuilder* pBuilder, + const a11yref& rAlly, FactoryFunction pUITestFactoryFunction, + void* pUserData, bool bTakeOwnership) + : SalInstanceWidget(pDrawingArea, pBuilder, bTakeOwnership) + , m_xDrawingArea(pDrawingArea) + { + m_xDrawingArea->SetAccessible(rAlly); + m_xDrawingArea->SetUITestFactory(std::move(pUITestFactoryFunction), pUserData); + m_xDrawingArea->SetPaintHdl(LINK(this, SalInstanceDrawingArea, PaintHdl)); + m_xDrawingArea->SetResizeHdl(LINK(this, SalInstanceDrawingArea, ResizeHdl)); + m_xDrawingArea->SetMousePressHdl(LINK(this, SalInstanceDrawingArea, MousePressHdl)); + m_xDrawingArea->SetMouseMoveHdl(LINK(this, SalInstanceDrawingArea, MouseMoveHdl)); + m_xDrawingArea->SetMouseReleaseHdl(LINK(this, SalInstanceDrawingArea, MouseReleaseHdl)); + m_xDrawingArea->SetKeyPressHdl(LINK(this, SalInstanceDrawingArea, KeyPressHdl)); + m_xDrawingArea->SetKeyReleaseHdl(LINK(this, SalInstanceDrawingArea, KeyReleaseHdl)); + m_xDrawingArea->SetStyleUpdatedHdl(LINK(this, SalInstanceDrawingArea, StyleUpdatedHdl)); + m_xDrawingArea->SetCommandHdl(LINK(this, SalInstanceDrawingArea, CommandHdl)); + m_xDrawingArea->SetQueryTooltipHdl(LINK(this, SalInstanceDrawingArea, QueryTooltipHdl)); + m_xDrawingArea->SetGetSurroundingHdl(LINK(this, SalInstanceDrawingArea, GetSurroundingHdl)); + m_xDrawingArea->SetStartDragHdl(LINK(this, SalInstanceDrawingArea, StartDragHdl)); + } + + virtual void queue_draw() override { m_xDrawingArea->Invalidate(); } + + virtual void queue_draw_area(int x, int y, int width, int height) override + { + m_xDrawingArea->Invalidate(tools::Rectangle(Point(x, y), Size(width, height))); + } + + virtual void queue_resize() override { m_xDrawingArea->queue_resize(); } + + virtual void connect_size_allocate(const Link<const Size&, void>& rLink) override + { + weld::Widget::connect_size_allocate(rLink); + } + + virtual void connect_key_press(const Link<const KeyEvent&, bool>& rLink) override + { + weld::Widget::connect_key_press(rLink); + } + + virtual void connect_key_release(const Link<const KeyEvent&, bool>& rLink) override + { + weld::Widget::connect_key_release(rLink); + } + + virtual void set_cursor(PointerStyle ePointerStyle) override + { + m_xDrawingArea->SetPointer(ePointerStyle); + } + + virtual void set_input_context(const InputContext& rInputContext) override + { + m_xDrawingArea->SetInputContext(rInputContext); + } + + virtual void im_context_set_cursor_location(const tools::Rectangle& rCursorRect, int nExtTextInputWidth) override + { + tools::Rectangle aCursorRect = m_xDrawingArea->PixelToLogic(rCursorRect); + m_xDrawingArea->SetCursorRect(&aCursorRect, m_xDrawingArea->PixelToLogic(Size(nExtTextInputWidth, 0)).Width()); + } + + virtual a11yref get_accessible_parent() override + { + vcl::Window* pParent = m_xDrawingArea->GetParent(); + if (pParent) + return pParent->GetAccessible(); + return css::uno::Reference<css::accessibility::XAccessible>(); + } + + virtual a11yrelationset get_accessible_relation_set() override + { + utl::AccessibleRelationSetHelper* pRelationSetHelper = new utl::AccessibleRelationSetHelper; + css::uno::Reference<css::accessibility::XAccessibleRelationSet> xSet = pRelationSetHelper; + vcl::Window* pWindow = m_xDrawingArea.get(); + if (pWindow) + { + vcl::Window* pLabeledBy = pWindow->GetAccessibleRelationLabeledBy(); + if (pLabeledBy && pLabeledBy != pWindow) + { + css::uno::Sequence<css::uno::Reference<css::uno::XInterface>> aSequence{ + pLabeledBy->GetAccessible() + }; + pRelationSetHelper->AddRelation(css::accessibility::AccessibleRelation( + css::accessibility::AccessibleRelationType::LABELED_BY, aSequence)); + } + vcl::Window* pMemberOf = pWindow->GetAccessibleRelationMemberOf(); + if (pMemberOf && pMemberOf != pWindow) + { + css::uno::Sequence<css::uno::Reference<css::uno::XInterface>> aSequence{ + pMemberOf->GetAccessible() + }; + pRelationSetHelper->AddRelation(css::accessibility::AccessibleRelation( + css::accessibility::AccessibleRelationType::MEMBER_OF, aSequence)); + } + } + return xSet; + } + + virtual Point get_accessible_location() override + { + return m_xDrawingArea->OutputToAbsoluteScreenPixel(Point()); + } + + virtual void enable_drag_source(rtl::Reference<TransferDataContainer>& rHelper, + sal_uInt8 eDNDConstants) override + { + m_xDrawingArea->SetDragHelper(rHelper, eDNDConstants); + } + + virtual ~SalInstanceDrawingArea() override + { + m_xDrawingArea->SetGetSurroundingHdl(Link<OUString&, int>()); + m_xDrawingArea->SetQueryTooltipHdl(Link<tools::Rectangle&, OUString>()); + m_xDrawingArea->SetCommandHdl(Link<const CommandEvent&, bool>()); + m_xDrawingArea->SetStyleUpdatedHdl(Link<VclDrawingArea&, void>()); + m_xDrawingArea->SetMousePressHdl(Link<const MouseEvent&, bool>()); + m_xDrawingArea->SetMouseMoveHdl(Link<const MouseEvent&, bool>()); + m_xDrawingArea->SetMouseReleaseHdl(Link<const MouseEvent&, bool>()); + m_xDrawingArea->SetKeyPressHdl(Link<const KeyEvent&, bool>()); + m_xDrawingArea->SetKeyReleaseHdl(Link<const KeyEvent&, bool>()); + m_xDrawingArea->SetResizeHdl(Link<const Size&, void>()); + m_xDrawingArea->SetPaintHdl( + Link<std::pair<vcl::RenderContext&, const tools::Rectangle&>, void>()); + } + + virtual OutputDevice& get_ref_device() override { return *m_xDrawingArea; } +}; + +} + +IMPL_LINK(SalInstanceDrawingArea, PaintHdl, target_and_area, aPayload, void) +{ + m_aDrawHdl.Call(aPayload); + tools::Rectangle aFocusRect(m_aGetFocusRectHdl.Call(*this)); + if (!aFocusRect.IsEmpty()) + DrawFocusRect(aPayload.first, aFocusRect); +} + +IMPL_LINK(SalInstanceDrawingArea, ResizeHdl, const Size&, rSize, void) +{ + m_aSizeAllocateHdl.Call(rSize); +} + +IMPL_LINK(SalInstanceDrawingArea, MousePressHdl, const MouseEvent&, rEvent, bool) +{ + return m_aMousePressHdl.Call(rEvent); +} + +IMPL_LINK(SalInstanceDrawingArea, MouseMoveHdl, const MouseEvent&, rEvent, bool) +{ + return m_aMouseMotionHdl.Call(rEvent); +} + +IMPL_LINK(SalInstanceDrawingArea, MouseReleaseHdl, const MouseEvent&, rEvent, bool) +{ + return m_aMouseReleaseHdl.Call(rEvent); +} + +IMPL_LINK(SalInstanceDrawingArea, KeyPressHdl, const KeyEvent&, rEvent, bool) +{ + return m_aKeyPressHdl.Call(rEvent); +} + +IMPL_LINK(SalInstanceDrawingArea, KeyReleaseHdl, const KeyEvent&, rEvent, bool) +{ + return m_aKeyReleaseHdl.Call(rEvent); +} + +IMPL_LINK_NOARG(SalInstanceDrawingArea, StyleUpdatedHdl, VclDrawingArea&, void) +{ + m_aStyleUpdatedHdl.Call(*this); +} + +IMPL_LINK(SalInstanceDrawingArea, CommandHdl, const CommandEvent&, rEvent, bool) +{ + return m_aCommandHdl.Call(rEvent); +} + +IMPL_LINK(SalInstanceDrawingArea, GetSurroundingHdl, OUString&, rSurrounding, int) +{ + return m_aGetSurroundingHdl.Call(rSurrounding); +} + +IMPL_LINK(SalInstanceDrawingArea, QueryTooltipHdl, tools::Rectangle&, rHelpArea, OUString) +{ + return m_aQueryTooltipHdl.Call(rHelpArea); +} + +IMPL_LINK_NOARG(SalInstanceDrawingArea, StartDragHdl, VclDrawingArea*, bool) +{ + if (m_aDragBeginHdl.Call(*this)) + return true; + return false; +} + +SalInstanceComboBoxWithoutEdit::SalInstanceComboBoxWithoutEdit(ListBox* pListBox, SalInstanceBuilder* pBuilder, + bool bTakeOwnership) + : SalInstanceComboBox<ListBox>(pListBox, pBuilder, bTakeOwnership) +{ + m_xComboBox->SetSelectHdl(LINK(this, SalInstanceComboBoxWithoutEdit, SelectHdl)); +} + +OUString SalInstanceComboBoxWithoutEdit::get_active_text() const { return m_xComboBox->GetSelectedEntry(); } + +void SalInstanceComboBoxWithoutEdit::remove(int pos) { m_xComboBox->RemoveEntry(pos); } + +void SalInstanceComboBoxWithoutEdit::insert(int pos, const OUString& rStr, const OUString* pId, + const OUString* pIconName, VirtualDevice* pImageSurface) +{ + auto nInsertPos = pos == -1 ? COMBOBOX_APPEND : pos; + sal_Int32 nInsertedAt; + if (!pIconName && !pImageSurface) + nInsertedAt = m_xComboBox->InsertEntry(rStr, nInsertPos); + else if (pIconName) + nInsertedAt = m_xComboBox->InsertEntry(rStr, createImage(*pIconName), nInsertPos); + else + nInsertedAt = m_xComboBox->InsertEntry(rStr, createImage(*pImageSurface), nInsertPos); + if (pId) + { + m_aUserData.emplace_back(std::make_unique<OUString>(*pId)); + m_xComboBox->SetEntryData(nInsertedAt, m_aUserData.back().get()); + } +} + +void SalInstanceComboBoxWithoutEdit::insert_separator(int pos, const OUString& /*rId*/) +{ + auto nInsertPos = pos == -1 ? m_xComboBox->GetEntryCount() : pos; + m_xComboBox->AddSeparator(nInsertPos - 1); +} + +bool SalInstanceComboBoxWithoutEdit::has_entry() const { return false; } + +bool SalInstanceComboBoxWithoutEdit::changed_by_direct_pick() const { return true; } + +void SalInstanceComboBoxWithoutEdit::set_entry_message_type(weld::EntryMessageType /*eType*/) +{ + assert(false); +} + +void SalInstanceComboBoxWithoutEdit::set_entry_text(const OUString& /*rText*/) { assert(false); } + +void SalInstanceComboBoxWithoutEdit::select_entry_region(int /*nStartPos*/, int /*nEndPos*/) { assert(false); } + +bool SalInstanceComboBoxWithoutEdit::get_entry_selection_bounds(int& /*rStartPos*/, int& /*rEndPos*/) +{ + assert(false); + return false; +} + +void SalInstanceComboBoxWithoutEdit::set_entry_width_chars(int /*nChars*/) { assert(false); } + +void SalInstanceComboBoxWithoutEdit::set_entry_max_length(int /*nChars*/) { assert(false); } + +void SalInstanceComboBoxWithoutEdit::set_entry_completion(bool, bool) { assert(false); } + +void SalInstanceComboBoxWithoutEdit::set_entry_placeholder_text(const OUString&) { assert(false); } + +void SalInstanceComboBoxWithoutEdit::set_entry_editable(bool /*bEditable*/) { assert(false); } + +void SalInstanceComboBoxWithoutEdit::cut_entry_clipboard() { assert(false); } + +void SalInstanceComboBoxWithoutEdit::copy_entry_clipboard() { assert(false); } + +void SalInstanceComboBoxWithoutEdit::paste_entry_clipboard() { assert(false); } + +void SalInstanceComboBoxWithoutEdit::set_entry_font(const vcl::Font&) { assert(false); } + +vcl::Font SalInstanceComboBoxWithoutEdit::get_entry_font() { assert(false); return vcl::Font(); } + +void SalInstanceComboBoxWithoutEdit::set_custom_renderer(bool /*bOn*/) +{ + assert(false && "not implemented"); +} + +int SalInstanceComboBoxWithoutEdit::get_max_mru_count() const +{ + assert(false && "not implemented"); + return 0; +} + +void SalInstanceComboBoxWithoutEdit::set_max_mru_count(int) +{ + assert(false && "not implemented"); +} + +OUString SalInstanceComboBoxWithoutEdit::get_mru_entries() const +{ + assert(false && "not implemented"); + return OUString(); +} + +void SalInstanceComboBoxWithoutEdit::set_mru_entries(const OUString&) +{ + assert(false && "not implemented"); +} + +void SalInstanceComboBoxWithoutEdit::HandleEventListener(VclWindowEvent& rEvent) +{ + CallHandleEventListener(rEvent); +} + +SalInstanceComboBoxWithoutEdit::~SalInstanceComboBoxWithoutEdit() +{ + m_xComboBox->SetSelectHdl(Link<ListBox&, void>()); +} + +IMPL_LINK_NOARG(SalInstanceComboBoxWithoutEdit, SelectHdl, ListBox&, void) +{ + return signal_changed(); +} + +SalInstanceComboBoxWithEdit::SalInstanceComboBoxWithEdit(::ComboBox* pComboBox, SalInstanceBuilder* pBuilder, + bool bTakeOwnership) + : SalInstanceComboBox<::ComboBox>(pComboBox, pBuilder, bTakeOwnership) + , m_aTextFilter(m_aEntryInsertTextHdl) + , m_bInSelect(false) +{ + m_xComboBox->SetModifyHdl(LINK(this, SalInstanceComboBoxWithEdit, ChangeHdl)); + m_xComboBox->SetSelectHdl(LINK(this, SalInstanceComboBoxWithEdit, SelectHdl)); + m_xComboBox->SetEntryActivateHdl(LINK(this, SalInstanceComboBoxWithEdit, EntryActivateHdl)); + m_xComboBox->SetTextFilter(&m_aTextFilter); +} + +bool SalInstanceComboBoxWithEdit::has_entry() const { return true; } + +bool SalInstanceComboBoxWithEdit::changed_by_direct_pick() const +{ + return m_bInSelect && !m_xComboBox->IsModifyByKeyboard() && !m_xComboBox->IsTravelSelect(); +} + +void SalInstanceComboBoxWithEdit::set_entry_message_type(weld::EntryMessageType eType) +{ + if (eType == weld::EntryMessageType::Error) + m_xComboBox->SetControlForeground(Color(0xf0, 0, 0)); + else if (eType == weld::EntryMessageType::Warning) + m_xComboBox->SetControlForeground(COL_YELLOW); + else + m_xComboBox->SetControlForeground(); +} + +OUString SalInstanceComboBoxWithEdit::get_active_text() const { return m_xComboBox->GetText(); } + +void SalInstanceComboBoxWithEdit::remove(int pos) { m_xComboBox->RemoveEntryAt(pos); } + +void SalInstanceComboBoxWithEdit::insert(int pos, const OUString& rStr, const OUString* pId, + const OUString* pIconName, VirtualDevice* pImageSurface) +{ + auto nInsertPos = pos == -1 ? COMBOBOX_APPEND : pos; + sal_Int32 nInsertedAt; + if (!pIconName && !pImageSurface) + nInsertedAt = m_xComboBox->InsertEntry(rStr, nInsertPos); + else if (pIconName) + nInsertedAt + = m_xComboBox->InsertEntryWithImage(rStr, createImage(*pIconName), nInsertPos); + else + nInsertedAt + = m_xComboBox->InsertEntryWithImage(rStr, createImage(*pImageSurface), nInsertPos); + if (pId) + { + m_aUserData.emplace_back(std::make_unique<OUString>(*pId)); + m_xComboBox->SetEntryData(nInsertedAt, m_aUserData.back().get()); + } +} + +void SalInstanceComboBoxWithEdit::insert_separator(int pos, const OUString& /*rId*/) +{ + auto nInsertPos = pos == -1 ? m_xComboBox->GetEntryCount() : pos; + m_xComboBox->AddSeparator(nInsertPos - 1); +} + +void SalInstanceComboBoxWithEdit::set_entry_text(const OUString& rText) { m_xComboBox->SetText(rText); } + +void SalInstanceComboBoxWithEdit::set_entry_width_chars(int nChars) +{ + m_xComboBox->SetWidthInChars(nChars); +} + +void SalInstanceComboBoxWithEdit::set_entry_max_length(int nChars) { m_xComboBox->SetMaxTextLen(nChars); } + +void SalInstanceComboBoxWithEdit::set_entry_completion(bool bEnable, bool bCaseSensitive) +{ + m_xComboBox->EnableAutocomplete(bEnable, bCaseSensitive); +} + +void SalInstanceComboBoxWithEdit::set_entry_placeholder_text(const OUString& rText) +{ + m_xComboBox->SetPlaceholderText(rText); +} + +void SalInstanceComboBoxWithEdit::set_entry_editable(bool bEditable) +{ + m_xComboBox->SetReadOnly(!bEditable); +} + +void SalInstanceComboBoxWithEdit::cut_entry_clipboard() +{ + m_xComboBox->Cut(); +} + +void SalInstanceComboBoxWithEdit::copy_entry_clipboard() +{ + m_xComboBox->Copy(); +} + +void SalInstanceComboBoxWithEdit::paste_entry_clipboard() +{ + m_xComboBox->Paste(); +} + +void SalInstanceComboBoxWithEdit::select_entry_region(int nStartPos, int nEndPos) +{ + m_xComboBox->SetSelection(Selection(nStartPos, nEndPos < 0 ? SELECTION_MAX : nEndPos)); +} + +bool SalInstanceComboBoxWithEdit::get_entry_selection_bounds(int& rStartPos, int& rEndPos) +{ + const Selection& rSelection = m_xComboBox->GetSelection(); + rStartPos = rSelection.Min(); + rEndPos = rSelection.Max(); + return rSelection.Len(); +} + +void SalInstanceComboBoxWithEdit::set_entry_font(const vcl::Font& rFont) +{ + Edit* pEdit = m_xComboBox->GetSubEdit(); + assert(pEdit); + pEdit->SetPointFont(*pEdit, rFont); + m_xComboBox->SetControlFont(rFont); // tdf#134601 set it as control font to take effect properly + pEdit->Invalidate(); +} + +vcl::Font SalInstanceComboBoxWithEdit::get_entry_font() +{ + Edit* pEdit = m_xComboBox->GetSubEdit(); + assert(pEdit); + return pEdit->GetPointFont(*pEdit); +} + +void SalInstanceComboBoxWithEdit::set_custom_renderer(bool bOn) +{ + if (m_xComboBox->IsUserDrawEnabled() == bOn) + return; + + auto nOldEntryHeight = m_xComboBox->GetDropDownEntryHeight(); + auto nDropDownLineCount = m_xComboBox->GetDropDownLineCount(); + + m_xComboBox->EnableUserDraw(bOn); + if (bOn) + m_xComboBox->SetUserDrawHdl(LINK(this, SalInstanceComboBoxWithEdit, UserDrawHdl)); + else + m_xComboBox->SetUserDrawHdl(Link<UserDrawEvent*, void>()); + + // adjust the line count to fit approx the height it would have been before + // changing the renderer + auto nNewEntryHeight = m_xComboBox->GetDropDownEntryHeight(); + double fRatio = nOldEntryHeight / static_cast<double>(nNewEntryHeight); + m_xComboBox->SetDropDownLineCount(nDropDownLineCount * fRatio); +} + +int SalInstanceComboBoxWithEdit::get_max_mru_count() const +{ + return m_xComboBox->GetMaxMRUCount(); +} + +void SalInstanceComboBoxWithEdit::set_max_mru_count(int nCount) +{ + return m_xComboBox->SetMaxMRUCount(nCount); +} + +OUString SalInstanceComboBoxWithEdit::get_mru_entries() const +{ + return m_xComboBox->GetMRUEntries(); +} + +void SalInstanceComboBoxWithEdit::set_mru_entries(const OUString& rEntries) +{ + m_xComboBox->SetMRUEntries(rEntries); +} + +void SalInstanceComboBoxWithEdit::HandleEventListener(VclWindowEvent& rEvent) +{ + if (rEvent.GetId() == VclEventId::DropdownPreOpen) + { + Size aRowSize(signal_custom_get_size(*m_xComboBox)); + m_xComboBox->SetUserItemSize(aRowSize); + } + CallHandleEventListener(rEvent); +} + +SalInstanceComboBoxWithEdit::~SalInstanceComboBoxWithEdit() +{ + m_xComboBox->SetTextFilter(nullptr); + m_xComboBox->SetEntryActivateHdl(Link<Edit&, bool>()); + m_xComboBox->SetModifyHdl(Link<Edit&, void>()); + m_xComboBox->SetSelectHdl(Link<::ComboBox&, void>()); +} + +IMPL_LINK_NOARG(SalInstanceComboBoxWithEdit, ChangeHdl, Edit&, void) +{ + if (!m_xComboBox->IsSyntheticModify()) // SelectHdl will be called + signal_changed(); +} + +IMPL_LINK_NOARG(SalInstanceComboBoxWithEdit, SelectHdl, ::ComboBox&, void) +{ + m_bInSelect = true; + signal_changed(); + m_bInSelect = false; +} + +IMPL_LINK_NOARG(SalInstanceComboBoxWithEdit, EntryActivateHdl, Edit&, bool) +{ + return m_aEntryActivateHdl.Call(*this); +} + +IMPL_LINK(SalInstanceComboBoxWithEdit, UserDrawHdl, UserDrawEvent*, pEvent, void) +{ + call_signal_custom_render(pEvent); +} + +class SalInstanceEntryTreeView : public SalInstanceContainer, public virtual weld::EntryTreeView +{ +private: + DECL_LINK(AutocompleteHdl, Edit&, void); + DECL_LINK(KeyPressListener, VclWindowEvent&, void); + SalInstanceEntry* m_pEntry; + SalInstanceTreeView* m_pTreeView; + bool m_bTreeChange; + +public: + SalInstanceEntryTreeView(vcl::Window* pContainer, SalInstanceBuilder* pBuilder, + bool bTakeOwnership, std::unique_ptr<weld::Entry> xEntry, + std::unique_ptr<weld::TreeView> xTreeView) + : EntryTreeView(std::move(xEntry), std::move(xTreeView)) + , SalInstanceContainer(pContainer, pBuilder, bTakeOwnership) + , m_pEntry(dynamic_cast<SalInstanceEntry*>(m_xEntry.get())) + , m_pTreeView(dynamic_cast<SalInstanceTreeView*>(m_xTreeView.get())) + , m_bTreeChange(false) + { + assert(m_pEntry && m_pTreeView); + + Edit& rEntry = m_pEntry->getEntry(); + rEntry.SetAutocompleteHdl(LINK(this, SalInstanceEntryTreeView, AutocompleteHdl)); + rEntry.AddEventListener(LINK(this, SalInstanceEntryTreeView, KeyPressListener)); + } + + virtual void insert_separator(int /*pos*/, const OUString& /*rId*/) override { assert(false); } + + virtual void make_sorted() override + { + vcl::Window* pTreeView = m_pTreeView->getWidget(); + pTreeView->SetStyle(pTreeView->GetStyle() | WB_SORT); + } + + virtual void set_entry_completion(bool bEnable, bool /*bCaseSensitive*/) override + { + assert(!bEnable && "not implemented yet"); + (void)bEnable; + Edit& rEntry = m_pEntry->getEntry(); + rEntry.SetAutocompleteHdl(Link<Edit&, void>()); + } + + virtual void set_entry_font(const vcl::Font& rFont) override + { + Edit& rEntry = m_pEntry->getEntry(); + rEntry.SetPointFont(rEntry, rFont); + rEntry.Invalidate(); + } + + virtual vcl::Font get_entry_font() override + { + Edit& rEntry = m_pEntry->getEntry(); + return rEntry.GetPointFont(rEntry); + } + + virtual void set_entry_placeholder_text(const OUString& rText) override + { + Edit& rEntry = m_pEntry->getEntry(); + rEntry.SetPlaceholderText(rText); + } + + virtual void set_entry_editable(bool bEditable) override + { + Edit& rEntry = m_pEntry->getEntry(); + rEntry.SetReadOnly(!bEditable); + } + + virtual void cut_entry_clipboard() override + { + Edit& rEntry = m_pEntry->getEntry(); + rEntry.Cut(); + } + + virtual void copy_entry_clipboard() override + { + Edit& rEntry = m_pEntry->getEntry(); + rEntry.Copy(); + } + + virtual void paste_entry_clipboard() override + { + Edit& rEntry = m_pEntry->getEntry(); + rEntry.Paste(); + } + + virtual void grab_focus() override { m_xEntry->grab_focus(); } + + virtual void connect_focus_in(const Link<Widget&, void>& rLink) override + { + m_xEntry->connect_focus_in(rLink); + } + + virtual void connect_focus_out(const Link<Widget&, void>& rLink) override + { + m_xEntry->connect_focus_out(rLink); + } + + virtual bool changed_by_direct_pick() const override { return m_bTreeChange; } + + virtual void set_custom_renderer(bool /*bOn*/) override + { + assert(false && "not implemented"); + } + + virtual int get_max_mru_count() const override + { + assert(false && "not implemented"); + return 0; + } + + virtual void set_max_mru_count(int) override + { + assert(false && "not implemented"); + } + + virtual OUString get_mru_entries() const override + { + assert(false && "not implemented"); + return OUString(); + } + + virtual void set_mru_entries(const OUString&) override + { + assert(false && "not implemented"); + } + + virtual void set_item_menu(const OString&, weld::Menu*) override + { + assert(false && "not implemented"); + } + + int get_menu_button_width() const override + { + assert(false && "not implemented"); + return 0; + } + + VclPtr<VirtualDevice> create_render_virtual_device() const override + { + return VclPtr<VirtualDevice>::Create(); + } + + virtual ~SalInstanceEntryTreeView() override + { + Edit& rEntry = m_pEntry->getEntry(); + rEntry.RemoveEventListener(LINK(this, SalInstanceEntryTreeView, KeyPressListener)); + rEntry.SetAutocompleteHdl(Link<Edit&, void>()); + } +}; + +IMPL_LINK(SalInstanceEntryTreeView, KeyPressListener, VclWindowEvent&, rEvent, void) +{ + if (rEvent.GetId() != VclEventId::WindowKeyInput) + return; + const KeyEvent& rKeyEvent = *static_cast<KeyEvent*>(rEvent.GetData()); + sal_uInt16 nKeyCode = rKeyEvent.GetKeyCode().GetCode(); + if (nKeyCode == KEY_UP || nKeyCode == KEY_DOWN || nKeyCode == KEY_PAGEUP + || nKeyCode == KEY_PAGEDOWN) + { + m_pTreeView->disable_notify_events(); + auto& rListBox = m_pTreeView->getTreeView(); + if (!rListBox.FirstSelected()) + { + if (SvTreeListEntry* pEntry = rListBox.First()) + rListBox.Select(pEntry, true); + } + else + rListBox.KeyInput(rKeyEvent); + m_xEntry->set_text(m_xTreeView->get_selected_text()); + m_xEntry->select_region(0, -1); + m_pTreeView->enable_notify_events(); + m_bTreeChange = true; + m_pEntry->fire_signal_changed(); + m_bTreeChange = false; + } +} + +IMPL_LINK(SalInstanceEntryTreeView, AutocompleteHdl, Edit&, rEdit, void) +{ + Selection aSel = rEdit.GetSelection(); + + OUString aFullText = rEdit.GetText(); + OUString aStartText = aFullText.copy(0, static_cast<sal_Int32>(aSel.Max())); + + int nPos = -1; + int nCount = m_xTreeView->n_children(); + for (int i = 0; i < nCount; ++i) + { + if (m_xTreeView->get_text(i).startsWithIgnoreAsciiCase(aStartText)) + { + nPos = i; + break; + } + } + + m_xTreeView->select(nPos); + + if (nPos != -1) + { + OUString aText = m_xTreeView->get_text(nPos); + Selection aSelection(aText.getLength(), aStartText.getLength()); + rEdit.SetText(aText, aSelection); + } +} + +SalInstanceBuilder::SalInstanceBuilder(vcl::Window* pParent, const OUString& rUIRoot, + const OUString& rUIFile) + : weld::Builder() + , m_xBuilder(new VclBuilder(pParent, rUIRoot, rUIFile, OString(), + css::uno::Reference<css::frame::XFrame>(), false)) +{ +} + +std::unique_ptr<weld::MessageDialog> SalInstanceBuilder::weld_message_dialog(const OString& id, + bool bTakeOwnership) +{ + MessageDialog* pMessageDialog = m_xBuilder->get<MessageDialog>(id); + std::unique_ptr<weld::MessageDialog> pRet( + pMessageDialog ? new SalInstanceMessageDialog(pMessageDialog, this, false) : nullptr); + if (bTakeOwnership && pMessageDialog) + { + assert(!m_aOwnedToplevel && "only one toplevel per .ui allowed"); + m_aOwnedToplevel.set(pMessageDialog); + m_xBuilder->drop_ownership(pMessageDialog); + } + return pRet; +} + +std::unique_ptr<weld::Dialog> SalInstanceBuilder::weld_dialog(const OString& id, + bool bTakeOwnership) +{ + Dialog* pDialog = m_xBuilder->get<Dialog>(id); + std::unique_ptr<weld::Dialog> pRet(pDialog ? new SalInstanceDialog(pDialog, this, false) + : nullptr); + if (bTakeOwnership && pDialog) + { + assert(!m_aOwnedToplevel && "only one toplevel per .ui allowed"); + m_aOwnedToplevel.set(pDialog); + m_xBuilder->drop_ownership(pDialog); + } + return pRet; +} + +std::unique_ptr<weld::Assistant> SalInstanceBuilder::weld_assistant(const OString& id, + bool bTakeOwnership) +{ + vcl::RoadmapWizard* pDialog = m_xBuilder->get<vcl::RoadmapWizard>(id); + std::unique_ptr<weld::Assistant> pRet(pDialog ? new SalInstanceAssistant(pDialog, this, false) + : nullptr); + if (bTakeOwnership && pDialog) + { + assert(!m_aOwnedToplevel && "only one toplevel per .ui allowed"); + m_aOwnedToplevel.set(pDialog); + m_xBuilder->drop_ownership(pDialog); + } + return pRet; +} + +std::unique_ptr<weld::Window> SalInstanceBuilder::create_screenshot_window() +{ + assert(!m_aOwnedToplevel && "only one toplevel per .ui allowed"); + + vcl::Window* pRoot = m_xBuilder->get_widget_root(); + if (SystemWindow* pWindow = dynamic_cast<SystemWindow*>(pRoot)) + { + std::unique_ptr<weld::Window> xRet(new SalInstanceWindow(pWindow, this, false)); + m_aOwnedToplevel.set(pWindow); + m_xBuilder->drop_ownership(pWindow); + return xRet; + } + + VclPtrInstance<Dialog> xDialog(nullptr, WB_HIDE | WB_STDDIALOG | WB_SIZEABLE | WB_CLOSEABLE, + Dialog::InitFlag::NoParent); + xDialog->SetText(utl::ConfigManager::getProductName()); + + auto xContentArea = VclPtr<VclVBox>::Create(xDialog, false, 12); + pRoot->SetParent(xContentArea); + assert(pRoot == xContentArea->GetWindow(GetWindowType::FirstChild)); + xContentArea->Show(); + pRoot->Show(); + xDialog->SetHelpId(pRoot->GetHelpId()); + + m_aOwnedToplevel.set(xDialog); + + return std::unique_ptr<weld::Dialog>(new SalInstanceDialog(xDialog, this, false)); +} + +std::unique_ptr<weld::Window> SalInstanceBuilder::weld_window(const OString& id, + bool bTakeOwnership) +{ + SystemWindow* pWindow = m_xBuilder->get<SystemWindow>(id); + return pWindow ? std::make_unique<SalInstanceWindow>(pWindow, this, bTakeOwnership) : nullptr; +} + +std::unique_ptr<weld::Widget> SalInstanceBuilder::weld_widget(const OString& id, + bool bTakeOwnership) +{ + vcl::Window* pWidget = m_xBuilder->get(id); + return pWidget ? std::make_unique<SalInstanceWidget>(pWidget, this, bTakeOwnership) : nullptr; +} + +std::unique_ptr<weld::Container> SalInstanceBuilder::weld_container(const OString& id, + bool bTakeOwnership) +{ + vcl::Window* pContainer = m_xBuilder->get(id); + return pContainer ? std::make_unique<SalInstanceContainer>(pContainer, this, bTakeOwnership) + : nullptr; +} + +std::unique_ptr<weld::Box> SalInstanceBuilder::weld_box(const OString& id, bool bTakeOwnership) +{ + vcl::Window* pContainer = m_xBuilder->get(id); + return pContainer ? std::make_unique<SalInstanceBox>(pContainer, this, bTakeOwnership) + : nullptr; +} + +std::unique_ptr<weld::Paned> SalInstanceBuilder::weld_paned(const OString& id, + bool bTakeOwnership) +{ + VclPaned* pPaned = m_xBuilder->get<VclPaned>(id); + return pPaned ? std::make_unique<SalInstancePaned>(pPaned, this, bTakeOwnership) + : nullptr; +} + +std::unique_ptr<weld::Frame> SalInstanceBuilder::weld_frame(const OString& id, bool bTakeOwnership) +{ + VclFrame* pFrame = m_xBuilder->get<VclFrame>(id); + std::unique_ptr<weld::Frame> pRet(pFrame ? new SalInstanceFrame(pFrame, this, false) : nullptr); + if (bTakeOwnership && pFrame) + { + assert(!m_aOwnedToplevel && "only one toplevel per .ui allowed"); + m_aOwnedToplevel.set(pFrame); + m_xBuilder->drop_ownership(pFrame); + } + return pRet; +} + +std::unique_ptr<weld::ScrolledWindow> SalInstanceBuilder::weld_scrolled_window(const OString& id, + bool bTakeOwnership) +{ + VclScrolledWindow* pScrolledWindow = m_xBuilder->get<VclScrolledWindow>(id); + return pScrolledWindow + ? std::make_unique<SalInstanceScrolledWindow>(pScrolledWindow, this, bTakeOwnership) + : nullptr; +} + +std::unique_ptr<weld::Notebook> SalInstanceBuilder::weld_notebook(const OString& id, + bool bTakeOwnership) +{ + vcl::Window* pNotebook = m_xBuilder->get(id); + if (!pNotebook) + return nullptr; + if (pNotebook->GetType() == WindowType::TABCONTROL) + return std::make_unique<SalInstanceNotebook>(static_cast<TabControl*>(pNotebook), this, + bTakeOwnership); + if (pNotebook->GetType() == WindowType::VERTICALTABCONTROL) + return std::make_unique<SalInstanceVerticalNotebook>( + static_cast<VerticalTabControl*>(pNotebook), this, bTakeOwnership); + return nullptr; +} + +std::unique_ptr<weld::Button> SalInstanceBuilder::weld_button(const OString& id, + bool bTakeOwnership) +{ + Button* pButton = m_xBuilder->get<Button>(id); + return pButton ? std::make_unique<SalInstanceButton>(pButton, this, bTakeOwnership) : nullptr; +} + +std::unique_ptr<weld::MenuButton> SalInstanceBuilder::weld_menu_button(const OString& id, + bool bTakeOwnership) +{ + MenuButton* pButton = m_xBuilder->get<MenuButton>(id); + return pButton ? std::make_unique<SalInstanceMenuButton>(pButton, this, bTakeOwnership) + : nullptr; +} + +std::unique_ptr<weld::LinkButton> SalInstanceBuilder::weld_link_button(const OString& id, + bool bTakeOwnership) +{ + FixedHyperlink* pButton = m_xBuilder->get<FixedHyperlink>(id); + return pButton ? std::make_unique<SalInstanceLinkButton>(pButton, this, bTakeOwnership) + : nullptr; +} + +std::unique_ptr<weld::ToggleButton> SalInstanceBuilder::weld_toggle_button(const OString& id, + bool bTakeOwnership) +{ + PushButton* pToggleButton = m_xBuilder->get<PushButton>(id); + return pToggleButton + ? std::make_unique<SalInstanceToggleButton>(pToggleButton, this, bTakeOwnership) + : nullptr; +} + +std::unique_ptr<weld::RadioButton> SalInstanceBuilder::weld_radio_button(const OString& id, + bool bTakeOwnership) +{ + RadioButton* pRadioButton = m_xBuilder->get<RadioButton>(id); + return pRadioButton + ? std::make_unique<SalInstanceRadioButton>(pRadioButton, this, bTakeOwnership) + : nullptr; +} + +std::unique_ptr<weld::CheckButton> SalInstanceBuilder::weld_check_button(const OString& id, + bool bTakeOwnership) +{ + CheckBox* pCheckButton = m_xBuilder->get<CheckBox>(id); + return pCheckButton + ? std::make_unique<SalInstanceCheckButton>(pCheckButton, this, bTakeOwnership) + : nullptr; +} + +std::unique_ptr<weld::Scale> SalInstanceBuilder::weld_scale(const OString& id, bool bTakeOwnership) +{ + Slider* pSlider = m_xBuilder->get<Slider>(id); + return pSlider ? std::make_unique<SalInstanceScale>(pSlider, this, bTakeOwnership) : nullptr; +} + +std::unique_ptr<weld::ProgressBar> SalInstanceBuilder::weld_progress_bar(const OString& id, + bool bTakeOwnership) +{ + ::ProgressBar* pProgress = m_xBuilder->get<::ProgressBar>(id); + return pProgress ? std::make_unique<SalInstanceProgressBar>(pProgress, this, bTakeOwnership) + : nullptr; +} + +std::unique_ptr<weld::Spinner> SalInstanceBuilder::weld_spinner(const OString& id, + bool bTakeOwnership) +{ + Throbber* pThrobber = m_xBuilder->get<Throbber>(id); + return pThrobber ? std::make_unique<SalInstanceSpinner>(pThrobber, this, bTakeOwnership) + : nullptr; +} + +std::unique_ptr<weld::Image> SalInstanceBuilder::weld_image(const OString& id, bool bTakeOwnership) +{ + FixedImage* pImage = m_xBuilder->get<FixedImage>(id); + return pImage ? std::make_unique<SalInstanceImage>(pImage, this, bTakeOwnership) : nullptr; +} + +std::unique_ptr<weld::Calendar> SalInstanceBuilder::weld_calendar(const OString& id, + bool bTakeOwnership) +{ + Calendar* pCalendar = m_xBuilder->get<Calendar>(id); + return pCalendar ? std::make_unique<SalInstanceCalendar>(pCalendar, this, bTakeOwnership) + : nullptr; +} + +std::unique_ptr<weld::Entry> SalInstanceBuilder::weld_entry(const OString& id, bool bTakeOwnership) +{ + Edit* pEntry = m_xBuilder->get<Edit>(id); + return pEntry ? std::make_unique<SalInstanceEntry>(pEntry, this, bTakeOwnership) : nullptr; +} + +std::unique_ptr<weld::SpinButton> SalInstanceBuilder::weld_spin_button(const OString& id, + bool bTakeOwnership) +{ + FormattedField* pSpinButton = m_xBuilder->get<FormattedField>(id); + return pSpinButton ? std::make_unique<SalInstanceSpinButton>(pSpinButton, this, bTakeOwnership) + : nullptr; +} + +std::unique_ptr<weld::MetricSpinButton> +SalInstanceBuilder::weld_metric_spin_button(const OString& id, FieldUnit eUnit, bool bTakeOwnership) +{ + std::unique_ptr<weld::SpinButton> xButton(weld_spin_button(id, bTakeOwnership)); + if (xButton) + { + SalInstanceSpinButton& rButton = dynamic_cast<SalInstanceSpinButton&>(*xButton); + rButton.SetUseThousandSep(); + } + return std::make_unique<weld::MetricSpinButton>(std::move(xButton), eUnit); +} + +std::unique_ptr<weld::FormattedSpinButton> +SalInstanceBuilder::weld_formatted_spin_button(const OString& id, bool bTakeOwnership) +{ + FormattedField* pSpinButton = m_xBuilder->get<FormattedField>(id); + return pSpinButton + ? std::make_unique<SalInstanceFormattedSpinButton>(pSpinButton, this, bTakeOwnership) + : nullptr; +} + +std::unique_ptr<weld::TimeSpinButton> +SalInstanceBuilder::weld_time_spin_button(const OString& id, TimeFieldFormat eFormat, + bool bTakeOwnership) +{ + std::unique_ptr<weld::TimeSpinButton> pRet( + new weld::TimeSpinButton(weld_spin_button(id, bTakeOwnership), eFormat)); + SalInstanceSpinButton& rButton = dynamic_cast<SalInstanceSpinButton&>(pRet->get_widget()); + rButton.DisableRemainderFactor(); //so with hh::mm::ss, incrementing mm will not reset ss + return pRet; +} + +std::unique_ptr<weld::ComboBox> SalInstanceBuilder::weld_combo_box(const OString& id, + bool bTakeOwnership) +{ + vcl::Window* pWidget = m_xBuilder->get(id); + ::ComboBox* pComboBox = dynamic_cast<::ComboBox*>(pWidget); + if (pComboBox) + return std::make_unique<SalInstanceComboBoxWithEdit>(pComboBox, this, bTakeOwnership); + ListBox* pListBox = dynamic_cast<ListBox*>(pWidget); + return pListBox + ? std::make_unique<SalInstanceComboBoxWithoutEdit>(pListBox, this, bTakeOwnership) + : nullptr; +} + +std::unique_ptr<weld::EntryTreeView> +SalInstanceBuilder::weld_entry_tree_view(const OString& containerid, const OString& entryid, + const OString& treeviewid, bool bTakeOwnership) +{ + vcl::Window* pContainer = m_xBuilder->get(containerid); + return pContainer ? std::make_unique<SalInstanceEntryTreeView>( + pContainer, this, bTakeOwnership, weld_entry(entryid, bTakeOwnership), + weld_tree_view(treeviewid, bTakeOwnership)) + : nullptr; +} + +std::unique_ptr<weld::TreeView> SalInstanceBuilder::weld_tree_view(const OString& id, + bool bTakeOwnership) +{ + SvTabListBox* pTreeView = m_xBuilder->get<SvTabListBox>(id); + return pTreeView ? std::make_unique<SalInstanceTreeView>(pTreeView, this, bTakeOwnership) + : nullptr; +} + +std::unique_ptr<weld::IconView> SalInstanceBuilder::weld_icon_view(const OString& id, + bool bTakeOwnership) +{ + IconView* pIconView = m_xBuilder->get<IconView>(id); + return pIconView ? std::make_unique<SalInstanceIconView>(pIconView, this, bTakeOwnership) + : nullptr; +} + +std::unique_ptr<weld::Label> SalInstanceBuilder::weld_label(const OString& id, bool bTakeOwnership) +{ + Control* pLabel = m_xBuilder->get<Control>(id); + return pLabel ? std::make_unique<SalInstanceLabel>(pLabel, this, bTakeOwnership) : nullptr; +} + +std::unique_ptr<weld::TextView> SalInstanceBuilder::weld_text_view(const OString& id, + bool bTakeOwnership) +{ + VclMultiLineEdit* pTextView = m_xBuilder->get<VclMultiLineEdit>(id); + return pTextView ? std::make_unique<SalInstanceTextView>(pTextView, this, bTakeOwnership) + : nullptr; +} + +std::unique_ptr<weld::Expander> SalInstanceBuilder::weld_expander(const OString& id, + bool bTakeOwnership) +{ + VclExpander* pExpander = m_xBuilder->get<VclExpander>(id); + return pExpander ? std::make_unique<SalInstanceExpander>(pExpander, this, bTakeOwnership) + : nullptr; +} + +std::unique_ptr<weld::DrawingArea> +SalInstanceBuilder::weld_drawing_area(const OString& id, const a11yref& rA11yImpl, + FactoryFunction pUITestFactoryFunction, void* pUserData, + bool bTakeOwnership) +{ + VclDrawingArea* pDrawingArea = m_xBuilder->get<VclDrawingArea>(id); + return pDrawingArea ? std::make_unique<SalInstanceDrawingArea>(pDrawingArea, this, rA11yImpl, + pUITestFactoryFunction, + pUserData, bTakeOwnership) + : nullptr; +} + +std::unique_ptr<weld::Menu> SalInstanceBuilder::weld_menu(const OString& id, bool bTakeOwnership) +{ + PopupMenu* pMenu = m_xBuilder->get_menu(id); + return pMenu ? std::make_unique<SalInstanceMenu>(pMenu, bTakeOwnership) : nullptr; +} + +std::unique_ptr<weld::Toolbar> SalInstanceBuilder::weld_toolbar(const OString& id, + bool bTakeOwnership) +{ + ToolBox* pToolBox = m_xBuilder->get<ToolBox>(id); + return pToolBox ? std::make_unique<SalInstanceToolbar>(pToolBox, this, bTakeOwnership) + : nullptr; +} + +std::unique_ptr<weld::SizeGroup> SalInstanceBuilder::create_size_group() +{ + return std::make_unique<SalInstanceSizeGroup>(); +} + +OString SalInstanceBuilder::get_current_page_help_id() const +{ + TabControl* pCtrl = m_xBuilder->get<TabControl>("tabcontrol"); + TabPage* pTabPage = pCtrl ? pCtrl->GetTabPage(pCtrl->GetCurPageId()) : nullptr; + vcl::Window* pTabChild = pTabPage ? pTabPage->GetWindow(GetWindowType::FirstChild) : nullptr; + pTabChild = pTabChild ? pTabChild->GetWindow(GetWindowType::FirstChild) : nullptr; + if (pTabChild) + return pTabChild->GetHelpId(); + return OString(); +} + +SalInstanceBuilder::~SalInstanceBuilder() +{ + if (VclBuilderContainer* pOwnedToplevel + = dynamic_cast<VclBuilderContainer*>(m_aOwnedToplevel.get())) + pOwnedToplevel->m_pUIBuilder = std::move(m_xBuilder); + else + m_xBuilder.reset(); + m_aOwnedToplevel.disposeAndClear(); +} + +weld::Builder* SalInstance::CreateBuilder(weld::Widget* pParent, const OUString& rUIRoot, + const OUString& rUIFile) +{ + SalInstanceWidget* pParentInstance = dynamic_cast<SalInstanceWidget*>(pParent); + vcl::Window* pParentWidget = pParentInstance ? pParentInstance->getWidget() : nullptr; + return new SalInstanceBuilder(pParentWidget, rUIRoot, rUIFile); +} + +weld::Builder* SalInstance::CreateInterimBuilder(vcl::Window* pParent, const OUString& rUIRoot, + const OUString& rUIFile) +{ + return new SalInstanceBuilder(pParent, rUIRoot, rUIFile); +} + +void SalInstanceWindow::help() +{ + //show help for widget with keyboard focus + vcl::Window* pWidget = ImplGetSVData()->mpWinData->mpFocusWin; + if (!pWidget) + pWidget = m_xWindow; + OString sHelpId = pWidget->GetHelpId(); + while (sHelpId.isEmpty()) + { + pWidget = pWidget->GetParent(); + if (!pWidget) + break; + sHelpId = pWidget->GetHelpId(); + } + std::unique_ptr<weld::Widget> xTemp( + pWidget != m_xWindow ? new SalInstanceWidget(pWidget, m_pBuilder, false) : nullptr); + weld::Widget* pSource = xTemp ? xTemp.get() : this; + bool bRunNormalHelpRequest = !m_aHelpRequestHdl.IsSet() || m_aHelpRequestHdl.Call(*pSource); + Help* pHelp = bRunNormalHelpRequest ? Application::GetHelp() : nullptr; + if (pHelp) + { + // tdf#126007, there's a nice fallback route for offline help where + // the current page of a notebook will get checked when the help + // button is pressed and there was no help for the dialog found. + // + // But for online help that route doesn't get taken, so bodge this here + // by using the page help id if available and if the help button itself + // was the original id + if (m_pBuilder && sHelpId.endsWith("/help")) + { + OString sPageId = m_pBuilder->get_current_page_help_id(); + if (!sPageId.isEmpty()) + sHelpId = sPageId; + else + { + // tdf#129068 likewise the help for the wrapping dialog is less + // helpful than the help for the content area could be + vcl::Window* pContentArea = nullptr; + if (::Dialog* pDialog = dynamic_cast<::Dialog*>(m_xWindow.get())) + pContentArea = pDialog->get_content_area(); + if (pContentArea) + { + vcl::Window* pContentWidget = pContentArea->GetWindow(GetWindowType::LastChild); + if (pContentWidget) + sHelpId = pContentWidget->GetHelpId(); + } + } + } + pHelp->Start(OStringToOUString(sHelpId, RTL_TEXTENCODING_UTF8), pSource); + } +} + +//iterate upwards through the hierarchy from this widgets through its parents +//calling func with their helpid until func returns true or we run out of parents +void SalInstanceWidget::help_hierarchy_foreach(const std::function<bool(const OString&)>& func) +{ + vcl::Window* pParent = m_xWidget; + while ((pParent = pParent->GetParent())) + { + if (func(pParent->GetHelpId())) + return; + } +} + +weld::MessageDialog* SalInstance::CreateMessageDialog(weld::Widget* pParent, + VclMessageType eMessageType, + VclButtonsType eButtonsType, + const OUString& rPrimaryMessage) +{ + SalInstanceWidget* pParentInstance = dynamic_cast<SalInstanceWidget*>(pParent); + SystemWindow* pParentWidget = pParentInstance ? pParentInstance->getSystemWindow() : nullptr; + VclPtrInstance<MessageDialog> xMessageDialog(pParentWidget, rPrimaryMessage, eMessageType, + eButtonsType); + return new SalInstanceMessageDialog(xMessageDialog, nullptr, true); +} + +weld::Window* SalInstance::GetFrameWeld(const css::uno::Reference<css::awt::XWindow>& rWindow) +{ + UnoWrapperBase* pWrapper = UnoWrapperBase::GetUnoWrapper(); + if (!pWrapper) + return nullptr; + VclPtr<vcl::Window> xWindow = pWrapper->GetWindow(rWindow); + if (!xWindow) + return nullptr; + return xWindow->GetFrameWeld(); +} + +weld::Window* SalFrame::GetFrameWeld() const +{ + if (!m_xFrameWeld) + { + vcl::Window* pWindow = GetWindow(); + pWindow = pWindow ? pWindow->ImplGetWindow() : nullptr; + assert(!pWindow || (pWindow->IsSystemWindow() || pWindow->IsDockingWindow())); + if (pWindow) + m_xFrameWeld.reset(new SalInstanceWindow(pWindow, nullptr, false)); + } + return m_xFrameWeld.get(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/source/app/scheduler.cxx b/vcl/source/app/scheduler.cxx new file mode 100644 index 000000000..aa716f88b --- /dev/null +++ b/vcl/source/app/scheduler.cxx @@ -0,0 +1,659 @@ +/* -*- 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 <cassert> +#include <cstdlib> +#include <exception> +#include <typeinfo> + +#include <com/sun/star/uno/Exception.hpp> +#include <sal/log.hxx> +#include <sal/types.h> +#include <svdata.hxx> +#include <tools/time.hxx> +#include <tools/debug.hxx> +#include <tools/diagnose_ex.h> +#include <unotools/configmgr.hxx> +#include <vcl/TaskStopwatch.hxx> +#include <vcl/scheduler.hxx> +#include <vcl/idle.hxx> +#include <saltimer.hxx> +#include <salinst.hxx> +#include <comphelper/profilezone.hxx> +#include <schedulerimpl.hxx> + +namespace { + +template< typename charT, typename traits > +std::basic_ostream<charT, traits> & operator <<( + std::basic_ostream<charT, traits> & stream, const Task& task ) +{ + stream << "a: " << task.IsActive() << " p: " << static_cast<int>(task.GetPriority()); + const char *name = task.GetDebugName(); + if( nullptr == name ) + return stream << " (nullptr)"; + else + return stream << " " << name; +} + +/** + * clang won't compile this in the Timer.hxx header, even with a class Idle + * forward definition, due to the incomplete Idle type in the template. + * Currently the code is just used in the Scheduler, so we keep it local. + * + * @see http://clang.llvm.org/compatibility.html#undep_incomplete + */ +template< typename charT, typename traits > +std::basic_ostream<charT, traits> & operator <<( + std::basic_ostream<charT, traits> & stream, const Timer& timer ) +{ + bool bIsIdle = (dynamic_cast<const Idle*>( &timer ) != nullptr); + stream << (bIsIdle ? "Idle " : "Timer") + << " a: " << timer.IsActive() << " p: " << static_cast<int>(timer.GetPriority()); + const char *name = timer.GetDebugName(); + if ( nullptr == name ) + stream << " (nullptr)"; + else + stream << " " << name; + if ( !bIsIdle ) + stream << " " << timer.GetTimeout() << "ms"; + stream << " (" << &timer << ")"; + return stream; +} + +template< typename charT, typename traits > +std::basic_ostream<charT, traits> & operator <<( + std::basic_ostream<charT, traits> & stream, const Idle& idle ) +{ + return stream << static_cast<const Timer*>( &idle ); +} + +template< typename charT, typename traits > +std::basic_ostream<charT, traits> & operator <<( + std::basic_ostream<charT, traits> & stream, const ImplSchedulerData& data ) +{ + stream << " i: " << data.mbInScheduler; + return stream; +} + +} // end anonymous namespace + +unsigned int TaskStopwatch::m_nTimeSlice = TaskStopwatch::nDefaultTimeSlice; + +void Scheduler::ImplDeInitScheduler() +{ + ImplSVData* pSVData = ImplGetSVData(); + assert( pSVData != nullptr ); + ImplSchedulerContext &rSchedCtx = pSVData->maSchedCtx; + + DBG_TESTSOLARMUTEX(); + + SchedulerGuard aSchedulerGuard; + + int nTaskPriority = 0; +#if OSL_DEBUG_LEVEL > 0 + sal_uInt32 nTasks = 0; + for (nTaskPriority = 0; nTaskPriority < PRIO_COUNT; ++nTaskPriority) + { + ImplSchedulerData* pSchedulerData = rSchedCtx.mpFirstSchedulerData[nTaskPriority]; + while ( pSchedulerData ) + { + ++nTasks; + pSchedulerData = pSchedulerData->mpNext; + } + } + SAL_INFO( "vcl.schedule.deinit", + "DeInit the scheduler - pending tasks: " << nTasks ); + + // clean up all the sfx::SfxItemDisruptor_Impl Idles + ProcessEventsToIdle(); +#endif + rSchedCtx.mbActive = false; + + assert( nullptr == rSchedCtx.mpSchedulerStack ); + assert( 1 == rSchedCtx.maMutex.lockDepth() ); + + if (rSchedCtx.mpSalTimer) rSchedCtx.mpSalTimer->Stop(); + DELETEZ( rSchedCtx.mpSalTimer ); + +#if OSL_DEBUG_LEVEL > 0 + sal_uInt32 nActiveTasks = 0, nIgnoredTasks = 0; +#endif + nTaskPriority = 0; + ImplSchedulerData* pSchedulerData = nullptr; + +next_priority: + pSchedulerData = rSchedCtx.mpFirstSchedulerData[nTaskPriority]; + while ( pSchedulerData ) + { + Task *pTask = pSchedulerData->mpTask; + if ( pTask ) + { + if ( pTask->mbActive ) + { +#if OSL_DEBUG_LEVEL > 0 + const char *sIgnored = ""; + ++nActiveTasks; + // TODO: shutdown these timers before Scheduler de-init + // TODO: remove Task from static object + if ( pTask->GetDebugName() && ( false + || !strcmp( pTask->GetDebugName(), "desktop::Desktop m_firstRunTimer" ) + || !strcmp( pTask->GetDebugName(), "DrawWorkStartupTimer" ) + || !strcmp( pTask->GetDebugName(), "editeng::ImpEditEngine aOnlineSpellTimer" ) + || !strcmp( pTask->GetDebugName(), "ImplHandleMouseMsg SalData::mpMouseLeaveTimer" ) + || !strcmp( pTask->GetDebugName(), "sc ScModule IdleTimer" ) + || !strcmp( pTask->GetDebugName(), "sd::CacheConfiguration maReleaseTimer" ) + || !strcmp( pTask->GetDebugName(), "svtools::GraphicCache maReleaseTimer" ) + || !strcmp( pTask->GetDebugName(), "svtools::GraphicObject mpSwapOutTimer" ) + || !strcmp( pTask->GetDebugName(), "svx OLEObjCache pTimer UnloadCheck" ) + || !strcmp( pTask->GetDebugName(), "vcl SystemDependentDataBuffer aSystemDependentDataBuffer" ) + )) + { + sIgnored = " (ignored)"; + ++nIgnoredTasks; + } + const Timer *timer = dynamic_cast<Timer*>( pTask ); + if ( timer ) + SAL_WARN( "vcl.schedule.deinit", "DeInit task: " << *timer << sIgnored ); + else + SAL_WARN( "vcl.schedule.deinit", "DeInit task: " << *pTask << sIgnored ); +#endif + pTask->mbActive = false; + } + pTask->mpSchedulerData = nullptr; + pTask->SetStatic(); + } + ImplSchedulerData* pDeleteSchedulerData = pSchedulerData; + pSchedulerData = pSchedulerData->mpNext; + delete pDeleteSchedulerData; + } + + ++nTaskPriority; + if (nTaskPriority < PRIO_COUNT) + goto next_priority; + +#if OSL_DEBUG_LEVEL > 0 + SAL_INFO( "vcl.schedule.deinit", "DeInit the scheduler - finished" ); + SAL_WARN_IF( 0 != nActiveTasks, "vcl.schedule.deinit", "DeInit active tasks: " + << nActiveTasks << " (ignored: " << nIgnoredTasks << ")" ); +// assert( nIgnoredTasks == nActiveTasks ); +#endif + + for (nTaskPriority = 0; nTaskPriority < PRIO_COUNT; ++nTaskPriority) + { + rSchedCtx.mpFirstSchedulerData[nTaskPriority] = nullptr; + rSchedCtx.mpLastSchedulerData[nTaskPriority] = nullptr; + } + rSchedCtx.mnTimerPeriod = InfiniteTimeoutMs; +} + +void SchedulerMutex::acquire( sal_uInt32 nLockCount ) +{ + assert(nLockCount > 0); + for (sal_uInt32 i = 0; i != nLockCount; ++i) { + if (!maMutex.acquire()) + abort(); + } + mnLockDepth += nLockCount; +} + +sal_uInt32 SchedulerMutex::release( bool bUnlockAll ) +{ + assert(mnLockDepth > 0); + const sal_uInt32 nLockCount = + (bUnlockAll || 0 == mnLockDepth) ? mnLockDepth : 1; + mnLockDepth -= nLockCount; + for (sal_uInt32 i = 0; i != nLockCount; ++i) { + if (!maMutex.release()) + abort(); + } + return nLockCount; +} + +void Scheduler::Lock( sal_uInt32 nLockCount ) +{ + ImplSVData* pSVData = ImplGetSVData(); + assert( pSVData != nullptr ); + pSVData->maSchedCtx.maMutex.acquire( nLockCount ); +} + +sal_uInt32 Scheduler::Unlock( bool bUnlockAll ) +{ + ImplSVData* pSVData = ImplGetSVData(); + assert( pSVData != nullptr ); + return pSVData->maSchedCtx.maMutex.release( bUnlockAll ); +} + +/** + * Start a new timer if we need to for nMS duration. + * + * if this is longer than the existing duration we're + * waiting for, do nothing - unless bForce - which means + * to reset the minimum period; used by the scheduled itself. + */ +void Scheduler::ImplStartTimer(sal_uInt64 nMS, bool bForce, sal_uInt64 nTime) +{ + ImplSVData* pSVData = ImplGetSVData(); + ImplSchedulerContext &rSchedCtx = pSVData->maSchedCtx; + if ( !rSchedCtx.mbActive ) + return; + + if (!rSchedCtx.mpSalTimer) + { + rSchedCtx.mnTimerStart = 0; + rSchedCtx.mnTimerPeriod = InfiniteTimeoutMs; + rSchedCtx.mpSalTimer = pSVData->mpDefInst->CreateSalTimer(); + rSchedCtx.mpSalTimer->SetCallback(Scheduler::CallbackTaskScheduling); + } + + assert(SAL_MAX_UINT64 - nMS >= nTime); + + sal_uInt64 nProposedTimeout = nTime + nMS; + sal_uInt64 nCurTimeout = ( rSchedCtx.mnTimerPeriod == InfiniteTimeoutMs ) + ? SAL_MAX_UINT64 : rSchedCtx.mnTimerStart + rSchedCtx.mnTimerPeriod; + + // Only if smaller timeout, to avoid skipping. + // Force instant wakeup on 0ms, if the previous period was not 0ms + if (bForce || nProposedTimeout < nCurTimeout || (!nMS && rSchedCtx.mnTimerPeriod)) + { + SAL_INFO( "vcl.schedule", " Starting scheduler system timer (" << nMS << "ms)" ); + rSchedCtx.mnTimerStart = nTime; + rSchedCtx.mnTimerPeriod = nMS; + rSchedCtx.mpSalTimer->Start( nMS ); + } +} + +void Scheduler::CallbackTaskScheduling() +{ + // this function is for the saltimer callback + Scheduler::ProcessTaskScheduling(); +} + +static bool g_bDeterministicMode = false; + +void Scheduler::SetDeterministicMode(bool bDeterministic) +{ + g_bDeterministicMode = bDeterministic; +} + +bool Scheduler::GetDeterministicMode() +{ + return g_bDeterministicMode; +} + +inline void Scheduler::UpdateSystemTimer( ImplSchedulerContext &rSchedCtx, + const sal_uInt64 nMinPeriod, + const bool bForce, const sal_uInt64 nTime ) +{ + if ( InfiniteTimeoutMs == nMinPeriod ) + { + SAL_INFO("vcl.schedule", " Stopping system timer"); + if ( rSchedCtx.mpSalTimer ) + rSchedCtx.mpSalTimer->Stop(); + rSchedCtx.mnTimerPeriod = nMinPeriod; + } + else + Scheduler::ImplStartTimer( nMinPeriod, bForce, nTime ); +} + +static void AppendSchedulerData( ImplSchedulerContext &rSchedCtx, + ImplSchedulerData * const pSchedulerData) +{ + assert(pSchedulerData->mpTask); + pSchedulerData->mePriority = pSchedulerData->mpTask->GetPriority(); + pSchedulerData->mpNext = nullptr; + + const int nTaskPriority = static_cast<int>(pSchedulerData->mePriority); + if (!rSchedCtx.mpLastSchedulerData[nTaskPriority]) + { + rSchedCtx.mpFirstSchedulerData[nTaskPriority] = pSchedulerData; + rSchedCtx.mpLastSchedulerData[nTaskPriority] = pSchedulerData; + } + else + { + rSchedCtx.mpLastSchedulerData[nTaskPriority]->mpNext = pSchedulerData; + rSchedCtx.mpLastSchedulerData[nTaskPriority] = pSchedulerData; + } +} + +static ImplSchedulerData* DropSchedulerData( + ImplSchedulerContext &rSchedCtx, ImplSchedulerData * const pPrevSchedulerData, + const ImplSchedulerData * const pSchedulerData, const int nTaskPriority) +{ + assert( pSchedulerData ); + if ( pPrevSchedulerData ) + assert( pPrevSchedulerData->mpNext == pSchedulerData ); + else + assert(rSchedCtx.mpFirstSchedulerData[nTaskPriority] == pSchedulerData); + + ImplSchedulerData * const pSchedulerDataNext = pSchedulerData->mpNext; + if ( pPrevSchedulerData ) + pPrevSchedulerData->mpNext = pSchedulerDataNext; + else + rSchedCtx.mpFirstSchedulerData[nTaskPriority] = pSchedulerDataNext; + if ( !pSchedulerDataNext ) + rSchedCtx.mpLastSchedulerData[nTaskPriority] = pPrevSchedulerData; + return pSchedulerDataNext; +} + +bool Scheduler::ProcessTaskScheduling() +{ + ImplSVData *pSVData = ImplGetSVData(); + ImplSchedulerContext &rSchedCtx = pSVData->maSchedCtx; + + DBG_TESTSOLARMUTEX(); + + SchedulerGuard aSchedulerGuard; + if ( !rSchedCtx.mbActive || InfiniteTimeoutMs == rSchedCtx.mnTimerPeriod ) + return false; + + sal_uInt64 nTime = tools::Time::GetSystemTicks(); + // Allow for decimals, so subtract in the compare (needed at least on iOS) + if ( nTime < rSchedCtx.mnTimerStart + rSchedCtx.mnTimerPeriod -1) + { + int nSleep = rSchedCtx.mnTimerStart + rSchedCtx.mnTimerPeriod - nTime; + UpdateSystemTimer(rSchedCtx, nSleep, true, nTime); + return false; + } + + ImplSchedulerData* pSchedulerData = nullptr; + ImplSchedulerData* pPrevSchedulerData = nullptr; + ImplSchedulerData *pMostUrgent = nullptr; + ImplSchedulerData *pPrevMostUrgent = nullptr; + int nMostUrgentPriority = 0; + sal_uInt64 nMinPeriod = InfiniteTimeoutMs; + sal_uInt64 nReadyPeriod = InfiniteTimeoutMs; + unsigned nTasks = 0; + int nTaskPriority = 0; + + for (; nTaskPriority < PRIO_COUNT; ++nTaskPriority) + { + pSchedulerData = rSchedCtx.mpFirstSchedulerData[nTaskPriority]; + pPrevSchedulerData = nullptr; + while (pSchedulerData) + { + ++nTasks; + const Timer *timer = dynamic_cast<Timer*>( pSchedulerData->mpTask ); + if ( timer ) + SAL_INFO( "vcl.schedule", tools::Time::GetSystemTicks() << " " + << pSchedulerData << " " << *pSchedulerData << " " << *timer ); + else if ( pSchedulerData->mpTask ) + SAL_INFO( "vcl.schedule", tools::Time::GetSystemTicks() << " " + << pSchedulerData << " " << *pSchedulerData + << " " << *pSchedulerData->mpTask ); + else + SAL_INFO( "vcl.schedule", tools::Time::GetSystemTicks() << " " + << pSchedulerData << " " << *pSchedulerData << " (to be deleted)" ); + + // Should the Task be released from scheduling? + assert(!pSchedulerData->mbInScheduler); + if (!pSchedulerData->mpTask || !pSchedulerData->mpTask->IsActive()) + { + ImplSchedulerData * const pSchedulerDataNext = + DropSchedulerData(rSchedCtx, pPrevSchedulerData, pSchedulerData, nTaskPriority); + if ( pSchedulerData->mpTask ) + pSchedulerData->mpTask->mpSchedulerData = nullptr; + delete pSchedulerData; + pSchedulerData = pSchedulerDataNext; + continue; + } + + assert(pSchedulerData->mpTask); + if (pSchedulerData->mpTask->IsActive()) + { + nReadyPeriod = pSchedulerData->mpTask->UpdateMinPeriod( nTime ); + if (ImmediateTimeoutMs == nReadyPeriod) + { + if (!pMostUrgent) + { + pPrevMostUrgent = pPrevSchedulerData; + pMostUrgent = pSchedulerData; + nMostUrgentPriority = nTaskPriority; + } + else + { + nMinPeriod = ImmediateTimeoutMs; + break; + } + } + else if (nMinPeriod > nReadyPeriod) + nMinPeriod = nReadyPeriod; + } + + pPrevSchedulerData = pSchedulerData; + pSchedulerData = pSchedulerData->mpNext; + } + + if (ImmediateTimeoutMs == nMinPeriod) + break; + } + + if ( InfiniteTimeoutMs != nMinPeriod ) + SAL_INFO("vcl.schedule", "Calculated minimum timeout as " << nMinPeriod + << " of " << nTasks << " tasks" ); + UpdateSystemTimer( rSchedCtx, nMinPeriod, true, nTime ); + + if ( pMostUrgent ) + { + SAL_INFO( "vcl.schedule", tools::Time::GetSystemTicks() << " " + << pMostUrgent << " invoke-in " << *pMostUrgent->mpTask ); + + Task *pTask = pMostUrgent->mpTask; + + comphelper::ProfileZone aZone( pTask->GetDebugName() ); + + // prepare Scheduler object for deletion after handling + pTask->SetDeletionFlags(); + + pMostUrgent->mbInScheduler = true; + + // always push the stack, as we don't traverse the whole list to push later + DropSchedulerData(rSchedCtx, pPrevMostUrgent, pMostUrgent, nMostUrgentPriority); + pMostUrgent->mpNext = rSchedCtx.mpSchedulerStack; + rSchedCtx.mpSchedulerStack = pMostUrgent; + rSchedCtx.mpSchedulerStackTop = pMostUrgent; + + // invoke the task + sal_uInt32 nLockCount = Unlock( true ); + try + { + pTask->Invoke(); + } + catch (css::uno::Exception&) + { + TOOLS_WARN_EXCEPTION("vcl.schedule", "Uncaught"); + std::abort(); + } + catch (std::exception& e) + { + SAL_WARN("vcl.schedule", "Uncaught " << typeid(e).name() << " " << e.what()); + std::abort(); + } + catch (...) + { + SAL_WARN("vcl.schedule", "Uncaught exception during Task::Invoke()!"); + std::abort(); + } + Lock( nLockCount ); + pMostUrgent->mbInScheduler = false; + + SAL_INFO( "vcl.schedule", tools::Time::GetSystemTicks() << " " + << pMostUrgent << " invoke-out" ); + + // pop the scheduler stack + pSchedulerData = rSchedCtx.mpSchedulerStack; + assert(pSchedulerData == pMostUrgent); + rSchedCtx.mpSchedulerStack = pSchedulerData->mpNext; + + const bool bTaskAlive = pMostUrgent->mpTask && pMostUrgent->mpTask->IsActive(); + if (!bTaskAlive) + { + if (pMostUrgent->mpTask) + pMostUrgent->mpTask->mpSchedulerData = nullptr; + delete pMostUrgent; + } + else + AppendSchedulerData(rSchedCtx, pMostUrgent); + + // this just happens for nested calls, which renders all accounting + // invalid, so we just enforce a rescheduling! + if (rSchedCtx.mpSchedulerStackTop != pSchedulerData) + { + UpdateSystemTimer( rSchedCtx, ImmediateTimeoutMs, true, + tools::Time::GetSystemTicks() ); + } + else if (bTaskAlive) + { + pMostUrgent->mnUpdateTime = nTime; + nReadyPeriod = pMostUrgent->mpTask->UpdateMinPeriod( nTime ); + if ( nMinPeriod > nReadyPeriod ) + nMinPeriod = nReadyPeriod; + UpdateSystemTimer( rSchedCtx, nMinPeriod, false, nTime ); + } + } + + return !!pMostUrgent; +} + +void Scheduler::Wakeup() +{ + Scheduler::ImplStartTimer( 0, false, tools::Time::GetSystemTicks() ); +} + +void Task::StartTimer( sal_uInt64 nMS ) +{ + Scheduler::ImplStartTimer( nMS, false, tools::Time::GetSystemTicks() ); +} + +void Task::SetDeletionFlags() +{ + mbActive = false; +} + +void Task::Start() +{ + ImplSVData *const pSVData = ImplGetSVData(); + ImplSchedulerContext &rSchedCtx = pSVData->maSchedCtx; + + SchedulerGuard aSchedulerGuard; + if ( !rSchedCtx.mbActive ) + return; + + // is the task scheduled in the correct priority queue? + // if not we have to get a new data object, as we don't want to traverse + // the whole list to move the data to the correct list, as the task list + // is just single linked. + // Task priority doesn't change that often AFAIK, or we might need to + // start caching ImplSchedulerData objects. + if (mpSchedulerData && mpSchedulerData->mePriority != mePriority) + { + mpSchedulerData->mpTask = nullptr; + mpSchedulerData = nullptr; + } + mbActive = true; + + if ( !mpSchedulerData ) + { + // insert Task + ImplSchedulerData* pSchedulerData = new ImplSchedulerData; + pSchedulerData->mpTask = this; + pSchedulerData->mbInScheduler = false; + // mePriority is set in AppendSchedulerData + mpSchedulerData = pSchedulerData; + + AppendSchedulerData( rSchedCtx, pSchedulerData ); + SAL_INFO( "vcl.schedule", tools::Time::GetSystemTicks() + << " " << mpSchedulerData << " added " << *this ); + } + else + SAL_INFO( "vcl.schedule", tools::Time::GetSystemTicks() + << " " << mpSchedulerData << " restarted " << *this ); + + mpSchedulerData->mnUpdateTime = tools::Time::GetSystemTicks(); +} + +void Task::Stop() +{ + SAL_INFO_IF( mbActive, "vcl.schedule", tools::Time::GetSystemTicks() + << " " << mpSchedulerData << " stopped " << *this ); + mbActive = false; +} + +void Task::SetPriority(TaskPriority ePriority) +{ + // you don't actually need to call Stop() before but Start() after, but we + // can't check that and don't know when Start() should be called. + SAL_WARN_IF(mpSchedulerData && mbActive, "vcl.schedule", + "Stop the task before changing the priority, as it will just " + "change after the task was scheduled with the old prio!"); + mePriority = ePriority; +} + +Task& Task::operator=( const Task& rTask ) +{ + if(this == &rTask) + return *this; + + if ( IsActive() ) + Stop(); + + mbActive = false; + mePriority = rTask.mePriority; + + if ( rTask.IsActive() ) + Start(); + + return *this; +} + +Task::Task( const char *pDebugName ) + : mpSchedulerData( nullptr ) + , mpDebugName( pDebugName ) + , mePriority( TaskPriority::DEFAULT ) + , mbActive( false ) + , mbStatic( false ) +{ +} + +Task::Task( const Task& rTask ) + : mpSchedulerData( nullptr ) + , mpDebugName( rTask.mpDebugName ) + , mePriority( rTask.mePriority ) + , mbActive( false ) + , mbStatic( false ) +{ + if ( rTask.IsActive() ) + Start(); +} + +Task::~Task() COVERITY_NOEXCEPT_FALSE +{ + if ( !IsStatic() ) + { + SchedulerGuard aSchedulerGuard; + if ( mpSchedulerData ) + mpSchedulerData->mpTask = nullptr; + } + else + assert(nullptr == mpSchedulerData || utl::ConfigManager::IsFuzzing()); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/source/app/session.cxx b/vcl/source/app/session.cxx new file mode 100644 index 000000000..98cad6bba --- /dev/null +++ b/vcl/source/app/session.cxx @@ -0,0 +1,414 @@ +/* -*- 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 <memory> +#include <sal/config.h> +#include <sal/log.hxx> + +#include <cppuhelper/basemutex.hxx> +#include <cppuhelper/compbase.hxx> + +#include <tools/diagnose_ex.h> +#include <vcl/svapp.hxx> + +#include <factory.hxx> +#include <svdata.hxx> +#include <salinst.hxx> +#include <salsession.hxx> + +#include <com/sun/star/frame/XSessionManagerClient.hpp> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include <com/sun/star/frame/XSessionManagerListener2.hpp> + +#include <vector> + +using namespace com::sun::star::uno; +using namespace com::sun::star::lang; +using namespace com::sun::star::frame; + +SalSession::~SalSession() +{ +} + +namespace { + +class VCLSession: + private cppu::BaseMutex, + public cppu::WeakComponentImplHelper < XSessionManagerClient > +{ + struct Listener + { + css::uno::Reference< XSessionManagerListener > m_xListener; + bool m_bInteractionRequested; + bool m_bInteractionDone; + bool m_bSaveDone; + + explicit Listener( const css::uno::Reference< XSessionManagerListener >& xListener ) + : m_xListener( xListener ), + m_bInteractionRequested( false ), + m_bInteractionDone( false ), + m_bSaveDone( false ) + {} + }; + + std::vector< Listener > m_aListeners; + std::unique_ptr< SalSession > m_xSession; + bool m_bInteractionRequested; + bool m_bInteractionGranted; + bool m_bInteractionDone; + bool m_bSaveDone; + + static void SalSessionEventProc( void* pData, SalSessionEvent* pEvent ); + + virtual ~VCLSession() override {} + + virtual void SAL_CALL addSessionManagerListener( const css::uno::Reference< XSessionManagerListener >& xListener ) override; + virtual void SAL_CALL removeSessionManagerListener( const css::uno::Reference< XSessionManagerListener>& xListener ) override; + virtual void SAL_CALL queryInteraction( const css::uno::Reference< XSessionManagerListener >& xListener ) override; + virtual void SAL_CALL interactionDone( const css::uno::Reference< XSessionManagerListener >& xListener ) override; + virtual void SAL_CALL saveDone( const css::uno::Reference< XSessionManagerListener >& xListener ) override; + virtual sal_Bool SAL_CALL cancelShutdown() override; + + void SAL_CALL disposing() override; + + void callSaveRequested( bool bShutdown ); + void callShutdownCancelled(); + void callInteractionGranted( bool bGranted ); + void callQuit(); + +public: + VCLSession(); +}; + +} + +VCLSession::VCLSession() + : cppu::WeakComponentImplHelper< XSessionManagerClient >( m_aMutex ), + m_xSession( ImplGetSVData()->mpDefInst->CreateSalSession() ), + m_bInteractionRequested( false ), + m_bInteractionGranted( false ), + m_bInteractionDone( false ), + m_bSaveDone( false ) +{ + SAL_INFO("vcl.se", "VCLSession::VCLSession" ); + + if (m_xSession) + m_xSession->SetCallback( SalSessionEventProc, this ); +} + +void VCLSession::callSaveRequested( bool bShutdown ) +{ + SAL_INFO("vcl.se", "VCLSession::callSaveRequested" ); + + std::vector< Listener > aListeners; + { + osl::MutexGuard aGuard( m_aMutex ); + // reset listener states + for (auto & listener : m_aListeners) { + listener.m_bSaveDone = listener.m_bInteractionRequested = listener.m_bInteractionDone = false; + } + + // copy listener vector since calling a listener may remove it. + aListeners = m_aListeners; + // set back interaction state + m_bSaveDone = false; + m_bInteractionDone = false; + // without session we assume UI is always possible, + // so it was requested and granted + m_bInteractionRequested = m_bInteractionGranted = !m_xSession; + + // answer the session manager even if no listeners available anymore + SAL_WARN_IF( aListeners.empty(), "vcl.se", "saveRequested but no listeners !" ); + + SAL_INFO("vcl.se.debug", " aListeners.empty() = " << (aListeners.empty() ? "true" : "false") << + ", bShutdown = " << (bShutdown ? "true" : "false")); + if( aListeners.empty() ) + { + if (m_xSession) + m_xSession->saveDone(); + return; + } + } + + SolarMutexReleaser aReleaser; + for (auto const & listener: aListeners) + listener.m_xListener->doSave( bShutdown, false/*bCancelable*/ ); +} + +void VCLSession::callInteractionGranted( bool bInteractionGranted ) +{ + SAL_INFO("vcl.se", "VCLSession::callInteractionGranted" ); + + std::vector< Listener > aListeners; + { + osl::MutexGuard aGuard( m_aMutex ); + // copy listener vector since calling a listener may remove it. + for (auto const & listener: m_aListeners) + if( listener.m_bInteractionRequested ) + aListeners.push_back( listener ); + + m_bInteractionGranted = bInteractionGranted; + + // answer the session manager even if no listeners available anymore + SAL_WARN_IF( aListeners.empty(), "vcl.se", "interactionGranted but no listeners !" ); + + SAL_INFO("vcl.se.debug", " aListeners.empty() = " << (aListeners.empty() ? "true" : "false") << + ", bInteractionGranted = " << (bInteractionGranted ? "true" : "false")); + if( aListeners.empty() ) + { + if (m_xSession) + m_xSession->interactionDone(); + return; + } + } + + SolarMutexReleaser aReleaser; + for (auto const & listener: aListeners) + listener.m_xListener->approveInteraction( bInteractionGranted ); +} + +void VCLSession::callShutdownCancelled() +{ + SAL_INFO("vcl.se", "VCLSession::callShutdownCancelled"); + + std::vector< Listener > aListeners; + { + osl::MutexGuard aGuard( m_aMutex ); + // copy listener vector since calling a listener may remove it. + aListeners = m_aListeners; + // set back interaction state + m_bInteractionRequested = m_bInteractionDone = m_bInteractionGranted = false; + } + + SolarMutexReleaser aReleaser; + for (auto const & listener: aListeners) + listener.m_xListener->shutdownCanceled(); +} + +void VCLSession::callQuit() +{ + SAL_INFO("vcl.se", "VCLSession::callQuit"); + + std::vector< Listener > aListeners; + { + osl::MutexGuard aGuard( m_aMutex ); + // copy listener vector since calling a listener may remove it. + aListeners = m_aListeners; + // set back interaction state + m_bInteractionRequested = m_bInteractionDone = m_bInteractionGranted = false; + } + + SolarMutexReleaser aReleaser; + for (auto const & listener: aListeners) + { + css::uno::Reference< XSessionManagerListener2 > xListener2( listener.m_xListener, UNO_QUERY ); + if( xListener2.is() ) + xListener2->doQuit(); + } +} + +void VCLSession::SalSessionEventProc( void* pData, SalSessionEvent* pEvent ) +{ + SAL_INFO("vcl.se", "VCLSession::SalSessionEventProc"); + + VCLSession * pThis = static_cast< VCLSession * >( pData ); + switch( pEvent->m_eType ) + { + case Interaction: + { + SAL_INFO("vcl.se.debug", " EventProcType = Interaction"); + SalSessionInteractionEvent* pIEv = static_cast<SalSessionInteractionEvent*>(pEvent); + pThis->callInteractionGranted( pIEv->m_bInteractionGranted ); + } + break; + case SaveRequest: + { + SAL_INFO("vcl.se.debug", " EventProcType = SaveRequest"); + SalSessionSaveRequestEvent* pSEv = static_cast<SalSessionSaveRequestEvent*>(pEvent); + pThis->callSaveRequested( pSEv->m_bShutdown ); + } + break; + case ShutdownCancel: + SAL_INFO("vcl.se.debug", " EventProcType = ShutdownCancel"); + pThis->callShutdownCancelled(); + break; + case Quit: + SAL_INFO("vcl.se.debug", " EventProcType = Quit"); + pThis->callQuit(); + break; + } +} + +void SAL_CALL VCLSession::addSessionManagerListener( const css::uno::Reference<XSessionManagerListener>& xListener ) +{ + SAL_INFO("vcl.se", "VCLSession::addSessionManagerListener" ); + + osl::MutexGuard aGuard( m_aMutex ); + + SAL_INFO("vcl.se.debug", " m_aListeners.size() = " << m_aListeners.size() ); + m_aListeners.emplace_back( xListener ); +} + +void SAL_CALL VCLSession::removeSessionManagerListener( const css::uno::Reference<XSessionManagerListener>& xListener ) +{ + SAL_INFO("vcl.se", "VCLSession::removeSessionManagerListener" ); + + osl::MutexGuard aGuard( m_aMutex ); + + SAL_INFO("vcl.se.debug", " m_aListeners.size() = " << m_aListeners.size() ); + + m_aListeners.erase(std::remove_if(m_aListeners.begin(), m_aListeners.end(), [&](Listener& listener) {return xListener == listener.m_xListener;}), m_aListeners.end()); +} + +void SAL_CALL VCLSession::queryInteraction( const css::uno::Reference<XSessionManagerListener>& xListener ) +{ + SAL_INFO("vcl.se", "VCLSession::queryInteraction"); + + SAL_INFO("vcl.se.debug", " m_bInteractionGranted = " << (m_bInteractionGranted ? "true" : "false") << + ", m_bInteractionRequested = "<< (m_bInteractionRequested ? "true" : "false")); + if( m_bInteractionGranted ) + { + if( m_bInteractionDone ) + xListener->approveInteraction( false ); + else + xListener->approveInteraction( true ); + return; + } + + osl::MutexGuard aGuard( m_aMutex ); + if( ! m_bInteractionRequested ) + { + m_xSession->queryInteraction(); + m_bInteractionRequested = true; + } + for (auto & listener: m_aListeners) + { + if( listener.m_xListener == xListener ) + { + SAL_INFO("vcl.se.debug", " listener.m_xListener == xListener"); + listener.m_bInteractionRequested = true; + listener.m_bInteractionDone = false; + } + } +} + +void SAL_CALL VCLSession::interactionDone( const css::uno::Reference< XSessionManagerListener >& xListener ) +{ + SAL_INFO("vcl.se", "VCLSession::interactionDone"); + + osl::MutexGuard aGuard( m_aMutex ); + int nRequested = 0, nDone = 0; + for (auto & listener: m_aListeners) + { + if( listener.m_bInteractionRequested ) + { + nRequested++; + if( xListener == listener.m_xListener ) + listener.m_bInteractionDone = true; + } + if( listener.m_bInteractionDone ) + nDone++; + } + + SAL_INFO("vcl.se.debug", " nDone = " << nDone << + ", nRequested =" << nRequested); + if( nDone == nRequested && nDone > 0 ) + { + m_bInteractionDone = true; + if (m_xSession) + m_xSession->interactionDone(); + } +} + +void SAL_CALL VCLSession::saveDone( const css::uno::Reference< XSessionManagerListener >& xListener ) +{ + SAL_INFO("vcl.se", "VCLSession::saveDone"); + + osl::MutexGuard aGuard( m_aMutex ); + + bool bSaveDone = true; + for (auto & listener: m_aListeners) + { + if( listener.m_xListener == xListener ) + listener.m_bSaveDone = true; + if( ! listener.m_bSaveDone ) + bSaveDone = false; + } + + SAL_INFO("vcl.se.debug", " bSaveDone = " << (bSaveDone ? "true" : "false")); + + if( bSaveDone && !m_bSaveDone ) + { + m_bSaveDone = true; + if (m_xSession) + m_xSession->saveDone(); + } +} + +sal_Bool SAL_CALL VCLSession::cancelShutdown() +{ + SAL_INFO("vcl.se", "VCLSession::cancelShutdown"); + + return m_xSession && m_xSession->cancelShutdown(); +} + +void VCLSession::disposing() { + SAL_INFO("vcl.se", "VCLSession::disposing"); + + std::vector<Listener> vector; + { + osl::MutexGuard g(m_aMutex); + vector.swap(m_aListeners); + } + css::lang::EventObject src(static_cast<OWeakObject *>(this)); + for (auto const & listener: vector) { + try { + listener.m_xListener->disposing(src); + SAL_INFO("vcl.se.debug", " call Listener disposing"); + } catch (css::uno::RuntimeException &) { + TOOLS_WARN_EXCEPTION("vcl.se", "ignoring"); + } + } +} + +// service implementation + +OUString vcl_session_getImplementationName() +{ + SAL_INFO("vcl.se", "vcl_session_getImplementationName"); + + return "com.sun.star.frame.VCLSessionManagerClient"; +} + +Sequence< OUString > vcl_session_getSupportedServiceNames() +{ + SAL_INFO("vcl.se", "vcl_session_getSupportedServiceNames"); + + Sequence< OUString > aRet { "com.sun.star.frame.SessionManagerClient" }; + return aRet; +} + +css::uno::Reference< XInterface > vcl_session_createInstance( SAL_UNUSED_PARAMETER const css::uno::Reference< XMultiServiceFactory > & ) +{ + SAL_INFO("vcl.se", "vcl_session_createInstance"); + + return static_cast< cppu::OWeakObject * >(new VCLSession); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/source/app/settings.cxx b/vcl/source/app/settings.cxx new file mode 100644 index 000000000..c2ca35835 --- /dev/null +++ b/vcl/source/app/settings.cxx @@ -0,0 +1,3233 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */ +/* + * 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_folders.h> + +#include <officecfg/Office/Common.hxx> + +#ifdef _WIN32 +#include <win/svsys.h> +#endif + +#include <comphelper/processfactory.hxx> +#include <rtl/bootstrap.hxx> + +#include <i18nlangtag/mslangid.hxx> +#include <i18nlangtag/languagetag.hxx> + +#include <comphelper/lok.hxx> + +#include <vcl/graphicfilter.hxx> +#include <IconThemeScanner.hxx> +#include <IconThemeSelector.hxx> +#include <vcl/IconThemeInfo.hxx> +#include <vcl/svapp.hxx> +#include <vcl/event.hxx> +#include <vcl/settings.hxx> +#include <vcl/i18nhelp.hxx> +#include <configsettings.hxx> +#include <vcl/outdev.hxx> + +#include <unotools/fontcfg.hxx> +#include <unotools/localedatawrapper.hxx> +#include <unotools/confignode.hxx> +#include <unotools/configmgr.hxx> +#include <unotools/syslocale.hxx> +#include <unotools/syslocaleoptions.hxx> + +using namespace ::com::sun::star; + +#include <svdata.hxx> + +struct ImplMouseData +{ + MouseSettingsOptions mnOptions = MouseSettingsOptions::NONE; + sal_uInt64 mnDoubleClkTime = 500; + long mnDoubleClkWidth = 2; + long mnDoubleClkHeight = 2; + long mnStartDragWidth = 2 ; + long mnStartDragHeight = 2; + sal_uLong mnButtonRepeat = 90; + sal_uLong mnMenuDelay = 150; + MouseFollowFlags mnFollow = MouseFollowFlags::Menu | MouseFollowFlags::DDList; + MouseMiddleButtonAction mnMiddleButtonAction= MouseMiddleButtonAction::AutoScroll; + MouseWheelBehaviour mnWheelBehavior = MouseWheelBehaviour::ALWAYS; +}; + +struct ImplStyleData +{ + ImplStyleData(); + ImplStyleData( const ImplStyleData& rData ); + + void SetStandardStyles(); + + Color maActiveBorderColor; + Color maActiveColor; + Color maActiveTextColor; + Color maAlternatingRowColor; + Color maDefaultButtonTextColor; + Color maButtonTextColor; + Color maDefaultActionButtonTextColor; + Color maActionButtonTextColor; + Color maFlatButtonTextColor; + Color maDefaultButtonRolloverTextColor; + Color maButtonRolloverTextColor; + Color maDefaultActionButtonRolloverTextColor; + Color maActionButtonRolloverTextColor; + Color maFlatButtonRolloverTextColor; + Color maDefaultButtonPressedRolloverTextColor; + Color maButtonPressedRolloverTextColor; + Color maDefaultActionButtonPressedRolloverTextColor; + Color maActionButtonPressedRolloverTextColor; + Color maFlatButtonPressedRolloverTextColor; + Color maCheckedColor; + Color maDarkShadowColor; + Color maDeactiveBorderColor; + Color maDeactiveColor; + Color maDeactiveTextColor; + Color maDialogColor; + Color maDialogTextColor; + Color maDisableColor; + Color maFaceColor; + Color maFieldColor; + Color maFieldTextColor; + Color maFieldRolloverTextColor; + Color maFontColor; + Color maGroupTextColor; + Color maHelpColor; + Color maHelpTextColor; + Color maHighlightColor; + Color maHighlightLinkColor; + Color maHighlightTextColor; + Color maLabelTextColor; + Color maLightBorderColor; + Color maLightColor; + Color maLinkColor; + Color maMenuBarColor; + Color maMenuBarRolloverColor; + Color maMenuBorderColor; + Color maMenuColor; + Color maMenuHighlightColor; + Color maMenuHighlightTextColor; + Color maMenuTextColor; + Color maMenuBarTextColor; + Color maMenuBarRolloverTextColor; + Color maMenuBarHighlightTextColor; + Color maMonoColor; + Color maRadioCheckTextColor; + Color maShadowColor; + Color maVisitedLinkColor; + Color maToolTextColor; + Color maWindowColor; + Color maWindowTextColor; + Color maWorkspaceColor; + Color maActiveTabColor; + Color maInactiveTabColor; + Color maTabTextColor; + Color maTabRolloverTextColor; + Color maTabHighlightTextColor; + vcl::Font maAppFont; + vcl::Font maHelpFont; + vcl::Font maTitleFont; + vcl::Font maFloatTitleFont; + vcl::Font maMenuFont; + vcl::Font maToolFont; + vcl::Font maLabelFont; + vcl::Font maRadioCheckFont; + vcl::Font maPushButtonFont; + vcl::Font maFieldFont; + vcl::Font maIconFont; + vcl::Font maTabFont; + vcl::Font maGroupFont; + long mnTitleHeight; + long mnFloatTitleHeight; + long mnScrollBarSize; + long mnSpinSize; + long mnCursorSize; + long mnAntialiasedMin; + sal_uInt64 mnCursorBlinkTime; + DragFullOptions mnDragFullOptions; + SelectionOptions mnSelectionOptions; + DisplayOptions mnDisplayOptions; + ToolbarIconSize mnToolbarIconSize; + bool mnUseFlatMenus; + StyleSettingsOptions mnOptions; + bool mbHighContrast; + bool mbUseSystemUIFonts; + /** + * Disabling AA doesn't actually disable AA of fonts, instead it is taken + * from system settings. + */ + bool mbUseFontAAFromSystem; + bool mbAutoMnemonic; + TriState meUseImagesInMenus; + bool mnUseFlatBorders; + bool mbPreferredUseImagesInMenus; + long mnMinThumbSize; + std::shared_ptr<vcl::IconThemeScanner> + mIconThemeScanner; + std::shared_ptr<vcl::IconThemeSelector> + mIconThemeSelector; + + OUString mIconTheme; + bool mbSkipDisabledInMenus; + bool mbHideDisabledMenuItems; + bool mbPreferredContextMenuShortcuts; + TriState meContextMenuShortcuts; + //mbPrimaryButtonWarpsSlider == true for "jump to here" behavior for primary button, otherwise + //primary means scroll by single page. Secondary button takes the alternative behaviour + bool mbPrimaryButtonWarpsSlider; + DialogStyle maDialogStyle; + + sal_uInt16 mnEdgeBlending; + Color maEdgeBlendingTopLeftColor; + Color maEdgeBlendingBottomRightColor; + sal_uInt16 mnListBoxMaximumLineCount; + sal_uInt16 mnColorValueSetColumnCount; + Size maListBoxPreviewDefaultLogicSize; + Size maListBoxPreviewDefaultPixelSize; + bool mbPreviewUsesCheckeredBackground; + + OUString maPersonaHeaderFooter; ///< Cache the settings to detect changes. + + BitmapEx maPersonaHeaderBitmap; ///< Cache the header bitmap. + BitmapEx maPersonaFooterBitmap; ///< Cache the footer bitmap. + std::optional<Color> maPersonaMenuBarTextColor; ///< Cache the menubar color. +}; + +struct ImplMiscData +{ + ImplMiscData(); + TriState mnEnableATT; + bool mbEnableLocalizedDecimalSep; + TriState mnDisablePrinting; +}; + +struct ImplHelpData +{ + sal_uLong mnTipTimeout = 3000; +}; + +struct ImplAllSettingsData +{ + ImplAllSettingsData(); + ImplAllSettingsData( const ImplAllSettingsData& rData ); + ~ImplAllSettingsData(); + + MouseSettings maMouseSettings; + StyleSettings maStyleSettings; + MiscSettings maMiscSettings; + HelpSettings maHelpSettings; + LanguageTag maLocale; + LanguageTag maUILocale; + std::unique_ptr<LocaleDataWrapper> mpLocaleDataWrapper; + std::unique_ptr<LocaleDataWrapper> mpUILocaleDataWrapper; + std::unique_ptr<LocaleDataWrapper> mpNeutralLocaleDataWrapper; + std::unique_ptr<vcl::I18nHelper> mpI18nHelper; + std::unique_ptr<vcl::I18nHelper> mpUII18nHelper; + SvtSysLocale maSysLocale; +}; + +void +MouseSettings::SetOptions(MouseSettingsOptions nOptions) +{ + CopyData(); + mxData->mnOptions = nOptions; +} + +MouseSettingsOptions +MouseSettings::GetOptions() const +{ + return mxData->mnOptions; +} + +void +MouseSettings::SetDoubleClickTime( sal_uInt64 nDoubleClkTime ) +{ + CopyData(); + mxData->mnDoubleClkTime = nDoubleClkTime; +} + +sal_uInt64 +MouseSettings::GetDoubleClickTime() const +{ + return mxData->mnDoubleClkTime; +} + +void +MouseSettings::SetDoubleClickWidth( long nDoubleClkWidth ) +{ + CopyData(); + mxData->mnDoubleClkWidth = nDoubleClkWidth; +} + +long +MouseSettings::GetDoubleClickWidth() const +{ + return mxData->mnDoubleClkWidth; +} + +void +MouseSettings::SetDoubleClickHeight( long nDoubleClkHeight ) +{ + CopyData(); + mxData->mnDoubleClkHeight = nDoubleClkHeight; +} + +long +MouseSettings::GetDoubleClickHeight() const +{ + return mxData->mnDoubleClkHeight; +} + +void +MouseSettings::SetStartDragWidth( long nDragWidth ) +{ + CopyData(); + mxData->mnStartDragWidth = nDragWidth; +} + +long +MouseSettings::GetStartDragWidth() const +{ + return mxData->mnStartDragWidth; +} + +void +MouseSettings::SetStartDragHeight( long nDragHeight ) +{ + CopyData(); + mxData->mnStartDragHeight = nDragHeight; +} + +long +MouseSettings::GetStartDragHeight() const +{ + return mxData->mnStartDragHeight; +} + +sal_uInt16 +MouseSettings::GetStartDragCode() +{ + return MOUSE_LEFT; +} + +sal_uInt16 +MouseSettings::GetContextMenuCode() +{ + return MOUSE_RIGHT; +} + +sal_uInt16 +MouseSettings::GetContextMenuClicks() +{ + return 1; +} + +sal_uLong +MouseSettings::GetScrollRepeat() +{ + return 100; +} + +sal_uLong +MouseSettings::GetButtonStartRepeat() +{ + return 370; +} + +void +MouseSettings::SetButtonRepeat( sal_uLong nRepeat ) +{ + CopyData(); + mxData->mnButtonRepeat = nRepeat; +} + +sal_uLong +MouseSettings::GetButtonRepeat() const +{ + return mxData->mnButtonRepeat; +} + +sal_uLong +MouseSettings::GetActionDelay() +{ + return 250; +} + +void +MouseSettings::SetMenuDelay( sal_uLong nDelay ) +{ + CopyData(); + mxData->mnMenuDelay = nDelay; +} + +sal_uLong +MouseSettings::GetMenuDelay() const +{ + return mxData->mnMenuDelay; +} + +void +MouseSettings::SetFollow( MouseFollowFlags nFollow ) +{ + CopyData(); + mxData->mnFollow = nFollow; +} + +MouseFollowFlags +MouseSettings::GetFollow() const +{ + return mxData->mnFollow; +} + +void +MouseSettings::SetMiddleButtonAction( MouseMiddleButtonAction nAction ) +{ + CopyData(); + mxData->mnMiddleButtonAction = nAction; +} + +MouseMiddleButtonAction +MouseSettings::GetMiddleButtonAction() const +{ + return mxData->mnMiddleButtonAction; +} + +void +MouseSettings::SetWheelBehavior( MouseWheelBehaviour nBehavior ) +{ + CopyData(); + mxData->mnWheelBehavior = nBehavior; +} + +MouseWheelBehaviour +MouseSettings::GetWheelBehavior() const +{ + return mxData->mnWheelBehavior; +} + +bool +MouseSettings::operator !=( const MouseSettings& rSet ) const +{ + return !(*this == rSet); +} + +MouseSettings::MouseSettings() + : mxData(std::make_shared<ImplMouseData>()) +{ +} + +void MouseSettings::CopyData() +{ + // copy if other references exist + if (mxData.use_count() > 1) + { + mxData = std::make_shared<ImplMouseData>(*mxData); + } +} + +bool MouseSettings::operator ==( const MouseSettings& rSet ) const +{ + if ( mxData == rSet.mxData ) + return true; + + return + (mxData->mnOptions == rSet.mxData->mnOptions) && + (mxData->mnDoubleClkTime == rSet.mxData->mnDoubleClkTime) && + (mxData->mnDoubleClkWidth == rSet.mxData->mnDoubleClkWidth) && + (mxData->mnDoubleClkHeight == rSet.mxData->mnDoubleClkHeight) && + (mxData->mnStartDragWidth == rSet.mxData->mnStartDragWidth) && + (mxData->mnStartDragHeight == rSet.mxData->mnStartDragHeight) && + (mxData->mnMiddleButtonAction == rSet.mxData->mnMiddleButtonAction) && + (mxData->mnButtonRepeat == rSet.mxData->mnButtonRepeat) && + (mxData->mnMenuDelay == rSet.mxData->mnMenuDelay) && + (mxData->mnFollow == rSet.mxData->mnFollow) && + (mxData->mnWheelBehavior == rSet.mxData->mnWheelBehavior ); +} + +ImplStyleData::ImplStyleData() : + mnScrollBarSize(16), + mnSpinSize(16), + mnCursorSize(2), + mnAntialiasedMin(0), + mnCursorBlinkTime(STYLE_CURSOR_NOBLINKTIME), + mnDragFullOptions(DragFullOptions::All), + mnSelectionOptions(SelectionOptions::NONE), + mnDisplayOptions(DisplayOptions::NONE), + mnToolbarIconSize(ToolbarIconSize::Unknown), + mnOptions(StyleSettingsOptions::NONE), + mbAutoMnemonic(true), + meUseImagesInMenus(TRISTATE_INDET), + mnMinThumbSize(16), + mIconThemeSelector(std::make_shared<vcl::IconThemeSelector>()), + meContextMenuShortcuts(TRISTATE_INDET), + mnEdgeBlending(35), + maEdgeBlendingTopLeftColor(Color(0xC0, 0xC0, 0xC0)), + maEdgeBlendingBottomRightColor(Color(0x40, 0x40, 0x40)), + mnListBoxMaximumLineCount(25), + // For some reason this isn't actually the column count that gets used, at least on iOS, but + // instead what SvtAccessibilityOptions_Impl::GetColorValueSetColumnCount() in + // svtools/source/config/accessibilityoptions.cxx returns. + mnColorValueSetColumnCount(12), +#ifdef IOS + maListBoxPreviewDefaultLogicSize(Size(30, 30)), +#else + maListBoxPreviewDefaultLogicSize(Size(15, 7)), +#endif + maListBoxPreviewDefaultPixelSize(Size(0, 0)), // on-demand calculated in GetListBoxPreviewDefaultPixelSize(), + mbPreviewUsesCheckeredBackground(true) +{ + SetStandardStyles(); +} + +ImplStyleData::ImplStyleData( const ImplStyleData& rData ) : + maActiveBorderColor( rData.maActiveBorderColor ), + maActiveColor( rData.maActiveColor ), + maActiveTextColor( rData.maActiveTextColor ), + maAlternatingRowColor( rData.maAlternatingRowColor ), + maDefaultButtonTextColor( rData.maDefaultButtonTextColor ), + maButtonTextColor( rData.maButtonTextColor ), + maDefaultActionButtonTextColor( rData.maDefaultActionButtonTextColor ), + maActionButtonTextColor( rData.maActionButtonTextColor ), + maFlatButtonTextColor( rData.maFlatButtonTextColor ), + maDefaultButtonRolloverTextColor( rData.maDefaultButtonRolloverTextColor ), + maButtonRolloverTextColor( rData.maButtonRolloverTextColor ), + maDefaultActionButtonRolloverTextColor( rData.maDefaultActionButtonRolloverTextColor ), + maActionButtonRolloverTextColor( rData.maActionButtonRolloverTextColor ), + maFlatButtonRolloverTextColor( rData.maFlatButtonRolloverTextColor ), + maDefaultButtonPressedRolloverTextColor( rData.maDefaultButtonPressedRolloverTextColor ), + maButtonPressedRolloverTextColor( rData.maButtonPressedRolloverTextColor ), + maDefaultActionButtonPressedRolloverTextColor( rData.maDefaultActionButtonPressedRolloverTextColor ), + maActionButtonPressedRolloverTextColor( rData.maActionButtonPressedRolloverTextColor ), + maFlatButtonPressedRolloverTextColor( rData.maFlatButtonPressedRolloverTextColor ), + maCheckedColor( rData.maCheckedColor ), + maDarkShadowColor( rData.maDarkShadowColor ), + maDeactiveBorderColor( rData.maDeactiveBorderColor ), + maDeactiveColor( rData.maDeactiveColor ), + maDeactiveTextColor( rData.maDeactiveTextColor ), + maDialogColor( rData.maDialogColor ), + maDialogTextColor( rData.maDialogTextColor ), + maDisableColor( rData.maDisableColor ), + maFaceColor( rData.maFaceColor ), + maFieldColor( rData.maFieldColor ), + maFieldTextColor( rData.maFieldTextColor ), + maFieldRolloverTextColor( rData.maFieldRolloverTextColor ), + maFontColor( rData.maFontColor ), + maGroupTextColor( rData.maGroupTextColor ), + maHelpColor( rData.maHelpColor ), + maHelpTextColor( rData.maHelpTextColor ), + maHighlightColor( rData.maHighlightColor ), + maHighlightLinkColor( rData.maHighlightLinkColor ), + maHighlightTextColor( rData.maHighlightTextColor ), + maLabelTextColor( rData.maLabelTextColor ), + maLightBorderColor( rData.maLightBorderColor ), + maLightColor( rData.maLightColor ), + maLinkColor( rData.maLinkColor ), + maMenuBarColor( rData.maMenuBarColor ), + maMenuBarRolloverColor( rData.maMenuBarRolloverColor ), + maMenuBorderColor( rData.maMenuBorderColor ), + maMenuColor( rData.maMenuColor ), + maMenuHighlightColor( rData.maMenuHighlightColor ), + maMenuHighlightTextColor( rData.maMenuHighlightTextColor ), + maMenuTextColor( rData.maMenuTextColor ), + maMenuBarTextColor( rData.maMenuBarTextColor ), + maMenuBarRolloverTextColor( rData.maMenuBarRolloverTextColor ), + maMenuBarHighlightTextColor( rData.maMenuBarHighlightTextColor ), + maMonoColor( rData.maMonoColor ), + maRadioCheckTextColor( rData.maRadioCheckTextColor ), + maShadowColor( rData.maShadowColor ), + maVisitedLinkColor( rData.maVisitedLinkColor ), + maToolTextColor( rData.maToolTextColor ), + maWindowColor( rData.maWindowColor ), + maWindowTextColor( rData.maWindowTextColor ), + maWorkspaceColor( rData.maWorkspaceColor ), + maActiveTabColor( rData.maActiveTabColor ), + maInactiveTabColor( rData.maInactiveTabColor ), + maTabTextColor( rData.maTabTextColor ), + maTabRolloverTextColor( rData.maTabRolloverTextColor ), + maTabHighlightTextColor( rData.maTabHighlightTextColor ), + maAppFont( rData.maAppFont ), + maHelpFont( rData.maAppFont ), + maTitleFont( rData.maTitleFont ), + maFloatTitleFont( rData.maFloatTitleFont ), + maMenuFont( rData.maMenuFont ), + maToolFont( rData.maToolFont ), + maLabelFont( rData.maLabelFont ), + maRadioCheckFont( rData.maRadioCheckFont ), + maPushButtonFont( rData.maPushButtonFont ), + maFieldFont( rData.maFieldFont ), + maIconFont( rData.maIconFont ), + maTabFont( rData.maTabFont ), + maGroupFont( rData.maGroupFont ), + mnTitleHeight(rData.mnTitleHeight), + mnFloatTitleHeight(rData.mnFloatTitleHeight), + mnScrollBarSize(rData.mnScrollBarSize), + mnSpinSize(rData.mnSpinSize), + mnCursorSize(rData.mnCursorSize), + mnAntialiasedMin(rData.mnAntialiasedMin), + mnCursorBlinkTime(rData.mnCursorBlinkTime), + mnDragFullOptions(rData.mnDragFullOptions), + mnSelectionOptions(rData.mnSelectionOptions), + mnDisplayOptions(rData.mnDisplayOptions), + mnToolbarIconSize(rData.mnToolbarIconSize), + mnUseFlatMenus(rData.mnUseFlatMenus), + mnOptions(rData.mnOptions), + mbHighContrast(rData.mbHighContrast), + mbUseSystemUIFonts(rData.mbUseSystemUIFonts), + mbUseFontAAFromSystem(rData.mbUseFontAAFromSystem), + mbAutoMnemonic(rData.mbAutoMnemonic), + meUseImagesInMenus(rData.meUseImagesInMenus), + mnUseFlatBorders(rData.mnUseFlatBorders), + mbPreferredUseImagesInMenus(rData.mbPreferredUseImagesInMenus), + mnMinThumbSize(rData.mnMinThumbSize), + mIconThemeSelector(std::make_shared<vcl::IconThemeSelector>(*rData.mIconThemeSelector)), + mIconTheme(rData.mIconTheme), + mbSkipDisabledInMenus(rData.mbSkipDisabledInMenus), + mbHideDisabledMenuItems(rData.mbHideDisabledMenuItems), + mbPreferredContextMenuShortcuts(rData.mbPreferredContextMenuShortcuts), + meContextMenuShortcuts(rData.meContextMenuShortcuts), + mbPrimaryButtonWarpsSlider(rData.mbPrimaryButtonWarpsSlider), + maDialogStyle( rData.maDialogStyle ), + mnEdgeBlending(rData.mnEdgeBlending), + maEdgeBlendingTopLeftColor(rData.maEdgeBlendingTopLeftColor), + maEdgeBlendingBottomRightColor(rData.maEdgeBlendingBottomRightColor), + mnListBoxMaximumLineCount(rData.mnListBoxMaximumLineCount), + mnColorValueSetColumnCount(rData.mnColorValueSetColumnCount), + maListBoxPreviewDefaultLogicSize(rData.maListBoxPreviewDefaultLogicSize), + maListBoxPreviewDefaultPixelSize(rData.maListBoxPreviewDefaultPixelSize), + mbPreviewUsesCheckeredBackground(rData.mbPreviewUsesCheckeredBackground), + maPersonaHeaderFooter( rData.maPersonaHeaderFooter ), + maPersonaHeaderBitmap( rData.maPersonaHeaderBitmap ), + maPersonaFooterBitmap( rData.maPersonaFooterBitmap ), + maPersonaMenuBarTextColor( rData.maPersonaMenuBarTextColor ) +{ + if (rData.mIconThemeScanner) + mIconThemeScanner = std::make_shared<vcl::IconThemeScanner>(*rData.mIconThemeScanner); +} + +void ImplStyleData::SetStandardStyles() +{ + vcl::Font aStdFont( FAMILY_SWISS, Size( 0, 8 ) ); + aStdFont.SetCharSet( osl_getThreadTextEncoding() ); + aStdFont.SetWeight( WEIGHT_NORMAL ); + if (!utl::ConfigManager::IsFuzzing()) + aStdFont.SetFamilyName(utl::DefaultFontConfiguration::get().getUserInterfaceFont(LanguageTag("en"))); + else + aStdFont.SetFamilyName("Liberation Sans"); + maAppFont = aStdFont; + maHelpFont = aStdFont; + maMenuFont = aStdFont; + maToolFont = aStdFont; + maGroupFont = aStdFont; + maLabelFont = aStdFont; + maRadioCheckFont = aStdFont; + maPushButtonFont = aStdFont; + maFieldFont = aStdFont; + maIconFont = aStdFont; + maTabFont = aStdFont; + aStdFont.SetWeight( WEIGHT_BOLD ); + maFloatTitleFont = aStdFont; + maTitleFont = aStdFont; + + maFaceColor = COL_LIGHTGRAY; + maCheckedColor = Color( 0xCC, 0xCC, 0xCC ); + maLightColor = COL_WHITE; + maLightBorderColor = COL_LIGHTGRAY; + maShadowColor = COL_GRAY; + maDarkShadowColor = COL_BLACK; + + maDefaultButtonTextColor = COL_BLACK; + maButtonTextColor = COL_BLACK; + maDefaultActionButtonTextColor = COL_BLACK; + maActionButtonTextColor = COL_BLACK; + maFlatButtonTextColor = COL_BLACK; + maDefaultButtonRolloverTextColor = COL_BLACK; + maButtonRolloverTextColor = COL_BLACK; + maDefaultActionButtonRolloverTextColor = COL_BLACK; + maActionButtonRolloverTextColor = COL_BLACK; + maFlatButtonRolloverTextColor = COL_BLACK; + maDefaultButtonPressedRolloverTextColor = COL_BLACK; + maButtonPressedRolloverTextColor = COL_BLACK; + maDefaultActionButtonPressedRolloverTextColor = COL_BLACK; + maActionButtonPressedRolloverTextColor = COL_BLACK; + maFlatButtonPressedRolloverTextColor = COL_BLACK; + + maRadioCheckTextColor = COL_BLACK; + maGroupTextColor = COL_BLACK; + maLabelTextColor = COL_BLACK; + maWindowColor = COL_WHITE; + maWindowTextColor = COL_BLACK; + maDialogColor = COL_LIGHTGRAY; + maDialogTextColor = COL_BLACK; + maWorkspaceColor = Color( 0xDF, 0xDF, 0xDE ); + maMonoColor = COL_BLACK; + maFieldColor = COL_WHITE; + maFieldTextColor = COL_BLACK; + maFieldRolloverTextColor = COL_BLACK; + maActiveColor = COL_BLUE; + maActiveTextColor = COL_WHITE; + maActiveBorderColor = COL_LIGHTGRAY; + maDeactiveColor = COL_GRAY; + maDeactiveTextColor = COL_LIGHTGRAY; + maDeactiveBorderColor = COL_LIGHTGRAY; + maMenuColor = COL_LIGHTGRAY; + maMenuBarColor = COL_LIGHTGRAY; + maMenuBarRolloverColor = COL_BLUE; + maMenuBorderColor = COL_LIGHTGRAY; + maMenuTextColor = COL_BLACK; + maMenuBarTextColor = COL_BLACK; + maMenuBarRolloverTextColor = COL_WHITE; + maMenuBarHighlightTextColor = COL_WHITE; + maMenuHighlightColor = COL_BLUE; + maMenuHighlightTextColor = COL_WHITE; + maHighlightColor = COL_BLUE; + maHighlightTextColor = COL_WHITE; + maActiveTabColor = COL_WHITE; + maInactiveTabColor = COL_LIGHTGRAY; + maTabTextColor = COL_BLACK; + maTabRolloverTextColor = COL_BLACK; + maTabHighlightTextColor = COL_BLACK; + maDisableColor = COL_GRAY; + maHelpColor = Color( 0xFF, 0xFF, 0xE0 ); + maHelpTextColor = COL_BLACK; + maLinkColor = COL_BLUE; + maVisitedLinkColor = Color( 0x00, 0x00, 0xCC ); + maToolTextColor = COL_BLACK; + maHighlightLinkColor = COL_LIGHTBLUE; + maFontColor = COL_BLACK; + maAlternatingRowColor = Color( 0xEE, 0xEE, 0xEE ); + + mnTitleHeight = 18; + mnFloatTitleHeight = 13; + mbHighContrast = false; + mbUseSystemUIFonts = true; + mbUseFontAAFromSystem = true; + mnUseFlatBorders = false; + mnUseFlatMenus = false; + mbPreferredUseImagesInMenus = true; + mbSkipDisabledInMenus = false; + mbHideDisabledMenuItems = false; + mbPreferredContextMenuShortcuts = true; + mbPrimaryButtonWarpsSlider = false; +} + +StyleSettings::StyleSettings() + : mxData(std::make_shared<ImplStyleData>()) +{ +} + +void +StyleSettings::SetFaceColor( const Color& rColor ) +{ + CopyData(); + mxData->maFaceColor = rColor; +} + +const Color& +StyleSettings::GetFaceColor() const +{ + return mxData->maFaceColor; +} + +void +StyleSettings::SetCheckedColor( const Color& rColor ) +{ + CopyData(); + mxData->maCheckedColor = rColor; +} + +const Color& +StyleSettings::GetCheckedColor() const +{ + return mxData->maCheckedColor; +} + +void +StyleSettings::SetLightColor( const Color& rColor ) +{ + CopyData(); + mxData->maLightColor = rColor; +} + +const Color& +StyleSettings::GetLightColor() const +{ + return mxData->maLightColor; +} + +void +StyleSettings::SetLightBorderColor( const Color& rColor ) +{ + CopyData(); + mxData->maLightBorderColor = rColor; +} + +const Color& +StyleSettings::GetLightBorderColor() const +{ + return mxData->maLightBorderColor; +} + +void +StyleSettings::SetShadowColor( const Color& rColor ) +{ + CopyData(); + mxData->maShadowColor = rColor; +} + +const Color& +StyleSettings::GetShadowColor() const +{ + return mxData->maShadowColor; +} + +void +StyleSettings::SetDarkShadowColor( const Color& rColor ) +{ + CopyData(); + mxData->maDarkShadowColor = rColor; +} + +const Color& +StyleSettings::GetDarkShadowColor() const +{ + return mxData->maDarkShadowColor; +} + +void +StyleSettings::SetDefaultButtonTextColor( const Color& rColor ) +{ + CopyData(); + mxData->maDefaultButtonTextColor = rColor; +} + +const Color& +StyleSettings::GetDefaultButtonTextColor() const +{ + return mxData->maDefaultButtonTextColor; +} + +void +StyleSettings::SetButtonTextColor( const Color& rColor ) +{ + CopyData(); + mxData->maButtonTextColor = rColor; +} + +const Color& +StyleSettings::GetButtonTextColor() const +{ + return mxData->maButtonTextColor; +} + +void +StyleSettings::SetDefaultActionButtonTextColor( const Color& rColor ) +{ + CopyData(); + mxData->maDefaultActionButtonTextColor = rColor; +} + +const Color& +StyleSettings::GetDefaultActionButtonTextColor() const +{ + return mxData->maDefaultActionButtonTextColor; +} + +void +StyleSettings::SetActionButtonTextColor( const Color& rColor ) +{ + CopyData(); + mxData->maActionButtonTextColor = rColor; +} + +const Color& +StyleSettings::GetActionButtonTextColor() const +{ + return mxData->maActionButtonTextColor; +} + +void +StyleSettings::SetFlatButtonTextColor( const Color& rColor ) +{ + CopyData(); + mxData->maFlatButtonTextColor = rColor; +} + +const Color& +StyleSettings::GetFlatButtonTextColor() const +{ + return mxData->maFlatButtonTextColor; +} + +void +StyleSettings::SetDefaultButtonRolloverTextColor( const Color& rColor ) +{ + CopyData(); + mxData->maDefaultButtonRolloverTextColor = rColor; +} + +const Color& +StyleSettings::GetDefaultButtonRolloverTextColor() const +{ + return mxData->maDefaultButtonRolloverTextColor; +} + +void +StyleSettings::SetButtonRolloverTextColor( const Color& rColor ) +{ + CopyData(); + mxData->maButtonRolloverTextColor = rColor; +} + +const Color& +StyleSettings::GetButtonRolloverTextColor() const +{ + return mxData->maButtonRolloverTextColor; +} + +void +StyleSettings::SetDefaultActionButtonRolloverTextColor( const Color& rColor ) +{ + CopyData(); + mxData->maDefaultActionButtonRolloverTextColor = rColor; +} + +const Color& +StyleSettings::GetDefaultActionButtonRolloverTextColor() const +{ + return mxData->maDefaultActionButtonRolloverTextColor; +} + +void +StyleSettings::SetActionButtonRolloverTextColor( const Color& rColor ) +{ + CopyData(); + mxData->maActionButtonRolloverTextColor = rColor; +} + +const Color& +StyleSettings::GetActionButtonRolloverTextColor() const +{ + return mxData->maActionButtonRolloverTextColor; +} + +void +StyleSettings::SetFlatButtonRolloverTextColor( const Color& rColor ) +{ + CopyData(); + mxData->maFlatButtonRolloverTextColor = rColor; +} + +const Color& +StyleSettings::GetFlatButtonRolloverTextColor() const +{ + return mxData->maFlatButtonRolloverTextColor; +} + +void +StyleSettings::SetDefaultButtonPressedRolloverTextColor( const Color& rColor ) +{ + CopyData(); + mxData->maDefaultButtonPressedRolloverTextColor = rColor; +} + +const Color& +StyleSettings::GetDefaultButtonPressedRolloverTextColor() const +{ + return mxData->maDefaultButtonPressedRolloverTextColor; +} + +void +StyleSettings::SetButtonPressedRolloverTextColor( const Color& rColor ) +{ + CopyData(); + mxData->maButtonPressedRolloverTextColor = rColor; +} + +const Color& +StyleSettings::GetButtonPressedRolloverTextColor() const +{ + return mxData->maButtonPressedRolloverTextColor; +} + +void +StyleSettings::SetDefaultActionButtonPressedRolloverTextColor( const Color& rColor ) +{ + CopyData(); + mxData->maDefaultActionButtonPressedRolloverTextColor = rColor; +} + +const Color& +StyleSettings::GetDefaultActionButtonPressedRolloverTextColor() const +{ + return mxData->maDefaultActionButtonPressedRolloverTextColor; +} + +void +StyleSettings::SetActionButtonPressedRolloverTextColor( const Color& rColor ) +{ + CopyData(); + mxData->maActionButtonPressedRolloverTextColor = rColor; +} + +const Color& +StyleSettings::GetActionButtonPressedRolloverTextColor() const +{ + return mxData->maActionButtonPressedRolloverTextColor; +} + +void +StyleSettings::SetFlatButtonPressedRolloverTextColor( const Color& rColor ) +{ + CopyData(); + mxData->maFlatButtonPressedRolloverTextColor = rColor; +} + +const Color& +StyleSettings::GetFlatButtonPressedRolloverTextColor() const +{ + return mxData->maFlatButtonPressedRolloverTextColor; +} + +void +StyleSettings::SetRadioCheckTextColor( const Color& rColor ) +{ + CopyData(); + mxData->maRadioCheckTextColor = rColor; +} + +const Color& +StyleSettings::GetRadioCheckTextColor() const +{ + return mxData->maRadioCheckTextColor; +} + +void +StyleSettings::SetGroupTextColor( const Color& rColor ) +{ + CopyData(); + mxData->maGroupTextColor = rColor; +} + +const Color& +StyleSettings::GetGroupTextColor() const +{ + return mxData->maGroupTextColor; +} + +void +StyleSettings::SetLabelTextColor( const Color& rColor ) +{ + CopyData(); + mxData->maLabelTextColor = rColor; +} + +const Color& +StyleSettings::GetLabelTextColor() const +{ + return mxData->maLabelTextColor; +} + +void +StyleSettings::SetWindowColor( const Color& rColor ) +{ + CopyData(); + mxData->maWindowColor = rColor; +} + +const Color& +StyleSettings::GetWindowColor() const +{ + return mxData->maWindowColor; +} + +void +StyleSettings::SetWindowTextColor( const Color& rColor ) +{ + CopyData(); + mxData->maWindowTextColor = rColor; +} + +const Color& +StyleSettings::GetWindowTextColor() const +{ + return mxData->maWindowTextColor; +} + +void +StyleSettings::SetDialogColor( const Color& rColor ) +{ + CopyData(); + mxData->maDialogColor = rColor; +} + +const Color& +StyleSettings::GetDialogColor() const +{ + return mxData->maDialogColor; +} + +void +StyleSettings::SetDialogTextColor( const Color& rColor ) +{ + CopyData(); + mxData->maDialogTextColor = rColor; +} + +const Color& +StyleSettings::GetDialogTextColor() const +{ + return mxData->maDialogTextColor; +} + +void +StyleSettings::SetWorkspaceColor( const Color& rColor ) +{ + CopyData(); + mxData->maWorkspaceColor = rColor; +} + +const Color& +StyleSettings::GetWorkspaceColor() const +{ + return mxData->maWorkspaceColor; +} + +void +StyleSettings::SetFieldColor( const Color& rColor ) +{ + CopyData(); + mxData->maFieldColor = rColor; +} + +const Color& +StyleSettings::GetFieldColor() const +{ + return mxData->maFieldColor; +} + +void +StyleSettings::SetFieldTextColor( const Color& rColor ) +{ + CopyData(); + mxData->maFieldTextColor = rColor; +} + +const Color& +StyleSettings::GetFieldTextColor() const +{ + return mxData->maFieldTextColor; +} + +void +StyleSettings::SetFieldRolloverTextColor( const Color& rColor ) +{ + CopyData(); + mxData->maFieldRolloverTextColor = rColor; +} + +const Color& +StyleSettings::GetFieldRolloverTextColor() const +{ + return mxData->maFieldRolloverTextColor; +} + +void +StyleSettings::SetActiveColor( const Color& rColor ) +{ + CopyData(); + mxData->maActiveColor = rColor; +} + +const Color& +StyleSettings::GetActiveColor() const +{ + return mxData->maActiveColor; +} + +void +StyleSettings::SetActiveTextColor( const Color& rColor ) +{ + CopyData(); + mxData->maActiveTextColor = rColor; +} + +const Color& +StyleSettings::GetActiveTextColor() const +{ + return mxData->maActiveTextColor; +} + +void +StyleSettings::SetActiveBorderColor( const Color& rColor ) +{ + CopyData(); + mxData->maActiveBorderColor = rColor; +} + +const Color& +StyleSettings::GetActiveBorderColor() const +{ + return mxData->maActiveBorderColor; +} + +void +StyleSettings::SetDeactiveColor( const Color& rColor ) +{ + CopyData(); + mxData->maDeactiveColor = rColor; +} + +const Color& +StyleSettings::GetDeactiveColor() const +{ + return mxData->maDeactiveColor; +} + +void +StyleSettings::SetDeactiveTextColor( const Color& rColor ) +{ + CopyData(); + mxData->maDeactiveTextColor = rColor; +} + +const Color& +StyleSettings::GetDeactiveTextColor() const +{ + return mxData->maDeactiveTextColor; +} + +void +StyleSettings::SetDeactiveBorderColor( const Color& rColor ) +{ + CopyData(); + mxData->maDeactiveBorderColor = rColor; +} + +const Color& +StyleSettings::GetDeactiveBorderColor() const +{ + return mxData->maDeactiveBorderColor; +} + +void +StyleSettings::SetHighlightColor( const Color& rColor ) +{ + CopyData(); + mxData->maHighlightColor = rColor; +} + +const Color& +StyleSettings::GetHighlightColor() const +{ + return mxData->maHighlightColor; +} + +void +StyleSettings::SetHighlightTextColor( const Color& rColor ) +{ + CopyData(); + mxData->maHighlightTextColor = rColor; +} + +const Color& +StyleSettings::GetHighlightTextColor() const +{ + return mxData->maHighlightTextColor; +} + +void +StyleSettings::SetDisableColor( const Color& rColor ) +{ + CopyData(); + mxData->maDisableColor = rColor; +} + +const Color& +StyleSettings::GetDisableColor() const +{ + return mxData->maDisableColor; +} + +void +StyleSettings::SetHelpColor( const Color& rColor ) +{ + CopyData(); + mxData->maHelpColor = rColor; +} + +const Color& +StyleSettings::GetHelpColor() const +{ + return mxData->maHelpColor; +} + +void +StyleSettings::SetHelpTextColor( const Color& rColor ) +{ + CopyData(); + mxData->maHelpTextColor = rColor; +} + +const Color& +StyleSettings::GetHelpTextColor() const +{ + return mxData->maHelpTextColor; +} + +void +StyleSettings::SetMenuColor( const Color& rColor ) +{ + CopyData(); + mxData->maMenuColor = rColor; +} + +const Color& +StyleSettings::GetMenuColor() const +{ + return mxData->maMenuColor; +} + +void +StyleSettings::SetMenuBarColor( const Color& rColor ) +{ + CopyData(); + mxData->maMenuBarColor = rColor; +} + +const Color& +StyleSettings::GetMenuBarColor() const +{ + return mxData->maMenuBarColor; +} + +void +StyleSettings::SetMenuBarRolloverColor( const Color& rColor ) +{ + CopyData(); + mxData->maMenuBarRolloverColor = rColor; +} + +const Color& +StyleSettings::GetMenuBarRolloverColor() const +{ + return mxData->maMenuBarRolloverColor; +} + +void +StyleSettings::SetMenuBorderColor( const Color& rColor ) +{ + CopyData(); + mxData->maMenuBorderColor = rColor; +} + +const Color& +StyleSettings::GetMenuBorderColor() const +{ + return mxData->maMenuBorderColor; +} + +void +StyleSettings::SetMenuTextColor( const Color& rColor ) +{ + CopyData(); + mxData->maMenuTextColor = rColor; +} + +const Color& +StyleSettings::GetMenuTextColor() const +{ + return mxData->maMenuTextColor; +} + +void +StyleSettings::SetMenuBarTextColor( const Color& rColor ) +{ + CopyData(); + mxData->maMenuBarTextColor = rColor; +} + +const Color& +StyleSettings::GetMenuBarTextColor() const +{ + return mxData->maMenuBarTextColor; +} + +void +StyleSettings::SetMenuBarRolloverTextColor( const Color& rColor ) +{ + CopyData(); + mxData->maMenuBarRolloverTextColor = rColor; +} + +const Color& +StyleSettings::GetMenuBarRolloverTextColor() const +{ + return mxData->maMenuBarRolloverTextColor; +} + +void +StyleSettings::SetMenuBarHighlightTextColor( const Color& rColor ) +{ + CopyData(); + mxData->maMenuBarHighlightTextColor = rColor; +} + +const Color& +StyleSettings::GetMenuBarHighlightTextColor() const +{ + return mxData->maMenuBarHighlightTextColor; +} + +void +StyleSettings::SetMenuHighlightColor( const Color& rColor ) +{ + CopyData(); + mxData->maMenuHighlightColor = rColor; +} + +const Color& +StyleSettings::GetMenuHighlightColor() const +{ + return mxData->maMenuHighlightColor; +} + +void +StyleSettings::SetMenuHighlightTextColor( const Color& rColor ) +{ + CopyData(); + mxData->maMenuHighlightTextColor = rColor; +} + +const Color& +StyleSettings::GetMenuHighlightTextColor() const +{ + return mxData->maMenuHighlightTextColor; +} + +void +StyleSettings::SetTabTextColor( const Color& rColor ) +{ + CopyData(); + mxData->maTabTextColor = rColor; +} + +const Color& +StyleSettings::GetTabTextColor() const +{ + return mxData->maTabTextColor; +} + +void +StyleSettings::SetTabRolloverTextColor( const Color& rColor ) +{ + CopyData(); + mxData->maTabRolloverTextColor = rColor; +} + +const Color& +StyleSettings::GetTabRolloverTextColor() const +{ + return mxData->maTabRolloverTextColor; +} + +void +StyleSettings::SetTabHighlightTextColor( const Color& rColor ) +{ + CopyData(); + mxData->maTabHighlightTextColor = rColor; +} + +const Color& +StyleSettings::GetTabHighlightTextColor() const +{ + return mxData->maTabHighlightTextColor; +} + +void +StyleSettings::SetLinkColor( const Color& rColor ) +{ + CopyData(); + mxData->maLinkColor = rColor; +} + +const Color& +StyleSettings::GetLinkColor() const +{ + return mxData->maLinkColor; +} + +void +StyleSettings::SetVisitedLinkColor( const Color& rColor ) +{ + CopyData(); + mxData->maVisitedLinkColor = rColor; +} + +const Color& +StyleSettings::GetVisitedLinkColor() const +{ + return mxData->maVisitedLinkColor; +} + +void +StyleSettings::SetToolTextColor( const Color& rColor ) +{ + CopyData(); + mxData->maToolTextColor = rColor; +} + +const Color& +StyleSettings::GetToolTextColor() const +{ + return mxData->maToolTextColor; +} + +const Color& +StyleSettings::GetHighlightLinkColor() const +{ + return mxData->maHighlightLinkColor; +} + +void +StyleSettings::SetMonoColor( const Color& rColor ) +{ + CopyData(); + mxData->maMonoColor = rColor; +} + +const Color& +StyleSettings::GetMonoColor() const +{ + return mxData->maMonoColor; +} + +void +StyleSettings::SetActiveTabColor( const Color& rColor ) +{ + CopyData(); + mxData->maActiveTabColor = rColor; +} + +const Color& +StyleSettings::GetActiveTabColor() const +{ + return mxData->maActiveTabColor; +} + +void +StyleSettings::SetInactiveTabColor( const Color& rColor ) +{ + CopyData(); + mxData->maInactiveTabColor = rColor; +} + +const Color& +StyleSettings::GetInactiveTabColor() const +{ + return mxData->maInactiveTabColor; +} + +void StyleSettings::SetAlternatingRowColor(const Color& rColor) +{ + CopyData(); + mxData->maAlternatingRowColor = rColor; +} + +const Color& +StyleSettings::GetAlternatingRowColor() const +{ + return mxData->maAlternatingRowColor; +} + +void +StyleSettings::SetUseSystemUIFonts( bool bUseSystemUIFonts ) +{ + CopyData(); + mxData->mbUseSystemUIFonts = bUseSystemUIFonts; +} + +bool +StyleSettings::GetUseSystemUIFonts() const +{ + return mxData->mbUseSystemUIFonts; +} + +void StyleSettings::SetUseFontAAFromSystem(bool bUseFontAAFromSystem) +{ + CopyData(); + mxData->mbUseFontAAFromSystem = bUseFontAAFromSystem; +} + +bool StyleSettings::GetUseFontAAFromSystem() const +{ + return mxData->mbUseFontAAFromSystem; +} + +void +StyleSettings::SetUseFlatBorders( bool bUseFlatBorders ) +{ + CopyData(); + mxData->mnUseFlatBorders = bUseFlatBorders; +} + +bool +StyleSettings::GetUseFlatBorders() const +{ + return mxData->mnUseFlatBorders; +} + +void +StyleSettings::SetUseFlatMenus( bool bUseFlatMenus ) +{ + CopyData(); + mxData->mnUseFlatMenus = bUseFlatMenus; +} + +bool +StyleSettings::GetUseFlatMenus() const +{ + return mxData->mnUseFlatMenus; +} + +void +StyleSettings::SetUseImagesInMenus( TriState eUseImagesInMenus ) +{ + CopyData(); + mxData->meUseImagesInMenus = eUseImagesInMenus; +} + +void +StyleSettings::SetPreferredUseImagesInMenus( bool bPreferredUseImagesInMenus ) +{ + CopyData(); + mxData->mbPreferredUseImagesInMenus = bPreferredUseImagesInMenus; +} + +bool +StyleSettings::GetPreferredUseImagesInMenus() const +{ + return mxData->mbPreferredUseImagesInMenus; +} + +void +StyleSettings::SetSkipDisabledInMenus( bool bSkipDisabledInMenus ) +{ + CopyData(); + mxData->mbSkipDisabledInMenus = bSkipDisabledInMenus; +} + +bool +StyleSettings::GetSkipDisabledInMenus() const +{ + return mxData->mbSkipDisabledInMenus; +} + +void +StyleSettings::SetHideDisabledMenuItems( bool bHideDisabledMenuItems ) +{ + CopyData(); + mxData->mbHideDisabledMenuItems = bHideDisabledMenuItems; +} + +bool +StyleSettings::GetHideDisabledMenuItems() const +{ + return mxData->mbHideDisabledMenuItems; +} + +void +StyleSettings::SetContextMenuShortcuts( TriState eContextMenuShortcuts ) +{ + CopyData(); + mxData->meContextMenuShortcuts = eContextMenuShortcuts; +} + +bool +StyleSettings::GetContextMenuShortcuts() const +{ + switch (mxData->meContextMenuShortcuts) + { + case TRISTATE_FALSE: + return false; + case TRISTATE_TRUE: + return true; + default: // TRISTATE_INDET: + return GetPreferredContextMenuShortcuts(); + } +} + +void +StyleSettings::SetPreferredContextMenuShortcuts( bool bContextMenuShortcuts ) +{ + CopyData(); + mxData->mbPreferredContextMenuShortcuts = bContextMenuShortcuts; +} + +bool +StyleSettings::GetPreferredContextMenuShortcuts() const +{ + return mxData->mbPreferredContextMenuShortcuts; +} + +void +StyleSettings::SetPrimaryButtonWarpsSlider( bool bPrimaryButtonWarpsSlider ) +{ + CopyData(); + mxData->mbPrimaryButtonWarpsSlider = bPrimaryButtonWarpsSlider; +} + +bool +StyleSettings::GetPrimaryButtonWarpsSlider() const +{ + return mxData->mbPrimaryButtonWarpsSlider; +} + +void +StyleSettings::SetAppFont( const vcl::Font& rFont ) +{ + CopyData(); + mxData->maAppFont = rFont; +} + +const vcl::Font& +StyleSettings::GetAppFont() const +{ + return mxData->maAppFont; +} + +void +StyleSettings::SetHelpFont( const vcl::Font& rFont ) +{ + CopyData(); + mxData->maHelpFont = rFont; +} + +const vcl::Font& +StyleSettings::GetHelpFont() const +{ + return mxData->maHelpFont; +} + +void +StyleSettings::SetTitleFont( const vcl::Font& rFont ) +{ + CopyData(); + mxData->maTitleFont = rFont; +} + +const vcl::Font& +StyleSettings::GetTitleFont() const +{ + return mxData->maTitleFont; +} + +void +StyleSettings::SetFloatTitleFont( const vcl::Font& rFont ) +{ + CopyData(); + mxData->maFloatTitleFont = rFont; +} + +const vcl::Font& +StyleSettings::GetFloatTitleFont() const +{ + return mxData->maFloatTitleFont; +} + +void +StyleSettings::SetMenuFont( const vcl::Font& rFont ) +{ + CopyData(); + mxData->maMenuFont = rFont; +} + +const vcl::Font& +StyleSettings::GetMenuFont() const +{ + return mxData->maMenuFont; +} + +void +StyleSettings::SetToolFont( const vcl::Font& rFont ) +{ + CopyData(); + mxData->maToolFont = rFont; +} + +const vcl::Font& +StyleSettings::GetToolFont() const +{ + return mxData->maToolFont; +} + +void +StyleSettings::SetGroupFont( const vcl::Font& rFont ) +{ + CopyData(); + mxData->maGroupFont = rFont; +} + +const vcl::Font& +StyleSettings::GetGroupFont() const +{ + return mxData->maGroupFont; +} + +void +StyleSettings::SetLabelFont( const vcl::Font& rFont ) +{ + CopyData(); + mxData->maLabelFont = rFont; +} + +const vcl::Font& +StyleSettings::GetLabelFont() const +{ + return mxData->maLabelFont; +} + +void +StyleSettings::SetRadioCheckFont( const vcl::Font& rFont ) +{ + CopyData(); + mxData->maRadioCheckFont = rFont; +} + +const vcl::Font& +StyleSettings::GetRadioCheckFont() const +{ + return mxData->maRadioCheckFont; +} + +void +StyleSettings::SetPushButtonFont( const vcl::Font& rFont ) +{ + CopyData(); + mxData->maPushButtonFont = rFont; +} + +const vcl::Font& +StyleSettings::GetPushButtonFont() const +{ + return mxData->maPushButtonFont; +} + +void +StyleSettings::SetFieldFont( const vcl::Font& rFont ) +{ + CopyData(); + mxData->maFieldFont = rFont; +} + +const vcl::Font& +StyleSettings::GetFieldFont() const +{ + return mxData->maFieldFont; +} + +void +StyleSettings::SetIconFont( const vcl::Font& rFont ) +{ + CopyData(); + mxData->maIconFont = rFont; +} + +const vcl::Font& +StyleSettings::GetIconFont() const +{ + return mxData->maIconFont; +} + +void +StyleSettings::SetTabFont( const vcl::Font& rFont ) +{ + CopyData(); + mxData->maTabFont = rFont; +} + +const vcl::Font& +StyleSettings::GetTabFont() const +{ + return mxData->maTabFont; +} + +long +StyleSettings::GetBorderSize() +{ + return 1; +} + +void +StyleSettings::SetTitleHeight( long nSize ) +{ + CopyData(); + mxData->mnTitleHeight = nSize; +} + +long +StyleSettings::GetTitleHeight() const +{ + return mxData->mnTitleHeight; +} + +void +StyleSettings::SetFloatTitleHeight( long nSize ) +{ + CopyData(); + mxData->mnFloatTitleHeight = nSize; +} + +long +StyleSettings::GetFloatTitleHeight() const +{ + return mxData->mnFloatTitleHeight; +} + +void +StyleSettings::SetScrollBarSize( long nSize ) +{ + CopyData(); + mxData->mnScrollBarSize = nSize; +} + +long +StyleSettings::GetScrollBarSize() const +{ + return mxData->mnScrollBarSize; +} + +void +StyleSettings::SetMinThumbSize( long nSize ) +{ + CopyData(); + mxData->mnMinThumbSize = nSize; +} + +long +StyleSettings::GetMinThumbSize() const +{ + return mxData->mnMinThumbSize; +} + +void +StyleSettings::SetSpinSize( long nSize ) +{ + CopyData(); + mxData->mnSpinSize = nSize; +} + +long +StyleSettings::GetSpinSize() const +{ + return mxData->mnSpinSize; +} + +long +StyleSettings::GetSplitSize() +{ + return 3; +} + +void +StyleSettings::SetCursorSize( long nSize ) +{ + CopyData(); + mxData->mnCursorSize = nSize; +} + +long +StyleSettings::GetCursorSize() const +{ + return mxData->mnCursorSize; +} + +void +StyleSettings::SetCursorBlinkTime( sal_uInt64 nBlinkTime ) +{ + CopyData(); + mxData->mnCursorBlinkTime = nBlinkTime; +} + +sal_uInt64 +StyleSettings::GetCursorBlinkTime() const +{ + return static_cast<long>(mxData->mnCursorBlinkTime); +} + +void +StyleSettings::SetDragFullOptions( DragFullOptions nOptions ) +{ + CopyData(); + mxData->mnDragFullOptions = nOptions; +} + +DragFullOptions +StyleSettings::GetDragFullOptions() const +{ + return mxData->mnDragFullOptions; +} + +void +StyleSettings::SetSelectionOptions( SelectionOptions nOptions ) +{ + CopyData(); + mxData->mnSelectionOptions = nOptions; +} + +SelectionOptions +StyleSettings::GetSelectionOptions() const +{ + return mxData->mnSelectionOptions; +} + +void +StyleSettings::SetDisplayOptions( DisplayOptions nOptions ) +{ + CopyData(); + mxData->mnDisplayOptions = nOptions; +} + +DisplayOptions +StyleSettings::GetDisplayOptions() const +{ + return mxData->mnDisplayOptions; +} + +void +StyleSettings::SetAntialiasingMinPixelHeight( long nMinPixel ) +{ + CopyData(); + mxData->mnAntialiasedMin = nMinPixel; +} + +sal_uLong +StyleSettings::GetAntialiasingMinPixelHeight() const +{ + return mxData->mnAntialiasedMin; +} + +void +StyleSettings::SetOptions( StyleSettingsOptions nOptions ) +{ + CopyData(); + mxData->mnOptions = nOptions; +} + +void +StyleSettings::SetAutoMnemonic( bool bAutoMnemonic ) +{ + CopyData(); + mxData->mbAutoMnemonic = bAutoMnemonic; +} + +bool +StyleSettings::GetAutoMnemonic() const +{ + return mxData->mbAutoMnemonic; +} + +bool +StyleSettings::GetDockingFloatsSupported() +{ + ImplSVData* pSVData = ImplGetSVData(); + return pSVData->maNWFData.mbCanDetermineWindowPosition; +} + +void +StyleSettings::SetFontColor( const Color& rColor ) +{ + CopyData(); + mxData->maFontColor = rColor; +} + +const Color& +StyleSettings::GetFontColor() const +{ + return mxData->maFontColor; +} + +void +StyleSettings::SetToolbarIconSize( ToolbarIconSize nSize ) +{ + CopyData(); + mxData->mnToolbarIconSize = nSize; +} + +ToolbarIconSize +StyleSettings::GetToolbarIconSize() const +{ + return mxData->mnToolbarIconSize; +} + +const DialogStyle& +StyleSettings::GetDialogStyle() const +{ + return mxData->maDialogStyle; +} + +void +StyleSettings::SetEdgeBlending(sal_uInt16 nCount) +{ + CopyData(); + mxData->mnEdgeBlending = nCount; +} + +sal_uInt16 +StyleSettings::GetEdgeBlending() const +{ + return mxData->mnEdgeBlending; +} + +const Color& +StyleSettings::GetEdgeBlendingTopLeftColor() const +{ + return mxData->maEdgeBlendingTopLeftColor; +} + +const Color& +StyleSettings::GetEdgeBlendingBottomRightColor() const +{ + return mxData->maEdgeBlendingBottomRightColor; +} + +void +StyleSettings::SetListBoxMaximumLineCount(sal_uInt16 nCount) +{ + CopyData(); + mxData->mnListBoxMaximumLineCount = nCount; +} + +sal_uInt16 +StyleSettings::GetListBoxMaximumLineCount() const +{ + return mxData->mnListBoxMaximumLineCount; +} + +void +StyleSettings::SetColorValueSetColumnCount(sal_uInt16 nCount) +{ + CopyData(); + mxData->mnColorValueSetColumnCount = nCount; +} + +sal_uInt16 +StyleSettings::GetColorValueSetColumnCount() const +{ + return mxData->mnColorValueSetColumnCount; +} + +sal_uInt16 +StyleSettings::GetColorValueSetMaximumRowCount() +{ + return 10; +} + +sal_uInt16 +StyleSettings::GetListBoxPreviewDefaultLineWidth() +{ + return 1; +} + +void +StyleSettings::SetPreviewUsesCheckeredBackground(bool bNew) +{ + CopyData(); + mxData->mbPreviewUsesCheckeredBackground = bNew; +} + +bool +StyleSettings::GetPreviewUsesCheckeredBackground() const +{ + return mxData->mbPreviewUsesCheckeredBackground; +} + +bool +StyleSettings::operator !=( const StyleSettings& rSet ) const +{ + return !(*this == rSet); +} + +void StyleSettings::SetListBoxPreviewDefaultLogicSize(Size const& rSize) +{ + mxData->maListBoxPreviewDefaultLogicSize = rSize; +} + +const Size& StyleSettings::GetListBoxPreviewDefaultPixelSize() const +{ + if(0 == mxData->maListBoxPreviewDefaultPixelSize.Width() || 0 == mxData->maListBoxPreviewDefaultPixelSize.Height()) + { + const_cast< StyleSettings* >(this)->mxData->maListBoxPreviewDefaultPixelSize = + Application::GetDefaultDevice()->LogicToPixel(mxData->maListBoxPreviewDefaultLogicSize, MapMode(MapUnit::MapAppFont)); + } + + return mxData->maListBoxPreviewDefaultPixelSize; +} + +void StyleSettings::Set3DColors( const Color& rColor ) +{ + CopyData(); + mxData->maFaceColor = rColor; + mxData->maLightBorderColor = rColor; + mxData->maMenuBorderColor = rColor; + mxData->maDarkShadowColor = COL_BLACK; + if ( rColor != COL_LIGHTGRAY ) + { + mxData->maLightColor = rColor; + mxData->maShadowColor = rColor; + mxData->maDarkShadowColor=rColor; + mxData->maLightColor.IncreaseLuminance( 64 ); + mxData->maShadowColor.DecreaseLuminance( 64 ); + mxData->maDarkShadowColor.DecreaseLuminance( 100 ); + sal_uLong nRed = mxData->maLightColor.GetRed(); + sal_uLong nGreen = mxData->maLightColor.GetGreen(); + sal_uLong nBlue = mxData->maLightColor.GetBlue(); + nRed += static_cast<sal_uLong>(mxData->maShadowColor.GetRed()); + nGreen += static_cast<sal_uLong>(mxData->maShadowColor.GetGreen()); + nBlue += static_cast<sal_uLong>(mxData->maShadowColor.GetBlue()); + mxData->maCheckedColor = Color( static_cast<sal_uInt8>(nRed/2), static_cast<sal_uInt8>(nGreen/2), static_cast<sal_uInt8>(nBlue/2) ); + } + else + { + mxData->maCheckedColor = Color( 0x99, 0x99, 0x99 ); + mxData->maLightColor = COL_WHITE; + mxData->maShadowColor = COL_GRAY; + } +} + +void StyleSettings::SetCheckedColorSpecialCase( ) +{ + CopyData(); + // Light gray checked color special case + if ( GetFaceColor() == COL_LIGHTGRAY ) + mxData->maCheckedColor = Color( 0xCC, 0xCC, 0xCC ); + else + { + sal_uInt8 nRed = static_cast<sal_uInt8>((static_cast<sal_uInt16>(mxData->maFaceColor.GetRed()) + static_cast<sal_uInt16>(mxData->maLightColor.GetRed()))/2); + sal_uInt8 nGreen = static_cast<sal_uInt8>((static_cast<sal_uInt16>(mxData->maFaceColor.GetGreen()) + static_cast<sal_uInt16>(mxData->maLightColor.GetGreen()))/2); + sal_uInt8 nBlue = static_cast<sal_uInt8>((static_cast<sal_uInt16>(mxData->maFaceColor.GetBlue()) + static_cast<sal_uInt16>(mxData->maLightColor.GetBlue()))/2); + mxData->maCheckedColor = Color( nRed, nGreen, nBlue ); + } +} + +bool StyleSettings::GetUseImagesInMenus() const +{ + // icon mode selected in Tools -> Options... -> OpenOffice.org -> View + switch (mxData->meUseImagesInMenus) { + case TRISTATE_FALSE: + return false; + case TRISTATE_TRUE: + return true; + default: // TRISTATE_INDET: + return GetPreferredUseImagesInMenus(); + } +} + +static BitmapEx readBitmapEx( const OUString& rPath ) +{ + OUString aPath( rPath ); + rtl::Bootstrap::expandMacros( aPath ); + + // import the image + Graphic aGraphic; + if ( GraphicFilter::LoadGraphic( aPath, OUString(), aGraphic ) != ERRCODE_NONE ) + return BitmapEx(); + return aGraphic.GetBitmapEx(); +} + +namespace { + +enum WhichPersona { PERSONA_HEADER, PERSONA_FOOTER }; + +} + +/** Update the setting of the Persona header / footer in ImplStyleData */ +static void setupPersonaHeaderFooter( WhichPersona eWhich, OUString& rHeaderFooter, BitmapEx& rHeaderFooterBitmap, std::optional<Color>& rMenuBarTextColor ) +{ + uno::Reference< uno::XComponentContext > xContext( comphelper::getProcessComponentContext() ); + if ( !xContext.is() ) + return; + + // don't burn time loading images we don't need. + if ( Application::IsHeadlessModeEnabled() ) + return; + + // read from the configuration + OUString aPersona( officecfg::Office::Common::Misc::Persona::get( xContext ) ); + OUString aPersonaSettings( officecfg::Office::Common::Misc::PersonaSettings::get( xContext ) ); + + // have the settings changed? marks if header /footer prepared before + //should maybe extended to a flag that marks if header /footer /both are loaded + OUString aOldValue= eWhich==PERSONA_HEADER?OUString(aPersona + ";" + aPersonaSettings+";h" ):OUString(aPersona + ";" + aPersonaSettings+";f" ); + if ( rHeaderFooter == aOldValue ) + return; + + rHeaderFooter = aOldValue; + rHeaderFooterBitmap = BitmapEx(); + rMenuBarTextColor.reset(); + + // now read the new values and setup bitmaps + OUString aHeader, aFooter; + if ( aPersona == "own" || aPersona == "default" ) + { + sal_Int32 nIndex = 0; + + // Skip the persona slug, name, and preview + aHeader = aPersonaSettings.getToken( 3, ';', nIndex ); + + if ( nIndex > 0 ) + aFooter = aPersonaSettings.getToken( 0, ';', nIndex ); + + // change menu text color, advance nIndex to skip the '#' + if ( nIndex > 0 ) + { + OUString aColor = aPersonaSettings.getToken( 0, ';', ++nIndex ); + rMenuBarTextColor = Color( aColor.toUInt32( 16 ) ); + } + } + + OUString aName; + switch ( eWhich ) { + case PERSONA_HEADER: aName = aHeader; break; + case PERSONA_FOOTER: aName = aFooter; break; + } + + if ( !aName.isEmpty() ) + { + OUString gallery(""); + // try the gallery first, then the program path: + if ( aPersona == "own" && !aPersonaSettings.startsWith( "vnd.sun.star.expand" ) ) + { + gallery = "${$BRAND_BASE_DIR/" LIBO_ETC_FOLDER "/" SAL_CONFIGFILE( "bootstrap") "::UserInstallation}"; + rtl::Bootstrap::expandMacros( gallery ); + gallery += "/user/gallery/personas/"; + } + else if ( aPersona == "default" ) + { + gallery = "$BRAND_BASE_DIR/" LIBO_SHARE_FOLDER "/gallery/personas/"; + } + rHeaderFooterBitmap = readBitmapEx( gallery + aName ); + + if ( rHeaderFooterBitmap.IsEmpty() ) + rHeaderFooterBitmap = readBitmapEx( "$BRAND_BASE_DIR/" LIBO_ETC_FOLDER "/" + aName ); + } + + // Something went wrong. Probably, the images are missing. Clear the persona properties in the registry. + + if( rHeaderFooterBitmap.IsEmpty() ) + { + std::shared_ptr<comphelper::ConfigurationChanges> batch(comphelper::ConfigurationChanges::create()); + officecfg::Office::Common::Misc::Persona::set( "no", batch ); + officecfg::Office::Common::Misc::PersonaSettings::set( "", batch ); + batch->commit(); + } +} + +BitmapEx const & StyleSettings::GetPersonaHeader() const +{ + setupPersonaHeaderFooter( PERSONA_HEADER, mxData->maPersonaHeaderFooter, mxData->maPersonaHeaderBitmap, mxData->maPersonaMenuBarTextColor ); + return mxData->maPersonaHeaderBitmap; +} + +BitmapEx const & StyleSettings::GetPersonaFooter() const +{ + setupPersonaHeaderFooter( PERSONA_FOOTER, mxData->maPersonaHeaderFooter, mxData->maPersonaFooterBitmap, mxData->maPersonaMenuBarTextColor ); + return mxData->maPersonaFooterBitmap; +} + +const std::optional<Color>& StyleSettings::GetPersonaMenuBarTextColor() const +{ + GetPersonaHeader(); + return mxData->maPersonaMenuBarTextColor; +} + +void StyleSettings::SetStandardStyles() +{ + CopyData(); + mxData->SetStandardStyles(); +} + +Color StyleSettings::GetFaceGradientColor() const +{ + // compute a brighter face color that can be used in gradients + // for a convex look (eg toolbars) + + sal_uInt16 h, s, b; + GetFaceColor().RGBtoHSB( h, s, b ); + if( s > 1) s=1; + if( b < 98) b=98; + return Color::HSBtoRGB( h, s, b ); +} + +Color StyleSettings::GetSeparatorColor() const +{ + // compute a brighter shadow color for separators (used in toolbars or between menubar and toolbars on Windows XP) + sal_uInt16 h, s, b; + GetShadowColor().RGBtoHSB( h, s, b ); + b += b/4; + s -= s/4; + return Color::HSBtoRGB( h, s, b ); +} + +void StyleSettings::CopyData() +{ + // copy if other references exist + if (mxData.use_count() > 1) + { + mxData = std::make_shared<ImplStyleData>(*mxData); + } +} + +bool StyleSettings::operator ==( const StyleSettings& rSet ) const +{ + if ( mxData == rSet.mxData ) + return true; + + if (mxData->mIconTheme != rSet.mxData->mIconTheme) { + return false; + } + + if (*mxData->mIconThemeSelector != *rSet.mxData->mIconThemeSelector) { + return false; + } + + return (mxData->mnOptions == rSet.mxData->mnOptions) && + (mxData->mbAutoMnemonic == rSet.mxData->mbAutoMnemonic) && + (mxData->mnDragFullOptions == rSet.mxData->mnDragFullOptions) && + (mxData->mnSelectionOptions == rSet.mxData->mnSelectionOptions) && + (mxData->mnDisplayOptions == rSet.mxData->mnDisplayOptions) && + (mxData->mnCursorSize == rSet.mxData->mnCursorSize) && + (mxData->mnCursorBlinkTime == rSet.mxData->mnCursorBlinkTime) && + (mxData->mnTitleHeight == rSet.mxData->mnTitleHeight) && + (mxData->mnFloatTitleHeight == rSet.mxData->mnFloatTitleHeight) && + (mxData->mnScrollBarSize == rSet.mxData->mnScrollBarSize) && + (mxData->mnMinThumbSize == rSet.mxData->mnMinThumbSize) && + (mxData->mnSpinSize == rSet.mxData->mnSpinSize) && + (mxData->mnAntialiasedMin == rSet.mxData->mnAntialiasedMin) && + (mxData->mbHighContrast == rSet.mxData->mbHighContrast) && + (mxData->mbUseSystemUIFonts == rSet.mxData->mbUseSystemUIFonts) && + (mxData->mbUseFontAAFromSystem == rSet.mxData->mbUseFontAAFromSystem) && + (mxData->mnUseFlatBorders == rSet.mxData->mnUseFlatBorders) && + (mxData->mnUseFlatMenus == rSet.mxData->mnUseFlatMenus) && + (mxData->maFaceColor == rSet.mxData->maFaceColor) && + (mxData->maCheckedColor == rSet.mxData->maCheckedColor) && + (mxData->maLightColor == rSet.mxData->maLightColor) && + (mxData->maLightBorderColor == rSet.mxData->maLightBorderColor) && + (mxData->maShadowColor == rSet.mxData->maShadowColor) && + (mxData->maDarkShadowColor == rSet.mxData->maDarkShadowColor) && + (mxData->maButtonTextColor == rSet.mxData->maButtonTextColor) && + (mxData->maDefaultActionButtonTextColor == rSet.mxData->maDefaultActionButtonTextColor) && + (mxData->maActionButtonTextColor == rSet.mxData->maActionButtonTextColor) && + (mxData->maButtonRolloverTextColor == rSet.mxData->maButtonRolloverTextColor) && + (mxData->maActionButtonRolloverTextColor == rSet.mxData->maActionButtonRolloverTextColor) && + (mxData->maRadioCheckTextColor == rSet.mxData->maRadioCheckTextColor) && + (mxData->maGroupTextColor == rSet.mxData->maGroupTextColor) && + (mxData->maLabelTextColor == rSet.mxData->maLabelTextColor) && + (mxData->maWindowColor == rSet.mxData->maWindowColor) && + (mxData->maWindowTextColor == rSet.mxData->maWindowTextColor) && + (mxData->maDialogColor == rSet.mxData->maDialogColor) && + (mxData->maDialogTextColor == rSet.mxData->maDialogTextColor) && + (mxData->maWorkspaceColor == rSet.mxData->maWorkspaceColor) && + (mxData->maMonoColor == rSet.mxData->maMonoColor) && + (mxData->maFieldColor == rSet.mxData->maFieldColor) && + (mxData->maFieldTextColor == rSet.mxData->maFieldTextColor) && + (mxData->maActiveColor == rSet.mxData->maActiveColor) && + (mxData->maActiveTextColor == rSet.mxData->maActiveTextColor) && + (mxData->maActiveBorderColor == rSet.mxData->maActiveBorderColor) && + (mxData->maDeactiveColor == rSet.mxData->maDeactiveColor) && + (mxData->maDeactiveTextColor == rSet.mxData->maDeactiveTextColor) && + (mxData->maDeactiveBorderColor == rSet.mxData->maDeactiveBorderColor) && + (mxData->maMenuColor == rSet.mxData->maMenuColor) && + (mxData->maMenuBarColor == rSet.mxData->maMenuBarColor) && + (mxData->maMenuBarRolloverColor == rSet.mxData->maMenuBarRolloverColor) && + (mxData->maMenuBorderColor == rSet.mxData->maMenuBorderColor) && + (mxData->maMenuTextColor == rSet.mxData->maMenuTextColor) && + (mxData->maMenuBarTextColor == rSet.mxData->maMenuBarTextColor) && + (mxData->maMenuBarRolloverTextColor == rSet.mxData->maMenuBarRolloverTextColor) && + (mxData->maMenuHighlightColor == rSet.mxData->maMenuHighlightColor) && + (mxData->maMenuHighlightTextColor == rSet.mxData->maMenuHighlightTextColor) && + (mxData->maHighlightColor == rSet.mxData->maHighlightColor) && + (mxData->maHighlightTextColor == rSet.mxData->maHighlightTextColor) && + (mxData->maTabTextColor == rSet.mxData->maTabTextColor) && + (mxData->maTabRolloverTextColor == rSet.mxData->maTabRolloverTextColor) && + (mxData->maTabHighlightTextColor == rSet.mxData->maTabHighlightTextColor) && + (mxData->maActiveTabColor == rSet.mxData->maActiveTabColor) && + (mxData->maInactiveTabColor == rSet.mxData->maInactiveTabColor) && + (mxData->maDisableColor == rSet.mxData->maDisableColor) && + (mxData->maHelpColor == rSet.mxData->maHelpColor) && + (mxData->maHelpTextColor == rSet.mxData->maHelpTextColor) && + (mxData->maLinkColor == rSet.mxData->maLinkColor) && + (mxData->maVisitedLinkColor == rSet.mxData->maVisitedLinkColor) && + (mxData->maToolTextColor == rSet.mxData->maToolTextColor) && + (mxData->maHighlightLinkColor == rSet.mxData->maHighlightLinkColor) && + (mxData->maAppFont == rSet.mxData->maAppFont) && + (mxData->maHelpFont == rSet.mxData->maHelpFont) && + (mxData->maTitleFont == rSet.mxData->maTitleFont) && + (mxData->maFloatTitleFont == rSet.mxData->maFloatTitleFont) && + (mxData->maMenuFont == rSet.mxData->maMenuFont) && + (mxData->maToolFont == rSet.mxData->maToolFont) && + (mxData->maGroupFont == rSet.mxData->maGroupFont) && + (mxData->maLabelFont == rSet.mxData->maLabelFont) && + (mxData->maRadioCheckFont == rSet.mxData->maRadioCheckFont) && + (mxData->maPushButtonFont == rSet.mxData->maPushButtonFont) && + (mxData->maFieldFont == rSet.mxData->maFieldFont) && + (mxData->maIconFont == rSet.mxData->maIconFont) && + (mxData->maTabFont == rSet.mxData->maTabFont) && + (mxData->meUseImagesInMenus == rSet.mxData->meUseImagesInMenus) && + (mxData->mbPreferredUseImagesInMenus == rSet.mxData->mbPreferredUseImagesInMenus) && + (mxData->mbSkipDisabledInMenus == rSet.mxData->mbSkipDisabledInMenus) && + (mxData->mbHideDisabledMenuItems == rSet.mxData->mbHideDisabledMenuItems) && + (mxData->mbPreferredContextMenuShortcuts == rSet.mxData->mbPreferredContextMenuShortcuts)&& + (mxData->meContextMenuShortcuts == rSet.mxData->meContextMenuShortcuts) && + (mxData->mbPrimaryButtonWarpsSlider == rSet.mxData->mbPrimaryButtonWarpsSlider) && + (mxData->maFontColor == rSet.mxData->maFontColor) && + (mxData->mnEdgeBlending == rSet.mxData->mnEdgeBlending) && + (mxData->maEdgeBlendingTopLeftColor == rSet.mxData->maEdgeBlendingTopLeftColor) && + (mxData->maEdgeBlendingBottomRightColor == rSet.mxData->maEdgeBlendingBottomRightColor) && + (mxData->mnListBoxMaximumLineCount == rSet.mxData->mnListBoxMaximumLineCount) && + (mxData->mnColorValueSetColumnCount == rSet.mxData->mnColorValueSetColumnCount) && + (mxData->maListBoxPreviewDefaultLogicSize == rSet.mxData->maListBoxPreviewDefaultLogicSize) && + (mxData->maListBoxPreviewDefaultPixelSize == rSet.mxData->maListBoxPreviewDefaultPixelSize) && + (mxData->mbPreviewUsesCheckeredBackground == rSet.mxData->mbPreviewUsesCheckeredBackground); +} + +ImplMiscData::ImplMiscData() : + mnEnableATT(TRISTATE_INDET), + mnDisablePrinting(TRISTATE_INDET) +{ + static const char* pEnv = getenv("SAL_DECIMALSEP_ENABLED" ); // set default without UI + mbEnableLocalizedDecimalSep = (pEnv != nullptr); +} + +MiscSettings::MiscSettings() + : mxData(std::make_shared<ImplMiscData>()) +{ +} + +bool MiscSettings::operator ==( const MiscSettings& rSet ) const +{ + if ( mxData == rSet.mxData ) + return true; + + return (mxData->mnEnableATT == rSet.mxData->mnEnableATT ) && + (mxData->mnDisablePrinting == rSet.mxData->mnDisablePrinting ) && + (mxData->mbEnableLocalizedDecimalSep == rSet.mxData->mbEnableLocalizedDecimalSep ); +} + +bool +MiscSettings::operator !=( const MiscSettings& rSet ) const +{ + return !(*this == rSet); +} + +bool MiscSettings::GetDisablePrinting() const +{ + if( mxData->mnDisablePrinting == TRISTATE_INDET ) + { + OUString aEnable = + vcl::SettingsConfigItem::get()-> + getValue( "DesktopManagement", + "DisablePrinting" ); + mxData->mnDisablePrinting = aEnable.equalsIgnoreAsciiCase("true") ? TRISTATE_TRUE : TRISTATE_FALSE; + } + + return mxData->mnDisablePrinting != TRISTATE_FALSE; +} + +bool MiscSettings::GetEnableATToolSupport() const +{ + +#ifdef _WIN32 + if( mxData->mnEnableATT == TRISTATE_INDET ) + { + // Check in the Windows registry if an AT tool wants Accessibility support to + // be activated .. + HKEY hkey; + + if( ERROR_SUCCESS == RegOpenKeyW(HKEY_CURRENT_USER, + L"Software\\LibreOffice\\Accessibility\\AtToolSupport", + &hkey) ) + { + DWORD dwType; + wchar_t Data[6]; // possible values: "true", "false", "1", "0", DWORD + DWORD cbData = sizeof(Data); + + if( ERROR_SUCCESS == RegQueryValueExW(hkey, L"SupportAssistiveTechnology", + nullptr, &dwType, reinterpret_cast<LPBYTE>(Data), &cbData) ) + { + switch (dwType) + { + case REG_SZ: + mxData->mnEnableATT = ((0 == wcsicmp(Data, L"1")) || (0 == wcsicmp(Data, L"true"))) ? TRISTATE_TRUE : TRISTATE_FALSE; + break; + case REG_DWORD: + switch (reinterpret_cast<DWORD *>(Data)[0]) { + case 0: + mxData->mnEnableATT = TRISTATE_FALSE; + break; + case 1: + mxData->mnEnableATT = TRISTATE_TRUE; + break; + default: + mxData->mnEnableATT = TRISTATE_INDET; + //TODO: or TRISTATE_TRUE? + break; + } + break; + default: + // Unsupported registry type + break; + } + } + + RegCloseKey(hkey); + } + } +#endif + + if( mxData->mnEnableATT == TRISTATE_INDET ) + { + static const char* pEnv = getenv("SAL_ACCESSIBILITY_ENABLED" ); + if( !pEnv || !*pEnv ) + { + OUString aEnable = + vcl::SettingsConfigItem::get()-> + getValue( "Accessibility", + "EnableATToolSupport" ); + mxData->mnEnableATT = aEnable.equalsIgnoreAsciiCase("true") ? TRISTATE_TRUE : TRISTATE_FALSE; + } + else + { + mxData->mnEnableATT = TRISTATE_TRUE; + } + } + + return mxData->mnEnableATT != TRISTATE_FALSE; +} + +#ifdef _WIN32 +void MiscSettings::SetEnableATToolSupport( bool bEnable ) +{ + if ( (bEnable ? TRISTATE_TRUE : TRISTATE_FALSE) != mxData->mnEnableATT ) + { + if( bEnable && !ImplInitAccessBridge() ) + return; + + HKEY hkey; + + // If the accessibility key in the Windows registry exists, change it synchronously + if( ERROR_SUCCESS == RegOpenKeyW(HKEY_CURRENT_USER, + L"Software\\LibreOffice\\Accessibility\\AtToolSupport", + &hkey) ) + { + DWORD dwType; + wchar_t Data[6]; // possible values: "true", "false", 1, 0 + DWORD cbData = sizeof(Data); + + if( ERROR_SUCCESS == RegQueryValueExW(hkey, L"SupportAssistiveTechnology", + nullptr, &dwType, reinterpret_cast<LPBYTE>(Data), &cbData) ) + { + switch (dwType) + { + case REG_SZ: + RegSetValueExW(hkey, L"SupportAssistiveTechnology", + 0, dwType, + reinterpret_cast<const BYTE*>(bEnable ? L"true" : L"false"), + bEnable ? sizeof(L"true") : sizeof(L"false")); + break; + case REG_DWORD: + reinterpret_cast<DWORD *>(Data)[0] = bEnable ? 1 : 0; + RegSetValueExW(hkey, L"SupportAssistiveTechnology", + 0, dwType, reinterpret_cast<const BYTE*>(Data), sizeof(DWORD)); + break; + default: + // Unsupported registry type + break; + } + } + + RegCloseKey(hkey); + } + + vcl::SettingsConfigItem::get()-> + setValue( "Accessibility", + "EnableATToolSupport", + bEnable ? OUString("true") : OUString("false" ) ); + mxData->mnEnableATT = bEnable ? TRISTATE_TRUE : TRISTATE_FALSE; + } +} +#endif + +void MiscSettings::SetEnableLocalizedDecimalSep( bool bEnable ) +{ + // copy if other references exist + if (mxData.use_count() > 1) + { + mxData = std::make_shared<ImplMiscData>(*mxData); + } + mxData->mbEnableLocalizedDecimalSep = bEnable; +} + +bool MiscSettings::GetEnableLocalizedDecimalSep() const +{ + return mxData->mbEnableLocalizedDecimalSep; +} + +HelpSettings::HelpSettings() + : mxData(std::make_shared<ImplHelpData>()) +{ +} + +bool HelpSettings::operator ==( const HelpSettings& rSet ) const +{ + if ( mxData == rSet.mxData ) + return true; + + return (mxData->mnTipTimeout == rSet.mxData->mnTipTimeout ); +} + +sal_uLong +HelpSettings::GetTipDelay() +{ + return 500; +} + +void +HelpSettings::SetTipTimeout( sal_uLong nTipTimeout ) +{ + // copy if other references exist + if (mxData.use_count() > 1) + { + mxData = std::make_shared<ImplHelpData>(*mxData); + } + mxData->mnTipTimeout = nTipTimeout; +} + +sal_uLong +HelpSettings::GetTipTimeout() const +{ + return mxData->mnTipTimeout; +} + +sal_uLong +HelpSettings::GetBalloonDelay() +{ + return 1500; +} + +bool +HelpSettings::operator !=( const HelpSettings& rSet ) const +{ + return !(*this == rSet); +} + +ImplAllSettingsData::ImplAllSettingsData() + : + maLocale( LANGUAGE_SYSTEM ), + maUILocale( LANGUAGE_SYSTEM ) +{ + if (!utl::ConfigManager::IsFuzzing()) + maMiscSettings.SetEnableLocalizedDecimalSep( maSysLocale.GetOptions().IsDecimalSeparatorAsLocale() ); +} + +ImplAllSettingsData::ImplAllSettingsData( const ImplAllSettingsData& rData ) : + maMouseSettings( rData.maMouseSettings ), + maStyleSettings( rData.maStyleSettings ), + maMiscSettings( rData.maMiscSettings ), + maHelpSettings( rData.maHelpSettings ), + maLocale( rData.maLocale ), + maUILocale( rData.maUILocale ) +{ + // Create the cache objects new when their getter is called. +} + +ImplAllSettingsData::~ImplAllSettingsData() +{ + mpLocaleDataWrapper.reset(); + mpUILocaleDataWrapper.reset(); + mpNeutralLocaleDataWrapper.reset(); + mpI18nHelper.reset(); + mpUII18nHelper.reset(); +} + +AllSettings::AllSettings() + : mxData(std::make_shared<ImplAllSettingsData>()) +{ +} + +void AllSettings::CopyData() +{ + // copy if other references exist + if (mxData.use_count() > 1) + { + mxData = std::make_shared<ImplAllSettingsData>(*mxData); + } + +} + +AllSettingsFlags AllSettings::Update( AllSettingsFlags nFlags, const AllSettings& rSet ) +{ + + AllSettingsFlags nChangeFlags = AllSettingsFlags::NONE; + + if ( nFlags & AllSettingsFlags::MOUSE ) + { + if ( mxData->maMouseSettings != rSet.mxData->maMouseSettings ) + { + CopyData(); + mxData->maMouseSettings = rSet.mxData->maMouseSettings; + nChangeFlags |= AllSettingsFlags::MOUSE; + } + } + + if ( nFlags & AllSettingsFlags::STYLE ) + { + if ( mxData->maStyleSettings != rSet.mxData->maStyleSettings ) + { + CopyData(); + mxData->maStyleSettings = rSet.mxData->maStyleSettings; + nChangeFlags |= AllSettingsFlags::STYLE; + } + } + + if ( nFlags & AllSettingsFlags::MISC ) + { + if ( mxData->maMiscSettings != rSet.mxData->maMiscSettings ) + { + CopyData(); + mxData->maMiscSettings = rSet.mxData->maMiscSettings; + nChangeFlags |= AllSettingsFlags::MISC; + } + } + + if ( nFlags & AllSettingsFlags::LOCALE ) + { + if ( mxData->maLocale != rSet.mxData->maLocale ) + { + SetLanguageTag( rSet.mxData->maLocale ); + nChangeFlags |= AllSettingsFlags::LOCALE; + } + } + + return nChangeFlags; +} + +AllSettingsFlags AllSettings::GetChangeFlags( const AllSettings& rSet ) const +{ + + AllSettingsFlags nChangeFlags = AllSettingsFlags::NONE; + + if ( mxData->maStyleSettings != rSet.mxData->maStyleSettings ) + nChangeFlags |= AllSettingsFlags::STYLE; + + if ( mxData->maMiscSettings != rSet.mxData->maMiscSettings ) + nChangeFlags |= AllSettingsFlags::MISC; + + if ( mxData->maLocale != rSet.mxData->maLocale ) + nChangeFlags |= AllSettingsFlags::LOCALE; + + return nChangeFlags; +} + +bool AllSettings::operator ==( const AllSettings& rSet ) const +{ + if ( mxData == rSet.mxData ) + return true; + + if ( (mxData->maMouseSettings == rSet.mxData->maMouseSettings) && + (mxData->maStyleSettings == rSet.mxData->maStyleSettings) && + (mxData->maMiscSettings == rSet.mxData->maMiscSettings) && + (mxData->maHelpSettings == rSet.mxData->maHelpSettings) && + (mxData->maLocale == rSet.mxData->maLocale) ) + { + return true; + } + + return false; +} + +void AllSettings::SetLanguageTag(const OUString& rLanguage, bool bCanonicalize) +{ + SetLanguageTag(LanguageTag(rLanguage, bCanonicalize)); +} + +void AllSettings::SetLanguageTag( const LanguageTag& rLanguageTag ) +{ + if (mxData->maLocale != rLanguageTag) + { + CopyData(); + + mxData->maLocale = rLanguageTag; + + if ( mxData->mpLocaleDataWrapper ) + { + mxData->mpLocaleDataWrapper.reset(); + } + if ( mxData->mpI18nHelper ) + { + mxData->mpI18nHelper.reset(); + } + } +} + +namespace +{ + bool GetConfigLayoutRTL(bool bMath) + { + static const char* pEnv = getenv("SAL_RTL_ENABLED" ); + static int nUIMirroring = -1; // -1: undef, 0: auto, 1: on 2: off + + // environment always overrides + if( pEnv ) + return true; + + bool bRTL = false; + + if( nUIMirroring == -1 ) + { + nUIMirroring = 0; // ask configuration only once + utl::OConfigurationNode aNode = utl::OConfigurationTreeRoot::tryCreateWithComponentContext( + comphelper::getProcessComponentContext(), + "org.openoffice.Office.Common/I18N/CTL" ); // note: case sensitive ! + if ( aNode.isValid() ) + { + bool bTmp = bool(); + css::uno::Any aValue = aNode.getNodeValue( "UIMirroring" ); + if( aValue >>= bTmp ) + { + // found true or false; if it was nil, nothing is changed + nUIMirroring = bTmp ? 1 : 2; + } + } + } + + if( nUIMirroring == 0 ) // no config found (eg, setup) or default (nil) was set: check language + { + LanguageType aLang = SvtSysLocaleOptions().GetRealUILanguageTag().getLanguageType(); + if (bMath) + bRTL = MsLangId::isRightToLeftMath( aLang ); + else + bRTL = MsLangId::isRightToLeft( aLang ); + } + else + bRTL = (nUIMirroring == 1); + + return bRTL; + } +} + +bool AllSettings::GetLayoutRTL() +{ + if (utl::ConfigManager::IsFuzzing()) + return false; + return GetConfigLayoutRTL(false); +} + +bool AllSettings::GetMathLayoutRTL() +{ + if (utl::ConfigManager::IsFuzzing()) + return false; + return GetConfigLayoutRTL(true); +} + +const LanguageTag& AllSettings::GetLanguageTag() const +{ + if (utl::ConfigManager::IsFuzzing()) + { + static LanguageTag aRet("en-US"); + return aRet; + } + + if (comphelper::LibreOfficeKit::isActive()) + return comphelper::LibreOfficeKit::getLanguageTag(); + + // SYSTEM locale means: use settings from SvtSysLocale that is resolved + if ( mxData->maLocale.isSystemLocale() ) + mxData->maLocale = mxData->maSysLocale.GetLanguageTag(); + + return mxData->maLocale; +} + +const LanguageTag& AllSettings::GetUILanguageTag() const +{ + if (utl::ConfigManager::IsFuzzing()) + { + static LanguageTag aRet("en-US"); + return aRet; + } + + if (comphelper::LibreOfficeKit::isActive()) + return comphelper::LibreOfficeKit::getLanguageTag(); + + // the UILocale is never changed + if ( mxData->maUILocale.isSystemLocale() ) + mxData->maUILocale = mxData->maSysLocale.GetUILanguageTag(); + + return mxData->maUILocale; +} + +const LocaleDataWrapper& AllSettings::GetLocaleDataWrapper() const +{ + if ( !mxData->mpLocaleDataWrapper ) + const_cast<AllSettings*>(this)->mxData->mpLocaleDataWrapper.reset( new LocaleDataWrapper( + comphelper::getProcessComponentContext(), GetLanguageTag() ) ); + return *mxData->mpLocaleDataWrapper; +} + +const LocaleDataWrapper& AllSettings::GetUILocaleDataWrapper() const +{ + if ( !mxData->mpUILocaleDataWrapper ) + const_cast<AllSettings*>(this)->mxData->mpUILocaleDataWrapper.reset( new LocaleDataWrapper( + comphelper::getProcessComponentContext(), GetUILanguageTag() ) ); + return *mxData->mpUILocaleDataWrapper; +} + +const LocaleDataWrapper& AllSettings::GetNeutralLocaleDataWrapper() const +{ + if ( !mxData->mpNeutralLocaleDataWrapper ) + const_cast<AllSettings*>(this)->mxData->mpNeutralLocaleDataWrapper.reset( new LocaleDataWrapper( + comphelper::getProcessComponentContext(), LanguageTag("en-US") ) ); + return *mxData->mpNeutralLocaleDataWrapper; +} + +const vcl::I18nHelper& AllSettings::GetLocaleI18nHelper() const +{ + if ( !mxData->mpI18nHelper ) { + const_cast<AllSettings*>(this)->mxData->mpI18nHelper.reset( new vcl::I18nHelper( + comphelper::getProcessComponentContext(), GetLanguageTag() ) ); + } + return *mxData->mpI18nHelper; +} + +const vcl::I18nHelper& AllSettings::GetUILocaleI18nHelper() const +{ + if ( !mxData->mpUII18nHelper ) { + const_cast<AllSettings*>(this)->mxData->mpUII18nHelper.reset( new vcl::I18nHelper( + comphelper::getProcessComponentContext(), GetUILanguageTag() ) ); + } + return *mxData->mpUII18nHelper; +} + +void AllSettings::LocaleSettingsChanged( ConfigurationHints nHint ) +{ + AllSettings aAllSettings( Application::GetSettings() ); + if ( nHint & ConfigurationHints::DecSep ) + { + MiscSettings aMiscSettings = aAllSettings.GetMiscSettings(); + bool bIsDecSepAsLocale = aAllSettings.mxData->maSysLocale.GetOptions().IsDecimalSeparatorAsLocale(); + if ( aMiscSettings.GetEnableLocalizedDecimalSep() != bIsDecSepAsLocale ) + { + aMiscSettings.SetEnableLocalizedDecimalSep( bIsDecSepAsLocale ); + aAllSettings.SetMiscSettings( aMiscSettings ); + } + } + + if ( nHint & ConfigurationHints::Locale ) + aAllSettings.SetLanguageTag( aAllSettings.mxData->maSysLocale.GetOptions().GetLanguageTag() ); + + Application::SetSettings( aAllSettings ); +} + +const StyleSettings& +AllSettings::GetStyleSettings() const +{ + return mxData->maStyleSettings; +} + +StyleSettingsOptions +StyleSettings::GetOptions() const +{ + return mxData->mnOptions; +} + +std::vector<vcl::IconThemeInfo> const & +StyleSettings::GetInstalledIconThemes() const +{ + if (!mxData->mIconThemeScanner) { + const_cast<StyleSettings*>(this)->mxData->mIconThemeScanner = vcl::IconThemeScanner::Create(vcl::IconThemeScanner::GetStandardIconThemePath()); + } + return mxData->mIconThemeScanner->GetFoundIconThemes(); +} + +/*static*/ OUString +StyleSettings::GetAutomaticallyChosenIconTheme() const +{ + OUString desktopEnvironment = Application::GetDesktopEnvironment(); + if (!mxData->mIconThemeScanner) { + const_cast<StyleSettings*>(this)->mxData->mIconThemeScanner = vcl::IconThemeScanner::Create(vcl::IconThemeScanner::GetStandardIconThemePath()); + } + OUString themeName = mxData->mIconThemeSelector->SelectIconThemeForDesktopEnvironment( + mxData->mIconThemeScanner->GetFoundIconThemes(), + desktopEnvironment + ); + return themeName; +} + +void +StyleSettings::SetIconTheme(const OUString& theme) +{ + CopyData(); + mxData->mIconTheme = theme; +} + +OUString +StyleSettings::DetermineIconTheme() const +{ + OUString sTheme(mxData->mIconTheme); + if (sTheme.isEmpty()) + { + if (utl::ConfigManager::IsFuzzing()) + sTheme = "colibre"; + else + { + // read from the configuration, or fallback to what the desktop wants + uno::Reference<uno::XComponentContext> xContext(comphelper::getProcessComponentContext()); + if (xContext.is()) + { + sTheme = officecfg::Office::Common::Misc::SymbolStyle::get(xContext); + + if (sTheme.isEmpty() || sTheme == "auto") + sTheme = GetAutomaticallyChosenIconTheme(); + } + } + } + + if (!mxData->mIconThemeScanner) { + const_cast<StyleSettings*>(this)->mxData->mIconThemeScanner = vcl::IconThemeScanner::Create(vcl::IconThemeScanner::GetStandardIconThemePath()); + } + OUString r = mxData->mIconThemeSelector->SelectIconTheme( + mxData->mIconThemeScanner->GetFoundIconThemes(), + sTheme); + return r; +} + +void +StyleSettings::SetHighContrastMode(bool bHighContrast ) +{ + if (mxData->mbHighContrast == bHighContrast) { + return; + } + + CopyData(); + mxData->mbHighContrast = bHighContrast; + mxData->mIconThemeSelector->SetUseHighContrastTheme(bHighContrast); +} + +bool +StyleSettings::GetHighContrastMode() const +{ + return mxData->mbHighContrast; +} + +void +StyleSettings::SetPreferredIconTheme(const OUString& theme, bool bDarkIconTheme) +{ + mxData->mIconThemeSelector->SetPreferredIconTheme(theme, bDarkIconTheme); +} + +void +AllSettings::SetMouseSettings( const MouseSettings& rSet ) +{ + CopyData(); + mxData->maMouseSettings = rSet; +} + +const MouseSettings& +AllSettings::GetMouseSettings() const +{ + return mxData->maMouseSettings; +} + +void +AllSettings::SetStyleSettings( const StyleSettings& rSet ) +{ + CopyData(); + mxData->maStyleSettings = rSet; +} + +void +AllSettings::SetMiscSettings( const MiscSettings& rSet ) +{ + CopyData(); + mxData->maMiscSettings = rSet; +} + +const MiscSettings& +AllSettings::GetMiscSettings() const +{ + return mxData->maMiscSettings; +} + +void +AllSettings::SetHelpSettings( const HelpSettings& rSet ) +{ + CopyData(); + mxData->maHelpSettings = rSet; +} + +const HelpSettings& +AllSettings::GetHelpSettings() const +{ + return mxData->maHelpSettings; +} + +bool +AllSettings::operator !=( const AllSettings& rSet ) const +{ + return !(*this == rSet); +} + +SvtSysLocale& +AllSettings::GetSysLocale() +{ + return mxData->maSysLocale; +} + + +void StyleSettings::BatchSetBackgrounds( const Color &aBackColor, + bool bCheckedColorSpecialCase ) +{ + Set3DColors( aBackColor ); + SetFaceColor( aBackColor ); + SetDialogColor( aBackColor ); + SetWorkspaceColor( aBackColor ); + + if (bCheckedColorSpecialCase) + SetCheckedColorSpecialCase(); +} + +void StyleSettings::BatchSetFonts( const vcl::Font& aAppFont, + const vcl::Font& aLabelFont ) +{ + SetAppFont( aAppFont ); + SetPushButtonFont( aAppFont ); + SetToolFont( aAppFont ); + SetHelpFont( aAppFont ); + + SetMenuFont( aLabelFont ); + SetTabFont( aLabelFont ); + SetLabelFont( aLabelFont ); + SetRadioCheckFont( aLabelFont ); + SetFieldFont( aLabelFont ); + SetGroupFont( aLabelFont ); + SetIconFont( aLabelFont ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/source/app/sound.cxx b/vcl/source/app/sound.cxx new file mode 100644 index 000000000..5d1c28e20 --- /dev/null +++ b/vcl/source/app/sound.cxx @@ -0,0 +1,38 @@ +/* -*- 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 <vcl/svapp.hxx> +#include <vcl/window.hxx> +#include <vcl/sound.hxx> + +#include <salframe.hxx> +#include <svdata.hxx> + +void Sound::Beep() +{ + // #i91990# + if ( Application::IsHeadlessModeEnabled() ) + return; + + vcl::Window* pWindow = ImplGetDefaultWindow(); + + pWindow->ImplGetFrame()->Beep(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/source/app/stdtext.cxx b/vcl/source/app/stdtext.cxx new file mode 100644 index 000000000..7d6bae820 --- /dev/null +++ b/vcl/source/app/stdtext.cxx @@ -0,0 +1,124 @@ +/* -*- 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 <vcl/stdtext.hxx> +#include <vcl/image.hxx> +#include <vcl/weld.hxx> +#include <bitmaps.hlst> +#include <strings.hrc> +#include <svdata.hxx> + +void ShowServiceNotAvailableError(weld::Widget* pParent, + const OUString& rServiceName, bool bError) +{ + OUString aText = VclResId(SV_STDTEXT_SERVICENOTAVAILABLE).replaceAll("%s", rServiceName); + std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(pParent, + bError ? VclMessageType::Error : VclMessageType::Warning, VclButtonsType::Ok, aText)); + xBox->run(); +} + +static void ImplInitMsgBoxImageList() +{ + ImplSVData* pSVData = ImplGetSVData(); + std::vector<Image> &rImages = pSVData->mpWinData->maMsgBoxImgList; + if (rImages.empty()) + { + rImages.emplace_back(Image(StockImage::Yes, SV_RESID_BITMAP_ERRORBOX)); + rImages.emplace_back(Image(StockImage::Yes, SV_RESID_BITMAP_QUERYBOX)); + rImages.emplace_back(Image(StockImage::Yes, SV_RESID_BITMAP_WARNINGBOX)); + rImages.emplace_back(Image(StockImage::Yes, SV_RESID_BITMAP_INFOBOX)); + } +} + +Image const & GetStandardInfoBoxImage() +{ + ImplInitMsgBoxImageList(); + return ImplGetSVData()->mpWinData->maMsgBoxImgList[3]; +} + +OUString GetStandardInfoBoxText() +{ + return VclResId(SV_MSGBOX_INFO); +} + +Image const & GetStandardWarningBoxImage() +{ + ImplInitMsgBoxImageList(); + return ImplGetSVData()->mpWinData->maMsgBoxImgList[2]; +} + +OUString GetStandardWarningBoxText() +{ + return VclResId(SV_MSGBOX_WARNING); +} + +Image const & GetStandardErrorBoxImage() +{ + ImplInitMsgBoxImageList(); + return ImplGetSVData()->mpWinData->maMsgBoxImgList[0]; +} + +OUString GetStandardErrorBoxText() +{ + return VclResId(SV_MSGBOX_ERROR); +} + +Image const & GetStandardQueryBoxImage() +{ + ImplInitMsgBoxImageList(); + return ImplGetSVData()->mpWinData->maMsgBoxImgList[1]; +} + +OUString GetStandardQueryBoxText() +{ + return VclResId(SV_MSGBOX_QUERY); +} + +OUString GetStandardText(StandardButtonType eButton) +{ + static const char* aResIdAry[static_cast<int>(StandardButtonType::Count)] = + { + // http://lists.freedesktop.org/archives/libreoffice/2013-January/044513.html + // Under windows we don't want accelerators on ok/cancel but do on other + // buttons +#ifdef _WIN32 + SV_BUTTONTEXT_OK_NOMNEMONIC, + SV_BUTTONTEXT_CANCEL_NOMNEMONIC, +#else + SV_BUTTONTEXT_OK, + SV_BUTTONTEXT_CANCEL, +#endif + SV_BUTTONTEXT_YES, + SV_BUTTONTEXT_NO, + SV_BUTTONTEXT_RETRY, + SV_BUTTONTEXT_HELP, + SV_BUTTONTEXT_CLOSE, + SV_BUTTONTEXT_MORE, + SV_BUTTONTEXT_IGNORE, + SV_BUTTONTEXT_ABORT, + SV_BUTTONTEXT_LESS, + STR_WIZDLG_PREVIOUS, + STR_WIZDLG_NEXT, + STR_WIZDLG_FINISH, + }; + + return VclResId(aResIdAry[static_cast<sal_uInt16>(eButton)]); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/source/app/svapp.cxx b/vcl/source/app/svapp.cxx new file mode 100644 index 000000000..720fb32d4 --- /dev/null +++ b/vcl/source/app/svapp.cxx @@ -0,0 +1,1707 @@ +/* -*- 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 <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 <unotools/configmgr.hxx> +#include <unotools/syslocaleoptions.hxx> + +#include <vcl/toolkit/dialog.hxx> +#include <vcl/dialoghelper.hxx> +#include <vcl/lok.hxx> +#include <vcl/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> +#if HAVE_FEATURE_OPENGL +#include <vcl/opengl/OpenGLWrapper.hxx> +#endif +#include <vcl/skia/SkiaHelper.hxx> + +#include <salinst.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/solarmutex.hxx> +#include <osl/process.h> + +#include <cassert> +#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 +static vcl::KeyCode const ReservedKeys[] +{ + vcl::KeyCode(KEY_F1,0) , + vcl::KeyCode(KEY_F1,KEY_SHIFT) , + vcl::KeyCode(KEY_F1,KEY_MOD1) , + vcl::KeyCode(KEY_F2,KEY_SHIFT) , + vcl::KeyCode(KEY_F4,KEY_MOD1) , + vcl::KeyCode(KEY_F4,KEY_MOD2) , + vcl::KeyCode(KEY_F4,KEY_MOD1|KEY_MOD2) , + vcl::KeyCode(KEY_F6,0) , + vcl::KeyCode(KEY_F6,KEY_MOD1) , + vcl::KeyCode(KEY_F6,KEY_SHIFT) , + vcl::KeyCode(KEY_F6,KEY_MOD1|KEY_SHIFT) , + vcl::KeyCode(KEY_F10,0) +#ifdef UNX + , + vcl::KeyCode(KEY_1,KEY_SHIFT|KEY_MOD1), + vcl::KeyCode(KEY_2,KEY_SHIFT|KEY_MOD1), + vcl::KeyCode(KEY_3,KEY_SHIFT|KEY_MOD1), + vcl::KeyCode(KEY_4,KEY_SHIFT|KEY_MOD1), + vcl::KeyCode(KEY_5,KEY_SHIFT|KEY_MOD1), + vcl::KeyCode(KEY_6,KEY_SHIFT|KEY_MOD1), + vcl::KeyCode(KEY_7,KEY_SHIFT|KEY_MOD1), + vcl::KeyCode(KEY_8,KEY_SHIFT|KEY_MOD1), + vcl::KeyCode(KEY_9,KEY_SHIFT|KEY_MOD1), + vcl::KeyCode(KEY_0,KEY_SHIFT|KEY_MOD1), + vcl::KeyCode(KEY_ADD,KEY_SHIFT|KEY_MOD1) +#endif +}; + +extern "C" { + typedef UnoWrapperBase* (*FN_TkCreateUnoWrapper)(); +} + +struct ImplPostEventData +{ + VclEventId mnEvent; + VclPtr<vcl::Window> mpWin; + ImplSVEvent * mnEventId; + KeyEvent maKeyEvent; + MouseEvent maMouseEvent; + GestureEvent maGestureEvent; + + ImplPostEventData(VclEventId nEvent, vcl::Window* pWin, const KeyEvent& rKeyEvent) + : mnEvent(nEvent) + , mpWin(pWin) + , mnEventId(nullptr) + , maKeyEvent(rKeyEvent) + {} + ImplPostEventData(VclEventId nEvent, vcl::Window* pWin, const MouseEvent& rMouseEvent) + : mnEvent(nEvent) + , mpWin(pWin) + , mnEventId(nullptr) + , maMouseEvent(rMouseEvent) + {} + ImplPostEventData(VclEventId nEvent, vcl::Window* pWin, const GestureEvent& rGestureEvent) + : mnEvent(nEvent) + , mpWin(pWin) + , mnEventId(nullptr) + , 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("LIBO_VERSION"), aValue(LIBO_VERSION_DOTTED); + osl_setEnvironment(aVar.pData, aValue.pData); + + ImplGetSVData()->mpApp = this; +} + +Application::~Application() +{ + ImplDeInitSVData(); + ImplGetSVData()->mpApp = nullptr; +} + +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; + +#ifdef DBG_UTIL + case ExceptionCategory::ResourceNotLoaded: + Abort("Resource not loaded"); + break; + default: + Abort("Unknown Error"); + break; +#else + default: + Abort(OUString()); + break; +#endif + } +} + +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]; +} + +IMPL_STATIC_LINK_NOARG( ImplSVAppData, ImplEndAllPopupsMsg, void*, void ) +{ + ImplSVData* pSVData = ImplGetSVData(); + while (pSVData->mpWinData->mpFirstFloat) + pSVData->mpWinData->mpFirstFloat->EndPopupMode(FloatWinPopupEndFlags::Cancel); +} + +IMPL_STATIC_LINK_NOARG( ImplSVAppData, ImplEndAllDialogsMsg, void*, void ) +{ + vcl::Window* pAppWindow = Application::GetFirstTopLevelWindow(); + while (pAppWindow) + { + vcl::EndAllDialogs(pAppWindow); + pAppWindow = Application::GetNextTopLevelWindow(pAppWindow); + } +} + +void Application::EndAllDialogs() +{ + Application::PostUserEvent( LINK( nullptr, ImplSVAppData, ImplEndAllDialogsMsg ) ); +} + +void Application::EndAllPopups() +{ + Application::PostUserEvent( LINK( nullptr, ImplSVAppData, ImplEndAllPopupsMsg ) ); +} + + +namespace +{ + VclPtr<vcl::Window> GetEventWindow() + { + VclPtr<vcl::Window> xWin(Application::GetFirstTopLevelWindow()); + while (xWin) + { + if (xWin->IsVisible()) + break; + xWin.reset(Application::GetNextTopLevelWindow(xWin)); + } + return xWin; + } + + bool InjectKeyEvent(SvStream& rStream) + { + VclPtr<vcl::Window> xWin(GetEventWindow()); + if (!xWin) + return false; + + // skip the first available cycle and insert on the next one when we + // are trying the initial event, flagged by a triggered but undeleted + // mpEventTestingIdle + ImplSVData* pSVData = ImplGetSVData(); + if (pSVData->maAppData.mpEventTestingIdle) + { + delete pSVData->maAppData.mpEventTestingIdle; + pSVData->maAppData.mpEventTestingIdle = nullptr; + return false; + } + + sal_uInt16 nCode, nCharCode; + rStream.ReadUInt16(nCode); + rStream.ReadUInt16(nCharCode); + if (!rStream.good()) + return false; + + KeyEvent aVCLKeyEvt(nCharCode, nCode); + Application::PostKeyEvent(VclEventId::WindowKeyInput, xWin.get(), &aVCLKeyEvt); + Application::PostKeyEvent(VclEventId::WindowKeyUp, xWin.get(), &aVCLKeyEvt); + return true; + } + + void CloseDialogsAndQuit() + { + Application::EndAllPopups(); + Application::EndAllDialogs(); + Application::PostUserEvent( LINK( nullptr, ImplSVAppData, ImplPrepareExitMsg ) ); + } +} + +IMPL_LINK_NOARG(ImplSVAppData, VclEventTestingHdl, Timer *, void) +{ + if (Application::AnyInput()) + { + mpEventTestingIdle->Start(); + } + else + { + Application::PostUserEvent( LINK( nullptr, ImplSVAppData, ImplVclEventTestingHdl ) ); + } +} + +IMPL_STATIC_LINK_NOARG( ImplSVAppData, ImplVclEventTestingHdl, void*, void ) +{ + ImplSVData* pSVData = ImplGetSVData(); + SAL_INFO("vcl.eventtesting", "EventTestLimit is " << pSVData->maAppData.mnEventTestLimit); + if (pSVData->maAppData.mnEventTestLimit == 0) + { + delete pSVData->maAppData.mpEventTestInput; + SAL_INFO("vcl.eventtesting", "Event Limit reached, exiting" << pSVData->maAppData.mnEventTestLimit); + CloseDialogsAndQuit(); + } + else + { + if (InjectKeyEvent(*pSVData->maAppData.mpEventTestInput)) + --pSVData->maAppData.mnEventTestLimit; + if (!pSVData->maAppData.mpEventTestInput->good()) + { + SAL_INFO("vcl.eventtesting", "Event Input exhausted, exit next cycle"); + pSVData->maAppData.mnEventTestLimit = 0; + } + Application::PostUserEvent( LINK( nullptr, ImplSVAppData, ImplVclEventTestingHdl ) ); + } +} + +IMPL_STATIC_LINK_NOARG( ImplSVAppData, ImplPrepareExitMsg, void*, void ) +{ + //now close top level frames + (void)GetpApp()->QueryExit(); +} + +void Application::Execute() +{ + ImplSVData* pSVData = ImplGetSVData(); + pSVData->maAppData.mbInAppExecute = true; + pSVData->maAppData.mbAppQuit = false; + + if (Application::IsEventTestingModeEnabled()) + { + pSVData->maAppData.mnEventTestLimit = 50; + pSVData->maAppData.mpEventTestingIdle = new Idle("eventtesting"); + pSVData->maAppData.mpEventTestingIdle->SetInvokeHandler(LINK(&(pSVData->maAppData), ImplSVAppData, VclEventTestingHdl)); + pSVData->maAppData.mpEventTestingIdle->SetPriority(TaskPriority::HIGH_IDLE); + pSVData->maAppData.mpEventTestInput = new SvFileStream("eventtesting", StreamMode::READ); + pSVData->maAppData.mpEventTestingIdle->Start(); + } + + while ( !pSVData->maAppData.mbAppQuit ) + Application::Yield(); + + 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 ) +{ + return ImplYield(false, i_bAllEvents); +} + +void Scheduler::ProcessEventsToIdle() +{ + int nSanity = 1; + while( Application::Reschedule( 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. + const ImplSVData* pSVData = ImplGetSVData(); + if ( !pSVData->mpDefInst->IsMainThread() ) + return; + for (int nTaskPriority = 0; nTaskPriority < PRIO_COUNT; ++nTaskPriority) + { + const ImplSchedulerData* pSchedulerData = pSVData->maSchedCtx.mpFirstSchedulerData[nTaskPriority]; + while (pSchedulerData) + { + if (pSchedulerData->mpTask && !pSchedulerData->mbInScheduler) + { + Idle *pIdle = dynamic_cast<Idle*>(pSchedulerData->mpTask); + if (pIdle && pIdle->IsActive()) + { + SAL_WARN("vcl.schedule", "Unprocessed Idle: " + << pIdle << " " << pIdle->GetDebugName()); + } + } + 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() +{ + ImplYield(true, false); +} + +IMPL_STATIC_LINK_NOARG( ImplSVAppData, ImplQuitMsg, void*, void ) +{ + assert(ImplGetSVData()->maAppData.mbAppQuit); +} + +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->ReleaseYieldMutexAll(); +} + +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::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.mpSettings ); + pSVData->maAppData.mbSettingsInit = true; + } + // side effect: ImplUpdateGlobalSettings does an ImplGetFrame()->UpdateSettings + pWindow->ImplUpdateGlobalSettings( rSettings, false ); + } +} + +void Application::SetSettings( const AllSettings& rSettings ) +{ + const SolarMutexGuard aGuard; + + ImplSVData* pSVData = ImplGetSVData(); + if ( !pSVData->maAppData.mpSettings ) + { + InitSettings(pSVData); + *pSVData->maAppData.mpSettings = rSettings; + } + else + { + AllSettings aOldSettings = *pSVData->maAppData.mpSettings; + if (aOldSettings.GetUILanguageTag().getLanguageType() != rSettings.GetUILanguageTag().getLanguageType() && + pSVData->mbResLocaleSet) + { + pSVData->mbResLocaleSet = false; + } + *pSVData->maAppData.mpSettings = rSettings; + AllSettingsFlags nChangeFlags = aOldSettings.GetChangeFlags( *pSVData->maAppData.mpSettings ); + if ( bool(nChangeFlags) ) + { + 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 + long nOldDPIX = 0; + long nOldDPIY = 0; + if ( pFirstFrame ) + { + nOldDPIX = pFirstFrame->GetDPIX(); + nOldDPIY = pFirstFrame->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->GetDPIX() != nOldDPIX) || + (pFirstFrame->GetDPIY() != nOldDPIY) ) + { + VirtualDevice* pVirDev = pSVData->maGDIData.mpFirstVirDev; + while ( pVirDev ) + { + if ( pVirDev->mbScreenComp && + (pVirDev->GetDPIX() == nOldDPIX) && + (pVirDev->GetDPIY() == nOldDPIY) ) + { + pVirDev->SetDPIX( pFirstFrame->GetDPIX() ); + pVirDev->SetDPIY( pFirstFrame->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.mpSettings ) + { + InitSettings(pSVData); + } + + return *(pSVData->maAppData.mpSettings); +} + +namespace { + +void InitSettings(ImplSVData* pSVData) +{ + assert(!pSVData->maAppData.mpSettings && "initialization should not happen twice!"); + + pSVData->maAppData.mpSettings.reset(new AllSettings()); + if (!utl::ConfigManager::IsFuzzing()) + { + pSVData->maAppData.mpCfgListener = new LocaleConfigurationListener; + pSVData->maAppData.mpSettings->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; + rVec.erase( std::remove(rVec.begin(), rVec.end(), rKeyListener ), rVec.end() ); +} + +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, GestureEvent 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 GestureEvent 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; +} + +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; + // Double check that this is indeed a vcl::Window instance. + assert(dynamic_cast<vcl::Window *>( + static_cast<OutputDevice *>(rLink.GetInstance())) == + static_cast<vcl::Window *>(rLink.GetInstance())); + 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() +{ + return ImplGetDefaultWindow(); +} + +vcl::Window* Application::GetFirstTopLevelWindow() +{ + ImplSVData* pSVData = ImplGetSVData(); + return pSVData->maFrameData.mpFirstFrame; +} + +vcl::Window* Application::GetNextTopLevelWindow( vcl::Window const * pWindow ) +{ + return pWindow->mpWindowImpl->mpFrameData->mpNextFrame; +} + +long Application::GetTopWindowCount() +{ + 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( long nIndex ) +{ + 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; +} + +OUString Application::GetAppName() +{ + ImplSVData* pSVData = ImplGetSVData(); + if ( pSVData->maAppData.mxAppName ) + return *(pSVData->maAppData.mxAppName); + else + return OUString(); +} + +enum {hwAll=0, hwEnv=1, hwUI=2}; + +static OUString Localize(const char *pId, const bool bLocalize) +{ + if (bLocalize) + return VclResId(pId); + else + return Translate::get(pId, Translate::Create("vcl", LanguageTag("en-US"))); +} + +OUString Application::GetHWOSConfInfo(const int bSelection, const bool bLocalize) +{ + ImplSVData* pSVData = ImplGetSVData(); + OUStringBuffer aDetails; + + const auto appendDetails = [&aDetails](const OUStringLiteral& sep, auto&& val) { + if (!aDetails.isEmpty() && sep.getLength()) + aDetails.append(sep); + aDetails.append(std::move(val)); + }; + + if (bSelection != hwUI) { + appendDetails("; ", Localize(SV_APP_CPUTHREADS, bLocalize) + + OUString::number(std::thread::hardware_concurrency())); + + OUString aVersion; + if ( pSVData && pSVData->mpDefInst ) + aVersion = pSVData->mpDefInst->getOSVersion(); + else + aVersion = "-"; + + appendDetails("; ", Localize(SV_APP_OSVERSION, bLocalize) + aVersion); + } + + if (bSelection != hwEnv) { + appendDetails("; ", Localize(SV_APP_UIRENDER, bLocalize)); +#if HAVE_FEATURE_SKIA + if ( SkiaHelper::isVCLSkiaEnabled() ) + { + switch(SkiaHelper::renderMethodToUse()) + { + case SkiaHelper::RenderVulkan: + appendDetails("", Localize(SV_APP_SKIA_VULKAN, bLocalize)); + break; + case SkiaHelper::RenderRaster: + appendDetails("", Localize(SV_APP_SKIA_RASTER, bLocalize)); + break; + } + } + else +#endif +#if HAVE_FEATURE_OPENGL + if ( OpenGLWrapper::isVCLOpenGLEnabled() ) + appendDetails("", Localize(SV_APP_GL, bLocalize)); + else +#endif + appendDetails("", Localize(SV_APP_DEFAULT, bLocalize)); + +#if (defined LINUX || defined _WIN32 || defined MACOSX) + appendDetails("; ", 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; +} + +bool Application::IsUnifiedDisplay() +{ + SalSystem* pSys = ImplGetSalSystem(); + return pSys == nullptr || pSys->IsUnifiedDisplay(); +} + +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; +} + +tools::Rectangle Application::GetScreenPosSizePixel( unsigned int nScreen ) +{ + SalSystem* pSys = ImplGetSalSystem(); + if (!pSys) + { + SAL_WARN("vcl", "Requesting screen size/pos for screen #" << nScreen << " failed"); + assert(false); + return tools::Rectangle(); + } + tools::Rectangle aRect = pSys->GetDisplayScreenPosSizePixel(nScreen); + if (aRect.getHeight() == 0) + SAL_WARN("vcl", "Requesting screen size/pos for screen #" << nScreen << " returned 0 height."); + return aRect; +} + +namespace { +unsigned long calcDistSquare( const Point& i_rPoint, const tools::Rectangle& i_rRect ) +{ + const Point aRectCenter( (i_rRect.Left() + i_rRect.Right())/2, + (i_rRect.Top() + i_rRect.Bottom())/ 2 ); + const long nDX = aRectCenter.X() - i_rPoint.X(); + const long nDY = aRectCenter.Y() - i_rPoint.Y(); + return nDX*nDX + nDY*nDY; +} +} + +unsigned int Application::GetBestScreen( const tools::Rectangle& i_rRect ) +{ + if( !IsUnifiedDisplay() ) + return GetDisplayBuiltInScreen(); + + const unsigned int nScreens = GetScreenCount(); + unsigned int nBestMatchScreen = 0; + unsigned long nOverlap = 0; + for( unsigned int i = 0; i < nScreens; i++ ) + { + const tools::Rectangle aCurScreenRect( GetScreenPosSizePixel( i ) ); + // if a screen contains the rectangle completely it is obviously the best screen + if( aCurScreenRect.IsInside( i_rRect ) ) + return i; + // next the screen which contains most of the area of the rect is the best + tools::Rectangle 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 Point aCenter( (i_rRect.Left() + i_rRect.Right())/2, + (i_rRect.Top() + i_rRect.Bottom())/2 ); + unsigned long nDist = ULONG_MAX; + for( unsigned int i = 0; i < nScreens; i++ ) + { + const tools::Rectangle aCurScreenRect( GetScreenPosSizePixel( i ) ); + const unsigned 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; +} + +OUString Application::GetToolkitName() +{ + ImplSVData* pSVData = ImplGetSVData(); + if ( pSVData->maAppData.mxToolkitName ) + return *(pSVData->maAppData.mxToolkitName); + else + return OUString(); +} + +vcl::Window* Application::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; +} + +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.get(); +} + +void Application::SetFilterHdl( const Link<ConvertData&,bool>& rLink ) +{ + ImplGetSVData()->maGDIData.mpGrfConverter->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 bEventTestingMode = false; + +bool Application::IsEventTestingModeEnabled() +{ + return bEventTestingMode; +} + +void Application::EnableEventTestingMode() +{ + bEventTestingMode = true; +} + +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 const OUString aNone("none"); + 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); +} + +bool InitAccessBridge() +{ +// Disable MSAA bridge on UNIX +#if defined UNX + return true; +#else + bool bRet = ImplInitAccessBridge(); + + if( !bRet ) + { + // disable accessibility if the user chooses to continue + AllSettings aSettings = Application::GetSettings(); + MiscSettings aMisc = aSettings.GetMiscSettings(); + aMisc.SetEnableATToolSupport( false ); + aSettings.SetMiscSettings( aMisc ); + Application::SetSettings( aSettings ); + } + return bRet; +#endif // !UNX +} + +// 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) + { + // 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; +} + +} // namespace lok, namespace vcl + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/source/app/svdata.cxx b/vcl/source/app/svdata.cxx new file mode 100644 index 000000000..05d53060b --- /dev/null +++ b/vcl/source/app/svdata.cxx @@ -0,0 +1,491 @@ +/* -*- 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 <string.h> + +#include <comphelper/lok.hxx> +#include <comphelper/processfactory.hxx> +#include <tools/diagnose_ex.h> +#include <unotools/resmgr.hxx> +#include <sal/log.hxx> + +#include <configsettings.hxx> +#include <vcl/dockwin.hxx> +#include <vcl/menu.hxx> +#include <vcl/print.hxx> +#include <vcl/settings.hxx> +#include <vcl/svapp.hxx> +#include <vcl/virdev.hxx> +#include <vcl/wrkwin.hxx> +#include <vcl/uitest/logger.hxx> +#include <salframe.hxx> +#include <scrwnd.hxx> +#include <helpwin.hxx> +#include <vcl/toolkit/dialog.hxx> +#include <salinst.hxx> +#include <salgdi.hxx> +#include <svdata.hxx> +#include <salsys.hxx> +#include <units.hrc> +#include <print.h> + +#include <com/sun/star/accessibility/MSAAService.hpp> + +#include <config_features.h> +#if HAVE_FEATURE_OPENGL +#include <vcl/opengl/OpenGLContext.hxx> +#endif +#include <basegfx/utils/systemdependentdata.hxx> +#include <cppuhelper/basemutex.hxx> + +using namespace com::sun::star::uno; +using namespace com::sun::star::lang; +using namespace com::sun::star::awt; + +namespace +{ + struct private_aImplSVData : + public rtl::Static<ImplSVData, private_aImplSVData> {}; + /// Default instance ensures that ImplSVData::mpHelpData is never null. + struct private_aImplSVHelpData : + public rtl::Static<ImplSVHelpData, private_aImplSVHelpData> {}; + + /// Default instance ensures that ImplSVData::mpWinData is never null. + struct private_aImplSVWinData : + public rtl::Static<ImplSVWinData, private_aImplSVWinData> {}; + +} + +ImplSVData* ImplGetSVData() { + return &private_aImplSVData::get(); +} + +SalSystem* ImplGetSalSystem() +{ + ImplSVData* pSVData = ImplGetSVData(); + if( ! pSVData->mpSalSystem ) + pSVData->mpSalSystem.reset( pSVData->mpDefInst->CreateSalSystem() ); + return pSVData->mpSalSystem.get(); +} + +void ImplDeInitSVData() +{ + ImplSVData* pSVData = ImplGetSVData(); + + // delete global instance data + pSVData->mpSettingsConfigItem.reset(); + + pSVData->mpDockingManager.reset(); + + pSVData->maCtrlData.maFieldUnitStrings.clear(); + pSVData->maCtrlData.maCleanUnitStrings.clear(); + pSVData->maPaperNames.clear(); +} + +namespace +{ + typedef ::std::map< basegfx::SystemDependentData_SharedPtr, sal_uInt32 > EntryMap; + + class SystemDependentDataBuffer final : public basegfx::SystemDependentDataManager, protected cppu::BaseMutex + { + private: + std::unique_ptr<AutoTimer> maTimer; + EntryMap maEntries; + + DECL_LINK(implTimeoutHdl, Timer *, void); + + public: + SystemDependentDataBuffer(const char* pDebugName) + : basegfx::SystemDependentDataManager(), + maTimer(std::make_unique<AutoTimer>(pDebugName)) + { + maTimer->SetTimeout(1000); + maTimer->SetInvokeHandler(LINK(this, SystemDependentDataBuffer, implTimeoutHdl)); + } + + virtual ~SystemDependentDataBuffer() override + { + flushAll(); + } + + void startUsage(basegfx::SystemDependentData_SharedPtr& rData) override + { + ::osl::MutexGuard aGuard(m_aMutex); + EntryMap::iterator aFound(maEntries.find(rData)); + + if(aFound == maEntries.end()) + { + if(maTimer && !maTimer->IsActive()) + { + maTimer->Start(); + } + + maEntries[rData] = rData->calculateCombinedHoldCyclesInSeconds(); + } + } + + void endUsage(basegfx::SystemDependentData_SharedPtr& rData) override + { + ::osl::MutexGuard aGuard(m_aMutex); + EntryMap::iterator aFound(maEntries.find(rData)); + + if(aFound != maEntries.end()) + { + maEntries.erase(aFound); + } + } + + void touchUsage(basegfx::SystemDependentData_SharedPtr& rData) override + { + ::osl::MutexGuard aGuard(m_aMutex); + EntryMap::iterator aFound(maEntries.find(rData)); + + if(aFound != maEntries.end()) + { + aFound->second = rData->calculateCombinedHoldCyclesInSeconds(); + } + } + + void flushAll() override + { + ::osl::MutexGuard aGuard(m_aMutex); + + if(maTimer) + { + maTimer->Stop(); + maTimer.reset(); + } + + maEntries.clear(); + } + }; + + IMPL_LINK_NOARG(SystemDependentDataBuffer, implTimeoutHdl, Timer *, void) + { + ::osl::MutexGuard aGuard(m_aMutex); + EntryMap::iterator aIter(maEntries.begin()); + + while(aIter != maEntries.end()) + { + if(aIter->second) + { + aIter->second--; + ++aIter; + } + else + { + aIter = maEntries.erase(aIter); + } + } + + if (maEntries.empty()) + maTimer->Stop(); + } +} + +basegfx::SystemDependentDataManager& ImplGetSystemDependentDataManager() +{ + static SystemDependentDataBuffer aSystemDependentDataBuffer("vcl SystemDependentDataBuffer aSystemDependentDataBuffer"); + + return aSystemDependentDataBuffer; +} + +/// Returns either the application window, or the default GL context window +vcl::Window* ImplGetDefaultWindow() +{ + ImplSVData* pSVData = ImplGetSVData(); + if (pSVData->maFrameData.mpAppWin) + return pSVData->maFrameData.mpAppWin; + else + return ImplGetDefaultContextWindow(); +} + +/// returns the default window created to hold the persistent VCL GL context. +vcl::Window *ImplGetDefaultContextWindow() +{ + ImplSVData* pSVData = ImplGetSVData(); + + // Double check locking on mpDefaultWin. + if ( !pSVData->mpDefaultWin ) + { + SolarMutexGuard aGuard; + + if (!pSVData->mpDefaultWin && !pSVData->mbDeInit) + { + try + { + SAL_INFO( "vcl", "ImplGetDefaultWindow(): No AppWindow" ); + + pSVData->mpDefaultWin = VclPtr<WorkWindow>::Create( nullptr, WB_DEFAULTWIN ); + pSVData->mpDefaultWin->SetText( "VCL ImplGetDefaultWindow" ); + +#if HAVE_FEATURE_OPENGL + // Add a reference to the default context so it never gets deleted + rtl::Reference<OpenGLContext> pContext = pSVData->mpDefaultWin->GetGraphics()->GetOpenGLContext(); + if( pContext.is() ) + pContext->acquire(); +#endif + } + catch (const css::uno::Exception&) + { + TOOLS_WARN_EXCEPTION("vcl", "unable to create Default Window"); + } + } + } + + return pSVData->mpDefaultWin; +} + +const std::locale& ImplGetResLocale() +{ + ImplSVData* pSVData = ImplGetSVData(); + if (!pSVData->mbResLocaleSet || comphelper::LibreOfficeKit::isActive()) + { + pSVData->maResLocale = Translate::Create("vcl"); + pSVData->mbResLocaleSet = true; + } + return pSVData->maResLocale; +} + +OUString VclResId(const char* pId) +{ + return Translate::get(pId, ImplGetResLocale()); +} + +const FieldUnitStringList& ImplGetFieldUnits() +{ + ImplSVData* pSVData = ImplGetSVData(); + if( pSVData->maCtrlData.maFieldUnitStrings.empty() ) + { + sal_uInt32 nUnits = SAL_N_ELEMENTS(SV_FUNIT_STRINGS); + pSVData->maCtrlData.maFieldUnitStrings.reserve( nUnits ); + for (sal_uInt32 i = 0; i < nUnits; i++) + { + std::pair<OUString, FieldUnit> aElement(VclResId(SV_FUNIT_STRINGS[i].first), SV_FUNIT_STRINGS[i].second); + pSVData->maCtrlData.maFieldUnitStrings.push_back( aElement ); + } + } + return pSVData->maCtrlData.maFieldUnitStrings; +} + +const FieldUnitStringList& ImplGetCleanedFieldUnits() +{ + ImplSVData* pSVData = ImplGetSVData(); + if( pSVData->maCtrlData.maCleanUnitStrings.empty() ) + { + const FieldUnitStringList& rUnits = ImplGetFieldUnits(); + size_t nUnits = rUnits.size(); + pSVData->maCtrlData.maCleanUnitStrings.reserve(nUnits); + for (size_t i = 0; i < nUnits; ++i) + { + OUString aUnit(rUnits[i].first); + aUnit = aUnit.replaceAll(" ", ""); + aUnit = aUnit.toAsciiLowerCase(); + std::pair<OUString, FieldUnit> aElement(aUnit, rUnits[i].second); + pSVData->maCtrlData.maCleanUnitStrings.push_back(aElement); + } + } + return pSVData->maCtrlData.maCleanUnitStrings; +} + +DockingManager* ImplGetDockingManager() +{ + ImplSVData* pSVData = ImplGetSVData(); + if ( !pSVData->mpDockingManager ) + pSVData->mpDockingManager.reset(new DockingManager()); + + return pSVData->mpDockingManager.get(); +} + +BlendFrameCache* ImplGetBlendFrameCache() +{ + ImplSVData* pSVData = ImplGetSVData(); + if ( !pSVData->mpBlendFrameCache) + pSVData->mpBlendFrameCache.reset( new BlendFrameCache() ); + + return pSVData->mpBlendFrameCache.get(); +} + +#ifdef _WIN32 +bool ImplInitAccessBridge() +{ + ImplSVData* pSVData = ImplGetSVData(); + if( ! pSVData->mxAccessBridge.is() ) + { + css::uno::Reference< XComponentContext > xContext(comphelper::getProcessComponentContext()); + + if (!HasAtHook() && !getenv("SAL_FORCE_IACCESSIBLE2")) + { + SAL_INFO("vcl", "Apparently no running AT -> " + "not enabling IAccessible2 integration"); + } + else + { + try { + pSVData->mxAccessBridge + = css::accessibility::MSAAService::create(xContext); + SAL_INFO("vcl", "got IAccessible2 bridge"); + return true; + } catch (css::uno::DeploymentException &) { + TOOLS_WARN_EXCEPTION( + "vcl", + "got no IAccessible2 bridge"); + return false; + } + } + } + + return true; +} +#endif + +void LocaleConfigurationListener::ConfigurationChanged( utl::ConfigurationBroadcaster*, ConfigurationHints nHint ) +{ + AllSettings::LocaleSettingsChanged( nHint ); +} + +ImplSVWinData* CreateSVWinData() +{ + if (!comphelper::LibreOfficeKit::isActive()) + return nullptr; + + ImplSVWinData* p = new ImplSVWinData; + + ImplSVData* pSVData = ImplGetSVData(); + assert(pSVData && pSVData->mpWinData); + + p->mpFocusWin = pSVData->mpWinData->mpFocusWin; + return p; +} + +void DestroySVWinData(ImplSVWinData* pData) +{ + delete pData; +} + +void SetSVWinData(ImplSVWinData* pSVWinData) +{ + if (!comphelper::LibreOfficeKit::isActive()) + return; + + ImplSVData* pSVData = ImplGetSVData(); + assert(pSVData != nullptr); + + if (pSVData->mpWinData != pSVWinData) + { + // If current one is the static, clean it up to avoid having lingering references. + if (pSVData->mpWinData == &private_aImplSVWinData::get()) + { + pSVData->mpWinData->mpFocusWin.reset(); + } + + pSVData->mpWinData = pSVWinData; + if (pSVData->mpWinData == nullptr) + { + pSVData->mpWinData = &private_aImplSVWinData::get(); // Never leave it null. + } + } +} + +ImplSVData::ImplSVData() +{ + mpHelpData = &private_aImplSVHelpData::get(); + mpWinData = &private_aImplSVWinData::get(); +} + +ImplSVHelpData* CreateSVHelpData() +{ + if (!comphelper::LibreOfficeKit::isActive()) + return nullptr; + + ImplSVHelpData* pNewData = new ImplSVHelpData; + + // Set options set globally + ImplSVHelpData& aStaticHelpData = private_aImplSVHelpData::get(); + pNewData->mbContextHelp = aStaticHelpData.mbContextHelp; + pNewData->mbExtHelp = aStaticHelpData.mbExtHelp; + pNewData->mbExtHelpMode = aStaticHelpData.mbExtHelpMode; + pNewData->mbOldBalloonMode = aStaticHelpData.mbOldBalloonMode; + pNewData->mbBalloonHelp = aStaticHelpData.mbBalloonHelp; + pNewData->mbQuickHelp = aStaticHelpData.mbQuickHelp; + + return pNewData; +} + +void DestroySVHelpData(ImplSVHelpData* pSVHelpData) +{ + if (!comphelper::LibreOfficeKit::isActive()) + return; + + // Change the SVData's help date if necessary + if(ImplGetSVData()->mpHelpData == pSVHelpData) + { + ImplGetSVData()->mpHelpData = &private_aImplSVHelpData::get(); + } + + if(pSVHelpData) + { + ImplDestroyHelpWindow(*pSVHelpData, false); + delete pSVHelpData; + } +} + +void SetSVHelpData(ImplSVHelpData* pSVHelpData) +{ + if (!comphelper::LibreOfficeKit::isActive()) + return; + + ImplSVData* pSVData = ImplGetSVData(); + if (pSVData->mpHelpData != pSVHelpData) + { + // If current one is the static, clean it up to avoid having lingering references. + if (pSVData->mpHelpData == &private_aImplSVHelpData::get()) + { + pSVData->mpHelpData->mpHelpWin.reset(); + } + + pSVData->mpHelpData = pSVHelpData; + if (pSVData->mpHelpData == nullptr) + { + pSVData->mpHelpData = &private_aImplSVHelpData::get(); // Never leave it null. + } + } +} + +ImplSVHelpData& ImplGetSVHelpData() +{ + ImplSVData* pSVData = ImplGetSVData(); + if(pSVData->mpHelpData) + { + return *pSVData->mpHelpData; + } + else + { + return private_aImplSVHelpData::get(); + } +} + +ImplSVData::~ImplSVData() {} +ImplSVAppData::~ImplSVAppData() {} +ImplSVGDIData::~ImplSVGDIData() {} +ImplSVFrameData::~ImplSVFrameData() {} +ImplSVWinData::~ImplSVWinData() {} +ImplSVHelpData::~ImplSVHelpData() {} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/source/app/svmain.cxx b/vcl/source/app/svmain.cxx new file mode 100644 index 000000000..e571c2f26 --- /dev/null +++ b/vcl/source/app/svmain.cxx @@ -0,0 +1,684 @@ +/* -*- 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 <sal/log.hxx> + +#include <cassert> + +#include <osl/file.hxx> +#include <osl/signal.h> + +#include <desktop/exithelper.h> + +#include <comphelper/processfactory.hxx> +#include <comphelper/asyncnotification.hxx> +#include <i18nlangtag/mslangid.hxx> +#include <unotools/syslocaleoptions.hxx> +#include <vcl/svapp.hxx> +#include <vcl/vclmain.hxx> +#include <vcl/wrkwin.hxx> +#include <vcl/cvtgrf.hxx> +#include <vcl/scheduler.hxx> +#include <vcl/image.hxx> +#include <vcl/ImageTree.hxx> +#include <vcl/settings.hxx> +#include <vcl/toolkit/unowrap.hxx> +#include <configsettings.hxx> +#include <vcl/lazydelete.hxx> +#include <vcl/embeddedfontshelper.hxx> +#include <vcl/toolkit/dialog.hxx> +#include <vcl/menu.hxx> +#include <vcl/virdev.hxx> +#include <vcl/print.hxx> +#include <debugevent.hxx> +#include <scrwnd.hxx> + +#ifdef _WIN32 +#include <svsys.h> +#include <process.h> +#include <ole2.h> +#else +#include <stdlib.h> +#endif + +#ifdef ANDROID +#include <cppuhelper/bootstrap.hxx> +#include <jni.h> +#endif + +#include <impfontcache.hxx> +#include <salinst.hxx> +#include <svdata.hxx> +#include <vcl/svmain.hxx> +#include <dbggui.hxx> +#include <accmgr.hxx> +#include <PhysicalFontCollection.hxx> +#include <print.h> +#include <salsys.hxx> +#include <saltimer.hxx> +#include <displayconnectiondispatch.hxx> + +#include <config_features.h> +#include <config_feature_opencl.h> + +#include <osl/process.h> +#include <com/sun/star/lang/XComponent.hpp> +#include <com/sun/star/frame/Desktop.hpp> + +#ifdef _WIN32 +#include <com/sun/star/datatransfer/clipboard/XClipboard.hpp> +#endif + +#include <comphelper/lok.hxx> +#include <cppuhelper/implbase.hxx> +#include <uno/current_context.hxx> + +#include <opencl/OpenCLZone.hxx> +#include <opengl/zone.hxx> +#include <skia/zone.hxx> +#include <watchdog.hxx> + +#include <basegfx/utils/systemdependentdata.hxx> +#include <tools/diagnose_ex.h> + +#if OSL_DEBUG_LEVEL > 0 +#include <typeinfo> +#include <rtl/strbuf.hxx> +#endif + +using namespace ::com::sun::star; + +static bool g_bIsLeanException; + +static oslSignalAction VCLExceptionSignal_impl( void* /*pData*/, oslSignalInfo* pInfo) +{ + static volatile bool bIn = false; + + // if we crash again, bail out immediately + if ( bIn || g_bIsLeanException) + return osl_Signal_ActCallNextHdl; + + ExceptionCategory nVCLException = ExceptionCategory::NONE; + + // UAE + if ( (pInfo->Signal == osl_Signal_AccessViolation) || + (pInfo->Signal == osl_Signal_IntegerDivideByZero) || + (pInfo->Signal == osl_Signal_FloatDivideByZero) || + (pInfo->Signal == osl_Signal_DebugBreak) ) + { + nVCLException = ExceptionCategory::System; +#if HAVE_FEATURE_OPENGL + if (OpenGLZone::isInZone()) + OpenGLZone::hardDisable(); +#endif +#if HAVE_FEATURE_SKIA + if (SkiaZone::isInZone()) + SkiaZone::hardDisable(); +#endif +#if HAVE_FEATURE_OPENCL + if (OpenCLZone::isInZone()) + { + OpenCLZone::hardDisable(); +#ifdef _WIN32 + if (OpenCLInitialZone::isInZone()) + TerminateProcess(GetCurrentProcess(), EXITHELPER_NORMAL_RESTART); +#endif + } +#endif + } + + // RC + if ((pInfo->Signal == osl_Signal_User) && + (pInfo->UserSignal == OSL_SIGNAL_USER_RESOURCEFAILURE) ) + nVCLException = ExceptionCategory::ResourceNotLoaded; + + // DISPLAY-Unix + if ((pInfo->Signal == osl_Signal_User) && + (pInfo->UserSignal == OSL_SIGNAL_USER_X11SUBSYSTEMERROR) ) + nVCLException = ExceptionCategory::UserInterface; + + if ( nVCLException != ExceptionCategory::NONE ) + { + bIn = true; + + vcl::SolarMutexTryAndBuyGuard aLock; + if( aLock.isAcquired()) + { + // do not stop timer because otherwise the UAE-Box will not be painted as well + ImplSVData* pSVData = ImplGetSVData(); + if ( pSVData->mpApp ) + { + SystemWindowFlags nOldMode = Application::GetSystemWindowMode(); + Application::SetSystemWindowMode( nOldMode & ~SystemWindowFlags::NOAUTOMODE ); + pSVData->mpApp->Exception( nVCLException ); + Application::SetSystemWindowMode( nOldMode ); + } + } + bIn = false; + } + + return osl_Signal_ActCallNextHdl; + +} + +int ImplSVMain() +{ + // The 'real' SVMain() + ImplSVData* pSVData = ImplGetSVData(); + + SAL_WARN_IF( !pSVData->mpApp, "vcl", "no instance of class Application" ); + + int nReturn = EXIT_FAILURE; + + const bool bWasInitVCL = IsVCLInit(); + const bool bInit = bWasInitVCL || InitVCL(); + int nRet = 0; + if (!bWasInitVCL && bInit && pSVData->mpDefInst->SVMainHook(&nRet)) + return nRet; + + if( bInit ) + { + // call application main + pSVData->maAppData.mbInAppMain = true; + nReturn = pSVData->mpApp->Main(); + pSVData->maAppData.mbInAppMain = false; + } + + if( pSVData->mxDisplayConnection.is() ) + { + pSVData->mxDisplayConnection->terminate(); + pSVData->mxDisplayConnection.clear(); + } + + // This is a hack to work around the problem of the asynchronous nature + // of bridging accessibility through Java: on shutdown there might still + // be some events in the AWT EventQueue, which need the SolarMutex which + // - on the other hand - is destroyed in DeInitVCL(). So empty the queue + // here .. + if( pSVData->mxAccessBridge.is() ) + { + { + SolarMutexReleaser aReleaser; + pSVData->mxAccessBridge->dispose(); + } + pSVData->mxAccessBridge.clear(); + } + + WatchdogThread::stop(); + DeInitVCL(); + + return nReturn; +} + +int SVMain() +{ + return ImplSVMain(); +} + +// This variable is set when no Application object has been instantiated +// before InitVCL is called +static Application * pOwnSvApp = nullptr; + +// Exception handler. pExceptionHandler != NULL => VCL already inited +static oslSignalHandler pExceptionHandler = nullptr; + +namespace { + +class DesktopEnvironmentContext: public cppu::WeakImplHelper< css::uno::XCurrentContext > +{ +public: + explicit DesktopEnvironmentContext( const css::uno::Reference< css::uno::XCurrentContext > & ctx) + : m_xNextContext( ctx ) {} + + // XCurrentContext + virtual css::uno::Any SAL_CALL getValueByName( const OUString& Name ) override; + +private: + css::uno::Reference< css::uno::XCurrentContext > m_xNextContext; +}; + +} + +uno::Any SAL_CALL DesktopEnvironmentContext::getValueByName( const OUString& Name) +{ + uno::Any retVal; + + if ( Name == "system.desktop-environment" ) + { + retVal <<= Application::GetDesktopEnvironment(); + } + else if( m_xNextContext.is() ) + { + // Call next context in chain if found + retVal = m_xNextContext->getValueByName( Name ); + } + return retVal; +} + +bool IsVCLInit() +{ + ImplSVData* pSVData = ImplGetSVData(); + return pExceptionHandler != nullptr && + pSVData->mpApp != nullptr && + pSVData->mpDefInst != nullptr; +} + +#ifdef DBG_UTIL +namespace vclmain +{ + bool isAlive() + { + return ImplGetSVData()->mpDefInst; + } +} +#endif + + +bool InitVCL() +{ + if (IsVCLInit()) + { + SAL_INFO("vcl.app", "Double initialization of vcl"); + return true; + } + + if( pExceptionHandler != nullptr ) + return false; + + EmbeddedFontsHelper::clearTemporaryFontFiles(); + + if( !ImplGetSVData()->mpApp ) + { + pOwnSvApp = new Application(); + } + + ImplSVData* pSVData = ImplGetSVData(); + + // remember Main-Thread-Id + pSVData->mnMainThreadId = ::osl::Thread::getCurrentIdentifier(); + + // Initialize Sal + pSVData->mpDefInst = CreateSalInstance(); + if ( !pSVData->mpDefInst ) + return false; + + // Desktop Environment context (to be able to get value of "system.desktop-environment" as soon as possible) + css::uno::setCurrentContext( + new DesktopEnvironmentContext( css::uno::getCurrentContext() ) ); + + // Initialize application instance (should be done after initialization of VCL SAL part) + if (pSVData->mpApp) + { + // call init to initialize application class + // soffice/sfx implementation creates the global service manager + pSVData->mpApp->Init(); + } + + try + { + //Now that uno has been bootstrapped we can ask the config what the UI language is so that we can + //force that in as $LANGUAGE. That way we can get gtk to render widgets RTL + //if we have a RTL UI in an otherwise LTR locale and get gettext using externals (e.g. python) + //to match their translations to our preferred UI language + OUString aLocaleString(SvtSysLocaleOptions().GetRealUILanguageTag().getGlibcLocaleString(".UTF-8")); + if (!aLocaleString.isEmpty()) + { + MsLangId::getSystemUILanguage(); //call this now to pin what the system UI really was + OUString envVar("LANGUAGE"); + osl_setEnvironment(envVar.pData, aLocaleString.pData); + } + } + catch (const uno::Exception &) + { + TOOLS_INFO_EXCEPTION("vcl.app", "Unable to get ui language:"); + } + + pSVData->mpDefInst->AfterAppInit(); + + // Fetch AppFileName and make it absolute before the workdir changes... + OUString aExeFileName; + osl_getExecutableFile( &aExeFileName.pData ); + + // convert path to native file format + OUString aNativeFileName; + osl::FileBase::getSystemPathFromFileURL( aExeFileName, aNativeFileName ); + pSVData->maAppData.mxAppFileName = aNativeFileName; + + // Initialize global data + pSVData->maGDIData.mxScreenFontList = std::make_shared<PhysicalFontCollection>(); + pSVData->maGDIData.mxScreenFontCache = std::make_shared<ImplFontCache>(); + pSVData->maGDIData.mpGrfConverter = new GraphicConverter; + + g_bIsLeanException = getenv("LO_LEAN_EXCEPTION") != nullptr; + // Set exception handler + pExceptionHandler = osl_addSignalHandler(VCLExceptionSignal_impl, nullptr); + +#ifndef NDEBUG + DbgGUIInitSolarMutexCheck(); +#endif + +#if OSL_DEBUG_LEVEL > 0 + DebugEventInjector::getCreate(); +#endif + +#ifndef _WIN32 + // Clear startup notification details for child processes + // See https://bugs.freedesktop.org/show_bug.cgi?id=11375 for discussion + unsetenv("DESKTOP_STARTUP_ID"); +#endif + + return true; +} + +namespace +{ + +/** Serves for destroying the VCL UNO wrapper as late as possible. This avoids + crash at exit in some special cases when a11y is enabled (e.g., when + a bundled extension is registered/deregistered during startup, forcing exit + while the app is still in splash screen.) + */ +class VCLUnoWrapperDeleter : public cppu::WeakImplHelper<css::lang::XEventListener> +{ + virtual void SAL_CALL disposing(lang::EventObject const& rSource) override; +}; + +void +VCLUnoWrapperDeleter::disposing(lang::EventObject const& /* rSource */) +{ + ImplSVData* const pSVData = ImplGetSVData(); + if (pSVData && pSVData->mpUnoWrapper) + { + pSVData->mpUnoWrapper->Destroy(); + pSVData->mpUnoWrapper = nullptr; + } +} + +} + +void DeInitVCL() +{ + // The LOK Windows map container should be empty + assert(vcl::Window::IsLOKWindowsEmpty()); + + //rhbz#1444437, when using LibreOffice like a library you can't realistically + //tear everything down and recreate them on the next call, there's too many + //(c++) singletons that point to stuff that gets deleted during shutdown + //which won't be recreated on restart. + if (comphelper::LibreOfficeKit::isActive()) + return; + + { + SolarMutexReleaser r; // unblock threads blocked on that so we can join + ::comphelper::JoinAsyncEventNotifiers(); + } + ImplSVData* pSVData = ImplGetSVData(); + + // lp#1560328: clear cache before disposing rest of VCL + if(pSVData->mpBlendFrameCache) + pSVData->mpBlendFrameCache->m_aLastResult.Clear(); + pSVData->mbDeInit = true; + + vcl::DeleteOnDeinitBase::ImplDeleteOnDeInit(); + +#if OSL_DEBUG_LEVEL > 0 + OStringBuffer aBuf( 256 ); + aBuf.append( "DeInitVCL: some top Windows are still alive\n" ); + long nTopWindowCount = Application::GetTopWindowCount(); + long nBadTopWindows = nTopWindowCount; + for( long i = 0; i < nTopWindowCount; i++ ) + { + vcl::Window* pWin = Application::GetTopWindow( i ); + // default window will be destroyed further down + // but may still be useful during deinit up to that point + if( pWin == pSVData->mpDefaultWin ) + nBadTopWindows--; + else + { + aBuf.append( "text = \"" ); + aBuf.append( OUStringToOString( pWin->GetText(), osl_getThreadTextEncoding() ) ); + aBuf.append( "\" type = \"" ); + aBuf.append( typeid(*pWin).name() ); + aBuf.append( "\", ptr = 0x" ); + aBuf.append( reinterpret_cast<sal_Int64>( pWin ), 16 ); + aBuf.append( "\n" ); + } + } + SAL_WARN_IF( nBadTopWindows!=0, "vcl", aBuf.getStr() ); +#endif + + ImageTree::get().shutdown(); + + osl_removeSignalHandler( pExceptionHandler); + pExceptionHandler = nullptr; + + // free global data + if (pSVData->maGDIData.mpGrfConverter) + { + delete pSVData->maGDIData.mpGrfConverter; + pSVData->maGDIData.mpGrfConverter = nullptr; + } + + pSVData->mpSettingsConfigItem.reset(); + + // prevent unnecessary painting during Scheduler shutdown + // as this processes all pending events in debug builds. + ImplGetSystemDependentDataManager().flushAll(); + + Scheduler::ImplDeInitScheduler(); + + pSVData->mpWinData->maMsgBoxImgList.clear(); + pSVData->maCtrlData.maCheckImgList.clear(); + pSVData->maCtrlData.maRadioImgList.clear(); + pSVData->maCtrlData.mpDisclosurePlus.reset(); + pSVData->maCtrlData.mpDisclosureMinus.reset(); + pSVData->mpDefaultWin.disposeAndClear(); + +#if defined _WIN32 + // See GetSystemClipboard (vcl/source/treelist/transfer2.cxx): + if (auto const comp = css::uno::Reference<css::lang::XComponent>( + pSVData->m_xSystemClipboard, css::uno::UNO_QUERY)) + { + SolarMutexReleaser r; // unblock pending "clipboard content changed" notifications + comp->dispose(); // will use CWinClipbImpl::s_aMutex + } +#endif + +#ifndef NDEBUG + DbgGUIDeInitSolarMutexCheck(); +#endif + + if ( pSVData->mpUnoWrapper ) + { + try + { + uno::Reference<frame::XDesktop2> const xDesktop = frame::Desktop::create( + comphelper::getProcessComponentContext() ); + xDesktop->addEventListener(new VCLUnoWrapperDeleter); + } + catch (uno::Exception const&) + { + // ignore + } + } + + if( pSVData->mpApp || pSVData->maDeInitHook.IsSet() ) + { + SolarMutexReleaser aReleaser; + // call deinit to deinitialize application class + // soffice/sfx implementation disposes the global service manager + // Warning: After this call you can't call uno services + if( pSVData->mpApp ) + { + pSVData->mpApp->DeInit(); + } + if( pSVData->maDeInitHook.IsSet() ) + { + pSVData->maDeInitHook.Call(nullptr); + } + } + + if ( pSVData->maAppData.mpSettings ) + { + if ( pSVData->maAppData.mpCfgListener ) + { + pSVData->maAppData.mpSettings->GetSysLocale().GetOptions().RemoveListener( pSVData->maAppData.mpCfgListener ); + delete pSVData->maAppData.mpCfgListener; + } + + pSVData->maAppData.mpSettings.reset(); + } + if ( pSVData->maAppData.mpAccelMgr ) + { + delete pSVData->maAppData.mpAccelMgr; + pSVData->maAppData.mpAccelMgr = nullptr; + } + pSVData->maAppData.maKeyListeners.clear(); + pSVData->mpBlendFrameCache.reset(); + + ImplDeletePrnQueueList(); + + // destroy all Sal interfaces before destroying the instance + // and thereby unloading the plugin + pSVData->mpSalSystem.reset(); + assert( !pSVData->maSchedCtx.mpSalTimer ); + delete pSVData->maSchedCtx.mpSalTimer; + pSVData->maSchedCtx.mpSalTimer = nullptr; + + pSVData->mpDefaultWin = nullptr; + pSVData->mpIntroWindow = nullptr; + pSVData->maAppData.mpActivePopupMenu = nullptr; + pSVData->maAppData.mpWheelWindow = nullptr; + pSVData->maGDIData.mpFirstWinGraphics = nullptr; + pSVData->maGDIData.mpLastWinGraphics = nullptr; + pSVData->maGDIData.mpFirstVirGraphics = nullptr; + pSVData->maGDIData.mpLastVirGraphics = nullptr; + pSVData->maGDIData.mpFirstPrnGraphics = nullptr; + pSVData->maGDIData.mpLastPrnGraphics = nullptr; + pSVData->maGDIData.mpFirstVirDev = nullptr; + pSVData->maGDIData.mpFirstPrinter = nullptr; + pSVData->maFrameData.mpFirstFrame = nullptr; + pSVData->maFrameData.mpAppWin = nullptr; + pSVData->maFrameData.mpActiveApplicationFrame = nullptr; + pSVData->mpWinData->mpCaptureWin = nullptr; + pSVData->mpWinData->mpLastDeacWin = nullptr; + pSVData->mpWinData->mpFirstFloat = nullptr; + pSVData->mpWinData->mpExecuteDialogs.clear(); + pSVData->mpWinData->mpExtTextInputWin = nullptr; + pSVData->mpWinData->mpTrackWin = nullptr; + pSVData->mpWinData->mpAutoScrollWin = nullptr; + pSVData->mpWinData->mpLastWheelWindow = nullptr; + + pSVData->maGDIData.mxScreenFontList.reset(); + pSVData->maGDIData.mxScreenFontCache.reset(); + pSVData->maGDIData.maScaleCache.remove_if([](const lru_scale_cache::key_value_pair_t&) + { return true; }); + + pSVData->maGDIData.maThemeDrawCommandsCache.clear(); + pSVData->maGDIData.maThemeImageCache.clear(); + + // Deinit Sal + if (pSVData->mpDefInst) + { + DestroySalInstance( pSVData->mpDefInst ); + pSVData->mpDefInst = nullptr; + } + + if( pOwnSvApp ) + { + delete pOwnSvApp; + pOwnSvApp = nullptr; + } + + EmbeddedFontsHelper::clearTemporaryFontFiles(); +} + +namespace { + +// only one call is allowed +struct WorkerThreadData +{ + oslWorkerFunction pWorker; + void * pThreadData; + WorkerThreadData( oslWorkerFunction pWorker_, void * pThreadData_ ) + : pWorker( pWorker_ ) + , pThreadData( pThreadData_ ) + { + } +}; + +} + +#ifdef _WIN32 +static HANDLE hThreadID = nullptr; +static unsigned __stdcall threadmain( void *pArgs ) +{ + OleInitialize( nullptr ); + static_cast<WorkerThreadData*>(pArgs)->pWorker( static_cast<WorkerThreadData*>(pArgs)->pThreadData ); + delete static_cast<WorkerThreadData*>(pArgs); + OleUninitialize(); + hThreadID = nullptr; + return 0; +} +#else +static oslThread hThreadID = nullptr; +extern "C" +{ +static void MainWorkerFunction( void* pArgs ) +{ + static_cast<WorkerThreadData*>(pArgs)->pWorker( static_cast<WorkerThreadData*>(pArgs)->pThreadData ); + delete static_cast<WorkerThreadData*>(pArgs); + hThreadID = nullptr; +} +} // extern "C" +#endif + +void CreateMainLoopThread( oslWorkerFunction pWorker, void * pThreadData ) +{ +#ifdef _WIN32 + // sal thread always call CoInitializeEx, so a system dependent implementation is necessary + + unsigned uThreadID; + hThreadID = reinterpret_cast<HANDLE>(_beginthreadex( + nullptr, // no security handle + 0, // stacksize 0 means default + threadmain, // thread worker function + new WorkerThreadData( pWorker, pThreadData ), // arguments for worker function + 0, // 0 means: create immediately otherwise use CREATE_SUSPENDED + &uThreadID )); // thread id to fill +#else + hThreadID = osl_createThread( MainWorkerFunction, new WorkerThreadData( pWorker, pThreadData ) ); +#endif +} + +void JoinMainLoopThread() +{ + if( hThreadID ) + { +#ifdef _WIN32 + WaitForSingleObject(hThreadID, INFINITE); +#else + osl_joinWithThread(hThreadID); + osl_destroyThread( hThreadID ); +#endif + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/source/app/timer.cxx b/vcl/source/app/timer.cxx new file mode 100644 index 000000000..75a97d867 --- /dev/null +++ b/vcl/source/app/timer.cxx @@ -0,0 +1,102 @@ +/* -*- 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/log.hxx> +#include <vcl/timer.hxx> +#include <vcl/scheduler.hxx> +#include <schedulerimpl.hxx> + +void Timer::SetDeletionFlags() +{ + // If no AutoTimer, then stop. + if ( !mbAuto ) + Task::SetDeletionFlags(); +} + +sal_uInt64 Timer::UpdateMinPeriod( sal_uInt64 nTimeNow ) const +{ + sal_uInt64 nWakeupTime = GetSchedulerData()->mnUpdateTime + mnTimeout; + return ( nWakeupTime <= nTimeNow ) + ? Scheduler::ImmediateTimeoutMs : nWakeupTime - nTimeNow; +} + +Timer::Timer( bool bAuto, const char *pDebugName ) + : Task( pDebugName ) + , mnTimeout( Scheduler::ImmediateTimeoutMs ) + , mbAuto( bAuto ) +{ + SetPriority( TaskPriority::DEFAULT ); +} + +Timer::Timer( const char *pDebugName ) + : Timer( false, pDebugName ) +{ +} + +Timer::Timer( const Timer& rTimer ) + : Timer( rTimer.mbAuto, rTimer.GetDebugName() ) +{ + maInvokeHandler = rTimer.maInvokeHandler; + mnTimeout = rTimer.mnTimeout; +} + +Timer::~Timer() +{ +} + +Timer& Timer::operator=( const Timer& rTimer ) +{ + Task::operator=( rTimer ); + maInvokeHandler = rTimer.maInvokeHandler; + mnTimeout = rTimer.mnTimeout; + SAL_WARN_IF( mbAuto != rTimer.mbAuto, "vcl.schedule", + "Copying Timer with different mbAuto value!" ); + return *this; +} + +void Timer::Invoke() +{ + maInvokeHandler.Call( this ); +} + +void Timer::Invoke( Timer *arg ) +{ + maInvokeHandler.Call( arg ); +} + +void Timer::Start() +{ + Task::Start(); + Task::StartTimer( mnTimeout ); +} + +void Timer::SetTimeout( sal_uInt64 nNewTimeout ) +{ + mnTimeout = nNewTimeout; + // If timer is active, then renew clock. + if ( IsActive() ) + StartTimer( mnTimeout ); +} + +AutoTimer::AutoTimer( const char *pDebugName ) + : Timer( true, pDebugName ) +{ +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/source/app/unohelp.cxx b/vcl/source/app/unohelp.cxx new file mode 100644 index 000000000..6d9e6f31a --- /dev/null +++ b/vcl/source/app/unohelp.cxx @@ -0,0 +1,233 @@ +/* -*- 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 <vcl/svapp.hxx> +#include <vcl/unohelp.hxx> + +#include <osl/diagnose.h> + +#include <comphelper/processfactory.hxx> + +#include <com/sun/star/i18n/BreakIterator.hpp> +#include <com/sun/star/i18n/CharacterClassification.hpp> +#include <com/sun/star/awt/FontWeight.hpp> +#include <com/sun/star/awt/FontWidth.hpp> +#include <com/sun/star/awt/XExtendedToolkit.hpp> +#include <com/sun/star/accessibility/AccessibleEventObject.hpp> +#include <com/sun/star/accessibility/AccessibleStateType.hpp> + +using namespace ::com::sun::star; + +uno::Reference < i18n::XBreakIterator > vcl::unohelper::CreateBreakIterator() +{ + uno::Reference< uno::XComponentContext > xContext = comphelper::getProcessComponentContext(); + return i18n::BreakIterator::create(xContext); +} + +uno::Reference < i18n::XCharacterClassification > vcl::unohelper::CreateCharacterClassification() +{ + return i18n::CharacterClassification::create( comphelper::getProcessComponentContext() ); +} + +void vcl::unohelper::NotifyAccessibleStateEventGlobally( const css::accessibility::AccessibleEventObject& rEventObject ) +{ + css::uno::Reference< css::awt::XExtendedToolkit > xExtToolkit( Application::GetVCLToolkit(), uno::UNO_QUERY ); + if ( xExtToolkit.is() ) + { + // Only for focus events + sal_Int16 nType = css::accessibility::AccessibleStateType::INVALID; + rEventObject.NewValue >>= nType; + if ( nType == css::accessibility::AccessibleStateType::FOCUSED ) + xExtToolkit->fireFocusGained( rEventObject.Source ); + else + { + rEventObject.OldValue >>= nType; + if ( nType == css::accessibility::AccessibleStateType::FOCUSED ) + xExtToolkit->fireFocusLost( rEventObject.Source ); + } + + } +} + +float vcl::unohelper::ConvertFontWidth( FontWidth eWidth ) +{ + if( eWidth == WIDTH_DONTKNOW ) + return css::awt::FontWidth::DONTKNOW; + else if( eWidth == WIDTH_ULTRA_CONDENSED ) + return css::awt::FontWidth::ULTRACONDENSED; + else if( eWidth == WIDTH_EXTRA_CONDENSED ) + return css::awt::FontWidth::EXTRACONDENSED; + else if( eWidth == WIDTH_CONDENSED ) + return css::awt::FontWidth::CONDENSED; + else if( eWidth == WIDTH_SEMI_CONDENSED ) + return css::awt::FontWidth::SEMICONDENSED; + else if( eWidth == WIDTH_NORMAL ) + return css::awt::FontWidth::NORMAL; + else if( eWidth == WIDTH_SEMI_EXPANDED ) + return css::awt::FontWidth::SEMIEXPANDED; + else if( eWidth == WIDTH_EXPANDED ) + return css::awt::FontWidth::EXPANDED; + else if( eWidth == WIDTH_EXTRA_EXPANDED ) + return css::awt::FontWidth::EXTRAEXPANDED; + else if( eWidth == WIDTH_ULTRA_EXPANDED ) + return css::awt::FontWidth::ULTRAEXPANDED; + + OSL_FAIL( "Unknown FontWidth" ); + return css::awt::FontWidth::DONTKNOW; +} + +FontWidth vcl::unohelper::ConvertFontWidth( float f ) +{ + if( f <= css::awt::FontWidth::DONTKNOW ) + return WIDTH_DONTKNOW; + else if( f <= css::awt::FontWidth::ULTRACONDENSED ) + return WIDTH_ULTRA_CONDENSED; + else if( f <= css::awt::FontWidth::EXTRACONDENSED ) + return WIDTH_EXTRA_CONDENSED; + else if( f <= css::awt::FontWidth::CONDENSED ) + return WIDTH_CONDENSED; + else if( f <= css::awt::FontWidth::SEMICONDENSED ) + return WIDTH_SEMI_CONDENSED; + else if( f <= css::awt::FontWidth::NORMAL ) + return WIDTH_NORMAL; + else if( f <= css::awt::FontWidth::SEMIEXPANDED ) + return WIDTH_SEMI_EXPANDED; + else if( f <= css::awt::FontWidth::EXPANDED ) + return WIDTH_EXPANDED; + else if( f <= css::awt::FontWidth::EXTRAEXPANDED ) + return WIDTH_EXTRA_EXPANDED; + else if( f <= css::awt::FontWidth::ULTRAEXPANDED ) + return WIDTH_ULTRA_EXPANDED; + + OSL_FAIL( "Unknown FontWidth" ); + return WIDTH_DONTKNOW; +} + +float vcl::unohelper::ConvertFontWeight( FontWeight eWeight ) +{ + if( eWeight == WEIGHT_DONTKNOW ) + return css::awt::FontWeight::DONTKNOW; + else if( eWeight == WEIGHT_THIN ) + return css::awt::FontWeight::THIN; + else if( eWeight == WEIGHT_ULTRALIGHT ) + return css::awt::FontWeight::ULTRALIGHT; + else if( eWeight == WEIGHT_LIGHT ) + return css::awt::FontWeight::LIGHT; + else if( eWeight == WEIGHT_SEMILIGHT ) + return css::awt::FontWeight::SEMILIGHT; + else if( ( eWeight == WEIGHT_NORMAL ) || ( eWeight == WEIGHT_MEDIUM ) ) + return css::awt::FontWeight::NORMAL; + else if( eWeight == WEIGHT_SEMIBOLD ) + return css::awt::FontWeight::SEMIBOLD; + else if( eWeight == WEIGHT_BOLD ) + return css::awt::FontWeight::BOLD; + else if( eWeight == WEIGHT_ULTRABOLD ) + return css::awt::FontWeight::ULTRABOLD; + else if( eWeight == WEIGHT_BLACK ) + return css::awt::FontWeight::BLACK; + + OSL_FAIL( "Unknown FontWeight" ); + return css::awt::FontWeight::DONTKNOW; +} + +FontWeight vcl::unohelper::ConvertFontWeight( float f ) +{ + if( f <= css::awt::FontWeight::DONTKNOW ) + return WEIGHT_DONTKNOW; + else if( f <= css::awt::FontWeight::THIN ) + return WEIGHT_THIN; + else if( f <= css::awt::FontWeight::ULTRALIGHT ) + return WEIGHT_ULTRALIGHT; + else if( f <= css::awt::FontWeight::LIGHT ) + return WEIGHT_LIGHT; + else if( f <= css::awt::FontWeight::SEMILIGHT ) + return WEIGHT_SEMILIGHT; + else if( f <= css::awt::FontWeight::NORMAL ) + return WEIGHT_NORMAL; + else if( f <= css::awt::FontWeight::SEMIBOLD ) + return WEIGHT_SEMIBOLD; + else if( f <= css::awt::FontWeight::BOLD ) + return WEIGHT_BOLD; + else if( f <= css::awt::FontWeight::ULTRABOLD ) + return WEIGHT_ULTRABOLD; + else if( f <= css::awt::FontWeight::BLACK ) + return WEIGHT_BLACK; + + OSL_FAIL( "Unknown FontWeight" ); + return WEIGHT_DONTKNOW; +} + +css::awt::FontSlant vcl::unohelper::ConvertFontSlant(FontItalic eItalic) +{ + css::awt::FontSlant eRet(css::awt::FontSlant_DONTKNOW); + switch (eItalic) + { + case ITALIC_NONE: + eRet = css::awt::FontSlant_NONE; + break; + case ITALIC_OBLIQUE: + eRet = css::awt::FontSlant_OBLIQUE; + break; + case ITALIC_NORMAL: + eRet = css::awt::FontSlant_ITALIC; + break; + case ITALIC_DONTKNOW: + eRet = css::awt::FontSlant_DONTKNOW; + break; + case FontItalic_FORCE_EQUAL_SIZE: + eRet = css::awt::FontSlant::FontSlant_MAKE_FIXED_SIZE; + break; + } + return eRet; +} + +FontItalic vcl::unohelper::ConvertFontSlant(css::awt::FontSlant eSlant) +{ + FontItalic eRet = ITALIC_DONTKNOW; + switch (eSlant) + { + case css::awt::FontSlant_NONE: + eRet = ITALIC_NONE; + break; + case css::awt::FontSlant_OBLIQUE: + eRet = ITALIC_OBLIQUE; + break; + case css::awt::FontSlant_ITALIC: + eRet = ITALIC_NORMAL; + break; + case css::awt::FontSlant_DONTKNOW: + eRet = ITALIC_DONTKNOW; + break; + case css::awt::FontSlant_REVERSE_OBLIQUE: + //there is no vcl reverse oblique + eRet = ITALIC_OBLIQUE; + break; + case css::awt::FontSlant_REVERSE_ITALIC: + //there is no vcl reverse normal + eRet = ITALIC_NORMAL; + break; + case css::awt::FontSlant::FontSlant_MAKE_FIXED_SIZE: + eRet = FontItalic_FORCE_EQUAL_SIZE; + break; + } + return eRet; +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/source/app/unohelp2.cxx b/vcl/source/app/unohelp2.cxx new file mode 100644 index 000000000..9005467d9 --- /dev/null +++ b/vcl/source/app/unohelp2.cxx @@ -0,0 +1,98 @@ +/* -*- 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/log.hxx> +#include <vcl/unohelp2.hxx> +#include <sot/exchange.hxx> +#include <sot/formats.hxx> +#include <vcl/svapp.hxx> +#include <com/sun/star/datatransfer/UnsupportedFlavorException.hpp> +#include <com/sun/star/datatransfer/clipboard/XClipboard.hpp> +#include <com/sun/star/datatransfer/clipboard/XFlushableClipboard.hpp> +#include <cppuhelper/queryinterface.hxx> + +using namespace ::com::sun::star; + +namespace vcl::unohelper { + + TextDataObject::TextDataObject( const OUString& rText ) : maText( rText ) + { + } + + TextDataObject::~TextDataObject() + { + } + + void TextDataObject::CopyStringTo( const OUString& rContent, + const uno::Reference< datatransfer::clipboard::XClipboard >& rxClipboard ) + { + SAL_WARN_IF( !rxClipboard.is(), "vcl", "TextDataObject::CopyStringTo: invalid clipboard!" ); + if ( !rxClipboard.is() ) + return; + + TextDataObject* pDataObj = new TextDataObject( rContent ); + + SolarMutexReleaser aReleaser; + try + { + rxClipboard->setContents( pDataObj, nullptr ); + + uno::Reference< datatransfer::clipboard::XFlushableClipboard > xFlushableClipboard( rxClipboard, uno::UNO_QUERY ); + if( xFlushableClipboard.is() ) + xFlushableClipboard->flushClipboard(); + } + catch( const uno::Exception& ) + { + } + } + + // css::uno::XInterface + uno::Any TextDataObject::queryInterface( const uno::Type & rType ) + { + uno::Any aRet = ::cppu::queryInterface( rType, static_cast< datatransfer::XTransferable* >(this) ); + return (aRet.hasValue() ? aRet : OWeakObject::queryInterface( rType )); + } + + // css::datatransfer::XTransferable + uno::Any TextDataObject::getTransferData( const datatransfer::DataFlavor& rFlavor ) + { + SotClipboardFormatId nT = SotExchange::GetFormat( rFlavor ); + if ( nT != SotClipboardFormatId::STRING ) + { + throw datatransfer::UnsupportedFlavorException(); + } + return uno::Any(maText); + } + + uno::Sequence< datatransfer::DataFlavor > TextDataObject::getTransferDataFlavors( ) + { + uno::Sequence< datatransfer::DataFlavor > aDataFlavors(1); + SotExchange::GetFormatDataFlavor( SotClipboardFormatId::STRING, aDataFlavors.getArray()[0] ); + return aDataFlavors; + } + + sal_Bool TextDataObject::isDataFlavorSupported( const datatransfer::DataFlavor& rFlavor ) + { + SotClipboardFormatId nT = SotExchange::GetFormat( rFlavor ); + return ( nT == SotClipboardFormatId::STRING ); + } + +} // namespace vcl::unohelper + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/source/app/vclevent.cxx b/vcl/source/app/vclevent.cxx new file mode 100644 index 000000000..3dfad4a4c --- /dev/null +++ b/vcl/source/app/vclevent.cxx @@ -0,0 +1,104 @@ +/* -*- 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 <vcl/vclevent.hxx> +#include <vcl/window.hxx> +#include <vcl/menu.hxx> + +#include <vcleventlisteners.hxx> + +using ::com::sun::star::uno::Reference; +using ::com::sun::star::accessibility::XAccessible; + + +VclAccessibleEvent::VclAccessibleEvent( VclEventId n, const Reference<XAccessible>& rxAccessible ) : + VclSimpleEvent(n), + mxAccessible(rxAccessible) +{ +} + +VclAccessibleEvent::~VclAccessibleEvent() +{ +} + + +void VclEventListeners::Call( VclSimpleEvent& rEvent ) const +{ + if ( m_aListeners.empty() ) + return; + + // Copy the list, because this can be destroyed when calling a Link... + std::vector<Link<VclSimpleEvent&,void>> aCopy( m_aListeners ); + std::vector<Link<VclSimpleEvent&,void>>::iterator aIter( aCopy.begin() ); + std::vector<Link<VclSimpleEvent&,void>>::const_iterator aEnd( aCopy.end() ); + if (VclWindowEvent* pWindowEvent = dynamic_cast<VclWindowEvent*>(&rEvent)) + { + VclPtr<vcl::Window> xWin(pWindowEvent->GetWindow()); + while ( aIter != aEnd && (!xWin || !xWin->IsDisposed()) ) + { + Link<VclSimpleEvent&,void> &rLink = *aIter; + // check this hasn't been removed in some re-enterancy scenario fdo#47368 + if( std::find(m_aListeners.begin(), m_aListeners.end(), rLink) != m_aListeners.end() ) + rLink.Call( rEvent ); + ++aIter; + } + } + else + { + while ( aIter != aEnd ) + { + Link<VclSimpleEvent&,void> &rLink = *aIter; + if( std::find(m_aListeners.begin(), m_aListeners.end(), rLink) != m_aListeners.end() ) + rLink.Call( rEvent ); + ++aIter; + } + } +} + +void VclEventListeners::addListener( const Link<VclSimpleEvent&,void>& rListener ) +{ + m_aListeners.push_back( rListener ); +} + +void VclEventListeners::removeListener( const Link<VclSimpleEvent&,void>& rListener ) +{ + m_aListeners.erase( std::remove(m_aListeners.begin(), m_aListeners.end(), rListener ), m_aListeners.end() ); +} + +VclWindowEvent::VclWindowEvent( vcl::Window* pWin, VclEventId n, void* pDat ) : VclSimpleEvent(n) +{ + pWindow = pWin; pData = pDat; +} + +VclWindowEvent::~VclWindowEvent() {} + +VclMenuEvent::VclMenuEvent( Menu* pM, VclEventId n, sal_uInt16 nPos ) + : VclSimpleEvent(n), pMenu(pM), mnPos(nPos) +{} + +VclMenuEvent::~VclMenuEvent() +{} + +Menu* VclMenuEvent::GetMenu() const +{ + return pMenu; +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/source/app/watchdog.cxx b/vcl/source/app/watchdog.cxx new file mode 100644 index 000000000..c45e51c04 --- /dev/null +++ b/vcl/source/app/watchdog.cxx @@ -0,0 +1,165 @@ +/* -*- 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/. + */ + +#include <watchdog.hxx> + +#include <config_features.h> + +#include <osl/conditn.hxx> +#include <rtl/ref.hxx> +#include <rtl/string.hxx> +#include <sal/log.hxx> +#include <comphelper/debuggerinfo.hxx> +#include <opengl/zone.hxx> +#include <skia/zone.hxx> + +#include <stdlib.h> + +#if defined HAVE_VALGRIND_HEADERS +#include <valgrind/memcheck.h> +#endif + +namespace +{ +static volatile bool gbWatchdogFiring = false; +static osl::Condition* gpWatchdogExit = nullptr; +static rtl::Reference<WatchdogThread> gxWatchdog; + +template <typename Zone> struct WatchdogHelper +{ + static inline sal_uInt64 nLastEnters = 0; + static inline int nUnchanged = 0; // how many unchanged nEnters + static inline bool bFired = false; + static inline bool bAbortFired = false; + static void setLastEnters() { nLastEnters = Zone::enterCount(); } + static void check() + { + if (Zone::isInZone()) + { + const CrashWatchdogTimingsValues& aTimingValues = Zone::getCrashWatchdogTimingsValues(); + + if (nLastEnters == Zone::enterCount()) + nUnchanged++; + else + nUnchanged = 0; + Zone::checkDebug(nUnchanged, aTimingValues); + + // Not making progress + if (nUnchanged >= aTimingValues.mnDisableEntries) + { + if (!bFired) + { + gbWatchdogFiring = true; + SAL_WARN("vcl.watchdog", + OStringLiteral("Watchdog triggered: hard disable ") + Zone::name()); + Zone::hardDisable(); + gbWatchdogFiring = false; + } + bFired = true; + + // we can hang using VCL in the abort handling -> be impatient + if (bAbortFired) + { + SAL_WARN("vcl.watchdog", + OStringLiteral("Watchdog gave up: hard exiting ") + Zone::name()); + _Exit(1); + } + } + + // Not making even more progress + if (nUnchanged >= aTimingValues.mnAbortAfter) + { + if (!bAbortFired) + { + SAL_WARN("vcl.watchdog", + OStringLiteral("Watchdog gave up: aborting ") + Zone::name()); + gbWatchdogFiring = true; + std::abort(); + } + // coverity[dead_error_line] - we might have caught SIGABRT and failed to exit yet + bAbortFired = true; + } + } + else + { + nUnchanged = 0; + } + } +}; + +} // namespace + +WatchdogThread::WatchdogThread() + : salhelper::Thread("Crash Watchdog") +{ +} + +void WatchdogThread::execute() +{ + TimeValue aQuarterSecond(0, 1000 * 1000 * 1000 * 0.25); + do + { +#if HAVE_FEATURE_OPENGL + WatchdogHelper<OpenGLZone>::setLastEnters(); +#endif +#if HAVE_FEATURE_SKIA + WatchdogHelper<SkiaZone>::setLastEnters(); +#endif + + gpWatchdogExit->wait(&aQuarterSecond); + +#if defined HAVE_VALGRIND_HEADERS + if (RUNNING_ON_VALGRIND) + continue; +#endif +#if defined DBG_UTIL + if (comphelper::isDebuggerAttached()) + continue; +#endif + +#if HAVE_FEATURE_OPENGL + WatchdogHelper<OpenGLZone>::check(); +#endif +#if HAVE_FEATURE_SKIA + WatchdogHelper<SkiaZone>::check(); +#endif + + } while (!gpWatchdogExit->check()); +} + +void WatchdogThread::start() +{ + if (gxWatchdog != nullptr) + return; // already running + if (getenv("SAL_DISABLE_WATCHDOG")) + return; + gpWatchdogExit = new osl::Condition(); + gxWatchdog.set(new WatchdogThread()); + gxWatchdog->launch(); +} + +void WatchdogThread::stop() +{ + if (gbWatchdogFiring) + return; // in watchdog thread + + if (gpWatchdogExit) + gpWatchdogExit->set(); + + if (gxWatchdog.is()) + { + gxWatchdog->join(); + gxWatchdog.clear(); + } + + delete gpWatchdogExit; + gpWatchdogExit = nullptr; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/source/app/weldutils.cxx b/vcl/source/app/weldutils.cxx new file mode 100644 index 000000000..69562cf0d --- /dev/null +++ b/vcl/source/app/weldutils.cxx @@ -0,0 +1,129 @@ +/* -*- 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/. + */ + +#include <vcl/builderpage.hxx> +#include <vcl/commandinfoprovider.hxx> +#include <vcl/settings.hxx> +#include <vcl/svapp.hxx> +#include <vcl/weldutils.hxx> + +BuilderPage::BuilderPage(weld::Widget* pParent, weld::DialogController* pController, + const OUString& rUIXMLDescription, const OString& rID) + : m_pDialogController(pController) + , m_xBuilder(Application::CreateBuilder(pParent, rUIXMLDescription)) + , m_xContainer(m_xBuilder->weld_container(rID)) +{ +} + +void BuilderPage::Activate() {} + +void BuilderPage::Deactivate() {} + +BuilderPage::~BuilderPage() COVERITY_NOEXCEPT_FALSE {} + +namespace weld +{ +bool DialogController::runAsync(const std::shared_ptr<DialogController>& rController, + const std::function<void(sal_Int32)>& func) +{ + return rController->getDialog()->runAsync(rController, func); +} + +DialogController::~DialogController() COVERITY_NOEXCEPT_FALSE {} + +Dialog* GenericDialogController::getDialog() { return m_xDialog.get(); } + +GenericDialogController::GenericDialogController(weld::Widget* pParent, const OUString& rUIFile, + const OString& rDialogId, bool bMobile) + : m_xBuilder(Application::CreateBuilder(pParent, rUIFile, bMobile)) + , m_xDialog(m_xBuilder->weld_dialog(rDialogId)) +{ +} + +GenericDialogController::~GenericDialogController() COVERITY_NOEXCEPT_FALSE {} + +Dialog* MessageDialogController::getDialog() { return m_xDialog.get(); } + +MessageDialogController::MessageDialogController(weld::Widget* pParent, const OUString& rUIFile, + const OString& rDialogId, + const OString& rRelocateId) + : m_xBuilder(Application::CreateBuilder(pParent, rUIFile)) + , m_xDialog(m_xBuilder->weld_message_dialog(rDialogId)) + , m_xContentArea(m_xDialog->weld_message_area()) +{ + if (!rRelocateId.isEmpty()) + { + m_xRelocate = m_xBuilder->weld_container(rRelocateId); + m_xOrigParent = m_xRelocate->weld_parent(); + //fdo#75121, a bit tricky because the widgets we want to align with + //don't actually exist in the ui description, they're implied + m_xOrigParent->move(m_xRelocate.get(), m_xContentArea.get()); + } +} + +MessageDialogController::~MessageDialogController() +{ + if (m_xRelocate) + { + m_xContentArea->move(m_xRelocate.get(), m_xOrigParent.get()); + } +} + +AssistantController::AssistantController(weld::Widget* pParent, const OUString& rUIFile, + const OString& rDialogId) + : m_xBuilder(Application::CreateBuilder(pParent, rUIFile)) + , m_xAssistant(m_xBuilder->weld_assistant(rDialogId)) +{ +} + +Dialog* AssistantController::getDialog() { return m_xAssistant.get(); } + +AssistantController::~AssistantController() {} + +void TriStateEnabled::ButtonToggled(weld::ToggleButton& rToggle) +{ + if (bTriStateEnabled) + { + switch (eState) + { + case TRISTATE_INDET: + rToggle.set_state(TRISTATE_FALSE); + break; + case TRISTATE_TRUE: + rToggle.set_state(TRISTATE_INDET); + break; + case TRISTATE_FALSE: + rToggle.set_state(TRISTATE_TRUE); + break; + } + } + eState = rToggle.get_state(); +} + +void RemoveParentKeepChildren(weld::TreeView& rTreeView, weld::TreeIter& rParent) +{ + if (rTreeView.iter_has_child(rParent)) + { + std::unique_ptr<weld::TreeIter> xNewParent(rTreeView.make_iterator(&rParent)); + if (!rTreeView.iter_parent(*xNewParent)) + xNewParent.reset(); + + while (true) + { + std::unique_ptr<weld::TreeIter> xChild(rTreeView.make_iterator(&rParent)); + if (!rTreeView.iter_children(*xChild)) + break; + rTreeView.move_subtree(*xChild, xNewParent.get(), -1); + } + } + rTreeView.remove(rParent); +} +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/source/app/winscheduler.cxx b/vcl/source/app/winscheduler.cxx new file mode 100644 index 000000000..0398faaa4 --- /dev/null +++ b/vcl/source/app/winscheduler.cxx @@ -0,0 +1,46 @@ +/* -*- 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 . + */ + +#ifdef _WIN32 + +#include <sal/config.h> + +#include <sal/log.hxx> +#include <vcl/winscheduler.hxx> + +#include <svsys.h> +#include <win/saldata.hxx> +#include <win/salinst.h> + +namespace +{ +void PostMessageToComWnd(UINT nMsg) +{ + bool const ret = PostMessageW(GetSalData()->mpInstance->mhComWnd, nMsg, 0, 0); + SAL_WARN_IF(!ret, "vcl.schedule", "ERROR: PostMessage() failed!"); +} +} + +void WinScheduler::SetForceRealTimer() { PostMessageToComWnd(SAL_MSG_FORCE_REAL_TIMER); } + +void WinScheduler::PostDummyMessage() { PostMessageToComWnd(SAL_MSG_DUMMY); } + +#endif // _WIN32 + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |