Rebased ref, commits from common ancestor: commit 9bd5a1d5d797db719f405ebf6a6011185b90c35b Author: Tomaž Vajngerl <tomaz.vajng...@collabora.co.uk> AuthorDate: Tue May 22 14:44:39 2018 +0900 Commit: Tomaž Vajngerl <tomaz.vajng...@collabora.co.uk> CommitDate: Mon Mar 1 13:35:31 2021 +0900
Command Popup Change-Id: I92cdd3130b8de42ee0863c9e7154e7c7246d9377 diff --git a/include/sfx2/sfxsids.hrc b/include/sfx2/sfxsids.hrc index 3f9d4a232fb7..e2a9a8cdaab6 100644 --- a/include/sfx2/sfxsids.hrc +++ b/include/sfx2/sfxsids.hrc @@ -385,7 +385,7 @@ class SvxSearchItem; // default-ids for windows -// free (SID_SFX_START + 610) +#define SID_COMMAND_POPUP (SID_SFX_START + 610) #define SID_NEWWINDOW (SID_SFX_START + 620) #define SID_CLOSEWIN (SID_SFX_START + 621) #define SID_VIEWSHELL (SID_SFX_START + 623) diff --git a/officecfg/registry/data/org/openoffice/Office/Accelerators.xcu b/officecfg/registry/data/org/openoffice/Office/Accelerators.xcu index 967aa98a04db..47118db2540e 100644 --- a/officecfg/registry/data/org/openoffice/Office/Accelerators.xcu +++ b/officecfg/registry/data/org/openoffice/Office/Accelerators.xcu @@ -313,6 +313,12 @@ Ctrl+Shift+e aka E_SHIFT_MOD1 under GTK/IBUS is for some emoji thing <value xml:lang="en-US" install:module="unxwnt">.uno:OptionsTreeDialog</value> </prop> </node> + <node oor:name="SPACE_MOD1" oor:op="replace"> + <prop oor:name="Command"> + <value xml:lang="x-no-translate">I10N SHORTCUTS - NO TRANSLATE</value> + <value xml:lang="en-US">.uno:CommandPopup</value> + </prop> + </node> </node> <node oor:name="Modules"> <node oor:name="com.sun.star.script.BasicIDE" oor:op="replace"> diff --git a/officecfg/registry/data/org/openoffice/Office/UI/GenericCommands.xcu b/officecfg/registry/data/org/openoffice/Office/UI/GenericCommands.xcu index 756bdd288b91..ad77ca0a19a5 100644 --- a/officecfg/registry/data/org/openoffice/Office/UI/GenericCommands.xcu +++ b/officecfg/registry/data/org/openoffice/Office/UI/GenericCommands.xcu @@ -6537,6 +6537,14 @@ bit 3 (0x8): #define UICOMMANDDESCRIPTION_PROPERTIES_TOGGLEBUTTON 8 <value>1</value> </prop> </node> + <node oor:name=".uno:CommandPopup" oor:op="replace"> + <prop oor:name="Label" oor:type="xs:string"> + <value xml:lang="en-US">Command Popup</value> + </prop> + <prop oor:name="Properties" oor:type="xs:int"> + <value>1</value> + </prop> + </node> <node oor:name=".uno:DevelopmentToolsDockingWindow" oor:op="replace"> <prop oor:name="Label" oor:type="xs:string"> <value xml:lang="en-US">Development Tool</value> diff --git a/sfx2/Library_sfx.mk b/sfx2/Library_sfx.mk index b5126ca87893..941b11e59f94 100644 --- a/sfx2/Library_sfx.mk +++ b/sfx2/Library_sfx.mk @@ -291,6 +291,7 @@ $(eval $(call gb_Library_add_exception_objects,sfx,\ sfx2/source/styles/StyleManager \ sfx2/source/toolbox/tbxitem \ sfx2/source/toolbox/weldutils \ + sfx2/source/view/CommandPopup \ sfx2/source/view/classificationcontroller \ sfx2/source/view/classificationhelper \ sfx2/source/view/frame \ diff --git a/sfx2/UIConfig_sfx.mk b/sfx2/UIConfig_sfx.mk index 7bdbd7a85a1e..ec80ab16de65 100644 --- a/sfx2/UIConfig_sfx.mk +++ b/sfx2/UIConfig_sfx.mk @@ -21,6 +21,7 @@ $(eval $(call gb_UIConfig_add_uifiles,sfx,\ sfx2/uiconfig/ui/classificationbox \ sfx2/uiconfig/ui/cmisinfopage \ sfx2/uiconfig/ui/cmisline \ + sfx2/uiconfig/ui/commandpopup \ sfx2/uiconfig/ui/custominfopage \ sfx2/uiconfig/ui/decktitlebar \ sfx2/uiconfig/ui/descriptioninfopage \ diff --git a/sfx2/sdi/frmslots.sdi b/sfx2/sdi/frmslots.sdi index 09aafef95b7d..a7c8a472e73d 100644 --- a/sfx2/sdi/frmslots.sdi +++ b/sfx2/sdi/frmslots.sdi @@ -262,6 +262,11 @@ interface TopWindow : BrowseWindow ExecMethod = MiscExec_Impl ; StateMethod = MiscState_Impl ; ] + SID_COMMAND_POPUP + [ + ExecMethod = MiscExec_Impl ; + StateMethod = MiscState_Impl ; + ] SID_CLOSEWIN // ole(no) api(final/play/rec) [ ExecMethod = Exec_Impl ; @@ -307,4 +312,3 @@ shell SfxViewFrame StateMethod = GetState_Impl ; ] } - diff --git a/sfx2/sdi/sfx.sdi b/sfx2/sdi/sfx.sdi index 425724440d13..85523a6f0b46 100644 --- a/sfx2/sdi/sfx.sdi +++ b/sfx2/sdi/sfx.sdi @@ -1271,6 +1271,23 @@ SfxStringItem FullName SID_DOCFULLNAME GroupId = ; ] +SfxVoidItem CommandPopup SID_COMMAND_POPUP +[ + AutoUpdate = TRUE, + FastCall = FALSE, + ReadOnlyDoc = TRUE, + Toggle = FALSE, + Container = TRUE, + RecordAbsolute = FALSE, + RecordPerSet; + Asynchron; + + + AccelConfig = TRUE, + MenuConfig = TRUE, + ToolBoxConfig = TRUE, + GroupId = SfxGroupId::View; +] SfxBoolItem FullScreen SID_WIN_FULLSCREEN diff --git a/sfx2/source/view/CommandPopup.cxx b/sfx2/source/view/CommandPopup.cxx new file mode 100644 index 000000000000..c35bb2db476e --- /dev/null +++ b/sfx2/source/view/CommandPopup.cxx @@ -0,0 +1,321 @@ +/* -*- 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 "CommandPopup.hxx" + +#include <vcl/layout.hxx> +#include <workwin.hxx> +#include <sfx2/msgpool.hxx> +#include <sfx2/bindings.hxx> + +#include <comphelper/processfactory.hxx> +#include <comphelper/dispatchcommand.hxx> + +#include <com/sun/star/frame/XFrame.hpp> +#include <com/sun/star/frame/XDispatchInformationProvider.hpp> +#include <com/sun/star/frame/theUICommandDescription.hpp> +#include <com/sun/star/ui/theUICategoryDescription.hpp> + +#include <com/sun/star/ui/theModuleUIConfigurationManagerSupplier.hpp> +#include <com/sun/star/ui/XUIConfigurationManagerSupplier.hpp> + +#include <com/sun/star/lang/IndexOutOfBoundsException.hpp> + +#include <vcl/commandinfoprovider.hxx> + +using namespace css; + +struct CurrentEntry +{ + OUString m_aCommandURL; + OUString m_aTooltip; + + CurrentEntry(OUString const& rCommandURL, OUString const& rTooltip) + : m_aCommandURL(rCommandURL) + , m_aTooltip(rTooltip) + { + } +}; + +struct MenuContent +{ + OUString m_aCommandURL; + OUString m_aLabel; + OUString m_aMenuLabel; + OUString m_aTooltip; + std::vector<MenuContent> m_aSubMenuContent; +}; + +#include <com/sun/star/frame/Desktop.hpp> +#include <com/sun/star/frame/XDispatch.hpp> +#include <com/sun/star/frame/XDispatchProvider.hpp> +#include <com/sun/star/frame/XNotifyingDispatch.hpp> +#include <com/sun/star/util/URL.hpp> +#include <com/sun/star/util/URLTransformer.hpp> + +class MenuContentHandler +{ +private: + css::uno::Reference<css::frame::XFrame> m_xFrame; + MenuContent m_aMenuContent; + OUString m_sModuleLongName; + +public: + MenuContentHandler(uno::Reference<frame::XFrame> const& xFrame) + : m_xFrame(xFrame) + , m_sModuleLongName(vcl::CommandInfoProvider::GetModuleIdentifier(xFrame)) + { + auto xComponentContext = comphelper::getProcessComponentContext(); + + //uno::Reference<container::XNameAccess> xGlobalCategoryInfo; + //xGlobalCategoryInfo = ui::theUICategoryDescription::get(xComponentContext); + + //uno::Reference<container::XNameAccess> xModuleCategoryInfo; + //xModuleCategoryInfo.set(xGlobalCategoryInfo->getByName(m_sModuleLongName), uno::UNO_QUERY); + + //uno::Reference<container::XNameAccess> xUICommandDescription; + //xUICommandDescription = frame::theUICommandDescription::get(xComponentContext); + + uno::Reference<ui::XModuleUIConfigurationManagerSupplier> xModuleConfigSupplier; + xModuleConfigSupplier.set( + ui::theModuleUIConfigurationManagerSupplier::get(xComponentContext)); + + uno::Reference<ui::XUIConfigurationManager> xConfigurationManager; + xConfigurationManager = xModuleConfigSupplier->getUIConfigurationManager(m_sModuleLongName); + + uno::Reference<container::XIndexAccess> xConfigData; + xConfigData = xConfigurationManager->getSettings("private:resource/menubar/menubar", false); + + recurse(xConfigData, m_aMenuContent); + } + + void recurse(uno::Reference<container::XIndexAccess> xIndexAccess, MenuContent& rMenuContent) + { + for (sal_Int32 n = 0; n < xIndexAccess->getCount(); n++) + { + MenuContent aNewContent; + OUString aModuleIdentifier; + uno::Sequence<beans::PropertyValue> aProperties; + uno::Reference<container::XIndexAccess> xIndexContainer; + try + { + if (xIndexAccess->getByIndex(n) >>= aProperties) + { + bool bIsVisible = true; + bool bIsEnabled = true; + + for (auto const& rProperty : aProperties) + { + OUString aPropertyName = rProperty.Name; + if (aPropertyName == "CommandURL") + rProperty.Value >>= aNewContent.m_aCommandURL; + else if (aPropertyName == "ItemDescriptorContainer") + rProperty.Value >>= xIndexContainer; + else if (aPropertyName == "ModuleIdentifier") + rProperty.Value >>= aModuleIdentifier; + else if (aPropertyName == "IsVisible") + rProperty.Value >>= bIsVisible; + else if (aPropertyName == "Enabled") + rProperty.Value >>= bIsEnabled; + } + + if (bIsEnabled && bIsVisible) + { + auto aCommandProperties = vcl::CommandInfoProvider::GetCommandProperties( + aNewContent.m_aCommandURL, m_sModuleLongName); + aNewContent.m_aMenuLabel + = vcl::CommandInfoProvider::GetMenuLabelForCommand(aCommandProperties) + .replaceAll("~", ""); + aNewContent.m_aLabel + = rMenuContent.m_aLabel + " -> " + aNewContent.m_aMenuLabel; + aNewContent.m_aTooltip = vcl::CommandInfoProvider::GetTooltipForCommand( + aNewContent.m_aCommandURL, aCommandProperties, m_xFrame); + if (xIndexContainer.is()) + recurse(xIndexContainer, aNewContent); + + rMenuContent.m_aSubMenuContent.push_back(aNewContent); + } + } + } + catch (const lang::IndexOutOfBoundsException&) + { + return; + } + } + } + + void findInMenu(OUString const& rText, std::unique_ptr<weld::TreeView>& rpCommandTreeView, + std::vector<CurrentEntry>& rCommandList) + { + findInMenuRecursive(m_aMenuContent, rText, rpCommandTreeView, rCommandList); + } + +private: + void findInMenuRecursive(MenuContent const& rMenuContent, OUString const& rText, + std::unique_ptr<weld::TreeView>& rpCommandTreeView, + std::vector<CurrentEntry>& rCommandList) + { + for (MenuContent const& aSubContent : rMenuContent.m_aSubMenuContent) + { + if (aSubContent.m_aMenuLabel.toAsciiLowerCase().startsWith(rText)) + { + OUString sCommandURL = aSubContent.m_aCommandURL; + util::URL aCommandURL; + aCommandURL.Complete = sCommandURL; + uno::Reference<uno::XComponentContext> xContext + = comphelper::getProcessComponentContext(); + uno::Reference<util::XURLTransformer> xParser + = util::URLTransformer::create(xContext); + xParser->parseStrict(aCommandURL); + + auto* pViewFrame = SfxViewFrame::Current(); + + SfxSlotPool& rSlotPool = SfxSlotPool::GetSlotPool(pViewFrame); + const SfxSlot* pSlot = rSlotPool.GetUnoSlot(aCommandURL.Path); + if (pSlot) + { + std::unique_ptr<SfxPoolItem> pState; + SfxItemState eState + = pViewFrame->GetBindings().QueryState(pSlot->GetSlotId(), pState); + + if (eState != SfxItemState::DISABLED) + { + auto xGraphic = vcl::CommandInfoProvider::GetXGraphicForCommand(sCommandURL, + m_xFrame); + rCommandList.emplace_back(sCommandURL, aSubContent.m_aTooltip); + + auto pIter = rpCommandTreeView->make_iterator(); + rpCommandTreeView->insert(nullptr, -1, &aSubContent.m_aLabel, nullptr, + nullptr, nullptr, false, pIter.get()); + rpCommandTreeView->set_image(*pIter, xGraphic); + } + } + } + findInMenuRecursive(aSubContent, rText, rpCommandTreeView, rCommandList); + } + } +}; + +CommandListBox::CommandListBox(vcl::Window* pParent, CommandPopup& rPopUp, + uno::Reference<frame::XFrame> const& xFrame) + : InterimItemWindow(pParent, "sfx/ui/commandpopup.ui", "CommandBox") + , m_rPopUp(rPopUp) + , m_pEntry(m_xBuilder->weld_entry("command_entry")) + , m_pCommandTreeView(m_xBuilder->weld_tree_view("command_treeview")) + , m_pLabel(m_xBuilder->weld_label("command_label")) + , m_pMenuContentHandler(std::make_unique<MenuContentHandler>(xFrame)) +{ + m_pEntry->connect_changed(LINK(this, CommandListBox, ModifyHdl)); + m_pCommandTreeView->connect_changed(LINK(this, CommandListBox, SelectionChanged)); +} + +void CommandListBox::initialize() +{ + Point aPoint = GetParent()->OutputToScreenPixel(Point(0, 0)); + Size aSize(400, 400); + SetSizePixel(aSize); + Show(); + + m_rPopUp.SetPosSizePixel(aPoint, aSize); + Size aFrameSize = m_rPopUp.GetParent()->GetOutputSizePixel(); + m_rPopUp.StartPopupMode(tools::Rectangle(Point(aFrameSize.Width(), 0), Size(0, 0)), + FloatWinPopupFlags::Down | FloatWinPopupFlags::GrabFocus); + + GrabFocus(); + m_pEntry->grab_focus(); +} + +void CommandListBox::dispose() +{ + m_pEntry.reset(); + m_pCommandTreeView.reset(); + InterimItemWindow::dispose(); +} + +IMPL_LINK_NOARG(CommandListBox, ModifyHdl, weld::Entry&, void) +{ + m_pCommandTreeView->clear(); + m_aCommandList.clear(); + + OUString sText = m_pEntry->get_text(); + if (sText.isEmpty()) + return; + + m_pCommandTreeView->freeze(); + m_pMenuContentHandler->findInMenu(sText.toAsciiLowerCase(), m_pCommandTreeView, m_aCommandList); + m_pCommandTreeView->thaw(); + + if (m_pCommandTreeView->n_children() > 0) + { + m_pCommandTreeView->set_cursor(0); + m_pCommandTreeView->select(0); + } + + m_pEntry->grab_focus(); + + SelectionChanged(*m_pCommandTreeView); +} + +IMPL_LINK(CommandListBox, SelectionChanged, weld::TreeView&, rTreeView, void) +{ + m_pLabel->set_label(""); + size_t nSelected = rTreeView.get_selected_index(); + if (nSelected < m_aCommandList.size()) + { + auto const& rCurrent = m_aCommandList[nSelected]; + m_pLabel->set_label(rCurrent.m_aTooltip); + } +} + +bool CommandListBox::EventNotify(NotifyEvent& rNotifyEvent) +{ + const KeyEvent* pKeyEvent = rNotifyEvent.GetKeyEvent(); + + if (pKeyEvent) + { + if (pKeyEvent->GetKeyCode().GetCode() == KEY_DOWN + || pKeyEvent->GetKeyCode().GetCode() == KEY_UP) + { + m_pCommandTreeView->grab_focus(); + m_pCommandTreeView->scroll_to_row(m_pCommandTreeView->get_selected_index()); + } + else if (pKeyEvent->GetKeyCode().GetCode() == KEY_RETURN) + { + size_t nSelected = m_pCommandTreeView->get_selected_index(); + if (nSelected < m_aCommandList.size()) + { + auto const& rCurrent = m_aCommandList[nSelected]; + dispatchCommandAndClose(rCurrent.m_aCommandURL); + } + } + else + { + m_pEntry->grab_focus(); + } + } + + return InterimItemWindow::EventNotify(rNotifyEvent); +} + +void CommandListBox::dispatchCommandAndClose(OUString const& rCommand) +{ + m_rPopUp.EndPopupMode(FloatWinPopupEndFlags::CloseAll); + comphelper::dispatchCommand(rCommand, uno::Sequence<beans::PropertyValue>()); +} + +CommandPopup::CommandPopup(vcl::Window* pParent) + : FloatingWindow(pParent, WB_BORDER | WB_SYSTEMWINDOW) +{ +} + +CommandPopup::~CommandPopup() { disposeOnce(); } + +void CommandPopup::dispose() { FloatingWindow::dispose(); } + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sfx2/source/view/CommandPopup.hxx b/sfx2/source/view/CommandPopup.hxx new file mode 100644 index 000000000000..db0e50deb96f --- /dev/null +++ b/sfx2/source/view/CommandPopup.hxx @@ -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/. + */ + +#pragma once + +#include <vcl/layout.hxx> + +#include <sfx2/dllapi.h> +#include <sfx2/viewfrm.hxx> +#include <vcl/floatwin.hxx> + +#include <com/sun/star/container/XNameAccess.hpp> + +#include <vcl/InterimItemWindow.hxx> +#include <vcl/weld.hxx> +#include <vcl/window.hxx> + +struct CurrentEntry; +class MenuContentHandler; + +class SFX2_DLLPUBLIC CommandPopup : public FloatingWindow +{ +public: + explicit CommandPopup(vcl::Window* pWorkWindow); + + ~CommandPopup() override; + + void dispose() override; +}; + +class SFX2_DLLPUBLIC CommandListBox final : public InterimItemWindow +{ +private: + CommandPopup& m_rPopUp; + + std::unique_ptr<weld::Entry> m_pEntry; + std::unique_ptr<weld::TreeView> m_pCommandTreeView; + std::unique_ptr<weld::Label> m_pLabel; + + std::vector<CurrentEntry> m_aCommandList; + OUString m_PreviousText; + std::unique_ptr<MenuContentHandler> m_pMenuContentHandler; + + DECL_LINK(ModifyHdl, weld::Entry&, void); + DECL_LINK(SelectionChanged, weld::TreeView&, void); + + void dispatchCommandAndClose(OUString const& rCommand); + bool EventNotify(NotifyEvent& rNotifyEvent) override; + +public: + CommandListBox(vcl::Window* pParent, CommandPopup& rPopUp, + css::uno::Reference<css::frame::XFrame> const& xFrame); + + void initialize(); + + void dispose() override; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sfx2/source/view/viewfrm.cxx b/sfx2/source/view/viewfrm.cxx index 170ce543da3b..d946525c4fd3 100644 --- a/sfx2/source/view/viewfrm.cxx +++ b/sfx2/source/view/viewfrm.cxx @@ -25,6 +25,7 @@ #include <sfx2/viewfrm.hxx> #include <sfx2/classificationhelper.hxx> #include <sfx2/notebookbar/SfxNotebookBar.hxx> +#include <svx/svdview.hxx> #include <com/sun/star/document/MacroExecMode.hpp> #include <com/sun/star/frame/Desktop.hpp> #include <com/sun/star/frame/DispatchRecorder.hpp> @@ -90,6 +91,7 @@ #include <unotools/configmgr.hxx> #include <comphelper/sequenceashashmap.hxx> +#include "CommandPopup.hxx" using namespace ::com::sun::star; using namespace ::com::sun::star::uno; @@ -2913,8 +2915,26 @@ void SfxViewFrame::MiscExec_Impl( SfxRequest& rReq ) rReq.Done(); break; } + case SID_COMMAND_POPUP: + { + static VclPtr<CommandPopup> spCommandPopup; + static VclPtr<CommandListBox> spCommandListBox; + + if (spCommandListBox) + spCommandListBox.disposeAndClear(); + + if (spCommandPopup) + spCommandPopup.disposeAndClear(); + + css::uno::Reference<css::frame::XFrame> xFrame(GetFrame().GetFrameInterface(), css::uno::UNO_QUERY); + spCommandPopup.reset(VclPtr<CommandPopup>::Create(&GetWindow())); - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + spCommandListBox.reset(VclPtr<CommandListBox>::Create(spCommandPopup.get(), *spCommandPopup.get(), xFrame)); + spCommandListBox->initialize(); + + rReq.Done(); + break; + } case SID_WIN_FULLSCREEN: { const SfxBoolItem* pItem = rReq.GetArg<SfxBoolItem>(rReq.GetSlot()); diff --git a/sfx2/uiconfig/ui/commandpopup.ui b/sfx2/uiconfig/ui/commandpopup.ui new file mode 100644 index 000000000000..c302f23ed52a --- /dev/null +++ b/sfx2/uiconfig/ui/commandpopup.ui @@ -0,0 +1,108 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Generated with glade 3.38.2 --> +<interface domain="sfx"> + <requires lib="gtk+" version="3.18"/> + <object class="GtkTreeStore" id="liststore1"> + <columns> + <!-- column-name icon --> + <column type="GdkPixbuf"/> + <!-- column-name text --> + <column type="gchararray"/> + <!-- column-name id --> + <column type="gchararray"/> + </columns> + </object> + <object class="GtkBox" id="CommandBox"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="hexpand">True</property> + <property name="vexpand">True</property> + <property name="border-width">6</property> + <property name="orientation">vertical</property> + <property name="spacing">6</property> + <child> + <object class="GtkEntry" id="command_entry"> + <property name="visible">True</property> + <property name="can-focus">True</property> + <property name="caps-lock-warning">False</property> + <property name="placeholder-text" translatable="yes" context="commandpopup|entry">Search command</property> + <accessibility> + <relation type="labelled-by" target="command_label"/> + </accessibility> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkScrolledWindow"> + <property name="visible">True</property> + <property name="can-focus">True</property> + <property name="shadow-type">in</property> + <child> + <object class="GtkViewport"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <child> + <object class="GtkTreeView" id="command_treeview"> + <property name="visible">True</property> + <property name="can-focus">True</property> + <property name="model">liststore1</property> + <property name="headers-visible">False</property> + <property name="headers-clickable">False</property> + <property name="enable-search">False</property> + <property name="search-column">0</property> + <property name="hover-selection">True</property> + <property name="show-expanders">False</property> + <property name="activate-on-single-click">True</property> + <child internal-child="selection"> + <object class="GtkTreeSelection"/> + </child> + <child> + <object class="GtkTreeViewColumn" id="column"> + <property name="sizing">fixed</property> + <child> + <object class="GtkCellRendererPixbuf" id="cellrenderericon"/> + <attributes> + <attribute name="pixbuf">0</attribute> + </attributes> + </child> + <child> + <object class="GtkCellRendererText" id="cellrenderertext"/> + <attributes> + <attribute name="text">1</attribute> + </attributes> + </child> + </object> + </child> + </object> + </child> + </object> + </child> + </object> + <packing> + <property name="expand">True</property> + <property name="fill">True</property> + <property name="position">1</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="command_label"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="label" translatable="no"></property> + <property name="selectable">True</property> + <accessibility> + <relation type="label-for" target="command_entry"/> + </accessibility> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">2</property> + </packing> + </child> + </object> +</interface> commit 0f2c2a83a8bbfe76eb9aceac83623b230be2ddc0 Author: Tomaž Vajngerl <tomaz.vajng...@collabora.co.uk> AuthorDate: Thu Nov 12 10:01:20 2020 +0100 Commit: Tomaž Vajngerl <tomaz.vajng...@collabora.co.uk> CommitDate: Mon Mar 1 13:35:31 2021 +0900 basegfx: added LengthUnit class as the base unit for length Change-Id: I1d4790b60dd784e8b2e2e438274f3ebd6db4b60c diff --git a/basegfx/CppunitTest_basegfx.mk b/basegfx/CppunitTest_basegfx.mk index a0379e76612a..b66b1381d69a 100644 --- a/basegfx/CppunitTest_basegfx.mk +++ b/basegfx/CppunitTest_basegfx.mk @@ -42,6 +42,7 @@ $(eval $(call gb_CppunitTest_add_exception_objects,basegfx,\ basegfx/test/basegfxtools \ basegfx/test/clipstate \ basegfx/test/genericclipper \ + basegfx/test/LengthUnitTest \ )) # vim: set noet sw=4 ts=4: diff --git a/basegfx/test/LengthUnitTest.cxx b/basegfx/test/LengthUnitTest.cxx new file mode 100644 index 000000000000..0b80c33da5b0 --- /dev/null +++ b/basegfx/test/LengthUnitTest.cxx @@ -0,0 +1,96 @@ +/* -*- 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 <basegfx/units/LengthUnit.hxx> + +#include <cppunit/TestFixture.h> +#include <cppunit/extensions/HelperMacros.h> + +class LengthUnitTest : public CppUnit::TestFixture +{ +public: + void test(); + + CPPUNIT_TEST_SUITE(LengthUnitTest); + CPPUNIT_TEST(test); + CPPUNIT_TEST_SUITE_END(); +}; + +void LengthUnitTest::test() +{ + gfx::LengthUnit cm = 1_cm + 5_cm - 2_cm; + CPPUNIT_ASSERT_DOUBLES_EQUAL(4.0, cm.as_cm(), 1e-4); + CPPUNIT_ASSERT_DOUBLES_EQUAL(0.04, cm.as_m(), 1e-4); + CPPUNIT_ASSERT_DOUBLES_EQUAL(40.0, cm.as_mm(), 1e-4); + CPPUNIT_ASSERT_EQUAL(sal_Int64(1440000), cm.raw()); + + gfx::LengthUnit cm2 = 5_cm * 2; + CPPUNIT_ASSERT_EQUAL(sal_Int64(3600000), cm2.raw()); + CPPUNIT_ASSERT_DOUBLES_EQUAL(10.0, cm2.as_cm(), 1e-4); + + // 1 km - 50 m = 950 m = 95000 cm + gfx::LengthUnit cm3 = 100000_cm - 5000_cm; + CPPUNIT_ASSERT_EQUAL(sal_Int64(34200000000), cm3.raw()); + CPPUNIT_ASSERT_DOUBLES_EQUAL(95000.0, cm3.as_cm(), 1e-4); + + gfx::LengthUnit cm4(1_cm); + cm4 /= 2; + CPPUNIT_ASSERT_EQUAL(sal_Int64(180000), cm4.raw()); + CPPUNIT_ASSERT_DOUBLES_EQUAL(0.5, cm4.as_cm(), 1e-4); + + // (635 * 20) + 3 * (635 * 15) = 41275EMU + gfx::LengthUnit pt = 1_pt + 3_px; + CPPUNIT_ASSERT_DOUBLES_EQUAL(3.25, pt.as_pt(), 1e-4); + CPPUNIT_ASSERT_DOUBLES_EQUAL(65.0, pt.as_twip(), 1e-4); + CPPUNIT_ASSERT_DOUBLES_EQUAL(0.0451, pt.as_in(), 1e-4); + CPPUNIT_ASSERT_EQUAL(sal_Int64(41275), pt.raw()); + + gfx::LengthUnit inch = 1_in; // 1440 * 635 + CPPUNIT_ASSERT_DOUBLES_EQUAL(1440.0, inch.as_twip(), 1e-4); + CPPUNIT_ASSERT_DOUBLES_EQUAL(96.0, inch.as_px(), 1e-4); + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0, inch.as_in(), 1e-4); + CPPUNIT_ASSERT_DOUBLES_EQUAL(914400.0, inch.as_emu(), 1e-4); + CPPUNIT_ASSERT_EQUAL(sal_Int64(914400), inch.raw()); + + // Conversion + sal_Int64 asNumber(17_pt); + asNumber += sal_Int64(1_pt); + gfx::LengthUnit asLength = gfx::LengthUnit::emu(asNumber); + CPPUNIT_ASSERT_EQUAL(sal_Int64(18 * 635 * 20), asLength.raw()); + + gfx::LengthUnit maximum = gfx::LengthUnit::emu(SAL_MAX_INT64); + CPPUNIT_ASSERT_DOUBLES_EQUAL(256204778801.5, maximum.as_m(), 1e-1); + // 256204778 km + CPPUNIT_ASSERT_EQUAL(sal_Int64(SAL_MAX_INT64), maximum.raw()); + + gfx::LengthUnit minimum = gfx::LengthUnit::emu(SAL_MIN_INT64); + CPPUNIT_ASSERT_DOUBLES_EQUAL(-256204778801.5, minimum.as_m(), 1e-1); + CPPUNIT_ASSERT_DOUBLES_EQUAL(double(SAL_MIN_INT64), minimum.as_emu(), 1e-1); + CPPUNIT_ASSERT_EQUAL(sal_Int64(SAL_MIN_INT64), minimum.raw()); + + // 27 emu + 33 emu + 360 emu = 420 + gfx::LengthUnit emus = 27_emu + 33_emu + 1_hmm; + CPPUNIT_ASSERT_EQUAL(sal_Int64(420), emus.raw()); + + // Creation from number + int number = 10; + auto asCm = gfx::LengthUnit::cm(number); + CPPUNIT_ASSERT_DOUBLES_EQUAL(10.0, asCm.as_cm(), 1e-4); + CPPUNIT_ASSERT_EQUAL(sal_Int64(3600000), asCm.raw()); + + auto asMm = gfx::LengthUnit::mm(number); + CPPUNIT_ASSERT_DOUBLES_EQUAL(10.0, asMm.as_mm(), 1e-4); + CPPUNIT_ASSERT_EQUAL(sal_Int64(360000), asMm.raw()); + + auto asInch = gfx::LengthUnit::in(number); + CPPUNIT_ASSERT_DOUBLES_EQUAL(10.0, asInch.as_in(), 1e-4); + CPPUNIT_ASSERT_EQUAL(sal_Int64(9144000), asInch.raw()); +} + +CPPUNIT_TEST_SUITE_REGISTRATION(LengthUnitTest); diff --git a/include/basegfx/units/LengthUnit.hxx b/include/basegfx/units/LengthUnit.hxx new file mode 100644 index 000000000000..4edd1cc4704f --- /dev/null +++ b/include/basegfx/units/LengthUnit.hxx @@ -0,0 +1,194 @@ +/* -*- 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/. + */ + +#pragma once + +#include <sal/types.h> + +namespace gfx +{ +namespace +{ +constexpr sal_Int64 constFactor_hmm_to_EMU = 360ll; +constexpr sal_Int64 constFactor_mm_to_EMU = constFactor_hmm_to_EMU * 100ll; +constexpr sal_Int64 constFactor_cm_to_EMU = constFactor_hmm_to_EMU * 1000ll; +constexpr sal_Int64 constFactor_m_to_EMU = constFactor_hmm_to_EMU * 100000ll; + +constexpr sal_Int64 constFactor_twip_to_EMU = 635ll; +constexpr sal_Int64 constFactor_in_to_EMU = constFactor_twip_to_EMU * 1440ll; +constexpr sal_Int64 constFactor_pt_to_EMU = constFactor_twip_to_EMU * 20ll; +constexpr sal_Int64 constFactor_px_to_EMU = constFactor_twip_to_EMU * 15ll; + +} // end anonymous namespace + +template <typename T> class LengthUnitBase +{ +private: + // value in EMU units + T m_nValue; + + constexpr explicit LengthUnitBase(T nValue) + : m_nValue(nValue) + { + } + +public: + static constexpr LengthUnitBase cm(T nValue) + { + return LengthUnitBase(gfx::constFactor_cm_to_EMU * nValue); + } + + static constexpr LengthUnitBase mm(T nValue) + { + return LengthUnitBase(gfx::constFactor_mm_to_EMU * nValue); + } + + static constexpr LengthUnitBase hmm(T nValue) + { + return LengthUnitBase(gfx::constFactor_hmm_to_EMU * nValue); + } + + static constexpr LengthUnitBase in(T nValue) + { + return LengthUnitBase(gfx::constFactor_in_to_EMU * nValue); + } + + static constexpr LengthUnitBase twip(T nValue) + { + return LengthUnitBase(gfx::constFactor_twip_to_EMU * nValue); + } + + static constexpr LengthUnitBase pt(T nValue) + { + return LengthUnitBase(gfx::constFactor_pt_to_EMU * nValue); + } + + static constexpr LengthUnitBase px(T nValue) + { + return LengthUnitBase(gfx::constFactor_px_to_EMU * nValue); + } + + static constexpr LengthUnitBase emu(T nValue) { return LengthUnitBase(nValue); } + + constexpr explicit LengthUnitBase() + : m_nValue(0) + { + } + + constexpr explicit operator T() const { return m_nValue; } + + constexpr LengthUnitBase& operator+=(LengthUnitBase const& rhs) + { + m_nValue += rhs.m_nValue; + return *this; + } + + constexpr LengthUnitBase& operator-=(LengthUnitBase const& rhs) + { + m_nValue -= rhs.m_nValue; + return *this; + } + + constexpr LengthUnitBase& operator*=(T const& rhs) + { + m_nValue *= rhs; + return *this; + } + + constexpr LengthUnitBase& operator/=(T const& rhs) + { + m_nValue /= rhs; + return *this; + } + + constexpr LengthUnitBase& operator-() + { + m_nValue = -m_nValue; + return *this; + } + + T raw() const { return m_nValue; } + + double as_hmm() const { return m_nValue / double(constFactor_hmm_to_EMU); } + double as_mm() const { return m_nValue / double(constFactor_mm_to_EMU); } + double as_cm() const { return m_nValue / double(constFactor_cm_to_EMU); } + double as_m() const { return m_nValue / double(constFactor_m_to_EMU); } + double as_twip() const { return m_nValue / double(constFactor_twip_to_EMU); } + double as_in() const { return m_nValue / double(constFactor_in_to_EMU); } + double as_pt() const { return m_nValue / double(constFactor_pt_to_EMU); } + double as_px() const { return m_nValue / double(constFactor_px_to_EMU); } + double as_emu() const { return double(m_nValue); } +}; + +template <typename T> +inline LengthUnitBase<T> operator+(LengthUnitBase<T> lhs, const LengthUnitBase<T>& rhs) +{ + return lhs += rhs; +} + +template <typename T> +inline LengthUnitBase<T> operator-(LengthUnitBase<T> lhs, const LengthUnitBase<T>& rhs) +{ + return lhs -= rhs; +} + +template <typename T> inline LengthUnitBase<T> operator*(LengthUnitBase<T> lhs, const long rhs) +{ + return lhs *= rhs; +} + +template <typename T> inline LengthUnitBase<T> operator/(LengthUnitBase<T> lhs, const long rhs) +{ + return lhs /= rhs; +} + +typedef LengthUnitBase<sal_Int64> LengthUnit; +typedef LengthUnitBase<double> LengthUnitD; + +} // end namespace gfx + +constexpr gfx::LengthUnit operator"" _emu(unsigned long long value) +{ + return gfx::LengthUnit::emu(value); +} + +constexpr gfx::LengthUnit operator"" _in(unsigned long long value) +{ + return gfx::LengthUnit::in(value); +} + +constexpr gfx::LengthUnit operator"" _cm(unsigned long long value) +{ + return gfx::LengthUnit::cm(value); +} + +constexpr gfx::LengthUnit operator"" _mm(unsigned long long value) +{ + return gfx::LengthUnit::mm(value); +} + +constexpr gfx::LengthUnit operator"" _hmm(unsigned long long value) +{ + return gfx::LengthUnit::hmm(value); +} + +constexpr gfx::LengthUnit operator"" _twip(unsigned long long value) +{ + return gfx::LengthUnit::twip(value); +} + +constexpr gfx::LengthUnit operator"" _pt(unsigned long long value) +{ + return gfx::LengthUnit::pt(value); +} + +constexpr gfx::LengthUnit operator"" _px(unsigned long long value) +{ + return gfx::LengthUnit::px(value); +} _______________________________________________ Libreoffice-commits mailing list libreoffice-comm...@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/libreoffice-commits