<commit_before>/* Copyright (c) 2017-2019 Hans-Kristian Arntzen
 *
 * 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 "texture_utils.hpp"

namespace Granite
{
namespace SceneFormats
{
struct MipmapGeneratorU8
{
	inline vec4 sample(const Vulkan::TextureFormatLayout &layout, const uvec2 &coord,
	                   uint32_t layer, uint32_t mip) const
	{
		uint8_t &v = *layout.data_generic<uint8_t>(coord.x, coord.y, layer, mip);
		return vec4(float(v) * (1.0f / 255.0f), 0.0f, 0.0f, 1.0f);
	}

	inline void write(const Vulkan::TextureFormatLayout &layout, const uvec2 &coord,
	                  uint32_t layer, uint32_t mip, const vec4 &v) const
	{
		float q = clamp(round(v.x * 255.0f), 0.0f, 255.0f);
		*layout.data_generic<uint8_t>(coord.x, coord.y, layer, mip) = uint8_t(q);
	}
};

struct MipmapGeneratorRGBA8Unorm
{
	inline vec4 sample(const Vulkan::TextureFormatLayout &layout, const uvec2 &coord,
	                   uint32_t layer, uint32_t mip) const
	{
		u8vec4 &v = *layout.data_generic<u8vec4>(coord.x, coord.y, layer, mip);
		return vec4(v) * (1.0f / 255.0f);
	}

	inline void write(const Vulkan::TextureFormatLayout &layout, const uvec2 &coord,
	                  uint32_t layer, uint32_t mip, const vec4 &v) const
	{
		auto q = clamp(round(v * 255.0f), vec4(0.0f), vec4(255.0f));
		*layout.data_generic<u8vec4>(coord.x, coord.y, layer, mip) = u8vec4(q);
	}
};

struct MipmapGeneratorRGBA8Srgb
{
	static inline float srgb_gamma_to_linear(float v)
	{
		if (v <= 0.04045f)
			return v * (1.0f / 12.92f);
		else
			return muglm::pow((v + 0.055f) / (1.0f + 0.055f), 2.4f);
	}

	static inline float srgb_linear_to_gamma(float v)
	{
		if (v <= 0.0031308f)
			return 12.92f * v;
		else
			return (1.0f + 0.055f) * muglm::pow(v, 1.0f / 2.4f) - 0.055f;
	}

	static inline vec4 srgb_gamma_to_linear(const vec4 &v)
	{
		return vec4(srgb_gamma_to_linear(v.x),
		            srgb_gamma_to_linear(v.y),
		            srgb_gamma_to_linear(v.z),
		            v.w);
	}

	static inline vec4 srgb_linear_to_gamma(const vec4 &v)
	{
		return vec4(srgb_linear_to_gamma(v.x),
		            srgb_linear_to_gamma(v.y),
		            srgb_linear_to_gamma(v.z),
		            v.w);
	}

	inline vec4 sample(const Vulkan::TextureFormatLayout &layout, const uvec2 &coord,
	                   uint32_t layer, uint32_t mip) const
	{
		u8vec4 &v = *layout.data_generic<u8vec4>(coord.x, coord.y, layer, mip);
		return srgb_gamma_to_linear(vec4(v) * (1.0f / 255.0f));
	}

	inline void write(const Vulkan::TextureFormatLayout &layout, const uvec2 &coord,
	                  uint32_t layer, uint32_t mip, const vec4 &v) const
	{
		auto q = clamp(round(srgb_linear_to_gamma(v) * 255.0f), vec4(0.0f), vec4(255.0f));
		*layout.data_generic<u8vec4>(coord.x, coord.y, layer, mip) = u8vec4(q);
	}
};

template <typename Ops>
inline void generate_mipmaps(const Vulkan::TextureFormatLayout &dst_layout,
                             const Vulkan::TextureFormatLayout &layout, const Ops &op)
{
	memcpy(dst_layout.data(0, 0), layout.data(0, 0), dst_layout.get_layer_size(0) * layout.get_layers());

	for (uint32_t level = 1; level < dst_layout.get_levels(); level++)
	{
		auto &dst_mip = dst_layout.get_mip_info(level);
		auto &src_mip = dst_layout.get_mip_info(level - 1);

		uint32_t dst_width = dst_mip.block_row_length;
		uint32_t dst_height = dst_mip.block_image_height;

		uint32_t src_width = src_mip.block_row_length;
		uint32_t src_height = src_mip.block_image_height;
		uvec2 max_coord(src_width - 1u, src_height - 1u);

		float src_width_f = float(src_mip.block_row_length);
		float src_height_f = float(src_mip.block_image_height);

		float rescale_width = src_width_f / float(dst_width);
		float rescale_height = src_height_f / float(dst_height);

		for (uint32_t layer = 0; layer < dst_layout.get_layers(); layer++)
		{
			for (uint32_t y = 0; y < dst_height; y++)
			{
				float coord_y = (float(y) + 0.5f) * rescale_height - 0.5f;
				for (uint32_t x = 0; x < dst_width; x++)
				{
					float coord_x = (float(x) + 0.5f) * rescale_width - 0.5f;
					vec2 base_coord = vec2(coord_x, coord_y);
					vec2 floor_coord = floor(base_coord);
					vec2 uv = base_coord - floor_coord;
					uvec2 c0(floor_coord);
					uvec2 c1 = min(c0 + uvec2(1, 0), max_coord);
					uvec2 c2 = min(c0 + uvec2(0, 1), max_coord);
					uvec2 c3 = min(c0 + uvec2(1, 1), max_coord);

					auto v0 = op.sample(dst_layout, c0, layer, level - 1);
					auto v1 = op.sample(dst_layout, c1, layer, level - 1);
					auto v2 = op.sample(dst_layout, c2, layer, level - 1);
					auto v3 = op.sample(dst_layout, c3, layer, level - 1);

					auto x0 = mix(v0, v1, uv.x);
					auto x1 = mix(v2, v3, uv.x);
					auto filtered = mix(x0, x1, uv.y);
					op.write(dst_layout, uvec2(x, y), layer, level, filtered);
				}
			}
		}
	}
}

static void copy_dimensions(MemoryMappedTexture &mapped, const Vulkan::TextureFormatLayout &layout, MemoryMappedTextureFlags flags)
{
	switch (layout.get_image_type())
	{
	case VK_IMAGE_TYPE_1D:
		mapped.set_1d(layout.get_format(), layout.get_width(), layout.get_layers(), 0);
		break;

	case VK_IMAGE_TYPE_2D:
		if (flags & MEMORY_MAPPED_TEXTURE_CUBE_MAP_COMPATIBLE_BIT)
			mapped.set_cube(layout.get_format(), layout.get_width(), layout.get_layers() / 6, 0);
		else
			mapped.set_2d(layout.get_format(), layout.get_width(), layout.get_height(), layout.get_layers(), 0);
		break;

	case VK_IMAGE_TYPE_3D:
		throw std::logic_error("3D is not supported for generate_mipmaps.");

	default:
		throw std::logic_error("Unknown image type.");
	}
}

static void generate(const MemoryMappedTexture &mapped, const Vulkan::TextureFormatLayout &layout)
{
	auto &dst_layout = mapped.get_layout();

	switch (layout.get_format())
	{
	case VK_FORMAT_R8_UNORM:
		generate_mipmaps(dst_layout, layout, MipmapGeneratorU8());
		break;

	case VK_FORMAT_R8G8B8A8_SRGB:
	case VK_FORMAT_B8G8R8A8_SRGB:
		generate_mipmaps(dst_layout, layout, MipmapGeneratorRGBA8Srgb());
		break;

	case VK_FORMAT_R8G8B8A8_UNORM:
	case VK_FORMAT_B8G8R8A8_UNORM:
		generate_mipmaps(dst_layout, layout, MipmapGeneratorRGBA8Unorm());
		break;

	default:
		throw std::logic_error("Unsupported format for generate_mipmaps.");
	}
}

MemoryMappedTexture generate_mipmaps_to_file(const std::string &path, const Vulkan::TextureFormatLayout &layout, MemoryMappedTextureFlags flags)
{
	MemoryMappedTexture mapped;
	copy_dimensions(mapped, layout, flags);
	if (!mapped.map_write(path))
		return {};
	generate(mapped, layout);
	return mapped;
}

MemoryMappedTexture generate_mipmaps(const Vulkan::TextureFormatLayout &layout, MemoryMappedTextureFlags flags)
{
	MemoryMappedTexture mapped;
	copy_dimensions(mapped, layout, flags);
	if (!mapped.map_write_scratch())
		return {};
	generate(mapped, layout);
	return mapped;
}
}
}<commit_msg>Fix MSVC build.<commit_after>/* Copyright (c) 2017-2019 Hans-Kristian Arntzen
 *
 * 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 "texture_utils.hpp"

namespace Granite
{
namespace SceneFormats
{
struct MipmapGeneratorU8
{
	inline vec4 sample(const Vulkan::TextureFormatLayout &layout, const uvec2 &coord,
	                   uint32_t layer, uint32_t mip) const
	{
		uint8_t &v = *layout.data_generic<uint8_t>(coord.x, coord.y, layer, mip);
		return vec4(float(v) * (1.0f / 255.0f), 0.0f, 0.0f, 1.0f);
	}

	inline void write(const Vulkan::TextureFormatLayout &layout, const uvec2 &coord,
	                  uint32_t layer, uint32_t mip, const vec4 &v) const
	{
		float q = muglm::clamp(muglm::round(v.x * 255.0f), 0.0f, 255.0f);
		*layout.data_generic<uint8_t>(coord.x, coord.y, layer, mip) = uint8_t(q);
	}
};

struct MipmapGeneratorRGBA8Unorm
{
	inline vec4 sample(const Vulkan::TextureFormatLayout &layout, const uvec2 &coord,
	                   uint32_t layer, uint32_t mip) const
	{
		u8vec4 &v = *layout.data_generic<u8vec4>(coord.x, coord.y, layer, mip);
		return vec4(v) * (1.0f / 255.0f);
	}

	inline void write(const Vulkan::TextureFormatLayout &layout, const uvec2 &coord,
	                  uint32_t layer, uint32_t mip, const vec4 &v) const
	{
		auto q = clamp(round(v * 255.0f), vec4(0.0f), vec4(255.0f));
		*layout.data_generic<u8vec4>(coord.x, coord.y, layer, mip) = u8vec4(q);
	}
};

struct MipmapGeneratorRGBA8Srgb
{
	static inline float srgb_gamma_to_linear(float v)
	{
		if (v <= 0.04045f)
			return v * (1.0f / 12.92f);
		else
			return muglm::pow((v + 0.055f) / (1.0f + 0.055f), 2.4f);
	}

	static inline float srgb_linear_to_gamma(float v)
	{
		if (v <= 0.0031308f)
			return 12.92f * v;
		else
			return (1.0f + 0.055f) * muglm::pow(v, 1.0f / 2.4f) - 0.055f;
	}

	static inline vec4 srgb_gamma_to_linear(const vec4 &v)
	{
		return vec4(srgb_gamma_to_linear(v.x),
		            srgb_gamma_to_linear(v.y),
		            srgb_gamma_to_linear(v.z),
		            v.w);
	}

	static inline vec4 srgb_linear_to_gamma(const vec4 &v)
	{
		return vec4(srgb_linear_to_gamma(v.x),
		            srgb_linear_to_gamma(v.y),
		            srgb_linear_to_gamma(v.z),
		            v.w);
	}

	inline vec4 sample(const Vulkan::TextureFormatLayout &layout, const uvec2 &coord,
	                   uint32_t layer, uint32_t mip) const
	{
		u8vec4 &v = *layout.data_generic<u8vec4>(coord.x, coord.y, layer, mip);
		return srgb_gamma_to_linear(vec4(v) * (1.0f / 255.0f));
	}

	inline void write(const Vulkan::TextureFormatLayout &layout, const uvec2 &coord,
	                  uint32_t layer, uint32_t mip, const vec4 &v) const
	{
		auto q = clamp(round(srgb_linear_to_gamma(v) * 255.0f), vec4(0.0f), vec4(255.0f));
		*layout.data_generic<u8vec4>(coord.x, coord.y, layer, mip) = u8vec4(q);
	}
};

template <typename Ops>
inline void generate_mipmaps(const Vulkan::TextureFormatLayout &dst_layout,
                             const Vulkan::TextureFormatLayout &layout, const Ops &op)
{
	memcpy(dst_layout.data(0, 0), layout.data(0, 0), dst_layout.get_layer_size(0) * layout.get_layers());

	for (uint32_t level = 1; level < dst_layout.get_levels(); level++)
	{
		auto &dst_mip = dst_layout.get_mip_info(level);
		auto &src_mip = dst_layout.get_mip_info(level - 1);

		uint32_t dst_width = dst_mip.block_row_length;
		uint32_t dst_height = dst_mip.block_image_height;

		uint32_t src_width = src_mip.block_row_length;
		uint32_t src_height = src_mip.block_image_height;
		uvec2 max_coord(src_width - 1u, src_height - 1u);

		float src_width_f = float(src_mip.block_row_length);
		float src_height_f = float(src_mip.block_image_height);

		float rescale_width = src_width_f / float(dst_width);
		float rescale_height = src_height_f / float(dst_height);

		for (uint32_t layer = 0; layer < dst_layout.get_layers(); layer++)
		{
			for (uint32_t y = 0; y < dst_height; y++)
			{
				float coord_y = (float(y) + 0.5f) * rescale_height - 0.5f;
				for (uint32_t x = 0; x < dst_width; x++)
				{
					float coord_x = (float(x) + 0.5f) * rescale_width - 0.5f;
					vec2 base_coord = vec2(coord_x, coord_y);
					vec2 floor_coord = floor(base_coord);
					vec2 uv = base_coord - floor_coord;
					uvec2 c0(floor_coord);
					uvec2 c1 = min(c0 + uvec2(1, 0), max_coord);
					uvec2 c2 = min(c0 + uvec2(0, 1), max_coord);
					uvec2 c3 = min(c0 + uvec2(1, 1), max_coord);

					auto v0 = op.sample(dst_layout, c0, layer, level - 1);
					auto v1 = op.sample(dst_layout, c1, layer, level - 1);
					auto v2 = op.sample(dst_layout, c2, layer, level - 1);
					auto v3 = op.sample(dst_layout, c3, layer, level - 1);

					auto x0 = mix(v0, v1, uv.x);
					auto x1 = mix(v2, v3, uv.x);
					auto filtered = mix(x0, x1, uv.y);
					op.write(dst_layout, uvec2(x, y), layer, level, filtered);
				}
			}
		}
	}
}

static void copy_dimensions(MemoryMappedTexture &mapped, const Vulkan::TextureFormatLayout &layout, MemoryMappedTextureFlags flags)
{
	switch (layout.get_image_type())
	{
	case VK_IMAGE_TYPE_1D:
		mapped.set_1d(layout.get_format(), layout.get_width(), layout.get_layers(), 0);
		break;

	case VK_IMAGE_TYPE_2D:
		if (flags & MEMORY_MAPPED_TEXTURE_CUBE_MAP_COMPATIBLE_BIT)
			mapped.set_cube(layout.get_format(), layout.get_width(), layout.get_layers() / 6, 0);
		else
			mapped.set_2d(layout.get_format(), layout.get_width(), layout.get_height(), layout.get_layers(), 0);
		break;

	case VK_IMAGE_TYPE_3D:
		throw std::logic_error("3D is not supported for generate_mipmaps.");

	default:
		throw std::logic_error("Unknown image type.");
	}
}

static void generate(const MemoryMappedTexture &mapped, const Vulkan::TextureFormatLayout &layout)
{
	auto &dst_layout = mapped.get_layout();

	switch (layout.get_format())
	{
	case VK_FORMAT_R8_UNORM:
		generate_mipmaps(dst_layout, layout, MipmapGeneratorU8());
		break;

	case VK_FORMAT_R8G8B8A8_SRGB:
	case VK_FORMAT_B8G8R8A8_SRGB:
		generate_mipmaps(dst_layout, layout, MipmapGeneratorRGBA8Srgb());
		break;

	case VK_FORMAT_R8G8B8A8_UNORM:
	case VK_FORMAT_B8G8R8A8_UNORM:
		generate_mipmaps(dst_layout, layout, MipmapGeneratorRGBA8Unorm());
		break;

	default:
		throw std::logic_error("Unsupported format for generate_mipmaps.");
	}
}

MemoryMappedTexture generate_mipmaps_to_file(const std::string &path, const Vulkan::TextureFormatLayout &layout, MemoryMappedTextureFlags flags)
{
	MemoryMappedTexture mapped;
	copy_dimensions(mapped, layout, flags);
	if (!mapped.map_write(path))
		return {};
	generate(mapped, layout);
	return mapped;
}

MemoryMappedTexture generate_mipmaps(const Vulkan::TextureFormatLayout &layout, MemoryMappedTextureFlags flags)
{
	MemoryMappedTexture mapped;
	copy_dimensions(mapped, layout, flags);
	if (!mapped.map_write_scratch())
		return {};
	generate(mapped, layout);
	return mapped;
}
}
}
<|endoftext|>


<commit_before>#include <QtGui>
#include <QtOpenGL>

#include <cmath>

#include "DiamondGLWidget.h"

typedef QList<QVector3D>::const_iterator PointIter;

const float TAIL_Z = -75;

const float INNER_CIRCLE_Z = 20;
const float INNER_CIRCLE_SIZE = 35;

const float OUTER_CIRCLE_Z = 0;
const float OUTER_CIRCLE_SIZE = 50;

static const QList<QVector3D> buildCircle(const float size, const float z, const bool twisted = false)
{
	QList<QVector3D> points;
	static const float PI = 3.14159;
	float offset = 0;
	if (twisted) {
		offset = PI / 16.0f;
	}
	for (int i = 0; i <= 16; i++) {
		float angle = offset + (i * (float) PI / 8.0f);
		float x = size * (float) sin(angle);
		float y = size * (float) cos(angle);

		points << QVector3D(x, y, z);
	}

	return points;
}

DiamondGLWidget::DiamondGLWidget(QWidget* const parent) :
	GLWidget(parent),
	useDepthTesting(true),
	backFaceCulling(true),
	drawBacksAsWireframe(true),
	correctWindingOrder(true)
{
	outerCircle.append(buildCircle(OUTER_CIRCLE_SIZE, OUTER_CIRCLE_Z));
	innerCircle.append(buildCircle(INNER_CIRCLE_SIZE, INNER_CIRCLE_Z, true));
}

void DiamondGLWidget::initializeGL()
{
	glShadeModel(GL_FLAT);

	setXRotation(50);
	setYRotation(-15);
	setZRotation(0);
}

void DiamondGLWidget::render()
{
	const static QColor lightColor(135, 206, 250);
	const static QColor darkColor(0, 191, 255);

	glClear(GL_DEPTH_BUFFER_BIT);

	glFrontFace(GL_CCW);

	if (usingDepthTesting()) {
		glEnable(GL_DEPTH_TEST);
	} else {
		glDisable(GL_DEPTH_TEST);
	}

	if (usingBackFaceCulling()) {
		glEnable(GL_CULL_FACE);
	} else {
		glDisable(GL_CULL_FACE);
		if (drawingBacksAsWireframes()) {
			glPolygonMode(GL_BACK, GL_LINE);
		} else {
			glPolygonMode(GL_BACK, GL_FILL);
		}
	}

	int evenOrOdd = 0;

	glBegin(GL_TRIANGLE_FAN);
	glVertex3f(0, 0, TAIL_Z);
	for (PointIter iter = outerCircle.begin(); iter != outerCircle.end(); ++iter) {
		const QColor* color = &lightColor;
		if (evenOrOdd++ % 2 == 0) {
			color = &darkColor;
		}
		glColor3f(color->redF(), color->greenF(), color->blueF());
		const QVector3D point = *iter;
		glVertex3d(point.x(), point.y(), point.z());
	}
	glEnd();

	{
		glBegin(GL_TRIANGLE_STRIP);
		PointIter innerIter = innerCircle.begin();
		PointIter outerIter = outerCircle.begin();
		while(innerIter != innerCircle.end() && outerIter != outerCircle.end()) {
			QVector3D point = *outerIter++;
			glColor3f(darkColor.redF(), darkColor.greenF(), darkColor.blueF());
			glVertex3d(point.x(), point.y(), point.z());
			point = *innerIter++;
			glColor3f(lightColor.redF(), lightColor.greenF(), lightColor.blueF());
			glVertex3d(point.x(), point.y(), point.z());
		}
		glEnd();
	}

	if (correctingWindingOrder()) {
		glFrontFace(GL_CW);
	}

	glBegin(GL_TRIANGLE_FAN);
	glVertex3f(0, 0, INNER_CIRCLE_Z);
	for (PointIter iter = innerCircle.begin(); iter != innerCircle.end(); ++iter) {
		const QColor* color = &lightColor;
		if (evenOrOdd++ % 2 == 0) {
			color = &darkColor;
		}
		glColor3f(color->redF(), color->greenF(), color->blueF());
		const QVector3D point = *iter;
		glVertex3d(point.x(), point.y(), point.z());
	}
	glEnd();
}
<commit_msg>Tweaked diamond's rotation for better look<commit_after>#include <QtGui>
#include <QtOpenGL>

#include <cmath>

#include "DiamondGLWidget.h"

typedef QList<QVector3D>::const_iterator PointIter;

const float TAIL_Z = -75;

const float INNER_CIRCLE_Z = 20;
const float INNER_CIRCLE_SIZE = 35;

const float OUTER_CIRCLE_Z = 0;
const float OUTER_CIRCLE_SIZE = 50;

static const QList<QVector3D> buildCircle(const float size, const float z, const bool twisted = false)
{
	QList<QVector3D> points;
	static const float PI = 3.14159;
	float offset = 0;
	if (twisted) {
		offset = PI / 16.0f;
	}
	for (int i = 0; i <= 16; i++) {
		float angle = offset + (i * (float) PI / 8.0f);
		float x = size * (float) sin(angle);
		float y = size * (float) cos(angle);

		points << QVector3D(x, y, z);
	}

	return points;
}

DiamondGLWidget::DiamondGLWidget(QWidget* const parent) :
	GLWidget(parent),
	useDepthTesting(true),
	backFaceCulling(true),
	drawBacksAsWireframe(true),
	correctWindingOrder(true)
{
	outerCircle.append(buildCircle(OUTER_CIRCLE_SIZE, OUTER_CIRCLE_Z));
	innerCircle.append(buildCircle(INNER_CIRCLE_SIZE, INNER_CIRCLE_Z, true));
}

void DiamondGLWidget::initializeGL()
{
	glShadeModel(GL_FLAT);

	setXRotation(-50);
	setYRotation(20);
}

void DiamondGLWidget::render()
{
	const static QColor lightColor(135, 206, 250);
	const static QColor darkColor(0, 191, 255);

	glClear(GL_DEPTH_BUFFER_BIT);

	glFrontFace(GL_CCW);

	if (usingDepthTesting()) {
		glEnable(GL_DEPTH_TEST);
	} else {
		glDisable(GL_DEPTH_TEST);
	}

	if (usingBackFaceCulling()) {
		glEnable(GL_CULL_FACE);
	} else {
		glDisable(GL_CULL_FACE);
		if (drawingBacksAsWireframes()) {
			glPolygonMode(GL_BACK, GL_LINE);
		} else {
			glPolygonMode(GL_BACK, GL_FILL);
		}
	}

	int evenOrOdd = 0;

	glBegin(GL_TRIANGLE_FAN);
	glVertex3f(0, 0, TAIL_Z);
	for (PointIter iter = outerCircle.begin(); iter != outerCircle.end(); ++iter) {
		const QColor* color = &lightColor;
		if (evenOrOdd++ % 2 == 0) {
			color = &darkColor;
		}
		glColor3f(color->redF(), color->greenF(), color->blueF());
		const QVector3D point = *iter;
		glVertex3d(point.x(), point.y(), point.z());
	}
	glEnd();

	{
		glBegin(GL_TRIANGLE_STRIP);
		PointIter innerIter = innerCircle.begin();
		PointIter outerIter = outerCircle.begin();
		while(innerIter != innerCircle.end() && outerIter != outerCircle.end()) {
			QVector3D point = *outerIter++;
			glColor3f(darkColor.redF(), darkColor.greenF(), darkColor.blueF());
			glVertex3d(point.x(), point.y(), point.z());
			point = *innerIter++;
			glColor3f(lightColor.redF(), lightColor.greenF(), lightColor.blueF());
			glVertex3d(point.x(), point.y(), point.z());
		}
		glEnd();
	}

	if (correctingWindingOrder()) {
		glFrontFace(GL_CW);
	}

	glBegin(GL_TRIANGLE_FAN);
	glVertex3f(0, 0, INNER_CIRCLE_Z);
	for (PointIter iter = innerCircle.begin(); iter != innerCircle.end(); ++iter) {
		const QColor* color = &lightColor;
		if (evenOrOdd++ % 2 == 0) {
			color = &darkColor;
		}
		glColor3f(color->redF(), color->greenF(), color->blueF());
		const QVector3D point = *iter;
		glVertex3d(point.x(), point.y(), point.z());
	}
	glEnd();
}
<|endoftext|>


<commit_before>#include "Game/CannonBall.h"

CannonBall::CannonBall(const QPixmap & pixmap, QVector2D velocity, QVector2D position, Player owner, QGraphicsItem * parent) : Entity(pixmap, owner, parent), m_initialVelocity(velocity), m_initialPosition(position), m_gravity(9.8), m_timeSinceShot(0)
{
	setScale(0.1);
	setPos(m_initialPosition.toPointF());
}

void CannonBall::updateEntity(double deltaTime)
{
    m_timeSinceShot += deltaTime/1000.0 * 2.0;

	double x = m_initialPosition.x() + m_initialVelocity.x() * m_timeSinceShot;
	double y = m_initialPosition.y() - m_initialVelocity.y() * m_timeSinceShot + 0.5 * m_gravity * m_timeSinceShot * m_timeSinceShot;

	setPos(x, y);

   update(boundingRect());
}
<commit_msg>Change gravity ;)<commit_after>#include "Game/CannonBall.h"

CannonBall::CannonBall(const QPixmap & pixmap, QVector2D velocity, QVector2D position, Player owner, QGraphicsItem * parent) : Entity(pixmap, owner, parent), m_initialVelocity(velocity), m_initialPosition(position), m_gravity(9.8), m_timeSinceShot(0)
{
	setScale(0.1);
	setPos(m_initialPosition.toPointF());
}

void CannonBall::updateEntity(double deltaTime)
{
    m_timeSinceShot += deltaTime/1000.0 * 2.0;

	double x = m_initialPosition.x() + m_initialVelocity.x() * m_timeSinceShot;
	double y = m_initialPosition.y() - m_initialVelocity.y() * m_timeSinceShot + 0.75 * m_gravity * m_timeSinceShot * m_timeSinceShot;

	setPos(x, y);

   update(boundingRect());
}
<|endoftext|>


<commit_before><commit_msg>Workaround Qt 5 macOS menu bug<commit_after><|endoftext|>


<commit_before>/***************************************************************************
 *   Copyright (c) 2004 Werner Mayer <wmayer[at]users.sourceforge.net>     *
 *                                                                         *
 *   This file is part of the FreeCAD CAx development system.              *
 *                                                                         *
 *   This library is free software; you can redistribute it and/or         *
 *   modify it under the terms of the GNU Library General Public           *
 *   License as published by the Free Software Foundation; either          *
 *   version 2 of the License, or (at your option) any later version.      *
 *                                                                         *
 *   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 Library General Public License for more details.                  *
 *                                                                         *
 *   You should have received a copy of the GNU Library General Public     *
 *   License along with this library; see the file COPYING.LIB. If not,    *
 *   write to the Free Software Foundation, Inc., 59 Temple Place,         *
 *   Suite 330, Boston, MA  02111-1307, USA                                *
 *                                                                         *
 ***************************************************************************/

#include "PreCompiled.h"
#ifndef _PreComp_
# include <QApplication>
# include <QGenericReturnArgument>
# include <QKeyEvent>
# include <QMessageBox>
# include <QMetaObject>
# include <QStatusBar>
# include <QThread>
# include <QTime>
# include <QTimer>
#endif

#include "ProgressBar.h"
#include "MainWindow.h"
#include "WaitCursor.h"

using namespace Gui;

namespace Gui {
struct SequencerPrivate
{
    ProgressBar* bar;
    WaitCursor* waitCursor;
    QTime measureTime;
    QTime progressTime;
    QString text;
    bool guiThread;
};

struct ProgressBarPrivate
{
    QTimer* delayShowTimer;
    int minimumDuration;
    int observeEventFilter;

    bool isModalDialog(QObject* o) const
    {
        QWidget* parent = qobject_cast<QWidget*>(o);
        while (parent) {
            QMessageBox* dlg = qobject_cast<QMessageBox*>(parent);
            if (dlg && dlg->isModal())
                return true;
            parent = parent->parentWidget();
        }

        return false;
    }
};
}

Sequencer* Sequencer::_pclSingleton = 0;

Sequencer* Sequencer::instance()
{
    // not initialized?
    if (!_pclSingleton)
    {
        _pclSingleton = new Sequencer();
    }

    return _pclSingleton;
}

Sequencer::Sequencer ()
{
    d = new SequencerPrivate;
    d->bar = 0;
    d->waitCursor = 0;
    d->guiThread = true;
}

Sequencer::~Sequencer ()
{
    delete d;
}

void Sequencer::pause()
{
    QThread *currentThread = QThread::currentThread();
    QThread *thr = d->bar->thread(); // this is the main thread
    if (thr != currentThread)
        return;

    // allow key handling of dialog and restore cursor
    d->bar->leaveControlEvents();
    d->waitCursor->restoreCursor();
    QApplication::setOverrideCursor(Qt::ArrowCursor);
}

void Sequencer::resume()
{
    QThread *currentThread = QThread::currentThread();
    QThread *thr = d->bar->thread(); // this is the main thread
    if (thr != currentThread)
        return;

    QApplication::restoreOverrideCursor();
    d->waitCursor->setWaitCursor();
    // must be called as last to get control before WaitCursor
    d->bar->enterControlEvents(); // grab again
}

void Sequencer::startStep()
{
    QThread *currentThread = QThread::currentThread();
    QThread *thr = d->bar->thread(); // this is the main thread
    if (thr != currentThread) {
        d->guiThread = false;
        d->bar->setRange(0, (int)nTotalSteps);
        if (nTotalSteps == 0) {
            d->progressTime.start();
        }
        d->measureTime.start();
        QMetaObject::invokeMethod(d->bar, "aboutToShow", Qt::QueuedConnection);
    }
    else {
        d->guiThread = true;
        d->bar->setRange(0, (int)nTotalSteps);
        if (nTotalSteps == 0) {
            d->progressTime.start();
        }

        d->measureTime.start();
        d->waitCursor = new Gui::WaitCursor;
        d->bar->enterControlEvents();
        d->bar->aboutToShow();
    }
}

void Sequencer::nextStep(bool canAbort)
{
    QThread *currentThread = QThread::currentThread();
    QThread *thr = d->bar->thread(); // this is the main thread
    if (thr != currentThread) {
        setValue((int)nProgress+1);
    }
    else {
        if (wasCanceled() && canAbort) {
            // restore cursor
            pause();
            bool ok = d->bar->canAbort();
            // continue and show up wait cursor if needed
            resume();

            // force to abort the operation
            if ( ok ) {
                abort();
            } else {
                rejectCancel();
                setValue((int)nProgress+1);
            }
        }
        else {
            setValue((int)nProgress+1);
        }
    }
}

void Sequencer::setProgress(size_t step)
{
    d->bar->show();
    setValue((int)step);
}

void Sequencer::setValue(int step)
{
    QThread *currentThread = QThread::currentThread();
    QThread *thr = d->bar->thread(); // this is the main thread
    // if number of total steps is unknown then increment only by one
    if (nTotalSteps == 0) {
        int elapsed = d->progressTime.elapsed();
        // allow an update every 500 milliseconds only
        if (elapsed > 500) {
            d->progressTime.restart();
            if (thr != currentThread) {
                QMetaObject::invokeMethod(d->bar, "setValue", Qt::/*Blocking*/QueuedConnection,
                    QGenericReturnArgument(), Q_ARG(int,d->bar->value()+1));
            }
            else {
                d->bar->setValue(d->bar->value()+1);
                qApp->processEvents();
            }
        }
    }
    else {
        if (thr != currentThread) {
            QMetaObject::invokeMethod(d->bar, "setValue", Qt::/*Blocking*/QueuedConnection,
                QGenericReturnArgument(), Q_ARG(int,step));
            if (d->bar->isVisible())
                showRemainingTime();
        }
        else {
            d->bar->setValue(step);
            if (d->bar->isVisible())
                showRemainingTime();
            d->bar->resetObserveEventFilter();
            qApp->processEvents();
        }
    }
}

void Sequencer::showRemainingTime()
{
    QThread *currentThread = QThread::currentThread();
    QThread *thr = d->bar->thread(); // this is the main thread

    int elapsed = d->measureTime.elapsed();
    int progress = d->bar->value();
    int totalSteps = d->bar->maximum() - d->bar->minimum();

    QString txt = d->text;
    // More than 5 percent complete or more than 5 secs have elapsed.
    if (progress * 20 > totalSteps || elapsed > 5000) {
        int rest = (int) ( (double) totalSteps/progress * elapsed ) - elapsed;

        // more than 1 secs have elapsed and at least 100 ms are remaining
        if (elapsed > 1000 && rest > 100) {
            QTime time( 0,0, 0);
            time = time.addSecs( rest/1000 );
            QString remain = Gui::ProgressBar::tr("Remaining: %1").arg(time.toString());
            QString status = QString::fromLatin1("%1\t[%2]").arg(txt).arg(remain);

            if (thr != currentThread) {
                QMetaObject::invokeMethod(getMainWindow()->statusBar(), "showMessage",
                    Qt::/*Blocking*/QueuedConnection,
                    QGenericReturnArgument(),
                    Q_ARG(QString,status));
            }
            else {
                getMainWindow()->showMessage(status);
            }
        }
    }
}

void Sequencer::resetData()
{
    QThread *currentThread = QThread::currentThread();
    QThread *thr = d->bar->thread(); // this is the main thread
    if (thr != currentThread) {
        QMetaObject::invokeMethod(d->bar, "reset", Qt::QueuedConnection);
        QMetaObject::invokeMethod(d->bar, "hide", Qt::QueuedConnection);
        QMetaObject::invokeMethod(getMainWindow()->statusBar(), "showMessage",
            Qt::/*Blocking*/QueuedConnection,
            QGenericReturnArgument(),
            Q_ARG(QString,QString()));
        QMetaObject::invokeMethod(getMainWindow(), "setPaneText",
            Qt::/*Blocking*/QueuedConnection,
            QGenericReturnArgument(),
            Q_ARG(int,1),
            Q_ARG(QString,QString()));
    }
    else {
        d->bar->reset();
        // Note: Under Qt 4.1.4 this forces to run QWindowsStyle::eventFilter() twice
        // handling the same event thus a warning is printed. Possibly, this is a bug
        // in Qt. The message is QEventDispatcherUNIX::unregisterTimer: invalid argument.
        d->bar->hide();
        delete d->waitCursor;
        d->waitCursor = 0;
        d->bar->leaveControlEvents();
        getMainWindow()->setPaneText(1, QString());
        getMainWindow()->showMessage(QString());
    }

    SequencerBase::resetData();
}

void Sequencer::abort()
{
    //resets
    resetData();
    Base::AbortException exc("Aborting...");
    throw exc;
}

void Sequencer::setText (const char* pszTxt)
{
    QThread *currentThread = QThread::currentThread();
    QThread *thr = d->bar->thread(); // this is the main thread

    // print message to the statusbar
    d->text = pszTxt ? QString::fromUtf8(pszTxt) : QLatin1String("");
    if (thr != currentThread) {
        QMetaObject::invokeMethod(getMainWindow()->statusBar(), "showMessage",
            Qt::/*Blocking*/QueuedConnection,
            QGenericReturnArgument(),
            Q_ARG(QString,d->text));
    }
    else {
        getMainWindow()->showMessage(d->text);
    }
}

bool Sequencer::isBlocking() const
{
    return d->guiThread;
}

QProgressBar* Sequencer::getProgressBar(QWidget* parent)
{
    if (!d->bar)
        d->bar = new ProgressBar(this, parent);
    return d->bar;
}

// -------------------------------------------------------

/* TRANSLATOR Gui::ProgressBar */

ProgressBar::ProgressBar (Sequencer* s, QWidget * parent)
    : QProgressBar(parent), sequencer(s)
{
    d = new Gui::ProgressBarPrivate;
    d->minimumDuration = 2000; // 2 seconds
    d->delayShowTimer = new QTimer(this);
    d->delayShowTimer->setSingleShot(true);
    connect(d->delayShowTimer, SIGNAL(timeout()), this, SLOT(delayedShow()));
    d->observeEventFilter = 0;

    setFixedWidth(120);

    // write percentage to the center
    setAlignment(Qt::AlignHCenter);
    hide();
}

ProgressBar::~ProgressBar ()
{
    disconnect(d->delayShowTimer, SIGNAL(timeout()), this, SLOT(delayedShow()));
    delete d->delayShowTimer;
    delete d;
}

int ProgressBar::minimumDuration() const
{
    return d->minimumDuration;
}

void ProgressBar::setMinimumDuration (int ms)
{
    if (value() == 0)
    {
        d->delayShowTimer->stop();
        d->delayShowTimer->start(ms);
    }

    d->minimumDuration = ms;
}

void ProgressBar::aboutToShow()
{
    // delay showing the bar
    d->delayShowTimer->start(d->minimumDuration);
}

void ProgressBar::delayedShow()
{
    if (!isVisible() && !sequencer->wasCanceled() && sequencer->isRunning())
        show();
}

bool ProgressBar::canAbort() const
{
    int ret = QMessageBox::question(getMainWindow(),tr("Aborting"),
    tr("Do you really want to abort the operation?"),  QMessageBox::Yes,
    QMessageBox::No|QMessageBox::Default);

    return (ret == QMessageBox::Yes) ? true : false;
}

void ProgressBar::showEvent(QShowEvent* e)
{
    QProgressBar::showEvent(e);
    d->delayShowTimer->stop();
}

void ProgressBar::hideEvent(QHideEvent* e)
{
    QProgressBar::hideEvent(e);
    d->delayShowTimer->stop();
}

void ProgressBar::resetObserveEventFilter()
{
    d->observeEventFilter = 0;
}

void ProgressBar::enterControlEvents()
{
    qApp->installEventFilter(this);

    // Make sure that we get the key events, otherwise the Inventor viewer usurps the key events
    // This also disables accelerators.
    grabKeyboard();
}

void ProgressBar::leaveControlEvents()
{
    qApp->removeEventFilter(this);

    // relase the keyboard again
    releaseKeyboard();
}

bool ProgressBar::eventFilter(QObject* o, QEvent* e)
{
    if (sequencer->isRunning() && e != 0) {
        switch ( e->type() )
        {
        // check for ESC
        case QEvent::KeyPress:
            {
                QKeyEvent* ke = (QKeyEvent*)e;
                if (ke->key() == Qt::Key_Escape) {
                    // eventFilter() was called from the application 50 times without performing a new step (app could hang)
                    if (d->observeEventFilter > 50) {
                        // tries to unlock the application if it hangs (propably due to incorrect usage of Base::Sequencer)
                        if (ke->modifiers() & (Qt::ControlModifier | Qt::AltModifier)) {
                            sequencer->resetData();
                            return true;
                        }
                    }

                    // cancel the operation
                    sequencer->tryToCancel();
                }

                return true;
            }   break;

        // ignore alle these events
        case QEvent::KeyRelease:
        case QEvent::Enter:
        case QEvent::Leave:
        case QEvent::MouseButtonDblClick:
        case QEvent::ContextMenu:
            {
                return true;
            }   break;

        // special case if the main window's close button was pressed
        case QEvent::Close:
            {
                // avoid to exit while app is working
                // note: all other widget types are allowed to be closed anyway
                if (o == getMainWindow()) {
                    e->ignore();
                    return true;
                }
            }   break;

        // do a system beep and ignore the event
        case QEvent::MouseButtonPress:
            {
                if (d->isModalDialog(o))
                    return false;
                QApplication::beep();
                return true;
            }   break;

        default:
            {
            }   break;
        }

        d->observeEventFilter++;
    }

    return QProgressBar::eventFilter(o, e);
}

#include "moc_ProgressBar.cpp"
<commit_msg>Add an update timer to the progress bar set to 500ms instead of nothing. STEP Reader is updating the progress bar every time a new Shape is decoded/transferred and loaded, which is a huge amount of "interrupt" and slow down drastically the global performance<commit_after>/***************************************************************************
 *   Copyright (c) 2004 Werner Mayer <wmayer[at]users.sourceforge.net>     *
 *                                                                         *
 *   This file is part of the FreeCAD CAx development system.              *
 *                                                                         *
 *   This library is free software; you can redistribute it and/or         *
 *   modify it under the terms of the GNU Library General Public           *
 *   License as published by the Free Software Foundation; either          *
 *   version 2 of the License, or (at your option) any later version.      *
 *                                                                         *
 *   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 Library General Public License for more details.                  *
 *                                                                         *
 *   You should have received a copy of the GNU Library General Public     *
 *   License along with this library; see the file COPYING.LIB. If not,    *
 *   write to the Free Software Foundation, Inc., 59 Temple Place,         *
 *   Suite 330, Boston, MA  02111-1307, USA                                *
 *                                                                         *
 ***************************************************************************/

#include "PreCompiled.h"
#ifndef _PreComp_
# include <QApplication>
# include <QGenericReturnArgument>
# include <QKeyEvent>
# include <QMessageBox>
# include <QMetaObject>
# include <QStatusBar>
# include <QThread>
# include <QTime>
# include <QTimer>
#endif

#include "ProgressBar.h"
#include "MainWindow.h"
#include "WaitCursor.h"

using namespace Gui;

namespace Gui {
struct SequencerPrivate
{
    ProgressBar* bar;
    WaitCursor* waitCursor;
    QTime measureTime;
    QTime progressTime;
    QString text;
    bool guiThread;
};

struct ProgressBarPrivate
{
    QTimer* delayShowTimer;
    int minimumDuration;
    int observeEventFilter;

    bool isModalDialog(QObject* o) const
    {
        QWidget* parent = qobject_cast<QWidget*>(o);
        while (parent) {
            QMessageBox* dlg = qobject_cast<QMessageBox*>(parent);
            if (dlg && dlg->isModal())
                return true;
            parent = parent->parentWidget();
        }

        return false;
    }
};
}

Sequencer* Sequencer::_pclSingleton = 0;

Sequencer* Sequencer::instance()
{
    // not initialized?
    if (!_pclSingleton)
    {
        _pclSingleton = new Sequencer();
    }

    return _pclSingleton;
}

Sequencer::Sequencer ()
{
    d = new SequencerPrivate;
    d->bar = 0;
    d->waitCursor = 0;
    d->guiThread = true;
}

Sequencer::~Sequencer ()
{
    delete d;
}

void Sequencer::pause()
{
    QThread *currentThread = QThread::currentThread();
    QThread *thr = d->bar->thread(); // this is the main thread
    if (thr != currentThread)
        return;

    // allow key handling of dialog and restore cursor
    d->bar->leaveControlEvents();
    d->waitCursor->restoreCursor();
    QApplication::setOverrideCursor(Qt::ArrowCursor);
}

void Sequencer::resume()
{
    QThread *currentThread = QThread::currentThread();
    QThread *thr = d->bar->thread(); // this is the main thread
    if (thr != currentThread)
        return;

    QApplication::restoreOverrideCursor();
    d->waitCursor->setWaitCursor();
    // must be called as last to get control before WaitCursor
    d->bar->enterControlEvents(); // grab again
}

void Sequencer::startStep()
{
    QThread *currentThread = QThread::currentThread();
    QThread *thr = d->bar->thread(); // this is the main thread
    if (thr != currentThread) {
        d->guiThread = false;
        d->bar->setRange(0, (int)nTotalSteps);
        if (nTotalSteps == 0) {
            d->progressTime.start();
        }
        d->measureTime.start();
        QMetaObject::invokeMethod(d->bar, "aboutToShow", Qt::QueuedConnection);
    }
    else {
        d->guiThread = true;
        d->bar->setRange(0, (int)nTotalSteps);
        if (nTotalSteps == 0) {
            d->progressTime.start();
        }

        d->measureTime.start();
        d->waitCursor = new Gui::WaitCursor;
        d->bar->enterControlEvents();
        d->bar->aboutToShow();
    }
}

void Sequencer::nextStep(bool canAbort)
{
    QThread *currentThread = QThread::currentThread();
    QThread *thr = d->bar->thread(); // this is the main thread
    if (thr != currentThread) {
        setValue((int)nProgress+1);
    }
    else {
        if (wasCanceled() && canAbort) {
            // restore cursor
            pause();
            bool ok = d->bar->canAbort();
            // continue and show up wait cursor if needed
            resume();

            // force to abort the operation
            if ( ok ) {
                abort();
            } else {
                rejectCancel();
                setValue((int)nProgress+1);
            }
        }
        else {
            setValue((int)nProgress+1);
        }
    }
}

void Sequencer::setProgress(size_t step)
{
    d->bar->show();
    setValue((int)step);
}

void Sequencer::setValue(int step)
{
    QThread *currentThread = QThread::currentThread();
    QThread *thr = d->bar->thread(); // this is the main thread
    // if number of total steps is unknown then increment only by one
    if (nTotalSteps == 0) {
        int elapsed = d->progressTime.elapsed();
        // allow an update every 500 milliseconds only
        if (elapsed > 500) {
            d->progressTime.restart();
            if (thr != currentThread) {
                QMetaObject::invokeMethod(d->bar, "setValue", Qt::/*Blocking*/QueuedConnection,
                    QGenericReturnArgument(), Q_ARG(int,d->bar->value()+1));
            }
            else {
                d->bar->setValue(d->bar->value()+1);
                qApp->processEvents();
            }
        }
    }
    else {
        if (thr != currentThread) {
            QMetaObject::invokeMethod(d->bar, "setValue", Qt::/*Blocking*/QueuedConnection,
                QGenericReturnArgument(), Q_ARG(int,step));
            if (d->bar->isVisible())
                showRemainingTime();
        }
        else {
	    int elapsed = d->progressTime.restart();
	    if (elapsed > 500) {
	            d->bar->setValue(step);
	            if (d->bar->isVisible())
	                showRemainingTime();
	            d->bar->resetObserveEventFilter();
	            qApp->processEvents();
	    }
        }
    }
}

void Sequencer::showRemainingTime()
{
    QThread *currentThread = QThread::currentThread();
    QThread *thr = d->bar->thread(); // this is the main thread

    int elapsed = d->measureTime.elapsed();
    int progress = d->bar->value();
    int totalSteps = d->bar->maximum() - d->bar->minimum();

    QString txt = d->text;
    // More than 5 percent complete or more than 5 secs have elapsed.
    if (progress * 20 > totalSteps || elapsed > 5000) {
        int rest = (int) ( (double) totalSteps/progress * elapsed ) - elapsed;

        // more than 1 secs have elapsed and at least 100 ms are remaining
        if (elapsed > 1000 && rest > 100) {
            QTime time( 0,0, 0);
            time = time.addSecs( rest/1000 );
            QString remain = Gui::ProgressBar::tr("Remaining: %1").arg(time.toString());
            QString status = QString::fromLatin1("%1\t[%2]").arg(txt).arg(remain);

            if (thr != currentThread) {
                QMetaObject::invokeMethod(getMainWindow()->statusBar(), "showMessage",
                    Qt::/*Blocking*/QueuedConnection,
                    QGenericReturnArgument(),
                    Q_ARG(QString,status));
            }
            else {
                getMainWindow()->showMessage(status);
            }
        }
    }
}

void Sequencer::resetData()
{
    QThread *currentThread = QThread::currentThread();
    QThread *thr = d->bar->thread(); // this is the main thread
    if (thr != currentThread) {
        QMetaObject::invokeMethod(d->bar, "reset", Qt::QueuedConnection);
        QMetaObject::invokeMethod(d->bar, "hide", Qt::QueuedConnection);
        QMetaObject::invokeMethod(getMainWindow()->statusBar(), "showMessage",
            Qt::/*Blocking*/QueuedConnection,
            QGenericReturnArgument(),
            Q_ARG(QString,QString()));
        QMetaObject::invokeMethod(getMainWindow(), "setPaneText",
            Qt::/*Blocking*/QueuedConnection,
            QGenericReturnArgument(),
            Q_ARG(int,1),
            Q_ARG(QString,QString()));
    }
    else {
        d->bar->reset();
        // Note: Under Qt 4.1.4 this forces to run QWindowsStyle::eventFilter() twice
        // handling the same event thus a warning is printed. Possibly, this is a bug
        // in Qt. The message is QEventDispatcherUNIX::unregisterTimer: invalid argument.
        d->bar->hide();
        delete d->waitCursor;
        d->waitCursor = 0;
        d->bar->leaveControlEvents();
        getMainWindow()->setPaneText(1, QString());
        getMainWindow()->showMessage(QString());
    }

    SequencerBase::resetData();
}

void Sequencer::abort()
{
    //resets
    resetData();
    Base::AbortException exc("Aborting...");
    throw exc;
}

void Sequencer::setText (const char* pszTxt)
{
    QThread *currentThread = QThread::currentThread();
    QThread *thr = d->bar->thread(); // this is the main thread

    // print message to the statusbar
    d->text = pszTxt ? QString::fromUtf8(pszTxt) : QLatin1String("");
    if (thr != currentThread) {
        QMetaObject::invokeMethod(getMainWindow()->statusBar(), "showMessage",
            Qt::/*Blocking*/QueuedConnection,
            QGenericReturnArgument(),
            Q_ARG(QString,d->text));
    }
    else {
        getMainWindow()->showMessage(d->text);
    }
}

bool Sequencer::isBlocking() const
{
    return d->guiThread;
}

QProgressBar* Sequencer::getProgressBar(QWidget* parent)
{
    if (!d->bar)
        d->bar = new ProgressBar(this, parent);
    return d->bar;
}

// -------------------------------------------------------

/* TRANSLATOR Gui::ProgressBar */

ProgressBar::ProgressBar (Sequencer* s, QWidget * parent)
    : QProgressBar(parent), sequencer(s)
{
    d = new Gui::ProgressBarPrivate;
    d->minimumDuration = 2000; // 2 seconds
    d->delayShowTimer = new QTimer(this);
    d->delayShowTimer->setSingleShot(true);
    connect(d->delayShowTimer, SIGNAL(timeout()), this, SLOT(delayedShow()));
    d->observeEventFilter = 0;

    setFixedWidth(120);

    // write percentage to the center
    setAlignment(Qt::AlignHCenter);
    hide();
}

ProgressBar::~ProgressBar ()
{
    disconnect(d->delayShowTimer, SIGNAL(timeout()), this, SLOT(delayedShow()));
    delete d->delayShowTimer;
    delete d;
}

int ProgressBar::minimumDuration() const
{
    return d->minimumDuration;
}

void ProgressBar::setMinimumDuration (int ms)
{
    if (value() == 0)
    {
        d->delayShowTimer->stop();
        d->delayShowTimer->start(ms);
    }

    d->minimumDuration = ms;
}

void ProgressBar::aboutToShow()
{
    // delay showing the bar
    d->delayShowTimer->start(d->minimumDuration);
}

void ProgressBar::delayedShow()
{
    if (!isVisible() && !sequencer->wasCanceled() && sequencer->isRunning())
        show();
}

bool ProgressBar::canAbort() const
{
    int ret = QMessageBox::question(getMainWindow(),tr("Aborting"),
    tr("Do you really want to abort the operation?"),  QMessageBox::Yes,
    QMessageBox::No|QMessageBox::Default);

    return (ret == QMessageBox::Yes) ? true : false;
}

void ProgressBar::showEvent(QShowEvent* e)
{
    QProgressBar::showEvent(e);
    d->delayShowTimer->stop();
}

void ProgressBar::hideEvent(QHideEvent* e)
{
    QProgressBar::hideEvent(e);
    d->delayShowTimer->stop();
}

void ProgressBar::resetObserveEventFilter()
{
    d->observeEventFilter = 0;
}

void ProgressBar::enterControlEvents()
{
    qApp->installEventFilter(this);

    // Make sure that we get the key events, otherwise the Inventor viewer usurps the key events
    // This also disables accelerators.
    grabKeyboard();
}

void ProgressBar::leaveControlEvents()
{
    qApp->removeEventFilter(this);

    // relase the keyboard again
    releaseKeyboard();
}

bool ProgressBar::eventFilter(QObject* o, QEvent* e)
{
    if (sequencer->isRunning() && e != 0) {
        switch ( e->type() )
        {
        // check for ESC
        case QEvent::KeyPress:
            {
                QKeyEvent* ke = (QKeyEvent*)e;
                if (ke->key() == Qt::Key_Escape) {
                    // eventFilter() was called from the application 50 times without performing a new step (app could hang)
                    if (d->observeEventFilter > 50) {
                        // tries to unlock the application if it hangs (propably due to incorrect usage of Base::Sequencer)
                        if (ke->modifiers() & (Qt::ControlModifier | Qt::AltModifier)) {
                            sequencer->resetData();
                            return true;
                        }
                    }

                    // cancel the operation
                    sequencer->tryToCancel();
                }

                return true;
            }   break;

        // ignore alle these events
        case QEvent::KeyRelease:
        case QEvent::Enter:
        case QEvent::Leave:
        case QEvent::MouseButtonDblClick:
        case QEvent::ContextMenu:
            {
                return true;
            }   break;

        // special case if the main window's close button was pressed
        case QEvent::Close:
            {
                // avoid to exit while app is working
                // note: all other widget types are allowed to be closed anyway
                if (o == getMainWindow()) {
                    e->ignore();
                    return true;
                }
            }   break;

        // do a system beep and ignore the event
        case QEvent::MouseButtonPress:
            {
                if (d->isModalDialog(o))
                    return false;
                QApplication::beep();
                return true;
            }   break;

        default:
            {
            }   break;
        }

        d->observeEventFilter++;
    }

    return QProgressBar::eventFilter(o, e);
}

#include "moc_ProgressBar.cpp"
<|endoftext|>


<commit_before>/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2017 Baldur Karlsson
 *
 * 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 "ToolWindowManagerTabBar.h"
#include "ToolWindowManagerArea.h"
#include "ToolWindowManagerWrapper.h"
#include <QMouseEvent>
#include <QStyle>
#include <QStylePainter>
#include <QStyleOption>

ToolWindowManagerTabBar::ToolWindowManagerTabBar(QWidget *parent) :
  QTabBar(parent)
{
  m_tabsClosable = false;

  setMouseTracking(true);

  m_area = qobject_cast<ToolWindowManagerArea *>(parent);

  // Workaround for extremely dodgy KDE behaviour - by default the KDE theme will install event
  // filters on various widgets such as QTabBar and any descendents, and if a click is detected on
  // them that isn't on a tab it will immediately start moving the window, interfering with our own
  // click-to-drag behaviour.
  setProperty("_kde_no_window_grab", true);

  QStyleOptionToolButton buttonOpt;

  int size = style()->pixelMetric(QStyle::PM_SmallIconSize, 0, this);

  buttonOpt.initFrom(parentWidget());
  buttonOpt.iconSize = QSize(size, size);
  buttonOpt.subControls = 0;
  buttonOpt.activeSubControls = 0;
  buttonOpt.features = QStyleOptionToolButton::None;
  buttonOpt.arrowType = Qt::NoArrow;
  buttonOpt.state |= QStyle::State_AutoRaise;

  // TODO make our own pin icon, that is pinned/unpinned
  m_pin.icon = style()->standardIcon(QStyle::SP_TitleBarNormalButton, &buttonOpt, this);
  m_close.icon = style()->standardIcon(QStyle::SP_TitleBarCloseButton, &buttonOpt, this);

  m_pin.hover = m_pin.clicked = false;
  m_close.hover = m_close.clicked = false;
}

ToolWindowManagerTabBar::~ToolWindowManagerTabBar() {
}

QSize ToolWindowManagerTabBar::sizeHint() const {
  if(useMinimalBar()) {
    if (floatingWindowChild())
      return QSize(0, 0);

    QFontMetrics fm = fontMetrics();

    int iconSize = style()->pixelMetric(QStyle::PM_SmallIconSize, 0, this);
    int mw = style()->pixelMetric(QStyle::PM_DockWidgetTitleMargin, 0, this);

    int h = qMax(fm.height(), iconSize) + 2*mw;

    return QSize(h, h);
  }

  return QTabBar::sizeHint();
}

bool ToolWindowManagerTabBar::useMinimalBar() const
{
  if (count() > 1)
    return false;

  if (m_area) {
    return m_area->useMinimalTabBar();
  }
  return true;
}

QSize ToolWindowManagerTabBar::minimumSizeHint() const {
  if (useMinimalBar()) {
    return sizeHint();
  }

  return QTabBar::minimumSizeHint();
}

bool ToolWindowManagerTabBar::inButton(QPoint pos) {
  return m_pin.rect.contains(pos) || m_close.rect.contains(pos);
}

void ToolWindowManagerTabBar::paintEvent(QPaintEvent *event) {
  if (useMinimalBar()) {
    if (floatingWindowChild())
      return;

    QStylePainter p(this);

    QStyleOptionDockWidget option;

    option.initFrom(parentWidget());
    option.rect = m_titleRect;
    option.title = tabText(0);
    option.closable = m_tabsClosable;
    option.movable = false;
    // we only set floatable true so we can hijack the float button for our own pin/auto-hide button
    option.floatable = true;

    Shape s = shape();
    option.verticalTitleBar = s == RoundedEast || s == TriangularEast ||
                              s == RoundedWest || s == TriangularWest;

    p.drawControl(QStyle::CE_DockWidgetTitle, option);

    int size = style()->pixelMetric(QStyle::PM_SmallIconSize, 0, this);

    QStyleOptionToolButton buttonOpt;

    buttonOpt.initFrom(parentWidget());
    buttonOpt.iconSize = QSize(size, size);
    buttonOpt.subControls = 0;
    buttonOpt.activeSubControls = 0;
    buttonOpt.features = QStyleOptionToolButton::None;
    buttonOpt.arrowType = Qt::NoArrow;
    buttonOpt.state = QStyle::State_Active|QStyle::State_Enabled|QStyle::State_AutoRaise;

    buttonOpt.rect = m_pin.rect;
    buttonOpt.icon = m_pin.icon;

    QStyle::State prevState = buttonOpt.state;

    if(m_pin.clicked)
      buttonOpt.state |= QStyle::State_Sunken;
    else if(m_pin.hover)
      buttonOpt.state |= QStyle::State_Raised | QStyle::State_MouseOver;

    if (style()->styleHint(QStyle::SH_DockWidget_ButtonsHaveFrame, 0, this)) {
      style()->drawPrimitive(QStyle::PE_PanelButtonTool, &buttonOpt, &p, this);
    }

    style()->drawComplexControl(QStyle::CC_ToolButton, &buttonOpt, &p, this);

    if (m_tabsClosable) {
      buttonOpt.rect = m_close.rect;
      buttonOpt.icon = m_close.icon;

      buttonOpt.state = prevState;

      if(m_close.clicked)
        buttonOpt.state |= QStyle::State_Sunken;
      else if(m_close.hover)
        buttonOpt.state |= QStyle::State_Raised | QStyle::State_MouseOver;

      if (style()->styleHint(QStyle::SH_DockWidget_ButtonsHaveFrame, 0, this)) {
        style()->drawPrimitive(QStyle::PE_PanelButtonTool, &buttonOpt, &p, this);
      }

      style()->drawComplexControl(QStyle::CC_ToolButton, &buttonOpt, &p, this);
    }
    return;
  }

  QTabBar::paintEvent(event);
}

void ToolWindowManagerTabBar::resizeEvent(QResizeEvent *event) {
  QTabBar::resizeEvent(event);

  if (count() > 1 || floatingWindowChild())
    return;

  m_titleRect = QRect(0, 0, size().width(), sizeHint().height());

  QStyleOptionDockWidget option;

  option.initFrom(parentWidget());
  option.rect = m_titleRect;
  option.closable = m_tabsClosable;
  option.movable = false;
  // we only set floatable true so we can hijack the float button for our own pin/auto-hide button
  option.floatable = true;

  m_pin.rect = style()->subElementRect(QStyle::SE_DockWidgetFloatButton, &option, this);
  m_close.rect = style()->subElementRect(QStyle::SE_DockWidgetCloseButton, &option, this);
}

void ToolWindowManagerTabBar::mousePressEvent(QMouseEvent *event) {
  QTabBar::mousePressEvent(event);

  if (count() > 1 || floatingWindowChild())
    return;

  ButtonData prevPin = m_pin;
  ButtonData prevClose = m_close;

  if (m_pin.rect.contains(mapFromGlobal(QCursor::pos())) &&
     event->buttons() & Qt::LeftButton) {
    m_pin.clicked = true;
  } else {
    m_pin.clicked = false;
  }

  if (m_close.rect.contains(mapFromGlobal(QCursor::pos())) &&
     event->buttons() & Qt::LeftButton) {
    m_close.clicked = true;
  } else {
    m_close.clicked = false;
  }

  if (prevPin != m_pin || prevClose != m_close)
    update();

  event->accept();
}

void ToolWindowManagerTabBar::mouseMoveEvent(QMouseEvent *event) {
  QTabBar::mouseMoveEvent(event);

  if (count() > 1 || floatingWindowChild())
    return;

  ButtonData prevPin = m_pin;
  ButtonData prevClose = m_close;

  if (m_pin.rect.contains(mapFromGlobal(QCursor::pos()))) {
    m_pin.hover = true;
    if (event->buttons() & Qt::LeftButton)
      m_pin.clicked = true;
  } else {
    m_pin.hover = false;
    m_pin.clicked = false;
  }

  if (m_close.rect.contains(mapFromGlobal(QCursor::pos()))) {
    m_close.hover = true;
    if (event->buttons() & Qt::LeftButton)
      m_close.clicked = true;
  } else {
    m_close.hover = false;
    m_close.clicked = false;
  }

  if (prevPin != m_pin || prevClose != m_close)
    update();
}

void ToolWindowManagerTabBar::mouseReleaseEvent(QMouseEvent *event) {
  QTabBar::mouseReleaseEvent(event);

  if (count() > 1 || floatingWindowChild())
    return;

  if (m_pin.rect.contains(mapFromGlobal(QCursor::pos()))) {
    // process a pin of these tabs

    m_pin.clicked = false;

    update();

    event->accept();
  }

  if (m_close.rect.contains(mapFromGlobal(QCursor::pos()))) {
    if (m_area)
      m_area->tabCloseRequested(0);

    m_close.clicked = false;

    update();

    event->accept();
  }
}

void ToolWindowManagerTabBar::tabInserted(int) {
  updateClosable();
}

void ToolWindowManagerTabBar::tabRemoved(int) {
  updateClosable();
}

void ToolWindowManagerTabBar::updateClosable() {
  QTabBar::setTabsClosable(m_tabsClosable && count() > 1);
}

bool ToolWindowManagerTabBar::floatingWindowChild() const {
  ToolWindowManagerArea *area = qobject_cast<ToolWindowManagerArea *>(parentWidget());

  if (area) {
    ToolWindowManagerWrapper *wrapper = qobject_cast<ToolWindowManagerWrapper *>(area->parentWidget());

    if (wrapper && wrapper->floating())
      return true;
  }

  return false;
}
<commit_msg>Don't have close buttons on minimal tab bar, when the tab isn't closable<commit_after>/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2017 Baldur Karlsson
 *
 * 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 "ToolWindowManager.h"
#include "ToolWindowManagerTabBar.h"
#include "ToolWindowManagerArea.h"
#include "ToolWindowManagerWrapper.h"
#include <QMouseEvent>
#include <QStyle>
#include <QStylePainter>
#include <QStyleOption>

ToolWindowManagerTabBar::ToolWindowManagerTabBar(QWidget *parent) :
  QTabBar(parent)
{
  m_tabsClosable = false;

  setMouseTracking(true);

  m_area = qobject_cast<ToolWindowManagerArea *>(parent);

  // Workaround for extremely dodgy KDE behaviour - by default the KDE theme will install event
  // filters on various widgets such as QTabBar and any descendents, and if a click is detected on
  // them that isn't on a tab it will immediately start moving the window, interfering with our own
  // click-to-drag behaviour.
  setProperty("_kde_no_window_grab", true);

  QStyleOptionToolButton buttonOpt;

  int size = style()->pixelMetric(QStyle::PM_SmallIconSize, 0, this);

  buttonOpt.initFrom(parentWidget());
  buttonOpt.iconSize = QSize(size, size);
  buttonOpt.subControls = 0;
  buttonOpt.activeSubControls = 0;
  buttonOpt.features = QStyleOptionToolButton::None;
  buttonOpt.arrowType = Qt::NoArrow;
  buttonOpt.state |= QStyle::State_AutoRaise;

  // TODO make our own pin icon, that is pinned/unpinned
  m_pin.icon = style()->standardIcon(QStyle::SP_TitleBarNormalButton, &buttonOpt, this);
  m_close.icon = style()->standardIcon(QStyle::SP_TitleBarCloseButton, &buttonOpt, this);

  m_pin.hover = m_pin.clicked = false;
  m_close.hover = m_close.clicked = false;
}

ToolWindowManagerTabBar::~ToolWindowManagerTabBar() {
}

QSize ToolWindowManagerTabBar::sizeHint() const {
  if(useMinimalBar()) {
    if (floatingWindowChild())
      return QSize(0, 0);

    QFontMetrics fm = fontMetrics();

    int iconSize = style()->pixelMetric(QStyle::PM_SmallIconSize, 0, this);
    int mw = style()->pixelMetric(QStyle::PM_DockWidgetTitleMargin, 0, this);

    int h = qMax(fm.height(), iconSize) + 2*mw;

    return QSize(h, h);
  }

  return QTabBar::sizeHint();
}

bool ToolWindowManagerTabBar::useMinimalBar() const
{
  if (count() > 1)
    return false;

  if (m_area) {
    return m_area->useMinimalTabBar();
  }
  return true;
}

QSize ToolWindowManagerTabBar::minimumSizeHint() const {
  if (useMinimalBar()) {
    return sizeHint();
  }

  return QTabBar::minimumSizeHint();
}

bool ToolWindowManagerTabBar::inButton(QPoint pos) {
  return m_pin.rect.contains(pos) || m_close.rect.contains(pos);
}

void ToolWindowManagerTabBar::paintEvent(QPaintEvent *event) {
  if (useMinimalBar()) {
    if (floatingWindowChild())
      return;

    QStylePainter p(this);

    QStyleOptionDockWidget option;

    option.initFrom(parentWidget());
    option.rect = m_titleRect;
    option.title = tabText(0);
    option.closable = m_tabsClosable;
    option.movable = false;
    // we only set floatable true so we can hijack the float button for our own pin/auto-hide button
    option.floatable = true;

    Shape s = shape();
    option.verticalTitleBar = s == RoundedEast || s == TriangularEast ||
                              s == RoundedWest || s == TriangularWest;

    p.drawControl(QStyle::CE_DockWidgetTitle, option);

    int size = style()->pixelMetric(QStyle::PM_SmallIconSize, 0, this);

    QStyleOptionToolButton buttonOpt;

    buttonOpt.initFrom(parentWidget());
    buttonOpt.iconSize = QSize(size, size);
    buttonOpt.subControls = 0;
    buttonOpt.activeSubControls = 0;
    buttonOpt.features = QStyleOptionToolButton::None;
    buttonOpt.arrowType = Qt::NoArrow;
    buttonOpt.state = QStyle::State_Active|QStyle::State_Enabled|QStyle::State_AutoRaise;

    buttonOpt.rect = m_pin.rect;
    buttonOpt.icon = m_pin.icon;

    ToolWindowManager::ToolWindowProperty props =
      m_area->m_manager->toolWindowProperties(m_area->widget(0));

    bool tabClosable = (props & ToolWindowManager::HideCloseButton) == 0;

    if (!tabClosable)
      buttonOpt.rect = m_close.rect;

    QStyle::State prevState = buttonOpt.state;

    if(m_pin.clicked)
      buttonOpt.state |= QStyle::State_Sunken;
    else if(m_pin.hover)
      buttonOpt.state |= QStyle::State_Raised | QStyle::State_MouseOver;

    if (style()->styleHint(QStyle::SH_DockWidget_ButtonsHaveFrame, 0, this)) {
      style()->drawPrimitive(QStyle::PE_PanelButtonTool, &buttonOpt, &p, this);
    }

    style()->drawComplexControl(QStyle::CC_ToolButton, &buttonOpt, &p, this);

    if (m_tabsClosable && tabClosable) {
      buttonOpt.rect = m_close.rect;
      buttonOpt.icon = m_close.icon;

      buttonOpt.state = prevState;

      if(m_close.clicked)
        buttonOpt.state |= QStyle::State_Sunken;
      else if(m_close.hover)
        buttonOpt.state |= QStyle::State_Raised | QStyle::State_MouseOver;

      if (style()->styleHint(QStyle::SH_DockWidget_ButtonsHaveFrame, 0, this)) {
        style()->drawPrimitive(QStyle::PE_PanelButtonTool, &buttonOpt, &p, this);
      }

      style()->drawComplexControl(QStyle::CC_ToolButton, &buttonOpt, &p, this);
    }
    return;
  }

  QTabBar::paintEvent(event);
}

void ToolWindowManagerTabBar::resizeEvent(QResizeEvent *event) {
  QTabBar::resizeEvent(event);

  if (count() > 1 || floatingWindowChild())
    return;

  m_titleRect = QRect(0, 0, size().width(), sizeHint().height());

  QStyleOptionDockWidget option;

  option.initFrom(parentWidget());
  option.rect = m_titleRect;
  option.closable = m_tabsClosable;
  option.movable = false;
  // we only set floatable true so we can hijack the float button for our own pin/auto-hide button
  option.floatable = true;

  m_pin.rect = style()->subElementRect(QStyle::SE_DockWidgetFloatButton, &option, this);
  m_close.rect = style()->subElementRect(QStyle::SE_DockWidgetCloseButton, &option, this);
}

void ToolWindowManagerTabBar::mousePressEvent(QMouseEvent *event) {
  QTabBar::mousePressEvent(event);

  if (count() > 1 || floatingWindowChild())
    return;

  ButtonData prevPin = m_pin;
  ButtonData prevClose = m_close;

  ToolWindowManager::ToolWindowProperty props =
    m_area->m_manager->toolWindowProperties(m_area->widget(0));

  bool tabClosable = (props & ToolWindowManager::HideCloseButton) == 0;

  QRect pinRect = m_pin.rect;
  QRect closeRect = m_close.rect;

  if (!tabClosable) {
    pinRect = closeRect;
    closeRect = QRect();
  }

  if (pinRect.contains(mapFromGlobal(QCursor::pos())) &&
     event->buttons() & Qt::LeftButton) {
    m_pin.clicked = true;
  } else {
    m_pin.clicked = false;
  }

  if (closeRect.contains(mapFromGlobal(QCursor::pos())) &&
     event->buttons() & Qt::LeftButton) {
    m_close.clicked = true;
  } else {
    m_close.clicked = false;
  }

  if (prevPin != m_pin || prevClose != m_close)
    update();

  event->accept();
}

void ToolWindowManagerTabBar::mouseMoveEvent(QMouseEvent *event) {
  QTabBar::mouseMoveEvent(event);

  if (count() > 1 || floatingWindowChild())
    return;

  ButtonData prevPin = m_pin;
  ButtonData prevClose = m_close;

  ToolWindowManager::ToolWindowProperty props =
    m_area->m_manager->toolWindowProperties(m_area->widget(0));

  bool tabClosable = (props & ToolWindowManager::HideCloseButton) == 0;

  QRect pinRect = m_pin.rect;
  QRect closeRect = m_close.rect;

  if (!tabClosable) {
    pinRect = closeRect;
    closeRect = QRect();
  }

  if (pinRect.contains(mapFromGlobal(QCursor::pos()))) {
    m_pin.hover = true;
    if (event->buttons() & Qt::LeftButton)
      m_pin.clicked = true;
  } else {
    m_pin.hover = false;
    m_pin.clicked = false;
  }

  if (closeRect.contains(mapFromGlobal(QCursor::pos()))) {
    m_close.hover = true;
    if (event->buttons() & Qt::LeftButton)
      m_close.clicked = true;
  } else {
    m_close.hover = false;
    m_close.clicked = false;
  }

  if (prevPin != m_pin || prevClose != m_close)
    update();
}

void ToolWindowManagerTabBar::mouseReleaseEvent(QMouseEvent *event) {
  QTabBar::mouseReleaseEvent(event);

  if (count() > 1 || floatingWindowChild())
    return;

  ToolWindowManager::ToolWindowProperty props =
    m_area->m_manager->toolWindowProperties(m_area->widget(0));

  bool tabClosable = (props & ToolWindowManager::HideCloseButton) == 0;

  QRect pinRect = m_pin.rect;
  QRect closeRect = m_close.rect;

  if (!tabClosable) {
    pinRect = closeRect;
    closeRect = QRect();
  }

  if (pinRect.contains(mapFromGlobal(QCursor::pos()))) {
    // process a pin of these tabs

    m_pin.clicked = false;

    update();

    event->accept();
  }

  if (closeRect.contains(mapFromGlobal(QCursor::pos()))) {
    if (m_area)
      m_area->tabCloseRequested(0);

    m_close.clicked = false;

    update();

    event->accept();
  }
}

void ToolWindowManagerTabBar::tabInserted(int) {
  updateClosable();
}

void ToolWindowManagerTabBar::tabRemoved(int) {
  updateClosable();
}

void ToolWindowManagerTabBar::updateClosable() {
  QTabBar::setTabsClosable(m_tabsClosable && count() > 1);
}

bool ToolWindowManagerTabBar::floatingWindowChild() const {
  ToolWindowManagerArea *area = qobject_cast<ToolWindowManagerArea *>(parentWidget());

  if (area) {
    ToolWindowManagerWrapper *wrapper = qobject_cast<ToolWindowManagerWrapper *>(area->parentWidget());

    if (wrapper && wrapper->floating())
      return true;
  }

  return false;
}
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2015 deipi.com LLC and contributors. All rights reserved.
 *
 * 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 "xapiand.h"

#include "utils.h"
#include "manager.h"

#include "tclap/CmdLine.h"
#include "tclap/ZshCompletionOutput.h"

#include <thread>
#include <stdlib.h>

using namespace TCLAP;

XapiandManager *manager_ptr = NULL;

static void sig_shutdown_handler(int sig)
{
	if (manager_ptr) {
		manager_ptr->sig_shutdown_handler(sig);
	}
}

void setup_signal_handlers(void) {
	signal(SIGHUP, SIG_IGN);
	signal(SIGPIPE, SIG_IGN);

	struct sigaction act;

	/* When the SA_SIGINFO flag is set in sa_flags then sa_sigaction is used.
	 * Otherwise, sa_handler is used. */
	sigemptyset(&act.sa_mask);
	act.sa_flags = 0;
	act.sa_handler = sig_shutdown_handler;
	sigaction(SIGTERM, &act, NULL);
	sigaction(SIGINT, &act, NULL);
}

void run(int num_servers, const char *cluster_name_, const char *node_name_, const char *discovery_group, int discovery_port, int http_port, int binary_port)
{
	ev::default_loop default_loop;

	setup_signal_handlers();

	XapiandManager manager(&default_loop, cluster_name_, node_name_, discovery_group, discovery_port, http_port, binary_port);

	manager_ptr = &manager;

	manager.run(num_servers);

	manager_ptr = NULL;
}

// This exemplifies how the output class can be overridden to provide
// user defined output.
class CmdOutput : public StdOutput
{
	public:
		virtual void failure(CmdLineInterface& _cmd, ArgException& e) {
			std::string progName = _cmd.getProgramName();

			std::cerr << "Error: " << e.argId() << std::endl;

			spacePrint(std::cerr, e.error(), 75, 3, 0);

			std::cerr << std::endl;

			if (_cmd.hasHelpAndVersion()) {
				std::cerr << "Usage: " << std::endl;

				_shortUsage( _cmd, std::cerr );

				std::cerr << std::endl << "For complete usage and help type: "
						  << std::endl << "   " << progName << " "
						  << Arg::nameStartString() << "help"
						  << std::endl << std::endl;
			} else {
				usage(_cmd);
			}

			throw ExitException(1);
		}

		virtual void usage(CmdLineInterface& _cmd) {
			spacePrint(std::cout, PACKAGE_STRING, 75, 0, 0);
			spacePrint(std::cout, "[" PACKAGE_BUGREPORT "]", 75, 0, 0);

			std::cout << std::endl;

			std::cout << "Usage: " << std::endl;

			_shortUsage(_cmd, std::cout);

			std::cout << std::endl << "Where: " << std::endl;

			_longUsage(_cmd, std::cout);
		}

		virtual void version(CmdLineInterface& _cmd) {
			std::string xversion = _cmd.getVersion();
			std::cout << xversion << std::endl;
		}
};

typedef struct opts_s {
	int verbosity;
	bool daemonize;
	bool glass;
	std::string cluster_name;
	std::string node_name;
	unsigned int http_port;
	unsigned int binary_port;
	unsigned int discovery_port;
	std::string pidfile;
	std::string uid;
	std::string gid;
	std::string discovery_group;
	unsigned int num_servers;
} opts_t;

void parseOptions(int argc, char** argv, opts_t &opts)
{
	unsigned int nthreads = std::thread::hardware_concurrency() * 2;

	try {
		CmdLine cmd("", ' ', PACKAGE_STRING);

		// ZshCompletionOutput zshoutput;
		// cmd.setOutput(&zshoutput);

		CmdOutput output;
		cmd.setOutput(&output);

		MultiSwitchArg verbosity("v", "verbose", "Increase verbosity.", cmd);
		SwitchArg daemonize("d", "daemon", "daemonize (run in background).", cmd);

#ifdef XAPIAN_HAS_GLASS_BACKEND
		SwitchArg glass("", "glass", "Try using glass databases.", cmd, false);
#endif

		ValueArg<std::string> cluster_name("", "cluster", "Cluster name to join.", false, XAPIAND_CLUSTER_NAME, "cluster", cmd);
		ValueArg<std::string> node_name("n", "name", "Node name.", false, "", "node", cmd);

		ValueArg<unsigned int> http_port("", "http", "HTTP REST API port", false, XAPIAND_HTTP_SERVERPORT, "port", cmd);
		ValueArg<unsigned int> binary_port("", "xapian", "Xapian binary protocol port", false, XAPIAND_BINARY_SERVERPORT, "port", cmd);
		ValueArg<unsigned int> discovery_port("", "discovery", "Discovery UDP port", false, XAPIAND_DISCOVERY_SERVERPORT, "port", cmd);
		ValueArg<std::string> discovery_group("", "group", "Discovery UDP group", false, XAPIAND_DISCOVERY_GROUP, "group", cmd);

		ValueArg<std::string> pidfile("p", "pid", "Write PID to <pidfile>.", false, "xapiand.pid", "pidfile", cmd);
		ValueArg<std::string> uid("u", "uid", "User ID.", false, "xapiand", "uid", cmd);
		ValueArg<std::string> gid("g", "gid", "Group ID.", false, "xapiand", "uid", cmd);

		ValueArg<unsigned int> num_servers("", "workers", "Number of worker servers.", false, nthreads, "servers", cmd);

		cmd.parse( argc, argv );

		opts.verbosity = verbosity.getValue();
		opts.daemonize = daemonize.getValue();
#ifdef XAPIAN_HAS_GLASS_BACKEND
		opts.glass = glass.getValue();
#endif
		opts.cluster_name = cluster_name.getValue();
		opts.node_name = node_name.getValue();
		opts.http_port = http_port.getValue();
		opts.binary_port = binary_port.getValue();
		opts.discovery_port = discovery_port.getValue();
		opts.discovery_group = discovery_group.getValue();
		opts.pidfile = pidfile.getValue();
		opts.uid = uid.getValue();
		opts.gid = gid.getValue();
		opts.num_servers = num_servers.getValue();

	} catch (ArgException &e) { // catch any exceptions
		std::cerr << "error: " << e.error() << " for arg " << e.argId() << std::endl;
	}
}

int main(int argc, char **argv)
{
	opts_t opts;

	parseOptions(argc, argv, opts);

	int http_port = (opts.http_port == XAPIAND_HTTP_SERVERPORT) ? 0 : opts.http_port;
	int binary_port = (opts.binary_port == XAPIAND_BINARY_SERVERPORT) ? 0 : opts.binary_port;
	int discovery_port = (opts.discovery_port == XAPIAND_DISCOVERY_SERVERPORT) ? 0 : opts.discovery_port;
	const char *discovery_group = (opts.discovery_group.empty() || opts.discovery_group == XAPIAND_DISCOVERY_GROUP) ? NULL : opts.discovery_group.c_str();
	const char *cluster_name = opts.cluster_name.c_str();;
	const char *node_name = opts.node_name.c_str();;

	INFO((void *)NULL,
		"\n\n"
		"  __  __           _                 _\n"
		"  \\ \\/ /__ _ _ __ (_) __ _ _ __   __| |\n"
		"   \\  // _` | '_ \\| |/ _` | '_ \\ / _` |\n"
		"   /  \\ (_| | |_) | | (_| | | | | (_| |\n"
		"  /_/\\_\\__,_| .__/|_|\\__,_|_| |_|\\__,_|\n"
		"            |_|  v%s\n"
		"   [%s]\n"
		"          Using Xapian v%s\n\n", PACKAGE_VERSION, PACKAGE_BUGREPORT, XAPIAN_VERSION);

	INFO((void *)NULL, "Joined cluster: %s\n", cluster_name);

#ifdef XAPIAN_HAS_GLASS_BACKEND
	if (opts.glass) {
		// Prefer glass database
		if (setenv("XAPIAN_PREFER_GLASS", "1", false) == 0) {
			INFO((void *)NULL, "Enabled glass database.\n");
		}
	}
#endif

	// Enable changesets
	if (setenv("XAPIAN_MAX_CHANGESETS", "10", false) == 0) {
		INFO((void *)NULL, "Database changesets set to 10.\n");
	}

	// Flush threshold increased
	int flush_threshold = 10000;  // Default is 10000 (if no set)
	const char *p = getenv("XAPIAN_FLUSH_THRESHOLD");
	if (p) flush_threshold = atoi(p);
	if (flush_threshold < 100000 && setenv("XAPIAN_FLUSH_THRESHOLD", "100000", false) == 0) {
		INFO((void *)NULL, "Increased flush threshold to 100000 (it was originally set to %d).\n", flush_threshold);
	}

	time(&init_time);
	struct tm *timeinfo = localtime(&init_time);
	timeinfo->tm_hour   = 0;
	timeinfo->tm_min    = 0;
	timeinfo->tm_sec    = 0;
	int diff_t = (int)(init_time - mktime(timeinfo));
	b_time.minute = diff_t / SLOT_TIME_SECOND;
	b_time.second =  diff_t % SLOT_TIME_SECOND;

	run(opts.num_servers, cluster_name, node_name, discovery_group, discovery_port, http_port, binary_port);

	INFO((void *)NULL, "Done with all work!\n");
	return 0;
}
<commit_msg>Name main thread<commit_after>/*
 * Copyright (C) 2015 deipi.com LLC and contributors. All rights reserved.
 *
 * 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 "xapiand.h"

#include "utils.h"
#include "manager.h"

#include "tclap/CmdLine.h"
#include "tclap/ZshCompletionOutput.h"

#include <thread>
#include <stdlib.h>

using namespace TCLAP;

XapiandManager *manager_ptr = NULL;

static void sig_shutdown_handler(int sig)
{
	if (manager_ptr) {
		manager_ptr->sig_shutdown_handler(sig);
	}
}

void setup_signal_handlers(void) {
	signal(SIGHUP, SIG_IGN);
	signal(SIGPIPE, SIG_IGN);

	struct sigaction act;

	/* When the SA_SIGINFO flag is set in sa_flags then sa_sigaction is used.
	 * Otherwise, sa_handler is used. */
	sigemptyset(&act.sa_mask);
	act.sa_flags = 0;
	act.sa_handler = sig_shutdown_handler;
	sigaction(SIGTERM, &act, NULL);
	sigaction(SIGINT, &act, NULL);
}

void run(int num_servers, const char *cluster_name_, const char *node_name_, const char *discovery_group, int discovery_port, int http_port, int binary_port)
{
#ifdef HAVE_PTHREAD_SETNAME_NP_2
    pthread_setname_np(pthread_self(), "==");
#else
    pthread_setname_np("==");
#endif

	ev::default_loop default_loop;

	setup_signal_handlers();

	XapiandManager manager(&default_loop, cluster_name_, node_name_, discovery_group, discovery_port, http_port, binary_port);

	manager_ptr = &manager;

	manager.run(num_servers);

	manager_ptr = NULL;
}

// This exemplifies how the output class can be overridden to provide
// user defined output.
class CmdOutput : public StdOutput
{
	public:
		virtual void failure(CmdLineInterface& _cmd, ArgException& e) {
			std::string progName = _cmd.getProgramName();

			std::cerr << "Error: " << e.argId() << std::endl;

			spacePrint(std::cerr, e.error(), 75, 3, 0);

			std::cerr << std::endl;

			if (_cmd.hasHelpAndVersion()) {
				std::cerr << "Usage: " << std::endl;

				_shortUsage( _cmd, std::cerr );

				std::cerr << std::endl << "For complete usage and help type: "
						  << std::endl << "   " << progName << " "
						  << Arg::nameStartString() << "help"
						  << std::endl << std::endl;
			} else {
				usage(_cmd);
			}

			throw ExitException(1);
		}

		virtual void usage(CmdLineInterface& _cmd) {
			spacePrint(std::cout, PACKAGE_STRING, 75, 0, 0);
			spacePrint(std::cout, "[" PACKAGE_BUGREPORT "]", 75, 0, 0);

			std::cout << std::endl;

			std::cout << "Usage: " << std::endl;

			_shortUsage(_cmd, std::cout);

			std::cout << std::endl << "Where: " << std::endl;

			_longUsage(_cmd, std::cout);
		}

		virtual void version(CmdLineInterface& _cmd) {
			std::string xversion = _cmd.getVersion();
			std::cout << xversion << std::endl;
		}
};

typedef struct opts_s {
	int verbosity;
	bool daemonize;
	bool glass;
	std::string cluster_name;
	std::string node_name;
	unsigned int http_port;
	unsigned int binary_port;
	unsigned int discovery_port;
	std::string pidfile;
	std::string uid;
	std::string gid;
	std::string discovery_group;
	unsigned int num_servers;
} opts_t;

void parseOptions(int argc, char** argv, opts_t &opts)
{
	unsigned int nthreads = std::thread::hardware_concurrency() * 2;

	try {
		CmdLine cmd("", ' ', PACKAGE_STRING);

		// ZshCompletionOutput zshoutput;
		// cmd.setOutput(&zshoutput);

		CmdOutput output;
		cmd.setOutput(&output);

		MultiSwitchArg verbosity("v", "verbose", "Increase verbosity.", cmd);
		SwitchArg daemonize("d", "daemon", "daemonize (run in background).", cmd);

#ifdef XAPIAN_HAS_GLASS_BACKEND
		SwitchArg glass("", "glass", "Try using glass databases.", cmd, false);
#endif

		ValueArg<std::string> cluster_name("", "cluster", "Cluster name to join.", false, XAPIAND_CLUSTER_NAME, "cluster", cmd);
		ValueArg<std::string> node_name("n", "name", "Node name.", false, "", "node", cmd);

		ValueArg<unsigned int> http_port("", "http", "HTTP REST API port", false, XAPIAND_HTTP_SERVERPORT, "port", cmd);
		ValueArg<unsigned int> binary_port("", "xapian", "Xapian binary protocol port", false, XAPIAND_BINARY_SERVERPORT, "port", cmd);
		ValueArg<unsigned int> discovery_port("", "discovery", "Discovery UDP port", false, XAPIAND_DISCOVERY_SERVERPORT, "port", cmd);
		ValueArg<std::string> discovery_group("", "group", "Discovery UDP group", false, XAPIAND_DISCOVERY_GROUP, "group", cmd);

		ValueArg<std::string> pidfile("p", "pid", "Write PID to <pidfile>.", false, "xapiand.pid", "pidfile", cmd);
		ValueArg<std::string> uid("u", "uid", "User ID.", false, "xapiand", "uid", cmd);
		ValueArg<std::string> gid("g", "gid", "Group ID.", false, "xapiand", "uid", cmd);

		ValueArg<unsigned int> num_servers("", "workers", "Number of worker servers.", false, nthreads, "servers", cmd);

		cmd.parse( argc, argv );

		opts.verbosity = verbosity.getValue();
		opts.daemonize = daemonize.getValue();
#ifdef XAPIAN_HAS_GLASS_BACKEND
		opts.glass = glass.getValue();
#endif
		opts.cluster_name = cluster_name.getValue();
		opts.node_name = node_name.getValue();
		opts.http_port = http_port.getValue();
		opts.binary_port = binary_port.getValue();
		opts.discovery_port = discovery_port.getValue();
		opts.discovery_group = discovery_group.getValue();
		opts.pidfile = pidfile.getValue();
		opts.uid = uid.getValue();
		opts.gid = gid.getValue();
		opts.num_servers = num_servers.getValue();

	} catch (ArgException &e) { // catch any exceptions
		std::cerr << "error: " << e.error() << " for arg " << e.argId() << std::endl;
	}
}

int main(int argc, char **argv)
{
	opts_t opts;

	parseOptions(argc, argv, opts);

	int http_port = (opts.http_port == XAPIAND_HTTP_SERVERPORT) ? 0 : opts.http_port;
	int binary_port = (opts.binary_port == XAPIAND_BINARY_SERVERPORT) ? 0 : opts.binary_port;
	int discovery_port = (opts.discovery_port == XAPIAND_DISCOVERY_SERVERPORT) ? 0 : opts.discovery_port;
	const char *discovery_group = (opts.discovery_group.empty() || opts.discovery_group == XAPIAND_DISCOVERY_GROUP) ? NULL : opts.discovery_group.c_str();
	const char *cluster_name = opts.cluster_name.c_str();;
	const char *node_name = opts.node_name.c_str();;

	INFO((void *)NULL,
		"\n\n"
		"  __  __           _                 _\n"
		"  \\ \\/ /__ _ _ __ (_) __ _ _ __   __| |\n"
		"   \\  // _` | '_ \\| |/ _` | '_ \\ / _` |\n"
		"   /  \\ (_| | |_) | | (_| | | | | (_| |\n"
		"  /_/\\_\\__,_| .__/|_|\\__,_|_| |_|\\__,_|\n"
		"            |_|  v%s\n"
		"   [%s]\n"
		"          Using Xapian v%s\n\n", PACKAGE_VERSION, PACKAGE_BUGREPORT, XAPIAN_VERSION);

	INFO((void *)NULL, "Joined cluster: %s\n", cluster_name);

#ifdef XAPIAN_HAS_GLASS_BACKEND
	if (opts.glass) {
		// Prefer glass database
		if (setenv("XAPIAN_PREFER_GLASS", "1", false) == 0) {
			INFO((void *)NULL, "Enabled glass database.\n");
		}
	}
#endif

	// Enable changesets
	if (setenv("XAPIAN_MAX_CHANGESETS", "10", false) == 0) {
		INFO((void *)NULL, "Database changesets set to 10.\n");
	}

	// Flush threshold increased
	int flush_threshold = 10000;  // Default is 10000 (if no set)
	const char *p = getenv("XAPIAN_FLUSH_THRESHOLD");
	if (p) flush_threshold = atoi(p);
	if (flush_threshold < 100000 && setenv("XAPIAN_FLUSH_THRESHOLD", "100000", false) == 0) {
		INFO((void *)NULL, "Increased flush threshold to 100000 (it was originally set to %d).\n", flush_threshold);
	}

	time(&init_time);
	struct tm *timeinfo = localtime(&init_time);
	timeinfo->tm_hour   = 0;
	timeinfo->tm_min    = 0;
	timeinfo->tm_sec    = 0;
	int diff_t = (int)(init_time - mktime(timeinfo));
	b_time.minute = diff_t / SLOT_TIME_SECOND;
	b_time.second =  diff_t % SLOT_TIME_SECOND;

	run(opts.num_servers, cluster_name, node_name, discovery_group, discovery_port, http_port, binary_port);

	INFO((void *)NULL, "Done with all work!\n");
	return 0;
}
<|endoftext|>


<commit_before>#include "HighlightedFile.hpp"
#include <utility>
#include <fstream>
#include <climits>
#include <iostream>

using namespace synth;

PrimitiveTag Markup::begin_tag() const
{
    std::string content("<");
    content += tag;
    for (auto const& kv : attrs) {
        content += ' ';
        content += kv.first;
        content += "=\"";
        content += kv.second; // TODO? Escape?
        content += '"';
    }
    content += '>';
    return {begin_offset, content};
}

PrimitiveTag Markup::end_tag() const
{
    return {end_offset, std::string("</") + tag + ">"};
}

static std::vector<PrimitiveTag> flatten(
    std::vector<Markup> const& sortedMarkups)
{
    std::vector<PrimitiveTag> r;
    std::vector<PrimitiveTag> pendingEnds;
    for (auto const& m : sortedMarkups) {
        while (!pendingEnds.empty()
            && m.begin_offset >= pendingEnds.back().offset
        ) {
            r.push_back(std::move(pendingEnds.back()));
            pendingEnds.pop_back();
        }
        r.push_back(m.begin_tag());
        pendingEnds.push_back(std::move(m).end_tag());
    }

    auto rit = pendingEnds.rbegin();
    auto rend = pendingEnds.rend();
    for (; rit != rend; ++rit)
        r.push_back(*rit);

    return r;
}

void HighlightedFile::prepareOutput()
{
    // ORDER BY begin_offset ASC, end_offset DESC
    std::sort(
        markups.begin(), markups.end(),
        [] (Markup const& lhs, Markup const& rhs) {
            return lhs.begin_offset != rhs.begin_offset
                ? lhs.begin_offset < rhs.begin_offset
                : lhs.end_offset > rhs.end_offset;
        });
}

static bool copyWithLinenosUntil(
    std::istream& in, std::ostream& out, unsigned& lineno, unsigned offset)
{
    if (lineno == 0) {
        ++lineno;
        out << "";
    }
    while (in && in.tellg() < offset) {
        int ch = in.get();
        if (ch == std::istream::traits_type::eof()) {
            return false;
        } else {
            switch (ch) {
                case '\n':
                    ++lineno;
                    out << "\n<a id=\"L" << std::to_string(lineno) << "\">";
                    break;
                case '<':
                    out << "<";
                    break;
                case '>':
                    out << ">";
                    break;
                case '&':
                    out << "&";
                    break;
                default:
                    out.put(static_cast<char>(ch));
            }
        }
    }
    return true;
}

void HighlightedFile::writeTo(std::ostream& out) const
{
    std::ifstream in(originalPath);
    if (!in)
        throw std::runtime_error("Could not reopen source " + originalPath);
    auto hls = flatten(markups);
    unsigned lineno = 0;
    bool eof;
    for (auto const& hl : hls) {
        eof = !copyWithLinenosUntil(in, out, lineno, hl.offset);
        if (eof) {
            std::cerr << "target offset " << hl.offset << " for " << hl.content << "\n";
            throw std::runtime_error("unexpected EOF in " + originalPath);
        }
        out << hl.content;
    }
    eof = !copyWithLinenosUntil(in, out, lineno, UINT_MAX);
    assert(eof);
}
<commit_msg>Make line number tags more useful for CSS.<commit_after>#include "HighlightedFile.hpp"
#include <utility>
#include <fstream>
#include <climits>
#include <iostream>

using namespace synth;

PrimitiveTag Markup::begin_tag() const
{
    std::string content("<");
    content += tag;
    for (auto const& kv : attrs) {
        content += ' ';
        content += kv.first;
        content += "=\"";
        content += kv.second; // TODO? Escape?
        content += '"';
    }
    content += '>';
    return {begin_offset, content};
}

PrimitiveTag Markup::end_tag() const
{
    return {end_offset, std::string("</") + tag + ">"};
}

static std::vector<PrimitiveTag> flatten(
    std::vector<Markup> const& sortedMarkups)
{
    std::vector<PrimitiveTag> r;
    std::vector<PrimitiveTag> pendingEnds;
    for (auto const& m : sortedMarkups) {
        while (!pendingEnds.empty()
            && m.begin_offset >= pendingEnds.back().offset
        ) {
            r.push_back(std::move(pendingEnds.back()));
            pendingEnds.pop_back();
        }
        r.push_back(m.begin_tag());
        pendingEnds.push_back(std::move(m).end_tag());
    }

    auto rit = pendingEnds.rbegin();
    auto rend = pendingEnds.rend();
    for (; rit != rend; ++rit)
        r.push_back(*rit);

    return r;
}

void HighlightedFile::prepareOutput()
{
    // ORDER BY begin_offset ASC, end_offset DESC
    std::sort(
        markups.begin(), markups.end(),
        [] (Markup const& lhs, Markup const& rhs) {
            return lhs.begin_offset != rhs.begin_offset
                ? lhs.begin_offset < rhs.begin_offset
                : lhs.end_offset > rhs.end_offset;
        });
}

static bool copyWithLinenosUntil(
    std::istream& in, std::ostream& out, unsigned& lineno, unsigned offset)
{
    if (lineno == 0) {
        ++lineno;
        out << "";
    }

    while (in && in.tellg() < offset) {
        int ch = in.get();
        if (ch == std::istream::traits_type::eof()) {
            out << "";
            return false;
        } else {
            switch (ch) {
                case '\n':
                    ++lineno;
                    out << "\n<span id=\"L"
                        << std::to_string(lineno)
                        << "\" class=\"Ln\">";
                    break;
                case '<':
                    out << "<";
                    break;
                case '>':
                    out << ">";
                    break;
                case '&':
                    out << "&";
                    break;
                default:
                    out.put(static_cast<char>(ch));
            }
        }
    }
    return true;
}

void HighlightedFile::writeTo(std::ostream& out) const
{
    std::ifstream in(originalPath);
    if (!in)
        throw std::runtime_error("Could not reopen source " + originalPath);
    auto hls = flatten(markups);
    unsigned lineno = 0;
    bool eof;
    for (auto const& hl : hls) {
        eof = !copyWithLinenosUntil(in, out, lineno, hl.offset);
        if (eof) {
            std::cerr << "target offset " << hl.offset << " for " << hl.content << "\n";
            throw std::runtime_error("unexpected EOF in " + originalPath);
        }
        out << hl.content;
    }
    eof = !copyWithLinenosUntil(in, out, lineno, UINT_MAX);
    assert(eof);
}
<|endoftext|>