summaryrefslogtreecommitdiffstats
path: root/vcl/source/uitest
diff options
context:
space:
mode:
Diffstat (limited to 'vcl/source/uitest')
-rw-r--r--vcl/source/uitest/logger.cxx624
-rw-r--r--vcl/source/uitest/uiobject.cxx1858
-rw-r--r--vcl/source/uitest/uitest.cxx80
-rw-r--r--vcl/source/uitest/uno/uiobject_uno.cxx224
-rw-r--r--vcl/source/uitest/uno/uiobject_uno.hxx55
-rw-r--r--vcl/source/uitest/uno/uitest_uno.cxx121
6 files changed, 2962 insertions, 0 deletions
diff --git a/vcl/source/uitest/logger.cxx b/vcl/source/uitest/logger.cxx
new file mode 100644
index 000000000..29520fd61
--- /dev/null
+++ b/vcl/source/uitest/logger.cxx
@@ -0,0 +1,624 @@
+/* -*- 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 <config_folders.h>
+
+#include <vcl/uitest/logger.hxx>
+
+#include <rtl/bootstrap.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <osl/file.hxx>
+#include <vcl/ctrl.hxx>
+#include <vcl/event.hxx>
+#include <vcl/uitest/uiobject.hxx>
+#include <vcl/uitest/eventdescription.hxx>
+#include <svdata.hxx>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <memory>
+
+namespace
+{
+bool isDialogWindow(vcl::Window const* pWindow)
+{
+ WindowType nType = pWindow->GetType();
+ if (nType == WindowType::DIALOG || nType == WindowType::MODELESSDIALOG)
+ return true;
+
+ // MESSBOX, INFOBOX, WARNINGBOX, ERRORBOX, QUERYBOX
+ if (nType >= WindowType::MESSBOX && nType <= WindowType::QUERYBOX)
+ return true;
+
+ if (nType == WindowType::TABDIALOG)
+ return true;
+
+ return false;
+}
+
+bool isTopWindow(vcl::Window const* pWindow)
+{
+ WindowType eType = pWindow->GetType();
+ if (eType == WindowType::FLOATINGWINDOW)
+ {
+ return pWindow->GetStyle() & WB_SYSTEMFLOATWIN;
+ }
+ return false;
+}
+
+vcl::Window* get_top_parent(vcl::Window* pWindow)
+{
+ if (isDialogWindow(pWindow) || isTopWindow(pWindow))
+ return pWindow;
+
+ vcl::Window* pParent = pWindow->GetParent();
+ if (!pParent)
+ return pWindow;
+
+ return get_top_parent(pParent);
+}
+}
+UITestLogger::UITestLogger()
+ : mbValid(false)
+{
+ static const char* pFile = std::getenv("LO_COLLECT_UIINFO");
+ if (pFile)
+ {
+ OUString aDirPath("${$BRAND_BASE_DIR/" LIBO_ETC_FOLDER
+ "/" SAL_CONFIGFILE("bootstrap") ":UserInstallation}/uitest/");
+ rtl::Bootstrap::expandMacros(aDirPath);
+ osl::Directory::createPath(aDirPath);
+ OUString aFilePath = aDirPath + OUString::fromUtf8(pFile);
+
+ maStream.Open(aFilePath, StreamMode::READWRITE | StreamMode::TRUNC);
+ mbValid = true;
+ }
+}
+
+void UITestLogger::logCommand(std::u16string_view rAction,
+ const css::uno::Sequence<css::beans::PropertyValue>& rArgs)
+{
+ if (!mbValid)
+ return;
+
+ OUStringBuffer aBuffer(rAction);
+
+ if (rArgs.hasElements())
+ {
+ aBuffer.append(" {");
+ for (const css::beans::PropertyValue& rProp : rArgs)
+ {
+ OUString aTypeName = rProp.Value.getValueTypeName();
+
+ if (aTypeName == "long" || aTypeName == "short")
+ {
+ sal_Int32 nValue = 0;
+ rProp.Value >>= nValue;
+ aBuffer.append("\"" + rProp.Name + "\": " + OUString::number(nValue) + ", ");
+ }
+ else if (aTypeName == "unsigned long")
+ {
+ sal_uInt32 nValue = 0;
+ rProp.Value >>= nValue;
+ aBuffer.append("\"" + rProp.Name + "\": " + OUString::number(nValue) + ", ");
+ }
+ else if (aTypeName == "boolean")
+ {
+ bool bValue = false;
+ rProp.Value >>= bValue;
+ aBuffer.append("\"" + rProp.Name + "\": ");
+ if (bValue)
+ aBuffer.append("True, ");
+ else
+ aBuffer.append("False, ");
+ }
+ }
+ aBuffer.append("}");
+ }
+
+ OUString aCommand(aBuffer.makeStringAndClear());
+ maStream.WriteLine(OUStringToOString(aCommand, RTL_TEXTENCODING_UTF8));
+}
+
+namespace
+{
+// most likely this should be recursive
+bool child_windows_have_focus(VclPtr<vcl::Window> const& xUIElement)
+{
+ sal_Int32 nCount = xUIElement->GetChildCount();
+ for (sal_Int32 i = 0; i < nCount; ++i)
+ {
+ vcl::Window* pChild = xUIElement->GetChild(i);
+ if (pChild->HasFocus())
+ {
+ return true;
+ }
+ if (child_windows_have_focus(VclPtr<vcl::Window>(pChild)))
+ return true;
+ }
+ return false;
+}
+}
+
+void UITestLogger::logAction(VclPtr<Control> const& xUIElement, VclEventId nEvent)
+{
+ if (!mbValid)
+ return;
+
+ if (xUIElement->get_id().isEmpty())
+ return;
+
+ std::unique_ptr<UIObject> pUIObject = xUIElement->GetUITestFactory()(xUIElement.get());
+ OUString aAction = pUIObject->get_action(nEvent);
+ if (!xUIElement->HasFocus() && !child_windows_have_focus(xUIElement))
+ {
+ return;
+ }
+
+ if (!aAction.isEmpty())
+ maStream.WriteLine(OUStringToOString(aAction, RTL_TEXTENCODING_UTF8));
+}
+
+void UITestLogger::logAction(vcl::Window* const& xUIWin, VclEventId nEvent)
+{
+ if (!mbValid)
+ return;
+
+ if (xUIWin->get_id().isEmpty())
+ return;
+
+ std::unique_ptr<UIObject> pUIObject = xUIWin->GetUITestFactory()(xUIWin);
+ OUString aAction = pUIObject->get_action(nEvent);
+
+ if (!aAction.isEmpty())
+ maStream.WriteLine(OUStringToOString(aAction, RTL_TEXTENCODING_UTF8));
+}
+
+void UITestLogger::log(std::u16string_view rString)
+{
+ if (!mbValid)
+ return;
+
+ if (rString.empty())
+ return;
+
+ maStream.WriteLine(OUStringToOString(rString, RTL_TEXTENCODING_UTF8));
+}
+
+void UITestLogger::logKeyInput(VclPtr<vcl::Window> const& xUIElement, const KeyEvent& rEvent)
+{
+ if (!mbValid)
+ return;
+
+ //We need to check for Parent's ID in case the UI Element is SubEdit of Combobox/SpinField
+ const OUString& rID
+ = xUIElement->get_id().isEmpty() ? xUIElement->GetParent()->get_id() : xUIElement->get_id();
+ if (rID.isEmpty())
+ return;
+
+ sal_Unicode nChar = rEvent.GetCharCode();
+ sal_uInt16 nKeyCode = rEvent.GetKeyCode().GetCode();
+ bool bShift = rEvent.GetKeyCode().IsShift();
+ bool bMod1 = rEvent.GetKeyCode().IsMod1();
+ bool bMod2 = rEvent.GetKeyCode().IsMod2();
+ bool bMod3 = rEvent.GetKeyCode().IsMod3();
+
+ std::map<OUString, sal_uInt16> aKeyMap
+ = { { "ESC", KEY_ESCAPE }, { "TAB", KEY_TAB }, { "DOWN", KEY_DOWN },
+ { "UP", KEY_UP }, { "LEFT", KEY_LEFT }, { "RIGHT", KEY_RIGHT },
+ { "DELETE", KEY_DELETE }, { "INSERT", KEY_INSERT }, { "BACKSPACE", KEY_BACKSPACE },
+ { "RETURN", KEY_RETURN }, { "HOME", KEY_HOME }, { "END", KEY_END },
+ { "PAGEUP", KEY_PAGEUP }, { "PAGEDOWN", KEY_PAGEDOWN } };
+
+ OUString aFound;
+ for (const auto& itr : aKeyMap)
+ {
+ if (itr.second == nKeyCode)
+ {
+ aFound = itr.first;
+ break;
+ }
+ }
+
+ OUString aKeyCode;
+ if (!aFound.isEmpty() || bShift || bMod1 || bMod2 || bMod3)
+ {
+ aKeyCode = "{\"KEYCODE\": \"";
+ if (bShift)
+ aKeyCode += "SHIFT+";
+
+ if (bMod1)
+ aKeyCode += "CTRL+";
+
+ if (bMod2)
+ aKeyCode += "ALT+";
+
+ if (aFound.isEmpty())
+ aKeyCode += OUStringChar(nChar) + "\"}";
+ else
+ aKeyCode += aFound + "\"}";
+ }
+ else
+ {
+ aKeyCode = "{\"TEXT\": \"" + OUStringChar(nChar) + "\"}";
+ }
+
+ std::unique_ptr<UIObject> pUIObject = xUIElement->GetUITestFactory()(xUIElement.get());
+
+ VclPtr<vcl::Window> pParent = xUIElement->GetParent();
+
+ while (pParent && !pParent->IsTopWindow())
+ {
+ pParent = pParent->GetParent();
+ }
+
+ OUString aParentID = pParent ? pParent->get_id() : OUString();
+
+ OUString aContent;
+
+ if (pUIObject->get_type() == "EditUIObject")
+ {
+ if (aParentID.isEmpty())
+ {
+ VclPtr<vcl::Window> pParent_top = get_top_parent(xUIElement);
+ aParentID = pParent_top->get_id();
+ }
+ if (aParentID.isEmpty())
+ {
+ aContent += "Type on '" + rID + "' " + aKeyCode;
+ }
+ else
+ {
+ aContent += "Type on '" + rID + "' " + aKeyCode + " from " + aParentID;
+ }
+ }
+ else if (pUIObject->get_type() == "SwEditWinUIObject" && rID == "writer_edit")
+ {
+ aContent = "Type on writer " + aKeyCode;
+ }
+ else if (pUIObject->get_type() == "ScGridWinUIObject" && rID == "grid_window")
+ {
+ aContent = "Type on current cell " + aKeyCode;
+ }
+ else if (pUIObject->get_type() == "ImpressWindowUIObject" && rID == "impress_win")
+ {
+ aContent = "Type on impress " + aKeyCode;
+ }
+ else if (pUIObject->get_type() == "WindowUIObject" && rID == "math_edit")
+ {
+ aContent = "Type on math " + aKeyCode;
+ }
+ else if (rID == "draw_win")
+ {
+ aContent = "Type on draw " + aKeyCode;
+ }
+ else
+ {
+ if (aParentID.isEmpty())
+ {
+ VclPtr<vcl::Window> pParent_top = get_top_parent(xUIElement);
+ aParentID = pParent_top->get_id();
+ }
+ if (aParentID.isEmpty())
+ {
+ aContent = "Type on '" + rID + "' " + aKeyCode;
+ }
+ else
+ {
+ aContent = "Type on '" + rID + "' " + aKeyCode + " from " + aParentID;
+ }
+ }
+ maStream.WriteLine(OUStringToOString(aContent, RTL_TEXTENCODING_UTF8));
+}
+
+namespace
+{
+OUString StringMapToOUString(const std::map<OUString, OUString>& rParameters)
+{
+ if (rParameters.empty())
+ return "";
+
+ OUStringBuffer aParameterString(static_cast<int>(rParameters.size() * 32));
+ aParameterString.append(" {");
+
+ for (std::map<OUString, OUString>::const_iterator itr = rParameters.begin();
+ itr != rParameters.end(); ++itr)
+ {
+ if (itr != rParameters.begin())
+ aParameterString.append(", ");
+ aParameterString.append("\"" + itr->first + "\": \"" + itr->second + "\"");
+ }
+
+ aParameterString.append("}");
+
+ return aParameterString.makeStringAndClear();
+}
+
+const OUString& GetValueInMapWithIndex(const std::map<OUString, OUString>& rParameters,
+ sal_Int32 index)
+{
+ sal_Int32 j = 0;
+
+ std::map<OUString, OUString>::const_iterator itr = rParameters.begin();
+
+ for (; itr != rParameters.end() && j < index; ++itr, ++j)
+ ;
+
+ assert(itr != rParameters.end());
+
+ return itr->second;
+}
+
+const OUString& GetKeyInMapWithIndex(const std::map<OUString, OUString>& rParameters,
+ sal_Int32 index)
+{
+ sal_Int32 j = 0;
+
+ std::map<OUString, OUString>::const_iterator itr = rParameters.begin();
+
+ for (; itr != rParameters.end() && j < index; ++itr, ++j)
+ ;
+
+ assert(itr != rParameters.end());
+
+ return itr->first;
+}
+}
+
+void UITestLogger::logEvent(const EventDescription& rDescription)
+{
+ OUString aParameterString = StringMapToOUString(rDescription.aParameters);
+
+ //here we will customize our statements depending on the caller of this function
+ OUString aLogLine;
+ //first check on general commands
+ if (rDescription.aAction == "SET")
+ {
+ aLogLine = "Set Zoom to " + GetValueInMapWithIndex(rDescription.aParameters, 0);
+ }
+ else if (rDescription.aAction == "SIDEBAR")
+ {
+ aLogLine = "From SIDEBAR Choose " + aParameterString;
+ }
+ else if (rDescription.aKeyWord == "ValueSet")
+ {
+ aLogLine = "Choose element with position "
+ + GetValueInMapWithIndex(rDescription.aParameters, 0) + " in '"
+ + rDescription.aID + "' from '" + rDescription.aParent + "'";
+ }
+ else if (rDescription.aAction == "SELECT" && rDescription.aID.isEmpty())
+ {
+ aLogLine = "Select " + aParameterString;
+ }
+ else if (rDescription.aID == "writer_edit")
+ {
+ if (rDescription.aAction == "GOTO")
+ {
+ aLogLine = "GOTO page number " + GetValueInMapWithIndex(rDescription.aParameters, 0);
+ }
+ else if (rDescription.aAction == "SELECT")
+ {
+ OUString to = GetValueInMapWithIndex(rDescription.aParameters, 0);
+ OUString from = GetValueInMapWithIndex(rDescription.aParameters, 1);
+ aLogLine = "Select from Pos " + from + " to Pos " + to;
+ }
+ else if (rDescription.aAction == "CREATE_TABLE")
+ {
+ OUString size = GetValueInMapWithIndex(rDescription.aParameters, 0);
+ aLogLine = "Create Table with " + size;
+ ;
+ }
+ else if (rDescription.aAction == "COPY")
+ {
+ aLogLine = "Copy the Selected Text";
+ }
+ else if (rDescription.aAction == "CUT")
+ {
+ aLogLine = "Cut the Selected Text";
+ }
+ else if (rDescription.aAction == "PASTE")
+ {
+ aLogLine = "Paste in the Current Cursor Location";
+ }
+ else if (rDescription.aAction == "BREAK_PAGE")
+ {
+ aLogLine = "Insert Break Page";
+ }
+ }
+ else if (rDescription.aID == "grid_window")
+ {
+ if (rDescription.aAction == "SELECT")
+ {
+ OUString type = GetKeyInMapWithIndex(rDescription.aParameters, 0);
+ if (type == "CELL" || type == "RANGE")
+ {
+ aLogLine = "Select from calc" + aParameterString;
+ }
+ else if (type == "TABLE")
+ {
+ aLogLine = "Switch to sheet number "
+ + GetValueInMapWithIndex(rDescription.aParameters, 0);
+ }
+ }
+ else if (rDescription.aAction == "LAUNCH")
+ {
+ aLogLine = "Launch" + GetKeyInMapWithIndex(rDescription.aParameters, 2) + " from Col "
+ + GetValueInMapWithIndex(rDescription.aParameters, 2) + " and Row "
+ + GetValueInMapWithIndex(rDescription.aParameters, 1);
+ }
+ else if (rDescription.aAction == "DELETE_CONTENT")
+ {
+ aLogLine = "Remove Content from This " + aParameterString;
+ }
+ else if (rDescription.aAction == "DELETE_CELLS")
+ {
+ aLogLine = "Delete The Cells in" + aParameterString;
+ }
+ else if (rDescription.aAction == "INSERT_CELLS")
+ {
+ aLogLine = "Insert Cell around the " + aParameterString;
+ }
+ else if (rDescription.aAction == "CUT")
+ {
+ aLogLine = "CUT the selected " + aParameterString;
+ }
+ else if (rDescription.aAction == "COPY")
+ {
+ aLogLine = "COPY the selected " + aParameterString;
+ }
+ else if (rDescription.aAction == "PASTE")
+ {
+ aLogLine = "Paste in the " + aParameterString;
+ }
+ else if (rDescription.aAction == "MERGE_CELLS")
+ {
+ aLogLine = "Merge " + aParameterString;
+ }
+ else if (rDescription.aAction == "UNMERGE_CELL")
+ {
+ aLogLine = "Delete the merged " + aParameterString;
+ }
+ else if (rDescription.aAction == "Rename_Sheet")
+ {
+ aLogLine = "Rename The Selected Tab to \""
+ + GetValueInMapWithIndex(rDescription.aParameters, 0) + "\"";
+ }
+ else if (rDescription.aAction == "InsertTab")
+ {
+ aLogLine = "Insert New Tab ";
+ }
+ else if (rDescription.aAction == "COMMENT")
+ {
+ OUString type = GetKeyInMapWithIndex(rDescription.aParameters, 0);
+ if (type == "OPEN")
+ {
+ aLogLine = "Open Comment";
+ }
+ else if (type == "CLOSE")
+ {
+ aLogLine = "Close Comment";
+ }
+ }
+ }
+ else if (rDescription.aID == "impress_win_or_draw_win")
+ {
+ if (rDescription.aAction == "Insert_New_Page_or_Slide")
+ {
+ if (UITestLogger::getInstance().getAppName() == "impress")
+ {
+ aLogLine = "Insert New Slide at Position "
+ + GetValueInMapWithIndex(rDescription.aParameters, 0);
+ }
+ else if (UITestLogger::getInstance().getAppName() == "draw")
+ {
+ aLogLine = "Insert New Page at Position "
+ + GetValueInMapWithIndex(rDescription.aParameters, 0);
+ }
+ }
+ else if (rDescription.aAction == "Delete_Slide_or_Page")
+ {
+ if (UITestLogger::getInstance().getAppName() == "impress")
+ {
+ aLogLine
+ = "Delete Slide number " + GetValueInMapWithIndex(rDescription.aParameters, 0);
+ }
+ else if (UITestLogger::getInstance().getAppName() == "draw")
+ {
+ aLogLine
+ = "Delete Page number " + GetValueInMapWithIndex(rDescription.aParameters, 0);
+ }
+ }
+ else if (rDescription.aAction == "Duplicate")
+ {
+ aLogLine = "Duplicate The Selected Slide ";
+ }
+ else if (rDescription.aAction == "RENAME")
+ {
+ if (UITestLogger::getInstance().getAppName() == "impress")
+ {
+ aLogLine = "Rename The Selected Slide from \""
+ + GetValueInMapWithIndex(rDescription.aParameters, 1) + "\" to \""
+ + GetValueInMapWithIndex(rDescription.aParameters, 0) + "\"";
+ }
+ else if (UITestLogger::getInstance().getAppName() == "draw")
+ {
+ aLogLine = "Rename The Selected Page from \""
+ + GetValueInMapWithIndex(rDescription.aParameters, 1) + "\" to \""
+ + GetValueInMapWithIndex(rDescription.aParameters, 0) + "\"";
+ }
+ }
+ }
+ else if (rDescription.aKeyWord == "SwEditWinUIObject")
+ {
+ if (rDescription.aAction == "LEAVE")
+ {
+ aLogLine = "Leave '" + rDescription.aID + "'";
+ }
+ else if (rDescription.aAction == "SHOW")
+ {
+ aLogLine = "Show '" + rDescription.aID + "'";
+ }
+ else if (rDescription.aAction == "HIDE")
+ {
+ aLogLine = "Hide '" + rDescription.aID + "'";
+ }
+ else if (rDescription.aAction == "DELETE")
+ {
+ aLogLine = "Delete '" + rDescription.aID + "'";
+ }
+ else if (rDescription.aAction == "SETRESOLVED")
+ {
+ aLogLine = "Resolve '" + rDescription.aID + "'";
+ }
+ }
+ else if (rDescription.aParent == "element_selector")
+ {
+ aLogLine = "Select element no " + rDescription.aID + " From " + rDescription.aParent;
+ }
+ else if (rDescription.aKeyWord == "MenuButton")
+ {
+ if (rDescription.aAction == "OPENLIST")
+ {
+ aLogLine = "Open List From " + rDescription.aID;
+ }
+ else if (rDescription.aAction == "CLOSELIST")
+ {
+ aLogLine = "Close List From " + rDescription.aID;
+ }
+ else if (rDescription.aAction == "OPENFROMLIST")
+ {
+ aLogLine = "Select item no " + GetValueInMapWithIndex(rDescription.aParameters, 0)
+ + " From List of " + rDescription.aID;
+ }
+ }
+ else if (rDescription.aKeyWord == "VerticalTab")
+ {
+ aLogLine = "Choose Tab number " + GetValueInMapWithIndex(rDescription.aParameters, 0)
+ + " in '" + rDescription.aID + "'";
+ }
+ else
+ {
+ aLogLine = rDescription.aKeyWord + " Action:" + rDescription.aAction + " Id:"
+ + rDescription.aID + " Parent:" + rDescription.aParent + aParameterString;
+ }
+ log(aLogLine);
+}
+
+UITestLogger& UITestLogger::getInstance()
+{
+ ImplSVData* const pSVData = ImplGetSVData();
+ assert(pSVData);
+
+ if (!pSVData->maFrameData.m_pUITestLogger)
+ {
+ pSVData->maFrameData.m_pUITestLogger.reset(new UITestLogger);
+ }
+
+ return *pSVData->maFrameData.m_pUITestLogger;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/uitest/uiobject.cxx b/vcl/source/uitest/uiobject.cxx
new file mode 100644
index 000000000..fa5c95346
--- /dev/null
+++ b/vcl/source/uitest/uiobject.cxx
@@ -0,0 +1,1858 @@
+/* -*- 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/uitest/uiobject.hxx>
+#include <vcl/uitest/metricfielduiobject.hxx>
+#include <vcl/uitest/formattedfielduiobject.hxx>
+
+#include <vcl/svapp.hxx>
+#include <vcl/toolkit/combobox.hxx>
+#include <vcl/event.hxx>
+#include <vcl/toolkit/floatwin.hxx>
+#include <vcl/menu.hxx>
+#include <vcl/tabpage.hxx>
+#include <vcl/tabctrl.hxx>
+#include <vcl/toolkit/lstbox.hxx>
+#include <vcl/toolkit/spin.hxx>
+#include <vcl/toolkit/fmtfield.hxx>
+#include <vcl/toolkit/spinfld.hxx>
+#include <vcl/toolkit/ivctrl.hxx>
+#include <vcl/toolkit/button.hxx>
+#include <vcl/toolkit/dialog.hxx>
+#include <vcl/toolkit/edit.hxx>
+#include <vcl/toolkit/field.hxx>
+#include <vcl/toolkit/treelistbox.hxx>
+#include <vcl/toolkit/treelistentry.hxx>
+#include <vcl/toolkit/svlbitm.hxx>
+#include <vcl/toolkit/menubtn.hxx>
+#include <vcl/toolkit/vclmedit.hxx>
+#include <vcl/uitest/logger.hxx>
+#include <uiobject-internal.hxx>
+#include <verticaltabctrl.hxx>
+#include <vcl/toolbox.hxx>
+
+#include <comphelper/string.hxx>
+#include <comphelper/lok.hxx>
+
+#include <rtl/ustrbuf.hxx>
+#include <sal/log.hxx>
+
+#include <iostream>
+#include <memory>
+#include <vector>
+
+UIObject::~UIObject()
+{
+}
+
+StringMap UIObject::get_state()
+{
+ StringMap aMap;
+ aMap["NotImplemented"] = "NotImplemented";
+ return aMap;
+}
+
+void UIObject::execute(const OUString& /*rAction*/,
+ const StringMap& /*rParameters*/)
+{
+ // should never be called
+ throw std::exception();
+}
+
+OUString UIObject::get_type() const
+{
+ return "Generic UIObject";
+}
+
+std::unique_ptr<UIObject> UIObject::get_child(const OUString&)
+{
+ return std::unique_ptr<UIObject>();
+}
+
+std::set<OUString> UIObject::get_children() const
+{
+ return std::set<OUString>();
+}
+
+OUString UIObject::dumpState() const
+{
+ return OUString();
+}
+
+OUString UIObject::dumpHierarchy() const
+{
+ return OUString();
+}
+
+OUString UIObject::get_action(VclEventId /*nEvent*/) const
+{
+ return OUString();
+}
+
+namespace {
+
+bool isDialogWindow(vcl::Window const * pWindow)
+{
+ WindowType nType = pWindow->GetType();
+ if (nType == WindowType::DIALOG || nType == WindowType::MODELESSDIALOG)
+ return true;
+
+ // MESSBOX, INFOBOX, WARNINGBOX, ERRORBOX, QUERYBOX
+ if (nType >= WindowType::MESSBOX && nType <= WindowType::QUERYBOX)
+ return true;
+
+ if (nType == WindowType::TABDIALOG)
+ return true;
+
+ return false;
+}
+
+bool isTopWindow(vcl::Window const * pWindow)
+{
+ WindowType eType = pWindow->GetType();
+ if (eType == WindowType::FLOATINGWINDOW)
+ {
+ return pWindow->GetStyle() & WB_SYSTEMFLOATWIN;
+ }
+ return false;
+}
+
+vcl::Window* get_top_parent(vcl::Window* pWindow)
+{
+ if (isDialogWindow(pWindow) || isTopWindow(pWindow))
+ return pWindow;
+
+ vcl::Window* pParent = pWindow->GetParent();
+ if (!pParent)
+ return pWindow;
+
+ return get_top_parent(pParent);
+}
+
+std::vector<KeyEvent> generate_key_events_from_text(const OUString& rStr)
+{
+ std::vector<KeyEvent> aEvents;
+ vcl::KeyCode aCode;
+ for (sal_Int32 i = 0, n = rStr.getLength();
+ i != n; ++i)
+ {
+ aEvents.emplace_back(rStr[i], aCode);
+ }
+ return aEvents;
+}
+
+sal_uInt16 get_key(sal_Unicode cChar, bool& bShift)
+{
+ bShift = false;
+ if (cChar >= 'a' && cChar <= 'z')
+ return KEY_A + (cChar - 'a');
+ else if (cChar >= 'A' && cChar <= 'Z')
+ {
+ bShift = true;
+ return KEY_A + (cChar - 'A');
+ }
+ else if (cChar >= '0' && cChar <= '9')
+ return KEY_0 + (cChar - 'A');
+
+ return cChar;
+}
+
+bool isFunctionKey(const OUString& rStr, sal_uInt16& rKeyCode)
+{
+ std::map<OUString, sal_uInt16> aFunctionKeyMap = {
+ {"F1", KEY_F1},
+ {"F2", KEY_F2},
+ {"F3", KEY_F3},
+ {"F4", KEY_F4},
+ {"F5", KEY_F5},
+ {"F6", KEY_F6},
+ {"F7", KEY_F7},
+ {"F8", KEY_F8},
+ {"F9", KEY_F9},
+ {"F10", KEY_F10},
+ {"F11", KEY_F11},
+ {"F12", KEY_F12}
+ };
+
+ rKeyCode = 0;
+ auto itr = aFunctionKeyMap.find(rStr);
+ if (itr == aFunctionKeyMap.end())
+ return false;
+
+ rKeyCode = itr->second;
+ return true;
+}
+
+std::vector<KeyEvent> generate_key_events_from_keycode(std::u16string_view rStr)
+{
+ std::vector<KeyEvent> aEvents;
+
+ std::map<OUString, sal_uInt16> aKeyMap = {
+ {"ESC", KEY_ESCAPE},
+ {"TAB", KEY_TAB},
+ {"DOWN", KEY_DOWN},
+ {"UP", KEY_UP},
+ {"LEFT", KEY_LEFT},
+ {"RIGHT", KEY_RIGHT},
+ {"DELETE", KEY_DELETE},
+ {"INSERT", KEY_INSERT},
+ {"SPACE", KEY_SPACE},
+ {"BACKSPACE", KEY_BACKSPACE},
+ {"RETURN", KEY_RETURN},
+ {"HOME", KEY_HOME},
+ {"END", KEY_END},
+ {"PAGEUP", KEY_PAGEUP},
+ {"PAGEDOWN", KEY_PAGEDOWN}
+ };
+
+ // split string along '+'
+ // then translate to keycodes
+ bool bShift = false;
+ bool bMod1 = false;
+ bool bMod2 = false;
+ OUString aRemainingText;
+
+ std::vector<OUString> aTokens = comphelper::string::split(rStr, '+');
+ for (auto const& token : aTokens)
+ {
+ OUString aToken = token.trim();
+ if (aToken == "CTRL")
+ {
+ bMod1 = true;
+ }
+ else if (aToken == "SHIFT")
+ {
+ bShift = true;
+ }
+ else if (aToken == "ALT")
+ {
+ bMod2 = true;
+ }
+ else
+ aRemainingText = aToken;
+ }
+
+ sal_uInt16 nFunctionKey = 0;
+ if (isFunctionKey(aRemainingText, nFunctionKey))
+ {
+ vcl::KeyCode aCode(nFunctionKey, bShift, bMod1, bMod2, false);
+ aEvents.emplace_back(0, aCode);
+ }
+ else if (aKeyMap.find(aRemainingText) != aKeyMap.end())
+ {
+ sal_uInt16 nKey = aKeyMap[aRemainingText];
+ vcl::KeyCode aCode(nKey, bShift, bMod1, bMod2, false);
+ aEvents.emplace_back( 'a', aCode);
+ }
+ else
+ {
+ for (sal_Int32 i = 0; i < aRemainingText.getLength(); ++i)
+ {
+ bool bShiftThroughKey = false;
+ sal_uInt16 nKey = get_key(aRemainingText[i], bShiftThroughKey);
+ vcl::KeyCode aCode(nKey, bShift || bShiftThroughKey, bMod1, bMod2, false);
+ aEvents.emplace_back(aRemainingText[i], aCode);
+ }
+ }
+
+ return aEvents;
+}
+
+OUString to_string(const Point& rPos)
+{
+ OUString sStr = OUString::number(rPos.X())
+ + "x"
+ + OUString::number(rPos.Y());
+
+ return sStr;
+}
+
+OUString to_string(const Size& rSize)
+{
+ OUString sStr = OUString::number(rSize.Width())
+ + "x"
+ + OUString::number(rSize.Height());
+
+ return sStr;
+}
+
+}
+
+WindowUIObject::WindowUIObject(const VclPtr<vcl::Window>& xWindow):
+ mxWindow(xWindow)
+{
+}
+
+StringMap WindowUIObject::get_state()
+{
+ // Double-buffering is not interesting for uitesting, but can result in direct paint for a
+ // double-buffered widget, which is incorrect.
+ if (mxWindow->SupportsDoubleBuffering())
+ mxWindow->RequestDoubleBuffering(false);
+
+ StringMap aMap;
+ aMap["Visible"] = OUString::boolean(mxWindow->IsVisible());
+ aMap["ReallyVisible"] = OUString::boolean(mxWindow->IsReallyVisible());
+ aMap["Enabled"] = OUString::boolean(mxWindow->IsEnabled());
+ aMap["HasFocus"] = OUString::boolean(mxWindow->HasChildPathFocus());
+ aMap["WindowType"] = OUString::number(static_cast<sal_uInt16>(mxWindow->GetType()), 16);
+
+ Point aPos = mxWindow->GetPosPixel();
+ aMap["RelPosition"] = to_string(aPos);
+ aMap["Size"] = to_string(mxWindow->GetSizePixel());
+ aMap["ID"] = mxWindow->get_id();
+ vcl::Window* pParent = mxWindow->GetParent();
+ if (pParent)
+ aMap["Parent"] = mxWindow->GetParent()->get_id();
+
+ bool bIgnoreAllExceptTop = isDialogWindow(mxWindow.get());
+ while(pParent)
+ {
+ Point aParentPos = pParent->GetPosPixel();
+ if (!bIgnoreAllExceptTop)
+ aPos += aParentPos;
+
+ if (isDialogWindow(pParent))
+ {
+ bIgnoreAllExceptTop = true;
+ }
+
+ pParent = pParent->GetParent();
+
+ if (!pParent && bIgnoreAllExceptTop)
+ aPos += aParentPos;
+ }
+ aMap["AbsPosition"] = to_string(aPos);
+ aMap["Text"] = mxWindow->GetText();
+ aMap["DisplayText"] = mxWindow->GetDisplayText();
+
+ return aMap;
+}
+
+void WindowUIObject::execute(const OUString& rAction,
+ const StringMap& rParameters)
+{
+ if (rAction == "SET")
+ {
+ for (auto const& parameter : rParameters)
+ {
+ std::cout << parameter.first;
+ }
+ }
+ else if (rAction == "TYPE")
+ {
+ auto it = rParameters.find("TEXT");
+ if (it != rParameters.end())
+ {
+ const OUString& rText = it->second;
+ auto aKeyEvents = generate_key_events_from_text(rText);
+ for (auto const& keyEvent : aKeyEvents)
+ {
+ mxWindow->KeyInput(keyEvent);
+ }
+ }
+ else if (rParameters.find("KEYCODE") != rParameters.end())
+ {
+ auto itr = rParameters.find("KEYCODE");
+ const OUString rText = itr->second;
+ auto aKeyEvents = generate_key_events_from_keycode(rText);
+ for (auto const& keyEvent : aKeyEvents)
+ {
+ mxWindow->KeyInput(keyEvent);
+ }
+ }
+ else
+ {
+ OStringBuffer buf;
+ for (auto const & rPair : rParameters)
+ buf.append("," + rPair.first.toUtf8() + "=" + rPair.second.toUtf8());
+ SAL_WARN("vcl.uitest", "missing parameter TEXT to action TYPE "
+ << buf.makeStringAndClear());
+ throw std::logic_error("missing parameter TEXT to action TYPE");
+ }
+ }
+ else if (rAction == "FOCUS")
+ {
+ mxWindow->GrabFocus();
+ }
+ else
+ {
+ OStringBuffer buf;
+ for (auto const & rPair : rParameters)
+ buf.append("," + rPair.first.toUtf8() + "=" + rPair.second.toUtf8());
+ SAL_WARN("vcl.uitest", "unknown action for " << get_name()
+ << ". Action: " << rAction << buf.makeStringAndClear());
+ throw std::logic_error("unknown action");
+ }
+}
+
+OUString WindowUIObject::get_type() const
+{
+ return get_name();
+}
+
+namespace {
+
+vcl::Window* findChild(vcl::Window* pParent, const OUString& rID, bool bRequireVisible = false)
+{
+ if (!pParent || pParent->isDisposed())
+ return nullptr;
+
+ if (pParent->get_id() == rID)
+ return pParent;
+
+ size_t nCount = pParent->GetChildCount();
+ for (size_t i = 0; i < nCount; ++i)
+ {
+ vcl::Window* pChild = pParent->GetChild(i);
+ bool bCandidate = !bRequireVisible || pChild->IsVisible();
+ if (!bCandidate)
+ continue;
+
+ if (pChild->get_id() == rID)
+ return pChild;
+
+ vcl::Window* pResult = findChild(pChild, rID);
+ if (pResult)
+ return pResult;
+ }
+
+ return nullptr;
+}
+
+void addChildren(vcl::Window const * pParent, std::set<OUString>& rChildren)
+{
+ if (!pParent)
+ return;
+
+ size_t nCount = pParent->GetChildCount();
+ for (size_t i = 0; i < nCount; ++i)
+ {
+ vcl::Window* pChild = pParent->GetChild(i);
+ if (pChild)
+ {
+ OUString aId = pChild->get_id();
+ if (!aId.isEmpty())
+ {
+ auto ret = rChildren.insert(aId);
+ SAL_WARN_IF(!ret.second, "vcl.uitest", "duplicate ids '" << aId << "' for ui elements. violates locally unique requirement");
+ }
+
+ addChildren(pChild, rChildren);
+ }
+ }
+}
+
+}
+
+std::unique_ptr<UIObject> WindowUIObject::get_child(const OUString& rID)
+{
+ // in a first step try the real children before moving to the top level parent
+ // This makes it easier to handle cases with the same ID as there is a way
+ // to resolve conflicts
+ vcl::Window* pWindow = findChild(mxWindow.get(), rID);
+ if (!pWindow)
+ {
+ vcl::Window* pDialogParent = get_top_parent(mxWindow.get());
+ pWindow = findChild(pDialogParent, rID);
+ }
+
+ if (!pWindow)
+ throw css::uno::RuntimeException("Could not find child with id: " + rID);
+
+ FactoryFunction aFunction = pWindow->GetUITestFactory();
+ return aFunction(pWindow);
+}
+
+std::unique_ptr<UIObject> WindowUIObject::get_visible_child(const OUString& rID)
+{
+ // in a first step try the real children before moving to the top level parent
+ // This makes it easier to handle cases with the same ID as there is a way
+ // to resolve conflicts
+ vcl::Window* pWindow = findChild(mxWindow.get(), rID, true);
+ if (!pWindow)
+ {
+ vcl::Window* pDialogParent = get_top_parent(mxWindow.get());
+ pWindow = findChild(pDialogParent, rID, true);
+ }
+
+ if (!pWindow)
+ throw css::uno::RuntimeException("Could not find child with id: " + rID);
+
+ FactoryFunction aFunction = pWindow->GetUITestFactory();
+ return aFunction(pWindow);
+}
+
+std::set<OUString> WindowUIObject::get_children() const
+{
+ std::set<OUString> aChildren;
+ vcl::Window* pDialogParent = get_top_parent(mxWindow.get());
+ if (!pDialogParent->isDisposed())
+ {
+ aChildren.insert(pDialogParent->get_id());
+ addChildren(pDialogParent, aChildren);
+ }
+ return aChildren;
+}
+
+OUString WindowUIObject::get_name() const
+{
+ return "WindowUIObject";
+}
+
+namespace {
+
+OUString escape(const OUString& rStr)
+{
+ return rStr.replaceAll("\"", "\\\"");
+}
+
+}
+
+OUString WindowUIObject::dumpState() const
+{
+ OUStringBuffer aStateString = "{\"name\":\"" + mxWindow->get_id() + "\"";
+ aStateString.append(", \"ImplementationName\":\"").appendAscii(typeid(*mxWindow).name()).append("\"");
+ StringMap aState = const_cast<WindowUIObject*>(this)->get_state();
+ for (auto const& elem : aState)
+ {
+ OUString property = ",\"" + elem.first + "\":\"" + escape(elem.second) + "\"";
+ aStateString.append(property);
+ }
+
+ size_t nCount = mxWindow->GetChildCount();
+
+ if (nCount)
+ aStateString.append(",\"children\":[");
+
+ for (size_t i = 0; i < nCount; ++i)
+ {
+ if (i != 0)
+ {
+ aStateString.append(",");
+ }
+ vcl::Window* pChild = mxWindow->GetChild(i);
+ std::unique_ptr<UIObject> pChildWrapper =
+ pChild->GetUITestFactory()(pChild);
+ OUString children = pChildWrapper->dumpState();
+ aStateString.append(children);
+ }
+
+ if (nCount)
+ aStateString.append("]");
+
+ aStateString.append("}");
+
+ OUString aString = aStateString.makeStringAndClear();
+ return aString.replaceAll("\n", "\\n");
+}
+
+OUString WindowUIObject::dumpHierarchy() const
+{
+ vcl::Window* pDialogParent = get_top_parent(mxWindow.get());
+ std::unique_ptr<UIObject> pParentWrapper =
+ pDialogParent->GetUITestFactory()(pDialogParent);
+ return pParentWrapper->dumpState();
+}
+
+OUString WindowUIObject::get_action(VclEventId nEvent) const
+{
+
+ OUString aActionName;
+ switch (nEvent)
+ {
+ case VclEventId::ControlGetFocus:
+ case VclEventId::ControlLoseFocus:
+ return OUString();
+
+ case VclEventId::ButtonClick:
+ case VclEventId::CheckboxToggle:
+ aActionName = "CLICK";
+ break;
+
+ case VclEventId::EditModify:
+ aActionName = "TYPE";
+ break;
+ default:
+ aActionName = OUString::number(static_cast<int>(nEvent));
+ }
+ return "Action on element: " + mxWindow->get_id() + " with action : " + aActionName;
+}
+
+std::unique_ptr<UIObject> WindowUIObject::create(vcl::Window* pWindow)
+{
+ return std::unique_ptr<UIObject>(new WindowUIObject(pWindow));
+}
+
+ButtonUIObject::ButtonUIObject(const VclPtr<Button>& xButton):
+ WindowUIObject(xButton),
+ mxButton(xButton)
+{
+}
+
+ButtonUIObject::~ButtonUIObject()
+{
+}
+
+StringMap ButtonUIObject::get_state()
+{
+ StringMap aMap = WindowUIObject::get_state();
+ // Move that to a Control base class
+ aMap["Label"] = mxButton->GetDisplayText();
+
+ return aMap;
+}
+
+void ButtonUIObject::execute(const OUString& rAction,
+ const StringMap& rParameters)
+{
+ if (rAction == "CLICK")
+ {
+ //Click doesn't call toggle when it's a pushbutton tweaked to be a toggle-button
+ if (PushButton *pPushButton = (mxButton->GetStyle() & WB_TOGGLE) ? dynamic_cast<PushButton*>(mxButton.get()) : nullptr)
+ {
+ pPushButton->Check(!pPushButton->IsChecked());
+ pPushButton->Toggle();
+ return;
+ }
+ mxButton->Click();
+ }
+ else
+ WindowUIObject::execute(rAction, rParameters);
+}
+
+OUString ButtonUIObject::get_name() const
+{
+ return "ButtonUIObject";
+}
+
+OUString ButtonUIObject::get_action(VclEventId nEvent) const
+{
+ if (nEvent == VclEventId::ButtonClick)
+ {
+ if(mxButton->get_id()=="writer_all")
+ {
+ UITestLogger::getInstance().setAppName("writer");
+ return "Start writer" ;
+ }
+ else if(mxButton->get_id()=="calc_all")
+ {
+ UITestLogger::getInstance().setAppName("calc");
+ return "Start calc" ;
+ }
+ else if(mxButton->get_id()=="impress_all")
+ {
+ UITestLogger::getInstance().setAppName("impress");
+ return "Start impress" ;
+ }
+ else if(mxButton->get_id()=="draw_all")
+ {
+ UITestLogger::getInstance().setAppName("draw");
+ return "Start draw" ;
+ }
+ else if(mxButton->get_id()=="math_all")
+ {
+ UITestLogger::getInstance().setAppName("math");
+ return "Start math" ;
+ }
+ else if(mxButton->get_id()=="database_all")
+ {
+ UITestLogger::getInstance().setAppName("database");
+ return "Start database" ;
+ }
+ else{
+ if (get_top_parent(mxButton)->get_id().isEmpty()){
+ //This part because if we don't have parent
+ return "Click on '" + mxButton->get_id() ;
+ }
+ return "Click on '" + mxButton->get_id() + "' from "+
+ get_top_parent(mxButton)->get_id();
+ }
+ }
+ else
+ return WindowUIObject::get_action(nEvent);
+}
+
+std::unique_ptr<UIObject> ButtonUIObject::create(vcl::Window* pWindow)
+{
+ Button* pButton = dynamic_cast<Button*>(pWindow);
+ assert(pButton);
+ return std::unique_ptr<UIObject>(new ButtonUIObject(pButton));
+}
+
+DialogUIObject::DialogUIObject(const VclPtr<Dialog>& xDialog):
+ WindowUIObject(xDialog),
+ mxDialog(xDialog)
+{
+}
+
+DialogUIObject::~DialogUIObject()
+{
+}
+
+StringMap DialogUIObject::get_state()
+{
+ StringMap aMap = WindowUIObject::get_state();
+ aMap["Modal"] = OUString::boolean(mxDialog->IsModalInputMode());
+
+ return aMap;
+}
+
+OUString DialogUIObject::get_name() const
+{
+ return "DialogUIObject";
+}
+
+std::unique_ptr<UIObject> DialogUIObject::create(vcl::Window* pWindow)
+{
+ Dialog* pDialog = dynamic_cast<Dialog*>(pWindow);
+ assert(pDialog);
+ return std::unique_ptr<UIObject>(new DialogUIObject(pDialog));
+}
+
+EditUIObject::EditUIObject(const VclPtr<Edit>& xEdit):
+ WindowUIObject(xEdit),
+ mxEdit(xEdit)
+{
+}
+
+EditUIObject::~EditUIObject()
+{
+}
+
+void EditUIObject::execute(const OUString& rAction,
+ const StringMap& rParameters)
+{
+ bool bHandled = true;
+ if (rAction == "TYPE")
+ {
+ auto it = rParameters.find("TEXT");
+ if (it != rParameters.end())
+ {
+ const OUString& rText = it->second;
+ auto aKeyEvents = generate_key_events_from_text(rText);
+ for (auto const& keyEvent : aKeyEvents)
+ {
+ mxEdit->KeyInput(keyEvent);
+ }
+ }
+ else
+ {
+ bHandled = false;
+ }
+ }
+ else if (rAction == "SET")
+ {
+ auto it = rParameters.find("TEXT");
+ if (it != rParameters.end())
+ {
+ mxEdit->SetText(it->second);
+ mxEdit->Modify();
+ }
+ else
+ bHandled = false;
+ }
+ else if (rAction == "SELECT")
+ {
+ if (rParameters.find("FROM") != rParameters.end() &&
+ rParameters.find("TO") != rParameters.end())
+ {
+ tools::Long nMin = rParameters.find("FROM")->second.toInt32();
+ tools::Long nMax = rParameters.find("TO")->second.toInt32();
+ Selection aSelection(nMin, nMax);
+ mxEdit->SetSelection(aSelection);
+ }
+ }
+ else if (rAction == "CLEAR")
+ {
+ mxEdit->SetText("");
+ mxEdit->Modify();
+ bHandled = true;
+ }
+ else
+ {
+ bHandled = false;
+ }
+
+ if (!bHandled)
+ WindowUIObject::execute(rAction, rParameters);
+}
+
+StringMap EditUIObject::get_state()
+{
+ StringMap aMap = WindowUIObject::get_state();
+ aMap["MaxTextLength"] = OUString::number(mxEdit->GetMaxTextLen());
+ aMap["QuickHelpText"] = mxEdit->GetQuickHelpText();
+ aMap["SelectedText"] = mxEdit->GetSelected();
+ aMap["Text"] = mxEdit->GetText();
+
+ return aMap;
+}
+
+OUString EditUIObject::get_action(VclEventId nEvent) const
+{
+ if (nEvent == VclEventId::EditSelectionChanged)
+ {
+ const Selection& rSelection = mxEdit->GetSelection();
+ tools::Long nMin = rSelection.Min();
+ tools::Long nMax = rSelection.Max();
+ if(get_top_parent(mxEdit)->get_id().isEmpty()){
+ //This part because if we don't have parent
+ return "Select in '" +
+ mxEdit->get_id() +
+ "' {\"FROM\": \"" + OUString::number(nMin) + "\", \"TO\": \"" +
+ OUString::number(nMax) + "\"}"
+ ;
+ }
+ return "Select in '" +
+ mxEdit->get_id() +
+ "' {\"FROM\": \"" + OUString::number(nMin) + "\", \"TO\": \"" +
+ OUString::number(nMax) + "\"} from "
+ + get_top_parent(mxEdit)->get_id()
+ ;
+ }
+ else
+ return WindowUIObject::get_action(nEvent);
+}
+
+OUString EditUIObject::get_name() const
+{
+ return "EditUIObject";
+}
+
+std::unique_ptr<UIObject> EditUIObject::create(vcl::Window* pWindow)
+{
+ Edit* pEdit = dynamic_cast<Edit*>(pWindow);
+ assert(pEdit);
+ return std::unique_ptr<UIObject>(new EditUIObject(pEdit));
+}
+
+MultiLineEditUIObject::MultiLineEditUIObject(const VclPtr<VclMultiLineEdit>& xEdit):
+ WindowUIObject(xEdit),
+ mxEdit(xEdit)
+{
+}
+
+MultiLineEditUIObject::~MultiLineEditUIObject()
+{
+}
+
+void MultiLineEditUIObject::execute(const OUString& rAction,
+ const StringMap& rParameters)
+{
+ bool bHandled = true;
+ if (rAction == "TYPE")
+ {
+ WindowUIObject aChildObj(mxEdit->GetTextWindow());
+ aChildObj.execute(rAction, rParameters);
+ }
+ else if (rAction == "SELECT")
+ {
+ if (rParameters.find("FROM") != rParameters.end() &&
+ rParameters.find("TO") != rParameters.end())
+ {
+ tools::Long nMin = rParameters.find("FROM")->second.toInt32();
+ tools::Long nMax = rParameters.find("TO")->second.toInt32();
+ Selection aSelection(nMin, nMax);
+ mxEdit->SetSelection(aSelection);
+ }
+ }
+ else
+ {
+ bHandled = false;
+ }
+
+ if (!bHandled)
+ WindowUIObject::execute(rAction, rParameters);
+}
+
+StringMap MultiLineEditUIObject::get_state()
+{
+ StringMap aMap = WindowUIObject::get_state();
+ aMap["MaxTextLength"] = OUString::number(mxEdit->GetMaxTextLen());
+ aMap["SelectedText"] = mxEdit->GetSelected();
+ aMap["Text"] = mxEdit->GetText();
+
+ return aMap;
+}
+
+OUString MultiLineEditUIObject::get_name() const
+{
+ return "MultiLineEditUIObject";
+}
+
+std::unique_ptr<UIObject> MultiLineEditUIObject::create(vcl::Window* pWindow)
+{
+ VclMultiLineEdit* pEdit = dynamic_cast<VclMultiLineEdit*>(pWindow);
+ assert(pEdit);
+ return std::unique_ptr<UIObject>(new MultiLineEditUIObject(pEdit));
+}
+
+ExpanderUIObject::ExpanderUIObject(const VclPtr<VclExpander>& xExpander)
+ : WindowUIObject(xExpander)
+ , mxExpander(xExpander)
+{
+}
+
+ExpanderUIObject::~ExpanderUIObject()
+{
+}
+
+void ExpanderUIObject::execute(const OUString& rAction, const StringMap& rParameters)
+{
+ if (rAction == "EXPAND")
+ {
+ mxExpander->set_expanded(true);
+ }
+ else if (rAction == "COLLAPSE")
+ {
+ mxExpander->set_expanded(false);
+ }
+ else
+ WindowUIObject::execute(rAction, rParameters);
+}
+
+StringMap ExpanderUIObject::get_state()
+{
+ StringMap aMap = WindowUIObject::get_state();
+ aMap["Expanded"] = OUString::boolean(mxExpander->get_expanded());
+ return aMap;
+}
+
+OUString ExpanderUIObject::get_name() const
+{
+ return "ExpanderUIObject";
+}
+
+std::unique_ptr<UIObject> ExpanderUIObject::create(vcl::Window* pWindow)
+{
+ VclExpander* pVclExpander = dynamic_cast<VclExpander*>(pWindow);
+ assert(pVclExpander);
+ return std::unique_ptr<UIObject>(new ExpanderUIObject(pVclExpander));
+}
+
+CheckBoxUIObject::CheckBoxUIObject(const VclPtr<CheckBox>& xCheckbox):
+ WindowUIObject(xCheckbox),
+ mxCheckBox(xCheckbox)
+{
+}
+
+CheckBoxUIObject::~CheckBoxUIObject()
+{
+}
+
+void CheckBoxUIObject::execute(const OUString& rAction,
+ const StringMap& rParameters)
+{
+ if (rAction == "CLICK")
+ {
+ // don't use toggle directly, it does not set the value
+ mxCheckBox->ImplCheck();
+ }
+ else
+ WindowUIObject::execute(rAction, rParameters);
+}
+
+StringMap CheckBoxUIObject::get_state()
+{
+ StringMap aMap = WindowUIObject::get_state();
+ aMap["Selected"] = OUString::boolean(mxCheckBox->IsChecked());
+ aMap["TriStateEnabled"] = OUString::boolean(mxCheckBox->IsTriStateEnabled());
+ return aMap;
+}
+
+OUString CheckBoxUIObject::get_name() const
+{
+ return "CheckBoxUIObject";
+}
+
+OUString CheckBoxUIObject::get_action(VclEventId nEvent) const
+{
+ if (nEvent == VclEventId::CheckboxToggle)
+ {
+ if(get_top_parent(mxCheckBox)->get_id().isEmpty()){
+ //This part because if we don't have parent
+ return "Toggle '" + mxCheckBox->get_id() + "' CheckBox";
+ }
+ return "Toggle '" + mxCheckBox->get_id() + "' CheckBox from " +
+ get_top_parent(mxCheckBox)->get_id();
+ }
+ else
+ return WindowUIObject::get_action(nEvent);
+}
+
+std::unique_ptr<UIObject> CheckBoxUIObject::create(vcl::Window* pWindow)
+{
+ CheckBox* pCheckBox = dynamic_cast<CheckBox*>(pWindow);
+ assert(pCheckBox);
+ return std::unique_ptr<UIObject>(new CheckBoxUIObject(pCheckBox));
+}
+
+RadioButtonUIObject::RadioButtonUIObject(const VclPtr<RadioButton>& xRadioButton):
+ WindowUIObject(xRadioButton),
+ mxRadioButton(xRadioButton)
+{
+}
+
+RadioButtonUIObject::~RadioButtonUIObject()
+{
+}
+
+void RadioButtonUIObject::execute(const OUString& rAction,
+ const StringMap& rParameters)
+{
+ if (rAction == "CLICK")
+ {
+ mxRadioButton->ImplCallClick();
+ }
+ else
+ WindowUIObject::execute(rAction, rParameters);
+}
+
+StringMap RadioButtonUIObject::get_state()
+{
+ StringMap aMap = WindowUIObject::get_state();
+ aMap["Checked"] = OUString::boolean(mxRadioButton->IsChecked());
+
+ return aMap;
+}
+
+OUString RadioButtonUIObject::get_name() const
+{
+ return "RadioButtonUIObject";
+}
+
+OUString RadioButtonUIObject::get_action(VclEventId nEvent) const
+{
+ if (nEvent == VclEventId::RadiobuttonToggle)
+ {
+ if(get_top_parent(mxRadioButton)->get_id().isEmpty()){
+ //This part because if we don't have parent
+ return "Select '" + mxRadioButton->get_id() + "' RadioButton";
+ }
+ return "Select '" + mxRadioButton->get_id() + "' RadioButton from " +
+ get_top_parent(mxRadioButton)->get_id();
+ }
+ else
+ return WindowUIObject::get_action(nEvent);
+}
+
+std::unique_ptr<UIObject> RadioButtonUIObject::create(vcl::Window* pWindow)
+{
+ RadioButton* pRadioButton = dynamic_cast<RadioButton*>(pWindow);
+ assert(pRadioButton);
+ return std::unique_ptr<UIObject>(new RadioButtonUIObject(pRadioButton));
+}
+
+TabPageUIObject::TabPageUIObject(const VclPtr<TabPage>& xTabPage):
+ WindowUIObject(xTabPage),
+ mxTabPage(xTabPage)
+{
+}
+
+TabPageUIObject::~TabPageUIObject()
+{
+}
+
+void TabPageUIObject::execute(const OUString& rAction,
+ const StringMap& rParameters)
+{
+ WindowUIObject::execute(rAction, rParameters);
+}
+
+StringMap TabPageUIObject::get_state()
+{
+ StringMap aMap = WindowUIObject::get_state();
+
+ return aMap;
+}
+
+OUString TabPageUIObject::get_name() const
+{
+ return "TabPageUIObject";
+}
+
+ListBoxUIObject::ListBoxUIObject(const VclPtr<ListBox>& xListBox):
+ WindowUIObject(xListBox),
+ mxListBox(xListBox)
+{
+}
+
+ListBoxUIObject::~ListBoxUIObject()
+{
+}
+
+void ListBoxUIObject::execute(const OUString& rAction,
+ const StringMap& rParameters)
+{
+ if (!mxListBox->IsEnabled())
+ return;
+
+ bool isTiledRendering = comphelper::LibreOfficeKit::isActive();
+ if (!isTiledRendering && !mxListBox->IsReallyVisible())
+ return;
+
+ if (rAction == "SELECT")
+ {
+ bool bSelect = true;
+ if (rParameters.find("POS") != rParameters.end())
+ {
+ auto itr = rParameters.find("POS");
+ OUString aVal = itr->second;
+ sal_Int32 nPos = aVal.toInt32();
+ mxListBox->SelectEntryPos(nPos, bSelect);
+ }
+ else if (rParameters.find("TEXT") != rParameters.end())
+ {
+ auto itr = rParameters.find("TEXT");
+ OUString aText = itr->second;
+ mxListBox->SelectEntry(aText, bSelect);
+ }
+ mxListBox->Select();
+ }
+ else
+ WindowUIObject::execute(rAction, rParameters);
+}
+
+StringMap ListBoxUIObject::get_state()
+{
+ StringMap aMap = WindowUIObject::get_state();
+ aMap["ReadOnly"] = OUString::boolean(mxListBox->IsReadOnly());
+ aMap["MultiSelect"] = OUString::boolean(mxListBox->IsMultiSelectionEnabled());
+ aMap["EntryCount"] = OUString::number(mxListBox->GetEntryCount());
+ aMap["SelectEntryCount"] = OUString::number(mxListBox->GetSelectedEntryCount());
+ aMap["SelectEntryPos"] = OUString::number(mxListBox->GetSelectedEntryPos());
+ aMap["SelectEntryText"] = mxListBox->GetSelectedEntry();
+
+ return aMap;
+}
+
+OUString ListBoxUIObject::get_name() const
+{
+ return "ListBoxUIObject";
+}
+
+OUString ListBoxUIObject::get_action(VclEventId nEvent) const
+{
+ if (nEvent == VclEventId::ListboxSelect)
+ {
+ sal_Int32 nPos = mxListBox->GetSelectedEntryPos();
+ if(get_top_parent(mxListBox)->get_id().isEmpty()){
+ //This part because if we don't have parent
+ return "Select element with position " + OUString::number(nPos) +
+ " in '" + mxListBox->get_id();
+ }
+ return "Select element with position " + OUString::number(nPos) +
+ " in '" + mxListBox->get_id() +"' from" + get_top_parent(mxListBox)->get_id() ;
+ }
+ else if (nEvent == VclEventId::ListboxFocus)
+ {
+ if(get_top_parent(mxListBox)->get_id().isEmpty())
+ {
+ //This part because if we don't have parent
+ return this->get_type() + " Action:FOCUS Id:" + mxListBox->get_id();
+ }
+ return this->get_type() + " Action:FOCUS Id:" + mxListBox->get_id() +
+ " Parent:" + get_top_parent(mxListBox)->get_id();
+ }
+ else
+ return WindowUIObject::get_action(nEvent);
+}
+
+std::unique_ptr<UIObject> ListBoxUIObject::create(vcl::Window* pWindow)
+{
+ ListBox* pListBox = dynamic_cast<ListBox*>(pWindow);
+ assert(pListBox);
+ return std::unique_ptr<UIObject>(new ListBoxUIObject(pListBox));
+}
+
+ComboBoxUIObject::ComboBoxUIObject(const VclPtr<ComboBox>& xComboBox):
+ WindowUIObject(xComboBox),
+ mxComboBox(xComboBox)
+{
+}
+
+ComboBoxUIObject::~ComboBoxUIObject()
+{
+}
+
+void ComboBoxUIObject::execute(const OUString& rAction,
+ const StringMap& rParameters)
+{
+ if (rAction == "SELECT")
+ {
+ if (rParameters.find("POS") != rParameters.end())
+ {
+ auto itr = rParameters.find("POS");
+ OUString aVal = itr->second;
+ sal_Int32 nPos = aVal.toInt32();
+ mxComboBox->SelectEntryPos(nPos);
+ }
+ else if(rParameters.find("TEXT") != rParameters.end()){
+ auto itr = rParameters.find("TEXT");
+ OUString aVal = itr->second;
+ sal_Int32 nPos = mxComboBox->GetEntryPos(aVal);
+ mxComboBox->SelectEntryPos(nPos);
+ }
+ mxComboBox->Select();
+ }
+ else if ( rAction == "TYPE" || rAction == "SET" || rAction == "CLEAR" ){
+ if (mxComboBox->GetSubEdit())
+ {
+ Edit* pEdit = mxComboBox->GetSubEdit();
+ std::unique_ptr<UIObject> pObj = EditUIObject::create(pEdit);
+ pObj->execute(rAction, rParameters);
+ }
+ else
+ WindowUIObject::execute(rAction, rParameters);
+ }
+ else
+ WindowUIObject::execute(rAction, rParameters);
+}
+
+StringMap ComboBoxUIObject::get_state()
+{
+ StringMap aMap = WindowUIObject::get_state();
+ aMap["SelectedText"] = mxComboBox->GetSelected();
+ return aMap;
+}
+
+OUString ComboBoxUIObject::get_name() const
+{
+ return "ComboBoxUIObject";
+}
+
+OUString ComboBoxUIObject::get_action(VclEventId nEvent) const
+{
+ if (nEvent == VclEventId::ComboboxSelect)
+ {
+ sal_Int32 nPos = mxComboBox->GetSelectedEntryPos();
+ if (get_top_parent(mxComboBox)->get_id().isEmpty()){
+ //This part because if we don't have parent
+ return "Select in '" + mxComboBox->get_id() +
+ "' ComboBox item number " + OUString::number(nPos);
+ }
+ return "Select in '" + mxComboBox->get_id() +
+ "' ComboBox item number " + OUString::number(nPos) +
+ " from " + get_top_parent(mxComboBox)->get_id();
+ }
+ else
+ return WindowUIObject::get_action(nEvent);
+}
+
+std::unique_ptr<UIObject> ComboBoxUIObject::create(vcl::Window* pWindow)
+{
+ ComboBox* pComboBox = dynamic_cast<ComboBox*>(pWindow);
+ assert(pComboBox);
+ return std::unique_ptr<UIObject>(new ComboBoxUIObject(pComboBox));
+}
+
+SpinUIObject::SpinUIObject(const VclPtr<SpinButton>& xSpinButton):
+ WindowUIObject(xSpinButton),
+ mxSpinButton(xSpinButton)
+{
+}
+
+SpinUIObject::~SpinUIObject()
+{
+}
+
+void SpinUIObject::execute(const OUString& rAction,
+ const StringMap& rParameters)
+{
+ if (rAction == "UP")
+ {
+ mxSpinButton->Up();
+ }
+ else if (rAction == "DOWN")
+ {
+ mxSpinButton->Down();
+ }
+ else
+ WindowUIObject::execute(rAction, rParameters);
+}
+
+StringMap SpinUIObject::get_state()
+{
+ StringMap aMap = WindowUIObject::get_state();
+ aMap["Min"] = OUString::number(mxSpinButton->GetRangeMin());
+ aMap["Max"] = OUString::number(mxSpinButton->GetRangeMax());
+ aMap["Step"] = OUString::number(mxSpinButton->GetValueStep());
+ aMap["Value"] = OUString::number(mxSpinButton->GetValue());
+
+ return aMap;
+}
+
+OUString SpinUIObject::get_action(VclEventId nEvent) const
+{
+ if (nEvent == VclEventId::SpinbuttonUp)
+ {
+ return this->get_type() + " Action:UP Id:" + mxSpinButton->get_id() +
+ " Parent:" + get_top_parent(mxSpinButton)->get_id();
+ }
+ else if (nEvent == VclEventId::SpinbuttonDown)
+ {
+ return this->get_type() + " Action:DOWN Id:" + mxSpinButton->get_id() +
+ " Parent:" + get_top_parent(mxSpinButton)->get_id();
+ }
+ else
+ return WindowUIObject::get_action(nEvent);
+}
+
+OUString SpinUIObject::get_name() const
+{
+ return "SpinUIObject";
+}
+
+SpinFieldUIObject::SpinFieldUIObject(const VclPtr<SpinField>& xSpinField):
+ EditUIObject(xSpinField),
+ mxSpinField(xSpinField)
+{
+}
+
+SpinFieldUIObject::~SpinFieldUIObject()
+{
+}
+
+void SpinFieldUIObject::execute(const OUString& rAction,
+ const StringMap& rParameters)
+{
+ if (rAction == "UP")
+ {
+ mxSpinField->Up();
+ }
+ else if (rAction == "DOWN")
+ {
+ mxSpinField->Down();
+ }
+ else if (rAction == "TYPE")
+ {
+ if (mxSpinField->GetSubEdit())
+ {
+ Edit* pSubEdit = mxSpinField->GetSubEdit();
+ EditUIObject aSubObject(pSubEdit);
+ aSubObject.execute(rAction, rParameters);
+ }
+ }
+ else
+ EditUIObject::execute(rAction, rParameters);
+}
+
+StringMap SpinFieldUIObject::get_state()
+{
+ StringMap aMap = EditUIObject::get_state();
+
+ return aMap;
+}
+
+OUString SpinFieldUIObject::get_action(VclEventId nEvent) const
+{
+ if (nEvent == VclEventId::SpinfieldUp)
+ {
+ if(get_top_parent(mxSpinField)->get_id().isEmpty())
+ {
+ //This part because if we don't have parent
+ return "Increase '" + mxSpinField->get_id();
+ }
+ return "Increase '" + mxSpinField->get_id() +
+ "' from " + get_top_parent(mxSpinField)->get_id();
+ }
+ else if (nEvent == VclEventId::SpinfieldDown)
+ {
+ if(get_top_parent(mxSpinField)->get_id().isEmpty())
+ {
+ //This part because if we don't have parent
+ return "Decrease '" + mxSpinField->get_id();
+ }
+ return "Decrease '" + mxSpinField->get_id() +
+ "' from " + get_top_parent(mxSpinField)->get_id();
+ }
+ else
+ return WindowUIObject::get_action(nEvent);
+}
+
+OUString SpinFieldUIObject::get_name() const
+{
+ return "SpinFieldUIObject";
+}
+
+std::unique_ptr<UIObject> SpinFieldUIObject::create(vcl::Window* pWindow)
+{
+ SpinField* pSpinField = dynamic_cast<SpinField*>(pWindow);
+ assert(pSpinField);
+ return std::unique_ptr<UIObject>(new SpinFieldUIObject(pSpinField));
+}
+
+
+MetricFieldUIObject::MetricFieldUIObject(const VclPtr<MetricField>& xMetricField):
+ SpinFieldUIObject(xMetricField),
+ mxMetricField(xMetricField)
+{
+}
+
+MetricFieldUIObject::~MetricFieldUIObject()
+{
+}
+
+void MetricFieldUIObject::execute(const OUString& rAction,
+ const StringMap& rParameters)
+{
+ if (rAction == "VALUE")
+ {
+ auto itPos = rParameters.find("VALUE");
+ if (itPos != rParameters.end())
+ {
+ mxMetricField->SetValueFromString(itPos->second);
+ }
+ }
+ else
+ SpinFieldUIObject::execute(rAction, rParameters);
+}
+
+StringMap MetricFieldUIObject::get_state()
+{
+ StringMap aMap = EditUIObject::get_state();
+ aMap["Value"] = mxMetricField->GetValueString();
+
+ return aMap;
+}
+
+OUString MetricFieldUIObject::get_name() const
+{
+ return "MetricFieldUIObject";
+}
+
+std::unique_ptr<UIObject> MetricFieldUIObject::create(vcl::Window* pWindow)
+{
+ MetricField* pMetricField = dynamic_cast<MetricField*>(pWindow);
+ assert(pMetricField);
+ return std::unique_ptr<UIObject>(new MetricFieldUIObject(pMetricField));
+}
+
+FormattedFieldUIObject::FormattedFieldUIObject(const VclPtr<FormattedField>& xFormattedField):
+ SpinFieldUIObject(xFormattedField),
+ mxFormattedField(xFormattedField)
+{
+}
+
+FormattedFieldUIObject::~FormattedFieldUIObject()
+{
+}
+
+void FormattedFieldUIObject::execute(const OUString& rAction,
+ const StringMap& rParameters)
+{
+ if (rAction == "VALUE")
+ {
+ auto itPos = rParameters.find("VALUE");
+ if (itPos != rParameters.end())
+ {
+ mxFormattedField->SetValueFromString(itPos->second);
+ }
+ }
+ else
+ SpinFieldUIObject::execute(rAction, rParameters);
+}
+
+StringMap FormattedFieldUIObject::get_state()
+{
+ StringMap aMap = EditUIObject::get_state();
+ aMap["Value"] = OUString::number(mxFormattedField->GetFormatter().GetValue());
+
+ return aMap;
+}
+
+OUString FormattedFieldUIObject::get_name() const
+{
+ return "FormattedFieldUIObject";
+}
+
+std::unique_ptr<UIObject> FormattedFieldUIObject::create(vcl::Window* pWindow)
+{
+ FormattedField* pFormattedField = dynamic_cast<FormattedField*>(pWindow);
+ assert(pFormattedField);
+ return std::unique_ptr<UIObject>(new FormattedFieldUIObject(pFormattedField));
+}
+
+TabControlUIObject::TabControlUIObject(const VclPtr<TabControl>& xTabControl):
+ WindowUIObject(xTabControl),
+ mxTabControl(xTabControl)
+{
+}
+
+TabControlUIObject::~TabControlUIObject()
+{
+}
+
+void TabControlUIObject::execute(const OUString& rAction,
+ const StringMap& rParameters)
+{
+ if (rAction == "SELECT")
+ {
+ if (rParameters.find("POS") != rParameters.end())
+ {
+ auto itr = rParameters.find("POS");
+ sal_uInt32 nPos = itr->second.toUInt32();
+ std::vector<sal_uInt16> aIds = mxTabControl->GetPageIDs();
+ mxTabControl->SelectTabPage(aIds[nPos]);
+ }
+ }
+ else
+ WindowUIObject::execute(rAction, rParameters);
+}
+
+StringMap TabControlUIObject::get_state()
+{
+ StringMap aMap = WindowUIObject::get_state();
+ aMap["PageCount"] = OUString::number(mxTabControl->GetPageCount());
+
+ sal_uInt16 nPageId = mxTabControl->GetCurPageId();
+ aMap["CurrPageId"] = OUString::number(nPageId);
+ aMap["CurrPagePos"] = OUString::number(mxTabControl->GetPagePos(nPageId));
+
+ return aMap;
+}
+
+OUString TabControlUIObject::get_action(VclEventId nEvent) const
+{
+ if (nEvent == VclEventId::TabpageActivate)
+ {
+ sal_Int32 nPageId = mxTabControl->GetCurPageId();
+
+ if(get_top_parent(mxTabControl)->get_id().isEmpty()){
+ //This part because if we don't have parent
+ return "Choose Tab number " + OUString::number(mxTabControl->GetPagePos(nPageId)) +
+ " in '" + mxTabControl->get_id();
+ }
+ return "Choose Tab number " + OUString::number(mxTabControl->GetPagePos(nPageId)) +
+ " in '" + mxTabControl->get_id()+
+ "' from " + get_top_parent(mxTabControl)->get_id() ;
+ }
+ else
+ return WindowUIObject::get_action(nEvent);
+}
+
+OUString TabControlUIObject::get_name() const
+{
+ return "TabControlUIObject";
+}
+
+std::unique_ptr<UIObject> TabControlUIObject::create(vcl::Window* pWindow)
+{
+ TabControl* pTabControl = dynamic_cast<TabControl*>(pWindow);
+ assert(pTabControl);
+ return std::unique_ptr<UIObject>(new TabControlUIObject(pTabControl));
+}
+
+RoadmapWizardUIObject::RoadmapWizardUIObject(const VclPtr<vcl::RoadmapWizard>& xRoadmapWizard):
+ WindowUIObject(xRoadmapWizard),
+ mxRoadmapWizard(xRoadmapWizard)
+{
+}
+
+RoadmapWizardUIObject::~RoadmapWizardUIObject()
+{
+}
+
+void RoadmapWizardUIObject::execute(const OUString& rAction,
+ const StringMap& rParameters)
+{
+ if (rAction == "SELECT")
+ {
+ if (rParameters.find("POS") != rParameters.end())
+ {
+ auto itr = rParameters.find("POS");
+ sal_uInt32 nPos = itr->second.toUInt32();
+ mxRoadmapWizard->SelectRoadmapItemByID(nPos);
+ }
+ }
+ else
+ WindowUIObject::execute(rAction, rParameters);
+}
+
+StringMap RoadmapWizardUIObject::get_state()
+{
+ StringMap aMap = WindowUIObject::get_state();
+
+ aMap["CurrentStep"] = OUString::number(mxRoadmapWizard->GetCurrentRoadmapItemID());
+
+ return aMap;
+}
+
+OUString RoadmapWizardUIObject::get_name() const
+{
+ return "RoadmapWizardUIObject";
+}
+
+std::unique_ptr<UIObject> RoadmapWizardUIObject::create(vcl::Window* pWindow)
+{
+ vcl::RoadmapWizard* pRoadmapWizard = dynamic_cast<vcl::RoadmapWizard*>(pWindow);
+ assert(pRoadmapWizard);
+ return std::unique_ptr<UIObject>(new RoadmapWizardUIObject(pRoadmapWizard));
+}
+
+VerticalTabControlUIObject::VerticalTabControlUIObject(const VclPtr<VerticalTabControl>& xTabControl):
+ WindowUIObject(xTabControl),
+ mxTabControl(xTabControl)
+{
+}
+
+VerticalTabControlUIObject::~VerticalTabControlUIObject()
+{
+}
+
+void VerticalTabControlUIObject::execute(const OUString& rAction,
+ const StringMap& rParameters)
+{
+ if (rAction == "SELECT")
+ {
+ if (rParameters.find("POS") != rParameters.end())
+ {
+ auto itr = rParameters.find("POS");
+ sal_uInt32 nPos = itr->second.toUInt32();
+ OString xid = mxTabControl->GetPageId(nPos);
+ mxTabControl->SetCurPageId(xid);
+ }
+ }
+ else
+ WindowUIObject::execute(rAction, rParameters);
+}
+
+StringMap VerticalTabControlUIObject::get_state()
+{
+ StringMap aMap = WindowUIObject::get_state();
+ aMap["PageCount"] = OUString::number(mxTabControl->GetPageCount());
+
+ OString nPageId = mxTabControl->GetCurPageId();
+ aMap["CurrPageTitel"] = mxTabControl->GetPageText(nPageId);
+ aMap["CurrPagePos"] = OUString::number(mxTabControl->GetPagePos(nPageId));
+
+ return aMap;
+}
+
+OUString VerticalTabControlUIObject::get_name() const
+{
+ return "VerticalTabControlUIObject";
+}
+
+std::unique_ptr<UIObject> VerticalTabControlUIObject::create(vcl::Window* pWindow)
+{
+ VerticalTabControl* pTabControl = dynamic_cast<VerticalTabControl*>(pWindow);
+ assert(pTabControl);
+ return std::unique_ptr<UIObject>(new VerticalTabControlUIObject(pTabControl));
+}
+
+
+ToolBoxUIObject::ToolBoxUIObject(const VclPtr<ToolBox>& xToolBox):
+ WindowUIObject(xToolBox),
+ mxToolBox(xToolBox)
+{
+}
+
+ToolBoxUIObject::~ToolBoxUIObject()
+{
+}
+
+void ToolBoxUIObject::execute(const OUString& rAction,
+ const StringMap& rParameters)
+{
+ if (rAction == "CLICK")
+ {
+ if (rParameters.find("POS") != rParameters.end())
+ {
+ auto itr = rParameters.find("POS");
+ sal_uInt16 nPos = itr->second.toUInt32();
+ mxToolBox->SetCurItemId(mxToolBox->GetItemId(nPos));
+ mxToolBox->Click();
+ mxToolBox->Select();
+ }
+ }
+ else
+ WindowUIObject::execute(rAction, rParameters);
+}
+
+OUString ToolBoxUIObject::get_action(VclEventId nEvent) const
+{
+ if (nEvent == VclEventId::ToolboxClick)
+ {
+ return "Click on item number " + OUString::number(sal_uInt16(mxToolBox->GetCurItemId())) +
+ " in " + mxToolBox->get_id();
+ }
+ else
+ return WindowUIObject::get_action(nEvent);
+}
+
+StringMap ToolBoxUIObject::get_state()
+{
+ StringMap aMap = WindowUIObject::get_state();
+ aMap["CurrSelectedItemID"] = OUString::number(sal_uInt16(mxToolBox->GetCurItemId()));
+ aMap["CurrSelectedItemText"] = mxToolBox->GetItemText(mxToolBox->GetCurItemId());
+ aMap["CurrSelectedItemCommand"] = mxToolBox->GetItemCommand(mxToolBox->GetCurItemId());
+ aMap["ItemCount"] = OUString::number(mxToolBox->GetItemCount());
+ return aMap;
+}
+
+OUString ToolBoxUIObject::get_name() const
+{
+ return "ToolBoxUIObject";
+}
+
+std::unique_ptr<UIObject> ToolBoxUIObject::create(vcl::Window* pWindow)
+{
+ ToolBox* pToolBox = dynamic_cast<ToolBox*>(pWindow);
+ assert(pToolBox);
+ return std::unique_ptr<UIObject>(new ToolBoxUIObject(pToolBox));
+}
+
+MenuButtonUIObject::MenuButtonUIObject(const VclPtr<MenuButton>& xMenuButton):
+ WindowUIObject(xMenuButton),
+ mxMenuButton(xMenuButton)
+{
+}
+
+MenuButtonUIObject::~MenuButtonUIObject()
+{
+}
+
+StringMap MenuButtonUIObject::get_state()
+{
+ StringMap aMap = WindowUIObject::get_state();
+ aMap["Label"] = mxMenuButton->GetDisplayText();
+ aMap["CurrentItem"] = OUString::createFromAscii(mxMenuButton->GetCurItemIdent());
+ return aMap;
+}
+
+void MenuButtonUIObject::execute(const OUString& rAction,
+ const StringMap& rParameters)
+{
+ if (rAction == "CLICK")
+ {
+ mxMenuButton->Check(!mxMenuButton->IsChecked());
+ mxMenuButton->Toggle();
+ }
+ else if (rAction == "OPENLIST")
+ {
+ mxMenuButton->ExecuteMenu();
+ }
+ else if (rAction == "OPENFROMLIST")
+ {
+ auto itr = rParameters.find("POS");
+ sal_uInt32 nPos = itr->second.toUInt32();
+
+ sal_uInt32 nId = mxMenuButton->GetPopupMenu()->GetItemId(nPos);
+ mxMenuButton->GetPopupMenu()->SetSelectedEntry(nId);
+ mxMenuButton->SetCurItemId();
+ mxMenuButton->Select();
+ }
+ else if (rAction == "CLOSELIST")
+ {
+ mxMenuButton->GetPopupMenu()->EndExecute();
+ }
+ else
+ WindowUIObject::execute(rAction, rParameters);
+}
+
+OUString MenuButtonUIObject::get_name() const
+{
+ return "MenuButtonUIObject";
+}
+
+std::unique_ptr<UIObject> MenuButtonUIObject::create(vcl::Window* pWindow)
+{
+ MenuButton* pMenuButton = dynamic_cast<MenuButton*>(pWindow);
+ assert(pMenuButton);
+ return std::unique_ptr<UIObject>(new MenuButtonUIObject(pMenuButton));
+}
+
+DrawingAreaUIObject::DrawingAreaUIObject(const VclPtr<vcl::Window>& rDrawingArea)
+ : WindowUIObject(rDrawingArea)
+ , mxDrawingArea(dynamic_cast<VclDrawingArea*>(rDrawingArea.get()))
+{
+ assert(mxDrawingArea);
+ mpController = static_cast<weld::CustomWidgetController*>(mxDrawingArea->GetUserData());
+}
+
+DrawingAreaUIObject::~DrawingAreaUIObject()
+{
+}
+
+void DrawingAreaUIObject::execute(const OUString& rAction, const StringMap& rParameters)
+{
+ if (rAction == "CLICK")
+ {
+ // POSX and POSY are percentage of width/height dimensions
+ if (rParameters.find("POSX") != rParameters.end() &&
+ rParameters.find("POSY") != rParameters.end())
+ {
+ auto aPosX = rParameters.find("POSX");
+ auto aPosY = rParameters.find("POSY");
+
+ OString sPosX2 = OUStringToOString(aPosX->second, RTL_TEXTENCODING_ASCII_US);
+ OString sPoxY2 = OUStringToOString(aPosY->second, RTL_TEXTENCODING_ASCII_US);
+
+ if (!sPosX2.isEmpty() && !sPoxY2.isEmpty())
+ {
+ double fPosX = std::atof(sPosX2.getStr());
+ double fPosY = std::atof(sPoxY2.getStr());
+
+ fPosX = fPosX * mxDrawingArea->GetOutputSizePixel().Width();
+ fPosY = fPosY * mxDrawingArea->GetOutputSizePixel().Height();
+
+ MouseEvent aEvent(Point(fPosX, fPosY), 1, MouseEventModifiers::NONE, MOUSE_LEFT, 0);
+ mxDrawingArea->MouseButtonDown(aEvent);
+ mxDrawingArea->MouseButtonUp(aEvent);
+ }
+ }
+ }
+ else
+ WindowUIObject::execute(rAction, rParameters);
+}
+
+std::unique_ptr<UIObject> DrawingAreaUIObject::create(vcl::Window* pWindow)
+{
+ VclDrawingArea* pVclDrawingArea = dynamic_cast<VclDrawingArea*>(pWindow);
+ assert(pVclDrawingArea);
+ return std::unique_ptr<UIObject>(new DrawingAreaUIObject(pVclDrawingArea));
+}
+
+IconViewUIObject::IconViewUIObject(const VclPtr<SvTreeListBox>& xIconView):
+ TreeListUIObject(xIconView)
+{
+}
+
+StringMap IconViewUIObject::get_state()
+{
+ StringMap aMap = TreeListUIObject::get_state();
+
+ SvTreeListEntry* pEntry = mxTreeList->FirstSelected();
+
+ OUString* pId = static_cast<OUString*>(pEntry->GetUserData());
+ if (pId)
+ aMap["SelectedItemId"] = *pId;
+
+ SvTreeList* pModel = mxTreeList->GetModel();
+ if (pModel)
+ aMap["SelectedItemPos"] = OUString::number(pModel->GetAbsPos(pEntry));
+
+ return aMap;
+}
+
+OUString IconViewUIObject::get_name() const
+{
+ return "IconViewUIObject";
+}
+
+std::unique_ptr<UIObject> IconViewUIObject::create(vcl::Window* pWindow)
+{
+ SvTreeListBox* pTreeList = dynamic_cast<SvTreeListBox*>(pWindow);
+ assert(pTreeList);
+ return std::unique_ptr<UIObject>(new IconViewUIObject(pTreeList));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/uitest/uitest.cxx b/vcl/source/uitest/uitest.cxx
new file mode 100644
index 000000000..f52e636f5
--- /dev/null
+++ b/vcl/source/uitest/uitest.cxx
@@ -0,0 +1,80 @@
+/* -*- 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 <memory>
+#include <vcl/toolkit/floatwin.hxx>
+#include <vcl/uitest/uitest.hxx>
+#include <vcl/uitest/uiobject.hxx>
+
+#include <vcl/toolkit/dialog.hxx>
+
+#include <svdata.hxx>
+
+#include <comphelper/dispatchcommand.hxx>
+#include <com/sun/star/beans/PropertyValue.hpp>
+
+bool UITest::executeCommand(const OUString& rCommand)
+{
+ return comphelper::dispatchCommand(
+ rCommand,
+ {{"SynchronMode", -1, css::uno::Any(true),
+ css::beans::PropertyState_DIRECT_VALUE}});
+}
+
+bool UITest::executeCommandWithParameters(const OUString& rCommand,
+ const css::uno::Sequence< css::beans::PropertyValue >& rArgs)
+{
+ css::uno::Sequence< css::beans::PropertyValue > lNewArgs =
+ {{"SynchronMode", -1, css::uno::Any(true),
+ css::beans::PropertyState_DIRECT_VALUE}};
+
+ if ( rArgs.hasElements() )
+ {
+ sal_uInt32 nIndex( lNewArgs.getLength() );
+ lNewArgs.realloc( lNewArgs.getLength()+rArgs.getLength() );
+
+ std::copy(rArgs.begin(), rArgs.end(), std::next(lNewArgs.getArray(), nIndex));
+ }
+ return comphelper::dispatchCommand(rCommand,lNewArgs);
+}
+
+bool UITest::executeDialog(const OUString& rCommand)
+{
+ return comphelper::dispatchCommand(
+ rCommand,
+ {{"SynchronMode", -1, css::uno::Any(false),
+ css::beans::PropertyState_DIRECT_VALUE}});
+}
+
+std::unique_ptr<UIObject> UITest::getFocusTopWindow()
+{
+ ImplSVData* pSVData = ImplGetSVData();
+ ImplSVWinData& rWinData = *pSVData->mpWinData;
+
+ if (!rWinData.mpExecuteDialogs.empty())
+ {
+ return rWinData.mpExecuteDialogs.back()->GetUITestFactory()(rWinData.mpExecuteDialogs.back());
+ }
+
+ return pSVData->maFrameData.mpFirstFrame->GetUITestFactory()(pSVData->maFrameData.mpFirstFrame);
+}
+
+std::unique_ptr<UIObject> UITest::getFloatWindow()
+{
+ ImplSVData* pSVData = ImplGetSVData();
+ ImplSVWinData& rWinData = *pSVData->mpWinData;
+
+ VclPtr<vcl::Window> pFloatWin = rWinData.mpFirstFloat;
+ if (pFloatWin)
+ return pFloatWin->GetUITestFactory()(pFloatWin);
+
+ return nullptr;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/uitest/uno/uiobject_uno.cxx b/vcl/source/uitest/uno/uiobject_uno.cxx
new file mode 100644
index 000000000..042b2fcf5
--- /dev/null
+++ b/vcl/source/uitest/uno/uiobject_uno.cxx
@@ -0,0 +1,224 @@
+/* -*- 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 <algorithm>
+#include <atomic>
+#include <condition_variable>
+#include <memory>
+#include <mutex>
+#include "uiobject_uno.hxx"
+#include <utility>
+#include <comphelper/propertyvalue.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <tools/link.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/idle.hxx>
+#include <vcl/window.hxx>
+
+#include <set>
+
+class Timer;
+
+namespace {
+
+struct Notifier {
+ std::condition_variable cv;
+ std::mutex mMutex;
+ bool mReady = false;
+
+ DECL_LINK( NotifyHdl, Timer*, void );
+};
+
+}
+
+UIObjectUnoObj::UIObjectUnoObj(std::unique_ptr<UIObject> pObj):
+ UIObjectBase(m_aMutex),
+ mpObj(std::move(pObj))
+{
+}
+
+UIObjectUnoObj::~UIObjectUnoObj()
+{
+ SolarMutexGuard aGuard;
+ mpObj.reset();
+}
+
+css::uno::Reference<css::ui::test::XUIObject> SAL_CALL UIObjectUnoObj::getChild(const OUString& rID)
+{
+ if (!mpObj)
+ throw css::uno::RuntimeException();
+
+ SolarMutexGuard aGuard;
+ std::unique_ptr<UIObject> pObj = mpObj->get_child(rID);
+ return new UIObjectUnoObj(std::move(pObj));
+}
+
+IMPL_LINK_NOARG(Notifier, NotifyHdl, Timer*, void)
+{
+ std::scoped_lock<std::mutex> lk(mMutex);
+ mReady = true;
+ cv.notify_all();
+}
+
+namespace {
+
+class ExecuteWrapper
+{
+ std::function<void()> mFunc;
+ Link<Timer*, void> mHandler;
+ std::atomic<bool> mbSignal;
+
+public:
+
+ ExecuteWrapper(std::function<void()> func, Link<Timer*, void> handler):
+ mFunc(std::move(func)),
+ mHandler(handler),
+ mbSignal(false)
+ {
+ }
+
+ void setSignal()
+ {
+ mbSignal = true;
+ }
+
+ DECL_LINK( ExecuteActionHdl, Timer*, void );
+};
+
+
+IMPL_LINK_NOARG(ExecuteWrapper, ExecuteActionHdl, Timer*, void)
+{
+ {
+ Idle aIdle("UI Test Idle Handler2");
+ {
+ mFunc();
+ aIdle.SetPriority(TaskPriority::LOWEST);
+ aIdle.SetInvokeHandler(mHandler);
+ aIdle.Start();
+ }
+
+ while (!mbSignal) {
+ Application::Reschedule();
+ }
+ }
+ delete this;
+}
+
+}
+
+void SAL_CALL UIObjectUnoObj::executeAction(const OUString& rAction, const css::uno::Sequence<css::beans::PropertyValue>& rPropValues)
+{
+ if (!mpObj)
+ throw css::uno::RuntimeException();
+
+ auto aIdle = std::make_unique<Idle>("UI Test Idle Handler");
+ aIdle->SetPriority(TaskPriority::HIGHEST);
+
+ std::function<void()> func = [&rAction, &rPropValues, this](){
+
+ SolarMutexGuard aGuard;
+ StringMap aMap;
+ for (const auto& rPropVal : rPropValues)
+ {
+ OUString aVal;
+ if (!(rPropVal.Value >>= aVal))
+ continue;
+
+ aMap[rPropVal.Name] = aVal;
+ }
+ mpObj->execute(rAction, aMap);
+ };
+
+ Notifier notifier;
+ ExecuteWrapper* pWrapper = new ExecuteWrapper(std::move(func), LINK(&notifier, Notifier, NotifyHdl));
+ aIdle->SetInvokeHandler(LINK(pWrapper, ExecuteWrapper, ExecuteActionHdl));
+ {
+ SolarMutexGuard aGuard;
+ aIdle->Start();
+ }
+
+ {
+ std::unique_lock<std::mutex> lk(notifier.mMutex);
+ notifier.cv.wait(lk, [&notifier]{return notifier.mReady;});
+ }
+ pWrapper->setSignal();
+
+ SolarMutexGuard aGuard;
+ aIdle.reset();
+}
+
+css::uno::Sequence<css::beans::PropertyValue> UIObjectUnoObj::getState()
+{
+ if (!mpObj)
+ throw css::uno::RuntimeException();
+
+ SolarMutexGuard aGuard;
+ StringMap aMap = mpObj->get_state();
+ css::uno::Sequence<css::beans::PropertyValue> aProps(aMap.size());
+ std::transform(aMap.begin(), aMap.end(), aProps.getArray(),
+ [](auto const& elem)
+ { return comphelper::makePropertyValue(elem.first, elem.second); });
+
+ return aProps;
+}
+
+css::uno::Sequence<OUString> UIObjectUnoObj::getChildren()
+{
+ if (!mpObj)
+ throw css::uno::RuntimeException();
+
+ std::set<OUString> aChildren;
+
+ {
+ SolarMutexGuard aGuard;
+ aChildren = mpObj->get_children();
+ }
+
+ css::uno::Sequence<OUString> aRet(aChildren.size());
+ std::copy(aChildren.begin(), aChildren.end(), aRet.getArray());
+
+ return aRet;
+}
+
+OUString SAL_CALL UIObjectUnoObj::getType()
+{
+ if (!mpObj)
+ throw css::uno::RuntimeException();
+
+ SolarMutexGuard aGuard;
+ return mpObj->get_type();
+}
+
+OUString SAL_CALL UIObjectUnoObj::getImplementationName()
+{
+ return "org.libreoffice.uitest.UIObject";
+}
+
+sal_Bool UIObjectUnoObj::supportsService(OUString const & ServiceName)
+{
+ return cppu::supportsService(this, ServiceName);
+}
+
+css::uno::Sequence<OUString> UIObjectUnoObj::getSupportedServiceNames()
+{
+ return { "com.sun.star.ui.test.UIObject" };
+}
+
+OUString SAL_CALL UIObjectUnoObj::getHierarchy()
+{
+ if (!mpObj)
+ throw css::uno::RuntimeException();
+
+ SolarMutexGuard aGuard;
+ return mpObj->dumpHierarchy();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/uitest/uno/uiobject_uno.hxx b/vcl/source/uitest/uno/uiobject_uno.hxx
new file mode 100644
index 000000000..47fc54696
--- /dev/null
+++ b/vcl/source/uitest/uno/uiobject_uno.hxx
@@ -0,0 +1,55 @@
+/* -*- 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 <cppuhelper/compbase.hxx>
+#include <cppuhelper/basemutex.hxx>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/ui/test/XUIObject.hpp>
+
+#include <memory>
+
+#include <vcl/uitest/uiobject.hxx>
+
+typedef ::cppu::WeakComponentImplHelper <
+ css::ui::test::XUIObject, css::lang::XServiceInfo
+ > UIObjectBase;
+
+class UIObjectUnoObj : public cppu::BaseMutex,
+ public UIObjectBase
+{
+private:
+ std::unique_ptr<UIObject> mpObj;
+
+public:
+
+ explicit UIObjectUnoObj(std::unique_ptr<UIObject> pObj);
+ virtual ~UIObjectUnoObj() override;
+
+ css::uno::Reference<css::ui::test::XUIObject> SAL_CALL getChild(const OUString& rID) override;
+
+ void SAL_CALL executeAction(const OUString& rAction, const css::uno::Sequence<css::beans::PropertyValue>& xPropValues) override;
+
+ css::uno::Sequence<css::beans::PropertyValue> SAL_CALL getState() override;
+
+ css::uno::Sequence<OUString> SAL_CALL getChildren() override;
+
+ OUString SAL_CALL getType() override;
+
+ OUString SAL_CALL getImplementationName() override;
+
+ sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override;
+
+ css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override;
+
+ OUString SAL_CALL getHierarchy() override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/uitest/uno/uitest_uno.cxx b/vcl/source/uitest/uno/uitest_uno.cxx
new file mode 100644
index 000000000..4e5a6e0e0
--- /dev/null
+++ b/vcl/source/uitest/uno/uitest_uno.cxx
@@ -0,0 +1,121 @@
+/* -*- 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 <cppuhelper/compbase.hxx>
+#include <cppuhelper/basemutex.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <com/sun/star/ui/test/XUITest.hpp>
+
+#include <memory>
+
+#include <vcl/uitest/uitest.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/window.hxx>
+
+#include "uiobject_uno.hxx"
+
+namespace
+{
+ typedef ::cppu::WeakComponentImplHelper <
+ css::ui::test::XUITest, css::lang::XServiceInfo
+ > UITestBase;
+
+class UITestUnoObj : public cppu::BaseMutex,
+ public UITestBase
+{
+private:
+ std::unique_ptr<UITest> mpUITest;
+
+public:
+
+ UITestUnoObj();
+
+ sal_Bool SAL_CALL executeCommand(const OUString& rCommand) override;
+
+ sal_Bool SAL_CALL executeCommandWithParameters(const OUString& rCommand,
+ const css::uno::Sequence< css::beans::PropertyValue >& rArgs) override;
+
+ sal_Bool SAL_CALL executeDialog(const OUString& rCommand) override;
+
+ css::uno::Reference<css::ui::test::XUIObject> SAL_CALL getTopFocusWindow() override;
+
+ css::uno::Reference<css::ui::test::XUIObject> SAL_CALL getFloatWindow() override;
+
+ OUString SAL_CALL getImplementationName() override;
+
+ sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override;
+
+ css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override;
+};
+
+}
+
+UITestUnoObj::UITestUnoObj():
+ UITestBase(m_aMutex),
+ mpUITest(new UITest)
+{
+}
+
+sal_Bool SAL_CALL UITestUnoObj::executeCommand(const OUString& rCommand)
+{
+ SolarMutexGuard aGuard;
+ return UITest::executeCommand(rCommand);
+}
+
+sal_Bool SAL_CALL UITestUnoObj::executeCommandWithParameters(const OUString& rCommand,
+ const css::uno::Sequence< css::beans::PropertyValue >& rArgs)
+{
+ SolarMutexGuard aGuard;
+ return UITest::executeCommandWithParameters(rCommand,rArgs);
+}
+
+sal_Bool SAL_CALL UITestUnoObj::executeDialog(const OUString& rCommand)
+{
+ SolarMutexGuard aGuard;
+ return UITest::executeDialog(rCommand);
+}
+
+css::uno::Reference<css::ui::test::XUIObject> SAL_CALL UITestUnoObj::getTopFocusWindow()
+{
+ SolarMutexGuard aGuard;
+ std::unique_ptr<UIObject> pObj = UITest::getFocusTopWindow();
+ return new UIObjectUnoObj(std::move(pObj));
+}
+
+css::uno::Reference<css::ui::test::XUIObject> SAL_CALL UITestUnoObj::getFloatWindow()
+{
+ SolarMutexGuard aGuard;
+ std::unique_ptr<UIObject> pObj = UITest::getFloatWindow();
+ return new UIObjectUnoObj(std::move(pObj));
+}
+
+OUString SAL_CALL UITestUnoObj::getImplementationName()
+{
+ return "org.libreoffice.uitest.UITest";
+}
+
+sal_Bool UITestUnoObj::supportsService(OUString const & ServiceName)
+{
+ return cppu::supportsService(this, ServiceName);
+}
+
+css::uno::Sequence<OUString> UITestUnoObj::getSupportedServiceNames()
+{
+ return { "com.sun.star.ui.test.UITest" };
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+UITest_get_implementation(css::uno::XComponentContext*, css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new UITestUnoObj());
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */