<commit_before>/*
 * Copyright 2014 Open Connectome Project (http://openconnecto.me)
 * Written by Da Zheng (zhengda1936@gmail.com)
 *
 * This file is part of FlashGraph.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <stdlib.h>
#include <malloc.h>

#include <boost/format.hpp>

#include "log.h"
#include "safs_file.h"
#include "cache.h"
#include "slab_allocator.h"
#include "native_file.h"

#include "in_mem_storage.h"
#include "graph_file_header.h"

using namespace safs;

namespace fg
{

class in_mem_byte_array: public page_byte_array
{
	off_t off;
	size_t size;
	const char *pages;

	void assign(in_mem_byte_array &arr) {
		this->off = arr.off;
		this->size = arr.size;
		this->pages = arr.pages;
	}

	in_mem_byte_array(in_mem_byte_array &arr) {
		assign(arr);
	}

	in_mem_byte_array &operator=(in_mem_byte_array &arr) {
		assign(arr);
		return *this;
	}
public:
	in_mem_byte_array(byte_array_allocator &alloc): page_byte_array(alloc) {
		off = 0;
		size = 0;
		pages = NULL;
	}

	in_mem_byte_array(const io_request &req, char *pages,
			byte_array_allocator &alloc): page_byte_array(alloc) {
		this->off = req.get_offset();
		this->size = req.get_size();
		this->pages = pages;
	}

	virtual off_t get_offset() const {
		return off;
	}

	virtual off_t get_offset_in_first_page() const {
		return off % PAGE_SIZE;
	}

	virtual const char *get_page(int pg_idx) const {
		return pages + pg_idx * PAGE_SIZE;
	}

	virtual size_t get_size() const {
		return size;
	}

	void lock() {
		ABORT_MSG("lock isn't implemented");
	}

	void unlock() {
		ABORT_MSG("unlock isn't implemented");
	}

	page_byte_array *clone() {
		in_mem_byte_array *arr = (in_mem_byte_array *) get_allocator().alloc();
		*arr = *this;
		return arr;
	}
};

class in_mem_byte_array_allocator: public byte_array_allocator
{
	class array_initiator: public obj_initiator<in_mem_byte_array>
	{
		in_mem_byte_array_allocator *alloc;
	public:
		array_initiator(in_mem_byte_array_allocator *alloc) {
			this->alloc = alloc;
		}

		virtual void init(in_mem_byte_array *obj) {
			new (obj) in_mem_byte_array(*alloc);
		}
	};

	class array_destructor: public obj_destructor<in_mem_byte_array>
	{
	public:
		void destroy(in_mem_byte_array *obj) {
			obj->~in_mem_byte_array();
		}
	};

	obj_allocator<in_mem_byte_array> allocator;
public:
	in_mem_byte_array_allocator(thread *t): allocator(
			"byte-array-allocator", t->get_node_id(), false, 1024 * 1024,
			params.get_max_obj_alloc_size(),
			obj_initiator<in_mem_byte_array>::ptr(new array_initiator(this)),
			obj_destructor<in_mem_byte_array>::ptr(new array_destructor())) {
	}

	virtual page_byte_array *alloc() {
		return allocator.alloc_obj();
	}

	virtual void free(page_byte_array *arr) {
		allocator.free((in_mem_byte_array *) arr);
	}
};

class in_mem_io: public io_interface
{
	const in_mem_graph &graph;
	int file_id;
	fifo_queue<io_request> req_buf;
	fifo_queue<user_compute *> compute_buf;
	fifo_queue<user_compute *> incomp_computes;
	std::unique_ptr<byte_array_allocator> array_allocator;

	callback::ptr cb;

	void process_req(const io_request &req);
	void process_computes();
public:
	in_mem_io(const in_mem_graph &_graph, int file_id,
			thread *t): io_interface(t), graph(_graph), req_buf(
				get_node_id(), 1024), compute_buf(get_node_id(), 1024,
				true), incomp_computes(get_node_id(), 1024, true) {
		this->file_id = file_id;
		array_allocator = std::unique_ptr<byte_array_allocator>(
				new in_mem_byte_array_allocator(t));
	}

	virtual int get_file_id() const {
		return file_id;
	}

	virtual bool support_aio() {
		return true;
	}

	virtual bool set_callback(callback::ptr cb) {
		this->cb = cb;
		return true;
	}

	virtual bool have_callback() const {
		return cb != NULL;
	}

	virtual callback &get_callback() {
		return *cb;
	}

	virtual void flush_requests() { }

	virtual int num_pending_ios() const {
		return 0;
	}

	virtual io_status access(char *buf, off_t off, ssize_t size,
			int access_method) {
		assert(access_method == READ);
		memcpy(buf, graph.graph_data + off, size);
		return IO_OK;
	}

	virtual void access(io_request *requests, int num, io_status *status);
	virtual int wait4complete(int) {
		assert(compute_buf.is_empty());
		if (!incomp_computes.is_empty()) {
			compute_buf.add(&incomp_computes);
			assert(incomp_computes.is_empty());
			process_computes();
		}
		return 0;
	}
};

enum
{
	IN_QUEUE,
};

void in_mem_io::process_req(const io_request &req)
{
	assert(req.get_req_type() == io_request::USER_COMPUTE);
	in_mem_byte_array byte_arr(req,
			graph.graph_data + ROUND_PAGE(req.get_offset()), *array_allocator);
	user_compute *compute = req.get_compute();
	compute->run(byte_arr);
	// If the user compute hasn't completed and it's not in the queue,
	// add it to the queue.
	if (!compute->has_completed() && !compute->test_flag(IN_QUEUE)) {
		compute->set_flag(IN_QUEUE, true);
		if (compute_buf.is_full())
			compute_buf.expand_queue(compute_buf.get_size() * 2);
		compute_buf.push_back(compute);
	}
	else
		compute->dec_ref();

	if (compute->has_completed() && !compute->test_flag(IN_QUEUE)) {
		assert(compute->get_ref() == 0);
		// It might still be referenced by the graph engine.
		if (compute->get_ref() == 0) {
			compute_allocator *alloc = compute->get_allocator();
			alloc->free(compute);
		}
	}
}

void in_mem_io::process_computes()
{
	while (!compute_buf.is_empty()) {
		user_compute *compute = compute_buf.pop_front();
		assert(compute->get_ref() > 0);
		while (compute->has_requests()) {
			compute->fetch_requests(this, req_buf, req_buf.get_size());
			while (!req_buf.is_empty()) {
				io_request new_req = req_buf.pop_front();
				process_req(new_req);
			}
		}
		if (compute->has_completed()) {
			compute->dec_ref();
			compute->set_flag(IN_QUEUE, false);
			assert(compute->get_ref() == 0);
			if (compute->get_ref() == 0) {
				compute_allocator *alloc = compute->get_allocator();
				alloc->free(compute);
			}
		}
		else {
			if (incomp_computes.is_full())
				incomp_computes.expand_queue(incomp_computes.get_size() * 2);
			incomp_computes.push_back(compute);
		}
	}
}

void in_mem_io::access(io_request *requests, int num, io_status *)
{
	for (int i = 0; i < num; i++) {
		io_request &req = requests[i];
		if (req.get_req_type() == io_request::USER_COMPUTE) {
			// Let's possess a reference to the user compute first. process_req()
			// will release the reference when the user compute is completed.
			req.get_compute()->inc_ref();
			process_req(req);
		}
		else {
			assert(req.get_req_type() == io_request::BASIC_REQ);
			memcpy(req.get_buf(), graph.graph_data + req.get_offset(), req.get_size());
			io_request *reqs[1];
			reqs[0] = &req;
			if (this->have_callback())
				this->get_callback().invoke(reqs, 1);
		}
	}
	process_computes();
}

class in_mem_io_factory: public file_io_factory
{
	const in_mem_graph &graph;
	int file_id;
public:
	in_mem_io_factory(const in_mem_graph &_graph, int file_id,
			const std::string file_name): file_io_factory(file_name), graph(_graph) {
		this->file_id = file_id;
	}

	virtual int get_file_id() const {
		return file_id;
	}

	virtual io_interface::ptr create_io(thread *t) {
		return io_interface::ptr(new in_mem_io(graph, file_id, t));
	}

	virtual void destroy_io(io_interface *) {
	}
};

in_mem_graph::ptr in_mem_graph::load_graph(const std::string &file_name)
{
	native_file local_f(file_name);
	if (!local_f.exist())
		throw io_exception(file_name + std::string(" doesn't exist"));

	ssize_t size = local_f.get_size();
	assert(size > 0);

	in_mem_graph::ptr graph = in_mem_graph::ptr(new in_mem_graph());
	graph->graph_size = size;
	graph->graph_data = (char *) malloc(size);
	assert(graph->graph_data);
	graph->graph_file_name = file_name;
	BOOST_LOG_TRIVIAL(info) << boost::format("load a graph of %1% bytes")
		% graph->graph_size;

	FILE *fd = fopen(file_name.c_str(), "r");
	if (fd == NULL)
		throw io_exception(std::string("can't open ") + file_name);
	if (fread(graph->graph_data, size, 1, fd) != 1)
		throw io_exception(std::string("can't read from ") + file_name);
	fclose(fd);

	graph_header *header = (graph_header *) graph->graph_data;
	header->verify();

	return graph;
}

in_mem_graph::ptr in_mem_graph::load_safs_graph(const std::string &file_name)
{
	file_io_factory::shared_ptr io_factory = ::create_io_factory(file_name,
			REMOTE_ACCESS);

	in_mem_graph::ptr graph = in_mem_graph::ptr(new in_mem_graph());
	graph->graph_size = io_factory->get_file_size();
	size_t num_pages = ROUNDUP_PAGE(graph->graph_size) / PAGE_SIZE;
	int ret = posix_memalign((void **) &graph->graph_data, PAGE_SIZE,
			num_pages * PAGE_SIZE);
	BOOST_VERIFY(ret == 0);
	graph->graph_file_name = file_name;

	BOOST_LOG_TRIVIAL(info) << boost::format("load a graph of %1% bytes")
		% graph->graph_size;
#if 0
	graph->graph_file_id = io_factory->get_file_id();
#endif
	io_interface::ptr io = io_factory->create_io(thread::get_curr_thread());
	const size_t MAX_IO_SIZE = 256 * 1024 * 1024;
	for (off_t off = 0; (size_t) off < graph->graph_size; off += MAX_IO_SIZE) {
		data_loc_t loc(io_factory->get_file_id(), off);
		size_t req_size = min(MAX_IO_SIZE, graph->graph_size - off);
		io_request req(graph->graph_data + off, loc, req_size, READ);
		io->access(&req, 1);
		io->wait4complete(1);
	}

	graph_header *header = (graph_header *) graph->graph_data;
	header->verify();

	return graph;
}

file_io_factory::shared_ptr in_mem_graph::create_io_factory() const
{
	return file_io_factory::shared_ptr(new in_mem_io_factory(*this,
				graph_file_id, graph_file_name));
}

}
<commit_msg>[Graph]: use comp_io_scheduler to process user compute in in_mem_io.<commit_after>/*
 * Copyright 2014 Open Connectome Project (http://openconnecto.me)
 * Written by Da Zheng (zhengda1936@gmail.com)
 *
 * This file is part of FlashGraph.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <stdlib.h>
#include <malloc.h>

#include <boost/format.hpp>

#include "log.h"
#include "safs_file.h"
#include "cache.h"
#include "slab_allocator.h"
#include "native_file.h"
#include "comp_io_scheduler.h"

#include "in_mem_storage.h"
#include "graph_file_header.h"

using namespace safs;

namespace fg
{

class in_mem_byte_array: public page_byte_array
{
	off_t off;
	size_t size;
	const char *pages;

	void assign(in_mem_byte_array &arr) {
		this->off = arr.off;
		this->size = arr.size;
		this->pages = arr.pages;
	}

	in_mem_byte_array(in_mem_byte_array &arr) {
		assign(arr);
	}

	in_mem_byte_array &operator=(in_mem_byte_array &arr) {
		assign(arr);
		return *this;
	}
public:
	in_mem_byte_array(byte_array_allocator &alloc): page_byte_array(alloc) {
		off = 0;
		size = 0;
		pages = NULL;
	}

	in_mem_byte_array(const io_request &req, char *pages,
			byte_array_allocator &alloc): page_byte_array(alloc) {
		this->off = req.get_offset();
		this->size = req.get_size();
		this->pages = pages;
	}

	virtual off_t get_offset() const {
		return off;
	}

	virtual off_t get_offset_in_first_page() const {
		return off % PAGE_SIZE;
	}

	virtual const char *get_page(int pg_idx) const {
		return pages + pg_idx * PAGE_SIZE;
	}

	virtual size_t get_size() const {
		return size;
	}

	void lock() {
		ABORT_MSG("lock isn't implemented");
	}

	void unlock() {
		ABORT_MSG("unlock isn't implemented");
	}

	page_byte_array *clone() {
		in_mem_byte_array *arr = (in_mem_byte_array *) get_allocator().alloc();
		*arr = *this;
		return arr;
	}
};

class in_mem_byte_array_allocator: public byte_array_allocator
{
	class array_initiator: public obj_initiator<in_mem_byte_array>
	{
		in_mem_byte_array_allocator *alloc;
	public:
		array_initiator(in_mem_byte_array_allocator *alloc) {
			this->alloc = alloc;
		}

		virtual void init(in_mem_byte_array *obj) {
			new (obj) in_mem_byte_array(*alloc);
		}
	};

	class array_destructor: public obj_destructor<in_mem_byte_array>
	{
	public:
		void destroy(in_mem_byte_array *obj) {
			obj->~in_mem_byte_array();
		}
	};

	obj_allocator<in_mem_byte_array> allocator;
public:
	in_mem_byte_array_allocator(thread *t): allocator(
			"byte-array-allocator", t->get_node_id(), false, 1024 * 1024,
			params.get_max_obj_alloc_size(),
			obj_initiator<in_mem_byte_array>::ptr(new array_initiator(this)),
			obj_destructor<in_mem_byte_array>::ptr(new array_destructor())) {
	}

	virtual page_byte_array *alloc() {
		return allocator.alloc_obj();
	}

	virtual void free(page_byte_array *arr) {
		allocator.free((in_mem_byte_array *) arr);
	}
};

class in_mem_io: public io_interface
{
	const in_mem_graph &graph;
	int file_id;
	fifo_queue<io_request> req_buf;
	comp_io_scheduler::ptr comp_io_sched;
	std::unique_ptr<byte_array_allocator> array_allocator;

	callback::ptr cb;

	void process_req(const io_request &req);
	void process_computes();
public:
	in_mem_io(const in_mem_graph &_graph, int file_id,
			thread *t): io_interface(t), graph(_graph), req_buf(
				get_node_id(), 1024) {
		this->file_id = file_id;
		array_allocator = std::unique_ptr<byte_array_allocator>(
				new in_mem_byte_array_allocator(t));
		comp_io_sched = comp_io_scheduler::ptr(
				new default_comp_io_scheduler(get_node_id()));
		comp_io_sched->set_io(this);
	}

	virtual int get_file_id() const {
		return file_id;
	}

	virtual bool support_aio() {
		return true;
	}

	virtual bool set_callback(callback::ptr cb) {
		this->cb = cb;
		return true;
	}

	virtual bool have_callback() const {
		return cb != NULL;
	}

	virtual callback &get_callback() {
		return *cb;
	}

	virtual void flush_requests() { }

	virtual int num_pending_ios() const {
		return 0;
	}

	virtual io_status access(char *buf, off_t off, ssize_t size,
			int access_method) {
		assert(access_method == READ);
		memcpy(buf, graph.graph_data + off, size);
		return IO_OK;
	}

	virtual void access(io_request *requests, int num, io_status *status);
	virtual int wait4complete(int) {
		return 0;
	}
};

enum
{
	IN_QUEUE,
};

void in_mem_io::process_req(const io_request &req)
{
	assert(req.get_req_type() == io_request::USER_COMPUTE);
	in_mem_byte_array byte_arr(req,
			graph.graph_data + ROUND_PAGE(req.get_offset()), *array_allocator);
	user_compute *compute = req.get_compute();
	compute->run(byte_arr);
	comp_io_sched->post_comp_process(compute);
}

void in_mem_io::process_computes()
{
	while (true) {
		comp_io_sched->get_requests(req_buf, req_buf.get_size());
		if (req_buf.is_empty())
			break;

		while (!req_buf.is_empty()) {
			io_request new_req = req_buf.pop_front();
			process_req(new_req);
		}
	}
}

void in_mem_io::access(io_request *requests, int num, io_status *)
{
	for (int i = 0; i < num; i++) {
		io_request &req = requests[i];
		if (req.get_req_type() == io_request::USER_COMPUTE) {
			// Let's possess a reference to the user compute first. process_req()
			// will release the reference when the user compute is completed.
			req.get_compute()->inc_ref();
			process_req(req);
		}
		else {
			assert(req.get_req_type() == io_request::BASIC_REQ);
			memcpy(req.get_buf(), graph.graph_data + req.get_offset(), req.get_size());
			io_request *reqs[1];
			reqs[0] = &req;
			if (this->have_callback())
				this->get_callback().invoke(reqs, 1);
		}
	}
	process_computes();
	comp_io_sched->gc_computes();
}

class in_mem_io_factory: public file_io_factory
{
	const in_mem_graph &graph;
	int file_id;
public:
	in_mem_io_factory(const in_mem_graph &_graph, int file_id,
			const std::string file_name): file_io_factory(file_name), graph(_graph) {
		this->file_id = file_id;
	}

	virtual int get_file_id() const {
		return file_id;
	}

	virtual io_interface::ptr create_io(thread *t) {
		return io_interface::ptr(new in_mem_io(graph, file_id, t));
	}

	virtual void destroy_io(io_interface *) {
	}
};

in_mem_graph::ptr in_mem_graph::load_graph(const std::string &file_name)
{
	native_file local_f(file_name);
	if (!local_f.exist())
		throw io_exception(file_name + std::string(" doesn't exist"));

	ssize_t size = local_f.get_size();
	assert(size > 0);

	in_mem_graph::ptr graph = in_mem_graph::ptr(new in_mem_graph());
	graph->graph_size = size;
	graph->graph_data = (char *) malloc(size);
	assert(graph->graph_data);
	graph->graph_file_name = file_name;
	BOOST_LOG_TRIVIAL(info) << boost::format("load a graph of %1% bytes")
		% graph->graph_size;

	FILE *fd = fopen(file_name.c_str(), "r");
	if (fd == NULL)
		throw io_exception(std::string("can't open ") + file_name);
	if (fread(graph->graph_data, size, 1, fd) != 1)
		throw io_exception(std::string("can't read from ") + file_name);
	fclose(fd);

	graph_header *header = (graph_header *) graph->graph_data;
	header->verify();

	return graph;
}

in_mem_graph::ptr in_mem_graph::load_safs_graph(const std::string &file_name)
{
	file_io_factory::shared_ptr io_factory = ::create_io_factory(file_name,
			REMOTE_ACCESS);

	in_mem_graph::ptr graph = in_mem_graph::ptr(new in_mem_graph());
	graph->graph_size = io_factory->get_file_size();
	size_t num_pages = ROUNDUP_PAGE(graph->graph_size) / PAGE_SIZE;
	int ret = posix_memalign((void **) &graph->graph_data, PAGE_SIZE,
			num_pages * PAGE_SIZE);
	BOOST_VERIFY(ret == 0);
	graph->graph_file_name = file_name;

	BOOST_LOG_TRIVIAL(info) << boost::format("load a graph of %1% bytes")
		% graph->graph_size;
#if 0
	graph->graph_file_id = io_factory->get_file_id();
#endif
	io_interface::ptr io = io_factory->create_io(thread::get_curr_thread());
	const size_t MAX_IO_SIZE = 256 * 1024 * 1024;
	for (off_t off = 0; (size_t) off < graph->graph_size; off += MAX_IO_SIZE) {
		data_loc_t loc(io_factory->get_file_id(), off);
		size_t req_size = min(MAX_IO_SIZE, graph->graph_size - off);
		io_request req(graph->graph_data + off, loc, req_size, READ);
		io->access(&req, 1);
		io->wait4complete(1);
	}

	graph_header *header = (graph_header *) graph->graph_data;
	header->verify();

	return graph;
}

file_io_factory::shared_ptr in_mem_graph::create_io_factory() const
{
	return file_io_factory::shared_ptr(new in_mem_io_factory(*this,
				graph_file_id, graph_file_name));
}

}
<|endoftext|>


<commit_before>#include <QtCore/QDir>
#include <QtGui/QPainter>
#include "window.hpp"

#define CONVERT_X(x) (x1 + ((x * scale + 1) / 2) * (x2 - x1))
#define CONVERT_Y(y) (y1 + ((y * scale + 1) / 2) * (y2 - y1))
#define CONVERT(point) CONVERT_X(point.x()), CONVERT_Y(point.y())
#define CONVERTBACK_X(x) ((qreal(x - x1) * 2 / (x2 - x1) - 1) / scale)
#define CONVERTBACK_Y(y) ((qreal(y - y1) * 2 / (y2 - y1) - 1) / scale)
#define DEFINE_RECT QRect rectangle = rect(); \
	int x1 = rectangle.topLeft().x(); \
	int x2 = rectangle.topRight().x(); \
	int y1 = rectangle.topLeft().y(); \
	int y2 = rectangle.bottomLeft().y()
#define POINTS 5
#define EPS 0.05

MyMainWindow::MyMainWindow(PolynomSystem *system):
	drawArea(system, this),
	toolBar("Toolbar", this),
	drawMeshAction(tr("Draw Mesh"), this),
	fileActionGroup(this),
	paramSlider(Qt::Horizontal, this)
{
	resize(1000, 800);
	setCentralWidget(&drawArea);
	addToolBar(&toolBar);
	setWindowTitle(tr("Differential equations"));

	QStringList nameFilters, files;
	nameFilters << "sys*.txt";
	files = QDir().entryList(nameFilters,
		QDir::Files | QDir::Readable);
	for (int i = 0; i < files.size(); ++i) {
		QAction *action = fileActionGroup.addAction(files.at(i));
		action->setCheckable(true);
		toolBar.addAction(action);
		if (!i) {
			action->setChecked(true);
			drawArea.loadFile(action);
		}
	}
	connect(&fileActionGroup, SIGNAL(triggered(QAction*)),
		&drawArea, SLOT(loadFile(QAction*)));
	paramSlider.setTickInterval(50);
	paramSlider.setTickPosition(QSlider::TicksBothSides);
	paramSlider.setRange(0, 300);
	paramSlider.setValue(150);
	connect(&paramSlider, SIGNAL(valueChanged(int)),
		&drawArea, SLOT(updateParam(int)));

	drawMeshAction.setCheckable(true);
	toolBar.addSeparator();
	toolBar.addAction(&drawMeshAction);
	connect(&drawMeshAction, SIGNAL(triggered(bool)),
		&drawArea, SLOT(drawMesh(bool)));
	toolBar.addSeparator();
	toolBar.addWidget(&paramSlider);
}

DrawArea::DrawArea(PolynomSystem *system, QWidget *parent):
	QWidget(parent),
	scale(.2),
	param(1),
	doDrawMesh(false),
	startPoint(2, 0),
	system(system)
{}

void DrawArea::loadFile(QAction *action) {
	fileName = action->text();
	updateSystem();
}

void DrawArea::updateParam(int sliderParam) {
	param = sliderParam * .02 - 2;
	updateSystem();
}

void DrawArea::updateSystem() {
	fillSystem(fileName, *system, param);
	PointType pointType = system->getPointType();
	pointTypeName = getPointTypeName(pointType);
	repaint();
}

QString DrawArea::getPointTypeName(PointType type) {
	switch(type) {
		case PointCenter:        return tr("Center");
		case PointFocusStable:   return tr("Stable focus");
		case PointFocusUnstable: return tr("Unstable focus");
		case PointKnotStable:    return tr("Stable knot");
		case PointKnotUnstable:  return tr("Unstable knot");
		case PointKnotSingularStable:   return tr("Stable singular knot");
		case PointKnotSingularUnstable: return tr("Unstable singular knot");
		case PointSaddle:        return tr("Saddle");
	}
	return QString();
}

void DrawArea::drawMesh(bool yes) {
	doDrawMesh = yes;
	repaint();
}

void DrawArea::paintEvent(QPaintEvent *event) {
	DEFINE_RECT;
	QWidget::paintEvent(event);
	drawAxes(5);
	qreal staticPoint = system->findPoincareStaticPoint(.1, 5, EPS);
	if (qAbs(staticPoint) > EPS) {
		drawPath(QPointF(staticPoint, 0), EPS);
	}
	if (doDrawMesh) {
		for (int i = 0; i <= POINTS; ++i) {
			drawPath(QPointF(CONVERTBACK_X((x1 * i + x2 * (POINTS - i)) / POINTS),
				CONVERTBACK_Y(y1)), EPS);
			drawPath(QPointF(CONVERTBACK_X((x1 * i + x2 * (POINTS - i)) / POINTS),
				CONVERTBACK_Y(y1)), -EPS);
			drawPath(QPointF(CONVERTBACK_X((x1 * i + x2 * (POINTS - i)) / POINTS),
				CONVERTBACK_Y(y2)), EPS);
			drawPath(QPointF(CONVERTBACK_X((x1 * i + x2 * (POINTS - i)) / POINTS),
				CONVERTBACK_Y(y2)), -EPS);
		}
	} else {
		drawPath(startPoint, -EPS, Qt::lightGray);
		drawPath(startPoint, EPS);
	}
	QString statusText = tr("Parameter: %1;  Point type: %2;  Scale: %3").arg(
		QString::number(param, 'f', 2),
		pointTypeName, QString::number(scale * 5, 'g', 3));
	QPainter(this).drawText(10, height() - 10, statusText);
}

void DrawArea::wheelEvent(QWheelEvent *event) {
	int delta = event->delta();
	qreal newScale = scale * qExp(qreal(delta) / 2048);
	if (newScale > 0.05 && newScale < 30) {
		scale = newScale;
		repaint();
	}
}

void DrawArea::mouseMoveEvent(QMouseEvent *event) {
	DEFINE_RECT;
	qreal x = CONVERTBACK_X(event->x());
	qreal y = CONVERTBACK_Y(event->y());
	startPoint = QPointF(x, y);
	repaint();
}

void DrawArea::mouseReleaseEvent(QMouseEvent *event) {
	mouseMoveEvent(event);
}

void DrawArea::drawAxes(qreal ticksize) {
	DEFINE_RECT;
	QPainter painter(this);
	painter.setPen(Qt::lightGray);
	painter.drawLine((x1+x2)/2, y1, (x1+x2)/2, y2);
	painter.drawLine(x1, (y1+y2)/2, x2, (y1+y2)/2);
	qreal newx = 0, startx = CONVERT_X(0), starty = CONVERT_Y(0);
	for (int x = 0; startx+newx < x2; ++x) {
		newx = x * scale * (x2 - x1) / 2;
		painter.drawLine(startx+newx, starty-ticksize, startx+newx, starty+ticksize);
		painter.drawLine(startx-newx, starty-ticksize, startx-newx, starty+ticksize);
	}
	qreal newy = 0;
	for (int y = 0; starty+newy < y2; ++y) {
		newy = y * scale * (y2 - y1) / 2;
		painter.drawLine(startx-ticksize, starty+newy, startx+ticksize, starty+newy);
		painter.drawLine(startx-ticksize, starty-newy, startx+ticksize, starty-newy);
	}
}

void DrawArea::drawPath(QPointF start, qreal eps, QColor color) {
	DEFINE_RECT;
	QPainter painter(this);
	painter.setPen(color);
	painter.setRenderHint(QPainter::Antialiasing);
	QPointF point;
	qreal staticPoint = system->findPoincareStaticPoint(.1, 5, qAbs(eps));
	qreal xdiff = !start.isNull();
	quint32 step = 0;
	while (qAbs(xdiff) > 1e-3 && step < 1000) {
		point = system->getNextValue(start, eps);
		painter.drawLine(CONVERT(start), CONVERT(point));
		if (qAbs(point.y()) < qAbs(eps) && point.x() > 0 && staticPoint > 0) {
			xdiff = point.x() - staticPoint;
		}
		start = point;
		++step;
	}
}
<commit_msg>Fix swapped top and bottom, thanks Roman Pryamonosov<commit_after>#include <QtCore/QDir>
#include <QtGui/QPainter>
#include "window.hpp"

#define CONVERT_X(x) (x1 + ((x * scale + 1) / 2) * (x2 - x1))
#define CONVERT_Y(y) (y1 + ((y * scale + 1) / 2) * (y2 - y1))
#define CONVERT(point) CONVERT_X(point.x()), CONVERT_Y(point.y())
#define CONVERTBACK_X(x) ((qreal(x - x1) * 2 / (x2 - x1) - 1) / scale)
#define CONVERTBACK_Y(y) ((qreal(y - y1) * 2 / (y2 - y1) - 1) / scale)
#define DEFINE_RECT QRect rectangle = rect(); \
	int x1 = rectangle.topLeft().x(); \
	int x2 = rectangle.topRight().x(); \
	int y1 = rectangle.bottomLeft().y(); \
	int y2 = rectangle.topLeft().y()
#define POINTS 5
#define EPS 0.05

MyMainWindow::MyMainWindow(PolynomSystem *system):
	drawArea(system, this),
	toolBar("Toolbar", this),
	drawMeshAction(tr("Draw Mesh"), this),
	fileActionGroup(this),
	paramSlider(Qt::Horizontal, this)
{
	resize(1000, 800);
	setCentralWidget(&drawArea);
	addToolBar(&toolBar);
	setWindowTitle(tr("Differential equations"));

	QStringList nameFilters, files;
	nameFilters << "sys*.txt";
	files = QDir().entryList(nameFilters,
		QDir::Files | QDir::Readable);
	for (int i = 0; i < files.size(); ++i) {
		QAction *action = fileActionGroup.addAction(files.at(i));
		action->setCheckable(true);
		toolBar.addAction(action);
		if (!i) {
			action->setChecked(true);
			drawArea.loadFile(action);
		}
	}
	connect(&fileActionGroup, SIGNAL(triggered(QAction*)),
		&drawArea, SLOT(loadFile(QAction*)));
	paramSlider.setTickInterval(50);
	paramSlider.setTickPosition(QSlider::TicksBothSides);
	paramSlider.setRange(0, 300);
	paramSlider.setValue(150);
	connect(&paramSlider, SIGNAL(valueChanged(int)),
		&drawArea, SLOT(updateParam(int)));

	drawMeshAction.setCheckable(true);
	toolBar.addSeparator();
	toolBar.addAction(&drawMeshAction);
	connect(&drawMeshAction, SIGNAL(triggered(bool)),
		&drawArea, SLOT(drawMesh(bool)));
	toolBar.addSeparator();
	toolBar.addWidget(&paramSlider);
}

DrawArea::DrawArea(PolynomSystem *system, QWidget *parent):
	QWidget(parent),
	scale(.2),
	param(1),
	doDrawMesh(false),
	startPoint(2, 0),
	system(system)
{}

void DrawArea::loadFile(QAction *action) {
	fileName = action->text();
	updateSystem();
}

void DrawArea::updateParam(int sliderParam) {
	param = sliderParam * .02 - 2;
	updateSystem();
}

void DrawArea::updateSystem() {
	fillSystem(fileName, *system, param);
	PointType pointType = system->getPointType();
	pointTypeName = getPointTypeName(pointType);
	repaint();
}

QString DrawArea::getPointTypeName(PointType type) {
	switch(type) {
		case PointCenter:        return tr("Center");
		case PointFocusStable:   return tr("Stable focus");
		case PointFocusUnstable: return tr("Unstable focus");
		case PointKnotStable:    return tr("Stable knot");
		case PointKnotUnstable:  return tr("Unstable knot");
		case PointKnotSingularStable:   return tr("Stable singular knot");
		case PointKnotSingularUnstable: return tr("Unstable singular knot");
		case PointSaddle:        return tr("Saddle");
	}
	return QString();
}

void DrawArea::drawMesh(bool yes) {
	doDrawMesh = yes;
	repaint();
}

void DrawArea::paintEvent(QPaintEvent *event) {
	DEFINE_RECT;
	QWidget::paintEvent(event);
	drawAxes(5);
	qreal staticPoint = system->findPoincareStaticPoint(.1, 5, EPS);
	if (qAbs(staticPoint) > EPS) {
		drawPath(QPointF(staticPoint, 0), EPS);
	}
	if (doDrawMesh) {
		for (int i = 0; i <= POINTS; ++i) {
			drawPath(QPointF(CONVERTBACK_X((x1 * i + x2 * (POINTS - i)) / POINTS),
				CONVERTBACK_Y(y1)), EPS);
			drawPath(QPointF(CONVERTBACK_X((x1 * i + x2 * (POINTS - i)) / POINTS),
				CONVERTBACK_Y(y1)), -EPS);
			drawPath(QPointF(CONVERTBACK_X((x1 * i + x2 * (POINTS - i)) / POINTS),
				CONVERTBACK_Y(y2)), EPS);
			drawPath(QPointF(CONVERTBACK_X((x1 * i + x2 * (POINTS - i)) / POINTS),
				CONVERTBACK_Y(y2)), -EPS);
		}
	} else {
		drawPath(startPoint, -EPS, Qt::lightGray);
		drawPath(startPoint, EPS);
	}
	QString statusText = tr("Parameter: %1;  Point type: %2;  Scale: %3").arg(
		QString::number(param, 'f', 2),
		pointTypeName, QString::number(scale * 5, 'g', 3));
	QPainter(this).drawText(10, height() - 10, statusText);
}

void DrawArea::wheelEvent(QWheelEvent *event) {
	int delta = event->delta();
	qreal newScale = scale * qExp(qreal(delta) / 2048);
	if (newScale > 0.05 && newScale < 30) {
		scale = newScale;
		repaint();
	}
}

void DrawArea::mouseMoveEvent(QMouseEvent *event) {
	DEFINE_RECT;
	qreal x = CONVERTBACK_X(event->x());
	qreal y = CONVERTBACK_Y(event->y());
	startPoint = QPointF(x, y);
	repaint();
}

void DrawArea::mouseReleaseEvent(QMouseEvent *event) {
	mouseMoveEvent(event);
}

void DrawArea::drawAxes(qreal ticksize) {
	DEFINE_RECT;
	QPainter painter(this);
	painter.setPen(Qt::lightGray);
	painter.drawLine((x1+x2)/2, y1, (x1+x2)/2, y2);
	painter.drawLine(x1, (y1+y2)/2, x2, (y1+y2)/2);
	qreal newx = 0, startx = CONVERT_X(0), starty = CONVERT_Y(0);
	for (int x = 0; startx+newx < x2; ++x) {
		newx = x * scale * (x2 - x1) / 2;
		painter.drawLine(startx+newx, starty-ticksize, startx+newx, starty+ticksize);
		painter.drawLine(startx-newx, starty-ticksize, startx-newx, starty+ticksize);
	}
	qreal newy = 0;
	for (int y = 0; starty+newy < y2; ++y) {
		newy = y * scale * (y2 - y1) / 2;
		painter.drawLine(startx-ticksize, starty+newy, startx+ticksize, starty+newy);
		painter.drawLine(startx-ticksize, starty-newy, startx+ticksize, starty-newy);
	}
}

void DrawArea::drawPath(QPointF start, qreal eps, QColor color) {
	DEFINE_RECT;
	QPainter painter(this);
	painter.setPen(color);
	painter.setRenderHint(QPainter::Antialiasing);
	QPointF point;
	qreal staticPoint = system->findPoincareStaticPoint(.1, 5, qAbs(eps));
	qreal xdiff = !start.isNull();
	quint32 step = 0;
	while (qAbs(xdiff) > 1e-3 && step < 1000) {
		point = system->getNextValue(start, eps);
		painter.drawLine(CONVERT(start), CONVERT(point));
		if (qAbs(point.y()) < qAbs(eps) && point.x() > 0 && staticPoint > 0) {
			xdiff = point.x() - staticPoint;
		}
		start = point;
		++step;
	}
}
<|endoftext|>


<commit_before>#include "customtextedit.h"
#include <QMimeData>
#include <QApplication>
#include <QMainWindow>
#include <QDebug>
#include <QRegularExpression>
#include <QTextBlock>

CustomTextEdit::CustomTextEdit()
{
    connect(this, SIGNAL(cursorPositionChanged()), this, SLOT(highlightCurrentLine()));
    giFlag = 1;
}

void CustomTextEdit::dragEnterEvent(QDragEnterEvent *event)
{
    event->acceptProposedAction();
}

void CustomTextEdit::dropEvent(QDropEvent *event)
{
    emit this->customTextEdit_signal_processDropEvent(event);
}

void CustomTextEdit::dragMoveEvent(QDragMoveEvent *event)
{
    event->acceptProposedAction();
}

void CustomTextEdit::highlightCurrentLine()
{
    if(giFlag == 1){
        QList<QTextEdit::ExtraSelection> extraSelections = this->extraSelections();

        if(extraSelections.count() > 0){
            extraSelections.removeLast();
        }

        QTextEdit::ExtraSelection selection;

        QColor lineColor = QColor(this->palette().background().color().darker(110));

        selection.format.setBackground(lineColor);
        selection.format.setProperty(QTextFormat::FullWidthSelection, true);
        selection.cursor = textCursor();
        selection.cursor.clearSelection();
        extraSelections.append(selection);
        setExtraSelections(extraSelections);
    }
}

void CustomTextEdit::cte_slot_disableHighLight()
{
    giFlag = 0;
}

void CustomTextEdit::cte_slot_enableHighLight()
{
    giFlag = 1;
}

void CustomTextEdit::keyPressEvent(QKeyEvent *e){

    QTextCursor cursor = this->textCursor();

    if(e->key() == Qt::Key_Tab && cursor.hasSelection()){

        int start = cursor.selectionStart();
        int end = cursor.selectionEnd();

        cursor.setPosition(end, QTextCursor::KeepAnchor);
        QTextBlock endBlock = cursor.block();

        cursor.setPosition(start, QTextCursor::KeepAnchor);
        QTextBlock block = cursor.block();

        for(; block.isValid() && !(endBlock < block); block = block.next())
        {
            if (!block.isValid())
                continue;

            cursor.movePosition(QTextCursor::StartOfLine);
            cursor.clearSelection();
            cursor.insertText("\t");
            cursor.movePosition(QTextCursor::NextBlock);
        }

    } else if(e->key() == Qt::Key_Backtab){

        int start = cursor.selectionStart();
        int end = cursor.selectionEnd();

        cursor.setPosition(end, QTextCursor::KeepAnchor);
        QTextBlock endBlock = cursor.block();

        cursor.setPosition(start, QTextCursor::KeepAnchor);
        QTextBlock block = cursor.block();

        for(; block.isValid() && !(endBlock < block); block = block.next())
        {
            if (!block.isValid())
                continue;

            if(cursor.positionInBlock() >= 1) {
                cursor.movePosition(QTextCursor::StartOfLine);
                cursor.clearSelection();
                cursor.deleteChar();
                cursor.movePosition(QTextCursor::NextBlock);
            }
        }

    } else {
        QPlainTextEdit::keyPressEvent(e);
    }
}
<commit_msg>Improved backtab action<commit_after>#include "customtextedit.h"
#include <QMimeData>
#include <QApplication>
#include <QMainWindow>
#include <QDebug>
#include <QRegularExpression>
#include <QTextBlock>

CustomTextEdit::CustomTextEdit()
{
    connect(this, SIGNAL(cursorPositionChanged()), this, SLOT(highlightCurrentLine()));
    giFlag = 1;
}

void CustomTextEdit::dragEnterEvent(QDragEnterEvent *event)
{
    event->acceptProposedAction();
}

void CustomTextEdit::dropEvent(QDropEvent *event)
{
    emit this->customTextEdit_signal_processDropEvent(event);
}

void CustomTextEdit::dragMoveEvent(QDragMoveEvent *event)
{
    event->acceptProposedAction();
}

void CustomTextEdit::highlightCurrentLine()
{
    if(giFlag == 1){
        QList<QTextEdit::ExtraSelection> extraSelections = this->extraSelections();

        if(extraSelections.count() > 0){
            extraSelections.removeLast();
        }

        QTextEdit::ExtraSelection selection;

        QColor lineColor = QColor(this->palette().background().color().darker(110));

        selection.format.setBackground(lineColor);
        selection.format.setProperty(QTextFormat::FullWidthSelection, true);
        selection.cursor = textCursor();
        selection.cursor.clearSelection();
        extraSelections.append(selection);
        setExtraSelections(extraSelections);
    }
}

void CustomTextEdit::cte_slot_disableHighLight()
{
    giFlag = 0;
}

void CustomTextEdit::cte_slot_enableHighLight()
{
    giFlag = 1;
}

void CustomTextEdit::keyPressEvent(QKeyEvent *e){

    QTextCursor cursor = this->textCursor();

    if(e->key() == Qt::Key_Tab && cursor.hasSelection()){

        int start = cursor.selectionStart();
        int end = cursor.selectionEnd();

        cursor.setPosition(end, QTextCursor::KeepAnchor);
        QTextBlock endBlock = cursor.block();

        cursor.setPosition(start, QTextCursor::KeepAnchor);
        QTextBlock block = cursor.block();

        for(; block.isValid() && !(endBlock < block); block = block.next())
        {
            if (!block.isValid())
                continue;

            cursor.movePosition(QTextCursor::StartOfLine);
            cursor.clearSelection();
            cursor.insertText("\t");
            cursor.movePosition(QTextCursor::NextBlock);
        }

    } else if(e->key() == Qt::Key_Backtab){

        int start = cursor.selectionStart();
        int end = cursor.selectionEnd();

        cursor.setPosition(end, QTextCursor::KeepAnchor);
        QTextBlock endBlock = cursor.block();

        cursor.setPosition(start, QTextCursor::KeepAnchor);
        QTextBlock block = cursor.block();

        for(; block.isValid() && !(endBlock < block); block = block.next())
        {
            if (!block.isValid())
                continue;

            cursor.setPosition(block.position() + 1, QTextCursor::KeepAnchor);
            cursor.clearSelection();
            if(block.text().startsWith("\t")) cursor.deletePreviousChar();
        }

    } else {
        QPlainTextEdit::keyPressEvent(e);
    }
}
<|endoftext|>


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

#include "flutter/flow/layers/clip_path_layer.h"

#if defined(OS_FUCHSIA)

#include "lib/ui/scenic/fidl_helpers.h"  // nogncheck

#endif  // defined(OS_FUCHSIA)

namespace flow {

ClipPathLayer::ClipPathLayer(Clip clip_behavior)
    : clip_behavior_(clip_behavior) {
  FML_DCHECK(clip_behavior != Clip::none);
}

ClipPathLayer::~ClipPathLayer() = default;

void ClipPathLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) {
  SkRect previous_cull_rect = context->cull_rect;
  SkRect clip_path_bounds = clip_path_.getBounds();
  if (context->cull_rect.intersect(clip_path_bounds)) {
    SkRect child_paint_bounds = SkRect::MakeEmpty();
    PrerollChildren(context, matrix, &child_paint_bounds);

    if (child_paint_bounds.intersect(clip_path_bounds)) {
      set_paint_bounds(child_paint_bounds);
    }
  }
  context->cull_rect = previous_cull_rect;
}

#if defined(OS_FUCHSIA)

void ClipPathLayer::UpdateScene(SceneUpdateContext& context) {
  FML_DCHECK(needs_system_composite());

  // TODO(MZ-140): Must be able to specify paths as shapes to nodes.
  //               Treating the shape as a rectangle for now.
  auto bounds = clip_path_.getBounds();
  scenic::Rectangle shape(context.session(),  // session
                          bounds.width(),     //  width
                          bounds.height()     //  height
  );

  // TODO(liyuqian): respect clip_behavior_
  SceneUpdateContext::Clip clip(context, shape, bounds);
  UpdateSceneChildren(context);
}

#endif  // defined(OS_FUCHSIA)

void ClipPathLayer::Paint(PaintContext& context) const {
  TRACE_EVENT0("flutter", "ClipPathLayer::Paint");
  FML_DCHECK(needs_painting());

  SkAutoCanvasRestore save(context.internal_nodes_canvas, true);
  context.internal_nodes_canvas->clipPath(clip_path_,
                                          clip_behavior_ != Clip::hardEdge);
  if (clip_behavior_ == Clip::antiAliasWithSaveLayer) {
    context.internal_nodes_canvas->saveLayer(paint_bounds(), nullptr);
  }
  PaintChildren(context);
  if (clip_behavior_ == Clip::antiAliasWithSaveLayer) {
    context.internal_nodes_canvas->restore();
  }
}

}  // namespace flow
<commit_msg>[fuchsia] Update scenic include (#7708)<commit_after>// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "flutter/flow/layers/clip_path_layer.h"

#if defined(OS_FUCHSIA)

#include "lib/ui/scenic/cpp/commands.h"

#endif  // defined(OS_FUCHSIA)

namespace flow {

ClipPathLayer::ClipPathLayer(Clip clip_behavior)
    : clip_behavior_(clip_behavior) {
  FML_DCHECK(clip_behavior != Clip::none);
}

ClipPathLayer::~ClipPathLayer() = default;

void ClipPathLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) {
  SkRect previous_cull_rect = context->cull_rect;
  SkRect clip_path_bounds = clip_path_.getBounds();
  if (context->cull_rect.intersect(clip_path_bounds)) {
    SkRect child_paint_bounds = SkRect::MakeEmpty();
    PrerollChildren(context, matrix, &child_paint_bounds);

    if (child_paint_bounds.intersect(clip_path_bounds)) {
      set_paint_bounds(child_paint_bounds);
    }
  }
  context->cull_rect = previous_cull_rect;
}

#if defined(OS_FUCHSIA)

void ClipPathLayer::UpdateScene(SceneUpdateContext& context) {
  FML_DCHECK(needs_system_composite());

  // TODO(MZ-140): Must be able to specify paths as shapes to nodes.
  //               Treating the shape as a rectangle for now.
  auto bounds = clip_path_.getBounds();
  scenic::Rectangle shape(context.session(),  // session
                          bounds.width(),     //  width
                          bounds.height()     //  height
  );

  // TODO(liyuqian): respect clip_behavior_
  SceneUpdateContext::Clip clip(context, shape, bounds);
  UpdateSceneChildren(context);
}

#endif  // defined(OS_FUCHSIA)

void ClipPathLayer::Paint(PaintContext& context) const {
  TRACE_EVENT0("flutter", "ClipPathLayer::Paint");
  FML_DCHECK(needs_painting());

  SkAutoCanvasRestore save(context.internal_nodes_canvas, true);
  context.internal_nodes_canvas->clipPath(clip_path_,
                                          clip_behavior_ != Clip::hardEdge);
  if (clip_behavior_ == Clip::antiAliasWithSaveLayer) {
    context.internal_nodes_canvas->saveLayer(paint_bounds(), nullptr);
  }
  PaintChildren(context);
  if (clip_behavior_ == Clip::antiAliasWithSaveLayer) {
    context.internal_nodes_canvas->restore();
  }
}

}  // namespace flow
<|endoftext|>


<commit_before>/* -*- Mode: C; indent-tabs-mode: s; c-basic-offset: 4; tab-width: 4 -*- */
/* vim:set et ai sw=4 ts=4 sts=4: tw=80 cino="(0,W2s,i2s,t0,l1,:0" */
#include "dcpcategorycomponent.h"
#include "dcpappletbuttons.h"

#include <QCoreApplication>
#include <DuiContainer>
#include <QGraphicsLinearLayout>
#include <DuiSceneManager>

#define DEBUG
#include "../../../lib/src/dcpdebug.h"

/*!
 * Constructor that uses category name to create the component. Should use
 * DcpCategoryInfo so this may be deprecated.
 */
DcpCategoryComponent::DcpCategoryComponent (
        DcpComponent      *category,
        const QString     &categoryName,
        const QString     &logicalId,
        QGraphicsWidget   *parent)
: DcpComponent (category, categoryName, parent, logicalId),
    m_AppletButtons (0),
    m_CategoryName (categoryName),
    m_CategoryInfo (0),
    m_LogicalId (logicalId)
{
    createContents ();
    setMattiID ("DcpCategoryComponent::" + logicalId);
}

/*!
 * Constructor that uses DcpCategoryInfo to create a DcpCategoryComponent.
 */
DcpCategoryComponent::DcpCategoryComponent (
            DcpComponent            *category,
            const DcpCategoryInfo  *categoryInfo,
            QGraphicsWidget        *parent)
: DcpComponent (category, categoryInfo->appletCategory, parent,
                categoryInfo->titleId),
    m_CategoryName (categoryInfo->appletCategory),
    m_CategoryInfo (categoryInfo)
{
    createContents ();
    setMattiID (QString("DcpCategoryComponent::") + categoryInfo->titleId);
}

DcpCategoryComponent::~DcpCategoryComponent()
{
}

void
DcpCategoryComponent::setTitle (
        const QString &title)
{
    m_Container->setTitle (title);
    DcpComponent::setTitle (title);
}

void
DcpCategoryComponent::retranslateUi ()
{
    if (m_CategoryInfo) {
        setTitle (qtTrId (m_CategoryInfo->titleId));
    } else {
        setTitle (qtTrId (DCP_STR(m_LogicalId)));
    }
}

/*!
 * The content is created in two phase. First an empty container with a spinner
 * is created, then the applets are loaded and the brief widgets are added. this
 * is the first phase.
 */
void
DcpCategoryComponent::createContents ()
{
    QGraphicsLinearLayout *layout;

    DCP_DEBUG ("");
    m_Container = new DuiContainer (this);
    m_Container->setSizePolicy (QSizePolicy::Expanding, QSizePolicy::Expanding);
    m_Container->setProgressIndicatorVisible (true);

    layout = new QGraphicsLinearLayout (Qt::Vertical, this);
    layout->addItem (m_Container);
}

/*!
 * The content is created in two phase. First an empty container with a spinner
 * is created, then the applets are loaded and the brief widgets are added. this
 * is the second phase.
 */
void
DcpCategoryComponent::createContentsLate ()
{
    DCP_DEBUG ("");

    if (m_CategoryInfo)
        m_AppletButtons = new DcpAppletButtons (m_CategoryInfo, title());
    else
        m_AppletButtons = new DcpAppletButtons (
                logicalId(), m_CategoryName, title());

    m_Container->setCentralWidget (m_AppletButtons);

    // stop progress indicator, when all applets are in place:
    connect (m_AppletButtons, SIGNAL(loadingFinished()),
             this, SLOT(onAppletButtonsLoaded ()));

    m_AppletButtons->startLoading();
}

void
DcpCategoryComponent::onAppletButtonsLoaded ()
{
    m_Container->setProgressIndicatorVisible (false);
    emit loadFinished();
}

void
DcpCategoryComponent::reload ()
{
    DCP_DEBUG ("WARNING: RELOADING");
    m_AppletButtons->reload();
}

// ! Can be misleading, because it returns the count of the items already loaded
int
DcpCategoryComponent::getItemCount ()
{
    return m_AppletButtons ? m_AppletButtons->getItemCount () : 0;
}

/*! \returns true if the container has loaded or loading items
 */
bool
DcpCategoryComponent::hasLoadingItems ()
{
    return m_AppletButtons ? m_AppletButtons->hasLoadingItems() : false;
}

QString
DcpCategoryComponent::mattiID ()
{
    return m_mattiID;
}

void
DcpCategoryComponent::setMattiID (
        const QString &mattiID)
{
    m_mattiID = mattiID;
}

<commit_msg>Fixes that sometimes most used items loading was restarted<commit_after>/* -*- Mode: C; indent-tabs-mode: s; c-basic-offset: 4; tab-width: 4 -*- */
/* vim:set et ai sw=4 ts=4 sts=4: tw=80 cino="(0,W2s,i2s,t0,l1,:0" */
#include "dcpcategorycomponent.h"
#include "dcpappletbuttons.h"

#include <QCoreApplication>
#include <DuiContainer>
#include <QGraphicsLinearLayout>
#include <DuiSceneManager>

#define DEBUG
#include "../../../lib/src/dcpdebug.h"

/*!
 * Constructor that uses category name to create the component. Should use
 * DcpCategoryInfo so this may be deprecated.
 */
DcpCategoryComponent::DcpCategoryComponent (
        DcpComponent      *category,
        const QString     &categoryName,
        const QString     &logicalId,
        QGraphicsWidget   *parent)
: DcpComponent (category, categoryName, parent, logicalId),
    m_AppletButtons (0),
    m_CategoryName (categoryName),
    m_CategoryInfo (0),
    m_LogicalId (logicalId)
{
    createContents ();
    setMattiID ("DcpCategoryComponent::" + logicalId);
}

/*!
 * Constructor that uses DcpCategoryInfo to create a DcpCategoryComponent.
 */
DcpCategoryComponent::DcpCategoryComponent (
            DcpComponent            *category,
            const DcpCategoryInfo  *categoryInfo,
            QGraphicsWidget        *parent)
: DcpComponent (category, categoryInfo->appletCategory, parent,
                categoryInfo->titleId),
    m_CategoryName (categoryInfo->appletCategory),
    m_CategoryInfo (categoryInfo)
{
    createContents ();
    setMattiID (QString("DcpCategoryComponent::") + categoryInfo->titleId);
}

DcpCategoryComponent::~DcpCategoryComponent()
{
}

void
DcpCategoryComponent::setTitle (
        const QString &title)
{
    m_Container->setTitle (title);
    DcpComponent::setTitle (title);
}

void
DcpCategoryComponent::retranslateUi ()
{
    if (m_CategoryInfo) {
        setTitle (qtTrId (m_CategoryInfo->titleId));
    } else {
        setTitle (qtTrId (DCP_STR(m_LogicalId)));
    }
}

/*!
 * The content is created in two phase. First an empty container with a spinner
 * is created, then the applets are loaded and the brief widgets are added. this
 * is the first phase.
 */
void
DcpCategoryComponent::createContents ()
{
    QGraphicsLinearLayout *layout;

    DCP_DEBUG ("");
    m_Container = new DuiContainer (this);
    m_Container->setSizePolicy (QSizePolicy::Expanding, QSizePolicy::Expanding);
    m_Container->setProgressIndicatorVisible (true);

    layout = new QGraphicsLinearLayout (Qt::Vertical, this);
    layout->addItem (m_Container);
    setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum);
}

/*!
 * The content is created in two phase. First an empty container with a spinner
 * is created, then the applets are loaded and the brief widgets are added. this
 * is the second phase.
 */
void
DcpCategoryComponent::createContentsLate ()
{
    DCP_DEBUG ("");

    // Do not create the contents twice:
    if (m_AppletButtons) {
        onAppletButtonsLoaded();
        return;
    }

    if (m_CategoryInfo)
        m_AppletButtons = new DcpAppletButtons (m_CategoryInfo, title());
    else
        m_AppletButtons = new DcpAppletButtons (
                logicalId(), m_CategoryName, title());

    m_Container->setCentralWidget (m_AppletButtons);

    // stop progress indicator, when all applets are in place:
    connect (m_AppletButtons, SIGNAL(loadingFinished()),
             this, SLOT(onAppletButtonsLoaded ()));

    m_AppletButtons->startLoading();
}

void
DcpCategoryComponent::onAppletButtonsLoaded ()
{
    m_Container->setProgressIndicatorVisible (false);
    emit loadFinished();
}

void
DcpCategoryComponent::reload ()
{
    DCP_DEBUG ("WARNING: RELOADING");
    m_AppletButtons->reload();
}

// ! Can be misleading, because it returns the count of the items already loaded
int
DcpCategoryComponent::getItemCount ()
{
    return m_AppletButtons ? m_AppletButtons->getItemCount () : 0;
}

/*! \returns true if the container has loaded or loading items
 */
bool
DcpCategoryComponent::hasLoadingItems ()
{
    return m_AppletButtons ? m_AppletButtons->hasLoadingItems() : false;
}

QString
DcpCategoryComponent::mattiID ()
{
    return m_mattiID;
}

void
DcpCategoryComponent::setMattiID (
        const QString &mattiID)
{
    m_mattiID = mattiID;
}

<|endoftext|>


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

#include "base/message_loop.h"
#include "base/scoped_ptr.h"
#include "base/utf_string_conversions.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/base/clipboard/clipboard.h"
#include "ui/base/clipboard/scoped_clipboard_writer.h"
#include "views/controls/textfield/textfield_views_model.h"
#include "views/test/test_views_delegate.h"
#include "views/views_delegate.h"

namespace views {

#define EXPECT_STR_EQ(ascii, utf16) \
  EXPECT_EQ(ASCIIToWide(ascii), UTF16ToWide(utf16))

TEST(TextfieldViewsModelTest, EditString) {
  TextfieldViewsModel model;
  // append two strings
  model.Append(ASCIIToUTF16("HILL"));
  EXPECT_STR_EQ("HILL", model.text());
  model.Append(ASCIIToUTF16("WORLD"));
  EXPECT_STR_EQ("HILLWORLD", model.text());

  // Insert "E" to make hello
  model.MoveCursorRight(false);
  model.Insert('E');
  EXPECT_STR_EQ("HEILLWORLD", model.text());
  // Replace "I" with "L"
  model.Replace('L');
  EXPECT_STR_EQ("HELLLWORLD", model.text());
  model.Replace('L');
  model.Replace('O');
  EXPECT_STR_EQ("HELLOWORLD", model.text());

  // Delete 6th char "W", then delete 5th char O"
  EXPECT_EQ(5U, model.cursor_pos());
  EXPECT_TRUE(model.Delete());
  EXPECT_STR_EQ("HELLOORLD", model.text());
  EXPECT_TRUE(model.Backspace());
  EXPECT_EQ(4U, model.cursor_pos());
  EXPECT_STR_EQ("HELLORLD", model.text());

  // Move the cursor to start. backspace should fail.
  model.MoveCursorToStart(false);
  EXPECT_FALSE(model.Backspace());
  EXPECT_STR_EQ("HELLORLD", model.text());
  // Move the cursor to the end. delete should fail.
  model.MoveCursorToEnd(false);
  EXPECT_FALSE(model.Delete());
  EXPECT_STR_EQ("HELLORLD", model.text());
  // but backspace should work.
  EXPECT_TRUE(model.Backspace());
  EXPECT_STR_EQ("HELLORL", model.text());
}

TEST(TextfieldViewsModelTest, EmptyString) {
  TextfieldViewsModel model;
  EXPECT_EQ(string16(), model.text());
  EXPECT_EQ(string16(), model.GetSelectedText());
  EXPECT_EQ(string16(), model.GetVisibleText());

  model.MoveCursorLeft(true);
  EXPECT_EQ(0U, model.cursor_pos());
  model.MoveCursorRight(true);
  EXPECT_EQ(0U, model.cursor_pos());

  EXPECT_EQ(string16(), model.GetSelectedText());

  EXPECT_FALSE(model.Delete());
  EXPECT_FALSE(model.Backspace());
}

TEST(TextfieldViewsModelTest, Selection) {
  TextfieldViewsModel model;
  model.Append(ASCIIToUTF16("HELLO"));
  model.MoveCursorRight(false);
  model.MoveCursorRight(true);
  EXPECT_STR_EQ("E", model.GetSelectedText());
  model.MoveCursorRight(true);
  EXPECT_STR_EQ("EL", model.GetSelectedText());

  model.MoveCursorToStart(true);
  EXPECT_STR_EQ("H", model.GetSelectedText());
  model.MoveCursorToEnd(true);
  EXPECT_STR_EQ("ELLO", model.GetSelectedText());
  model.ClearSelection();
  EXPECT_EQ(string16(), model.GetSelectedText());
  model.SelectAll();
  EXPECT_STR_EQ("HELLO", model.GetSelectedText());

  // Select and move cursor
  model.MoveCursorTo(1U, false);
  model.MoveCursorTo(3U, true);
  EXPECT_STR_EQ("EL", model.GetSelectedText());
  model.MoveCursorLeft(false);
  EXPECT_EQ(1U, model.cursor_pos());
  model.MoveCursorTo(1U, false);
  model.MoveCursorTo(3U, true);
  model.MoveCursorRight(false);
  EXPECT_EQ(3U, model.cursor_pos());

  // Select all and move cursor
  model.SelectAll();
  model.MoveCursorLeft(false);
  EXPECT_EQ(0U, model.cursor_pos());
  model.SelectAll();
  model.MoveCursorRight(false);
  EXPECT_EQ(5U, model.cursor_pos());
}

TEST(TextfieldViewsModelTest, SelectionAndEdit) {
  TextfieldViewsModel model;
  model.Append(ASCIIToUTF16("HELLO"));
  model.MoveCursorRight(false);
  model.MoveCursorRight(true);
  model.MoveCursorRight(true);  // select "EL"
  EXPECT_TRUE(model.Backspace());
  EXPECT_STR_EQ("HLO", model.text());

  model.Append(ASCIIToUTF16("ILL"));
  model.MoveCursorRight(true);
  model.MoveCursorRight(true);  // select "LO"
  EXPECT_TRUE(model.Delete());
  EXPECT_STR_EQ("HILL", model.text());
  EXPECT_EQ(1U, model.cursor_pos());
  model.MoveCursorRight(true);  // select "I"
  model.Insert('E');
  EXPECT_STR_EQ("HELL", model.text());
  model.MoveCursorToStart(false);
  model.MoveCursorRight(true);  // select "H"
  model.Replace('B');
  EXPECT_STR_EQ("BELL", model.text());
  model.MoveCursorToEnd(false);
  model.MoveCursorLeft(true);
  model.MoveCursorLeft(true);  // select ">LL"
  model.Replace('E');
  EXPECT_STR_EQ("BEE", model.text());
}

TEST(TextfieldViewsModelTest, Password) {
  TextfieldViewsModel model;
  model.set_is_password(true);
  model.Append(ASCIIToUTF16("HELLO"));
  EXPECT_STR_EQ("*****", model.GetVisibleText());
  EXPECT_STR_EQ("HELLO", model.text());
  EXPECT_TRUE(model.Delete());

  EXPECT_STR_EQ("****", model.GetVisibleText());
  EXPECT_STR_EQ("ELLO", model.text());
  EXPECT_EQ(0U, model.cursor_pos());

  model.SelectAll();
  EXPECT_STR_EQ("ELLO", model.GetSelectedText());
  EXPECT_EQ(0U, model.cursor_pos());

  model.Insert('X');
  EXPECT_STR_EQ("*", model.GetVisibleText());
  EXPECT_STR_EQ("X", model.text());
}

TEST(TextfieldViewsModelTest, Word) {
  TextfieldViewsModel model;
  model.Append(
      ASCIIToUTF16("The answer to Life, the Universe, and Everything"));
  model.MoveCursorToNextWord(false);
  EXPECT_EQ(3U, model.cursor_pos());
  model.MoveCursorToNextWord(false);
  EXPECT_EQ(10U, model.cursor_pos());
  model.MoveCursorToNextWord(false);
  model.MoveCursorToNextWord(false);
  EXPECT_EQ(18U, model.cursor_pos());

  // Should passes the non word char ','
  model.MoveCursorToNextWord(true);
  EXPECT_EQ(23U, model.cursor_pos());
  EXPECT_STR_EQ(", the", model.GetSelectedText());

  // Move to the end.
  model.MoveCursorToNextWord(true);
  model.MoveCursorToNextWord(true);
  model.MoveCursorToNextWord(true);
  EXPECT_STR_EQ(", the Universe, and Everything", model.GetSelectedText());
  // Should be safe to go next word at the end.
  model.MoveCursorToNextWord(true);
  EXPECT_STR_EQ(", the Universe, and Everything", model.GetSelectedText());
  model.Insert('2');
  EXPECT_EQ(19U, model.cursor_pos());

  // Now backwards.
  model.MoveCursorLeft(false);  // leave 2.
  model.MoveCursorToPreviousWord(true);
  EXPECT_EQ(14U, model.cursor_pos());
  EXPECT_STR_EQ("Life", model.GetSelectedText());
  model.MoveCursorToPreviousWord(true);
  EXPECT_STR_EQ("to Life", model.GetSelectedText());
  model.MoveCursorToPreviousWord(true);
  model.MoveCursorToPreviousWord(true);
  model.MoveCursorToPreviousWord(true);  // Select to the begining.
  EXPECT_STR_EQ("The answer to Life", model.GetSelectedText());
  // Should be safe to go pervious word at the begining.
  model.MoveCursorToPreviousWord(true);
  EXPECT_STR_EQ("The answer to Life", model.GetSelectedText());
  model.Replace('4');
  EXPECT_EQ(string16(), model.GetSelectedText());
  EXPECT_STR_EQ("42", model.GetVisibleText());
}

TEST(TextfieldViewsModelTest, TextFragment) {
  TextfieldViewsModel model;
  TextfieldViewsModel::TextFragments fragments;
  // Empty string
  model.GetFragments(&fragments);
  EXPECT_EQ(1U, fragments.size());
  fragments.clear();
  EXPECT_EQ(0U, fragments[0].begin);
  EXPECT_EQ(0U, fragments[0].end);
  EXPECT_FALSE(fragments[0].selected);

  // Some string
  model.Append(ASCIIToUTF16("Hello world"));
  model.GetFragments(&fragments);
  EXPECT_EQ(1U, fragments.size());
  EXPECT_EQ(0U, fragments[0].begin);
  EXPECT_EQ(11U, fragments[0].end);
  EXPECT_FALSE(fragments[0].selected);

  // Select 1st word
  model.MoveCursorToNextWord(true);
  model.GetFragments(&fragments);
  EXPECT_EQ(2U, fragments.size());
  EXPECT_EQ(0U, fragments[0].begin);
  EXPECT_EQ(5U, fragments[0].end);
  EXPECT_TRUE(fragments[0].selected);
  EXPECT_EQ(5U, fragments[1].begin);
  EXPECT_EQ(11U, fragments[1].end);
  EXPECT_FALSE(fragments[1].selected);

  // Select empty string
  model.ClearSelection();
  model.MoveCursorRight(true);
  model.GetFragments(&fragments);
  EXPECT_EQ(3U, fragments.size());
  EXPECT_EQ(0U, fragments[0].begin);
  EXPECT_EQ(5U, fragments[0].end);
  EXPECT_FALSE(fragments[0].selected);
  EXPECT_EQ(5U, fragments[1].begin);
  EXPECT_EQ(6U, fragments[1].end);
  EXPECT_TRUE(fragments[1].selected);

  EXPECT_EQ(6U, fragments[2].begin);
  EXPECT_EQ(11U, fragments[2].end);
  EXPECT_FALSE(fragments[2].selected);

  // Select to the end.
  model.MoveCursorToEnd(true);
  model.GetFragments(&fragments);
  EXPECT_EQ(2U, fragments.size());
  EXPECT_EQ(0U, fragments[0].begin);
  EXPECT_EQ(5U, fragments[0].end);
  EXPECT_FALSE(fragments[0].selected);
  EXPECT_EQ(5U, fragments[1].begin);
  EXPECT_EQ(11U, fragments[1].end);
  EXPECT_TRUE(fragments[1].selected);
}

TEST(TextfieldViewsModelTest, SetText) {
  TextfieldViewsModel model;
  model.Append(ASCIIToUTF16("HELLO"));
  model.MoveCursorToEnd(false);
  model.SetText(ASCIIToUTF16("GOODBYE"));
  EXPECT_STR_EQ("GOODBYE", model.text());
  EXPECT_EQ(5U, model.cursor_pos());
  model.SelectAll();
  EXPECT_STR_EQ("GOODBYE", model.GetSelectedText());
  // Selection move the current pos to the begining.
  EXPECT_EQ(0U, model.cursor_pos());
  model.MoveCursorToEnd(false);
  EXPECT_EQ(7U, model.cursor_pos());

  model.SetText(ASCIIToUTF16("BYE"));
  EXPECT_EQ(3U, model.cursor_pos());
  EXPECT_EQ(string16(), model.GetSelectedText());
  model.SetText(ASCIIToUTF16(""));
  EXPECT_EQ(0U, model.cursor_pos());
}

TEST(TextfieldViewsModelTest, Clipboard) {
  views::ViewsDelegate::views_delegate = new TestViewsDelegate();
  ui::Clipboard* clipboard
      = views::ViewsDelegate::views_delegate->GetClipboard();
  string16 initial_clipboard_text;
  clipboard->ReadText(ui::Clipboard::BUFFER_STANDARD, &initial_clipboard_text);
  string16 clipboard_text;
  TextfieldViewsModel model;
  model.Append(ASCIIToUTF16("HELLO WORLD"));
  model.MoveCursorToEnd(false);

  // Test for cut: Empty selection.
  EXPECT_FALSE(model.Cut());
  clipboard->ReadText(ui::Clipboard::BUFFER_STANDARD, &clipboard_text);
  EXPECT_STR_EQ(UTF16ToUTF8(initial_clipboard_text), clipboard_text);
  EXPECT_STR_EQ("HELLO WORLD", model.text());
  EXPECT_EQ(11U, model.cursor_pos());

  // Test for cut: Non-empty selection.
  model.MoveCursorToPreviousWord(true);
  EXPECT_TRUE(model.Cut());
  clipboard->ReadText(ui::Clipboard::BUFFER_STANDARD, &clipboard_text);
  EXPECT_STR_EQ("WORLD", clipboard_text);
  EXPECT_STR_EQ("HELLO ", model.text());
  EXPECT_EQ(6U, model.cursor_pos());

  // Test for copy: Empty selection.
  model.Copy();
  clipboard->ReadText(ui::Clipboard::BUFFER_STANDARD, &clipboard_text);
  EXPECT_STR_EQ("WORLD", clipboard_text);
  EXPECT_STR_EQ("HELLO ", model.text());
  EXPECT_EQ(6U, model.cursor_pos());

  // Test for copy: Non-empty selection.
  model.Append(ASCIIToUTF16("HELLO WORLD"));
  model.SelectAll();
  model.Copy();
  clipboard->ReadText(ui::Clipboard::BUFFER_STANDARD, &clipboard_text);
  EXPECT_STR_EQ("HELLO HELLO WORLD", clipboard_text);
  EXPECT_STR_EQ("HELLO HELLO WORLD", model.text());
  EXPECT_EQ(0U, model.cursor_pos());

  // Test for paste.
  model.ClearSelection();
  model.MoveCursorToEnd(false);
  model.MoveCursorToPreviousWord(true);
  EXPECT_TRUE(model.Paste());
  clipboard->ReadText(ui::Clipboard::BUFFER_STANDARD, &clipboard_text);
  EXPECT_STR_EQ("HELLO HELLO WORLD", clipboard_text);
  EXPECT_STR_EQ("HELLO HELLO HELLO HELLO WORLD", model.text());
  EXPECT_EQ(29U, model.cursor_pos());
}

}  // namespace views
<commit_msg>Delete test view delegate at end of test<commit_after>// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "base/message_loop.h"
#include "base/scoped_ptr.h"
#include "base/utf_string_conversions.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/base/clipboard/clipboard.h"
#include "ui/base/clipboard/scoped_clipboard_writer.h"
#include "views/controls/textfield/textfield_views_model.h"
#include "views/test/test_views_delegate.h"
#include "views/views_delegate.h"

namespace views {

#define EXPECT_STR_EQ(ascii, utf16) \
  EXPECT_EQ(ASCIIToWide(ascii), UTF16ToWide(utf16))

TEST(TextfieldViewsModelTest, EditString) {
  TextfieldViewsModel model;
  // append two strings
  model.Append(ASCIIToUTF16("HILL"));
  EXPECT_STR_EQ("HILL", model.text());
  model.Append(ASCIIToUTF16("WORLD"));
  EXPECT_STR_EQ("HILLWORLD", model.text());

  // Insert "E" to make hello
  model.MoveCursorRight(false);
  model.Insert('E');
  EXPECT_STR_EQ("HEILLWORLD", model.text());
  // Replace "I" with "L"
  model.Replace('L');
  EXPECT_STR_EQ("HELLLWORLD", model.text());
  model.Replace('L');
  model.Replace('O');
  EXPECT_STR_EQ("HELLOWORLD", model.text());

  // Delete 6th char "W", then delete 5th char O"
  EXPECT_EQ(5U, model.cursor_pos());
  EXPECT_TRUE(model.Delete());
  EXPECT_STR_EQ("HELLOORLD", model.text());
  EXPECT_TRUE(model.Backspace());
  EXPECT_EQ(4U, model.cursor_pos());
  EXPECT_STR_EQ("HELLORLD", model.text());

  // Move the cursor to start. backspace should fail.
  model.MoveCursorToStart(false);
  EXPECT_FALSE(model.Backspace());
  EXPECT_STR_EQ("HELLORLD", model.text());
  // Move the cursor to the end. delete should fail.
  model.MoveCursorToEnd(false);
  EXPECT_FALSE(model.Delete());
  EXPECT_STR_EQ("HELLORLD", model.text());
  // but backspace should work.
  EXPECT_TRUE(model.Backspace());
  EXPECT_STR_EQ("HELLORL", model.text());
}

TEST(TextfieldViewsModelTest, EmptyString) {
  TextfieldViewsModel model;
  EXPECT_EQ(string16(), model.text());
  EXPECT_EQ(string16(), model.GetSelectedText());
  EXPECT_EQ(string16(), model.GetVisibleText());

  model.MoveCursorLeft(true);
  EXPECT_EQ(0U, model.cursor_pos());
  model.MoveCursorRight(true);
  EXPECT_EQ(0U, model.cursor_pos());

  EXPECT_EQ(string16(), model.GetSelectedText());

  EXPECT_FALSE(model.Delete());
  EXPECT_FALSE(model.Backspace());
}

TEST(TextfieldViewsModelTest, Selection) {
  TextfieldViewsModel model;
  model.Append(ASCIIToUTF16("HELLO"));
  model.MoveCursorRight(false);
  model.MoveCursorRight(true);
  EXPECT_STR_EQ("E", model.GetSelectedText());
  model.MoveCursorRight(true);
  EXPECT_STR_EQ("EL", model.GetSelectedText());

  model.MoveCursorToStart(true);
  EXPECT_STR_EQ("H", model.GetSelectedText());
  model.MoveCursorToEnd(true);
  EXPECT_STR_EQ("ELLO", model.GetSelectedText());
  model.ClearSelection();
  EXPECT_EQ(string16(), model.GetSelectedText());
  model.SelectAll();
  EXPECT_STR_EQ("HELLO", model.GetSelectedText());

  // Select and move cursor
  model.MoveCursorTo(1U, false);
  model.MoveCursorTo(3U, true);
  EXPECT_STR_EQ("EL", model.GetSelectedText());
  model.MoveCursorLeft(false);
  EXPECT_EQ(1U, model.cursor_pos());
  model.MoveCursorTo(1U, false);
  model.MoveCursorTo(3U, true);
  model.MoveCursorRight(false);
  EXPECT_EQ(3U, model.cursor_pos());

  // Select all and move cursor
  model.SelectAll();
  model.MoveCursorLeft(false);
  EXPECT_EQ(0U, model.cursor_pos());
  model.SelectAll();
  model.MoveCursorRight(false);
  EXPECT_EQ(5U, model.cursor_pos());
}

TEST(TextfieldViewsModelTest, SelectionAndEdit) {
  TextfieldViewsModel model;
  model.Append(ASCIIToUTF16("HELLO"));
  model.MoveCursorRight(false);
  model.MoveCursorRight(true);
  model.MoveCursorRight(true);  // select "EL"
  EXPECT_TRUE(model.Backspace());
  EXPECT_STR_EQ("HLO", model.text());

  model.Append(ASCIIToUTF16("ILL"));
  model.MoveCursorRight(true);
  model.MoveCursorRight(true);  // select "LO"
  EXPECT_TRUE(model.Delete());
  EXPECT_STR_EQ("HILL", model.text());
  EXPECT_EQ(1U, model.cursor_pos());
  model.MoveCursorRight(true);  // select "I"
  model.Insert('E');
  EXPECT_STR_EQ("HELL", model.text());
  model.MoveCursorToStart(false);
  model.MoveCursorRight(true);  // select "H"
  model.Replace('B');
  EXPECT_STR_EQ("BELL", model.text());
  model.MoveCursorToEnd(false);
  model.MoveCursorLeft(true);
  model.MoveCursorLeft(true);  // select ">LL"
  model.Replace('E');
  EXPECT_STR_EQ("BEE", model.text());
}

TEST(TextfieldViewsModelTest, Password) {
  TextfieldViewsModel model;
  model.set_is_password(true);
  model.Append(ASCIIToUTF16("HELLO"));
  EXPECT_STR_EQ("*****", model.GetVisibleText());
  EXPECT_STR_EQ("HELLO", model.text());
  EXPECT_TRUE(model.Delete());

  EXPECT_STR_EQ("****", model.GetVisibleText());
  EXPECT_STR_EQ("ELLO", model.text());
  EXPECT_EQ(0U, model.cursor_pos());

  model.SelectAll();
  EXPECT_STR_EQ("ELLO", model.GetSelectedText());
  EXPECT_EQ(0U, model.cursor_pos());

  model.Insert('X');
  EXPECT_STR_EQ("*", model.GetVisibleText());
  EXPECT_STR_EQ("X", model.text());
}

TEST(TextfieldViewsModelTest, Word) {
  TextfieldViewsModel model;
  model.Append(
      ASCIIToUTF16("The answer to Life, the Universe, and Everything"));
  model.MoveCursorToNextWord(false);
  EXPECT_EQ(3U, model.cursor_pos());
  model.MoveCursorToNextWord(false);
  EXPECT_EQ(10U, model.cursor_pos());
  model.MoveCursorToNextWord(false);
  model.MoveCursorToNextWord(false);
  EXPECT_EQ(18U, model.cursor_pos());

  // Should passes the non word char ','
  model.MoveCursorToNextWord(true);
  EXPECT_EQ(23U, model.cursor_pos());
  EXPECT_STR_EQ(", the", model.GetSelectedText());

  // Move to the end.
  model.MoveCursorToNextWord(true);
  model.MoveCursorToNextWord(true);
  model.MoveCursorToNextWord(true);
  EXPECT_STR_EQ(", the Universe, and Everything", model.GetSelectedText());
  // Should be safe to go next word at the end.
  model.MoveCursorToNextWord(true);
  EXPECT_STR_EQ(", the Universe, and Everything", model.GetSelectedText());
  model.Insert('2');
  EXPECT_EQ(19U, model.cursor_pos());

  // Now backwards.
  model.MoveCursorLeft(false);  // leave 2.
  model.MoveCursorToPreviousWord(true);
  EXPECT_EQ(14U, model.cursor_pos());
  EXPECT_STR_EQ("Life", model.GetSelectedText());
  model.MoveCursorToPreviousWord(true);
  EXPECT_STR_EQ("to Life", model.GetSelectedText());
  model.MoveCursorToPreviousWord(true);
  model.MoveCursorToPreviousWord(true);
  model.MoveCursorToPreviousWord(true);  // Select to the begining.
  EXPECT_STR_EQ("The answer to Life", model.GetSelectedText());
  // Should be safe to go pervious word at the begining.
  model.MoveCursorToPreviousWord(true);
  EXPECT_STR_EQ("The answer to Life", model.GetSelectedText());
  model.Replace('4');
  EXPECT_EQ(string16(), model.GetSelectedText());
  EXPECT_STR_EQ("42", model.GetVisibleText());
}

TEST(TextfieldViewsModelTest, TextFragment) {
  TextfieldViewsModel model;
  TextfieldViewsModel::TextFragments fragments;
  // Empty string
  model.GetFragments(&fragments);
  EXPECT_EQ(1U, fragments.size());
  fragments.clear();
  EXPECT_EQ(0U, fragments[0].begin);
  EXPECT_EQ(0U, fragments[0].end);
  EXPECT_FALSE(fragments[0].selected);

  // Some string
  model.Append(ASCIIToUTF16("Hello world"));
  model.GetFragments(&fragments);
  EXPECT_EQ(1U, fragments.size());
  EXPECT_EQ(0U, fragments[0].begin);
  EXPECT_EQ(11U, fragments[0].end);
  EXPECT_FALSE(fragments[0].selected);

  // Select 1st word
  model.MoveCursorToNextWord(true);
  model.GetFragments(&fragments);
  EXPECT_EQ(2U, fragments.size());
  EXPECT_EQ(0U, fragments[0].begin);
  EXPECT_EQ(5U, fragments[0].end);
  EXPECT_TRUE(fragments[0].selected);
  EXPECT_EQ(5U, fragments[1].begin);
  EXPECT_EQ(11U, fragments[1].end);
  EXPECT_FALSE(fragments[1].selected);

  // Select empty string
  model.ClearSelection();
  model.MoveCursorRight(true);
  model.GetFragments(&fragments);
  EXPECT_EQ(3U, fragments.size());
  EXPECT_EQ(0U, fragments[0].begin);
  EXPECT_EQ(5U, fragments[0].end);
  EXPECT_FALSE(fragments[0].selected);
  EXPECT_EQ(5U, fragments[1].begin);
  EXPECT_EQ(6U, fragments[1].end);
  EXPECT_TRUE(fragments[1].selected);

  EXPECT_EQ(6U, fragments[2].begin);
  EXPECT_EQ(11U, fragments[2].end);
  EXPECT_FALSE(fragments[2].selected);

  // Select to the end.
  model.MoveCursorToEnd(true);
  model.GetFragments(&fragments);
  EXPECT_EQ(2U, fragments.size());
  EXPECT_EQ(0U, fragments[0].begin);
  EXPECT_EQ(5U, fragments[0].end);
  EXPECT_FALSE(fragments[0].selected);
  EXPECT_EQ(5U, fragments[1].begin);
  EXPECT_EQ(11U, fragments[1].end);
  EXPECT_TRUE(fragments[1].selected);
}

TEST(TextfieldViewsModelTest, SetText) {
  TextfieldViewsModel model;
  model.Append(ASCIIToUTF16("HELLO"));
  model.MoveCursorToEnd(false);
  model.SetText(ASCIIToUTF16("GOODBYE"));
  EXPECT_STR_EQ("GOODBYE", model.text());
  EXPECT_EQ(5U, model.cursor_pos());
  model.SelectAll();
  EXPECT_STR_EQ("GOODBYE", model.GetSelectedText());
  // Selection move the current pos to the begining.
  EXPECT_EQ(0U, model.cursor_pos());
  model.MoveCursorToEnd(false);
  EXPECT_EQ(7U, model.cursor_pos());

  model.SetText(ASCIIToUTF16("BYE"));
  EXPECT_EQ(3U, model.cursor_pos());
  EXPECT_EQ(string16(), model.GetSelectedText());
  model.SetText(ASCIIToUTF16(""));
  EXPECT_EQ(0U, model.cursor_pos());
}

TEST(TextfieldViewsModelTest, Clipboard) {
  scoped_ptr<TestViewsDelegate> test_views_delegate(new TestViewsDelegate());
  views::ViewsDelegate::views_delegate = test_views_delegate.get();
  ui::Clipboard* clipboard
      = views::ViewsDelegate::views_delegate->GetClipboard();
  string16 initial_clipboard_text;
  clipboard->ReadText(ui::Clipboard::BUFFER_STANDARD, &initial_clipboard_text);
  string16 clipboard_text;
  TextfieldViewsModel model;
  model.Append(ASCIIToUTF16("HELLO WORLD"));
  model.MoveCursorToEnd(false);

  // Test for cut: Empty selection.
  EXPECT_FALSE(model.Cut());
  clipboard->ReadText(ui::Clipboard::BUFFER_STANDARD, &clipboard_text);
  EXPECT_STR_EQ(UTF16ToUTF8(initial_clipboard_text), clipboard_text);
  EXPECT_STR_EQ("HELLO WORLD", model.text());
  EXPECT_EQ(11U, model.cursor_pos());

  // Test for cut: Non-empty selection.
  model.MoveCursorToPreviousWord(true);
  EXPECT_TRUE(model.Cut());
  clipboard->ReadText(ui::Clipboard::BUFFER_STANDARD, &clipboard_text);
  EXPECT_STR_EQ("WORLD", clipboard_text);
  EXPECT_STR_EQ("HELLO ", model.text());
  EXPECT_EQ(6U, model.cursor_pos());

  // Test for copy: Empty selection.
  model.Copy();
  clipboard->ReadText(ui::Clipboard::BUFFER_STANDARD, &clipboard_text);
  EXPECT_STR_EQ("WORLD", clipboard_text);
  EXPECT_STR_EQ("HELLO ", model.text());
  EXPECT_EQ(6U, model.cursor_pos());

  // Test for copy: Non-empty selection.
  model.Append(ASCIIToUTF16("HELLO WORLD"));
  model.SelectAll();
  model.Copy();
  clipboard->ReadText(ui::Clipboard::BUFFER_STANDARD, &clipboard_text);
  EXPECT_STR_EQ("HELLO HELLO WORLD", clipboard_text);
  EXPECT_STR_EQ("HELLO HELLO WORLD", model.text());
  EXPECT_EQ(0U, model.cursor_pos());

  // Test for paste.
  model.ClearSelection();
  model.MoveCursorToEnd(false);
  model.MoveCursorToPreviousWord(true);
  EXPECT_TRUE(model.Paste());
  clipboard->ReadText(ui::Clipboard::BUFFER_STANDARD, &clipboard_text);
  EXPECT_STR_EQ("HELLO HELLO WORLD", clipboard_text);
  EXPECT_STR_EQ("HELLO HELLO HELLO HELLO WORLD", model.text());
  EXPECT_EQ(29U, model.cursor_pos());
}

}  // namespace views
<|endoftext|>


<commit_before>/**
 * Copyright (c) 2016 zScale Technology GmbH <legal@zscale.io>
 * Authors:
 *   - Paul Asmuth <paul@zscale.io>
 *
 * This program is free software: you can redistribute it and/or modify it under
 * the terms of the GNU Affero General Public License ("the license") as
 * published by the Free Software Foundation, either version 3 of the License,
 * or any later version.
 *
 * In accordance with Section 7(e) of the license, the licensing of the Program
 * under the license does not imply a trademark license. Therefore any rights,
 * title and interest in our trademarks remain entirely with us.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the license for more details.
 *
 * You can be released from the requirements of the license by purchasing a
 * commercial license. Buying such a license is mandatory as soon as you develop
 * commercial activities involving this program without disclosing the source
 * code of your own applications
 */
#include "unistd.h"
#include <eventql/util/logging.h>
#include <eventql/util/wallclock.h>
#include <eventql/util/application.h>
#include <eventql/db/ReplicationWorker.h>
#include <eventql/db/Partition.h>
#include <eventql/server/server_stats.h>

#include "eventql/eventql.h"

namespace eventql {

ReplicationInfo::ReplicationInfo() {
  reset();
}

void ReplicationInfo::reset() {
  std::unique_lock<std::mutex> lk(mutex_);
  is_idle_ = true;
  cur_partition_.clear();
  cur_target_host_.clear();
  cur_partition_since_ = UnixTime(0);
  cur_target_host_since_ = UnixTime(0);
  cur_target_host_bytes_sent_ = 0;
  cur_target_host_records_sent_ = 0;
}

void ReplicationInfo::setPartition(String name) {
  std::unique_lock<std::mutex> lk(mutex_);
  is_idle_ = false;
  cur_partition_ = name;
  cur_partition_since_ = WallClock::now();
}

void ReplicationInfo::setTargetHost(String host_name) {
  std::unique_lock<std::mutex> lk(mutex_);
  cur_target_host_ = host_name;
  cur_target_host_since_ = WallClock::now();
  cur_target_host_bytes_sent_ = 0;
  cur_target_host_records_sent_ = 0;
}

void ReplicationInfo::setTargetHostStatus(
    size_t bytes_sent,
    size_t records_sent) {
  cur_target_host_bytes_sent_ = bytes_sent;
  cur_target_host_records_sent_ = records_sent;
}

String ReplicationInfo::toString() const {
  std::unique_lock<std::mutex> lk(mutex_);
  if (is_idle_) {
    return "Idle";
  }

  if (cur_target_host_.empty()) {
    return StringUtil::format(
        "Replicating partition $0 (since $1s)",
        cur_partition_,
        (WallClock::unixMicros() - cur_partition_since_.unixMicros()) / kMicrosPerSecond);

  } else {
    auto duration = (WallClock::unixMicros() - cur_target_host_since_.unixMicros()) / kMicrosPerSecond;
    return StringUtil::format(
        "Replicating partition $0 (since $1s) to host $2 (since $3s); records_sent=$4 bytes_sent=$5MB bw=$6kb/s",
        cur_partition_,
        (WallClock::unixMicros() - cur_partition_since_.unixMicros()) / kMicrosPerSecond,
        cur_target_host_,
        duration,
        cur_target_host_records_sent_,
        cur_target_host_bytes_sent_ / double(1024 * 1024),
        (cur_target_host_bytes_sent_ / (duration < 1 ? 1 : duration)) / 1024);
  }
}

ReplicationWorker::ReplicationWorker(
    RefPtr<ReplicationScheme> repl_scheme,
    PartitionMap* pmap,
    http::HTTPConnectionPool* http) :
    repl_scheme_(repl_scheme),
    pmap_(pmap),
    http_(http),
    queue_([] (
        const Pair<uint64_t, RefPtr<Partition>>& a,
        const Pair<uint64_t, RefPtr<Partition>>& b) {
      return a.first < b.first;
    }),
    running_(false),
    num_replication_threads_(8),
    replication_infos_(num_replication_threads_) {
  pmap->subscribeToPartitionChanges([this] (
      RefPtr<eventql::PartitionChangeNotification> change) {
    enqueuePartition(change->partition);
  });

  start();
}

ReplicationWorker::~ReplicationWorker() {
  stop();
}

void ReplicationWorker::enqueuePartition(RefPtr<Partition> partition) {
  std::unique_lock<std::mutex> lk(mutex_);
  enqueuePartitionWithLock(partition, kReplicationCorkWindowMicros);
}

void ReplicationWorker::enqueuePartition(
    RefPtr<Partition> partition,
    uint64_t delay_usecs) {
  std::unique_lock<std::mutex> lk(mutex_);
  enqueuePartitionWithLock(partition, delay_usecs);
}

void ReplicationWorker::enqueuePartitionWithLock(
    RefPtr<Partition> partition,
    uint64_t delay_usecs /* = 0 */) {
  auto uuid = partition->uuid();
  if (waitset_.count(uuid) > 0) {
    return;
  }

  queue_.emplace(
      WallClock::unixMicros() + delay_usecs,
      partition);

  z1stats()->replication_queue_length.set(queue_.size());

  waitset_.emplace(uuid);
  cv_.notify_all();
}

void ReplicationWorker::start() {
  running_ = true;

  for (int i = 0; i < num_replication_threads_; ++i) {
    threads_.emplace_back(std::bind(&ReplicationWorker::work, this, i));
  }
}

void ReplicationWorker::stop() {
  if (!running_) {
    return;
  }

  running_ = false;
  cv_.notify_all();

  for (auto& t : threads_) {
    t.join();
  }
}

void ReplicationWorker::work(size_t thread_id) {
  Application::setCurrentThreadName("z1d-replication");
  auto replication_info = &replication_infos_[thread_id];

  std::unique_lock<std::mutex> lk(mutex_);

  while (running_) {
    if (queue_.size() == 0) {
      cv_.wait(lk);
    }

    if (queue_.size() == 0) {
      continue;
    }

    auto now = WallClock::unixMicros();
    if (now < queue_.begin()->first) {
      cv_.wait_for(
          lk,
          std::chrono::microseconds(queue_.begin()->first - now));

      continue;
    }

    auto partition = queue_.begin()->second;
    queue_.erase(queue_.begin());
    auto repl_scheme = repl_scheme_;

    RefPtr<PartitionReplication> repl;
    bool success = true;
    {
      lk.unlock();

      auto snap = partition->getSnapshot();
      replication_info->setPartition(StringUtil::format(
          "$0/$1/$2",
          snap->state.tsdb_namespace(),
          snap->state.table_key(),
          snap->key));

      try {
        repl = partition->getReplicationStrategy(repl_scheme, http_);
        success = repl->replicate(replication_info);
      } catch (const StandardException& e) {
        logError("evqld", e, "ReplicationWorker error");
        success = false;
      }

      replication_info->reset();

      if (!success) {
        auto snap = partition->getSnapshot();

        logError(
            "evqld",
            "Replication failed for partition $0/$1/$2",
            snap->state.tsdb_namespace(),
            snap->state.table_key(),
            snap->key.toString());
      }

      lk.lock();
    }

    if (success) {
      waitset_.erase(partition->uuid());

      repl = partition->getReplicationStrategy(repl_scheme, http_);
      if (repl->needsReplication()) {
        enqueuePartitionWithLock(partition, kReplicationCorkWindowMicros);
      } else {
        repl = partition->getReplicationStrategy(repl_scheme, http_);
        if (repl->shouldDropPartition()) {
          auto snap = partition->getSnapshot();
          auto dropped =
              pmap_->dropLocalPartition(
                  snap->state.tsdb_namespace(),
                  snap->state.table_key(),
                  snap->key);

          if (!dropped) {
            enqueuePartitionWithLock(partition, kReplicationCorkWindowMicros);
          }
        }
      }
    } else {
      auto delay = 30 * kMicrosPerSecond; // FIXPAUL increasing delay..
      queue_.emplace(now + delay, partition);
    }

    z1stats()->replication_queue_length.set(queue_.size());
  }
}

size_t ReplicationWorker::getNumThreads() const {
  return num_replication_threads_;
}

const ReplicationInfo* ReplicationWorker::getReplicationInfo(
    size_t thread_id) const {
  if (thread_id >= num_replication_threads_) {
    return nullptr;
  }

  return &replication_infos_[thread_id];
}

} // namespace eventql
<commit_msg>set number of repl threads to 32<commit_after>/**
 * Copyright (c) 2016 zScale Technology GmbH <legal@zscale.io>
 * Authors:
 *   - Paul Asmuth <paul@zscale.io>
 *
 * This program is free software: you can redistribute it and/or modify it under
 * the terms of the GNU Affero General Public License ("the license") as
 * published by the Free Software Foundation, either version 3 of the License,
 * or any later version.
 *
 * In accordance with Section 7(e) of the license, the licensing of the Program
 * under the license does not imply a trademark license. Therefore any rights,
 * title and interest in our trademarks remain entirely with us.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the license for more details.
 *
 * You can be released from the requirements of the license by purchasing a
 * commercial license. Buying such a license is mandatory as soon as you develop
 * commercial activities involving this program without disclosing the source
 * code of your own applications
 */
#include "unistd.h"
#include <eventql/util/logging.h>
#include <eventql/util/wallclock.h>
#include <eventql/util/application.h>
#include <eventql/db/ReplicationWorker.h>
#include <eventql/db/Partition.h>
#include <eventql/server/server_stats.h>

#include "eventql/eventql.h"

namespace eventql {

ReplicationInfo::ReplicationInfo() {
  reset();
}

void ReplicationInfo::reset() {
  std::unique_lock<std::mutex> lk(mutex_);
  is_idle_ = true;
  cur_partition_.clear();
  cur_target_host_.clear();
  cur_partition_since_ = UnixTime(0);
  cur_target_host_since_ = UnixTime(0);
  cur_target_host_bytes_sent_ = 0;
  cur_target_host_records_sent_ = 0;
}

void ReplicationInfo::setPartition(String name) {
  std::unique_lock<std::mutex> lk(mutex_);
  is_idle_ = false;
  cur_partition_ = name;
  cur_partition_since_ = WallClock::now();
}

void ReplicationInfo::setTargetHost(String host_name) {
  std::unique_lock<std::mutex> lk(mutex_);
  cur_target_host_ = host_name;
  cur_target_host_since_ = WallClock::now();
  cur_target_host_bytes_sent_ = 0;
  cur_target_host_records_sent_ = 0;
}

void ReplicationInfo::setTargetHostStatus(
    size_t bytes_sent,
    size_t records_sent) {
  cur_target_host_bytes_sent_ = bytes_sent;
  cur_target_host_records_sent_ = records_sent;
}

String ReplicationInfo::toString() const {
  std::unique_lock<std::mutex> lk(mutex_);
  if (is_idle_) {
    return "Idle";
  }

  if (cur_target_host_.empty()) {
    return StringUtil::format(
        "Replicating partition $0 (since $1s)",
        cur_partition_,
        (WallClock::unixMicros() - cur_partition_since_.unixMicros()) / kMicrosPerSecond);

  } else {
    auto duration = (WallClock::unixMicros() - cur_target_host_since_.unixMicros()) / kMicrosPerSecond;
    return StringUtil::format(
        "Replicating partition $0 (since $1s) to host $2 (since $3s); records_sent=$4 bytes_sent=$5MB bw=$6kb/s",
        cur_partition_,
        (WallClock::unixMicros() - cur_partition_since_.unixMicros()) / kMicrosPerSecond,
        cur_target_host_,
        duration,
        cur_target_host_records_sent_,
        cur_target_host_bytes_sent_ / double(1024 * 1024),
        (cur_target_host_bytes_sent_ / (duration < 1 ? 1 : duration)) / 1024);
  }
}

ReplicationWorker::ReplicationWorker(
    RefPtr<ReplicationScheme> repl_scheme,
    PartitionMap* pmap,
    http::HTTPConnectionPool* http) :
    repl_scheme_(repl_scheme),
    pmap_(pmap),
    http_(http),
    queue_([] (
        const Pair<uint64_t, RefPtr<Partition>>& a,
        const Pair<uint64_t, RefPtr<Partition>>& b) {
      return a.first < b.first;
    }),
    running_(false),
    num_replication_threads_(32),
    replication_infos_(num_replication_threads_) {
  pmap->subscribeToPartitionChanges([this] (
      RefPtr<eventql::PartitionChangeNotification> change) {
    enqueuePartition(change->partition);
  });

  start();
}

ReplicationWorker::~ReplicationWorker() {
  stop();
}

void ReplicationWorker::enqueuePartition(RefPtr<Partition> partition) {
  std::unique_lock<std::mutex> lk(mutex_);
  enqueuePartitionWithLock(partition, kReplicationCorkWindowMicros);
}

void ReplicationWorker::enqueuePartition(
    RefPtr<Partition> partition,
    uint64_t delay_usecs) {
  std::unique_lock<std::mutex> lk(mutex_);
  enqueuePartitionWithLock(partition, delay_usecs);
}

void ReplicationWorker::enqueuePartitionWithLock(
    RefPtr<Partition> partition,
    uint64_t delay_usecs /* = 0 */) {
  auto uuid = partition->uuid();
  if (waitset_.count(uuid) > 0) {
    return;
  }

  queue_.emplace(
      WallClock::unixMicros() + delay_usecs,
      partition);

  z1stats()->replication_queue_length.set(queue_.size());

  waitset_.emplace(uuid);
  cv_.notify_all();
}

void ReplicationWorker::start() {
  running_ = true;

  for (int i = 0; i < num_replication_threads_; ++i) {
    threads_.emplace_back(std::bind(&ReplicationWorker::work, this, i));
  }
}

void ReplicationWorker::stop() {
  if (!running_) {
    return;
  }

  running_ = false;
  cv_.notify_all();

  for (auto& t : threads_) {
    t.join();
  }
}

void ReplicationWorker::work(size_t thread_id) {
  Application::setCurrentThreadName("z1d-replication");
  auto replication_info = &replication_infos_[thread_id];

  std::unique_lock<std::mutex> lk(mutex_);

  while (running_) {
    if (queue_.size() == 0) {
      cv_.wait(lk);
    }

    if (queue_.size() == 0) {
      continue;
    }

    auto now = WallClock::unixMicros();
    if (now < queue_.begin()->first) {
      cv_.wait_for(
          lk,
          std::chrono::microseconds(queue_.begin()->first - now));

      continue;
    }

    auto partition = queue_.begin()->second;
    queue_.erase(queue_.begin());
    auto repl_scheme = repl_scheme_;

    RefPtr<PartitionReplication> repl;
    bool success = true;
    {
      lk.unlock();

      auto snap = partition->getSnapshot();
      replication_info->setPartition(StringUtil::format(
          "$0/$1/$2",
          snap->state.tsdb_namespace(),
          snap->state.table_key(),
          snap->key));

      try {
        repl = partition->getReplicationStrategy(repl_scheme, http_);
        success = repl->replicate(replication_info);
      } catch (const StandardException& e) {
        logError("evqld", e, "ReplicationWorker error");
        success = false;
      }

      replication_info->reset();

      if (!success) {
        auto snap = partition->getSnapshot();

        logError(
            "evqld",
            "Replication failed for partition $0/$1/$2",
            snap->state.tsdb_namespace(),
            snap->state.table_key(),
            snap->key.toString());
      }

      lk.lock();
    }

    if (success) {
      waitset_.erase(partition->uuid());

      repl = partition->getReplicationStrategy(repl_scheme, http_);
      if (repl->needsReplication()) {
        enqueuePartitionWithLock(partition, kReplicationCorkWindowMicros);
      } else {
        repl = partition->getReplicationStrategy(repl_scheme, http_);
        if (repl->shouldDropPartition()) {
          auto snap = partition->getSnapshot();
          auto dropped =
              pmap_->dropLocalPartition(
                  snap->state.tsdb_namespace(),
                  snap->state.table_key(),
                  snap->key);

          if (!dropped) {
            enqueuePartitionWithLock(partition, kReplicationCorkWindowMicros);
          }
        }
      }
    } else {
      auto delay = 30 * kMicrosPerSecond; // FIXPAUL increasing delay..
      queue_.emplace(now + delay, partition);
    }

    z1stats()->replication_queue_length.set(queue_.size());
  }
}

size_t ReplicationWorker::getNumThreads() const {
  return num_replication_threads_;
}

const ReplicationInfo* ReplicationWorker::getReplicationInfo(
    size_t thread_id) const {
  if (thread_id >= num_replication_threads_) {
    return nullptr;
  }

  return &replication_infos_[thread_id];
}

} // namespace eventql
<|endoftext|>