library/forms/jsonview.cpp (1,386 lines of code) (raw):
/*
* Copyright (c) 2015, 2019, Oracle and/or its affiliates. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, version 2.0,
* as published by the Free Software Foundation.
*
* This program is designed to work with certain software (including
* but not limited to OpenSSL) that is licensed under separate terms, as
* designated in a particular file or component or in included license
* documentation. The authors of MySQL hereby grant you an additional
* permission to link the program and your derivative works with the
* separately licensed software that they have either included with
* the program or referenced in the documentation.
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
* the GNU General Public License, version 2.0, for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <set>
#include <sstream>
#include <cctype>
#include <future>
#ifdef __APPLE__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wcomma"
#endif
#include <boost/date_time.hpp>
#ifdef __APPLE__
#pragma GCC diagnostic pop
#endif
#include "mforms/jsonview.h"
#include "mforms/panel.h"
#include "mforms/treeview.h"
#include "mforms/code_editor.h"
#include "mforms/tabview.h"
#include "mforms/menubar.h"
#include "mforms/button.h"
#include "mforms/label.h"
#include "mforms/textentry.h"
#include "base/string_utilities.h"
#undef min
#include "rapidjson/stringbuffer.h"
#include "rapidjson/writer.h"
#include "rapidjson/prettywriter.h"
using namespace mforms;
using namespace rapidjson;
namespace ph = std::placeholders;
namespace bt = boost::posix_time;
// JSON Data structures implementation
//--------------------------------------------------------------------------------------------------
// JSON Control Implementation
static void findNode(TreeNodeRef parent, const std::string &text, JsonTreeBaseView::TreeNodeVectorMap &found) {
if (parent.is_valid()) {
auto node = parent;
if (base::contains_string(node->get_string(1), text, false))
found[text].push_back(node);
int count = node->count();
for (int i = 0; i < count; ++i) {
TreeNodeRef child(node->get_child(i));
if (child)
findNode(child, text, found);
}
}
}
//--------------------------------------------------------------------------------------------------
static std::string getParseErrorText(ParseErrorCode code) {
std::string text = "No error.";
switch (code) {
case kParseErrorDocumentEmpty:
text = "The document is empty.";
break;
case kParseErrorDocumentRootNotSingular:
text = "The document root must not follow by other values.";
break;
case kParseErrorValueInvalid:
text = "Invalid value.";
break;
case kParseErrorObjectMissName:
text = "Missing a name for object member.";
break;
case kParseErrorObjectMissColon:
text = "Missing a colon after a name of object member.";
break;
case kParseErrorObjectMissCommaOrCurlyBracket:
text = "Missing a comma or '}' after an object member.";
break;
case kParseErrorArrayMissCommaOrSquareBracket:
text = "Missing a comma or ']' after an array element.";
break;
case kParseErrorStringUnicodeEscapeInvalidHex:
text = "Incorrect hex digit after \\u escape in string.";
break;
case kParseErrorStringUnicodeSurrogateInvalid:
text = "The surrogate pair in string is invalid.";
break;
case kParseErrorStringEscapeInvalid:
text = "Invalid escape character in string.";
break;
case kParseErrorStringMissQuotationMark:
text = "Missing a closing quotation mark in string.";
break;
case kParseErrorStringInvalidEncoding:
text = "Invalid encoding in string.";
break;
case kParseErrorNumberTooBig:
text = "Number too big to be stored in double.";
break;
case kParseErrorNumberMissFraction:
text = "Miss fraction part in number.";
break;
case kParseErrorNumberMissExponent:
text = "Miss exponent in number.";
break;
case kParseErrorTermination:
text = "Parsing was terminated.";
break;
case kParseErrorUnspecificSyntaxError:
text = "Unspecific syntax error.";
break;
case kParseErrorNone:
default:
text = "No error";
}
return text;
}
//--------------------------------------------------------------------------------------------------
JsonInputDlg::JsonInputDlg(mforms::Form *owner, bool showTextEntry)
: mforms::Form(owner, mforms::FormResizable),
_textEditor(manage(new CodeEditor())),
_save(nullptr),
_cancel(nullptr),
_textEntry(nullptr),
_validated(false) {
setup(showTextEntry);
}
//--------------------------------------------------------------------------------------------------
JsonInputDlg::~JsonInputDlg() {
}
//--------------------------------------------------------------------------------------------------
std::string JsonInputDlg::objectName() const {
return (_textEntry != NULL) ? _textEntry->get_string_value() : "";
}
//--------------------------------------------------------------------------------------------------
const std::string &JsonInputDlg::text() const {
return _text;
}
//--------------------------------------------------------------------------------------------------
const rapidjson::Value &JsonInputDlg::data() const {
return _value;
}
//--------------------------------------------------------------------------------------------------
bool JsonInputDlg::run() {
return run_modal(NULL, _cancel);
}
//--------------------------------------------------------------------------------------------------
void JsonInputDlg::setup(bool showTextEntry) {
Box *box = manage(new Box(false));
Box *hbox = manage(new Box(true));
Button *check = manage(new Button());
if (showTextEntry) {
Box *textEntryBox = manage(new Box(true));
textEntryBox->set_padding(12);
textEntryBox->set_spacing(12);
Label *nameDescription = manage(new Label("Object name:"));
_textEntry = manage(new TextEntry());
textEntryBox->add(nameDescription, false, false);
textEntryBox->add(_textEntry, true, true);
box->add(textEntryBox, false, true);
}
_cancel = manage(new Button());
_save = manage(new Button());
set_title("JSON Editor");
set_content(box);
box->set_padding(12);
box->set_spacing(12);
_textEditor->set_language(mforms::LanguageJson);
_textEditor->set_features(mforms::FeatureWrapText, false);
_textEditor->set_features(mforms::FeatureReadOnly, false);
box->add(_textEditor, true, true);
box->add(hbox, false, true);
hbox->add_end(_cancel, false, true);
hbox->add_end(_save, false, true);
hbox->add_end(check, false, true);
hbox->set_spacing(12);
check->set_text("Validate");
_save->set_text("Save");
_save->set_enabled(false);
_cancel->set_text("Cancel");
scoped_connect(check->signal_clicked(), std::bind(&JsonInputDlg::validate, this));
scoped_connect(_save->signal_clicked(), std::bind(&JsonInputDlg::save, this));
scoped_connect(_textEditor->signal_changed(),
std::bind(&JsonInputDlg::editorContentChanged, this, std::placeholders::_1, std::placeholders::_2,
std::placeholders::_3, std::placeholders::_4));
set_size(800, 500);
center();
}
//--------------------------------------------------------------------------------------------------
void JsonInputDlg::save() {
if (_textEntry) {
auto text = _textEntry->get_string_value();
if (text.empty() && _textEntry->is_enabled()) {
mforms::Utilities::show_error("JSON Editor.", "The field 'name' can not be empty", "Ok");
return;
}
}
end_modal(true);
}
//--------------------------------------------------------------------------------------------------
void JsonInputDlg::setText(const std::string &text, bool readonly) {
if (_textEntry) {
_textEntry->set_value(text);
_textEntry->set_enabled(!readonly);
}
}
//--------------------------------------------------------------------------------------------------
void JsonInputDlg::setJson(const Value &json) {
Document d;
d.CopyFrom(json, d.GetAllocator());
StringBuffer buffer;
Writer<StringBuffer> writer(buffer);
d.Accept(writer);
_textEditor->set_text(buffer.GetString());
}
//--------------------------------------------------------------------------------------------------
void JsonInputDlg::validate() {
auto text = _textEditor->get_text(false);
if (text.empty())
return;
_document.Parse(text);
_save->set_enabled(true);
_validated = true;
_value.CopyFrom(_document, _document.GetAllocator());
_text = _textEditor->get_string_value();
if (_document.HasParseError())
mforms::Utilities::show_error(
"JSON check.", base::strfmt("Validation failed: '%s'", getParseErrorText(_document.GetParseError()).c_str()), "Ok");
}
//--------------------------------------------------------------------------------------------------
void JsonInputDlg::editorContentChanged(Sci_Position /*position*/, Sci_Position /*length*/,
Sci_Position /*numberOfLines*/, bool /*inserted*/) {
_save->set_enabled(false);
_validated = false;
_text = "";
_value = Value();
}
//--------------------------------------------------------------------------------------------------
JsonBaseView::JsonBaseView(Document &doc) : Panel(TransparentPanel), _document(doc) {
}
//--------------------------------------------------------------------------------------------------
bool JsonBaseView::isDateTime(const std::string &text) {
static std::string validChars = "0123456789-.: ";
if (text.find_first_not_of(validChars) != std::string::npos)
return false;
bt::time_input_facet *isoFacet = new bt::time_input_facet();
isoFacet->set_iso_format();
bt::time_input_facet *extendedIsoFacet = new bt::time_input_facet();
isoFacet->set_iso_extended_format();
static const std::locale formats[] = {
std::locale(std::locale::classic(), isoFacet),
std::locale(std::locale::classic(), extendedIsoFacet),
std::locale(std::locale::classic(), new bt::time_input_facet("%Y-%m-%d %H:%M:%S")),
std::locale(std::locale::classic(), new bt::time_input_facet("%Y/%m/%d %H:%M:%S")),
std::locale(std::locale::classic(), new bt::time_input_facet("%d.%m.%Y %H:%M:%S")),
std::locale(std::locale::classic(), new bt::time_input_facet("%Y-%m-%d"))
};
static const size_t formatCounts = sizeof(formats) / sizeof(formats[0]);
bt::ptime pt;
bool ret = false;
for (size_t i = 0; i < formatCounts; ++i) {
std::istringstream is(text);
is.imbue(formats[i]);
is >> pt;
if (pt != bt::ptime()) {
ret = true;
break;
}
}
return ret;
}
//--------------------------------------------------------------------------------------------------
JsonBaseView::~JsonBaseView() {
}
//--------------------------------------------------------------------------------------------------
boost::signals2::signal<void(bool)> *JsonBaseView::dataChanged() {
return &_dataChanged;
}
//--------------------------------------------------------------------------------------------------
void JsonBaseView::clear() {
}
//--------------------------------------------------------------------------------------------------
JsonTreeBaseView::JsonTreeBaseView(rapidjson::Document &doc) : JsonBaseView(doc), _useFilter(false), _searchIdx(0) {
_contextMenu = mforms::manage(new mforms::ContextMenu());
_contextMenu->signal_will_show()->connect(std::bind(&JsonTreeBaseView::prepareMenu, this));
}
//--------------------------------------------------------------------------------------------------
void JsonTreeBaseView::prepareMenu() {
if (_contextMenu) {
_contextMenu->remove_all();
auto node = _treeView->get_selected_node();
if (!node.is_valid())
return;
auto *data = dynamic_cast<JsonValueNodeData *>(node->get_data());
if (data != NULL) {
auto &jv = data->getData();
bool showAddModify = true;
if (!jv.IsObject() && !jv.IsArray())
showAddModify = false;
auto *item = mforms::manage(new mforms::MenuItem("Add new value"));
item->set_name("Add New Document");
item->setInternalName("add_new_doc");
item->signal_clicked()->connect(std::bind(&JsonTreeBaseView::handleMenuCommand, this, item->getInternalName()));
item->set_enabled(showAddModify);
_contextMenu->add_item(item);
item = mforms::manage(new mforms::MenuItem("Delete JSON"));
item->set_name("Delete Document");
item->setInternalName("delete_doc");
item->signal_clicked()->connect(std::bind(&JsonTreeBaseView::handleMenuCommand, this, item->getInternalName()));
_contextMenu->add_item(item);
item = mforms::manage(new mforms::MenuItem("Modify JSON"));
item->set_name("Modify Document");
item->setInternalName("modify_doc");
item->signal_clicked()->connect(std::bind(&JsonTreeBaseView::handleMenuCommand, this, item->getInternalName()));
item->set_enabled(showAddModify);
_contextMenu->add_item(item);
}
}
}
//--------------------------------------------------------------------------------------------------
void JsonTreeBaseView::handleMenuCommand(const std::string &command) {
auto node = _treeView->get_selected_node();
if (command == "add_new_doc") {
openInputJsonWindow(node);
return;
}
if (command == "delete_doc") {
auto data = dynamic_cast<JsonValueNodeData *>(node->get_data());
if (data != nullptr) {
auto &jv = data->getData();
auto parent = node->get_parent();
if (parent != nullptr) {
auto parentData = dynamic_cast<JsonValueNodeData *>(parent->get_data());
if (parentData != nullptr) {
auto &value = parentData->getData();
if (value.IsArray()) {
for (auto &item : value.GetArray()) {
if (item == jv) {
value.Erase(&item);
break;
}
}
} else if (value.IsObject()) {
for (auto it = value.MemberBegin(); it != value.MemberEnd(); ++it) {
if (it->value == jv) {
value.RemoveMember(it);
break;
}
}
}
}
}
node->set_data(nullptr); // This will explicitly delete the data.
}
node->remove_from_parent();
_dataChanged(false);
return;
}
if (command == "modify_doc") {
openInputJsonWindow(node, true);
}
}
//--------------------------------------------------------------------------------------------------
void JsonTreeBaseView::openInputJsonWindow(TreeNodeRef node, bool updateMode /*= false*/) {
auto data = dynamic_cast<JsonValueNodeData *>(node->get_data());
if (data != nullptr) {
auto &jv = data->getData();
JsonInputDlg dlg(_treeView->get_parent_form(), jv.IsObject());
if (updateMode) {
if (jv.IsObject()) {
auto tag = node->get_tag();
dlg.setText(tag, true);
}
dlg.setJson(jv);
}
if (dlg.run()) {
Value value;
value.CopyFrom(dlg.data(), _document.GetAllocator());
auto objectName = dlg.objectName();
switch (jv.GetType()) {
case kObjectType: {
jv.AddMember(Value(objectName, _document.GetAllocator()), value, _document.GetAllocator());
if (updateMode) {
node->remove_children();
}
auto newNode = (updateMode) ? node : node->add_child();
generateTree(objectName.empty() ? jv : jv[objectName], 0, newNode);
newNode->set_string(0, objectName + "{" + std::to_string(jv.MemberCount()) + "}");
newNode->set_tag(objectName);
_dataChanged(false);
break;
}
case kArrayType: {
if (updateMode) {
jv.Clear();
node->remove_children();
jv.CopyFrom(value, _document.GetAllocator());
} else {
jv.PushBack(value, _document.GetAllocator());
}
auto newNode = (updateMode) ? node : node->add_child();
generateTree((updateMode) ? jv : *(jv.End()-1), 0, newNode);
newNode->set_string(0, objectName + "[" + std::to_string(jv.Size()) + "]");
_dataChanged(false);
break;
}
default:
break;
}
}
}
}
//--------------------------------------------------------------------------------------------------
JsonTreeBaseView::~JsonTreeBaseView() {
}
//--------------------------------------------------------------------------------------------------
void JsonTreeBaseView::generateStringInTree(rapidjson::Value &value, int columnId, TreeNodeRef node) {
auto text = value.GetString();
setStringData(columnId, node, text);
node->set_data(new JsonTreeBaseView::JsonValueNodeData(value));
node->expand();
}
//--------------------------------------------------------------------------------------------------
void JsonTreeBaseView::highlightMatchNode(const std::string &text, bool backward) {
if (_textToFind != text) {
_textToFind = text;
_searchIdx = 0;
}
bool needSearch = false;
auto it = _viewFindResult.find(text);
if (it != _viewFindResult.end()) {
if (_searchIdx >= it->second.size())
_searchIdx = 0;
auto node = it->second[_searchIdx];
if (base::contains_string(node->get_string(1), text, false)) {
_treeView->select_node(node);
_treeView->scrollToNode(node);
_searchIdx++;
} else {
_viewFindResult.erase(text);
needSearch = true;
}
} else
needSearch = true;
if (needSearch) {
_searchIdx = 0;
auto node = _treeView->get_selected_node();
if (!node.is_valid())
node = _treeView->root_node();
findNode(node, text, _viewFindResult);
auto it = _viewFindResult.find(text);
if (it != _viewFindResult.end()) {
auto node = it->second[_searchIdx];
_treeView->select_node(node);
_treeView->scrollToNode(node);
_treeView->focus();
}
}
}
//--------------------------------------------------------------------------------------------------
void JsonTreeBaseView::collectParents(TreeNodeRef node, std::list<TreeNodeRef> &parents) {
auto parent = node->get_parent();
if (parent->is_valid()) {
parents.push_back(parent);
collectParents(parent, parents);
}
}
//--------------------------------------------------------------------------------------------------
void JsonTreeBaseView::reCreateTree(Value &value) {
_useFilter = false;
_treeView->clear();
auto node = _treeView->root_node()->add_child();
_treeView->BeginUpdate();
Value o(kObjectType);
o.CopyFrom(value, _document.GetAllocator());
generateTree(value, 0, node);
_treeView->EndUpdate();
}
//--------------------------------------------------------------------------------------------------
bool JsonTreeBaseView::filterView(const std::string &text, rapidjson::Value &value) {
auto selectedNode = _treeView->get_selected_node();
if (!selectedNode.is_valid())
selectedNode = _treeView->root_node();
TreeNodeVectorMap viewFilterResult;
findNode(selectedNode, text, viewFilterResult);
auto it = viewFilterResult.find(text);
if (it != viewFilterResult.end()) {
std::shared_ptr<TreeNodeList> branch(new TreeNodeList);
for (auto node : it->second) {
branch->push_back(node);
collectParents(node, *branch);
}
_filterGuard.clear();
auto actualNode = _treeView->root_node();
while (!branch->empty()) {
auto node = branch->back();
branch->pop_back();
auto data = dynamic_cast<JsonValueNodeData *>(node->get_data());
if (data != nullptr) {
auto &jv = data->getData();
if (_filterGuard.count(&jv))
continue;
_filterGuard.insert(&jv);
}
}
_useFilter = true;
_treeView->clear();
generateTree(value, 0, _treeView->root_node());
}
return _useFilter;
}
//--------------------------------------------------------------------------------------------------
void JsonTreeBaseView::generateTree(rapidjson::Value &value, int columnId, TreeNodeRef node, bool addNew) {
switch (value.GetType()) {
case kNumberType:
generateNumberInTree(value, columnId, node);
break;
case kFalseType:
case kTrueType:
generateBoolInTree(value, columnId, node);
break;
case kStringType:
generateStringInTree(value, columnId, node);
break;
case kObjectType:
generateObjectInTree(value, columnId, node, addNew);
break;
case kArrayType:
generateArrayInTree(value, columnId, node);
break;
case kNullType:
generateNullInTree(value, columnId, node);
break;
default:
break;
}
}
//--------------------------------------------------------------------------------------------------
void JsonTreeBaseView::setCellValue(mforms::TreeNodeRef node, int column, const std::string &value) {
auto data = dynamic_cast<JsonValueNodeData *>(node->get_data());
bool setData = false;
if (data != nullptr) {
std::stringstream buffer;
double number = 0;
auto &storedValue = data->getData();
switch (storedValue.GetType()) {
case kNumberType:
if (!base::is_number(value))
break;
buffer << value;
buffer >> number;
storedValue = Value(number).Move();
setData = true;
break;
case kTrueType:
storedValue = Value(true).Move();
setData = true;
break;
case kFalseType:
storedValue = Value(false).Move();
setData = true;
break;
case kStringType:
storedValue = Value(value, _document.GetAllocator()).Move();
setStringData(column, node, value);
setData = true;
break;
default:
break;
}
}
if (setData) {
node->set_string(column, value);
_dataChanged(false);
}
}
//--------------------------------------------------------------------------------------------------
JsonTextView::JsonTextView(Document &doc)
: JsonBaseView(doc), _textEditor(manage(new CodeEditor())), _modified(false), _position(0) {
init();
}
//--------------------------------------------------------------------------------------------------
void JsonTextView::setText(const std::string &jsonText, bool validateJson /*= true*/) {
_textEditor->set_value(jsonText.c_str());
if (validateJson)
validate();
_text = jsonText;
}
//--------------------------------------------------------------------------------------------------
const Value &JsonTextView::getJson() const {
return _json;
}
//--------------------------------------------------------------------------------------------------
const std::string &JsonTextView::getText() const {
return _text;
}
//--------------------------------------------------------------------------------------------------
JsonTextView::~JsonTextView() {
}
//--------------------------------------------------------------------------------------------------
void JsonTextView::clear() {
_textEditor->set_value("");
}
//--------------------------------------------------------------------------------------------------
void JsonTextView::init() {
assert(_textEditor != NULL);
_textEditor->set_language(mforms::LanguageJson);
_textEditor->set_features(mforms::FeatureWrapText, false);
_textEditor->set_features(mforms::FeatureReadOnly, false);
scoped_connect(_textEditor->signal_changed(),
[this](Sci_Position position, Sci_Position length, Sci_Position numberOfLines, bool inserted) {
editorContentChanged(position, length, numberOfLines, inserted);
});
scoped_connect(_textEditor->signal_dwell(),
[this](bool started, size_t position, int x, int y) { dwellEvent(started, position, x, y); });
Box *box = manage(new Box(false));
box->set_padding(5);
box->set_spacing(5);
box->add(_textEditor, true, true);
add(box);
}
//--------------------------------------------------------------------------------------------------
void JsonTextView::editorContentChanged(Sci_Position position, Sci_Position length, Sci_Position numberOfLines,
bool inserted) {
if (_stopTextProcessing)
_stopTextProcessing();
_modified = true;
_position = position;
_text = _textEditor->get_text(false);
if (_startTextProcessing) {
_startTextProcessing([this]() -> bool {
_dataChanged(true);
return false;
});
} else
_dataChanged(true);
}
//--------------------------------------------------------------------------------------------------
bool JsonTextView::validate() {
bool ret = true;
if (_modified) {
std::future<std::string> validateFuture = std::async(std::launch::async, [&, this]() -> std::string {
_document.Parse(_text);
if (_document.HasParseError()) {
return getParseErrorText(_document.GetParseError());
} else {
_json.CopyFrom(_document, _document.GetAllocator());
}
return "";
});
validateFuture.wait();
auto text = validateFuture.get();
if (text.empty()) {
_textEditor->remove_markup(LineMarkupAll, -1);
_textEditor->remove_indicator(mforms::RangeIndicatorError, 0, _textEditor->text_length());
_errorEntry.clear();
_modified = false;
ret = true;
} else {
int line = (int)_textEditor->line_from_position(_position);
_textEditor->show_markup(LineMarkupError, line);
std::size_t posBegin = _textEditor->position_from_line(line);
posBegin = _text.find_first_not_of(" \t\r\n", posBegin);
std::size_t posEnd = _text.find_first_of("\n\r", posBegin + 1);
_textEditor->show_indicator(mforms::RangeIndicatorError, posBegin, posEnd - posBegin);
_errorEntry.push_back(JsonErrorEntry{ text, posBegin, posEnd - posBegin });
ret = false;
}
}
return ret;
}
//--------------------------------------------------------------------------------------------------
void JsonTextView::dwellEvent(bool started, size_t position, int x, int y) {
if (started) {
if (_textEditor->indicator_at(position) == mforms::RangeIndicatorError) {
auto end = _errorEntry.cend();
auto it = std::find_if(_errorEntry.cbegin(), end, [&](const JsonErrorEntry &value) {
return value.pos <= position && position <= value.pos + value.length;
});
if (it != end)
_textEditor->show_calltip(true, position, it->text);
}
} else
_textEditor->show_calltip(false, 0, "");
}
//--------------------------------------------------------------------------------------------------
void JsonTextView::findAndHighlightText(const std::string &text, bool backward /*= false*/) {
_textEditor->find_and_highlight_text(text, mforms::FindDefault, true, backward);
}
//--------------------------------------------------------------------------------------------------
JsonTreeView::JsonTreeView(Document &doc) : JsonTreeBaseView(doc) {
_treeView = manage(new mforms::TreeView(mforms::TreeAltRowColors | mforms::TreeShowRowLines |
mforms::TreeShowColumnLines | mforms::TreeNoBorder));
_treeView->add_column(IconStringColumnType, "Key", 150, false, true);
_treeView->add_column(StringLTColumnType, "Value", 200, true, true);
_treeView->add_column(StringLTColumnType, "Type", 200, false, true);
_treeView->end_columns();
_treeView->set_cell_edit_handler(std::bind(&JsonTreeBaseView::setCellValue, this, ph::_1, ph::_2, ph::_3));
_treeView->set_selection_mode(TreeSelectSingle);
_treeView->set_context_menu(_contextMenu);
init();
}
//--------------------------------------------------------------------------------------------------
void JsonTreeView::init() {
assert(_treeView != nullptr);
add(_treeView);
}
//--------------------------------------------------------------------------------------------------
JsonTreeView::~JsonTreeView() {
_treeView->clear();
}
//--------------------------------------------------------------------------------------------------
void JsonTreeView::clear() {
_treeView->clear();
_viewFindResult.clear();
_textToFind = "";
_searchIdx = 0;
_useFilter = false;
}
//--------------------------------------------------------------------------------------------------
void JsonTreeView::setJson(rapidjson::Value &value) {
clear();
auto node = _treeView->root_node()->add_child();
generateTree(value, 0, node);
}
//--------------------------------------------------------------------------------------------------
void JsonTreeView::appendJson(rapidjson::Value &value) {
TreeNodeRef node = _treeView->root_node();
_viewFindResult.clear();
_textToFind = "";
_searchIdx = 0;
generateTree(value, 0, node);
}
//--------------------------------------------------------------------------------------------------
void JsonTreeView::generateObjectInTree(rapidjson::Value &value, int /*columnId*/, TreeNodeRef node, bool addNew) {
if (_useFilter && _filterGuard.count(&value) == 0)
return;
size_t size = 0;
node->set_data(new JsonTreeBaseView::JsonValueNodeData(value));
for (auto it = value.MemberBegin(); it != value.MemberEnd(); ++it) {
std::string text = it->name.GetString();
std::stringstream textSize;
switch (it->value.GetType()) {
case kArrayType: {
auto &arrayVal = it->value;
size = arrayVal.Size();
node->set_tag(text);
textSize << size;
text += "[";
text += textSize.str();
text += "]";
break;
}
case kObjectType: {
auto &objectVal = it->value;
size = objectVal.MemberCount();
textSize << size;
text += "{";
text += textSize.str();
text += "}";
break;
}
default:
break;
}
auto node2 = (addNew) ? node->add_child() : node;
if (addNew) {
node->set_icon_path(0, "JS_Datatype_Object.png");
std::string name = node->get_string(0);
if (name.empty())
node->set_string(0, "<unnamed>");
node->set_string(1, "");
node->set_string(2, "Object");
}
node2->set_string(0, text);
node2->set_tag(text);
generateTree(it->value, 1, node2);
node2->expand();
}
}
//--------------------------------------------------------------------------------------------------
void JsonTreeView::generateArrayInTree(rapidjson::Value &value, int /*columnId*/, TreeNodeRef node) {
if (_useFilter && _filterGuard.count(&value) == 0)
return;
node->set_icon_path(0, "JS_Datatype_Array.png");
std::string name = node->get_string(0);
if (name.empty())
node->set_string(0, "<unnamed>");
node->set_string(1, "");
node->set_string(2, "Array");
std::string tagName = node->get_tag();
node->set_data(new JsonTreeBaseView::JsonValueNodeData(value));
int index = 0;
for (auto &v : value.GetArray()) {
if (_useFilter && _filterGuard.count(&v) == 0)
continue;
auto arrrayNode = node->add_child();
bool addNew = false;
if (v.GetType() == kArrayType || v.GetType() == kObjectType)
addNew = true;
std::string keyName = tagName.empty() ? "key[%d]" : tagName + "[%d]";
arrrayNode->set_string(0, base::strfmt(keyName.c_str(), index));
arrrayNode->set_string(1, "");
generateTree(v, 1, arrrayNode, addNew);
index++;
}
node->expand();
}
//--------------------------------------------------------------------------------------------------
void JsonTreeView::generateBoolInTree(rapidjson::Value &value, int /*columnId*/, TreeNodeRef node) {
node->set_icon_path(0, "JS_Datatype_Bool.png");
node->set_attributes(1, mforms::TextAttributes("#4b4a4c", false, false));
node->set_bool(1, value.GetBool());
node->set_string(2, "Boolean");
node->set_data(new JsonTreeBaseView::JsonValueNodeData(value));
node->expand();
}
//--------------------------------------------------------------------------------------------------
void JsonTreeView::generateNumberInTree(rapidjson::Value &value, int /*columnId*/, TreeNodeRef node) {
node->set_icon_path(0, "JS_Datatype_Number.png");
node->set_attributes(1, mforms::TextAttributes("#4b4a4c", false, false));
if (value.IsDouble()) {
node->set_string(1, std::to_string(value.GetDouble()));
node->set_string(2, "Double");
} else if (value.IsInt64()) {
node->set_string(1, std::to_string(value.GetInt64()));
node->set_string(2, "Long Integer");
} else if (value.IsUint64()) {
node->set_string(1, std::to_string(value.GetUint64()));
node->set_string(2, "Unsigned Long Integer");
}
node->set_data(new JsonTreeBaseView::JsonValueNodeData(value));
node->expand();
}
//--------------------------------------------------------------------------------------------------
void JsonTreeView::generateNullInTree(rapidjson::Value &value, int /*columnId*/, TreeNodeRef node) {
node->set_icon_path(0, "JS_Datatype_Null.png");
node->set_string(0, "null");
node->set_string(1, "");
node->set_string(2, "Null");
node->set_data(new JsonTreeBaseView::JsonValueNodeData(value));
node->expand();
}
//--------------------------------------------------------------------------------------------------
void JsonTreeView::setStringData(int /*columnId*/, TreeNodeRef node, const std::string &text) {
/*if (isDateTime(text))
{
node->set_icon_path(0, "JS_Datatype_Date.png");
node->set_string(2, "Date/Time");
}
else*/
{
node->set_icon_path(0, "JS_Datatype_String.png");
node->set_string(2, "String");
}
node->set_attributes(1, mforms::TextAttributes("#4b4a4c", false, false));
node->set_string(1, text.c_str());
}
//--------------------------------------------------------------------------------------------------
JsonGridView::JsonGridView(Document &doc)
: JsonTreeBaseView(doc),
_level(0),
_headerAdded(false),
_noNameColId(-1),
_columnIndex(0),
_rowNum(1),
_actualParent(20) {
init();
}
//--------------------------------------------------------------------------------------------------
/**
* @brief Init flat grid view
*
* Based of readed JSON data control function initialize mforms control TreNodeView
*/
void JsonGridView::init() {
_treeView = manage(new mforms::TreeView(mforms::TreeAltRowColors | mforms::TreeShowRowLines |
mforms::TreeShowColumnLines | mforms::TreeNoBorder));
assert(_treeView != nullptr);
_treeView->add_column(StringLTColumnType, "", 30, false, false);
_treeView->set_cell_edit_handler(
std::bind(&JsonGridView::setCellValue, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
_treeView->signal_node_activated()->connect(
std::bind(&JsonGridView::nodeActivated, this, std::placeholders::_1, std::placeholders::_2));
_treeView->set_selection_mode(TreeSelectSingle);
_treeView->set_context_menu(_contextMenu);
_goUpButton = manage(new Button());
_goUpButton->set_text("Back <<<");
_goUpButton->set_enabled(false);
scoped_connect(_goUpButton->signal_clicked(), std::bind(&JsonGridView::goUp, this));
_content = manage(new Box(false));
_content->add(_treeView, true, true);
Box *hbox = manage(new Box(true));
hbox->add_end(_goUpButton, false, false);
hbox->set_size(-1, 30);
_content->add(hbox, false, false);
add(_content);
}
//--------------------------------------------------------------------------------------------------
void JsonGridView::goUp() {
if (_level <= 0 || _actualParent.empty())
return;
rapidjson::Value *value = _actualParent.at(_level - 1);
if (value == NULL)
return;
setJson(*value);
if (--_level <= 0)
_goUpButton->set_enabled(false);
}
//--------------------------------------------------------------------------------------------------
JsonGridView::~JsonGridView() {
_treeView->clear();
}
//--------------------------------------------------------------------------------------------------
void JsonGridView::clear() {
_treeView->clear();
_viewFindResult.clear();
_textToFind = "";
_searchIdx = 0;
_useFilter = false;
}
//--------------------------------------------------------------------------------------------------
void JsonGridView::setJson(rapidjson::Value &value) {
clear();
_rowNum = 1;
if (!_headerAdded) {
_columnIndex = 1;
_level = 0;
_noNameColId = -1;
generateColumnNames(value);
_treeView->end_columns();
_headerAdded = true;
}
if (_level >= (int)_actualParent.size())
_actualParent.resize(_actualParent.size() * 2);
_actualParent[_level] = &value;
TreeNodeRef node = _treeView->root_node();
generateTree(value, 0, node);
}
//--------------------------------------------------------------------------------------------------
void JsonGridView::reCreateTree(rapidjson::Value &value) {
remove(_content);
init();
_headerAdded = false;
_colNameToColId.clear();
setJson(value);
}
//--------------------------------------------------------------------------------------------------
void JsonGridView::addColumn(int size, Type type, Value *value, const std::string &name) {
switch (type) {
case kArrayType:
case kObjectType:
_treeView->add_column(IconStringColumnType, name, size, false, true);
break;
case kNumberType: {
if (value != nullptr && (value->IsFloat() || value->IsDouble())) {
_treeView->add_column(FloatColumnType, name, size, true, true);
} else {
_treeView->add_column(LongIntegerColumnType, name, size, true, true);
}
break;
}
case kTrueType:
case kFalseType:
case kStringType:
case kNullType:
default:
_treeView->add_column(IconStringColumnType, name, size, true, true);
break;
}
}
//--------------------------------------------------------------------------------------------------
void JsonGridView::generateColumnNames(rapidjson::Value &value) {
if (_level != 0)
return;
switch (value.GetType()) {
case kObjectType: {
for (auto it = value.MemberBegin(); it != value.MemberEnd(); ++it) {
if (_colNameToColId.count(it->name.GetString()) == 1)
continue;
addColumn(100, it->value.GetType(), &value, it->name.GetString());
_colNameToColId[it->name.GetString()] = _columnIndex++;
if (it->value.GetType() == kObjectType || it->value.GetType() == kArrayType)
generateColumnNames(it->value);
}
break;
}
case kArrayType: {
for (auto &item : value.GetArray()) {
if (item.GetType() == kObjectType) {
Value &obj = item;
for (auto it = obj.MemberBegin(); it != obj.MemberEnd(); ++it) {
if (_colNameToColId.count(it->name.GetString()) == 1)
continue;
addColumn(100, it->value.GetType(), &value, it->name.GetString());
_colNameToColId[it->name.GetString()] = _columnIndex++;
if (it->value.GetType() == kObjectType || it->value.GetType() == kArrayType)
generateColumnNames(it->value);
}
} else {
if (_noNameColId > 0)
continue;
addColumn(100, kStringType, nullptr, "");
_noNameColId = _columnIndex++;
}
if (item.GetType() == kObjectType || item.GetType() == kArrayType)
generateColumnNames(item);
}
break;
}
default:
break;
}
}
//--------------------------------------------------------------------------------------------------
void JsonGridView::setCellValue(mforms::TreeNodeRef node, int column, const std::string &value) {
JsonValueNodeData *data = dynamic_cast<JsonValueNodeData *>(node->get_data());
if (data == NULL)
return;
const std::map<std::string, int>::const_iterator it =
std::find_if(_colNameToColId.begin(), _colNameToColId.end(),
[&column](const std::pair<std::string, int> &elem) { return column == elem.second; });
if (it == _colNameToColId.end() || it->first.empty())
return;
std::string key = it->first;
rapidjson::Value &valData = data->getData();
if (valData.FindMember(it->first) == valData.MemberEnd())
return;
rapidjson::Value &storedValue = valData[key];
if (data != NULL) {
std::stringstream buffer;
double number = 0;
int64_t number2 = 0;
uint64_t number3 = 0;
long number4 = 0;
bool retBool = false;
switch (storedValue.GetType()) {
case kNumberType: {
if (!base::is_number(value))
break;
if (storedValue.IsDouble()) {
buffer << value;
buffer >> number;
storedValue = number;
node->set_float(column, number);
} else if (storedValue.IsInt64() || storedValue.IsInt()) {
buffer << value;
buffer >> number2;
storedValue.SetInt64(number2);
node->set_long(column, number2);
} else if (storedValue.IsUint64()) {
buffer << value;
buffer >> number3;
storedValue.SetUint64(number3);
node->set_float(column, (double)number3);
} else {
buffer << value;
buffer >> number4;
storedValue.SetInt(number4);
node->set_long(column, number4);
}
break;
}
case kTrueType:
case kFalseType:
if (!base::isBool(value))
break;
buffer << value;
buffer >> std::boolalpha >> retBool;
storedValue.SetBool(retBool);
node->set_bool(column, retBool);
_dataChanged(false);
break;
case kStringType:
storedValue.SetString(value, _document.GetAllocator());
setStringData(column, node, value);
node->set_string(column, value);
_dataChanged(false);
break;
default:
break;
}
}
}
//--------------------------------------------------------------------------------------------------
void JsonGridView::openInputJsonWindow(rapidjson::Value &value) {
JsonInputDlg dlg(_treeView->get_parent_form(), false);
dlg.setJson(value);
if (dlg.run()) {
value.CopyFrom(dlg.data(), _document.GetAllocator());
_actualParent[_level] = &value;
reCreateTree(*_actualParent.at(0));
setJson(*_actualParent.at(_level));
_dataChanged(false);
}
}
//--------------------------------------------------------------------------------------------------
void JsonGridView::handleMenuCommand(const std::string &command) {
rapidjson::Value *parent = _actualParent.at(_level);
if (parent == nullptr)
return;
if (command == "add_new_doc" || command == "modify_doc") {
openInputJsonWindow(*parent);
return;
}
if (command == "delete_doc") {
TreeNodeRef node = _treeView->get_selected_node();
if (!node.is_valid())
return;
auto *data = dynamic_cast<JsonValueNodeData *>(node->get_data());
if (data != nullptr) {
rapidjson::Value &jv = data->getData();
if (parent->IsArray()) {
for (auto &item : parent->GetArray()) {
if (item == jv) {
parent->Erase(&item);
break;
}
}
} else if (parent->IsObject()) {
parent->RemoveAllMembers();
}
node->set_data(nullptr); // This will explicitly delete the data.
}
node->remove_from_parent();
_dataChanged(false);
}
}
//--------------------------------------------------------------------------------------------------
void JsonGridView::appendJson(rapidjson::Value & /*value*/) {
}
//--------------------------------------------------------------------------------------------------
void JsonGridView::generateObjectInTree(rapidjson::Value &value, int columnId, TreeNodeRef node, bool addNew) {
auto child = node;
if (addNew)
child = node->add_child();
size_t size = 0;
std::stringstream textSize;
child->set_data(new JsonTreeBaseView::JsonValueNodeData(value));
node->set_string(0, std::to_string(_rowNum++));
for (auto it = value.MemberBegin(); it != value.MemberEnd(); ++it) {
std::string text = it->name.GetString();
if (_colNameToColId.count(text) == 0)
continue;
int index = _colNameToColId[text];
switch (it->value.GetType()) {
case kArrayType: {
auto const& arrayVal = it->value.GetArray();
size = arrayVal.Size();
textSize << size;
text = "Array [";
text += textSize.str();
text += "]";
child->set_icon_path(index, "JS_Datatype_Array.png");
child->set_string(index, text);
break;
}
case kObjectType: {
auto &objectVal = it->value;
size = objectVal.MemberCount();
textSize << size;
text = "Object {";
text += textSize.str();
text += "}";
child->set_icon_path(index, "JS_Datatype_Object.png");
child->set_string(index, text);
break;
}
case kNumberType:
generateNumberInTree(it->value, index, child);
break;
case kTrueType:
case kFalseType:
generateBoolInTree(it->value, index, child);
break;
case kStringType:
setStringData(index, child, it->value.GetString());
break;
case kNullType:
generateNullInTree(it->value, index, child);
break;
default:
break;
}
}
}
//--------------------------------------------------------------------------------------------------
void JsonGridView::generateArrayInTree(rapidjson::Value &value, int /*columnId*/, TreeNodeRef /*node*/) {
auto const& arrayType = value.GetArray();
for (auto &item : arrayType) {
mforms::TreeNodeRef arrrayNode = _treeView->root_node()->add_child();
arrrayNode->set_string(0, std::to_string(_rowNum++));
switch (item.GetType()) {
case kArrayType: {
auto const& arrayVal = item.GetArray();
size_t size = arrayVal.Size();
std::stringstream textSize;
textSize << size;
std::string text = "Array [";
text += textSize.str();
text += "]";
arrrayNode->set_icon_path(_noNameColId, "JS_Datatype_Array.png");
arrrayNode->set_string(_noNameColId, text);
arrrayNode->set_data(new JsonTreeBaseView::JsonValueNodeData(item));
break;
}
case kObjectType:
_rowNum--;
generateObjectInTree(item, 0, arrrayNode, false);
break;
case kNumberType:
generateNumberInTree(item, _noNameColId, arrrayNode);
arrrayNode->set_data(new JsonTreeBaseView::JsonValueNodeData(item));
break;
case kTrueType:
case kFalseType:
generateBoolInTree(item, _noNameColId, arrrayNode);
arrrayNode->set_data(new JsonTreeBaseView::JsonValueNodeData(item));
break;
case kStringType:
setStringData(_noNameColId, arrrayNode, item.GetString());
arrrayNode->set_data(new JsonTreeBaseView::JsonValueNodeData(item));
break;
case kNullType:
generateNullInTree(item, _noNameColId, arrrayNode);
arrrayNode->set_data(new JsonTreeBaseView::JsonValueNodeData(item));
break;
default:
break;
}
}
}
//--------------------------------------------------------------------------------------------------
void JsonGridView::nodeActivated(TreeNodeRef node, int column) {
if (column > 0) {
JsonValueNodeData *data = dynamic_cast<JsonValueNodeData *>(node->get_data());
if (!data)
return;
rapidjson::Value &storedValue = data->getData();
if (storedValue.GetType() == kObjectType) {
const std::map<std::string, int>::const_iterator it =
std::find_if(_colNameToColId.begin(), _colNameToColId.end(),
[&column](const std::pair<std::string, int> &elem) { return column == elem.second; });
if (it != _colNameToColId.end() && storedValue.FindMember(it->first) != storedValue.MemberEnd()) {
rapidjson::Value &clickedValue = storedValue[it->first];
if (clickedValue.GetType() != kObjectType && clickedValue.GetType() != kArrayType)
return;
_level++;
setJson(clickedValue);
_goUpButton->set_enabled(true);
}
}
if (storedValue.GetType() == kArrayType) {
_level++;
setJson(storedValue);
_goUpButton->set_enabled(true);
}
}
}
//--------------------------------------------------------------------------------------------------
void JsonGridView::generateBoolInTree(rapidjson::Value &value, int columnId, TreeNodeRef node) {
node->set_bool(columnId, value.GetBool());
}
//--------------------------------------------------------------------------------------------------
void JsonGridView::generateNumberInTree(rapidjson::Value &value, int columnId, TreeNodeRef node) {
if (value.IsDouble()) {
node->set_float(columnId, value.GetDouble());
} else if (value.IsInt64()) {
node->set_long(columnId, value.GetInt64());
} else if (value.IsUint64()) {
node->set_long(columnId, value.GetUint64());
} else if (value.IsNumber()) {
node->set_long(columnId, value.GetInt());
}
}
//--------------------------------------------------------------------------------------------------
void JsonGridView::generateNullInTree(rapidjson::Value &value, int columnId, TreeNodeRef node) {
node->set_string(columnId, "null");
}
//--------------------------------------------------------------------------------------------------
void JsonGridView::setStringData(int columnId, TreeNodeRef node, const std::string &text) {
if (isDateTime(text))
node->set_icon_path(0, "JS_Datatype_Date.png");
node->set_attributes(columnId, mforms::TextAttributes("#4b4a4c", false, false));
node->set_string(columnId, text.c_str());
}
//--------------------------------------------------------------------------------------------------
void JsonTabView::Setup() {
assert(_tabView != NULL);
_tabView->set_name("JSON Editor");
_tabId.textTabId = _tabView->add_page(_textView, "Text");
_tabId.treeViewTabId = _tabView->add_page(_treeView, "Tree");
_tabId.gridViewTabId = _tabView->add_page(_gridView, "Grid");
add(_tabView);
scoped_connect(_textView->dataChanged(), std::bind(&JsonTabView::dataChanged, this, std::placeholders::_1));
scoped_connect(_treeView->dataChanged(), std::bind(&JsonTabView::dataChanged, this, std::placeholders::_1));
scoped_connect(_gridView->dataChanged(), std::bind(&JsonTabView::dataChanged, this, std::placeholders::_1));
scoped_connect(_tabView->signal_tab_changed(), std::bind(&JsonTabView::tabChanged, this));
}
//--------------------------------------------------------------------------------------------------
JsonTabView::JsonTabView(bool tabLess, JsonTabViewType defaultView)
: Panel(TransparentPanel),
_textView(manage(new JsonTextView(_document))),
_treeView(manage(new JsonTreeView(_document))),
_gridView(manage(new JsonGridView(_document))),
_tabView(manage(new TabView(tabLess ? TabViewTabless : TabViewPalette))),
_updating(false),
_defaultView(defaultView) {
Setup();
}
//--------------------------------------------------------------------------------------------------
JsonTabView::~JsonTabView() {
}
//--------------------------------------------------------------------------------------------------
void JsonTabView::setJson(const rapidjson::Value &value) {
Document d;
_json.CopyFrom(value, d.GetAllocator());
_ident = 0;
_updating = true;
d.CopyFrom(_json, d.GetAllocator());
StringBuffer buffer;
PrettyWriter<StringBuffer> writer(buffer);
d.Accept(writer);
_jsonText = buffer.GetString();
_updateView = { true, true, true };
switch (_defaultView) {
case JsonTabView::TabText:
_textView->setText(_jsonText, false);
_updateView.textViewUpdate = false;
break;
case JsonTabView::TabTree:
_treeView->setJson(_json);
_updateView.treeViewUpdate = false;
break;
case JsonTabView::TabGrid:
_gridView->setJson(_json);
_updateView.gridViewUpdate = false;
break;
}
switchTab(_defaultView);
_updating = false;
}
//--------------------------------------------------------------------------------------------------
void JsonTabView::setText(const std::string &text, bool validate) {
_jsonText = text;
_textView->setText(text, validate);
_updateView.textViewUpdate = false;
}
//--------------------------------------------------------------------------------------------------
void JsonTabView::tabChanged() {
int tabId = _tabView->get_active_tab();
if (tabId == _tabId.textTabId && _updateView.textViewUpdate) {
_updating = true;
_textView->setText(_jsonText);
_updateView.textViewUpdate = false;
_updating = false;
_dataChanged(_jsonText);
} else if (tabId == _tabId.treeViewTabId && _updateView.treeViewUpdate) {
_treeView->reCreateTree(_json);
_updateView.treeViewUpdate = false;
_dataChanged(_jsonText);
} else if (tabId == _tabId.gridViewTabId && _updateView.gridViewUpdate) {
_gridView->reCreateTree(_json);
_updateView.gridViewUpdate = false;
_dataChanged(_jsonText);
}
}
//--------------------------------------------------------------------------------------------------
void JsonTabView::dataChanged(bool forceUpdate) {
if (_updating)
return;
int tabId = _tabView->get_active_tab();
if (tabId != _tabId.textTabId) {
_document.CopyFrom(_json, _document.GetAllocator());
StringBuffer buffer;
Writer<StringBuffer> writer(buffer);
_document.Accept(writer);
_jsonText = buffer.GetString();
} else {
if (_textView->validate()) {
_jsonText = _textView->getText();
_json.CopyFrom(_textView->getJson(), _document.GetAllocator());
} else
return;
}
_updateView.textViewUpdate = tabId != _tabId.textTabId;
_updateView.treeViewUpdate = tabId != _tabId.treeViewTabId;
_updateView.gridViewUpdate = tabId != _tabId.gridViewTabId;
_dataChanged(_jsonText);
}
//--------------------------------------------------------------------------------------------------
boost::signals2::signal<void(const std::string &text)> *JsonTabView::editorDataChanged() {
return &_dataChanged;
}
//--------------------------------------------------------------------------------------------------
void JsonTabView::clear() {
_jsonText.clear();
_textView->clear();
_treeView->clear();
_gridView->clear();
}
//--------------------------------------------------------------------------------------------------
void JsonTabView::highlightMatch(const std::string &text) {
_matchText = text;
int tabId = _tabView->get_active_tab();
if (tabId == _tabId.textTabId) {
_textView->findAndHighlightText(text);
} else if (tabId == _tabId.treeViewTabId) {
_treeView->highlightMatchNode(text);
} else if (tabId == _tabId.gridViewTabId) {
_gridView->highlightMatchNode(text);
}
}
//--------------------------------------------------------------------------------------------------
void JsonTabView::highlightNextMatch() {
int tabId = _tabView->get_active_tab();
if (tabId == _tabId.textTabId && !_matchText.empty()) {
_textView->findAndHighlightText(_matchText);
} else if (tabId == _tabId.treeViewTabId && !_matchText.empty()) {
_treeView->highlightMatchNode(_matchText);
} else if (tabId == _tabId.gridViewTabId && !_matchText.empty()) {
_gridView->highlightMatchNode(_matchText);
}
}
//--------------------------------------------------------------------------------------------------
void JsonTabView::highlightPreviousMatch() {
int tabId = _tabView->get_active_tab();
if (tabId == _tabId.textTabId && !_matchText.empty()) {
_textView->findAndHighlightText(_matchText, true);
} else if (tabId == _tabId.treeViewTabId && !_matchText.empty()) {
_treeView->highlightMatchNode(_matchText, true);
} else if (tabId == _tabId.gridViewTabId && !_matchText.empty()) {
_gridView->highlightMatchNode(_matchText, true);
}
}
//--------------------------------------------------------------------------------------------------
bool JsonTabView::filterView(const std::string &text) {
int tabId = _tabView->get_active_tab();
bool ret = false;
if (tabId == _tabId.textTabId) {
return false; // no filtering for text view
} else if (tabId == _tabId.treeViewTabId) {
ret = _treeView->filterView(text, _json);
} else if (tabId == _tabId.gridViewTabId) {
ret = _gridView->filterView(text, _json);
}
return ret;
}
//--------------------------------------------------------------------------------------------------
void JsonTabView::restoreOrginalResult() {
int tabId = _tabView->get_active_tab();
if (tabId == _tabId.textTabId) {
return;
} else if (tabId == _tabId.treeViewTabId) {
_treeView->reCreateTree(_json);
} else if (tabId == _tabId.gridViewTabId) {
_gridView->reCreateTree(_json);
}
}
//--------------------------------------------------------------------------------------------------
void JsonTabView::switchTab(JsonTabViewType tab) const {
switch (tab) {
case JsonTabViewType::TabText:
_tabView->set_active_tab(_tabId.textTabId);
break;
case JsonTabViewType::TabTree:
_tabView->set_active_tab(_tabId.treeViewTabId);
break;
case JsonTabViewType::TabGrid:
_tabView->set_active_tab(_tabId.gridViewTabId);
break;
default:
_tabView->set_active_tab(_tabId.textTabId);
}
}
//--------------------------------------------------------------------------------------------------
JsonTabView::JsonTabViewType JsonTabView::getActiveTab() const {
int tabId = _tabView->get_active_tab();
if (tabId == _tabId.textTabId)
return JsonTabViewType::TabText;
else if (tabId == _tabId.treeViewTabId)
return JsonTabViewType::TabTree;
else
return JsonTabViewType::TabGrid;
}
//--------------------------------------------------------------------------------------------------
const std::string &JsonTabView::text() const {
return _jsonText;
}
//--------------------------------------------------------------------------------------------------
const rapidjson::Value &JsonTabView::json() const {
return _json;
}
//--------------------------------------------------------------------------------------------------
void JsonTabView::setTextProcessingStartHandler(std::function<void(std::function<bool()>)> callback) {
if (_textView)
_textView->_startTextProcessing = callback;
}
//--------------------------------------------------------------------------------------------------
void JsonTabView::setTextProcessingStopHandler(std::function<void()> callabck) {
if (_textView)
_textView->_stopTextProcessing = callabck;
}
//--------------------------------------------------------------------------------------------------