<commit_before>/***********************************************************************************************************************
**
** Copyright (c) 2011, 2014 ETH Zurich
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
** following conditions are met:
**
**    * Redistributions of source code must retain the above copyright notice, this list of conditions and the following
**      disclaimer.
**    * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
**      following disclaimer in the documentation and/or other materials provided with the distribution.
**    * Neither the name of the ETH Zurich nor the names of its contributors may be used to endorse or promote products
**      derived from this software without specific prior written permission.
**
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
** INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
** DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
** SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**
***********************************************************************************************************************/

#include "DynamicGridFormElement.h"
#include "../items/Item.h"
#include "DeclarativeItemBase.h"

namespace Visualization {

DynamicGridFormElement::DynamicGridFormElement(const DynamicGridFormElement& other) : LayoutFormElement{other},
	nodesGetterFunction_{other.nodesGetterFunction_}, spanGetterFunction_{other.spanGetterFunction_},
	spaceBetweenColumns_{other.spaceBetweenColumns_}, spaceBetweenRows_{other.spaceBetweenRows_},
	majorAxis_{other.majorAxis_},
	horizontalAlignment_{other.horizontalAlignment_}, verticalAlignment_{other.verticalAlignment_}
{
	// There are no child memebrs to adjust
}

DynamicGridFormElement::~DynamicGridFormElement()
{
	for (auto data : itemData_)
	{
		for (auto list : data->itemGrid_)
			for (auto item : list)
				SAFE_DELETE_ITEM(item);

		SAFE_DELETE(data);
	}
	itemData_.clear();
}

DynamicGridFormElement* DynamicGridFormElement::clone() const
{
	return new DynamicGridFormElement(*this);
}

bool DynamicGridFormElement::sizeDependsOnParent(const Item*) const
{
	return false;
}

DynamicGridFormElement::ItemData&  DynamicGridFormElement::dataForItem(const Item* item) const
{
	auto dataIterator = itemData_.find(item);
	if (dataIterator != itemData_.end()) return *(dataIterator.value());

	auto data = new ItemData;
	itemData_.insert(item, data);
	return *data;
}

void DynamicGridFormElement::computeSize(Item* item, int availableWidth, int availableHeight)
{
	auto& data = dataForItem(item);
	QSize finalSize;

	if (majorAxis_ == GridLayouter::NoMajor)
		finalSize = GridLayouter::computeSize<true>(availableWidth, availableHeight, GridLayouter::NoMajor,
			[&data](){return data.numRows_;},	// numRows
			[&data](){return data.numColumns_;},	// numColumns
			[&data](int x, int y){return data.itemGrid_[x][y];},	// has
			[&data](int x, int y){return data.itemSpan_[x][y];},	// spanGrid
			[&data](int x, int y){return data.itemGrid_[x][y]->widthInParent();},	// width
			[&data](int x, int y){return data.itemGrid_[x][y]->heightInParent();},	// height
			[](int, int, int, int){},	// computeElementSize
			[&data](int x, int y, int w, int h){data.itemGrid_[x][y]->changeGeometry(w, h);},	// changeGeometry
			[&data](int x, int y){return data.itemGrid_[x][y]->sizeDependsOnParent();},	// isStretchable
			[&data](int x, int y, QPoint pos){data.itemPositionWithinLayout_[x][y] = pos;},	// setPosition
			[](int){return 0;},	// rowStretchFactors
			[](int){return 0;},	// columnStretchFactors
			[this](int, int){return horizontalAlignment_;},	// horizontalAlignment
			[this](int, int){return verticalAlignment_;},	// verticalAlignment
			[this](){return spaceBetweenRows_;},	// spaceBetweenRows
			[this](){return spaceBetweenColumns_;},	// spaceBetweenColumns
			[this](){return topMargin();},	// topMargin
			[this](){return bottomMargin();},	// bottomMargin
			[this](){return leftMargin();},	// leftMargin
			[this](){return rightMargin();},	// rightMargin
			[](){return 0;},	// minWidth
			[](){return 0;}	// minHeight
		);
	else
		finalSize = GridLayouter::computeSize<false>(availableWidth, availableHeight, majorAxis_,
				[&data](){return data.numRows_;},	// numRows
				[&data](){return data.numColumns_;},	// numColumns
				[&data](int x, int y){return data.itemGrid_[x][y];},	// has
				[](int, int){return QPair<int, int>{1, 1};},	// spanGrid
				[&data](int x, int y){return data.itemGrid_[x][y]->widthInParent();},	// width
				[&data](int x, int y){return data.itemGrid_[x][y]->heightInParent();},	// height
				[](int, int, int, int){},	// computeElementSize
				[&data](int x, int y, int w, int h){data.itemGrid_[x][y]->changeGeometry(w, h);},	// changeGeometry
				[&data](int x, int y){return data.itemGrid_[x][y]->sizeDependsOnParent();},	// isStretchable
				[&data](int x, int y, QPoint pos){data.itemPositionWithinLayout_[x][y] = pos;},	// setPosition
				[](int){return 0;},	// rowStretchFactors
				[](int){return 0;},	// columnStretchFactors
				[this](int, int){return horizontalAlignment_;},	// horizontalAlignment
				[this](int, int){return verticalAlignment_;},	// verticalAlignment
				[this](){return spaceBetweenRows_;},	// spaceBetweenRows
				[this](){return spaceBetweenColumns_;},	// spaceBetweenColumns
				[this](){return topMargin();},	// topMargin
				[this](){return bottomMargin();},	// bottomMargin
				[this](){return leftMargin();},	// leftMargin
				[this](){return rightMargin();},	// rightMargin
				[](){return 0;},	// minWidth
				[](){return 0;}	// minHeight
			);

	setSize(item, finalSize);
}

void DynamicGridFormElement::synchronizeWithItem(Item* item)
{
	auto nodes = nodesGetterFunction_(item);
	auto& data = dataForItem(item);

	// Remove all elements from the current grid that do not match a node
	// If a node exists record it's location
	QHash<Model::Node*, Item*> existingItems;

	for (int x = 0; x < data.numColumns_; ++x)
		for (int y = 0; y < data.numRows_; ++y)
			if (data.itemGrid_[x][y] != nullptr)
			{
				bool found = false;

				for (int n = 0; n < nodes.size(); ++n)
					if (nodes[n].contains(data.itemGrid_[x][y]->node()))
					{
						found = true;
						item->synchronizeItem(data.itemGrid_[x][y], data.itemGrid_[x][y]->node());
						existingItems.insert(data.itemGrid_[x][y]->node(), data.itemGrid_[x][y]);
						break;
					}

				if (!found) SAFE_DELETE_ITEM(data.itemGrid_[x][y]);
				data.itemGrid_[x][y] = nullptr;
			}

	// Get the new size.
	auto resizeAxes = [](const QVector<QVector<Model::Node*>>& nodes, int& sizeMajor, int& sizeMinor){
		sizeMajor = nodes.size();
		sizeMinor = nodes.isEmpty() ? 0 : std::max_element(nodes.begin(), nodes.end(),
				[](const QVector<Model::Node*>& a, const QVector<Model::Node*>& b){return a.size() < b.size();})->size();
	};
	if (majorAxis_ == GridLayouter::ColumnMajor) resizeAxes(nodes, data.numColumns_, data.numRows_);
	else resizeAxes(nodes, data.numRows_, data.numColumns_);

	// Set new grid size
	data.itemGrid_.resize(data.numColumns_);
	data.itemPositionWithinLayout_.resize(data.numColumns_);
	for (auto x = 0; x < data.numColumns_; ++x)
	{
		data.itemGrid_[x].resize(data.numRows_);
		data.itemPositionWithinLayout_[x].resize(data.numRows_);
	}

	for (int major = 0; major<nodes.size(); ++major)
		for (int minor = 0; minor<nodes[major].size(); ++minor)
		{
			if (nodes[major][minor])
			{
				auto existingIterator = existingItems.find(nodes[major][minor]);
				auto value = existingIterator != existingItems.end()
						? existingIterator.value()
						: item->renderer()->render(item, nodes[major][minor]);

				if (majorAxis_ == GridLayouter::ColumnMajor) data.itemGrid_[major][minor] = value;
				else data.itemGrid_[minor][major] = value;

				// Note that we don't need to update the position here since it will be overwritten anyway by
				// computeSize()
			}
		}

	// Set the span if any
	if (spanGetterFunction_) data.itemSpan_ = spanGetterFunction_(item);
	else
	{
		data.itemSpan_.clear();
		data.itemSpan_.resize(data.numColumns_);
		for (int x = 0; x < data.numColumns_; ++x)
			data.itemSpan_[x] = QVector<QPair<int, int>>(data.numRows_, {1, 1});
	}
}

void DynamicGridFormElement::setItemPositions(Item* item, int parentX, int parentY)
{
	auto& data = dataForItem(item);

	for (int xIt = 0; xIt<data.numColumns_; ++xIt)
		for (int yIt = 0; yIt < data.numRows_; ++yIt)
			if (auto childItem = data.itemGrid_[xIt][yIt])
			{
				// Note that margins have already been accounted for.
				QPointF newPos{parentX + x(item) + data.itemPositionWithinLayout_[xIt][yIt].x(),
							parentY + y(item) + data.itemPositionWithinLayout_[xIt][yIt].y()};

				if (newPos != childItem->pos())
					childItem->setPos(newPos); // setting the position is an expensive operation so only do it if it changed
			}
}

void DynamicGridFormElement::destroyChildItems(Item* item,
										 QList<const Item* const DeclarativeItemBase::*> handledChildren)
{
	LayoutFormElement::destroyChildItems(item, handledChildren);

	auto dataIterator = itemData_.find(item);
	if (dataIterator != itemData_.end())
	{
		for (auto list : dataIterator.value()->itemGrid_)
			for (auto item : list)
				SAFE_DELETE_ITEM(item);

		SAFE_DELETE(dataIterator.value());
		itemData_.erase(dataIterator);
	}
}

QPoint DynamicGridFormElement::focusedElementIndex(Item* item) const
{
	auto& data = dataForItem(item);

	for (int x=0; x<data.numColumns_; ++x)
		for (int y=0; y<data.numRows_; ++y)
			if ( data.itemGrid_[x][y] && data.itemGrid_[x][y]->itemOrChildHasFocus()) return QPoint(x, y);

	return QPoint(-1, -1);
}

bool DynamicGridFormElement::elementOrChildHasFocus(Item* item) const
{
	if (LayoutFormElement::elementOrChildHasFocus(item))
		return true;
	for (auto list : dataForItem(item).itemGrid_)
		for (auto item : list)
			if (item && item->itemOrChildHasFocus())
				return true;
	return false;
}

QList<ItemRegion> DynamicGridFormElement::regions(DeclarativeItemBase* item, int parentX, int parentY)
{
	auto& data = dataForItem(item);

	return GridLayouter::regions(item, this, parentX + x(item), parentY + y(item), majorAxis_, true, true, true,
			item->style()->extraCursorsOutsideShape(), true, true,
			[&data](){return data.numRows_;},	// numRows
			[&data](){return data.numColumns_;},	// numColumns
			[&data](int x, int y){return data.itemGrid_[x][y];},	// has
			[&data](int x, int y){return data.itemGrid_[x][y]->widthInParent();},	// width
			[&data](int x, int y){return data.itemGrid_[x][y]->heightInParent();},	// height
			[&data](int x, int y){return (int) data.itemGrid_[x][y]->x();},	// xPos
			[&data](int x, int y){return (int) data.itemGrid_[x][y]->y();},	// yPos
			[&data](int x, int y){return data.itemGrid_[x][y];},	// childItem
			[this](){return spaceBetweenRows_;},	// spaceBetweenRows
			[this](){return spaceBetweenColumns_;},	// spaceBetweenColumns
			[this](){return topMargin();},	// topMargin
			[this](){return bottomMargin();},	// bottomMargin
			[this](){return leftMargin();},	// leftMargin
			[this](){return rightMargin();}	// rightMargin
		);
}

} // namespace Visualization
<commit_msg>Can now contain the same node multiple times.<commit_after>/***********************************************************************************************************************
**
** Copyright (c) 2011, 2014 ETH Zurich
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
** following conditions are met:
**
**    * Redistributions of source code must retain the above copyright notice, this list of conditions and the following
**      disclaimer.
**    * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
**      following disclaimer in the documentation and/or other materials provided with the distribution.
**    * Neither the name of the ETH Zurich nor the names of its contributors may be used to endorse or promote products
**      derived from this software without specific prior written permission.
**
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
** INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
** DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
** SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**
***********************************************************************************************************************/

#include "DynamicGridFormElement.h"
#include "../items/Item.h"
#include "DeclarativeItemBase.h"

namespace Visualization {

DynamicGridFormElement::DynamicGridFormElement(const DynamicGridFormElement& other) : LayoutFormElement{other},
	nodesGetterFunction_{other.nodesGetterFunction_}, spanGetterFunction_{other.spanGetterFunction_},
	spaceBetweenColumns_{other.spaceBetweenColumns_}, spaceBetweenRows_{other.spaceBetweenRows_},
	majorAxis_{other.majorAxis_},
	horizontalAlignment_{other.horizontalAlignment_}, verticalAlignment_{other.verticalAlignment_}
{
	// There are no child memebrs to adjust
}

DynamicGridFormElement::~DynamicGridFormElement()
{
	for (auto data : itemData_)
	{
		for (auto list : data->itemGrid_)
			for (auto item : list)
				SAFE_DELETE_ITEM(item);

		SAFE_DELETE(data);
	}
	itemData_.clear();
}

DynamicGridFormElement* DynamicGridFormElement::clone() const
{
	return new DynamicGridFormElement(*this);
}

bool DynamicGridFormElement::sizeDependsOnParent(const Item*) const
{
	return false;
}

DynamicGridFormElement::ItemData&  DynamicGridFormElement::dataForItem(const Item* item) const
{
	auto dataIterator = itemData_.find(item);
	if (dataIterator != itemData_.end()) return *(dataIterator.value());

	auto data = new ItemData;
	itemData_.insert(item, data);
	return *data;
}

void DynamicGridFormElement::computeSize(Item* item, int availableWidth, int availableHeight)
{
	auto& data = dataForItem(item);
	QSize finalSize;

	if (majorAxis_ == GridLayouter::NoMajor)
		finalSize = GridLayouter::computeSize<true>(availableWidth, availableHeight, GridLayouter::NoMajor,
			[&data](){return data.numRows_;},	// numRows
			[&data](){return data.numColumns_;},	// numColumns
			[&data](int x, int y){return data.itemGrid_[x][y];},	// has
			[&data](int x, int y){return data.itemSpan_[x][y];},	// spanGrid
			[&data](int x, int y){return data.itemGrid_[x][y]->widthInParent();},	// width
			[&data](int x, int y){return data.itemGrid_[x][y]->heightInParent();},	// height
			[](int, int, int, int){},	// computeElementSize
			[&data](int x, int y, int w, int h){data.itemGrid_[x][y]->changeGeometry(w, h);},	// changeGeometry
			[&data](int x, int y){return data.itemGrid_[x][y]->sizeDependsOnParent();},	// isStretchable
			[&data](int x, int y, QPoint pos){data.itemPositionWithinLayout_[x][y] = pos;},	// setPosition
			[](int){return 0;},	// rowStretchFactors
			[](int){return 0;},	// columnStretchFactors
			[this](int, int){return horizontalAlignment_;},	// horizontalAlignment
			[this](int, int){return verticalAlignment_;},	// verticalAlignment
			[this](){return spaceBetweenRows_;},	// spaceBetweenRows
			[this](){return spaceBetweenColumns_;},	// spaceBetweenColumns
			[this](){return topMargin();},	// topMargin
			[this](){return bottomMargin();},	// bottomMargin
			[this](){return leftMargin();},	// leftMargin
			[this](){return rightMargin();},	// rightMargin
			[](){return 0;},	// minWidth
			[](){return 0;}	// minHeight
		);
	else
		finalSize = GridLayouter::computeSize<false>(availableWidth, availableHeight, majorAxis_,
				[&data](){return data.numRows_;},	// numRows
				[&data](){return data.numColumns_;},	// numColumns
				[&data](int x, int y){return data.itemGrid_[x][y];},	// has
				[](int, int){return QPair<int, int>{1, 1};},	// spanGrid
				[&data](int x, int y){return data.itemGrid_[x][y]->widthInParent();},	// width
				[&data](int x, int y){return data.itemGrid_[x][y]->heightInParent();},	// height
				[](int, int, int, int){},	// computeElementSize
				[&data](int x, int y, int w, int h){data.itemGrid_[x][y]->changeGeometry(w, h);},	// changeGeometry
				[&data](int x, int y){return data.itemGrid_[x][y]->sizeDependsOnParent();},	// isStretchable
				[&data](int x, int y, QPoint pos){data.itemPositionWithinLayout_[x][y] = pos;},	// setPosition
				[](int){return 0;},	// rowStretchFactors
				[](int){return 0;},	// columnStretchFactors
				[this](int, int){return horizontalAlignment_;},	// horizontalAlignment
				[this](int, int){return verticalAlignment_;},	// verticalAlignment
				[this](){return spaceBetweenRows_;},	// spaceBetweenRows
				[this](){return spaceBetweenColumns_;},	// spaceBetweenColumns
				[this](){return topMargin();},	// topMargin
				[this](){return bottomMargin();},	// bottomMargin
				[this](){return leftMargin();},	// leftMargin
				[this](){return rightMargin();},	// rightMargin
				[](){return 0;},	// minWidth
				[](){return 0;}	// minHeight
			);

	setSize(item, finalSize);
}

void DynamicGridFormElement::synchronizeWithItem(Item* item)
{
	auto nodes = nodesGetterFunction_(item);
	auto& data = dataForItem(item);

	// Remove all elements from the current grid that do not match a node
	// If a node exists record it's location
	// One node might be visualized multiple times
	QHash<Model::Node*, QList<Item*>> existingItems;

	//Keep track of the positions at which we already found visualizations for nodes
	QList<QPoint> usedPositions;

	for (int x = 0; x < data.numColumns_; ++x)
		for (int y = 0; y < data.numRows_; ++y)
			if (data.itemGrid_[x][y] != nullptr)
			{
				bool found = false;

				for (int n = 0; n < nodes.size(); ++n)
					for (int j = 0; j < nodes[n].size(); ++j)
						//We must make sure not to visualize one node with the same item twice
						if (nodes[n][j] == data.itemGrid_[x][y]->node() && !usedPositions.contains(QPoint(n, j)))
						{
							item->synchronizeItem(data.itemGrid_[x][y], nodes[n][j]);
							if (!existingItems.contains(nodes[n][j]))
								existingItems.insert(nodes[n][j], {});
							existingItems[nodes[n][j]].append(data.itemGrid_[x][y]);
							usedPositions.append(QPoint(n, j));
							found = true;
							break;
						}

				if (!found) SAFE_DELETE_ITEM(data.itemGrid_[x][y]);
				data.itemGrid_[x][y] = nullptr;
			}

	// Get the new size.
	auto resizeAxes = [](const QVector<QVector<Model::Node*>>& nodes, int& sizeMajor, int& sizeMinor){
		sizeMajor = nodes.size();
		sizeMinor = nodes.isEmpty() ? 0 : std::max_element(nodes.begin(), nodes.end(),
				[](const QVector<Model::Node*>& a, const QVector<Model::Node*>& b){return a.size() < b.size();})->size();
	};
	if (majorAxis_ == GridLayouter::ColumnMajor) resizeAxes(nodes, data.numColumns_, data.numRows_);
	else resizeAxes(nodes, data.numRows_, data.numColumns_);

	// Set new grid size
	data.itemGrid_.resize(data.numColumns_);
	data.itemPositionWithinLayout_.resize(data.numColumns_);
	for (auto x = 0; x < data.numColumns_; ++x)
	{
		data.itemGrid_[x].resize(data.numRows_);
		data.itemPositionWithinLayout_[x].resize(data.numRows_);
	}

	for (int major = 0; major<nodes.size(); ++major)
		for (int minor = 0; minor<nodes[major].size(); ++minor)
		{
			if (nodes[major][minor])
			{
				//We try to find an existing visualization of our current node, and create a new one if we don't
				auto existingIterator = existingItems.find(nodes[major][minor]);
				Item* value;
				if (existingIterator != existingItems.end() && !existingIterator.value().isEmpty())
					value = existingIterator.value().takeFirst();
				else
					value = item->renderer()->render(item, nodes[major][minor]);

				if (majorAxis_ == GridLayouter::ColumnMajor) data.itemGrid_[major][minor] = value;
				else data.itemGrid_[minor][major] = value;

				// Note that we don't need to update the position here since it will be overwritten anyway by
				// computeSize()
			}
		}

	// Set the span if any
	if (spanGetterFunction_) data.itemSpan_ = spanGetterFunction_(item);
	else
	{
		data.itemSpan_.clear();
		data.itemSpan_.resize(data.numColumns_);
		for (int x = 0; x < data.numColumns_; ++x)
			data.itemSpan_[x] = QVector<QPair<int, int>>(data.numRows_, {1, 1});
	}
}

void DynamicGridFormElement::setItemPositions(Item* item, int parentX, int parentY)
{
	auto& data = dataForItem(item);

	for (int xIt = 0; xIt<data.numColumns_; ++xIt)
		for (int yIt = 0; yIt < data.numRows_; ++yIt)
			if (auto childItem = data.itemGrid_[xIt][yIt])
			{
				// Note that margins have already been accounted for.
				QPointF newPos{parentX + x(item) + data.itemPositionWithinLayout_[xIt][yIt].x(),
							parentY + y(item) + data.itemPositionWithinLayout_[xIt][yIt].y()};

				if (newPos != childItem->pos())
					childItem->setPos(newPos); // setting the position is an expensive operation so only do it if it changed
			}
}

void DynamicGridFormElement::destroyChildItems(Item* item,
										 QList<const Item* const DeclarativeItemBase::*> handledChildren)
{
	LayoutFormElement::destroyChildItems(item, handledChildren);

	auto dataIterator = itemData_.find(item);
	if (dataIterator != itemData_.end())
	{
		for (auto list : dataIterator.value()->itemGrid_)
			for (auto item : list)
				SAFE_DELETE_ITEM(item);

		SAFE_DELETE(dataIterator.value());
		itemData_.erase(dataIterator);
	}
}

QPoint DynamicGridFormElement::focusedElementIndex(Item* item) const
{
	auto& data = dataForItem(item);

	for (int x=0; x<data.numColumns_; ++x)
		for (int y=0; y<data.numRows_; ++y)
			if ( data.itemGrid_[x][y] && data.itemGrid_[x][y]->itemOrChildHasFocus()) return QPoint(x, y);

	return QPoint(-1, -1);
}

bool DynamicGridFormElement::elementOrChildHasFocus(Item* item) const
{
	if (LayoutFormElement::elementOrChildHasFocus(item))
		return true;
	for (auto list : dataForItem(item).itemGrid_)
		for (auto item : list)
			if (item && item->itemOrChildHasFocus())
				return true;
	return false;
}

QList<ItemRegion> DynamicGridFormElement::regions(DeclarativeItemBase* item, int parentX, int parentY)
{
	auto& data = dataForItem(item);

	return GridLayouter::regions(item, this, parentX + x(item), parentY + y(item), majorAxis_, true, true, true,
			item->style()->extraCursorsOutsideShape(), true, true,
			[&data](){return data.numRows_;},	// numRows
			[&data](){return data.numColumns_;},	// numColumns
			[&data](int x, int y){return data.itemGrid_[x][y];},	// has
			[&data](int x, int y){return data.itemGrid_[x][y]->widthInParent();},	// width
			[&data](int x, int y){return data.itemGrid_[x][y]->heightInParent();},	// height
			[&data](int x, int y){return (int) data.itemGrid_[x][y]->x();},	// xPos
			[&data](int x, int y){return (int) data.itemGrid_[x][y]->y();},	// yPos
			[&data](int x, int y){return data.itemGrid_[x][y];},	// childItem
			[this](){return spaceBetweenRows_;},	// spaceBetweenRows
			[this](){return spaceBetweenColumns_;},	// spaceBetweenColumns
			[this](){return topMargin();},	// topMargin
			[this](){return bottomMargin();},	// bottomMargin
			[this](){return leftMargin();},	// leftMargin
			[this](){return rightMargin();}	// rightMargin
		);
}

} // namespace Visualization
<|endoftext|>


<commit_before>/*
 *  Copyright (c) 2014-present, Facebook, Inc.
 *  All rights reserved.
 *
 *  This source code is licensed under the BSD-style license found in the
 *  LICENSE file in the root directory of this source tree. An additional grant
 *  of patent rights can be found in the PATENTS file in the same directory.
 *
 */

#include <cstring>

#include <math.h>
#include <signal.h>

#ifndef WIN32
#include <sys/wait.h>
#endif

#include <boost/filesystem.hpp>

#include <osquery/filesystem.h>
#include <osquery/logger.h>
#include <osquery/sql.h>

#include "osquery/core/watcher.h"
#include "osquery/core/process.h"

extern char** environ;

namespace fs = boost::filesystem;

namespace osquery {

const std::map<WatchdogLimitType, std::vector<size_t>> kWatchdogLimits = {
    // Maximum MB worker can privately allocate.
    {MEMORY_LIMIT, {80, 50, 30, 1000}},
    // Percent of user or system CPU worker can utilize for LATENCY_LIMIT
    // seconds.
    {UTILIZATION_LIMIT, {90, 80, 60, 1000}},
    // Number of seconds the worker should run, else consider the exit fatal.
    {RESPAWN_LIMIT, {20, 20, 20, 5}},
    // If the worker respawns too quickly, backoff on creating additional.
    {RESPAWN_DELAY, {5, 5, 5, 1}},
    // Seconds of tolerable UTILIZATION_LIMIT sustained latency.
    {LATENCY_LIMIT, {12, 6, 3, 1}},
    // How often to poll for performance limit violations.
    {INTERVAL, {3, 3, 3, 1}},
};

CLI_FLAG(int32,
         watchdog_level,
         0,
         "Performance limit level (0=loose, 1=normal, 2=restrictive, 3=debug)");

CLI_FLAG(bool, disable_watchdog, false, "Disable userland watchdog process");

void Watcher::resetWorkerCounters(size_t respawn_time) {
  // Reset the monitoring counters for the watcher.
  auto& state = instance().state_;
  state.sustained_latency = 0;
  state.user_time = 0;
  state.system_time = 0;
  state.last_respawn_time = respawn_time;
}

void Watcher::resetExtensionCounters(const std::string& extension,
                                     size_t respawn_time) {
  WatcherLocker locker;
  auto& state = instance().extension_states_[extension];
  state.sustained_latency = 0;
  state.user_time = 0;
  state.system_time = 0;
  state.last_respawn_time = respawn_time;
}

std::string Watcher::getExtensionPath(const PlatformProcess& child) {
  for (const auto& extension : extensions()) {
    if (*extension.second == child) {
      return extension.first;
    }
  }
  return "";
}

void Watcher::removeExtensionPath(const std::string& extension) {
  WatcherLocker locker;
  instance().extensions_.erase(extension);
  instance().extension_states_.erase(extension);
}

PerformanceState& Watcher::getState(const PlatformProcess& child) {
  if (child == *instance().worker_) {
    return instance().state_;
  } else {
    return instance().extension_states_[getExtensionPath(child)];
  }
}

PerformanceState& Watcher::getState(const std::string& extension) {
  return instance().extension_states_[extension];
}

void Watcher::setExtension(const std::string& extension,
                           const std::shared_ptr<PlatformProcess>& child) {
  WatcherLocker locker;
  instance().extensions_[extension] = child;
}

void Watcher::reset(const PlatformProcess& child) {
  if (child == *instance().worker_) {
    instance().worker_ = 0;
    resetWorkerCounters(0);
    return;
  }

  // If it was not the worker pid then find the extension name to reset.
  for (const auto& extension : extensions()) {
    if (*extension.second == child) {
      setExtension(extension.first, std::make_shared<PlatformProcess>());
      resetExtensionCounters(extension.first, 0);
    }
  }
}

void Watcher::addExtensionPath(const std::string& path) {
  setExtension(path, std::make_shared<PlatformProcess>());
  resetExtensionCounters(path, 0);
}

bool Watcher::hasManagedExtensions() {
  if (instance().extensions_.size() > 0) {
    return true;
  }

  // A watchdog process may hint to a worker the number of managed extensions.
  // Setting this counter to 0 will prevent the worker from waiting for missing
  // dependent config plugins. Otherwise, its existence, will cause a worker to
  // wait for missing plugins to broadcast from managed extensions.
  return getEnvVar("OSQUERY_EXTENSIONS").is_initialized();
}

bool WatcherRunner::ok() {
  // Inspect the exit code, on success or catastrophic, end the watcher.
  auto status = Watcher::getWorkerStatus();
  if (status == EXIT_SUCCESS || status == EXIT_CATASTROPHIC) {
    return false;
  }
  // Watcher is OK to run if a worker or at least one extension exists.
  return (Watcher::getWorker().isValid() || Watcher::hasManagedExtensions());
}

void WatcherRunner::start() {
  // Set worker performance counters to an initial state.
  Watcher::resetWorkerCounters(0);

  // Enter the watch loop.
  do {
    if (use_worker_ && !watch(Watcher::getWorker())) {
      if (Watcher::fatesBound()) {
        // A signal has interrupted the watcher.
        break;
      }
      // The watcher failed, create a worker.
      createWorker();
    }

    // Loop over every managed extension and check sanity.
    std::vector<std::string> failing_extensions;
    for (const auto& extension : Watcher::extensions()) {
      if (!watch(*extension.second)) {
        if (!createExtension(extension.first)) {
          failing_extensions.push_back(extension.first);
        }
      }
    }
    // If any extension creations failed, stop managing them.
    for (const auto& failed_extension : failing_extensions) {
      Watcher::removeExtensionPath(failed_extension);
    }
    pauseMilli(getWorkerLimit(INTERVAL) * 1000);
  } while (!interrupted() && ok());
}

bool WatcherRunner::watch(const PlatformProcess& child) const {
  int status = 0;

  ProcessState result = checkChildProcessStatus(child, status);
  if (Watcher::fatesBound()) {
    // A signal was handled while the watcher was watching.
    return false;
  }

  if (!child.isValid() || result == PROCESS_ERROR) {
    // Worker does not exist or never existed.
    return false;
  } else if (result == PROCESS_STILL_ALIVE) {
    // If the inspect finds problems it will stop/restart the worker.
    if (!isChildSane(child)) {
      stopChild(child);
      return false;
    }
    return true;
  }

  if (result == PROCESS_EXITED) {
    // If the worker process existed, store the exit code.
    Watcher::instance().worker_status_ = status;
  }

  return true;
}

void WatcherRunner::stopChild(const PlatformProcess& child) const {
  child.kill();

  // Clean up the defunct (zombie) process.
  cleanupDefunctProcesses();
}

bool WatcherRunner::isChildSane(const PlatformProcess& child) const {
  auto rows =
      SQL::selectAllFrom("processes", "pid", EQUALS, INTEGER(child.pid()));
  if (rows.size() == 0) {
    // Could not find worker process?
    return false;
  }

  // Get the performance state for the worker or extension.
  size_t sustained_latency = 0;
  // Compare CPU utilization since last check.
  size_t footprint = 0;
  pid_t parent = 0;
  // IV is the check interval in seconds, and utilization is set per-second.
  auto iv = std::max(getWorkerLimit(INTERVAL), (size_t)1);

  {
    WatcherLocker locker;
    auto& state = Watcher::getState(child);
    UNSIGNED_BIGINT_LITERAL user_time = 0, system_time = 0;
    try {
      parent = AS_LITERAL(BIGINT_LITERAL, rows[0].at("parent"));
      user_time = AS_LITERAL(BIGINT_LITERAL, rows[0].at("user_time")) / iv;
      system_time = AS_LITERAL(BIGINT_LITERAL, rows[0].at("system_time")) / iv;
      footprint = AS_LITERAL(BIGINT_LITERAL, rows[0].at("resident_size"));
    } catch (const std::exception& e) {
      state.sustained_latency = 0;
    }

    // Check the difference of CPU time used since last check.
    if (user_time - state.user_time > getWorkerLimit(UTILIZATION_LIMIT) ||
        system_time - state.system_time > getWorkerLimit(UTILIZATION_LIMIT)) {
      state.sustained_latency++;
    } else {
      state.sustained_latency = 0;
    }
    // Update the current CPU time.
    state.user_time = user_time;
    state.system_time = system_time;

    // Check if the sustained difference exceeded the acceptable latency limit.
    sustained_latency = state.sustained_latency;

    // Set the memory footprint as the amount of resident bytes allocated
    // since the process image was created (estimate).
    // A more-meaningful check would limit this to writable regions.
    if (state.initial_footprint == 0) {
      state.initial_footprint = footprint;
    }

    // Set the measured/limit-applied footprint to the post-launch allocations.
    if (footprint < state.initial_footprint) {
      footprint = 0;
    } else {
      footprint = footprint - state.initial_footprint;
    }
  }

  // Only make a decision about the child sanity if it is still the watcher's
  // child. It's possible for the child to die, and its pid reused.
  if (parent != PlatformProcess::getCurrentProcess()->pid()) {
    // The child's parent is not the watcher.
    Watcher::reset(child);
    // Do not stop or call the child insane, since it is not our child.
    return true;
  }

  if (sustained_latency > 0 &&
      sustained_latency * iv >= getWorkerLimit(LATENCY_LIMIT)) {
    LOG(WARNING) << "osqueryd worker (" << child.pid()
                 << ") system performance limits exceeded";
    return false;
  }
  // Check if the private memory exceeds a memory limit.
  if (footprint > 0 && footprint > getWorkerLimit(MEMORY_LIMIT) * 1024 * 1024) {
    LOG(WARNING) << "osqueryd worker (" << child.pid()
                 << ") memory limits exceeded: " << footprint;
    return false;
  }

  // The worker is sane, no action needed.
  // Attempt to flush status logs to the well-behaved worker.
  if (use_worker_) {
    relayStatusLogs();
  }

  return true;
}

void WatcherRunner::createWorker() {
  {
    WatcherLocker locker;
    if (Watcher::getState(Watcher::getWorker()).last_respawn_time >
        getUnixTime() - getWorkerLimit(RESPAWN_LIMIT)) {
      LOG(WARNING) << "osqueryd worker respawning too quickly: "
                   << Watcher::workerRestartCount() << " times";
      Watcher::workerRestarted();
      // The configured automatic delay.
      size_t delay = getWorkerLimit(RESPAWN_DELAY) * 1000;
      // Exponential back off for quickly-respawning clients.
      delay += pow(2, Watcher::workerRestartCount()) * 1000;
      pauseMilli(delay);
    }
  }

  // Get the path of the current process.
  auto qd =
      SQL::selectAllFrom("processes",
                         "pid",
                         EQUALS,
                         INTEGER(PlatformProcess::getCurrentProcess()->pid()));
  if (qd.size() != 1 || qd[0].count("path") == 0 || qd[0]["path"].size() == 0) {
    LOG(ERROR) << "osquery watcher cannot determine process path for worker";
    Initializer::requestShutdown(EXIT_FAILURE);
    return;
  }

  // Set an environment signaling to potential plugin-dependent workers to wait
  // for extensions to broadcast.
  if (Watcher::hasManagedExtensions()) {
    setEnvVar("OSQUERY_EXTENSIONS", "true");
  }

  // Get the complete path of the osquery process binary.
  auto exec_path = fs::system_complete(fs::path(qd[0]["path"]));
  if (!safePermissions(
          exec_path.parent_path().string(), exec_path.string(), true)) {
    // osqueryd binary has become unsafe.
    LOG(ERROR) << RLOG(1382)
               << "osqueryd has unsafe permissions: " << exec_path.string();
    Initializer::requestShutdown(EXIT_FAILURE);
    return;
  }

  auto worker = PlatformProcess::launchWorker(exec_path.string(), argc_, argv_);
  if (worker == nullptr) {
    // Unrecoverable error, cannot create a worker process.
    LOG(ERROR) << "osqueryd could not create a worker process";
    Initializer::shutdown(EXIT_FAILURE);
    return;
  }

  Watcher::setWorker(worker);
  Watcher::resetWorkerCounters(getUnixTime());
  VLOG(1) << "osqueryd watcher (" << PlatformProcess::getCurrentProcess()->pid()
          << ") executing worker (" << worker->pid() << ")";
}

bool WatcherRunner::createExtension(const std::string& extension) {
  {
    WatcherLocker locker;
    if (Watcher::getState(extension).last_respawn_time >
        getUnixTime() - getWorkerLimit(RESPAWN_LIMIT)) {
      LOG(WARNING) << "Extension respawning too quickly: " << extension;
      // Unlike a worker, if an extension respawns to quickly we give up.
      return false;
    }
  }

  // Check the path to the previously-discovered extension binary.
  auto exec_path = fs::system_complete(fs::path(extension));
  if (!safePermissions(
          exec_path.parent_path().string(), exec_path.string(), true)) {
    // Extension binary has become unsafe.
    LOG(WARNING) << RLOG(1382)
                 << "Extension binary has unsafe permissions: " << extension;
    return false;
  }

  auto ext_process =
      PlatformProcess::launchExtension(exec_path.string(),
                                       extension,
                                       Flag::getValue("extensions_socket"),
                                       Flag::getValue("extensions_timeout"),
                                       Flag::getValue("extensions_interval"),
                                       Flag::getValue("verbose"));
  if (ext_process == nullptr) {
    // Unrecoverable error, cannot create an extension process.
    LOG(ERROR) << "Cannot create extension process: " << extension;
    Initializer::shutdown(EXIT_FAILURE);
  }

  Watcher::setExtension(extension, ext_process);
  Watcher::resetExtensionCounters(extension, getUnixTime());
  VLOG(1) << "Created and monitoring extension child (" << ext_process->pid()
          << "): " << extension;

  return true;
}

void WatcherWatcherRunner::start() {
  while (!interrupted()) {
    if (isLauncherProcessDead(*watcher_)) {
      // Watcher died, the worker must follow.
      VLOG(1) << "osqueryd worker ("
              << PlatformProcess::getCurrentProcess()->pid()
              << ") detected killed watcher (" << watcher_->pid() << ")";
      // The watcher watcher is a thread. Do not join services after removing.
      Initializer::requestShutdown();
      break;
    }
    pauseMilli(getWorkerLimit(INTERVAL) * 1000);
  }
}

size_t getWorkerLimit(WatchdogLimitType name, int level) {
  if (kWatchdogLimits.count(name) == 0) {
    return 0;
  }

  // If no level was provided then use the default (config/switch).
  if (level == -1) {
    level = FLAGS_watchdog_level;
  }
  if (level > 3) {
    return kWatchdogLimits.at(name).back();
  }
  return kWatchdogLimits.at(name).at(level);
}
}

<commit_msg>Simplify watchdog limits configuration (#2173)<commit_after>/*
 *  Copyright (c) 2014-present, Facebook, Inc.
 *  All rights reserved.
 *
 *  This source code is licensed under the BSD-style license found in the
 *  LICENSE file in the root directory of this source tree. An additional grant
 *  of patent rights can be found in the PATENTS file in the same directory.
 *
 */

#include <cstring>

#include <math.h>
#include <signal.h>

#ifndef WIN32
#include <sys/wait.h>
#endif

#include <boost/filesystem.hpp>

#include <osquery/filesystem.h>
#include <osquery/logger.h>
#include <osquery/sql.h>

#include "osquery/core/watcher.h"
#include "osquery/core/process.h"

extern char** environ;

namespace fs = boost::filesystem;

namespace osquery {

struct LimitDefinition {
  size_t normal;
  size_t restrictive;
  size_t disabled;
};

using WatchdogLimitMap = std::map<WatchdogLimitType, LimitDefinition>;

const WatchdogLimitMap kWatchdogLimits = {
    // Maximum MB worker can privately allocate.
    {MEMORY_LIMIT, {100, 50, 1000}},
    // User or system CPU worker can utilize for LATENCY_LIMIT seconds.
    {UTILIZATION_LIMIT, {90, 80, 1000}},
    // Number of seconds the worker should run, else consider the exit fatal.
    {RESPAWN_LIMIT, {20, 20, 1000}},
    // If the worker respawns too quickly, backoff on creating additional.
    {RESPAWN_DELAY, {5, 5, 1}},
    // Seconds of tolerable UTILIZATION_LIMIT sustained latency.
    {LATENCY_LIMIT, {12, 6, 1000}},
    // How often to poll for performance limit violations.
    {INTERVAL, {3, 3, 3}},
};

CLI_FLAG(int32,
         watchdog_level,
         0,
         "Performance limit level (0=normal, 1=restrictive, -1=off)");

CLI_FLAG(bool, disable_watchdog, false, "Disable userland watchdog process");

void Watcher::resetWorkerCounters(size_t respawn_time) {
  // Reset the monitoring counters for the watcher.
  auto& state = instance().state_;
  state.sustained_latency = 0;
  state.user_time = 0;
  state.system_time = 0;
  state.last_respawn_time = respawn_time;
}

void Watcher::resetExtensionCounters(const std::string& extension,
                                     size_t respawn_time) {
  WatcherLocker locker;
  auto& state = instance().extension_states_[extension];
  state.sustained_latency = 0;
  state.user_time = 0;
  state.system_time = 0;
  state.last_respawn_time = respawn_time;
}

std::string Watcher::getExtensionPath(const PlatformProcess& child) {
  for (const auto& extension : extensions()) {
    if (*extension.second == child) {
      return extension.first;
    }
  }
  return "";
}

void Watcher::removeExtensionPath(const std::string& extension) {
  WatcherLocker locker;
  instance().extensions_.erase(extension);
  instance().extension_states_.erase(extension);
}

PerformanceState& Watcher::getState(const PlatformProcess& child) {
  if (child == *instance().worker_) {
    return instance().state_;
  } else {
    return instance().extension_states_[getExtensionPath(child)];
  }
}

PerformanceState& Watcher::getState(const std::string& extension) {
  return instance().extension_states_[extension];
}

void Watcher::setExtension(const std::string& extension,
                           const std::shared_ptr<PlatformProcess>& child) {
  WatcherLocker locker;
  instance().extensions_[extension] = child;
}

void Watcher::reset(const PlatformProcess& child) {
  if (child == *instance().worker_) {
    instance().worker_ = 0;
    resetWorkerCounters(0);
    return;
  }

  // If it was not the worker pid then find the extension name to reset.
  for (const auto& extension : extensions()) {
    if (*extension.second == child) {
      setExtension(extension.first, std::make_shared<PlatformProcess>());
      resetExtensionCounters(extension.first, 0);
    }
  }
}

void Watcher::addExtensionPath(const std::string& path) {
  setExtension(path, std::make_shared<PlatformProcess>());
  resetExtensionCounters(path, 0);
}

bool Watcher::hasManagedExtensions() {
  if (instance().extensions_.size() > 0) {
    return true;
  }

  // A watchdog process may hint to a worker the number of managed extensions.
  // Setting this counter to 0 will prevent the worker from waiting for missing
  // dependent config plugins. Otherwise, its existence, will cause a worker to
  // wait for missing plugins to broadcast from managed extensions.
  return getEnvVar("OSQUERY_EXTENSIONS").is_initialized();
}

bool WatcherRunner::ok() {
  // Inspect the exit code, on success or catastrophic, end the watcher.
  auto status = Watcher::getWorkerStatus();
  if (status == EXIT_SUCCESS || status == EXIT_CATASTROPHIC) {
    return false;
  }
  // Watcher is OK to run if a worker or at least one extension exists.
  return (Watcher::getWorker().isValid() || Watcher::hasManagedExtensions());
}

void WatcherRunner::start() {
  // Set worker performance counters to an initial state.
  Watcher::resetWorkerCounters(0);

  // Enter the watch loop.
  do {
    if (use_worker_ && !watch(Watcher::getWorker())) {
      if (Watcher::fatesBound()) {
        // A signal has interrupted the watcher.
        break;
      }
      // The watcher failed, create a worker.
      createWorker();
    }

    // Loop over every managed extension and check sanity.
    std::vector<std::string> failing_extensions;
    for (const auto& extension : Watcher::extensions()) {
      if (!watch(*extension.second)) {
        if (!createExtension(extension.first)) {
          failing_extensions.push_back(extension.first);
        }
      }
    }
    // If any extension creations failed, stop managing them.
    for (const auto& failed_extension : failing_extensions) {
      Watcher::removeExtensionPath(failed_extension);
    }
    pauseMilli(getWorkerLimit(INTERVAL) * 1000);
  } while (!interrupted() && ok());
}

bool WatcherRunner::watch(const PlatformProcess& child) const {
  int status = 0;

  ProcessState result = checkChildProcessStatus(child, status);
  if (Watcher::fatesBound()) {
    // A signal was handled while the watcher was watching.
    return false;
  }

  if (!child.isValid() || result == PROCESS_ERROR) {
    // Worker does not exist or never existed.
    return false;
  } else if (result == PROCESS_STILL_ALIVE) {
    // If the inspect finds problems it will stop/restart the worker.
    if (!isChildSane(child)) {
      stopChild(child);
      return false;
    }
    return true;
  }

  if (result == PROCESS_EXITED) {
    // If the worker process existed, store the exit code.
    Watcher::instance().worker_status_ = status;
  }

  return true;
}

void WatcherRunner::stopChild(const PlatformProcess& child) const {
  child.kill();

  // Clean up the defunct (zombie) process.
  cleanupDefunctProcesses();
}

bool WatcherRunner::isChildSane(const PlatformProcess& child) const {
  auto rows =
      SQL::selectAllFrom("processes", "pid", EQUALS, INTEGER(child.pid()));
  if (rows.size() == 0) {
    // Could not find worker process?
    return false;
  }

  // Get the performance state for the worker or extension.
  size_t sustained_latency = 0;
  // Compare CPU utilization since last check.
  size_t footprint = 0;
  pid_t parent = 0;
  // IV is the check interval in seconds, and utilization is set per-second.
  auto iv = std::max(getWorkerLimit(INTERVAL), (size_t)1);

  {
    WatcherLocker locker;
    auto& state = Watcher::getState(child);
    UNSIGNED_BIGINT_LITERAL user_time = 0, system_time = 0;
    try {
      parent = AS_LITERAL(BIGINT_LITERAL, rows[0].at("parent"));
      user_time = AS_LITERAL(BIGINT_LITERAL, rows[0].at("user_time")) / iv;
      system_time = AS_LITERAL(BIGINT_LITERAL, rows[0].at("system_time")) / iv;
      footprint = AS_LITERAL(BIGINT_LITERAL, rows[0].at("resident_size"));
    } catch (const std::exception& e) {
      state.sustained_latency = 0;
    }

    // Check the difference of CPU time used since last check.
    if (user_time - state.user_time > getWorkerLimit(UTILIZATION_LIMIT) ||
        system_time - state.system_time > getWorkerLimit(UTILIZATION_LIMIT)) {
      state.sustained_latency++;
    } else {
      state.sustained_latency = 0;
    }
    // Update the current CPU time.
    state.user_time = user_time;
    state.system_time = system_time;

    // Check if the sustained difference exceeded the acceptable latency limit.
    sustained_latency = state.sustained_latency;

    // Set the memory footprint as the amount of resident bytes allocated
    // since the process image was created (estimate).
    // A more-meaningful check would limit this to writable regions.
    if (state.initial_footprint == 0) {
      state.initial_footprint = footprint;
    }

    // Set the measured/limit-applied footprint to the post-launch allocations.
    if (footprint < state.initial_footprint) {
      footprint = 0;
    } else {
      footprint = footprint - state.initial_footprint;
    }
  }

  // Only make a decision about the child sanity if it is still the watcher's
  // child. It's possible for the child to die, and its pid reused.
  if (parent != PlatformProcess::getCurrentProcess()->pid()) {
    // The child's parent is not the watcher.
    Watcher::reset(child);
    // Do not stop or call the child insane, since it is not our child.
    return true;
  }

  if (sustained_latency > 0 &&
      sustained_latency * iv >= getWorkerLimit(LATENCY_LIMIT)) {
    LOG(WARNING) << "osqueryd worker (" << child.pid()
                 << ") system performance limits exceeded";
    return false;
  }
  // Check if the private memory exceeds a memory limit.
  if (footprint > 0 && footprint > getWorkerLimit(MEMORY_LIMIT) * 1024 * 1024) {
    LOG(WARNING) << "osqueryd worker (" << child.pid()
                 << ") memory limits exceeded: " << footprint;
    return false;
  }

  // The worker is sane, no action needed.
  // Attempt to flush status logs to the well-behaved worker.
  if (use_worker_) {
    relayStatusLogs();
  }

  return true;
}

void WatcherRunner::createWorker() {
  {
    WatcherLocker locker;
    if (Watcher::getState(Watcher::getWorker()).last_respawn_time >
        getUnixTime() - getWorkerLimit(RESPAWN_LIMIT)) {
      LOG(WARNING) << "osqueryd worker respawning too quickly: "
                   << Watcher::workerRestartCount() << " times";
      Watcher::workerRestarted();
      // The configured automatic delay.
      size_t delay = getWorkerLimit(RESPAWN_DELAY) * 1000;
      // Exponential back off for quickly-respawning clients.
      delay += pow(2, Watcher::workerRestartCount()) * 1000;
      pauseMilli(delay);
    }
  }

  // Get the path of the current process.
  auto qd =
      SQL::selectAllFrom("processes",
                         "pid",
                         EQUALS,
                         INTEGER(PlatformProcess::getCurrentProcess()->pid()));
  if (qd.size() != 1 || qd[0].count("path") == 0 || qd[0]["path"].size() == 0) {
    LOG(ERROR) << "osquery watcher cannot determine process path for worker";
    Initializer::requestShutdown(EXIT_FAILURE);
    return;
  }

  // Set an environment signaling to potential plugin-dependent workers to wait
  // for extensions to broadcast.
  if (Watcher::hasManagedExtensions()) {
    setEnvVar("OSQUERY_EXTENSIONS", "true");
  }

  // Get the complete path of the osquery process binary.
  auto exec_path = fs::system_complete(fs::path(qd[0]["path"]));
  if (!safePermissions(
          exec_path.parent_path().string(), exec_path.string(), true)) {
    // osqueryd binary has become unsafe.
    LOG(ERROR) << RLOG(1382)
               << "osqueryd has unsafe permissions: " << exec_path.string();
    Initializer::requestShutdown(EXIT_FAILURE);
    return;
  }

  auto worker = PlatformProcess::launchWorker(exec_path.string(), argc_, argv_);
  if (worker == nullptr) {
    // Unrecoverable error, cannot create a worker process.
    LOG(ERROR) << "osqueryd could not create a worker process";
    Initializer::shutdown(EXIT_FAILURE);
    return;
  }

  Watcher::setWorker(worker);
  Watcher::resetWorkerCounters(getUnixTime());
  VLOG(1) << "osqueryd watcher (" << PlatformProcess::getCurrentProcess()->pid()
          << ") executing worker (" << worker->pid() << ")";
}

bool WatcherRunner::createExtension(const std::string& extension) {
  {
    WatcherLocker locker;
    if (Watcher::getState(extension).last_respawn_time >
        getUnixTime() - getWorkerLimit(RESPAWN_LIMIT)) {
      LOG(WARNING) << "Extension respawning too quickly: " << extension;
      // Unlike a worker, if an extension respawns to quickly we give up.
      return false;
    }
  }

  // Check the path to the previously-discovered extension binary.
  auto exec_path = fs::system_complete(fs::path(extension));
  if (!safePermissions(
          exec_path.parent_path().string(), exec_path.string(), true)) {
    // Extension binary has become unsafe.
    LOG(WARNING) << RLOG(1382)
                 << "Extension binary has unsafe permissions: " << extension;
    return false;
  }

  auto ext_process =
      PlatformProcess::launchExtension(exec_path.string(),
                                       extension,
                                       Flag::getValue("extensions_socket"),
                                       Flag::getValue("extensions_timeout"),
                                       Flag::getValue("extensions_interval"),
                                       Flag::getValue("verbose"));
  if (ext_process == nullptr) {
    // Unrecoverable error, cannot create an extension process.
    LOG(ERROR) << "Cannot create extension process: " << extension;
    Initializer::shutdown(EXIT_FAILURE);
  }

  Watcher::setExtension(extension, ext_process);
  Watcher::resetExtensionCounters(extension, getUnixTime());
  VLOG(1) << "Created and monitoring extension child (" << ext_process->pid()
          << "): " << extension;

  return true;
}

void WatcherWatcherRunner::start() {
  while (!interrupted()) {
    if (isLauncherProcessDead(*watcher_)) {
      // Watcher died, the worker must follow.
      VLOG(1) << "osqueryd worker ("
              << PlatformProcess::getCurrentProcess()->pid()
              << ") detected killed watcher (" << watcher_->pid() << ")";
      // The watcher watcher is a thread. Do not join services after removing.
      Initializer::requestShutdown();
      break;
    }
    pauseMilli(getWorkerLimit(INTERVAL) * 1000);
  }
}

size_t getWorkerLimit(WatchdogLimitType name, int level) {
  if (kWatchdogLimits.count(name) == 0) {
    return 0;
  }

  // If no level was provided then use the default (config/switch).
  if (level == -1) {
    return kWatchdogLimits.at(name).disabled;
  }

  if (level == 1) {
    return kWatchdogLimits.at(name).restrictive;
  }
  return kWatchdogLimits.at(name).normal;
}
}
<|endoftext|>


<commit_before>/* $Id$ */

/*
 * Copyright (c) 2010 .SE (The Internet Infrastructure Foundation)
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/*****************************************************************************
 BotanHashAlgorithm.cpp

 Base class for Botan hash algorithm classes
 *****************************************************************************/

#include "config.h"
#include "BotanHashAlgorithm.h"
#include <botan/filters.h>
using namespace Botan;

// Base constructor
BotanHashAlgorithm::BotanHashAlgorithm()
{
	hash = NULL;
}

// Destructor
BotanHashAlgorithm::~BotanHashAlgorithm()
{
	delete hash;
}

// Hashing functions
bool BotanHashAlgorithm::hashInit()
{
	if (!HashAlgorithm::hashInit())
	{
		return false;
	}

	// Initialise digesting
	if (hash == NULL)
	{
		hash = getHash();
	}
	else
	{
		hash->clear();
	}

	return true;
}

bool BotanHashAlgorithm::hashUpdate(const ByteString& data)
{
	if (!HashAlgorithm::hashUpdate(data))
	{
		return false;
	}

	// Continue digesting
	hash->update(data.const_byte_str(), data.size());

	return true;
}

bool BotanHashAlgorithm::hashFinal(ByteString& hashedData)
{
	if (!HashAlgorithm::hashFinal(hashedData))
	{
		return false;
	}

	hashedData.resize(hash->OUTPUT_LENGTH);
	unsigned int outLen = hashedData.size();

	hash->final(&hashedData[0]);

	return true;
}
<commit_msg>More comments<commit_after>/* $Id$ */

/*
 * Copyright (c) 2010 .SE (The Internet Infrastructure Foundation)
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/*****************************************************************************
 BotanHashAlgorithm.cpp

 Base class for Botan hash algorithm classes
 *****************************************************************************/

#include "config.h"
#include "BotanHashAlgorithm.h"
#include <botan/filters.h>
using namespace Botan;

// Base constructor
BotanHashAlgorithm::BotanHashAlgorithm()
{
	hash = NULL;
}

// Destructor
BotanHashAlgorithm::~BotanHashAlgorithm()
{
	delete hash;
}

// Hashing functions
bool BotanHashAlgorithm::hashInit()
{
	if (!HashAlgorithm::hashInit())
	{
		return false;
	}

	// Initialise digesting
	if (hash == NULL)
	{
		hash = getHash();
	}
	else
	{
		hash->clear();
	}

	return true;
}

bool BotanHashAlgorithm::hashUpdate(const ByteString& data)
{
	if (!HashAlgorithm::hashUpdate(data))
	{
		return false;
	}

	// Continue digesting
	hash->update(data.const_byte_str(), data.size());

	return true;
}

bool BotanHashAlgorithm::hashFinal(ByteString& hashedData)
{
	if (!HashAlgorithm::hashFinal(hashedData))
	{
		return false;
	}

	// Resize
	hashedData.resize(hash->OUTPUT_LENGTH);
	unsigned int outLen = hashedData.size();

	// Read the digest
	hash->final(&hashedData[0]);

	return true;
}
<|endoftext|>


<commit_before>// ======================================================================
//
// Copyright (c) 2008-2022 Inc. All rights reserved.
//
// ߣ
// 䣺china0851@foxmail.com
//
// ======================================================================

#include <Extend/Controls/GifBox.h>

namespace suic
{

ImplementRTTIOfClass(GifBox, suic::FrameworkElement)

suic::DpProperty* GifBox::UriProperty;
suic::DpProperty* GifBox::PlayProperty;

GifBox::GifBox()
    : _parser(NULL)
{
    GifBox::StaticInit();
    _timer = new suic::AssignerTimer();
    _timer->ref();
}

GifBox::~GifBox()
{
    if (NULL != _parser)
    {
        _parser->unref();
    }

    if (NULL != _timer)
    {
        _timer->unref();
    }
}

void GifBox::OnUriPropChanged(suic::DpObject* d, suic::DpPropChangedEventArg* e)
{
    GifBox* gifBox = suic::RTTICast<GifBox>(d);
    suic::ResourceUri resUri(e->GetNewValue()->ToString());
    if (NULL != gifBox->_parser)
    {
        gifBox->_parser->unref();
    }
    gifBox->_parser = new suic::GIFParser();
    gifBox->_parser->ref();
    gifBox->_parser->LoadUri(&resUri);
    gifBox->_parser->MoveNext();
}

void GifBox::OnPlayPropChanged(suic::DpObject* d, suic::DpPropChangedEventArg* e)
{
}

void GifBox::StaticInit()
{
    if (NULL == UriProperty)
    {
        UriProperty = suic::DpProperty::Register(_U("Uri"), RTTIType(), suic::OString::RTTIType(),
            suic::DpPropMemory::GetPropMeta(NULL, suic::PropMetadataOptions::AffectsParentMeasure, &GifBox::OnUriPropChanged));
        PlayProperty = suic::DpProperty::Register(_U("Play"), RTTIType(), suic::Boolean::RTTIType(),
            suic::DpPropMemory::GetPropMeta(suic::Boolean::False, suic::PropMetadataOptions::AffectsRender, &GifBox::OnPlayPropChanged));
    }
}

suic::String GifBox::GetUri()
{
    return GetValue(UriProperty)->ToString();
}

void GifBox::SetUri(suic::String uri)
{
    SetValue(UriProperty, new suic::OString(uri));
}

bool GifBox::IsPlay()
{
    return GetValue(PlayProperty)->ToBool();
}

suic::Size GifBox::OnMeasure(const suic::Size& constraint)
{
    suic::Size measureSize;
    if (NULL != _parser)
    {
        suic::ImageParse::Info info;
        _parser->GetInfo(info);
        measureSize.cx = info.wid;
        measureSize.cy = info.hei;
    }
    return measureSize;
}

void GifBox::OnArrange(const suic::Size& arrangeSize)
{
    FrameworkElement::OnArrange(arrangeSize);
}

void GifBox::OnRender(suic::Drawing * drawing)
{
    suic::FrameworkElement::OnRender(drawing);

    if (_bitmap.IsValid())
    {
        suic::fRect rect(0, 0, GetActualWidth(), GetActualHeight());
        suic::fRect rcImg(0, 0, _bitmap.Width(), _bitmap.Height());

        suic::Float width = 1.0;
        suic::Float height = 1.0;

        width = rect.Width() / rcImg.Width();
        height = rect.Height() / rcImg.Height();

        suic::Float fRatio = (width < height) ? width : height;

        if (fRatio > 1)
        {
            fRatio = 1.0f;
        }

        suic::Float w = rcImg.Width() * fRatio;
        suic::Float h = rcImg.Height() * fRatio;

        rect.left = (rect.Width() - w) / 2.0f;
        rect.top = (rect.Height() - h) / 2.0f;
        rect.right = rect.left + w;
        rect.bottom = rect.top + h;

        drawing->DrawImage(DrawCtx::DefDraw, &_bitmap, rect, rcImg);
    }
}

void GifBox::OnTick(suic::Object* sender, suic::EventArg* e)
{
    if (NULL != _parser)
    {
        int iDelay = 100;
        _parser->MoveNext();
        iDelay = _parser->GetDelay();
        _parser->GetImage(_bitmap);

        if (iDelay <= 0)
        {
            iDelay = 100;
        }

        _timer->Stop();
        _timer->SetInterval(iDelay);
        _timer->Start();

        InvalidateVisual();
    }
}

void GifBox::OnInitialized(suic::EventArg* e)
{
    FrameworkElement::OnInitialized(e);
    _timer->SetTick(suic::EventHandler(this, &GifBox::OnTick));

    if (IsPlay())
    {
        _timer->Start();
    }
}

void GifBox::OnLoaded(suic::LoadedEventArg* e)
{
    FrameworkElement::OnLoaded(e);
}

}
<commit_msg>解决Gif动画控件文件不可用崩溃问题<commit_after>// ======================================================================
//
// Copyright (c) 2008-2022 Inc. All rights reserved.
//
// ߣ
// 䣺china0851@foxmail.com
//
// ======================================================================

#include <Extend/Controls/GifBox.h>

namespace suic
{

ImplementRTTIOfClass(GifBox, suic::FrameworkElement)

suic::DpProperty* GifBox::UriProperty;
suic::DpProperty* GifBox::PlayProperty;

GifBox::GifBox()
    : _parser(NULL)
{
    GifBox::StaticInit();
    _timer = new suic::AssignerTimer();
    _timer->ref();
}

GifBox::~GifBox()
{
    if (NULL != _parser)
    {
        _parser->unref();
    }

    if (NULL != _timer)
    {
        _timer->unref();
    }
}

void GifBox::OnUriPropChanged(suic::DpObject* d, suic::DpPropChangedEventArg* e)
{
    GifBox* gifBox = suic::RTTICast<GifBox>(d);
    suic::ResourceUri resUri(e->GetNewValue()->ToString());
    if (NULL != gifBox->_parser)
    {
        gifBox->_parser->unref();
    }
    gifBox->_parser = new suic::GIFParser();
    gifBox->_parser->ref();
    gifBox->_parser->LoadUri(&resUri);
    gifBox->_parser->MoveNext();
}

void GifBox::OnPlayPropChanged(suic::DpObject* d, suic::DpPropChangedEventArg* e)
{
    GifBox* gifBox = suic::RTTICast<GifBox>(d);
    if (NULL != gifBox)
    {
        gifBox->StartGif();
    }
}

void GifBox::StaticInit()
{
    if (NULL == UriProperty)
    {
        UriProperty = suic::DpProperty::Register(_U("Uri"), RTTIType(), suic::OString::RTTIType(),
            suic::DpPropMemory::GetPropMeta(NULL, suic::PropMetadataOptions::AffectsParentMeasure, &GifBox::OnUriPropChanged));
        PlayProperty = suic::DpProperty::Register(_U("Play"), RTTIType(), suic::Boolean::RTTIType(),
            suic::DpPropMemory::GetPropMeta(suic::Boolean::False, suic::PropMetadataOptions::AffectsRender, &GifBox::OnPlayPropChanged));
    }
}

suic::String GifBox::GetUri()
{
    return GetValue(UriProperty)->ToString();
}

void GifBox::SetUri(suic::String uri)
{
    SetValue(UriProperty, new suic::OString(uri));
}

bool GifBox::IsPlay()
{
    return GetValue(PlayProperty)->ToBool();
}

suic::Size GifBox::OnMeasure(const suic::Size& constraint)
{
    suic::Size measureSize;
    if (NULL != _parser)
    {
        suic::ImageParse::Info info;
        _parser->GetInfo(info);
        measureSize.cx = info.wid;
        measureSize.cy = info.hei;
    }
    return measureSize;
}

void GifBox::OnArrange(const suic::Size& arrangeSize)
{
    FrameworkElement::OnArrange(arrangeSize);
}

void GifBox::OnRender(suic::Drawing * drawing)
{
    suic::FrameworkElement::OnRender(drawing);

    if (_bitmap.IsValid())
    {
        suic::fRect rect(0, 0, GetActualWidth(), GetActualHeight());
        suic::fRect rcImg(0, 0, _bitmap.Width(), _bitmap.Height());

        suic::Float width = 1.0;
        suic::Float height = 1.0;

        width = rect.Width() / rcImg.Width();
        height = rect.Height() / rcImg.Height();

        suic::Float fRatio = (width < height) ? width : height;

        if (fRatio > 1)
        {
            fRatio = 1.0f;
        }

        suic::Float w = rcImg.Width() * fRatio;
        suic::Float h = rcImg.Height() * fRatio;

        rect.left = (rect.Width() - w) / 2.0f;
        rect.top = (rect.Height() - h) / 2.0f;
        rect.right = rect.left + w;
        rect.bottom = rect.top + h;

        drawing->DrawImage(DrawCtx::DefDraw, &_bitmap, rect, rcImg);
    }
}

void GifBox::OnTick(suic::Object* sender, suic::EventArg* e)
{
    if (NULL != _parser)
    {
        int iDelay = 100;
        _parser->MoveNext();
        iDelay = _parser->GetDelay();
        _parser->GetImage(_bitmap);

        if (iDelay <= 0)
        {
            iDelay = 100;
        }

        _timer->Stop();
        _timer->SetInterval(iDelay);
        _timer->Start();

        InvalidateVisual();
    }
}

void GifBox::OnInitialized(suic::EventArg* e)
{
    FrameworkElement::OnInitialized(e);
    _timer->SetTick(suic::EventHandler(this, &GifBox::OnTick));

    StartGif();
}

void GifBox::OnLoaded(suic::LoadedEventArg* e)
{
    FrameworkElement::OnLoaded(e);
}

void GifBox::StartGif()
{
    if (IsValid() && IsPlay())
    {
        _timer->Start();
    }
}

}
<|endoftext|>


<commit_before>#include "refWindow.h"
#include "ui_refWindow.h"
#include "../kernel/ids.h"

RefWindow::RefWindow(const qrRepo::RepoApi *mApi, QString name,
					 QAbstractItemModel* tModel, int r, const QModelIndex &ind,
					 qReal::MainWindow *mWindow, QWidget *parent):
	QWidget(parent),
	ui(new Ui::RefWindow),
	api(mApi),
	typeName(name),
	model(tModel),
	role(r),
	index(ind),
	mainWindow(mWindow),
	mItem(NULL)
{
	ui->setupUi(this);
	qReal::IdList idList = api->elementsByType(typeName);
	int size = idList.size();
	ui->listWidget->setSelectionMode(QAbstractItemView::MultiSelection);
	QString str = model->data(index, role).toString();
	int sizeStr = str.count("$$");
	for (int i = 0; i < size; ++i)
	{
		qReal::IdList parentsIdList = api->parents(idList[i]);
		int parentsListSize = parentsIdList.size();
		for (int j = 0; j < parentsListSize; ++j)
		{
			QString parentName = api->name(parentsIdList[j]);
			if (parentName.contains("Diagram"))
			{
				QListWidgetItem *item = new QListWidgetItem();
				QString text = parentName + "::" + api->name(idList[i]);
				item->setText(text);
				QVariant val = idList[i].toString();
				item->setData(Qt::ToolTipRole, val);
				ui->listWidget->addItem(item);
				for (int k = 0; k < sizeStr; ++k)
				{
					if (val == str.section("$$", k, k))
						item->setSelected(true);
				}
			}
		}
	}
	ui->mButtonOk->setEnabled(false);
	connect(ui->listWidget, SIGNAL(itemClicked(QListWidgetItem*)), this,
			SLOT(highlightElement(QListWidgetItem*)));
	connect(ui->mButtonCancel, SIGNAL(clicked()), this, SLOT(cancel()));
	connect(ui->listWidget, SIGNAL(itemClicked(QListWidgetItem*)), this,
			SLOT(enableOkButton(QListWidgetItem*)));
	connect(ui->mButtonOk, SIGNAL(clicked()), this, SLOT(setElementId()));
}

RefWindow::~RefWindow()
{
	delete ui;
}

void RefWindow::keyPressEvent(QKeyEvent *event)
{
	if ((event->key()) == (Qt::Key_Escape))
	{
		if (mItem)
			highlightElement(mItem, false);
		close();
	}
	if (((event->key()) == (Qt::Key_Return)) && (ui->mButtonOk->isEnabled()))
	{
		setPropertyValue();
		setElementId();
	}
}

void RefWindow::setPropertyValue()
{
	QList<QListWidgetItem *> itemList = ui->listWidget->selectedItems();
	if (!itemList.isEmpty())
	{
		QString idStr(NULL);
		for (int i = 0; i < itemList.size(); ++i)
			idStr.append(itemList[i]->data(Qt::ToolTipRole).toString() + "$$");
		model->setData(index, idStr, role);
	}
	close();
}

void RefWindow::highlightElement(QListWidgetItem *item, bool bl)
{
	Q_UNUSED(bl)
	mItem = item;
	qReal::Id const id = qReal::Id::loadFromString(item->data(Qt::ToolTipRole).toString());
	if (item->isSelected())
	{
		mainWindow->activateItemOrDiagram(id, true, false);
		elementList << item;
	}
	else
	{
		mainWindow->activateItemOrDiagram(id, false, false);
	}
}

void RefWindow::cancel()
{
	QVariant data = model->data(index, role);
	model->setData(index, data, role);
	setElementId();
	close();
}

void RefWindow::enableOkButton(QListWidgetItem* item)
{
	Q_UNUSED(item);
	ui->mButtonOk->setEnabled(true);
}

void RefWindow::setElementId()
{
	for (int i = 0; i < elementList.size(); ++i)
	{
		qReal::Id const id = qReal::Id::loadFromString(elementList[i]->data(Qt::ToolTipRole).toString());
		mainWindow->activateItemOrDiagram(id, false, false);
	}
	mainWindow->activateItemOrDiagram(index, false);
}
<commit_msg>Qt 4.7 build fixed.<commit_after>#include "refWindow.h"
#include "ui_refWindow.h"
#include "../kernel/ids.h"

RefWindow::RefWindow(const qrRepo::RepoApi *mApi, QString name,
					 QAbstractItemModel* tModel, int r, const QModelIndex &ind,
					 qReal::MainWindow *mWindow, QWidget *parent):
	QWidget(parent),
	ui(new Ui::RefWindow),
	api(mApi),
	typeName(name),
	model(tModel),
	role(r),
	index(ind),
	mainWindow(mWindow),
	mItem(NULL)
{
	ui->setupUi(this);
	qReal::IdList idList = api->elementsByType(typeName);
	int size = idList.size();
	ui->listWidget->setSelectionMode(QAbstractItemView::MultiSelection);
	QString str = model->data(index, role).toString();
	int sizeStr = str.count("$$");
	for (int i = 0; i < size; ++i)
	{
		qReal::IdList parentsIdList = api->parents(idList[i]);
		int parentsListSize = parentsIdList.size();
		for (int j = 0; j < parentsListSize; ++j)
		{
			QString parentName = api->name(parentsIdList[j]);
			if (parentName.contains("Diagram"))
			{
				QListWidgetItem *item = new QListWidgetItem();
				QString text = parentName + "::" + api->name(idList[i]);
				item->setText(text);
				QVariant val = idList[i].toString();
				item->setData(Qt::ToolTipRole, val);
				ui->listWidget->addItem(item);
				for (int k = 0; k < sizeStr; ++k)
				{
					if (val == str.section("$$", k, k))
						item->setSelected(true);
				}
			}
		}
	}
	ui->mButtonOk->setEnabled(false);
	connect(ui->listWidget, SIGNAL(itemClicked(QListWidgetItem*)), this,
			SLOT(highlightElement(QListWidgetItem*)));
	connect(ui->mButtonCancel, SIGNAL(clicked()), this, SLOT(cancel()));
	connect(ui->listWidget, SIGNAL(itemClicked(QListWidgetItem*)), this,
			SLOT(enableOkButton(QListWidgetItem*)));
	connect(ui->mButtonOk, SIGNAL(clicked()), this, SLOT(setElementId()));
}

RefWindow::~RefWindow()
{
	delete ui;
}

void RefWindow::keyPressEvent(QKeyEvent *event)
{
	if ((event->key()) == (Qt::Key_Escape))
	{
		if (mItem)
			highlightElement(mItem, false);
		close();
	}
	if (((event->key()) == (Qt::Key_Return)) && (ui->mButtonOk->isEnabled()))
	{
		setPropertyValue();
		setElementId();
	}
}

void RefWindow::setPropertyValue()
{
	QList<QListWidgetItem *> itemList = ui->listWidget->selectedItems();
	if (!itemList.isEmpty())
	{
		QString idStr;
		for (int i = 0; i < itemList.size(); ++i)
			idStr.append(itemList[i]->data(Qt::ToolTipRole).toString() + "$$");
		model->setData(index, idStr, role);
	}
	close();
}

void RefWindow::highlightElement(QListWidgetItem *item, bool bl)
{
	Q_UNUSED(bl)
	mItem = item;
	qReal::Id const id = qReal::Id::loadFromString(item->data(Qt::ToolTipRole).toString());
	if (item->isSelected())
	{
		mainWindow->activateItemOrDiagram(id, true, false);
		elementList << item;
	}
	else
	{
		mainWindow->activateItemOrDiagram(id, false, false);
	}
}

void RefWindow::cancel()
{
	QVariant data = model->data(index, role);
	model->setData(index, data, role);
	setElementId();
	close();
}

void RefWindow::enableOkButton(QListWidgetItem* item)
{
	Q_UNUSED(item);
	ui->mButtonOk->setEnabled(true);
}

void RefWindow::setElementId()
{
	for (int i = 0; i < elementList.size(); ++i)
	{
		qReal::Id const id = qReal::Id::loadFromString(elementList[i]->data(Qt::ToolTipRole).toString());
		mainWindow->activateItemOrDiagram(id, false, false);
	}
	mainWindow->activateItemOrDiagram(index, false);
}
<|endoftext|>


<commit_before>// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "ash/wm/workspace/snap_sizer.h"

#include <cmath>

#include "ash/screen_ash.h"
#include "ash/wm/window_resizer.h"
#include "ui/aura/window.h"
#include "ui/gfx/screen.h"

namespace ash {
namespace internal {

namespace {

// Percent of the screen (width) to give the window.
const float kPercents[] = { .5f, 2.0f / 3.0f, .8f };

// Windows are initially snapped to the percent at index 0. The index into
// |kPercents| is changed if any of the following happen:
// . The user stops moving the mouse for |kDelayBeforeIncreaseMS| and then moves
//   the mouse again.
// . The mouse moves |kPixelsBeforeAdjust| horizontal pixels.
// . The mouse is against the edge of the screen and the mouse is moved
//   |kMovesBeforeAdjust| times.
const int kDelayBeforeIncreaseMS = 500;
const int kMovesBeforeAdjust = 50;
const int kPixelsBeforeAdjust = 100;

}  // namespace

SnapSizer::SnapSizer(aura::Window* window,
                     const gfx::Point& start,
                     Edge edge,
                     int grid_size)
    : window_(window),
      edge_(edge),
      grid_size_(grid_size),
      time_last_update_(base::TimeTicks::Now()),
      percent_index_(0),
      num_moves_since_adjust_(0),
      last_adjust_x_(start.x()),
      last_update_x_(start.x()) {
  target_bounds_ = GetTargetBounds();
}

void SnapSizer::Update(const gfx::Point& location) {
  // See description above for details on this behavior.
  num_moves_since_adjust_++;
  if ((base::TimeTicks::Now() - time_last_update_).InMilliseconds() >
      kDelayBeforeIncreaseMS) {
    ChangeBounds(location.x(),
                 CalculateIncrement(location.x(), last_update_x_));
  } else {
    bool along_edge = AlongEdge(location.x());
    if (std::abs(location.x() - last_adjust_x_) >= kPixelsBeforeAdjust ||
        (along_edge && num_moves_since_adjust_ >= kMovesBeforeAdjust)) {
      ChangeBounds(location.x(),
                   CalculateIncrement(location.x(), last_adjust_x_));
    }
  }
  last_update_x_ = location.x();
  time_last_update_ = base::TimeTicks::Now();
}

int SnapSizer::CalculateIncrement(int x, int reference_x) const {
  if (AlongEdge(x))
    return 1;
  if (x == reference_x)
    return 0;
  if (edge_ == LEFT_EDGE) {
    if (x < reference_x)
      return 1;
    return -1;
  }
  // edge_ == RIGHT_EDGE.
  if (x > reference_x)
    return 1;
  return -1;
}

void SnapSizer::ChangeBounds(int x, int delta) {
  int index = std::min(static_cast<int>(arraysize(kPercents)) - 1,
                       std::max(percent_index_ + delta, 0));
  if (index != percent_index_) {
    percent_index_ = index;
    target_bounds_ = GetTargetBounds();
  }
  num_moves_since_adjust_ = 0;
  last_adjust_x_ = x;
}

gfx::Rect SnapSizer::GetTargetBounds() const {
  gfx::Rect work_area(ScreenAsh::GetUnmaximizedWorkAreaBounds(window_));
  int y = WindowResizer::AlignToGridRoundUp(work_area.y(), grid_size_);
  int max_y =
      WindowResizer::AlignToGridRoundDown(work_area.bottom(), grid_size_);
  int width = static_cast<float>(work_area.width()) * kPercents[percent_index_];
  if (edge_ == LEFT_EDGE) {
    int x = WindowResizer::AlignToGridRoundUp(work_area.x(), grid_size_);
    int mid_x = WindowResizer::AlignToGridRoundUp(
        work_area.x() + width, grid_size_);
    return gfx::Rect(x, y, mid_x - x, max_y - y);
  }
  int max_x =
      WindowResizer::AlignToGridRoundDown(work_area.right(), grid_size_);
  int x = WindowResizer::AlignToGridRoundUp(max_x - width, grid_size_);
  return gfx::Rect(x , y, max_x - x, max_y - y);
}

bool SnapSizer::AlongEdge(int x) const {
  // TODO: need to support multi-monitor.
  gfx::Rect area(gfx::Screen::GetMonitorAreaNearestWindow(window_));
  return (x <= area.x()) || (x >= area.right() - 1);
}

}  // namespace internal
}  // namespace ash
<commit_msg>Makes the bottom of snapped windows ignore the grid. This is necessary in auto-hide mode since the launcher height (2 pixels) doesn't align to the grid.<commit_after>// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "ash/wm/workspace/snap_sizer.h"

#include <cmath>

#include "ash/screen_ash.h"
#include "ash/wm/window_resizer.h"
#include "ui/aura/window.h"
#include "ui/gfx/screen.h"

namespace ash {
namespace internal {

namespace {

// Percent of the screen (width) to give the window.
const float kPercents[] = { .5f, 2.0f / 3.0f, .8f };

// Windows are initially snapped to the percent at index 0. The index into
// |kPercents| is changed if any of the following happen:
// . The user stops moving the mouse for |kDelayBeforeIncreaseMS| and then moves
//   the mouse again.
// . The mouse moves |kPixelsBeforeAdjust| horizontal pixels.
// . The mouse is against the edge of the screen and the mouse is moved
//   |kMovesBeforeAdjust| times.
const int kDelayBeforeIncreaseMS = 500;
const int kMovesBeforeAdjust = 50;
const int kPixelsBeforeAdjust = 100;

}  // namespace

SnapSizer::SnapSizer(aura::Window* window,
                     const gfx::Point& start,
                     Edge edge,
                     int grid_size)
    : window_(window),
      edge_(edge),
      grid_size_(grid_size),
      time_last_update_(base::TimeTicks::Now()),
      percent_index_(0),
      num_moves_since_adjust_(0),
      last_adjust_x_(start.x()),
      last_update_x_(start.x()) {
  target_bounds_ = GetTargetBounds();
}

void SnapSizer::Update(const gfx::Point& location) {
  // See description above for details on this behavior.
  num_moves_since_adjust_++;
  if ((base::TimeTicks::Now() - time_last_update_).InMilliseconds() >
      kDelayBeforeIncreaseMS) {
    ChangeBounds(location.x(),
                 CalculateIncrement(location.x(), last_update_x_));
  } else {
    bool along_edge = AlongEdge(location.x());
    if (std::abs(location.x() - last_adjust_x_) >= kPixelsBeforeAdjust ||
        (along_edge && num_moves_since_adjust_ >= kMovesBeforeAdjust)) {
      ChangeBounds(location.x(),
                   CalculateIncrement(location.x(), last_adjust_x_));
    }
  }
  last_update_x_ = location.x();
  time_last_update_ = base::TimeTicks::Now();
}

int SnapSizer::CalculateIncrement(int x, int reference_x) const {
  if (AlongEdge(x))
    return 1;
  if (x == reference_x)
    return 0;
  if (edge_ == LEFT_EDGE) {
    if (x < reference_x)
      return 1;
    return -1;
  }
  // edge_ == RIGHT_EDGE.
  if (x > reference_x)
    return 1;
  return -1;
}

void SnapSizer::ChangeBounds(int x, int delta) {
  int index = std::min(static_cast<int>(arraysize(kPercents)) - 1,
                       std::max(percent_index_ + delta, 0));
  if (index != percent_index_) {
    percent_index_ = index;
    target_bounds_ = GetTargetBounds();
  }
  num_moves_since_adjust_ = 0;
  last_adjust_x_ = x;
}

gfx::Rect SnapSizer::GetTargetBounds() const {
  gfx::Rect work_area(ScreenAsh::GetUnmaximizedWorkAreaBounds(window_));
  int y = WindowResizer::AlignToGridRoundUp(work_area.y(), grid_size_);
  // We don't align to the bottom of the grid as the launcher may not
  // necessarily align to the grid (happens when auto-hidden).
  int max_y = work_area.bottom();
  int width = static_cast<float>(work_area.width()) * kPercents[percent_index_];
  if (edge_ == LEFT_EDGE) {
    int x = WindowResizer::AlignToGridRoundUp(work_area.x(), grid_size_);
    int mid_x = WindowResizer::AlignToGridRoundUp(
        work_area.x() + width, grid_size_);
    return gfx::Rect(x, y, mid_x - x, max_y - y);
  }
  int max_x =
      WindowResizer::AlignToGridRoundDown(work_area.right(), grid_size_);
  int x = WindowResizer::AlignToGridRoundUp(max_x - width, grid_size_);
  return gfx::Rect(x , y, max_x - x, max_y - y);
}

bool SnapSizer::AlongEdge(int x) const {
  // TODO: need to support multi-monitor.
  gfx::Rect area(gfx::Screen::GetMonitorAreaNearestWindow(window_));
  return (x <= area.x()) || (x >= area.right() - 1);
}

}  // namespace internal
}  // namespace ash
<|endoftext|>


<commit_before>/*************************************************************************
 *
 *  $RCSfile: ci_text2.hxx,v $
 *
 *  $Revision: 1.3 $
 *
 *  last change: $Author: obo $ $Date: 2004-11-15 13:27:13 $
 *
 *  The Contents of this file are made available subject to the terms of
 *  either of the following licenses
 *
 *         - GNU Lesser General Public License Version 2.1
 *         - Sun Industry Standards Source License Version 1.1
 *
 *  Sun Microsystems Inc., October, 2000
 *
 *  GNU Lesser General Public License Version 2.1
 *  =============================================
 *  Copyright 2000 by Sun Microsystems, Inc.
 *  901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License version 2.1, as published by the Free Software Foundation.
 *
 *  This library 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
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *  MA  02111-1307  USA
 *
 *
 *  Sun Industry Standards Source License Version 1.1
 *  =================================================
 *  The contents of this file are subject to the Sun Industry Standards
 *  Source License Version 1.1 (the "License"); You may not use this file
 *  except in compliance with the License. You may obtain a copy of the
 *  License at http://www.openoffice.org/license.html.
 *
 *  Software provided under this License is provided on an "AS IS" basis,
 *  WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
 *  WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS,
 *  MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING.
 *  See the License for the specific provisions governing your rights and
 *  obligations concerning the Software.
 *
 *  The Initial Developer of the Original Code is: Sun Microsystems, Inc.
 *
 *  Copyright: 2000 by Sun Microsystems, Inc.
 *
 *  All Rights Reserved.
 *
 *  Contributor(s): _______________________________________
 *
 *
 ************************************************************************/

#ifndef ARY_CINFO_CI_TEXT2_HXX
#define ARY_CINFO_CI_TEXT2_HXX

// USED SERVICES
    // BASE CLASSES
    // COMPONENTS
    // PARAMETERS

namespace ary
{
namespace info
{

class DocumentationDisplay;

class DocuToken
{
  public:
    virtual             ~DocuToken() {}
    virtual void        DisplayAt(
                            DocumentationDisplay &
                                                o_rDisplay ) const = 0;
};

class DocuTex2
{
  public:
    typedef std::vector< DocuToken * >  TokenList;

                        DocuTex2();
    virtual             ~DocuTex2();

    virtual void        DisplayAt(
                            DocumentationDisplay &
                                                o_rDisplay ) const;

    void                AddToken(
                            DYN DocuToken &     let_drToken )
                                                { aTokens.push_back(&let_drToken); }
    const TokenList &   Tokens() const          { return aTokens; }
    bool                IsEmpty() const;
    const String &      TextOfFirstToken() const;

    String &            Access_TextOfFirstToken();

  private:
    TokenList           aTokens;
};

// IMPLEMENTATION

}   // namespace info
}   // namespace ary

#endif

<commit_msg>INTEGRATION: CWS ooo19126 (1.3.18); FILE MERGED 2005/09/05 13:09:38 rt 1.3.18.1: #i54170# Change license header: remove SISSL<commit_after>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: ci_text2.hxx,v $
 *
 *  $Revision: 1.4 $
 *
 *  last change: $Author: rt $ $Date: 2005-09-07 16:24:35 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License version 2.1, as published by the Free Software Foundation.
 *
 *    This library 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
 *    Lesser General Public License for more details.
 *
 *    You should have received a copy of the GNU Lesser General Public
 *    License along with this library; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *    MA  02111-1307  USA
 *
 ************************************************************************/

#ifndef ARY_CINFO_CI_TEXT2_HXX
#define ARY_CINFO_CI_TEXT2_HXX

// USED SERVICES
    // BASE CLASSES
    // COMPONENTS
    // PARAMETERS

namespace ary
{
namespace info
{

class DocumentationDisplay;

class DocuToken
{
  public:
    virtual             ~DocuToken() {}
    virtual void        DisplayAt(
                            DocumentationDisplay &
                                                o_rDisplay ) const = 0;
};

class DocuTex2
{
  public:
    typedef std::vector< DocuToken * >  TokenList;

                        DocuTex2();
    virtual             ~DocuTex2();

    virtual void        DisplayAt(
                            DocumentationDisplay &
                                                o_rDisplay ) const;

    void                AddToken(
                            DYN DocuToken &     let_drToken )
                                                { aTokens.push_back(&let_drToken); }
    const TokenList &   Tokens() const          { return aTokens; }
    bool                IsEmpty() const;
    const String &      TextOfFirstToken() const;

    String &            Access_TextOfFirstToken();

  private:
    TokenList           aTokens;
};

// IMPLEMENTATION

}   // namespace info
}   // namespace ary

#endif

<|endoftext|>


<commit_before>// Copyright 2020 The Google Research Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "experiment_util.h"

#include <algorithm>
#include <functional>
#include <memory>
#include <ostream>

#include "google/protobuf/repeated_field.h"
#include "absl/algorithm/container.h"
#include "absl/container/node_hash_map.h"
#include "absl/memory/memory.h"
#include "task.pb.h"

namespace automl_zero {

using ::absl::make_unique;  // NOLINT
using ::google::protobuf::RepeatedField;
using ::std::endl;           // NOLINT
using ::std::equal;          // NOLINT
using ::std::function;       // NOLINT
using ::std::set;            // NOLINT
using ::std::unique_ptr;     // NOLINT
using ::std::unordered_set;  // NOLINT
using ::std::vector;         // NOLINT

vector<Op> ExtractOps(const RepeatedField<int>& ops_src) {
  vector<Op> ops_dest;
  for (IntegerT i = 0; i < ops_src.size(); ++i) {
    ops_dest.push_back(static_cast<Op>(ops_src.Get(i)));
  }
  return ops_dest;
}

}  // namespace automl_zero
<commit_msg>Internal change<commit_after>// Copyright 2020 The Google Research Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "experiment_util.h"

#include <algorithm>
#include <functional>
#include <memory>
#include <ostream>

#include "google/protobuf/repeated_field.h"
#include "absl/algorithm/container.h"
#include "absl/container/node_hash_map.h"
#include "absl/container/node_hash_set.h"
#include "absl/memory/memory.h"
#include "task.pb.h"

namespace automl_zero {

using ::absl::make_unique;  // NOLINT
using ::google::protobuf::RepeatedField;
using ::std::endl;           // NOLINT
using ::std::equal;          // NOLINT
using ::std::function;       // NOLINT
using ::std::set;            // NOLINT
using ::std::unique_ptr;     // NOLINT
// NOLINT
using ::std::vector;         // NOLINT

vector<Op> ExtractOps(const RepeatedField<int>& ops_src) {
  vector<Op> ops_dest;
  for (IntegerT i = 0; i < ops_src.size(); ++i) {
    ops_dest.push_back(static_cast<Op>(ops_src.Get(i)));
  }
  return ops_dest;
}

}  // namespace automl_zero
<|endoftext|>


<commit_before><commit_msg>Support block_size greater than chunk_size in Blocker<commit_after><|endoftext|>