Hello community,

here is the log from the commit of package libyui-rest-api for openSUSE:Factory 
checked in at 2019-12-25 10:54:14
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/libyui-rest-api (Old)
 and      /work/SRC/openSUSE:Factory/.libyui-rest-api.new.6675 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "libyui-rest-api"

Wed Dec 25 10:54:14 2019 rev:3 rq:755632 version:0.3.0

Changes:
--------
--- /work/SRC/openSUSE:Factory/libyui-rest-api/libyui-rest-api.changes  
2019-07-11 13:14:24.114848721 +0200
+++ 
/work/SRC/openSUSE:Factory/.libyui-rest-api.new.6675/libyui-rest-api.changes    
    2019-12-25 10:54:19.705620406 +0100
@@ -1,0 +2,9 @@
+Fri Nov 29 14:44:10 UTC 2019 - Rodion Iafarov <[email protected]>
+
+- Add support to operate on many widgets with rest-api (bsc#1132247)
+- Support column index when selecting a row
+- Update documentation
+- Increase SO version to 11
+- 0.3.0
+
+-------------------------------------------------------------------

Old:
----
  libyui-rest-api-0.2.0.tar.bz2

New:
----
  libyui-rest-api-0.3.0.tar.bz2

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ libyui-rest-api.spec ++++++
--- /var/tmp/diff_new_pack.sJT0Me/_old  2019-12-25 10:54:20.205620563 +0100
+++ /var/tmp/diff_new_pack.sJT0Me/_new  2019-12-25 10:54:20.205620563 +0100
@@ -16,12 +16,12 @@
 #
 
 
-%define so_version 10
+%define so_version 11
 %define bin_name %{name}%{so_version}
-%define libyui_devel_version libyui-devel >= 3.6.0
+%define libyui_devel_version libyui-devel >= 3.8.0
 
 Name:           libyui-rest-api
-Version:        0.2.0
+Version:        0.3.0
 Release:        0
 Summary:        Libyui - REST API plugin, the shared part
 License:        LGPL-2.1-only OR LGPL-3.0-only

++++++ libyui-rest-api-0.2.0.tar.bz2 -> libyui-rest-api-0.3.0.tar.bz2 ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/libyui-rest-api-0.2.0/README.md 
new/libyui-rest-api-0.3.0/README.md
--- old/libyui-rest-api-0.2.0/README.md 2019-07-03 08:16:11.000000000 +0200
+++ new/libyui-rest-api-0.3.0/README.md 2019-12-10 17:02:47.000000000 +0100
@@ -38,7 +38,7 @@
 - [ ] Allow sending more user actions
 - [ ] Some widgets do not send notify events when changed via the API
 - [ ] SSL encryption/peer verification (needed for secure transferring of 
sensitive data
-      like passwords)
+    like passwords)
 - [ ] Allow connection via Unix domain sockets
 
 ### Usage
@@ -50,6 +50,10 @@
 After that, you can get the documentation how to interact with the UI by 
accessing
 http://localhost:9999 (or http://ipv6-localhost:9999 via IPv6).
 
+NOTE: For MultiItemSelector and CustomItemSelector, rest-api doesn't work as 
expected
+in ncurses with `notify` set to true, when using pure C++ code. This 
limitation is
+due to widget implementation. With ruby wrapper, there is no issue.
+
 ### Remote Access
 
 By setting `YUI_HTTP_REMOTE=1` environmental variable, one can allow 
connections
@@ -76,53 +80,49 @@
 
 ---
 <h1>LibYUI Embedded Webserver</h1>
+<p>This webserver provides a REST API for the LibYUI application.</p>
+<p>It can be used for testing and controlling the application in automated 
tests.</p> <br>
 <h2>Short Documentation</h2>
 <h3>Application</h3>
-<p>Request: <pre>GET /application</pre>
-</p>
+<p>Request: <pre>GET /application</pre></p>
 <h4>Description</h4>
 <p>Get the application and UI generic properties like text or graphical mode, 
dialog size, screen size and supported UI featues.</p>
 <h4>Response</h4>
 <p>JSON format</p>
 <h4>Examples</h4>
 <p>
-        <pre>curl http://localhost:9999/application</pre>
+    <pre>curl http://localhost:9999/application</pre>
 </p>
 <hr>
 <h3>Dump Whole Dialog</h3>
-<p>Request: <pre>GET /dialog</pre>
-</p>
+ <p>Request: <pre>GET /dialog</pre></p>
 <h4>Description</h4>
 <p>Get the complete dialog structure in the JSON format. The result contains a 
nested structure exactly following the structure of the current dialog.</p>
 <h4>Response</h4>
 <p>JSON format</p>
 <h4>Examples</h4>
 <p>
-        <pre>curl http://localhost:9999/dialog</pre>
+    <pre>curl http://localhost:9999/dialog</pre>
 </p>
 <hr>
 <h3>Read Specific Widgets</h3>
-<p>Request: <pre>GET /widgets</pre>
-</p>
+<p>Request: <pre>GET /widgets</pre></p>
 <h4>Description</h4>
 <p>Return only the selected widgets (in JSON format). The result is a flat 
list (no nested structures).</p>
 <h4>Parameters</h4>
 <p>Filter widgets: <ul>
-                <li>
-                        <b>id</b> - widget ID serialized as string, might 
include special characters like backtick (\`)</li>
-                <li>
-                        <b>label</b> - widget label as currently displayed 
(i.e. translated!) </li>
-                <li>
-                        <b>type</b> - widget type</li>
-        </ul>
+        <li><b>id</b> - widget ID serialized as string, might include special 
characters like backtick (\`)</li>
+        <li><b>label</b> - widget label as currently displayed (i.e. 
translated!) </li>
+        <li><b>type</b> - widget type</li>
+    </ul>
 </p>
 <h4>Response</h4>
 <p>JSON format</p>
 <h4>Examples</h4>
 <p>
-        <pre>curl 'http://localhost:9999/widgets?id=next'</pre>
-        <pre>curl 'http://localhost:9999/widgets?label=Next'</pre>
-        <pre>curl 'http://localhost:9999/widgets?type=YCheckBox'</pre>
+    <pre>curl 'http://localhost:9999/widgets?id=next'</pre>
+    <pre>curl 'http://localhost:9999/widgets?label=Next'</pre>
+    <pre>curl 'http://localhost:9999/widgets?type=YCheckBox'</pre>
 </p>
 <hr>
 <h3>Change Widgets, Do an Action</h3>
@@ -131,25 +131,35 @@
 <p>Do an action with specified widgets.</p>
 <h4>Parameters</h4>
 <p>Filter the widgets, one of: <ul>
-                <li>
-                        <b>id</b> - widget ID serialized as string, might 
include special characters like backtick (\`)</li>
-                <li>
-                        <b>label</b> - widget label as currently displayed 
(i.e. translated!) </li>
-                <li>
-                        <b>type</b> - widget type</li>
-        </ul> Then specify the action: <ul>
-                <li>
-                        <b>action</b> - action to do</li>
-                <li>
-                        <b>value</b> (optional) - new value or a parameter of 
the action</li>
-        </ul>
+        <li><b>id</b> - widget ID serialized as string, might include special 
characters like backtick (\`)</li>
+        <li><b>label</b> - widget label as currently displayed (i.e. 
translated!) </li>
+        <li><b>type</b> - widget type</li>
+    </ul> Then specify the action: <ul>
+        <li><b>action</b> - action to do</li>
+        <li><b>value</b> (optional) - new value or a parameter of the 
action</li>
+        <li><b>column</b> (optional) - column id when selecting item in the 
table</li>
+    </ul>
+</p> Supported actions: <ul>
+    <li><b>press</b> - to press the button</li>
+    <li><b>check</b>|<b>uncheck</b>|<b>toggle</b> - check, uncheck or toggle 
checkbox</li>
+    <li><b>enter_text</b> - set text in the field, requires <b>value</b> 
parameter</li>
+    <li><b>switch_radio</b> - activate radio button</li>
+    <li><b>select</b> - select value in the combobox, row in the table or node 
in the tree, requires <b>value</b> parameter<br />
+        In case of table: select row in the table with given value. If 
<b>column</b> parameters is not provided, first column will be used. <br />
+        In case of tree: select node in the tree. Use <b>'|'</b> as delimiter 
for child nodes.</li>
+
+</ul>
 </p>
 <h4>Response</h4>
 <p>JSON format</p>
 <h4>Examples</h4>
 <p>
-        Press the "next" button:
-        <pre>curl -X POST 
'http://localhost:9999/widgets?id=next&action=press'</pre>
-        Set value "test" for the InputField with label "Description":
-        <pre>curl -X POST 
'http://localhost:9999/widgets?label=Description&action=enter&value=test'</pre>
+    <pre>  # press the "next" button
+curl -X POST 'http://localhost:9999/widgets?id=next&action=press'</pre>
+    <pre>  # set value "test" for the InputField with label "Description"
+curl -X POST 
'http://localhost:9999/widgets?label=Description&action=enter_text&value=test'</pre>
+    <pre>  # select row with "test" cell value in the 2-nd column (counting 
from zero) in table with id "names"
+curl -X POST 
'http://localhost:9999/widgets?id=names&action=select&value=test&column=2'</pre>
+    <pre>  # select tree item with in tree with id "files"
+curl -X POST 
'http://localhost:9999/widgets?id=files&action=select&value=root|subnode|subnode'</pre>
 </p>
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/libyui-rest-api-0.2.0/VERSION.cmake 
new/libyui-rest-api-0.3.0/VERSION.cmake
--- old/libyui-rest-api-0.2.0/VERSION.cmake     2019-07-03 08:16:11.000000000 
+0200
+++ new/libyui-rest-api-0.3.0/VERSION.cmake     2019-12-10 17:02:47.000000000 
+0100
@@ -1,5 +1,5 @@
 SET( VERSION_MAJOR "0")
-SET( VERSION_MINOR "2" )
+SET( VERSION_MINOR "3" )
 SET( VERSION_PATCH "0" )
 SET( VERSION 
"${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}${GIT_SHA1_VERSION}" )
 
@@ -8,7 +8,7 @@
 # Currently you must also change so_version in libyui.spec
 # *and also in **all** other* libyui-*.spec files in the other repositories.
 # Yes, such a design is suboptimal.
-SET( SONAME_MAJOR "10" )
+SET( SONAME_MAJOR "11" )
 SET( SONAME_MINOR "0" )
 SET( SONAME_PATCH "0" )
 SET( SONAME "${SONAME_MAJOR}.${SONAME_MINOR}.${SONAME_PATCH}" )
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/libyui-rest-api-0.2.0/package/libyui-rest-api.changes 
new/libyui-rest-api-0.3.0/package/libyui-rest-api.changes
--- old/libyui-rest-api-0.2.0/package/libyui-rest-api.changes   2019-07-03 
08:16:11.000000000 +0200
+++ new/libyui-rest-api-0.3.0/package/libyui-rest-api.changes   2019-12-10 
17:02:47.000000000 +0100
@@ -1,4 +1,13 @@
 -------------------------------------------------------------------
+Fri Nov 29 14:44:10 UTC 2019 - Rodion Iafarov <[email protected]>
+
+- Add support to operate on many widgets with rest-api (bsc#1132247)
+- Support column index when selecting a row
+- Update documentation
+- Increase SO version to 11
+- 0.3.0
+
+-------------------------------------------------------------------
 Fri Jun 28 19:35:51 UTC 2019 - Ladislav Slezák <[email protected]>
 
 - Split the libyui-rest-api plugin to separate Qt and Ncurses parts
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/libyui-rest-api-0.2.0/package/libyui-rest-api.spec 
new/libyui-rest-api-0.3.0/package/libyui-rest-api.spec
--- old/libyui-rest-api-0.2.0/package/libyui-rest-api.spec      2019-07-03 
08:16:11.000000000 +0200
+++ new/libyui-rest-api-0.3.0/package/libyui-rest-api.spec      2019-12-10 
17:02:47.000000000 +0100
@@ -16,12 +16,12 @@
 #
 
 
-%define so_version 10
+%define so_version 11
 %define bin_name %{name}%{so_version}
-%define libyui_devel_version libyui-devel >= 3.6.0
+%define libyui_devel_version libyui-devel >= 3.8.0
 
 Name:           libyui-rest-api
-Version:        0.2.0
+Version:        0.3.0
 Release:        0
 Summary:        Libyui - REST API plugin, the shared part
 License:        LGPL-2.1-only OR LGPL-3.0-only
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/libyui-rest-api-0.2.0/src/YHttpRootHandler.cc 
new/libyui-rest-api-0.3.0/src/YHttpRootHandler.cc
--- old/libyui-rest-api-0.2.0/src/YHttpRootHandler.cc   2019-07-03 
08:16:11.000000000 +0200
+++ new/libyui-rest-api-0.3.0/src/YHttpRootHandler.cc   2019-12-10 
17:02:47.000000000 +0100
@@ -100,6 +100,18 @@
 "        <ul>"
 "          <li><b>action</b> - action to do</li>"
 "          <li><b>value</b> (optional) - new value or a parameter of the 
action</li>"
+"          <li><b>column</b> (optional) - column id when selecting item in the 
table</li>"
+"        </ul>"
+"      </p>"
+" Supported actions:"
+"        <ul>"
+"          <li><b>press</b> - to press the button</li>"
+"          <li><b>check</b>|<b>uncheck</b>|<b>toggle</b> - check, uncheck or 
toggle checkbox</li>"
+"          <li><b>enter_text</b> - set text in the field, requires 
<b>value</b> parameter</li>"
+"          <li><b>switch_radio</b> - activate radio button</li>"
+"          <li><b>select</b> - select value in the combobox, row in the table 
or node in the tree, requires <b>value</b> parameter<br />"
+"                 In case of table: select row in the table with given value. 
If <b>column</b> parameters is not provided, first column will be used. <br />"
+"                 In case of tree: select node in the tree. Use <b>'|'</b> as 
delimiter for child nodes.</li>"
 "        </ul>"
 "      </p>"
 "      <h4>Response</h4>"
@@ -109,7 +121,11 @@
 "        <pre>  # press the `next button\n"
 "  curl -X POST 'http://localhost:@port@/widgets?id=`next&action=press'</pre>"
 "        <pre>  # set value \"test\" for the InputField with label 
\"Description\"\n"
-"  curl -X POST 
'http://localhost:@port@/widgets?label=Description&action=enter&value=test'</pre>"
+"  curl -X POST 
'http://localhost:@port@/widgets?label=Description&action=enter_text&value=test'</pre>"
+"        <pre>  # select row with \"test\" cell value in the 2-nd column 
(counting from zero) in table with id \"names\"\n"
+"  curl -X POST 
'http://localhost:@port@/widgets?id=names&action=select&value=test&column=2'</pre>"
+"        <pre>  # select tree item with in tree with id \"files\"\n"
+"  curl -X POST 
'http://localhost:@port@/widgets?id=files&action=select&value=root|subnode|subnode'</pre>"
 "      </p>"
 "    </body>"
 "</html>";
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/libyui-rest-api-0.2.0/src/YHttpWidgetsActionHandler.cc 
new/libyui-rest-api-0.3.0/src/YHttpWidgetsActionHandler.cc
--- old/libyui-rest-api-0.2.0/src/YHttpWidgetsActionHandler.cc  2019-07-03 
08:16:11.000000000 +0200
+++ new/libyui-rest-api-0.3.0/src/YHttpWidgetsActionHandler.cc  2019-12-10 
17:02:47.000000000 +0100
@@ -14,13 +14,29 @@
   Floor, Boston, MA 02110-1301 USA
 */
 
+#include "YCheckBox.h"
 #include "YComboBox.h"
 #include "YDialog.h"
-#include "YCheckBox.h"
+#include "YDumbTab.h"
 #include "YInputField.h"
+#include "YIntField.h"
+#include "YItemSelector.h"
+#include "YMenuButton.h"
+#include "YMultiLineEdit.h"
 #include "YPushButton.h"
 #include "YRadioButton.h"
+#include "YRichText.h"
 #include "YTable.h"
+#include "YTree.h"
+#include "YTreeItem.h"
+#include "YSelectionBox.h"
+
+#include <codecvt>
+#include <vector>
+#include <sstream>
+#include <cstdlib>
+#include <string>
+#include <boost/algorithm/string.hpp>
 
 #include "YHttpWidgetsActionHandler.h"
 
@@ -28,36 +44,43 @@
     const char* url, const char* method, const char* upload_data,
     size_t* upload_data_size, std::ostream& body, bool *redraw)
 {
-    if (YDialog::topmostDialog(false))  {
+    if ( YDialog::topmostDialog(false) )
+    {
         WidgetArray widgets;
 
-        if (const char* label = MHD_lookup_connection_value(connection, 
MHD_GET_ARGUMENT_KIND, "label"))
-            widgets = YWidgetFinder::by_label(label);
-        else if (const char* id = MHD_lookup_connection_value(connection, 
MHD_GET_ARGUMENT_KIND, "id"))
-            widgets = YWidgetFinder::by_id(id);
-        else if (const char* type = MHD_lookup_connection_value(connection, 
MHD_GET_ARGUMENT_KIND, "type"))
-            widgets = YWidgetFinder::by_type(type);
-        else {
+        const char* label = MHD_lookup_connection_value(connection, 
MHD_GET_ARGUMENT_KIND, "label");
+        const char* id = MHD_lookup_connection_value(connection, 
MHD_GET_ARGUMENT_KIND, "id");
+        const char* type = MHD_lookup_connection_value(connection, 
MHD_GET_ARGUMENT_KIND, "type");
+
+        if ( label || id || type )
+        {
+            widgets = YWidgetFinder::find(label, id, type);
+        }
+        else
+        {
             widgets = YWidgetFinder::all();
         }
 
-        if (widgets.empty()) {
+        if ( widgets.empty() )
+        {
             body << "{ \"error\" : \"Widget not found\" }" << std::endl;
             _error_code = MHD_HTTP_NOT_FOUND;
         }
-        else if (const char* action = MHD_lookup_connection_value(connection, 
MHD_GET_ARGUMENT_KIND, "action"))
+        else if ( const char* action = MHD_lookup_connection_value(connection, 
MHD_GET_ARGUMENT_KIND, "action") )
         {
-            std::string value;
-            if (const char* val = MHD_lookup_connection_value(connection, 
MHD_GET_ARGUMENT_KIND, "value"))
-                value = val;
-
-            _error_code = do_action(widgets, action, value, body);
+            if( widgets.size() != 1 )
+            {
+                body << "{ \"error\" : \"Multiple widgets found to act on, try 
using multicriteria search (label+id+type)\" }" << std::endl;
+                _error_code = MHD_HTTP_NOT_FOUND;
+            }
+            _error_code = do_action(widgets[0], action, connection, body);
 
             // the action possibly changed something in the UI, signalize 
redraw needed
-            if (redraw && _error_code == MHD_HTTP_OK)
+            if ( redraw && _error_code == MHD_HTTP_OK )
                 *redraw = true;
         }
-        else {
+        else
+        {
             body << "{ \"error\" : \"Missing action parameter\" }" << 
std::endl;
             _error_code = MHD_HTTP_NOT_FOUND;
         }
@@ -73,87 +96,325 @@
     return "application/json";
 }
 
-int YHttpWidgetsActionHandler::do_action(WidgetArray widgets, const 
std::string &action, const std::string &value, std::ostream& body) {
+int YHttpWidgetsActionHandler::do_action(YWidget *widget, const std::string 
&action, struct MHD_Connection *connection, std::ostream& body) {
+
     yuiMilestone() << "Starting action: " << action << std::endl;
 
     // TODO improve this, maybe use better names for the actions...
 
     // press a button
-    if (action == "press") {
-        return action_handler<YPushButton>(widgets, [] (YPushButton *button) {
-            yuiMilestone() << "Pressing button \"" << button->label() << '"' 
<< std::endl;
-            button->setKeyboardFocus();
-            button->activate();
-        } );
+    if ( action == "press" )
+    {
+        yuiMilestone() << "Received action: press" << std::endl;
+        if (dynamic_cast<YPushButton*>(widget))
+        {
+            return action_handler<YPushButton>(widget, [] (YPushButton *button)
+            {
+                yuiMilestone() << "Pressing button \"" << button->label() << 
'"' << std::endl;
+                button->setKeyboardFocus();
+                button->activate();
+            } );
+        }
+        else if ( dynamic_cast<YRichText*>(widget) )
+        {
+            std::string value;
+            if ( const char* val = MHD_lookup_connection_value(connection, 
MHD_GET_ARGUMENT_KIND, "value") )
+                value = val;
+            return action_handler<YRichText>(widget, [&] (YRichText *rt) {
+                yuiMilestone() << "Activating hyperlink on richtext: \"" << 
value << '"' << std::endl;
+                rt->setKeyboardFocus();
+                rt->activateLink(value);
+            } );
+        }
+        else if( dynamic_cast<YMenuButton*>(widget) )
+        {
+            std::string value;
+            if ( const char* val = MHD_lookup_connection_value(connection, 
MHD_GET_ARGUMENT_KIND, "value") )
+                value = val;
+            return action_handler<YMenuButton>(widget, [&] (YMenuButton *mb) {
+                // Vector of string to store path to the tree item
+                std::vector<std::string> path;
+                boost::split( path, value, boost::is_any_of( TreePathDelimiter 
) );
+                YMenuItem * item = mb->findItem( path );
+                if ( item )
+                {
+                    yuiMilestone() << "Activating Item by path :" << value << 
" in \"" << mb->label() << "\" MenuButton" << std::endl;
+                    mb->setKeyboardFocus();
+                    mb->activateItem( item );
+                }
+                else
+                {
+                    body << "Item with path: \"" << value << "\" cannot be 
found in the MenuButton widget" << std::endl;
+                    throw YUIException("Item cannot be found in the MenuButton 
widget");
+                }
+            } );
+        }
+        else
+        {
+            body << "Action is not supported for the selected widget: " << 
widget->widgetClass() << std::endl;
+            return MHD_HTTP_NOT_FOUND;
+        }
     }
     // check a checkbox
-    else if (action == "check") {
-        return action_handler<YCheckBox>(widgets, [] (YCheckBox *checkbox) {
-            if (checkbox->isChecked()) return;
-            yuiMilestone() << "Checking \"" << checkbox->label() << '"' << 
std::endl;
-            checkbox->setKeyboardFocus();
-            checkbox->setChecked(true);
-        } );
+    else if ( action == "check" )
+    {
+        if ( dynamic_cast<YCheckBox*>(widget) )
+        {
+            return action_handler<YCheckBox>(widget, [] (YCheckBox *checkbox) {
+                if (checkbox->isChecked()) return;
+                yuiMilestone() << "Checking \"" << checkbox->label() << '"' << 
std::endl;
+                checkbox->setKeyboardFocus();
+                checkbox->setChecked(true);
+            } );
+        }
+        else if( dynamic_cast<YItemSelector*>(widget) )
+        {
+            std::string value;
+            if (const char* val = MHD_lookup_connection_value(connection, 
MHD_GET_ARGUMENT_KIND, "value"))
+                value = val;
+
+            return get_item_selector_handler(widget, value, body, 1);
+        }
+        else
+        {
+            body << "Action is not supported for the selected widget" << 
widget->widgetClass() << std::endl;
+            return MHD_HTTP_NOT_FOUND;
+        }
     }
     // uncheck a checkbox
-    else if (action == "uncheck") {
-        return action_handler<YCheckBox>(widgets, [] (YCheckBox *checkbox) {
-            if (!checkbox->isChecked()) return;
-            yuiMilestone() << "Unchecking \"" << checkbox->label() << '"' << 
std::endl;
-            checkbox->setKeyboardFocus();
-            checkbox->setChecked(false);
-        } );
+    else if ( action == "uncheck" )
+    {
+        if ( dynamic_cast<YCheckBox*>(widget) )
+        {
+            return action_handler<YCheckBox>(widget, [] (YCheckBox *checkbox) {
+                if (!checkbox->isChecked()) return;
+                yuiMilestone() << "Unchecking \"" << checkbox->label() << '"' 
<< std::endl;
+                checkbox->setKeyboardFocus();
+                checkbox->setChecked(false);
+            } );
+        }
+        else if( dynamic_cast<YItemSelector*>(widget) )
+        {
+            std::string value;
+            if ( const char* val = MHD_lookup_connection_value(connection, 
MHD_GET_ARGUMENT_KIND, "value") )
+                value = val;
+
+            return get_item_selector_handler(widget, value, body, 0);
+        }
+        else
+        {
+            body << "Action is not supported for the selected widget" << 
widget->widgetClass() << std::endl;
+            return MHD_HTTP_NOT_FOUND;
+        }
     }
     // toggle a checkbox (reverse the state)
-    else if (action == "toggle") {
-        return action_handler<YCheckBox>(widgets, [] (YCheckBox *checkbox) {
-            yuiMilestone() << "Toggling \"" << checkbox->label() << '"' << 
std::endl;
-            checkbox->setKeyboardFocus();
-            checkbox->setChecked(!checkbox->isChecked());
-        } );
+    else if ( action == "toggle" ) {
+        if ( dynamic_cast<YCheckBox*>(widget) )
+        {
+            return action_handler<YCheckBox>(widget, [] (YCheckBox *checkbox) {
+                yuiMilestone() << "Toggling \"" << checkbox->label() << '"' << 
std::endl;
+                checkbox->setKeyboardFocus();
+                checkbox->setChecked(!checkbox->isChecked());
+            } );
+        }
+        else if( dynamic_cast<YItemSelector*>(widget) )
+        {
+            std::string value;
+            if ( const char* val = MHD_lookup_connection_value(connection, 
MHD_GET_ARGUMENT_KIND, "value") )
+                value = val;
+
+            return get_item_selector_handler(widget, value, body);
+        }
+        else
+        {
+            body << "Action is not supported for the selected widget" << 
widget->widgetClass() << std::endl;
+            return MHD_HTTP_NOT_FOUND;
+        }
     }
     // enter input field text
-    else if (action == "enter_text") {
-        return action_handler<YInputField>(widgets, [&] (YInputField *input) {
-            yuiMilestone() << "Setting value for InputField \"" << 
input->label() << '"' << std::endl;
-            input->setKeyboardFocus();
-            input->setValue(value);
-        } );
-    }
-    else if (action == "switch_radio") {
-        return action_handler<YRadioButton>(widgets, [&] (YRadioButton *rb) {
-            yuiMilestone() << "Activating RadioButton \"" << rb->label() << 
'"' << std::endl;
-            rb->setKeyboardFocus();
-            rb->setValue(true);
-        } );
-    }
-    else if (action == "select_combo") {
-        return action_handler<YComboBox>(widgets, [&] (YComboBox *cb) {
-            yuiMilestone() << "Activating ComboBox \"" << cb->label() << '"' 
<< std::endl;
-            cb->setKeyboardFocus();
-            cb->setValue(value);
-        } );
-    }
-    else if (action == "select_table") {
-        return action_handler<YTable>(widgets, [&] (YTable *tb) {
-           YItem * item = tb->findItem(value, 0);
-           if (item) {
-                yuiMilestone() << "Activating Table \"" << tb->label() << '"' 
<< std::endl;
-                tb->setKeyboardFocus();
-                tb->selectItem(item);
-           }
-           else {
-               yuiWarning() << '"' << value << "\" is not a valid item" << 
std::endl;
-           }
-        } );
+    else if ( action == "enter_text" )
+    {
+        std::string value;
+        if ( const char* val = MHD_lookup_connection_value(connection, 
MHD_GET_ARGUMENT_KIND, "value") )
+            value = val;
+
+        if ( dynamic_cast<YInputField*>(widget) )
+        {
+            return action_handler<YInputField>(widget, [&] (YInputField 
*input) {
+                yuiMilestone() << "Setting value for InputField \"" << 
input->label() << '"' << std::endl;
+                input->setKeyboardFocus();
+                input->setValue(value);
+            } );
+        }
+        else if ( dynamic_cast<YIntField*>(widget) )
+        {
+            return action_handler<YIntField>(widget, [&] (YIntField *input) {
+                yuiMilestone() << "Setting value for YIntField \"" << 
input->label() << '"' << std::endl;
+                input->setKeyboardFocus();
+                input->setValue(atoi(value.c_str()));
+            } );
+        }
+        else if ( dynamic_cast<YMultiLineEdit*>(widget) )
+        {
+            return action_handler<YMultiLineEdit>(widget, [&] (YMultiLineEdit 
*input) {
+                yuiMilestone() << "Setting value for YMultiLineEdit \"" << 
input->label() << '"' << std::endl;
+                input->setKeyboardFocus();
+                input->setValue(value);
+            } );
+        }
+        else
+        {
+            body << "Action is not supported for the selected widget: " << 
widget->widgetClass() << std::endl;
+            return MHD_HTTP_NOT_FOUND;
+        }
+    }
+    else if ( action == "select" )
+    {
+        std::string value;
+        if (const char* val = MHD_lookup_connection_value(connection, 
MHD_GET_ARGUMENT_KIND, "value"))
+            value = val;
+
+        if ( dynamic_cast<YComboBox*>(widget) )
+        {
+            return action_handler<YComboBox>(widget, [&] (YComboBox *cb) {
+                yuiMilestone() << "Activating ComboBox \"" << cb->label() << 
'"' << std::endl;
+                cb->setKeyboardFocus();
+                cb->setValue(value);
+            } );
+        }
+        else if( dynamic_cast<YTable*>(widget) )
+        {
+            int column_id = 0;
+            if ( const char* val = MHD_lookup_connection_value(connection, 
MHD_GET_ARGUMENT_KIND, "column") )
+                column_id = atoi(val);
+            return action_handler<YTable>(widget, [&] (YTable *tb) {
+                YItem * item = tb->findItem(value, column_id);
+                if ( item )
+                {
+                        yuiMilestone() << "Activating Table \"" << tb->label() 
<< '"' << std::endl;
+                        tb->setKeyboardFocus();
+                        tb->selectItem(item);
+                }
+                else
+                {
+                    body << '"' << value << "\" item cannot be found in the 
table" << std::endl;
+                    throw YUIException("Item cannot be found in the table");
+                }
+            } );
+        }
+        else if( dynamic_cast<YTree*>(widget) )
+        {
+            return action_handler<YTree>(widget, [&] (YTree *tree) {
+                // Vector of string to store path to the tree item
+                std::vector<std::string> path;
+                boost::split( path, value, boost::is_any_of( TreePathDelimiter 
) );
+                YItem * item = tree->findItem( path );
+                if (item)
+                {
+                    yuiMilestone() << "Activating Tree Item \"" << 
item->label() << '"' << std::endl;
+                    tree->setKeyboardFocus();
+                    tree->selectItem(item);
+                    tree->activate();
+                }
+                else
+                {
+                    body << '"' << value << "\" item cannot be found in the 
tree" << std::endl;
+                    throw YUIException("Item cannot be found in the tree");
+                }
+            } );
+        }
+        else if ( dynamic_cast<YDumbTab*>(widget) )
+        {
+            return action_handler<YDumbTab>(widget, [&] (YDumbTab *tab) {
+                YItem * item = tab->findItem( value );
+                if ( item )
+                {
+                    yuiMilestone() << "Activating Tree Item \"" << 
item->label() << '"' << std::endl;
+                    tab->setKeyboardFocus();
+                    tab->selectItem(item);
+                    tab->activate();
+                }
+                else
+                {
+                    body << '"' << value << "\" item cannot be found in the 
tree" << std::endl;
+                    throw YUIException("Item cannot be found in the tree");
+                }
+            } );
+        }
+        else if( dynamic_cast<YRadioButton*>(widget) )
+        {
+            return action_handler<YRadioButton>(widget, [&] (YRadioButton *rb) 
{
+                yuiMilestone() << "Activating RadioButton \"" << rb->label() 
<< '"' << std::endl;
+                rb->setKeyboardFocus();
+                rb->setValue(true);
+            } );
+        }
+        else if( dynamic_cast<YSelectionBox*>(widget) )
+        {
+            return action_handler<YSelectionBox>(widget, [&] (YSelectionBox 
*sb) {
+                YItem * item = sb->findItem( value );
+                if ( item )
+                {
+                    yuiMilestone() << "Activating selection box \"" << 
sb->label() << '"' << std::endl;
+                    sb->setKeyboardFocus();
+                    sb->selectItem(item);
+                }
+                else
+                {
+                    body << '"' << value << "\" item cannot be found in the 
selection box" << std::endl;
+                    throw YUIException("Item cannot be found in the selection 
box");
+                }
+            } );
+        }
+        else if( dynamic_cast<YItemSelector*>(widget) )
+        {
+            return get_item_selector_handler(widget, value, body, 1);
+        }
+        else
+        {
+            body << "Action is not supported for the selected widget" << 
widget->widgetClass() << std::endl;
+            return MHD_HTTP_NOT_FOUND;
+        }
     }
     // TODO: more actions
     // else if (action == "enter") {
     // }
-    else {
+    else
+    {
         body << "{ \"error\" : \"Unknown action\" }" << std::endl;
         return MHD_HTTP_NOT_FOUND;
     }
 
     return MHD_HTTP_OK;
 }
+
+int YHttpWidgetsActionHandler::get_item_selector_handler(YWidget *widget, 
const std::string &value, std::ostream& body, const int state) {
+    return action_handler<YItemSelector>(widget, [&] (YItemSelector *is) {
+        YItem * item = is->findItem( value );
+        if ( item )
+        {
+            yuiMilestone() << "Activating item selector with item \"" << value 
<< '"' << std::endl;
+            is->setKeyboardFocus();
+            // Toggle in case state is undefined
+            bool select = state < 0  ? !item->selected() :
+                          state == 0 ? false :
+                                       true;
+            if( state < 0 )
+            {
+                select = !item->selected();
+            }
+            else
+            {
+                select = state == 0 ? false : true;
+            }
+            item->setSelected( select );
+            is->selectItem( item, select );
+            is->activateItem( item );
+        }
+        else
+        {
+            body << '"' << value << "\" item cannot be found in the item 
selector" << std::endl;
+            throw YUIException("Item cannot be found in the item selector");
+        }
+    } );
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/libyui-rest-api-0.2.0/src/YHttpWidgetsActionHandler.h 
new/libyui-rest-api-0.3.0/src/YHttpWidgetsActionHandler.h
--- old/libyui-rest-api-0.2.0/src/YHttpWidgetsActionHandler.h   2019-07-03 
08:16:11.000000000 +0200
+++ new/libyui-rest-api-0.3.0/src/YHttpWidgetsActionHandler.h   2019-12-10 
17:02:47.000000000 +0100
@@ -25,7 +25,9 @@
 #include <functional>
 #include <microhttpd.h>
 
-#define YUILogComponent "rest-api"
+#define YUILogComponent   "rest-api"
+#define TreePathDelimiter "|"
+
 #include "YUILog.h"
 
 class YHttpWidgetsActionHandler : public YHttpHandler
@@ -50,39 +52,40 @@
 
     int _error_code;
 
-
     // TODO: move this somewhere else...
 
-    int do_action(WidgetArray widgets, const std::string &action, const 
std::string &value, std::ostream& body);
+    int do_action(YWidget *widget, const std::string &action, struct 
MHD_Connection *connection, std::ostream& body);
 
     template<typename T>
-    int action_handler(WidgetArray widgets, std::function<void (T*)> 
handler_func) {
-        for(YWidget *widget: widgets) {
-            if (auto w = dynamic_cast<T*>(widget)) {
-                try
-                {
-                    // allow changing only the enabled widgets, disabled ones
-                    // cannot be changed by user from the UI, do not be more 
powerfull
-                    if (handler_func && widget->isEnabled()) handler_func(w);
-                }
-                // some widgets may throw an exception when setting invalid 
values
-                catch (const YUIException &e)
-                {
-                    return MHD_HTTP_UNPROCESSABLE_ENTITY;
-                }
+    int action_handler(YWidget *widget, std::function<void (T*)> handler_func) 
{
+        if (auto w = dynamic_cast<T*>(widget)) {
+            try
+            {
+                // allow changing only the enabled widgets, disabled ones
+                // cannot be changed by user from the UI, do not be more 
powerfull
+                if (handler_func && widget->isEnabled()) handler_func(w);
             }
-            else {
-                // TODO: demangle the C++ names here ?
-                // 
https://gcc.gnu.org/onlinedocs/libstdc++/manual/ext_demangling.html
-                yuiError() << "Expected " << typeid(T).name() << " widget, 
found " << widget->widgetClass()
-                     << " (" << typeid(*widget).name() << ')' << std::endl;
-                return MHD_HTTP_NOT_FOUND;
+            // some widgets may throw an exception when setting invalid values
+            catch (const YUIException &e)
+            {
+                yuiError() << "Error while processing action on widget "
+                    << typeid(*widget).name() << " " << e.what() << std::endl;
+                return MHD_HTTP_UNPROCESSABLE_ENTITY;
             }
         }
+        else {
+            // TODO: demangle the C++ names here ?
+            // 
https://gcc.gnu.org/onlinedocs/libstdc++/manual/ext_demangling.html
+            yuiError() << "Expected " << typeid(T).name() << " widget, found " 
<< widget->widgetClass()
+                 << " (" << typeid(*widget).name() << ')' << std::endl;
+            return MHD_HTTP_NOT_FOUND;
+        }
 
         return MHD_HTTP_OK;
     }
 
+    int get_item_selector_handler(YWidget *widget, const std::string &value, 
std::ostream& body, const int state = -1);
+
 };
 
 #endif // YHttpWidgetsActionHandler_h
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/libyui-rest-api-0.2.0/src/YJsonSerializer.cc 
new/libyui-rest-api-0.3.0/src/YJsonSerializer.cc
--- old/libyui-rest-api-0.2.0/src/YJsonSerializer.cc    2019-07-03 
08:16:11.000000000 +0200
+++ new/libyui-rest-api-0.3.0/src/YJsonSerializer.cc    2019-12-10 
17:02:47.000000000 +0100
@@ -267,6 +267,16 @@
         json["password_mode"] = inp->passwordMode();
     }
 
+    if (auto inp = dynamic_cast<YMultiLineEdit*>(widget))
+    {
+        json["value"] = inp->value();
+    }
+
+    if (auto inp = dynamic_cast<YProgressBar*>(widget))
+    {
+        json["value"] = inp->value();
+    }
+
     if (auto intf = dynamic_cast<YIntField*>(widget))
     {
         json["value"] = intf->value();
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/libyui-rest-api-0.2.0/src/YWidgetFinder.cc 
new/libyui-rest-api-0.3.0/src/YWidgetFinder.cc
--- old/libyui-rest-api-0.2.0/src/YWidgetFinder.cc      2019-07-03 
08:16:11.000000000 +0200
+++ new/libyui-rest-api-0.3.0/src/YWidgetFinder.cc      2019-12-10 
17:02:47.000000000 +0100
@@ -16,6 +16,7 @@
 
 // boost::erase_all
 #include <boost/algorithm/string.hpp>
+#include <functional>
 
 #include "YDialog.h"
 #include "YWidget.h"
@@ -24,105 +25,101 @@
 #include "YWidgetFinder.h"
 
 // internal helper methods
-static void find_widgets_by_label_rec(YWidget *w, const std::string &label, 
WidgetArray &array);
-static void find_widgets_by_id_rec(YWidget *w, const std::string &id, 
WidgetArray &array);
-static void find_widgets_by_type_rec(YWidget *w, const std::string &type, 
WidgetArray &array);
-static void find_all_widgets_rec(YWidget *w, WidgetArray &array);
+static bool filter_by_label_rec(YWidget *w, const std::string &label);
+static bool filter_by_id_rec(YWidget *w, const std::string &id);
+static bool filter_by_type_rec(YWidget *w, const std::string &type);
+static void find_widgets(YWidget *w, WidgetArray &array, std::function<bool 
(YWidget*)> filter_func);
+
+// WidgetArray YWidgetFinder::find(const std::string &label, const std::string 
&id, const std::string &type)
+WidgetArray YWidgetFinder::find( const char* label, const char* id, const 
char* type )
+{
+    WidgetArray ret;
+    find_widgets(YDialog::topmostDialog(), ret, [& label, & id, & type] 
(YWidget *w) {
+        return (!label || filter_by_label_rec(w, label)) &&
+               (!id || filter_by_id_rec(w, id)) &&
+               (!type || filter_by_type_rec(w, type));
+    } );
+    return ret;
+}
 
 WidgetArray YWidgetFinder::by_label(const std::string &label)
 {
     WidgetArray ret;
-    find_widgets_by_label_rec(YDialog::topmostDialog(), label, ret);
+    find_widgets(YDialog::topmostDialog(), ret, [& label] (YWidget *w) {
+        return filter_by_label_rec(w, label);
+    } );
     return ret;
 }
 
 WidgetArray YWidgetFinder::by_id(const std::string &id)
 {
     WidgetArray ret;
-    find_widgets_by_id_rec(YDialog::topmostDialog(), id, ret);
+    find_widgets(YDialog::topmostDialog(), ret, [& id] (YWidget *w) {
+        return filter_by_id_rec(w, id);
+    } );
     return ret;
 }
 
 WidgetArray YWidgetFinder::by_type(const std::string &type)
 {
     WidgetArray ret;
-    find_widgets_by_type_rec(YDialog::topmostDialog(), type, ret);
+    find_widgets(YDialog::topmostDialog(), ret, [& type] (YWidget *w) {
+        return filter_by_type_rec(w, type);
+    } );
     return ret;
 }
 
 WidgetArray YWidgetFinder::all()
 {
     WidgetArray ret;
-    find_all_widgets_rec(YDialog::topmostDialog(), ret);
+    find_widgets(YDialog::topmostDialog(), ret, [] (YWidget *w) {
+        return true;
+    } );
     return ret;
 }
 
+void find_widgets(YWidget *w, WidgetArray &array, std::function<bool 
(YWidget*)> filter_func) {
+    if ( !w )
+        return;
 
-void find_all_widgets_rec(YWidget *w, WidgetArray &array)
-{
-    if (!w) return;
-    array.push_back(w);
+    if( filter_func(w) )
+        array.push_back(w);
 
-    // search also in the children widgets
-    // YWidget provides begin() and end() so we can iterate it just like any 
container
     for(YWidget *child: *w)
     {
-        find_all_widgets_rec(child, array);
+        find_widgets(child, array, filter_func);
     };
 }
 
-void find_widgets_by_label_rec(YWidget *w, const std::string &label, 
WidgetArray &array)
+static bool filter_by_label_rec(YWidget *w, const std::string &label)
 {
-    if (!w) return;
     // check the widget label if it is defined
-    if (w->propertySet().contains("Label"))
+    if ( w->propertySet().contains("Label") )
     {
         std::string widget_label = w->getProperty("Label").stringVal();
         boost::erase_all(widget_label, "&");
 
-        if (widget_label == label)
-        {
-            array.push_back(w);
-        }
+        if ( widget_label == label )
+            return true;
     }
-
-    // search also in the children widgets
-    // YWidget provides begin() and end() so we can iterate it just like any 
container
-    for(YWidget *child: *w)
-    {
-        find_widgets_by_label_rec(child, label, array);
-    };
+    return false;
 }
 
-void find_widgets_by_id_rec(YWidget *w, const std::string &id, WidgetArray 
&array)
+static bool filter_by_id_rec(YWidget *w, const std::string &id)
 {
-    if (!w) return;
-    // check the widget ID if it is defined
-    if (w->hasId() && w->id()->toString() == id) {
-        array.push_back(w);
-    }
+    if ( w->hasId() && w->id()->toString() == id )
+        return true;
 
-    // search also in the children widgets
-    // YWidget provides begin() and end() so we can iterate it just like any 
container
-    for(YWidget *child: *w)
-    {
-        find_widgets_by_id_rec(child, id, array);
-    };
+    return false;
 }
 
-void find_widgets_by_type_rec(YWidget *w, const std::string &type, WidgetArray 
&array)
+
+static bool filter_by_type_rec(YWidget *w, const std::string &type)
 {
-    if (!w) return;
     auto propSet = w->propertySet();
 
-    if (propSet.contains("WidgetClass") && 
w->getProperty("WidgetClass").stringVal() == type)
-        array.push_back(w);
+    if ( propSet.contains("WidgetClass") && 
w->getProperty("WidgetClass").stringVal() == type )
+        return true;
 
-    // search also in the children widgets
-    // YWidget provides begin() and end() so we can iterate it just like any 
container
-    for(YWidget *child: *w)
-    {
-        find_widgets_by_type_rec(child, type, array);
-    };
+    return false;
 }
-
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/libyui-rest-api-0.2.0/src/YWidgetFinder.h 
new/libyui-rest-api-0.3.0/src/YWidgetFinder.h
--- old/libyui-rest-api-0.2.0/src/YWidgetFinder.h       2019-07-03 
08:16:11.000000000 +0200
+++ new/libyui-rest-api-0.3.0/src/YWidgetFinder.h       2019-12-10 
17:02:47.000000000 +0100
@@ -29,6 +29,9 @@
 
 public:
 
+    // static WidgetArray find(const std::string &label, const std::string 
&id, const std::string &type);
+    static WidgetArray find( const char* label, const char* id, const char* 
type );
+
     static WidgetArray by_label(const std::string &label);
 
     static WidgetArray by_id(const std::string &id);


Reply via email to