1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
10 #include <jsdialog/jsdialogbuilder.hxx>
11 #include <o3tl/string_view.hxx>
12 #include <vcl/weld.hxx>
13 #include <vcl/jsdialog/executor.hxx>
14 #include <sal/log.hxx>
15 #include <rtl/uri.hxx>
16 #include <boost/property_tree/json_parser.hpp>
20 StringMap
jsonToStringMap(const char* pJSON
)
23 if (pJSON
&& pJSON
[0] != '\0')
25 std::stringstream
aStream(pJSON
);
26 boost::property_tree::ptree aTree
;
27 boost::property_tree::read_json(aStream
, aTree
);
29 for (const auto& rPair
: aTree
)
31 aArgs
[OUString::fromUtf8(rPair
.first
)]
32 = OUString::fromUtf8(rPair
.second
.get_value
<std::string
>("."));
38 void SendFullUpdate(const OUString
& nWindowId
, const OUString
& rWidget
)
40 weld::Widget
* pWidget
= JSInstanceBuilder::FindWeldWidgetsMap(nWindowId
, rWidget
);
41 if (auto pJSWidget
= dynamic_cast<BaseJSWidget
*>(pWidget
))
42 pJSWidget
->sendFullUpdate();
45 void SendAction(const OUString
& nWindowId
, const OUString
& rWidget
,
46 std::unique_ptr
<ActionDataMap
> pData
)
48 weld::Widget
* pWidget
= JSInstanceBuilder::FindWeldWidgetsMap(nWindowId
, rWidget
);
49 if (auto pJSWidget
= dynamic_cast<BaseJSWidget
*>(pWidget
))
50 pJSWidget
->sendAction(std::move(pData
));
53 bool ExecuteAction(const OUString
& nWindowId
, const OUString
& rWidget
, StringMap
& rData
)
55 weld::Widget
* pWidget
= JSInstanceBuilder::FindWeldWidgetsMap(nWindowId
, rWidget
);
57 OUString sControlType
= rData
["type"];
58 OUString sAction
= rData
["cmd"];
60 if (sControlType
== "responsebutton")
62 auto pButton
= dynamic_cast<weld::Button
*>(pWidget
);
63 if (pWidget
== nullptr || (pButton
&& !pButton
->is_custom_handler_set()))
65 // welded wrapper not found - use response code instead
66 pWidget
= JSInstanceBuilder::FindWeldWidgetsMap(nWindowId
, "__DIALOG__");
67 sControlType
= "dialog";
72 // welded wrapper for button found - use it
73 sControlType
= "pushbutton";
77 if (pWidget
!= nullptr)
79 if (sAction
== "grab_focus")
81 pWidget
->grab_focus();
85 if (sControlType
== "tabcontrol")
87 auto pNotebook
= dynamic_cast<weld::Notebook
*>(pWidget
);
90 if (sAction
== "selecttab")
92 sal_Int32 page
= o3tl::toInt32(rData
["data"]);
94 OUString aCurrentPage
= pNotebook
->get_current_page_ident();
95 LOKTrigger::leave_page(*pNotebook
, aCurrentPage
);
96 pNotebook
->set_current_page(page
);
97 LOKTrigger::enter_page(*pNotebook
, pNotebook
->get_page_ident(page
));
103 else if (sControlType
== "combobox")
105 auto pCombobox
= dynamic_cast<weld::ComboBox
*>(pWidget
);
108 if (sAction
== "selected")
110 OUString sSelectedData
= rData
["data"];
111 int separatorPos
= sSelectedData
.indexOf(';');
112 if (separatorPos
> 0)
114 std::u16string_view entryPos
= sSelectedData
.subView(0, separatorPos
);
115 sal_Int32 pos
= o3tl::toInt32(entryPos
);
116 pCombobox
->set_active(pos
);
117 LOKTrigger::trigger_changed(*pCombobox
);
121 else if (sAction
== "change")
123 // it might be other class than JSComboBox
124 auto pJSCombobox
= dynamic_cast<JSComboBox
*>(pWidget
);
126 pJSCombobox
->set_entry_text_without_notify(rData
["data"]);
128 pCombobox
->set_entry_text(rData
["data"]);
129 LOKTrigger::trigger_changed(*pCombobox
);
134 else if (sControlType
== "pushbutton")
136 auto pButton
= dynamic_cast<weld::Button
*>(pWidget
);
139 if (sAction
== "click")
144 else if (sAction
== "toggle")
146 LOKTrigger::trigger_toggled(dynamic_cast<weld::Toggleable
&>(*pWidget
));
151 else if (sControlType
== "menubutton")
153 auto pButton
= dynamic_cast<weld::MenuButton
*>(pWidget
);
156 if (sAction
== "toggle")
158 if (pButton
->get_active())
159 pButton
->set_active(false);
161 pButton
->set_active(true);
163 BaseJSWidget
* pMenuButton
= dynamic_cast<BaseJSWidget
*>(pButton
);
165 pMenuButton
->sendUpdate(true);
169 else if (sAction
== "select")
171 LOKTrigger::trigger_selected(*pButton
, rData
["data"]);
176 else if (sControlType
== "checkbox")
178 auto pCheckButton
= dynamic_cast<weld::CheckButton
*>(pWidget
);
181 if (sAction
== "change")
183 bool bChecked
= rData
["data"] == "true";
184 pCheckButton
->set_state(bChecked
? TRISTATE_TRUE
: TRISTATE_FALSE
);
185 LOKTrigger::trigger_toggled(*static_cast<weld::Toggleable
*>(pCheckButton
));
190 else if (sControlType
== "drawingarea")
192 auto pArea
= dynamic_cast<weld::DrawingArea
*>(pWidget
);
195 if (sAction
== "click" || sAction
== "dblclick" || sAction
== "mousemove"
196 || sAction
== "mousedown" || sAction
== "mouseup")
198 OUString sClickData
= rData
["data"];
199 int nSeparatorPos
= sClickData
.indexOf(';');
200 if (nSeparatorPos
> 0)
203 std::u16string_view nClickPosX
= sClickData
.subView(0, nSeparatorPos
);
204 std::u16string_view nClickPosY
= sClickData
.subView(nSeparatorPos
+ 1);
206 if (nClickPosX
.empty() || nClickPosY
.empty())
209 double fPosX
= o3tl::toDouble(nClickPosX
);
210 double fPosY
= o3tl::toDouble(nClickPosY
);
211 OutputDevice
& rRefDevice
= pArea
->get_ref_device();
212 // We send OutPutSize for the drawing area bitmap
213 // get_size_request is not necessarily updated
214 // therefore it may be incorrect.
215 Size size
= rRefDevice
.GetOutputSizePixel();
216 fPosX
= fPosX
* size
.Width();
217 fPosY
= fPosY
* size
.Height();
219 if (sAction
== "click")
220 LOKTrigger::trigger_click(*pArea
, Point(fPosX
, fPosY
));
221 else if (sAction
== "dblclick")
222 LOKTrigger::trigger_dblclick(*pArea
, Point(fPosX
, fPosY
));
223 else if (sAction
== "mouseup")
224 LOKTrigger::trigger_mouse_up(*pArea
, Point(fPosX
, fPosY
));
225 else if (sAction
== "mousedown")
226 LOKTrigger::trigger_mouse_down(*pArea
, Point(fPosX
, fPosY
));
227 else if (sAction
== "mousemove")
228 LOKTrigger::trigger_mouse_move(*pArea
, Point(fPosX
, fPosY
));
233 else if (sAction
== "keypress")
235 sal_uInt32 nKeyNo
= rData
["data"].toUInt32();
236 LOKTrigger::trigger_key_press(*pArea
, KeyEvent(nKeyNo
, vcl::KeyCode(nKeyNo
)));
237 LOKTrigger::trigger_key_release(*pArea
, KeyEvent(nKeyNo
, vcl::KeyCode(nKeyNo
)));
240 else if (sAction
== "textselection")
242 OUString sTextData
= rData
["data"];
243 int nSeparatorPos
= sTextData
.indexOf(';');
244 if (nSeparatorPos
<= 0)
247 int nSeparator2Pos
= sTextData
.indexOf(';', nSeparatorPos
+ 1);
248 int nSeparator3Pos
= 0;
250 if (nSeparator2Pos
> 0)
252 // start;end;startPara;endPara
253 nSeparator3Pos
= sTextData
.indexOf(';', nSeparator2Pos
+ 1);
254 if (nSeparator3Pos
<= 0)
264 std::u16string_view aStartPos
= sTextData
.subView(0, nSeparatorPos
);
265 std::u16string_view aEndPos
266 = sTextData
.subView(nSeparatorPos
+ 1, nSeparator2Pos
- nSeparatorPos
+ 1);
268 if (aStartPos
.empty() || aEndPos
.empty())
271 sal_Int32 nStart
= o3tl::toInt32(aStartPos
);
272 sal_Int32 nEnd
= o3tl::toInt32(aEndPos
);
273 sal_Int32 nStartPara
= 0;
274 sal_Int32 nEndPara
= 0;
277 if (nSeparator2Pos
&& nSeparator3Pos
)
279 std::u16string_view aStartPara
= sTextData
.subView(
280 nSeparator2Pos
+ 1, nSeparator3Pos
- nSeparator2Pos
+ 1);
281 std::u16string_view aEndPara
= sTextData
.subView(nSeparator3Pos
+ 1);
283 if (aStartPara
.empty() || aEndPara
.empty())
286 nStartPara
= o3tl::toInt32(aStartPara
);
287 nEndPara
= o3tl::toInt32(aEndPara
);
290 // pass information about paragraph number in the additional data
291 // handled in sc/source/ui/app/inputwin.cxx
292 Point
* pParaPoint
= new Point(nStartPara
, nEndPara
);
293 const void* pCmdData
= pParaPoint
;
295 Point
aPos(nStart
, nEnd
);
296 CommandEvent
aCEvt(aPos
, CommandEventId::CursorPos
, false, pCmdData
);
297 LOKTrigger::command(*pArea
, aCEvt
);
303 else if (sControlType
== "spinfield")
305 auto pSpinField
= dynamic_cast<weld::SpinButton
*>(pWidget
);
308 if (sAction
== "change" || sAction
== "value")
310 if (rData
["data"] == "undefined")
313 // The Document will not scroll if that is in focus
314 // maybe we could send a message with: sAction == "grab_focus"
315 pWidget
->grab_focus();
317 double nValue
= o3tl::toDouble(rData
["data"]);
318 pSpinField
->set_value(nValue
319 * weld::SpinButton::Power10(pSpinField
->get_digits()));
320 LOKTrigger::trigger_value_changed(*pSpinField
);
323 if (sAction
== "plus")
325 pSpinField
->set_value(pSpinField
->get_value() + 1);
326 LOKTrigger::trigger_value_changed(*pSpinField
);
329 else if (sAction
== "minus")
331 pSpinField
->set_value(pSpinField
->get_value() - 1);
332 LOKTrigger::trigger_value_changed(*pSpinField
);
337 else if (sControlType
== "toolbox")
339 auto pToolbar
= dynamic_cast<weld::Toolbar
*>(pWidget
);
342 if (sAction
== "click")
344 LOKTrigger::trigger_clicked(*pToolbar
, rData
["data"]);
347 else if (sAction
== "togglemenu")
349 const OUString
& sId
= rData
["data"];
350 bool bIsActive
= pToolbar
->get_menu_item_active(sId
);
351 pToolbar
->set_menu_item_active(sId
, !bIsActive
);
354 else if (sAction
== "closemenu")
356 pToolbar
->set_menu_item_active(rData
["data"], false);
359 else if (sAction
== "openmenu")
361 pToolbar
->set_menu_item_active(rData
["data"], true);
366 else if (sControlType
== "edit")
368 auto pEdit
= dynamic_cast<JSEntry
*>(pWidget
);
371 if (sAction
== "change")
373 pEdit
->set_text_without_notify(rData
["data"]);
374 LOKTrigger::trigger_changed(*pEdit
);
379 auto pTextView
= dynamic_cast<JSTextView
*>(pWidget
);
382 if (sAction
== "change")
384 int rStartPos
, rEndPos
;
385 pTextView
->get_selection_bounds(rStartPos
, rEndPos
);
386 pTextView
->set_text_without_notify(rData
["data"]);
387 pTextView
->select_region(rStartPos
, rEndPos
);
388 LOKTrigger::trigger_changed(*pTextView
);
391 else if (sAction
== "textselection")
394 OUString sTextData
= rData
["data"];
395 int nSeparatorPos
= sTextData
.indexOf(';');
396 if (nSeparatorPos
<= 0)
399 std::u16string_view aStartPos
= sTextData
.subView(0, nSeparatorPos
);
400 std::u16string_view aEndPos
= sTextData
.subView(nSeparatorPos
+ 1);
402 if (aStartPos
.empty() || aEndPos
.empty())
405 sal_Int32 nStart
= o3tl::toInt32(aStartPos
);
406 sal_Int32 nEnd
= o3tl::toInt32(aEndPos
);
408 pTextView
->select_region(nStart
, nEnd
);
409 LOKTrigger::trigger_changed(*pTextView
);
415 else if (sControlType
== "treeview")
417 auto pTreeView
= dynamic_cast<JSTreeView
*>(pWidget
);
420 if (sAction
== "change")
422 OUString sDataJSON
= rtl::Uri::decode(
423 rData
["data"], rtl_UriDecodeMechanism::rtl_UriDecodeWithCharset
,
424 RTL_TEXTENCODING_UTF8
);
425 StringMap
aMap(jsonToStringMap(
426 OUStringToOString(sDataJSON
, RTL_TEXTENCODING_ASCII_US
).getStr()));
428 sal_Int32 nRow
= o3tl::toInt32(aMap
["row"]);
429 bool bValue
= aMap
["value"] == "true";
431 pTreeView
->set_toggle(nRow
, bValue
? TRISTATE_TRUE
: TRISTATE_FALSE
);
435 else if (sAction
== "select")
437 sal_Int32 nAbsPos
= o3tl::toInt32(rData
["data"]);
439 pTreeView
->unselect_all();
441 std::unique_ptr
<weld::TreeIter
> itEntry(pTreeView
->make_iterator());
442 pTreeView
->get_iter_abs_pos(*itEntry
, nAbsPos
);
443 pTreeView
->select(*itEntry
);
444 pTreeView
->set_cursor_without_notify(*itEntry
);
445 pTreeView
->grab_focus();
446 LOKTrigger::trigger_changed(*pTreeView
);
449 else if (sAction
== "activate")
451 sal_Int32 nRow
= o3tl::toInt32(rData
["data"]);
453 pTreeView
->unselect_all();
454 std::unique_ptr
<weld::TreeIter
> itEntry(pTreeView
->make_iterator());
455 pTreeView
->get_iter_abs_pos(*itEntry
, nRow
);
456 pTreeView
->select(nRow
);
457 pTreeView
->set_cursor_without_notify(*itEntry
);
458 pTreeView
->grab_focus();
459 LOKTrigger::trigger_changed(*pTreeView
);
460 LOKTrigger::trigger_row_activated(*pTreeView
);
463 else if (sAction
== "expand")
465 sal_Int32 nAbsPos
= o3tl::toInt32(rData
["data"]);
466 std::unique_ptr
<weld::TreeIter
> itEntry(pTreeView
->make_iterator());
467 pTreeView
->get_iter_abs_pos(*itEntry
, nAbsPos
);
468 pTreeView
->set_cursor_without_notify(*itEntry
);
469 pTreeView
->grab_focus();
470 pTreeView
->expand_row(*itEntry
);
473 else if (sAction
== "dragstart")
475 sal_Int32 nRow
= o3tl::toInt32(rData
["data"]);
477 pTreeView
->select(nRow
);
478 pTreeView
->drag_start();
482 else if (sAction
== "dragend")
484 pTreeView
->drag_end();
489 else if (sControlType
== "iconview")
491 auto pIconView
= dynamic_cast<weld::IconView
*>(pWidget
);
494 if (sAction
== "select")
496 sal_Int32 nPos
= o3tl::toInt32(rData
["data"]);
498 pIconView
->select(nPos
);
499 LOKTrigger::trigger_changed(*pIconView
);
503 else if (sAction
== "activate")
505 sal_Int32 nPos
= o3tl::toInt32(rData
["data"]);
507 pIconView
->select(nPos
);
508 LOKTrigger::trigger_changed(*pIconView
);
509 LOKTrigger::trigger_item_activated(*pIconView
);
515 else if (sControlType
== "expander")
517 auto pExpander
= dynamic_cast<weld::Expander
*>(pWidget
);
520 if (sAction
== "toggle")
522 pExpander
->set_expanded(!pExpander
->get_expanded());
527 else if (sControlType
== "dialog")
529 auto pDialog
= dynamic_cast<weld::Dialog
*>(pWidget
);
532 if (sAction
== "close")
534 pDialog
->response(RET_CANCEL
);
537 else if (sAction
== "response")
539 sal_Int32 nResponse
= o3tl::toInt32(rData
["data"]);
540 pDialog
->response(nResponse
);
545 else if (sControlType
== "popover")
547 auto pPopover
= dynamic_cast<weld::Popover
*>(pWidget
);
550 if (sAction
== "close")
552 LOKTrigger::trigger_closed(*pPopover
);
557 else if (sControlType
== "radiobutton")
559 auto pRadioButton
= dynamic_cast<weld::RadioButton
*>(pWidget
);
562 if (sAction
== "change")
564 bool bChecked
= rData
["data"] == "true";
565 pRadioButton
->set_state(bChecked
? TRISTATE_TRUE
: TRISTATE_FALSE
);
566 LOKTrigger::trigger_toggled(*static_cast<weld::Toggleable
*>(pRadioButton
));
571 else if (sControlType
== "scrolledwindow")
573 auto pScrolledWindow
= dynamic_cast<JSScrolledWindow
*>(pWidget
);
576 if (sAction
== "scrollv")
578 sal_Int32 nValue
= o3tl::toInt32(rData
["data"]);
579 pScrolledWindow
->vadjustment_set_value_no_notification(nValue
);
580 LOKTrigger::trigger_scrollv(*pScrolledWindow
);
583 else if (sAction
== "scrollh")
585 sal_Int32 nValue
= o3tl::toInt32(rData
["data"]);
586 pScrolledWindow
->hadjustment_set_value_no_notification(nValue
);
587 LOKTrigger::trigger_scrollh(*pScrolledWindow
);
598 /* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */