<commit_before>/*************************************************************************/
/*  graph_edit.cpp                                                       */
/*************************************************************************/
/*                       This file is part of:                           */
/*                           GODOT ENGINE                                */
/*                      https://godotengine.org                          */
/*************************************************************************/
/* Copyright (c) 2007-2020 <NAME>, <NAME>.                 */
/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md).   */
/*                                                                       */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the       */
/* "Software"), to deal in the Software without restriction, including   */
/* without limitation the rights to use, copy, modify, merge, publish,   */
/* distribute, sublicense, and/or sell copies of the Software, and to    */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions:                                             */
/*                                                                       */
/* The above copyright notice and this permission notice shall be        */
/* included in all copies or substantial portions of the Software.       */
/*                                                                       */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
/*************************************************************************/

#include "graph_edit.h"

#include "core/os/input.h"
#include "core/os/keyboard.h"
#include "scene/gui/box_container.h"

#ifdef TOOLS_ENABLED
#include "editor/editor_scale.h"
#endif

#define ZOOM_SCALE 1.2

#define MIN_ZOOM (((1 / ZOOM_SCALE) / ZOOM_SCALE) / ZOOM_SCALE)
#define MAX_ZOOM (1 * ZOOM_SCALE * ZOOM_SCALE * ZOOM_SCALE)

bool GraphEditFilter::has_point(const Point2 &p_point) const {

	return ge->_filter_input(p_point);
}

GraphEditFilter::GraphEditFilter(GraphEdit *p_edit) {

	ge = p_edit;
}

Error GraphEdit::connect_node(const StringName &p_from, int p_from_port, const StringName &p_to, int p_to_port) {

	if (is_node_connected(p_from, p_from_port, p_to, p_to_port))
		return OK;
	Connection c;
	c.from = p_from;
	c.from_port = p_from_port;
	c.to = p_to;
	c.to_port = p_to_port;
	c.activity = 0;
	connections.push_back(c);
	top_layer->update();
	update();
	connections_layer->update();

	return OK;
}

bool GraphEdit::is_node_connected(const StringName &p_from, int p_from_port, const StringName &p_to, int p_to_port) {

	for (List<Connection>::Element *E = connections.front(); E; E = E->next()) {

		if (E->get().from == p_from && E->get().from_port == p_from_port && E->get().to == p_to && E->get().to_port == p_to_port)
			return true;
	}

	return false;
}

void GraphEdit::disconnect_node(const StringName &p_from, int p_from_port, const StringName &p_to, int p_to_port) {

	for (List<Connection>::Element *E = connections.front(); E; E = E->next()) {

		if (E->get().from == p_from && E->get().from_port == p_from_port && E->get().to == p_to && E->get().to_port == p_to_port) {

			connections.erase(E);
			top_layer->update();
			update();
			connections_layer->update();
			return;
		}
	}
}

bool GraphEdit::clips_input() const {

	return true;
}

void GraphEdit::get_connection_list(List<Connection> *r_connections) const {

	*r_connections = connections;
}

void GraphEdit::set_scroll_ofs(const Vector2 &p_ofs) {

	setting_scroll_ofs = true;
	h_scroll->set_value(p_ofs.x);
	v_scroll->set_value(p_ofs.y);
	_update_scroll();
	setting_scroll_ofs = false;
}

Vector2 GraphEdit::get_scroll_ofs() const {

	return Vector2(h_scroll->get_value(), v_scroll->get_value());
}

void GraphEdit::_scroll_moved(double) {

	if (!awaiting_scroll_offset_update) {
		call_deferred("_update_scroll_offset");
		awaiting_scroll_offset_update = true;
	}
	top_layer->update();
	update();

	if (!setting_scroll_ofs) { //in godot, signals on change value are avoided as a convention
		emit_signal("scroll_offset_changed", get_scroll_ofs());
	}
}

void GraphEdit::_update_scroll_offset() {

	set_block_minimum_size_adjust(true);

	for (int i = 0; i < get_child_count(); i++) {

		GraphNode *gn = Object::cast_to<GraphNode>(get_child(i));
		if (!gn)
			continue;

		Point2 pos = gn->get_offset() * zoom;
		pos -= Point2(h_scroll->get_value(), v_scroll->get_value());
		gn->set_position(pos);
		if (gn->get_scale() != Vector2(zoom, zoom)) {
			gn->set_scale(Vector2(zoom, zoom));
		}
	}

	connections_layer->set_position(-Point2(h_scroll->get_value(), v_scroll->get_value()));
	set_block_minimum_size_adjust(false);
	awaiting_scroll_offset_update = false;
}

void GraphEdit::_update_scroll() {

	if (updating)
		return;

	updating = true;

	set_block_minimum_size_adjust(true);

	Rect2 screen;
	for (int i = 0; i < get_child_count(); i++) {

		GraphNode *gn = Object::cast_to<GraphNode>(get_child(i));
		if (!gn)
			continue;

		Rect2 r;
		r.position = gn->get_offset() * zoom;
		r.size = gn->get_size() * zoom;
		screen = screen.merge(r);
	}

	screen.position -= get_size();
	screen.size += get_size() * 2.0;

	h_scroll->set_min(screen.position.x);
	h_scroll->set_max(screen.position.x + screen.size.x);
	h_scroll->set_page(get_size().x);
	if (h_scroll->get_max() - h_scroll->get_min() <= h_scroll->get_page())
		h_scroll->hide();
	else
		h_scroll->show();

	v_scroll->set_min(screen.position.y);
	v_scroll->set_max(screen.position.y + screen.size.y);
	v_scroll->set_page(get_size().y);

	if (v_scroll->get_max() - v_scroll->get_min() <= v_scroll->get_page())
		v_scroll->hide();
	else
		v_scroll->show();

	Size2 hmin = h_scroll->get_combined_minimum_size();
	Size2 vmin = v_scroll->get_combined_minimum_size();

	// Avoid scrollbar overlapping.
	h_scroll->set_anchor_and_margin(MARGIN_RIGHT, ANCHOR_END, v_scroll->is_visible() ? -vmin.width : 0);
	v_scroll->set_anchor_and_margin(MARGIN_BOTTOM, ANCHOR_END, h_scroll->is_visible() ? -hmin.height : 0);

	set_block_minimum_size_adjust(false);

	if (!awaiting_scroll_offset_update) {
		call_deferred("_update_scroll_offset");
		awaiting_scroll_offset_update = true;
	}

	updating = false;
}

void GraphEdit::_graph_node_raised(Node *p_gn) {

	GraphNode *gn = Object::cast_to<GraphNode>(p_gn);
	ERR_FAIL_COND(!gn);
	if (gn->is_comment()) {
		move_child(gn, 0);
	} else {
		gn->raise();
	}
	int first_not_comment = 0;
	for (int i = 0; i < get_child_count(); i++) {
		GraphNode *gn2 = Object::cast_to<GraphNode>(get_child(i));
		if (gn2 && !gn2->is_comment()) {
			first_not_comment = i;
			break;
		}
	}

	move_child(connections_layer, first_not_comment);
	top_layer->raise();
	emit_signal("node_selected", p_gn);
}

void GraphEdit::_graph_node_moved(Node *p_gn) {

	GraphNode *gn = Object::cast_to<GraphNode>(p_gn);
	ERR_FAIL_COND(!gn);
	top_layer->update();
	update();
	connections_layer->update();
}

void GraphEdit::add_child_notify(Node *p_child) {

	Control::add_child_notify(p_child);

	top_layer->call_deferred("raise"); //top layer always on top!
	GraphNode *gn = Object::cast_to<GraphNode>(p_child);
	if (gn) {
		gn->set_scale(Vector2(zoom, zoom));
		gn->connect("offset_changed", this, "_graph_node_moved", varray(gn));
		gn->connect("raise_request", this, "_graph_node_raised", varray(gn));
		gn->connect("item_rect_changed", connections_layer, "update");
		_graph_node_moved(gn);
		gn->set_mouse_filter(MOUSE_FILTER_PASS);
	}
}

void GraphEdit::remove_child_notify(Node *p_child) {

	Control::remove_child_notify(p_child);
	if (is_inside_tree()) {
		top_layer->call_deferred("raise"); //top layer always on top!
	}
	GraphNode *gn = Object::cast_to<GraphNode>(p_child);
	if (gn) {
		gn->disconnect("offset_changed", this, "_graph_node_moved");
		gn->disconnect("raise_request", this, "_graph_node_raised");
	}
}

void GraphEdit::_notification(int p_what) {

	if (p_what == NOTIFICATION_ENTER_TREE || p_what == NOTIFICATION_THEME_CHANGED) {
		port_grab_distance_horizontal = get_constant("port_grab_distance_horizontal");
		port_grab_distance_vertical = get_constant("port_grab_distance_vertical");
<commit_msg>Disconnect item_rect_changed when removing a child of GraphEdit<commit_after>/*************************************************************************/
/*  graph_edit.cpp                                                       */
/*************************************************************************/
/*                       This file is part of:                           */
/*                           GODOT ENGINE                                */
/*                      https://godotengine.org                          */
/*************************************************************************/
/* Copyright (c) 2007-2020 <NAME>, <NAME>.                 */
/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md).   */
/*                                                                       */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the       */
/* "Software"), to deal in the Software without restriction, including   */
/* without limitation the rights to use, copy, modify, merge, publish,   */
/* distribute, sublicense, and/or sell copies of the Software, and to    */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions:                                             */
/*                                                                       */
/* The above copyright notice and this permission notice shall be        */
/* included in all copies or substantial portions of the Software.       */
/*                                                                       */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
/*************************************************************************/

#include "graph_edit.h"

#include "core/os/input.h"
#include "core/os/keyboard.h"
#include "scene/gui/box_container.h"

#ifdef TOOLS_ENABLED
#include "editor/editor_scale.h"
#endif

#define ZOOM_SCALE 1.2

#define MIN_ZOOM (((1 / ZOOM_SCALE) / ZOOM_SCALE) / ZOOM_SCALE)
#define MAX_ZOOM (1 * ZOOM_SCALE * ZOOM_SCALE * ZOOM_SCALE)

bool GraphEditFilter::has_point(const Point2 &p_point) const {

	return ge->_filter_input(p_point);
}

GraphEditFilter::GraphEditFilter(GraphEdit *p_edit) {

	ge = p_edit;
}

Error GraphEdit::connect_node(const StringName &p_from, int p_from_port, const StringName &p_to, int p_to_port) {

	if (is_node_connected(p_from, p_from_port, p_to, p_to_port))
		return OK;
	Connection c;
	c.from = p_from;
	c.from_port = p_from_port;
	c.to = p_to;
	c.to_port = p_to_port;
	c.activity = 0;
	connections.push_back(c);
	top_layer->update();
	update();
	connections_layer->update();

	return OK;
}

bool GraphEdit::is_node_connected(const StringName &p_from, int p_from_port, const StringName &p_to, int p_to_port) {

	for (List<Connection>::Element *E = connections.front(); E; E = E->next()) {

		if (E->get().from == p_from && E->get().from_port == p_from_port && E->get().to == p_to && E->get().to_port == p_to_port)
			return true;
	}

	return false;
}

void GraphEdit::disconnect_node(const StringName &p_from, int p_from_port, const StringName &p_to, int p_to_port) {

	for (List<Connection>::Element *E = connections.front(); E; E = E->next()) {

		if (E->get().from == p_from && E->get().from_port == p_from_port && E->get().to == p_to && E->get().to_port == p_to_port) {

			connections.erase(E);
			top_layer->update();
			update();
			connections_layer->update();
			return;
		}
	}
}

bool GraphEdit::clips_input() const {

	return true;
}

void GraphEdit::get_connection_list(List<Connection> *r_connections) const {

	*r_connections = connections;
}

void GraphEdit::set_scroll_ofs(const Vector2 &p_ofs) {

	setting_scroll_ofs = true;
	h_scroll->set_value(p_ofs.x);
	v_scroll->set_value(p_ofs.y);
	_update_scroll();
	setting_scroll_ofs = false;
}

Vector2 GraphEdit::get_scroll_ofs() const {

	return Vector2(h_scroll->get_value(), v_scroll->get_value());
}

void GraphEdit::_scroll_moved(double) {

	if (!awaiting_scroll_offset_update) {
		call_deferred("_update_scroll_offset");
		awaiting_scroll_offset_update = true;
	}
	top_layer->update();
	update();

	if (!setting_scroll_ofs) { //in godot, signals on change value are avoided as a convention
		emit_signal("scroll_offset_changed", get_scroll_ofs());
	}
}

void GraphEdit::_update_scroll_offset() {

	set_block_minimum_size_adjust(true);

	for (int i = 0; i < get_child_count(); i++) {

		GraphNode *gn = Object::cast_to<GraphNode>(get_child(i));
		if (!gn)
			continue;

		Point2 pos = gn->get_offset() * zoom;
		pos -= Point2(h_scroll->get_value(), v_scroll->get_value());
		gn->set_position(pos);
		if (gn->get_scale() != Vector2(zoom, zoom)) {
			gn->set_scale(Vector2(zoom, zoom));
		}
	}

	connections_layer->set_position(-Point2(h_scroll->get_value(), v_scroll->get_value()));
	set_block_minimum_size_adjust(false);
	awaiting_scroll_offset_update = false;
}

void GraphEdit::_update_scroll() {

	if (updating)
		return;

	updating = true;

	set_block_minimum_size_adjust(true);

	Rect2 screen;
	for (int i = 0; i < get_child_count(); i++) {

		GraphNode *gn = Object::cast_to<GraphNode>(get_child(i));
		if (!gn)
			continue;

		Rect2 r;
		r.position = gn->get_offset() * zoom;
		r.size = gn->get_size() * zoom;
		screen = screen.merge(r);
	}

	screen.position -= get_size();
	screen.size += get_size() * 2.0;

	h_scroll->set_min(screen.position.x);
	h_scroll->set_max(screen.position.x + screen.size.x);
	h_scroll->set_page(get_size().x);
	if (h_scroll->get_max() - h_scroll->get_min() <= h_scroll->get_page())
		h_scroll->hide();
	else
		h_scroll->show();

	v_scroll->set_min(screen.position.y);
	v_scroll->set_max(screen.position.y + screen.size.y);
	v_scroll->set_page(get_size().y);

	if (v_scroll->get_max() - v_scroll->get_min() <= v_scroll->get_page())
		v_scroll->hide();
	else
		v_scroll->show();

	Size2 hmin = h_scroll->get_combined_minimum_size();
	Size2 vmin = v_scroll->get_combined_minimum_size();

	// Avoid scrollbar overlapping.
	h_scroll->set_anchor_and_margin(MARGIN_RIGHT, ANCHOR_END, v_scroll->is_visible() ? -vmin.width : 0);
	v_scroll->set_anchor_and_margin(MARGIN_BOTTOM, ANCHOR_END, h_scroll->is_visible() ? -hmin.height : 0);

	set_block_minimum_size_adjust(false);

	if (!awaiting_scroll_offset_update) {
		call_deferred("_update_scroll_offset");
		awaiting_scroll_offset_update = true;
	}

	updating = false;
}

void GraphEdit::_graph_node_raised(Node *p_gn) {

	GraphNode *gn = Object::cast_to<GraphNode>(p_gn);
	ERR_FAIL_COND(!gn);
	if (gn->is_comment()) {
		move_child(gn, 0);
	} else {
		gn->raise();
	}
	int first_not_comment = 0;
	for (int i = 0; i < get_child_count(); i++) {
		GraphNode *gn2 = Object::cast_to<GraphNode>(get_child(i));
		if (gn2 && !gn2->is_comment()) {
			first_not_comment = i;
			break;
		}
	}

	move_child(connections_layer, first_not_comment);
	top_layer->raise();
	emit_signal("node_selected", p_gn);
}

void GraphEdit::_graph_node_moved(Node *p_gn) {

	GraphNode *gn = Object::cast_to<GraphNode>(p_gn);
	ERR_FAIL_COND(!gn);
	top_layer->update();
	update();
	connections_layer->update();
}

void GraphEdit::add_child_notify(Node *p_child) {

	Control::add_child_notify(p_child);

	top_layer->call_deferred("raise"); //top layer always on top!
	GraphNode *gn = Object::cast_to<GraphNode>(p_child);
	if (gn) {
		gn->set_scale(Vector2(zoom, zoom));
		gn->connect("offset_changed", this, "_graph_node_moved", varray(gn));
		gn->connect("raise_request", this, "_graph_node_raised", varray(gn));
		gn->connect("item_rect_changed", connections_layer, "update");
		_graph_node_moved(gn);
		gn->set_mouse_filter(MOUSE_FILTER_PASS);
	}
}

void GraphEdit::remove_child_notify(Node *p_child) {

	Control::remove_child_notify(p_child);
	if (is_inside_tree()) {
		top_layer->call_deferred("raise"); //top layer always on top!
	}
	GraphNode *gn = Object::cast_to<GraphNode>(p_child);
	if (gn) {
		gn->disconnect("offset_changed", this, "_graph_node_moved");
		gn->disconnect("raise_request", this, "_graph_node_raised");
		gn->disconnect("item_rect_changed", connections_layer, "update");
	}
}

void GraphEdit::_notification(int p_what) {

	if (p_what == NOTIFICATION_ENTER_TREE || p_what == NOTIFICATION_THEME_CHANGED) {
		port_grab_distance_horizontal = get_constant("port_grab_distance_horizontal");
		port_grab_distance_vertical = get_constant("port_grab_distance_vertical");

{'fraction_non_alphanumeric': 0.1698935688405797, 'fraction_numerical': 0.005321557971014493, 'mean_word_length': 3.792457948996202, 'pattern_counts': {'":': 0, '<': 37, '<?xml version=': 0, '>': 164, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 26, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1326028', 'n_tokens_mistral': 6191, 'n_tokens_neox': 5833, 'n_words': 1228}