<commit_before>/*
 * transition_qtblend.cpp -- Qt composite transition
 * Copyright (c) 2016 Jean-Baptiste Mardelle <jb@kdenlive.org>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */

#include "common.h"
#include <framework/mlt.h>
#include <stdio.h>
#include <string.h>
#include <QImage>
#include <QPainter>
#include <QTransform>

static int get_image( mlt_frame a_frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable )
{
	int error = 0;
	mlt_frame b_frame = mlt_frame_pop_frame( a_frame );
	mlt_properties b_properties = MLT_FRAME_PROPERTIES( b_frame );
	mlt_properties properties = MLT_FRAME_PROPERTIES( a_frame );
	mlt_transition transition = MLT_TRANSITION( mlt_frame_pop_service( a_frame ) );
	mlt_properties transition_properties = MLT_TRANSITION_PROPERTIES( transition );

	uint8_t *b_image = NULL;
	bool hasAlpha = *format == mlt_image_rgba;
	double opacity = 1.0;
	QTransform transform;
	// reference rect
	mlt_rect rect;

	// Determine length
	mlt_position length = mlt_transition_get_length( transition );
	// Get current position
	mlt_position position =  mlt_transition_get_position( transition, a_frame );

	// Obtain the normalised width and height from the a_frame
	mlt_profile profile = mlt_service_profile( MLT_TRANSITION_SERVICE( transition ) );
	int normalised_width = profile->width;
	int normalised_height = profile->height;
	double consumer_ar = mlt_profile_sar( profile );
	int b_width = mlt_properties_get_int( b_properties, "meta.media.width" );
	int b_height = mlt_properties_get_int( b_properties, "meta.media.height" );
	bool distort = mlt_properties_get_int( transition_properties, "distort" );

	// Check the producer's native format before fetching image
	if (mlt_properties_get_int( b_properties, "format" ) == mlt_image_rgba) {
		hasAlpha = true;
		*format = mlt_image_rgba;
	}

	if ( b_height == 0  || (!distort && ( b_height < *height || b_width < *width) ) )
	{
		b_width = *width;
		b_height = *height;
	}
	double b_ar = mlt_frame_get_aspect_ratio( b_frame );
	double b_dar = b_ar * b_width / b_height;
	rect.w = -1;
	rect.h = -1;

	// Check transform
	if ( mlt_properties_get( transition_properties, "rect" ) )
	{
		rect = mlt_properties_anim_get_rect( transition_properties, "rect", position, length );
		if (::strchr(mlt_properties_get(transition_properties, "rect"), '%')) {
			// We have percentage values, scale to frame size
			rect.x *= *width;
			rect.y *= *height;
			rect.w *= *width;
			rect.h *= *height;
		}
		else
		{
			// Adjust to preview scaling
			double scale = mlt_profile_scale_width(profile, *width);
			if ( scale != 1.0 )
			{
				rect.x *= scale;
				rect.w *= scale;
				if ( distort )
				{
					b_width *= scale;
				}
			}
			scale = mlt_profile_scale_height(profile, *height);
			if ( scale != 1.0 )
			{
				rect.y *= scale;
				rect.h *= scale;
				if ( distort )
				{
					b_height *= scale;
				}
			}
		}

		transform.translate(rect.x, rect.y);
		opacity = rect.o;
		if ( !distort )
		{
			b_width = qMin((int)rect.w, b_width);
			b_height = qMin((int)rect.h, b_height);
			transform.translate( ( rect.w - b_width ) / 2.0, ( rect.h - b_height ) / 2.0 );
		}
		if ( opacity < 1 || rect.x > 0 || rect.y > 0 || ( rect.x + rect.w < *width ) || ( rect.y + rect.w < *height ) )
		{
			// we will process operations on top frame, so also process b_frame
			hasAlpha = true;
		}
	}
	else
	{
		b_height = *height;
		b_width = *width;
	}

	double output_ar = mlt_profile_sar( profile );
	if ( mlt_frame_get_aspect_ratio( b_frame ) == 0 )
	{
		mlt_frame_set_aspect_ratio( b_frame, output_ar );
	}

	if ( mlt_properties_get( transition_properties, "rotation" ) )
	{
		double angle = mlt_properties_anim_get_double( transition_properties, "rotation", position, length );
		if (angle != 0.0) {
			if ( mlt_properties_get_int( transition_properties, "rotate_center" ) )
			{
				transform.translate( rect.w / 2.0, rect.h / 2.0 );
				transform.rotate( angle );
				transform.translate( -rect.w / 2.0, -rect.h / 2.0 );
			}
			else
			{
				transform.rotate( angle );
			}
			hasAlpha = true;
		}
	}

	// This is not a field-aware transform.
	mlt_properties_set_int( b_properties, "consumer.progressive", 1 );

	// Suppress padding and aspect normalization.
	char *interps = mlt_properties_get( properties, "consumer.rescale" );
	if ( interps )
		interps = strdup( interps );

	if ( error )
	{
		return error;
	}
	if ( distort && b_width != 0 && b_height != 0 )
	{
		transform.scale( rect.w / b_width, rect.h / b_height );
	}
	if ( rect.w == -1 )
	{
		// No transform, request profile sized image
		if (b_dar != mlt_profile_dar( profile ) )
		{
			// Activate transparency if the clips don't have the same aspect ratio
			hasAlpha = true;
		}
	}
	if ( !hasAlpha && ( mlt_properties_get_int( transition_properties, "compositing" ) != 0 || b_width < *width || b_height < *height ) )
	{
		hasAlpha = true;
	}

	// Check if we have transparency
	int request_width = b_width;
	int request_height = b_height;
	bool imageFetched = false;
	if ( !hasAlpha || *format == mlt_image_rgba )
	{
		// fetch image
		error = mlt_frame_get_image( b_frame, &b_image, format, &b_width, &b_height, 0 );
		bool imageFetched = true;
		if ( !hasAlpha && ( *format == mlt_image_rgba || mlt_frame_get_alpha( b_frame ) ) )
		{
			hasAlpha = true;
		}
		hasAlpha = hasAlpha && !mlt_image_rgba_opaque( b_image, b_width, b_height );
	}
	if ( !hasAlpha )
	{
		// Prepare output image
		if ( b_frame->convert_image && ( b_width != request_width || b_height != request_height ) )
		{
			mlt_properties_set_int( b_properties, "convert_image_width", request_width );
			mlt_properties_set_int( b_properties, "convert_image_height", request_height );
			b_frame->convert_image( b_frame, &b_image, format, *format );
			*width = request_width;
			*height = request_height;
		}
		else
		{
			*width = b_width;
			*height = b_height;
		}
		*image = b_image;
		free( interps );
		return 0;
	}

	if ( !imageFetched )
	{
		*format = mlt_image_rgba;
		error = mlt_frame_get_image( b_frame, &b_image, format, &b_width, &b_height, 0 );
	}
	if ( b_frame->convert_image && ( *format != mlt_image_rgba || b_width != request_width || b_height != request_height ) )
	{
		mlt_properties_set_int( b_properties, "convert_image_width", request_width );
		mlt_properties_set_int( b_properties, "convert_image_height", request_height );
		b_frame->convert_image( b_frame, &b_image, format, mlt_image_rgba );
		b_width = request_width;
		b_height = request_height;
	}
	*format = mlt_image_rgba;

	// Get bottom frame
	uint8_t *a_image = NULL;
	error = mlt_frame_get_image( a_frame, &a_image, format, width, height, 0 );
	if (error)
	{
		free( interps );
		return error;
	}
	// Prepare output image
	int image_size = mlt_image_format_size( *format, *width, *height, NULL );
	*image = (uint8_t *) mlt_pool_alloc( image_size );

	// Copy bottom frame in output
	memcpy( *image, a_image, image_size );

	bool hqPainting = false;
	if ( interps )
	{
		if ( strcmp( interps, "bilinear" ) == 0 || strcmp( interps, "bicubic" ) == 0 )
		{
			hqPainting = true;
		}
	}

	// convert bottom mlt image to qimage
	QImage bottomImg;
	convert_mlt_to_qimage_rgba( *image, &bottomImg, *width, *height );

	// convert top mlt image to qimage
	QImage topImg;
	convert_mlt_to_qimage_rgba( b_image, &topImg, b_width, b_height );

	// setup Qt drawing
	QPainter painter( &bottomImg );
	painter.setCompositionMode( ( QPainter::CompositionMode ) mlt_properties_get_int( transition_properties, "compositing" ) );
	painter.setRenderHints( QPainter::Antialiasing | QPainter::SmoothPixmapTransform, hqPainting );
	painter.setTransform(transform);
	painter.setOpacity(opacity);

	// Composite top frame
	painter.drawImage(0, 0, topImg);

	// finish Qt drawing
	painter.end();
	convert_qimage_to_mlt_rgba( &bottomImg, *image, *width, *height );
	mlt_frame_set_image( a_frame, *image, image_size, mlt_pool_release);
	// Remove potentially large image on the B frame.
	mlt_frame_set_image( b_frame, NULL, 0, NULL );
	free( interps );
	return error;
}

static mlt_frame process( mlt_transition transition, mlt_frame a_frame, mlt_frame b_frame )
{
	mlt_frame_push_service( a_frame, transition );
	mlt_frame_push_frame( a_frame, b_frame );
	mlt_frame_push_get_image( a_frame, get_image );
	return a_frame;
}

extern "C" {

mlt_transition transition_qtblend_init( mlt_profile profile, mlt_service_type type, const char *id, void *arg )
{
	mlt_transition transition = mlt_transition_new();

	if ( transition )
	{
		mlt_properties properties = MLT_TRANSITION_PROPERTIES( transition );

		if ( !createQApplicationIfNeeded( MLT_TRANSITION_SERVICE(transition) ) )
		{
			mlt_transition_close( transition );
			return NULL;
		}
		transition->process = process;
		mlt_properties_set_int( properties, "_transition_type", 1 ); // video only
		mlt_properties_set( properties, "rect", (char *) arg );
		mlt_properties_set_int( properties, "compositing", 0 );
		mlt_properties_set_int( properties, "distort", 0 );
		mlt_properties_set_int( properties, "rotate_center", 0 );
	}

	return transition;
}

} // extern "C"
<commit_msg>Fix qtblend with images using different aspect ratio<commit_after>/*
 * transition_qtblend.cpp -- Qt composite transition
 * Copyright (c) 2016 Jean-Baptiste Mardelle <jb@kdenlive.org>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */

#include "common.h"
#include <framework/mlt.h>
#include <stdio.h>
#include <string.h>
#include <QImage>
#include <QPainter>
#include <QTransform>

static int get_image( mlt_frame a_frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable )
{
	int error = 0;
	mlt_frame b_frame = mlt_frame_pop_frame( a_frame );
	mlt_properties b_properties = MLT_FRAME_PROPERTIES( b_frame );
	mlt_properties properties = MLT_FRAME_PROPERTIES( a_frame );
	mlt_transition transition = MLT_TRANSITION( mlt_frame_pop_service( a_frame ) );
	mlt_properties transition_properties = MLT_TRANSITION_PROPERTIES( transition );

	uint8_t *b_image = NULL;
	bool hasAlpha = *format == mlt_image_rgba;
	double opacity = 1.0;
	QTransform transform;
	// reference rect
	mlt_rect rect;

	// Determine length
	mlt_position length = mlt_transition_get_length( transition );
	// Get current position
	mlt_position position =  mlt_transition_get_position( transition, a_frame );

	// Obtain the normalised width and height from the a_frame
	mlt_profile profile = mlt_service_profile( MLT_TRANSITION_SERVICE( transition ) );
	int normalised_width = profile->width;
	int normalised_height = profile->height;
	double consumer_ar = mlt_profile_sar( profile );
	int b_width = mlt_properties_get_int( b_properties, "meta.media.width" );
	int b_height = mlt_properties_get_int( b_properties, "meta.media.height" );
	bool distort = mlt_properties_get_int( transition_properties, "distort" );

	// Check the producer's native format before fetching image
	if (mlt_properties_get_int( b_properties, "format" ) == mlt_image_rgba) {
		hasAlpha = true;
		*format = mlt_image_rgba;
	}

	if ( b_height == 0 )
	{
		b_width = *width;
		b_height = *height;
	}
	double b_ar = mlt_frame_get_aspect_ratio( b_frame );
	double b_dar = b_ar * b_width / b_height;
	rect.w = -1;
	rect.h = -1;
	if ( !distort && ( b_height < *height || b_width < *width) )
	{
		b_width = *width;
		b_height = *height;
	}

	// Check transform
	if ( mlt_properties_get( transition_properties, "rect" ) )
	{
		rect = mlt_properties_anim_get_rect( transition_properties, "rect", position, length );
		if (::strchr(mlt_properties_get(transition_properties, "rect"), '%')) {
			// We have percentage values, scale to frame size
			rect.x *= *width;
			rect.y *= *height;
			rect.w *= *width;
			rect.h *= *height;
		}
		else
		{
			// Adjust to preview scaling
			double scale = mlt_profile_scale_width(profile, *width);
			if ( scale != 1.0 )
			{
				rect.x *= scale;
				rect.w *= scale;
				if ( distort )
				{
					b_width *= scale;
				}
			}
			scale = mlt_profile_scale_height(profile, *height);
			if ( scale != 1.0 )
			{
				rect.y *= scale;
				rect.h *= scale;
				if ( distort )
				{
					b_height *= scale;
				}
			}
		}

		transform.translate(rect.x, rect.y);
		opacity = rect.o;
		if ( !distort )
		{
			b_width = qMin((int)rect.w, b_width);
			b_height = qMin((int)rect.h, b_height);
			transform.translate( ( rect.w - b_width ) / 2.0, ( rect.h - b_height ) / 2.0 );
		}
		if ( opacity < 1 || rect.x > 0 || rect.y > 0 || ( rect.x + rect.w < *width ) || ( rect.y + rect.w < *height ) )
		{
			// we will process operations on top frame, so also process b_frame
			hasAlpha = true;
		}
	}
	else
	{
		b_height = *height;
		b_width = *width;
	}

	double output_ar = mlt_profile_sar( profile );
	if ( mlt_frame_get_aspect_ratio( b_frame ) == 0 )
	{
		mlt_frame_set_aspect_ratio( b_frame, output_ar );
	}

	if ( mlt_properties_get( transition_properties, "rotation" ) )
	{
		double angle = mlt_properties_anim_get_double( transition_properties, "rotation", position, length );
		if (angle != 0.0) {
			if ( mlt_properties_get_int( transition_properties, "rotate_center" ) )
			{
				transform.translate( rect.w / 2.0, rect.h / 2.0 );
				transform.rotate( angle );
				transform.translate( -rect.w / 2.0, -rect.h / 2.0 );
			}
			else
			{
				transform.rotate( angle );
			}
			hasAlpha = true;
		}
	}

	// This is not a field-aware transform.
	mlt_properties_set_int( b_properties, "consumer.progressive", 1 );

	// Suppress padding and aspect normalization.
	char *interps = mlt_properties_get( properties, "consumer.rescale" );
	if ( interps )
		interps = strdup( interps );

	if ( error )
	{
		return error;
	}
	if ( distort && b_width != 0 && b_height != 0 )
	{
		transform.scale( rect.w / b_width, rect.h / b_height );
	}
	if ( rect.w == -1 )
	{
		// No transform, request profile sized image
		if (b_dar != mlt_profile_dar( profile ) )
		{
			// Activate transparency if the clips don't have the same aspect ratio
			hasAlpha = true;
		}
	}
	if ( !hasAlpha && ( mlt_properties_get_int( transition_properties, "compositing" ) != 0 || b_width < *width || b_height < *height ) )
	{
		hasAlpha = true;
	}

	// Check if we have transparency
	int request_width = b_width;
	int request_height = b_height;
	bool imageFetched = false;
	if ( !hasAlpha || *format == mlt_image_rgba )
	{
		// fetch image
		error = mlt_frame_get_image( b_frame, &b_image, format, &b_width, &b_height, 0 );
		bool imageFetched = true;
		if ( !hasAlpha && ( *format == mlt_image_rgba || mlt_frame_get_alpha( b_frame ) ) )
		{
			hasAlpha = true;
		}
		hasAlpha = hasAlpha && !mlt_image_rgba_opaque( b_image, b_width, b_height );
	}
	if ( !hasAlpha )
	{
		// Prepare output image
		if ( b_frame->convert_image && ( b_width != request_width || b_height != request_height ) )
		{
			mlt_properties_set_int( b_properties, "convert_image_width", request_width );
			mlt_properties_set_int( b_properties, "convert_image_height", request_height );
			b_frame->convert_image( b_frame, &b_image, format, *format );
			*width = request_width;
			*height = request_height;
		}
		else
		{
			*width = b_width;
			*height = b_height;
		}
		*image = b_image;
		free( interps );
		return 0;
	}

	if ( !imageFetched )
	{
		*format = mlt_image_rgba;
		error = mlt_frame_get_image( b_frame, &b_image, format, &b_width, &b_height, 0 );
	}
	if ( b_frame->convert_image && ( *format != mlt_image_rgba || b_width != request_width || b_height != request_height ) )
	{
		mlt_properties_set_int( b_properties, "convert_image_width", request_width );
		mlt_properties_set_int( b_properties, "convert_image_height", request_height );
		b_frame->convert_image( b_frame, &b_image, format, mlt_image_rgba );
		b_width = request_width;
		b_height = request_height;
	}
	*format = mlt_image_rgba;

	// Get bottom frame
	uint8_t *a_image = NULL;
	error = mlt_frame_get_image( a_frame, &a_image, format, width, height, 0 );
	if (error)
	{
		free( interps );
		return error;
	}
	// Prepare output image
	int image_size = mlt_image_format_size( *format, *width, *height, NULL );
	*image = (uint8_t *) mlt_pool_alloc( image_size );

	// Copy bottom frame in output
	memcpy( *image, a_image, image_size );

	bool hqPainting = false;
	if ( interps )
	{
		if ( strcmp( interps, "bilinear" ) == 0 || strcmp( interps, "bicubic" ) == 0 )
		{
			hqPainting = true;
		}
	}

	// convert bottom mlt image to qimage
	QImage bottomImg;
	convert_mlt_to_qimage_rgba( *image, &bottomImg, *width, *height );

	// convert top mlt image to qimage
	QImage topImg;
	convert_mlt_to_qimage_rgba( b_image, &topImg, b_width, b_height );

	// setup Qt drawing
	QPainter painter( &bottomImg );
	painter.setCompositionMode( ( QPainter::CompositionMode ) mlt_properties_get_int( transition_properties, "compositing" ) );
	painter.setRenderHints( QPainter::Antialiasing | QPainter::SmoothPixmapTransform, hqPainting );
	painter.setTransform(transform);
	painter.setOpacity(opacity);

	// Composite top frame
	painter.drawImage(0, 0, topImg);

	// finish Qt drawing
	painter.end();
	convert_qimage_to_mlt_rgba( &bottomImg, *image, *width, *height );
	mlt_frame_set_image( a_frame, *image, image_size, mlt_pool_release);
	// Remove potentially large image on the B frame.
	mlt_frame_set_image( b_frame, NULL, 0, NULL );
	free( interps );
	return error;
}

static mlt_frame process( mlt_transition transition, mlt_frame a_frame, mlt_frame b_frame )
{
	mlt_frame_push_service( a_frame, transition );
	mlt_frame_push_frame( a_frame, b_frame );
	mlt_frame_push_get_image( a_frame, get_image );
	return a_frame;
}

extern "C" {

mlt_transition transition_qtblend_init( mlt_profile profile, mlt_service_type type, const char *id, void *arg )
{
	mlt_transition transition = mlt_transition_new();

	if ( transition )
	{
		mlt_properties properties = MLT_TRANSITION_PROPERTIES( transition );

		if ( !createQApplicationIfNeeded( MLT_TRANSITION_SERVICE(transition) ) )
		{
			mlt_transition_close( transition );
			return NULL;
		}
		transition->process = process;
		mlt_properties_set_int( properties, "_transition_type", 1 ); // video only
		mlt_properties_set( properties, "rect", (char *) arg );
		mlt_properties_set_int( properties, "compositing", 0 );
		mlt_properties_set_int( properties, "distort", 0 );
		mlt_properties_set_int( properties, "rotate_center", 0 );
	}

	return transition;
}

} // extern "C"
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2004-2012  See the AUTHORS file for details.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 as published
 * by the Free Software Foundation.
 */

#include "znc/ZNCDebug.h"
#include "znc/FileUtils.h"
#include "znc/Config.h"
#include <cstdlib>

class CConfigTest {
public:
	CConfigTest(const CString& sConfig) : m_sConfig(sConfig) { }
	virtual ~CConfigTest() { m_File.Delete(); }

	virtual bool Test() = 0;

protected:
	CFile& WriteFile() {
		char sName[] = "./temp-XXXXXX";
		int fd = mkstemp(sName);
		m_File.Open(sName, O_RDWR);
		close(fd);

		m_File.Write(m_sConfig);

		return m_File;
	}

private:
	CFile   m_File;
	CString m_sConfig;
};

class CConfigErrorTest : public CConfigTest {
public:
	CConfigErrorTest(const CString& sConfig, const CString& sError)
		: CConfigTest(sConfig), m_sError(sError) { }

	bool Test() {
		CFile &File = WriteFile();

		CConfig conf;
		CString sError;
		bool res = conf.Parse(File, sError);
		if (res) {
			std::cout << "Didn't error out!\n";
			return false;
		}

		if (sError != m_sError) {
			std::cout << "Wrong error\n Expected: " << m_sError << "\n Got: " << sError << std::endl;
			return false;
		}

		return true;
	}
private:
	CString m_sError;
};

class CConfigSuccessTest : public CConfigTest {
public:
	CConfigSuccessTest(const CString& sConfig, const CString& sExpectedOutput)
		: CConfigTest(sConfig), m_sOutput(sExpectedOutput) { }

	bool Test() {
		CFile &File = WriteFile();
		// Verify that Parse() rewinds the file
		File.Seek(12);

		CConfig conf;
		CString sError;
		bool res = conf.Parse(File, sError);
		if (!res) {
			std::cout << "Error'd out! (" + sError + ")\n";
			return false;
		}
		if (!sError.empty()) {
			std::cout << "Non-empty error string!\n";
			return false;
		}

		CString sOutput;
		ToString(sOutput, conf);

		if (sOutput != m_sOutput) {
			std::cout << "Wrong output\n Expected: " << m_sOutput << "\n Got: " << sOutput << std::endl;
			return false;
		}

		return true;
	}

	void ToString(CString& sRes, CConfig& conf) {
		CConfig::EntryMapIterator it = conf.BeginEntries();
		while (it != conf.EndEntries()) {
			const CString& sKey = it->first;
			const VCString& vsEntries = it->second;
			VCString::const_iterator i = vsEntries.begin();
			if (i == vsEntries.end())
				sRes += sKey + " <- Error, empty list!\n";
			else
				while (i != vsEntries.end()) {
					sRes += sKey + "=" + *i + "\n";
					i++;
				}
			++it;
		}

		CConfig::SubConfigMapIterator it2 = conf.BeginSubConfigs();
		while (it2 != conf.EndSubConfigs()) {
			map<CString, CConfigEntry>::const_iterator it3 = it2->second.begin();

			while (it3 != it2->second.end()) {
				sRes += "->" + it2->first + "/" + it3->first + "\n";
				ToString(sRes, *it3->second.m_pSubConfig);
				sRes += "<-\n";
				++it3;
			}

			++it2;
		}
	}

private:
	CString m_sOutput;
};

int main() {
#define TEST_ERROR(a, b) new CConfigErrorTest(a, b)
#define TEST_SUCCESS(a, b) new CConfigSuccessTest(a, b)
#define ARRAY_SIZE(a) (sizeof(a)/(sizeof((a)[0])))
	CConfigTest *tests[] = {
		TEST_SUCCESS("", ""),
		/* duplicate entries */
		TEST_SUCCESS("Foo = bar\nFoo = baz\n", "foo=bar\nfoo=baz\n"),
		TEST_SUCCESS("Foo = baz\nFoo = bar\n", "foo=baz\nfoo=bar\n"),
		/* sub configs */
		TEST_ERROR("</foo>", "Error on line 1: Closing tag \"foo\" which is not open."),
		TEST_ERROR("<foo a>\n</bar>\n", "Error on line 2: Closing tag \"bar\" which is not open."),
		TEST_ERROR("<foo bar>", "Error on line 1: Not all tags are closed at the end of the file. Inner-most open tag is \"foo\"."),
		TEST_ERROR("<foo>\n</foo>", "Error on line 1: Empty block name at begin of block."),
		TEST_ERROR("<foo 1>\n</foo>\n<foo 1>\n</foo>", "Error on line 4: Duplicate entry for tag \"foo\" name \"1\"."),
		TEST_SUCCESS("<foo a>\n</foo>", "->foo/a\n<-\n"),
		TEST_SUCCESS("\n  <c d>\n </c>\n", "->a/b\n->c/d\n<-\n<-\n"),
		TEST_SUCCESS(" \t \nfoo = bar\n\tFooO = bar\n", "->a/B\nfoo=bar\nfooo=bar\n<-\n"),
		/* comments */
		TEST_SUCCESS("Foo = bar // baz\n// Bar = baz", "foo=bar // baz\n"),
		TEST_SUCCESS("Foo = bar /* baz */\n/*** Foo = baz ***/\n   /**** asdsdfdf \n Some quite invalid stuff ***/\n", "foo=bar /* baz */\n"),
		TEST_ERROR("<foo foo>\n/* Just a comment\n</foo>", "Error on line 3: Comment not closed at end of file."),
		TEST_SUCCESS("/* Foo\n/* Bar */", ""),
		TEST_SUCCESS("/* Foo\n// */", ""),
	};
	unsigned int i;
	unsigned int failed = 0;

	for (i = 0; i < ARRAY_SIZE(tests); i++) {
		if (!tests[i]->Test())
			failed++;
		delete tests[i];
	}

	return failed;
}
<commit_msg>Fix the tests... again.<commit_after>/*
 * Copyright (C) 2004-2012  See the AUTHORS file for details.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 as published
 * by the Free Software Foundation.
 */

#include "znc/ZNCDebug.h"
#include "znc/FileUtils.h"
#include "znc/Config.h"
#include <cstdlib>

class CConfigTest {
public:
	CConfigTest(const CString& sConfig) : m_sConfig(sConfig) { }
	virtual ~CConfigTest() { m_File.Delete(); }

	virtual bool Test() = 0;

protected:
	CFile& WriteFile() {
		char sName[] = "./temp-XXXXXX";
		int fd = mkstemp(sName);
		m_File.Open(sName, O_RDWR);
		close(fd);

		m_File.Write(m_sConfig);

		return m_File;
	}

private:
	CFile   m_File;
	CString m_sConfig;
};

class CConfigErrorTest : public CConfigTest {
public:
	CConfigErrorTest(const CString& sConfig, const CString& sError)
		: CConfigTest(sConfig), m_sError(sError) { }

	bool Test() {
		CFile &File = WriteFile();

		CConfig conf;
		CString sError;
		bool res = conf.Parse(File, sError);
		if (res) {
			std::cout << "Didn't error out!\n";
			return false;
		}

		if (sError != m_sError) {
			std::cout << "Wrong error\n Expected: " << m_sError << "\n Got: " << sError << std::endl;
			return false;
		}

		return true;
	}
private:
	CString m_sError;
};

class CConfigSuccessTest : public CConfigTest {
public:
	CConfigSuccessTest(const CString& sConfig, const CString& sExpectedOutput)
		: CConfigTest(sConfig), m_sOutput(sExpectedOutput) { }

	bool Test() {
		CFile &File = WriteFile();
		// Verify that Parse() rewinds the file
		File.Seek(12);

		CConfig conf;
		CString sError;
		bool res = conf.Parse(File, sError);
		if (!res) {
			std::cout << "Error'd out! (" + sError + ")\n";
			return false;
		}
		if (!sError.empty()) {
			std::cout << "Non-empty error string!\n";
			return false;
		}

		CString sOutput;
		ToString(sOutput, conf);

		if (sOutput != m_sOutput) {
			std::cout << "Wrong output\n Expected: " << m_sOutput << "\n Got: " << sOutput << std::endl;
			return false;
		}

		return true;
	}

	void ToString(CString& sRes, CConfig& conf) {
		CConfig::EntryMapIterator it = conf.BeginEntries();
		while (it != conf.EndEntries()) {
			const CString& sKey = it->first;
			const VCString& vsEntries = it->second;
			VCString::const_iterator i = vsEntries.begin();
			if (i == vsEntries.end())
				sRes += sKey + " <- Error, empty list!\n";
			else
				while (i != vsEntries.end()) {
					sRes += sKey + "=" + *i + "\n";
					i++;
				}
			++it;
		}

		CConfig::SubConfigMapIterator it2 = conf.BeginSubConfigs();
		while (it2 != conf.EndSubConfigs()) {
			std::map<CString, CConfigEntry>::const_iterator it3 = it2->second.begin();

			while (it3 != it2->second.end()) {
				sRes += "->" + it2->first + "/" + it3->first + "\n";
				ToString(sRes, *it3->second.m_pSubConfig);
				sRes += "<-\n";
				++it3;
			}

			++it2;
		}
	}

private:
	CString m_sOutput;
};

int main() {
#define TEST_ERROR(a, b) new CConfigErrorTest(a, b)
#define TEST_SUCCESS(a, b) new CConfigSuccessTest(a, b)
#define ARRAY_SIZE(a) (sizeof(a)/(sizeof((a)[0])))
	CConfigTest *tests[] = {
		TEST_SUCCESS("", ""),
		/* duplicate entries */
		TEST_SUCCESS("Foo = bar\nFoo = baz\n", "foo=bar\nfoo=baz\n"),
		TEST_SUCCESS("Foo = baz\nFoo = bar\n", "foo=baz\nfoo=bar\n"),
		/* sub configs */
		TEST_ERROR("</foo>", "Error on line 1: Closing tag \"foo\" which is not open."),
		TEST_ERROR("<foo a>\n</bar>\n", "Error on line 2: Closing tag \"bar\" which is not open."),
		TEST_ERROR("<foo bar>", "Error on line 1: Not all tags are closed at the end of the file. Inner-most open tag is \"foo\"."),
		TEST_ERROR("<foo>\n</foo>", "Error on line 1: Empty block name at begin of block."),
		TEST_ERROR("<foo 1>\n</foo>\n<foo 1>\n</foo>", "Error on line 4: Duplicate entry for tag \"foo\" name \"1\"."),
		TEST_SUCCESS("<foo a>\n</foo>", "->foo/a\n<-\n"),
		TEST_SUCCESS("\n  <c d>\n </c>\n", "->a/b\n->c/d\n<-\n<-\n"),
		TEST_SUCCESS(" \t \nfoo = bar\n\tFooO = bar\n", "->a/B\nfoo=bar\nfooo=bar\n<-\n"),
		/* comments */
		TEST_SUCCESS("Foo = bar // baz\n// Bar = baz", "foo=bar // baz\n"),
		TEST_SUCCESS("Foo = bar /* baz */\n/*** Foo = baz ***/\n   /**** asdsdfdf \n Some quite invalid stuff ***/\n", "foo=bar /* baz */\n"),
		TEST_ERROR("<foo foo>\n/* Just a comment\n</foo>", "Error on line 3: Comment not closed at end of file."),
		TEST_SUCCESS("/* Foo\n/* Bar */", ""),
		TEST_SUCCESS("/* Foo\n// */", ""),
	};
	unsigned int i;
	unsigned int failed = 0;

	for (i = 0; i < ARRAY_SIZE(tests); i++) {
		if (!tests[i]->Test())
			failed++;
		delete tests[i];
	}

	return failed;
}
<|endoftext|>


<commit_before>AliAnalysisTask *AddTaskTender(Bool_t useV0=kFALSE){
  //get the current analysis manager
  Bool_t checkEvtSelection = useV0;
  AliAnalysisManager *mgr = AliAnalysisManager::GetAnalysisManager();
  if (!mgr) {
    Error("AddTask_tender_Tender", "No analysis manager found.");
    return 0;
  }
  // currently don't accept AOD input
  if (!mgr->GetInputEventHandler()->InheritsFrom(AliESDInputHandler::Class())) {
    Error("AddTask_tender_Tender","The analysis tender only works with ESD input!");
    return 0;
  }

  //========= Add tender to the ANALYSIS manager and set default storage =====
  AliTender *tender=new AliTender("AnalysisTender");
  tender->SetCheckEventSelection(checkEvtSelection);
//  tender->SetDefaultCDBStorage("raw://");
  tender->SetDefaultCDBStorage("alien://?Folder=/alice/data/2010/OCDB");
  mgr->AddTask(tender);
  if (checkEvtSelection) {
     if (mgr->GetTasks()->First() != (TObject*)tender) {
        ::Error("When setting the tender to check the event selection, it has to be the first wagon ! Aborting.");
        return NULL;
     }
  }

  //========= Attach VZERO supply ======
  if (useV0) {
     AliVZEROTenderSupply *vzeroSupply=new AliVZEROTenderSupply("VZEROtender");
     vzeroSupply->SetDebug(kFALSE);
     tender->AddSupply(vzeroSupply);
  }
  //========= Attach TPC supply ======
  AliTPCTenderSupply *tpcSupply=new AliTPCTenderSupply("TPCtender");
  tpcSupply->SetDebugLevel(2);
  //tpcSupply->SetMip(50.);
  tender->AddSupply(tpcSupply);

  //========= Attach TOF supply ======
  AliTOFTenderSupply *tofTender = new AliTOFTenderSupply("TOFtender");
  tofTender->SetTimeZeroType(AliESDpid::kTOF_T0);
  tender->AddSupply(tofTender);

  //========= Attach TRD supply ======
  AliTRDTenderSupply *trdSupply=new AliTRDTenderSupply("TRDtender");
  tender->AddSupply(trdSupply);

  //========= Attach PID supply ======
  tender->AddSupply(new AliPIDTenderSupply("PIDtender"));

  //========= Attach Primary Vertex supply ======
  tender->AddSupply(new AliVtxTenderSupply("PriVtxtender"));

  //================================================
  //              data containers
  //================================================

    //            define output containers, please use 'username'_'somename'
  AliAnalysisDataContainer *coutput1 =
      mgr->CreateContainer("tender_event", AliESDEvent::Class(),
                           AliAnalysisManager::kExchangeContainer,"default_tender");

  //           connect containers
  mgr->ConnectInput  (tender,  0, mgr->GetCommonInputContainer() );
  mgr->ConnectOutput (tender,  1, coutput1);

  return tender;
}
<commit_msg>switched back to raw:// CDB<commit_after>AliAnalysisTask *AddTaskTender(Bool_t useV0=kFALSE){
  //get the current analysis manager
  Bool_t checkEvtSelection = useV0;
  AliAnalysisManager *mgr = AliAnalysisManager::GetAnalysisManager();
  if (!mgr) {
    Error("AddTask_tender_Tender", "No analysis manager found.");
    return 0;
  }
  // currently don't accept AOD input
  if (!mgr->GetInputEventHandler()->InheritsFrom(AliESDInputHandler::Class())) {
    Error("AddTask_tender_Tender","The analysis tender only works with ESD input!");
    return 0;
  }

  //========= Add tender to the ANALYSIS manager and set default storage =====
  AliTender *tender=new AliTender("AnalysisTender");
  tender->SetCheckEventSelection(checkEvtSelection);
  tender->SetDefaultCDBStorage("raw://");
  mgr->AddTask(tender);
  if (checkEvtSelection) {
     if (mgr->GetTasks()->First() != (TObject*)tender) {
        ::Error("When setting the tender to check the event selection, it has to be the first wagon ! Aborting.");
        return NULL;
     }
  }

  //========= Attach VZERO supply ======
  if (useV0) {
     AliVZEROTenderSupply *vzeroSupply=new AliVZEROTenderSupply("VZEROtender");
     vzeroSupply->SetDebug(kFALSE);
     tender->AddSupply(vzeroSupply);
  }
  //========= Attach TPC supply ======
  AliTPCTenderSupply *tpcSupply=new AliTPCTenderSupply("TPCtender");
  tpcSupply->SetDebugLevel(2);
  //tpcSupply->SetMip(50.);
  tender->AddSupply(tpcSupply);

  //========= Attach TOF supply ======
  AliTOFTenderSupply *tofTender = new AliTOFTenderSupply("TOFtender");
  tofTender->SetTimeZeroType(AliESDpid::kTOF_T0);
  tender->AddSupply(tofTender);

  //========= Attach TRD supply ======
  AliTRDTenderSupply *trdSupply=new AliTRDTenderSupply("TRDtender");
  tender->AddSupply(trdSupply);

  //========= Attach PID supply ======
  tender->AddSupply(new AliPIDTenderSupply("PIDtender"));

  //========= Attach Primary Vertex supply ======
  tender->AddSupply(new AliVtxTenderSupply("PriVtxtender"));

  //================================================
  //              data containers
  //================================================

    //            define output containers, please use 'username'_'somename'
  AliAnalysisDataContainer *coutput1 =
      mgr->CreateContainer("tender_event", AliESDEvent::Class(),
                           AliAnalysisManager::kExchangeContainer,"default_tender");

  //           connect containers
  mgr->ConnectInput  (tender,  0, mgr->GetCommonInputContainer() );
  mgr->ConnectOutput (tender,  1, coutput1);

  return tender;
}
<|endoftext|>


<commit_before>#include "joedb/io/merge.h"
#include "joedb/interpreter/Database.h"

#include "gtest/gtest.h"

using namespace joedb;

/////////////////////////////////////////////////////////////////////////////
TEST(Merge_Test, merge_test)
/////////////////////////////////////////////////////////////////////////////
{
 const int databases = 3;
 Database db[databases];

 Table_Id person;
 Table_Id city;

 Field_Id person_name;
 Field_Id person_home;
 Field_Id city_name;

 for (int i = databases; --i >= 0;)
 {
  db[i].create_table("person");
  db[i].create_table("city");

  person = db[i].find_table("person");
  city = db[i].find_table("city");

  db[i].add_field(person, "name", Type::string());
  db[i].add_field(person, "home", Type::reference(city));
  db[i].add_field(city, "name", Type::string());

  person_name = db[i].find_field(person, "name");
  person_home = db[i].find_field(person, "home");
  city_name = db[i].find_field(city, "name");
 }

 db[1].insert_into(city, 1);
 db[1].update_string(city, 1, city_name, "Lille");
 db[1].insert_into(city, 2);
 db[1].update_string(city, 2, city_name, "Maubeuge");
 db[1].insert_into(person, 1);
 db[1].update_string(person, 1, person_name, "Toto");
 db[1].update_reference(person, 1, person_home, 1);

 db[2].insert_into(city, 1);
 db[2].update_string(city, 1, city_name, "Paris");
 db[2].insert_into(person, 1);
 db[2].update_string(person, 1, person_name, "Titi");
 db[2].update_reference(person, 1, person_home, 1);
 db[2].insert_into(person, 2);
 db[2].update_string(person, 2, person_name, "Tutu");
 db[2].update_reference(person, 2, person_home, 0);

 merge(db[0], db[1]);
 merge(db[0], db[2]);

 EXPECT_EQ(db[0].get_last_record_id(city), 3UL);
 EXPECT_EQ(db[0].get_last_record_id(person), 3UL);
 EXPECT_EQ(db[0].get_string(city, 1, city_name), "Lille");
 EXPECT_EQ(db[0].get_string(city, 3, city_name), "Paris");
 EXPECT_EQ(db[0].get_reference(person, 1, person_home), 1UL);
 EXPECT_EQ(db[0].get_reference(person, 2, person_home), 3UL);
 EXPECT_EQ(db[0].get_reference(person, 3, person_home), 0UL);
}
<commit_msg>add test: merging with an empty database<commit_after>#include "joedb/io/merge.h"
#include "joedb/interpreter/Database.h"

#include "gtest/gtest.h"

using namespace joedb;

/////////////////////////////////////////////////////////////////////////////
TEST(Merge_Test, merge_test)
/////////////////////////////////////////////////////////////////////////////
{
 const int databases = 4;
 Database db[databases];

 Table_Id person;
 Table_Id city;

 Field_Id person_name;
 Field_Id person_home;
 Field_Id city_name;

 for (int i = databases; --i >= 0;)
 {
  db[i].create_table("person");
  db[i].create_table("city");

  person = db[i].find_table("person");
  city = db[i].find_table("city");

  db[i].add_field(person, "name", Type::string());
  db[i].add_field(person, "home", Type::reference(city));
  db[i].add_field(city, "name", Type::string());

  person_name = db[i].find_field(person, "name");
  person_home = db[i].find_field(person, "home");
  city_name = db[i].find_field(city, "name");
 }

 db[1].insert_into(city, 1);
 db[1].update_string(city, 1, city_name, "Lille");
 db[1].insert_into(city, 2);
 db[1].update_string(city, 2, city_name, "Maubeuge");
 db[1].insert_into(person, 1);
 db[1].update_string(person, 1, person_name, "Toto");
 db[1].update_reference(person, 1, person_home, 1);

 db[2].insert_into(city, 1);
 db[2].update_string(city, 1, city_name, "Paris");
 db[2].insert_into(person, 1);
 db[2].update_string(person, 1, person_name, "Titi");
 db[2].update_reference(person, 1, person_home, 1);
 db[2].insert_into(person, 2);
 db[2].update_string(person, 2, person_name, "Tutu");
 db[2].update_reference(person, 2, person_home, 0);

 merge(db[0], db[1]);
 merge(db[0], db[2]);
 merge(db[0], db[3]);

 EXPECT_EQ(db[0].get_last_record_id(city), 3UL);
 EXPECT_EQ(db[0].get_last_record_id(person), 3UL);
 EXPECT_EQ(db[0].get_string(city, 1, city_name), "Lille");
 EXPECT_EQ(db[0].get_string(city, 3, city_name), "Paris");
 EXPECT_EQ(db[0].get_reference(person, 1, person_home), 1UL);
 EXPECT_EQ(db[0].get_reference(person, 2, person_home), 3UL);
 EXPECT_EQ(db[0].get_reference(person, 3, person_home), 0UL);
}
<|endoftext|>


<commit_before>/*
	This file is part of solidity.

	solidity is free software: you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.

	solidity is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with solidity.  If not, see <http://www.gnu.org/licenses/>.

	The Implementation originally from https://msdn.microsoft.com/en-us/library/windows/desktop/aa365592(v=vs.85).aspx
*/
/** @file RPCSession.cpp
 * @author Dimtiry Khokhlov <dimitry@ethdev.com>
 * @date 2016
 */

#include <string>
#include <stdio.h>
#include <thread>
#include <libdevcore/CommonData.h>
#include <json/reader.h>
#include <json/writer.h>
#include "RPCSession.h"

using namespace std;
using namespace dev;

IPCSocket::IPCSocket(string const& _path): m_path(_path)
{
#if defined(_WIN32)
	m_socket = CreateFile(
		m_path.c_str(),   // pipe name
		GENERIC_READ |  // read and write access
		GENERIC_WRITE,
		0,              // no sharing
		NULL,           // default security attribute
		OPEN_EXISTING,  // opens existing pipe
		0,              // default attributes
		NULL);          // no template file

	if (m_socket == INVALID_HANDLE_VALUE)
		BOOST_FAIL("Error creating IPC socket object!");

#else
	if (_path.length() >= sizeof(sockaddr_un::sun_path))
		BOOST_FAIL("Error opening IPC: socket path is too long!");

	struct sockaddr_un saun;
	memset(&saun, 0, sizeof(sockaddr_un));
	saun.sun_family = AF_UNIX;
	strcpy(saun.sun_path, _path.c_str());

// http://idletechnology.blogspot.ca/2011/12/unix-domain-sockets-on-osx.html
//
// SUN_LEN() might be optimal, but it seemingly affects the portability,
// with at least Android missing this macro.  Just using the sizeof() for
// structure seemingly works, and would only have the side-effect of
// sending larger-than-required packets over the socket.  Given that this
// code is only used for unit-tests, that approach seems simpler.
#if defined(__APPLE__)
	saun.sun_len = sizeof(struct sockaddr_un);
#endif //  defined(__APPLE__)

	if ((m_socket = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
		BOOST_FAIL("Error creating IPC socket object");

	if (connect(m_socket, reinterpret_cast<struct sockaddr const*>(&saun), sizeof(struct sockaddr_un)) < 0)
		BOOST_FAIL("Error connecting to IPC socket: " << _path);

	m_fp = fdopen(m_socket, "r");
	if (!m_fp)
		BOOST_FAIL("Error opening IPC socket: " << _path);
#endif
}

string IPCSocket::sendRequest(string const& _req)
{
#if defined(_WIN32)
	string returnStr;
	DWORD cbWritten;
	BOOL fSuccess = WriteFile(
		m_socket,               // pipe handle
		_req.c_str(),           // message
		_req.size(),            // message length
		&cbWritten,             // bytes written
		NULL);                  // not overlapped

	if (!fSuccess)
		BOOST_FAIL("WriteFile to pipe failed");

	DWORD  cbRead;
	TCHAR  chBuf[c_buffsize];

	// Read from the pipe.
	fSuccess = ReadFile(
		m_socket,  // pipe handle
		chBuf,     // buffer to receive reply
		c_buffsize,// size of buffer
		&cbRead,   // number of bytes read
		NULL);     // not overlapped

	returnStr += chBuf;

	if (!fSuccess)
		BOOST_FAIL("ReadFile from pipe failed");

	// This is needed for Appveyor, otherwise it may terminate
	// the session due to the inactivity.
	cerr << ".";
	return returnStr;
#else
	send(m_socket, _req.c_str(), _req.length(), 0);

	char c;
	string response;
	while ((c = fgetc(m_fp)) != EOF)
	{
		if (c != '\n')
			response += c;
		else
			break;
	}
	return response;
#endif
}

RPCSession& RPCSession::instance(const string& _path)
{
	static RPCSession session(_path);
	BOOST_REQUIRE_EQUAL(session.m_ipcSocket.path(), _path);
	return session;
}

string RPCSession::eth_getCode(string const& _address, string const& _blockNumber)
{
	return rpcCall("eth_getCode", { quote(_address), quote(_blockNumber) }).asString();
}

RPCSession::TransactionReceipt RPCSession::eth_getTransactionReceipt(string const& _transactionHash)
{
	TransactionReceipt receipt;
	Json::Value const result = rpcCall("eth_getTransactionReceipt", { quote(_transactionHash) });
	BOOST_REQUIRE(!result.isNull());
	receipt.gasUsed = result["gasUsed"].asString();
	receipt.contractAddress = result["contractAddress"].asString();
	for (auto const& log: result["logs"])
	{
		LogEntry entry;
		entry.address = log["address"].asString();
		entry.data = log["data"].asString();
		for (auto const& topic: log["topics"])
			entry.topics.push_back(topic.asString());
		receipt.logEntries.push_back(entry);
	}
	return receipt;
}

string RPCSession::eth_sendTransaction(TransactionData const& _td)
{
	return rpcCall("eth_sendTransaction", { _td.toJson() }).asString();
}

string RPCSession::eth_call(TransactionData const& _td, string const& _blockNumber)
{
	return rpcCall("eth_call", { _td.toJson(), quote(_blockNumber) }).asString();
}

string RPCSession::eth_sendTransaction(string const& _transaction)
{
	return rpcCall("eth_sendTransaction", { _transaction }).asString();
}

string RPCSession::eth_getBalance(string const& _address, string const& _blockNumber)
{
	string address = (_address.length() == 20) ? "0x" + _address : _address;
	return rpcCall("eth_getBalance", { quote(address), quote(_blockNumber) }).asString();
}

string RPCSession::eth_getStorageRoot(string const& _address, string const& _blockNumber)
{
	string address = (_address.length() == 20) ? "0x" + _address : _address;
	return rpcCall("eth_getStorageRoot", { quote(address), quote(_blockNumber) }).asString();
}

void RPCSession::personal_unlockAccount(string const& _address, string const& _password, int _duration)
{
	BOOST_REQUIRE(rpcCall("personal_unlockAccount", { quote(_address), quote(_password), to_string(_duration) }) == true);
}

string RPCSession::personal_newAccount(string const& _password)
{
	return rpcCall("personal_newAccount", { quote(_password) }).asString();
}

void RPCSession::test_setChainParams(vector<string> const& _accounts)
{
	static std::string const c_configString = R"(
	{
		"sealEngine": "NoProof",
		"params": {
			"accountStartNonce": "0x",
			"maximumExtraDataSize": "0x1000000",
			"blockReward": "0x",
			"allowFutureBlocks": "1"
		},
		"genesis": {
			"author": "0000000000000010000000000000000000000000",
			"timestamp": "0x00",
			"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
			"extraData": "0x",
			"gasLimit": "0x1000000000000"
		},
		"accounts": {
			"0000000000000000000000000000000000000001": { "wei": "1", "precompiled": { "name": "ecrecover", "linear": { "base": 3000, "word": 0 } } },
			"0000000000000000000000000000000000000002": { "wei": "1", "precompiled": { "name": "sha256", "linear": { "base": 60, "word": 12 } } },
			"0000000000000000000000000000000000000003": { "wei": "1", "precompiled": { "name": "ripemd160", "linear": { "base": 600, "word": 120 } } },
			"0000000000000000000000000000000000000004": { "wei": "1", "precompiled": { "name": "identity", "linear": { "base": 15, "word": 3 } } }
		}
	}
	)";

	Json::Value config;
	BOOST_REQUIRE(Json::Reader().parse(c_configString, config));
	for (auto const& account: _accounts)
		config["accounts"][account]["wei"] = "0x100000000000000000000000000000000000000000";
	test_setChainParams(Json::FastWriter().write(config));
}

void RPCSession::test_setChainParams(string const& _config)
{
	BOOST_REQUIRE(rpcCall("test_setChainParams", { _config }) == true);
}

void RPCSession::test_rewindToBlock(size_t _blockNr)
{
	BOOST_REQUIRE(rpcCall("test_rewindToBlock", { to_string(_blockNr) }) == true);
}

void RPCSession::test_mineBlocks(int _number)
{
	u256 startBlock = fromBigEndian<u256>(fromHex(rpcCall("eth_blockNumber").asString()));
	BOOST_REQUIRE(rpcCall("test_mineBlocks", { to_string(_number) }, true) == true);

	bool mined = false;

	// We auto-calibrate the time it takes to mine the transaction.
	// It would be better to go without polling, but that would probably need a change to the test client

	unsigned sleepTime = m_sleepTime;
	size_t polls = 0;
	for (; polls < 14 && !mined; ++polls)
	{
		std::this_thread::sleep_for(chrono::milliseconds(sleepTime));
		if (fromBigEndian<u256>(fromHex(rpcCall("eth_blockNumber").asString())) >= startBlock + _number)
			mined = true;
		else
			sleepTime *= 2;
	}
	if (polls > 1)
	{
		m_successfulMineRuns = 0;
		m_sleepTime += 2;
	}
	else if (polls == 1)
	{
		m_successfulMineRuns++;
		if (m_successfulMineRuns > 5)
		{
			m_successfulMineRuns = 0;
			if (m_sleepTime > 2)
				m_sleepTime--;
		}
	}

	if (!mined)
		BOOST_FAIL("Error in test_mineBlocks: block mining timeout!");
}

void RPCSession::test_modifyTimestamp(size_t _timestamp)
{
	BOOST_REQUIRE(rpcCall("test_modifyTimestamp", { to_string(_timestamp) }) == true);
}

Json::Value RPCSession::rpcCall(string const& _methodName, vector<string> const& _args, bool _canFail)
{
	string request = "{\"jsonrpc\":\"2.0\",\"method\":\"" + _methodName + "\",\"params\":[";
	for (size_t i = 0; i < _args.size(); ++i)
	{
		request += _args[i];
		if (i + 1 != _args.size())
			request += ", ";
	}

	request += "],\"id\":" + to_string(m_rpcSequence) + "}";
	++m_rpcSequence;

	//cout << "Request: " << request << endl;
	string reply = m_ipcSocket.sendRequest(request);
	//cout << "Reply: " << reply << endl;

	Json::Value result;
	BOOST_REQUIRE(Json::Reader().parse(reply, result, false));

	if (result.isMember("error"))
	{
		if (_canFail)
			return Json::Value();

		BOOST_FAIL("Error on JSON-RPC call: " + result["error"]["message"].asString());
	}
	return result["result"];
}

string const& RPCSession::accountCreateIfNotExists(size_t _id)
{
	if (_id >= m_accounts.size())
	{
		m_accounts.push_back(personal_newAccount(""));
		personal_unlockAccount(m_accounts.back(), "", 100000);
	}
	return m_accounts[_id];
}

RPCSession::RPCSession(const string& _path):
	m_ipcSocket(_path)
{
	string account = personal_newAccount("");
	personal_unlockAccount(account, "", 100000);
	m_accounts.push_back(account);
	test_setChainParams(m_accounts);
}

string RPCSession::TransactionData::toJson() const
{
	Json::Value json;
	json["from"] = (from.length() == 20) ? "0x" + from : from;
	json["to"] = (to.length() == 20 || to == "") ? "0x" + to :  to;
	json["gas"] = gas;
	json["gasprice"] = gasPrice;
	json["value"] = value;
	json["data"] = data;
	return Json::FastWriter().write(json);

}
<commit_msg>Do not log dots in soltest on windows<commit_after>/*
	This file is part of solidity.

	solidity is free software: you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.

	solidity is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with solidity.  If not, see <http://www.gnu.org/licenses/>.

	The Implementation originally from https://msdn.microsoft.com/en-us/library/windows/desktop/aa365592(v=vs.85).aspx
*/
/** @file RPCSession.cpp
 * @author Dimtiry Khokhlov <dimitry@ethdev.com>
 * @date 2016
 */

#include <string>
#include <stdio.h>
#include <thread>
#include <libdevcore/CommonData.h>
#include <json/reader.h>
#include <json/writer.h>
#include "RPCSession.h"

using namespace std;
using namespace dev;

IPCSocket::IPCSocket(string const& _path): m_path(_path)
{
#if defined(_WIN32)
	m_socket = CreateFile(
		m_path.c_str(),   // pipe name
		GENERIC_READ |  // read and write access
		GENERIC_WRITE,
		0,              // no sharing
		NULL,           // default security attribute
		OPEN_EXISTING,  // opens existing pipe
		0,              // default attributes
		NULL);          // no template file

	if (m_socket == INVALID_HANDLE_VALUE)
		BOOST_FAIL("Error creating IPC socket object!");

#else
	if (_path.length() >= sizeof(sockaddr_un::sun_path))
		BOOST_FAIL("Error opening IPC: socket path is too long!");

	struct sockaddr_un saun;
	memset(&saun, 0, sizeof(sockaddr_un));
	saun.sun_family = AF_UNIX;
	strcpy(saun.sun_path, _path.c_str());

// http://idletechnology.blogspot.ca/2011/12/unix-domain-sockets-on-osx.html
//
// SUN_LEN() might be optimal, but it seemingly affects the portability,
// with at least Android missing this macro.  Just using the sizeof() for
// structure seemingly works, and would only have the side-effect of
// sending larger-than-required packets over the socket.  Given that this
// code is only used for unit-tests, that approach seems simpler.
#if defined(__APPLE__)
	saun.sun_len = sizeof(struct sockaddr_un);
#endif //  defined(__APPLE__)

	if ((m_socket = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
		BOOST_FAIL("Error creating IPC socket object");

	if (connect(m_socket, reinterpret_cast<struct sockaddr const*>(&saun), sizeof(struct sockaddr_un)) < 0)
		BOOST_FAIL("Error connecting to IPC socket: " << _path);

	m_fp = fdopen(m_socket, "r");
	if (!m_fp)
		BOOST_FAIL("Error opening IPC socket: " << _path);
#endif
}

string IPCSocket::sendRequest(string const& _req)
{
#if defined(_WIN32)
	string returnStr;
	DWORD cbWritten;
	BOOL fSuccess = WriteFile(
		m_socket,               // pipe handle
		_req.c_str(),           // message
		_req.size(),            // message length
		&cbWritten,             // bytes written
		NULL);                  // not overlapped

	if (!fSuccess)
		BOOST_FAIL("WriteFile to pipe failed");

	DWORD  cbRead;
	TCHAR  chBuf[c_buffsize];

	// Read from the pipe.
	fSuccess = ReadFile(
		m_socket,  // pipe handle
		chBuf,     // buffer to receive reply
		c_buffsize,// size of buffer
		&cbRead,   // number of bytes read
		NULL);     // not overlapped

	returnStr += chBuf;

	if (!fSuccess)
		BOOST_FAIL("ReadFile from pipe failed");

	return returnStr;
#else
	send(m_socket, _req.c_str(), _req.length(), 0);

	char c;
	string response;
	while ((c = fgetc(m_fp)) != EOF)
	{
		if (c != '\n')
			response += c;
		else
			break;
	}
	return response;
#endif
}

RPCSession& RPCSession::instance(const string& _path)
{
	static RPCSession session(_path);
	BOOST_REQUIRE_EQUAL(session.m_ipcSocket.path(), _path);
	return session;
}

string RPCSession::eth_getCode(string const& _address, string const& _blockNumber)
{
	return rpcCall("eth_getCode", { quote(_address), quote(_blockNumber) }).asString();
}

RPCSession::TransactionReceipt RPCSession::eth_getTransactionReceipt(string const& _transactionHash)
{
	TransactionReceipt receipt;
	Json::Value const result = rpcCall("eth_getTransactionReceipt", { quote(_transactionHash) });
	BOOST_REQUIRE(!result.isNull());
	receipt.gasUsed = result["gasUsed"].asString();
	receipt.contractAddress = result["contractAddress"].asString();
	for (auto const& log: result["logs"])
	{
		LogEntry entry;
		entry.address = log["address"].asString();
		entry.data = log["data"].asString();
		for (auto const& topic: log["topics"])
			entry.topics.push_back(topic.asString());
		receipt.logEntries.push_back(entry);
	}
	return receipt;
}

string RPCSession::eth_sendTransaction(TransactionData const& _td)
{
	return rpcCall("eth_sendTransaction", { _td.toJson() }).asString();
}

string RPCSession::eth_call(TransactionData const& _td, string const& _blockNumber)
{
	return rpcCall("eth_call", { _td.toJson(), quote(_blockNumber) }).asString();
}

string RPCSession::eth_sendTransaction(string const& _transaction)
{
	return rpcCall("eth_sendTransaction", { _transaction }).asString();
}

string RPCSession::eth_getBalance(string const& _address, string const& _blockNumber)
{
	string address = (_address.length() == 20) ? "0x" + _address : _address;
	return rpcCall("eth_getBalance", { quote(address), quote(_blockNumber) }).asString();
}

string RPCSession::eth_getStorageRoot(string const& _address, string const& _blockNumber)
{
	string address = (_address.length() == 20) ? "0x" + _address : _address;
	return rpcCall("eth_getStorageRoot", { quote(address), quote(_blockNumber) }).asString();
}

void RPCSession::personal_unlockAccount(string const& _address, string const& _password, int _duration)
{
	BOOST_REQUIRE(rpcCall("personal_unlockAccount", { quote(_address), quote(_password), to_string(_duration) }) == true);
}

string RPCSession::personal_newAccount(string const& _password)
{
	return rpcCall("personal_newAccount", { quote(_password) }).asString();
}

void RPCSession::test_setChainParams(vector<string> const& _accounts)
{
	static std::string const c_configString = R"(
	{
		"sealEngine": "NoProof",
		"params": {
			"accountStartNonce": "0x",
			"maximumExtraDataSize": "0x1000000",
			"blockReward": "0x",
			"allowFutureBlocks": "1"
		},
		"genesis": {
			"author": "0000000000000010000000000000000000000000",
			"timestamp": "0x00",
			"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
			"extraData": "0x",
			"gasLimit": "0x1000000000000"
		},
		"accounts": {
			"0000000000000000000000000000000000000001": { "wei": "1", "precompiled": { "name": "ecrecover", "linear": { "base": 3000, "word": 0 } } },
			"0000000000000000000000000000000000000002": { "wei": "1", "precompiled": { "name": "sha256", "linear": { "base": 60, "word": 12 } } },
			"0000000000000000000000000000000000000003": { "wei": "1", "precompiled": { "name": "ripemd160", "linear": { "base": 600, "word": 120 } } },
			"0000000000000000000000000000000000000004": { "wei": "1", "precompiled": { "name": "identity", "linear": { "base": 15, "word": 3 } } }
		}
	}
	)";

	Json::Value config;
	BOOST_REQUIRE(Json::Reader().parse(c_configString, config));
	for (auto const& account: _accounts)
		config["accounts"][account]["wei"] = "0x100000000000000000000000000000000000000000";
	test_setChainParams(Json::FastWriter().write(config));
}

void RPCSession::test_setChainParams(string const& _config)
{
	BOOST_REQUIRE(rpcCall("test_setChainParams", { _config }) == true);
}

void RPCSession::test_rewindToBlock(size_t _blockNr)
{
	BOOST_REQUIRE(rpcCall("test_rewindToBlock", { to_string(_blockNr) }) == true);
}

void RPCSession::test_mineBlocks(int _number)
{
	u256 startBlock = fromBigEndian<u256>(fromHex(rpcCall("eth_blockNumber").asString()));
	BOOST_REQUIRE(rpcCall("test_mineBlocks", { to_string(_number) }, true) == true);

	bool mined = false;

	// We auto-calibrate the time it takes to mine the transaction.
	// It would be better to go without polling, but that would probably need a change to the test client

	unsigned sleepTime = m_sleepTime;
	size_t polls = 0;
	for (; polls < 14 && !mined; ++polls)
	{
		std::this_thread::sleep_for(chrono::milliseconds(sleepTime));
		if (fromBigEndian<u256>(fromHex(rpcCall("eth_blockNumber").asString())) >= startBlock + _number)
			mined = true;
		else
			sleepTime *= 2;
	}
	if (polls > 1)
	{
		m_successfulMineRuns = 0;
		m_sleepTime += 2;
	}
	else if (polls == 1)
	{
		m_successfulMineRuns++;
		if (m_successfulMineRuns > 5)
		{
			m_successfulMineRuns = 0;
			if (m_sleepTime > 2)
				m_sleepTime--;
		}
	}

	if (!mined)
		BOOST_FAIL("Error in test_mineBlocks: block mining timeout!");
}

void RPCSession::test_modifyTimestamp(size_t _timestamp)
{
	BOOST_REQUIRE(rpcCall("test_modifyTimestamp", { to_string(_timestamp) }) == true);
}

Json::Value RPCSession::rpcCall(string const& _methodName, vector<string> const& _args, bool _canFail)
{
	string request = "{\"jsonrpc\":\"2.0\",\"method\":\"" + _methodName + "\",\"params\":[";
	for (size_t i = 0; i < _args.size(); ++i)
	{
		request += _args[i];
		if (i + 1 != _args.size())
			request += ", ";
	}

	request += "],\"id\":" + to_string(m_rpcSequence) + "}";
	++m_rpcSequence;

	//cout << "Request: " << request << endl;
	string reply = m_ipcSocket.sendRequest(request);
	//cout << "Reply: " << reply << endl;

	Json::Value result;
	BOOST_REQUIRE(Json::Reader().parse(reply, result, false));

	if (result.isMember("error"))
	{
		if (_canFail)
			return Json::Value();

		BOOST_FAIL("Error on JSON-RPC call: " + result["error"]["message"].asString());
	}
	return result["result"];
}

string const& RPCSession::accountCreateIfNotExists(size_t _id)
{
	if (_id >= m_accounts.size())
	{
		m_accounts.push_back(personal_newAccount(""));
		personal_unlockAccount(m_accounts.back(), "", 100000);
	}
	return m_accounts[_id];
}

RPCSession::RPCSession(const string& _path):
	m_ipcSocket(_path)
{
	string account = personal_newAccount("");
	personal_unlockAccount(account, "", 100000);
	m_accounts.push_back(account);
	test_setChainParams(m_accounts);
}

string RPCSession::TransactionData::toJson() const
{
	Json::Value json;
	json["from"] = (from.length() == 20) ? "0x" + from : from;
	json["to"] = (to.length() == 20 || to == "") ? "0x" + to :  to;
	json["gas"] = gas;
	json["gasprice"] = gasPrice;
	json["value"] = value;
	json["data"] = data;
	return Json::FastWriter().write(json);

}
<|endoftext|>


<commit_before>// Copyright (c) 2006-2008 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 "chrome/browser/external_tab_container.h"

#include "base/logging.h"
#include "base/win_util.h"
#include "chrome/browser/automation/automation_provider.h"
#include "chrome/browser/profile.h"
#include "chrome/browser/tab_contents/tab_contents.h"
#include "chrome/browser/views/tab_contents_container_view.h"
#include "chrome/browser/tab_contents/web_contents.h"
#include "chrome/common/chrome_constants.h"
#include "chrome/common/win_util.h"
// Included for SetRootViewForHWND.
#include "chrome/views/widget_win.h"
#include "chrome/test/automation/automation_messages.h"

static const wchar_t kWindowObjectKey[] = L"ChromeWindowObject";

// TODO(sanjeevr): The external_accel_table_ and external_accel_entry_count_
// member variables are now obsolete and we don't use them.
// We need to remove them.
ExternalTabContainer::ExternalTabContainer(
    AutomationProvider* automation)
    : automation_(automation),
      root_view_(this),
      tab_contents_(NULL),
      external_accel_table_(NULL),
      external_accel_entry_count_(0),
      tab_contents_container_(NULL) {
}

ExternalTabContainer::~ExternalTabContainer() {
}

bool ExternalTabContainer::Init(Profile* profile, HWND parent,
                                const gfx::Rect& dimensions,
                                unsigned int style) {
  if (IsWindow()) {
    NOTREACHED();
    return false;
  }
  // First create the container window
  if (!Create(NULL, dimensions.ToRECT())) {
    NOTREACHED();
    return false;
  }

  // We don't ever remove the prop because the lifetime of this object
  // is the same as the lifetime of the window
  SetProp(*this, kWindowObjectKey, this);

  views::SetRootViewForHWND(m_hWnd, &root_view_);
  // CreateFocusManager will subclass this window and delete the FocusManager
  // instance when this window goes away.
  views::FocusManager* focus_manager =
      views::FocusManager::CreateFocusManager(m_hWnd, GetRootView());
  DCHECK(focus_manager);
  focus_manager->AddKeystrokeListener(this);
  tab_contents_ = TabContents::CreateWithType(TAB_CONTENTS_WEB, profile, NULL);
  if (!tab_contents_) {
    NOTREACHED();
    DestroyWindow();
    return false;
  }
  tab_contents_->SetupController(profile);
  tab_contents_->set_delegate(this);

  WebContents* web_conents = tab_contents_->AsWebContents();
  if (web_conents)
    web_conents->render_view_host()->AllowExternalHostBindings();

  // Create a TabContentsContainerView to handle focus cycling using Tab and
  // Shift-Tab.
  tab_contents_container_ = new TabContentsContainerView();
  root_view_.AddChildView(tab_contents_container_);
  // Note that SetTabContents must be called after AddChildView is called
  tab_contents_container_->SetTabContents(tab_contents_);
  // Add a dummy view to catch when the user tabs out of the tab
  // Create a dummy FocusTraversable object to represent the frame of the
  // external host. This will allow Tab and Shift-Tab to cycle into the
  // external frame.  When the tab_contents_container_ loses focus,
  // the focus will be moved to this class (See OnSetFocus in this file).
  // An alternative to using views::View and catching when the focus manager
  // shifts the focus to the dummy view could be to implement our own view
  // and handle AboutToRequestFocusFromTabTraversal.
  views::View* dummy = new views::View();
  dummy->SetFocusable(true);
  DCHECK(dummy->IsFocusable());
  root_view_.AddChildView(dummy);

  NavigationController* controller = tab_contents_->controller();
  DCHECK(controller);
  registrar_.Add(this, NotificationType::NAV_ENTRY_COMMITTED,
                 Source<NavigationController>(controller));
  NotificationService::current()->Notify(
      NotificationType::EXTERNAL_TAB_CREATED,
      Source<NavigationController>(controller),
      NotificationService::NoDetails());

  // Now apply the parenting and style
  if (parent)
    SetParent(parent);

  // We need WS_POPUP to be on the window during initialization, but
  // once initialized and parent-ed, we apply the requested style which
  // may or may not include the popup bit.
  ModifyStyle(WS_POPUP, style, 0);

  ::ShowWindow(tab_contents_->GetNativeView(), SW_SHOW);
  return true;
}

void ExternalTabContainer::OnDestroy() {
  views::FocusManager* focus_manager =
      views::FocusManager::GetFocusManager(GetHWND());
  if (focus_manager) {
    focus_manager->RemoveKeystrokeListener(this);
  }
  root_view_.RemoveAllChildViews(true);
  if (tab_contents_) {
    NavigationController* controller = tab_contents_->controller();
    DCHECK(controller);

    NotificationService::current()->Notify(
        NotificationType::EXTERNAL_TAB_CLOSED,
        Source<NavigationController>(controller),
        Details<ExternalTabContainer>(this));
    tab_contents_->set_delegate(NULL);
    tab_contents_->CloseContents();
    // WARNING: tab_contents_ has likely been deleted.
    tab_contents_ = NULL;
  }
}

void ExternalTabContainer::OnFinalMessage(HWND window) {
  delete this;
}

LRESULT ExternalTabContainer::OnSize(UINT, WPARAM, LPARAM, BOOL& handled) {
  if (tab_contents_) {
    RECT client_rect = {0};
    GetClientRect(&client_rect);
    ::SetWindowPos(tab_contents_->GetNativeView(), NULL, client_rect.left,
                   client_rect.top, client_rect.right - client_rect.left,
                   client_rect.bottom - client_rect.top, SWP_NOZORDER);
  }
  return 0;
}

LRESULT ExternalTabContainer::OnSetFocus(UINT msg, WPARAM wp, LPARAM lp,
                                         BOOL& handled) {
  if (automation_) {
    views::FocusManager* focus_manager =
        views::FocusManager::GetFocusManager(GetHWND());
    DCHECK(focus_manager);
    if (focus_manager) {
      focus_manager->ClearFocus();
      automation_->Send(new AutomationMsg_TabbedOut(win_util::IsShiftPressed(),
                                                    false));
    }
  }

  return 0;
}

void ExternalTabContainer::OpenURLFromTab(TabContents* source,
                           const GURL& url,
                           const GURL& referrer,
                           WindowOpenDisposition disposition,
                           PageTransition::Type transition) {
  switch (disposition) {
    case CURRENT_TAB:
    case NEW_FOREGROUND_TAB:
    case NEW_BACKGROUND_TAB:
    case NEW_WINDOW:
      if (automation_) {
        automation_->Send(new AutomationMsg_OpenURL(0, url, disposition));
      }
      break;
    default:
      break;
   }
}

void ExternalTabContainer::NavigationStateChanged(const TabContents* source,
                                                  unsigned changed_flags) {
  if (automation_) {
    automation_->Send(
        new AutomationMsg_NavigationStateChanged(0, changed_flags));
  }
}

void ExternalTabContainer::ReplaceContents(TabContents* source, TabContents* new_contents) {
}

void ExternalTabContainer::AddNewContents(TabContents* source,
                            TabContents* new_contents,
                            WindowOpenDisposition disposition,
                            const gfx::Rect& initial_pos,
                            bool user_gesture) {
}

void ExternalTabContainer::ActivateContents(TabContents* contents) {
}

void ExternalTabContainer::LoadingStateChanged(TabContents* source) {
}

void ExternalTabContainer::CloseContents(TabContents* source) {
}

void ExternalTabContainer::MoveContents(TabContents* source,
                                        const gfx::Rect& pos) {
}

bool ExternalTabContainer::IsPopup(TabContents* source) {
  return false;
}

void ExternalTabContainer::URLStarredChanged(TabContents* source,
                                             bool starred) {
}

void ExternalTabContainer::UpdateTargetURL(TabContents* source,
                                           const GURL& url) {
  if (automation_) {
    std::wstring url_string = CA2W(url.spec().c_str());
    automation_->Send(
        new AutomationMsg_UpdateTargetUrl(0, url_string));
  }
}

void ExternalTabContainer::ContentsZoomChange(bool zoom_in) {
}

void ExternalTabContainer::ToolbarSizeChanged(TabContents* source,
                                              bool finished) {
}

void ExternalTabContainer::ForwardMessageToExternalHost(
    const std::string& receiver, const std::string& message) {
  if(automation_) {
    automation_->Send(
        new AutomationMsg_ForwardMessageToExternalHost(0, receiver, message));
  }
}

void ExternalTabContainer::Observe(NotificationType type,
                                   const NotificationSource& source,
                                   const NotificationDetails& details) {
  switch (type.value) {
    case NotificationType::NAV_ENTRY_COMMITTED:
      if (automation_) {
        const NavigationController::LoadCommittedDetails* commit =
            Details<NavigationController::LoadCommittedDetails>(details).ptr();

        // When the previous entry index is invalid, it will be -1, which will
        // still make the computation come out right (navigating to the 0th
        // entry will be +1).
        automation_->Send(new AutomationMsg_DidNavigate(
            0, commit->type,
            commit->previous_entry_index -
                tab_contents_->controller()->GetLastCommittedEntryIndex()));
      }
      break;
    default:
      NOTREACHED();
  }
}

void ExternalTabContainer::GetBounds(gfx::Rect* out,
                                     bool including_frame) const {
  CRect crect;
  GetWindowRect(&crect);
  *out = gfx::Rect(crect);
}

void ExternalTabContainer::MoveToFront(bool should_activate) {
}

HWND ExternalTabContainer::GetHWND() const {
  return m_hWnd;
}

void ExternalTabContainer::PaintNow(const gfx::Rect& update_rect) {
  RECT native_update_rect = update_rect.ToRECT();
  RedrawWindow(&native_update_rect,
               NULL,
               RDW_INVALIDATE | RDW_ALLCHILDREN | RDW_NOERASE);
}

views::RootView* ExternalTabContainer::GetRootView() {
  return const_cast<views::RootView*>(&root_view_);
}

bool ExternalTabContainer::IsVisible() {
  return !!::IsWindowVisible(*this);
}

bool ExternalTabContainer::IsActive() {
  return win_util::IsWindowActive(*this);
}

bool ExternalTabContainer::ProcessKeyDown(HWND window, UINT message,
                                          WPARAM wparam, LPARAM lparam) {
  if (!automation_) {
    return false;
  }
  if ((wparam == VK_TAB) && !win_util::IsCtrlPressed()) {
    // Tabs are handled separately (except if this is Ctrl-Tab or
    // Ctrl-Shift-Tab)
    return false;
  }
  int flags = HIWORD(lparam);
  if ((flags & KF_EXTENDED) || (flags & KF_ALTDOWN) ||
      (wparam >= VK_F1 && wparam <= VK_F24) ||
      win_util::IsShiftPressed() || win_util::IsCtrlPressed()) {
    // If this is an extended key or if one or more of Alt, Shift and Control
    // are pressed, this might be an accelerator that the external host wants
    // to handle. If the host does not handle this accelerator, it will reflect
    // the accelerator back to us via the ProcessUnhandledAccelerator method.
    MSG msg = {0};
    msg.hwnd = window;
    msg.message = message;
    msg.wParam = wparam;
    msg.lParam = lparam;
    automation_->Send(new AutomationMsg_HandleAccelerator(0, msg));
    return true;
  }
  return false;
}

void ExternalTabContainer::SetAccelerators(HACCEL accel_table,
                                           int accel_table_entry_count) {
  external_accel_table_ = accel_table;
  external_accel_entry_count_ = accel_table_entry_count;
}

void ExternalTabContainer::ProcessUnhandledAccelerator(const MSG& msg) {
  // We just received an accelerator key that we had sent to external host
  // back. Since the external host was not interested in handling this, we
  // need to dispatch this message as if we had just peeked this out. (we
  // also need to call TranslateMessage to generate a WM_CHAR if needed).
  TranslateMessage(&msg);
  DispatchMessage(&msg);
}

void ExternalTabContainer::SetInitialFocus(bool reverse) {
  DCHECK(tab_contents_);
  if (tab_contents_) {
    tab_contents_->SetInitialFocus(reverse);
  }
}

// static
bool ExternalTabContainer::IsExternalTabContainer(HWND window) {
  std::wstring class_name = win_util::GetClassName(window);
  return _wcsicmp(class_name.c_str(), chrome::kExternalTabWindowClass) == 0;
}

// static
ExternalTabContainer* ExternalTabContainer::GetContainerForTab(
    HWND tab_window) {
  HWND parent_window = ::GetParent(tab_window);
  if (!::IsWindow(parent_window)) {
    return NULL;
  }
  if (!IsExternalTabContainer(parent_window)) {
    return NULL;
  }
  ExternalTabContainer* container = reinterpret_cast<ExternalTabContainer*>(
      GetProp(parent_window, kWindowObjectKey));
  return container;
}
<commit_msg>Fixing reverse tabbing.<commit_after>// Copyright (c) 2006-2008 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 "chrome/browser/external_tab_container.h"

#include "base/logging.h"
#include "base/win_util.h"
#include "chrome/browser/automation/automation_provider.h"
#include "chrome/browser/profile.h"
#include "chrome/browser/tab_contents/tab_contents.h"
#include "chrome/browser/views/tab_contents_container_view.h"
#include "chrome/browser/tab_contents/web_contents.h"
#include "chrome/common/chrome_constants.h"
#include "chrome/common/win_util.h"
// Included for SetRootViewForHWND.
#include "chrome/views/widget_win.h"
#include "chrome/test/automation/automation_messages.h"

static const wchar_t kWindowObjectKey[] = L"ChromeWindowObject";

// TODO(sanjeevr): The external_accel_table_ and external_accel_entry_count_
// member variables are now obsolete and we don't use them.
// We need to remove them.
ExternalTabContainer::ExternalTabContainer(
    AutomationProvider* automation)
    : automation_(automation),
      root_view_(this),
      tab_contents_(NULL),
      external_accel_table_(NULL),
      external_accel_entry_count_(0),
      tab_contents_container_(NULL) {
}

ExternalTabContainer::~ExternalTabContainer() {
}

bool ExternalTabContainer::Init(Profile* profile, HWND parent,
                                const gfx::Rect& dimensions,
                                unsigned int style) {
  if (IsWindow()) {
    NOTREACHED();
    return false;
  }
  // First create the container window
  if (!Create(NULL, dimensions.ToRECT())) {
    NOTREACHED();
    return false;
  }

  // We don't ever remove the prop because the lifetime of this object
  // is the same as the lifetime of the window
  SetProp(*this, kWindowObjectKey, this);

  views::SetRootViewForHWND(m_hWnd, &root_view_);
  // CreateFocusManager will subclass this window and delete the FocusManager
  // instance when this window goes away.
  views::FocusManager* focus_manager =
      views::FocusManager::CreateFocusManager(m_hWnd, GetRootView());
  DCHECK(focus_manager);
  focus_manager->AddKeystrokeListener(this);
  tab_contents_ = TabContents::CreateWithType(TAB_CONTENTS_WEB, profile, NULL);
  if (!tab_contents_) {
    NOTREACHED();
    DestroyWindow();
    return false;
  }
  tab_contents_->SetupController(profile);
  tab_contents_->set_delegate(this);

  WebContents* web_conents = tab_contents_->AsWebContents();
  if (web_conents)
    web_conents->render_view_host()->AllowExternalHostBindings();

  // Create a TabContentsContainerView to handle focus cycling using Tab and
  // Shift-Tab.
  tab_contents_container_ = new TabContentsContainerView();
  root_view_.AddChildView(tab_contents_container_);
  // Note that SetTabContents must be called after AddChildView is called
  tab_contents_container_->SetTabContents(tab_contents_);
  // Add a dummy view to catch when the user tabs out of the tab
  // Create a dummy FocusTraversable object to represent the frame of the
  // external host. This will allow Tab and Shift-Tab to cycle into the
  // external frame.  When the tab_contents_container_ loses focus,
  // the focus will be moved to this class (See OnSetFocus in this file).
  // An alternative to using views::View and catching when the focus manager
  // shifts the focus to the dummy view could be to implement our own view
  // and handle AboutToRequestFocusFromTabTraversal.
  views::View* dummy = new views::View();
  dummy->SetFocusable(true);
  DCHECK(dummy->IsFocusable());
  root_view_.AddChildView(dummy);

  NavigationController* controller = tab_contents_->controller();
  DCHECK(controller);
  registrar_.Add(this, NotificationType::NAV_ENTRY_COMMITTED,
                 Source<NavigationController>(controller));
  NotificationService::current()->Notify(
      NotificationType::EXTERNAL_TAB_CREATED,
      Source<NavigationController>(controller),
      NotificationService::NoDetails());

  // Now apply the parenting and style
  if (parent)
    SetParent(parent);

  // We need WS_POPUP to be on the window during initialization, but
  // once initialized and parent-ed, we apply the requested style which
  // may or may not include the popup bit.
  ModifyStyle(WS_POPUP, style, 0);

  ::ShowWindow(tab_contents_->GetNativeView(), SW_SHOW);
  return true;
}

void ExternalTabContainer::OnDestroy() {
  views::FocusManager* focus_manager =
      views::FocusManager::GetFocusManager(GetHWND());
  if (focus_manager) {
    focus_manager->RemoveKeystrokeListener(this);
  }
  root_view_.RemoveAllChildViews(true);
  if (tab_contents_) {
    NavigationController* controller = tab_contents_->controller();
    DCHECK(controller);

    NotificationService::current()->Notify(
        NotificationType::EXTERNAL_TAB_CLOSED,
        Source<NavigationController>(controller),
        Details<ExternalTabContainer>(this));
    tab_contents_->set_delegate(NULL);
    tab_contents_->CloseContents();
    // WARNING: tab_contents_ has likely been deleted.
    tab_contents_ = NULL;
  }
}

void ExternalTabContainer::OnFinalMessage(HWND window) {
  delete this;
}

LRESULT ExternalTabContainer::OnSize(UINT, WPARAM, LPARAM, BOOL& handled) {
  if (tab_contents_) {
    RECT client_rect = {0};
    GetClientRect(&client_rect);
    ::SetWindowPos(tab_contents_->GetNativeView(), NULL, client_rect.left,
                   client_rect.top, client_rect.right - client_rect.left,
                   client_rect.bottom - client_rect.top, SWP_NOZORDER);
  }
  return 0;
}

LRESULT ExternalTabContainer::OnSetFocus(UINT msg, WPARAM wp, LPARAM lp,
                                         BOOL& handled) {
  if (automation_) {
    views::FocusManager* focus_manager =
        views::FocusManager::GetFocusManager(GetHWND());
    DCHECK(focus_manager);
    if (focus_manager) {
      focus_manager->ClearFocus();
      automation_->Send(new AutomationMsg_TabbedOut(0,
          win_util::IsShiftPressed()));
    }
  }

  return 0;
}

void ExternalTabContainer::OpenURLFromTab(TabContents* source,
                           const GURL& url,
                           const GURL& referrer,
                           WindowOpenDisposition disposition,
                           PageTransition::Type transition) {
  switch (disposition) {
    case CURRENT_TAB:
    case NEW_FOREGROUND_TAB:
    case NEW_BACKGROUND_TAB:
    case NEW_WINDOW:
      if (automation_) {
        automation_->Send(new AutomationMsg_OpenURL(0, url, disposition));
      }
      break;
    default:
      break;
   }
}

void ExternalTabContainer::NavigationStateChanged(const TabContents* source,
                                                  unsigned changed_flags) {
  if (automation_) {
    automation_->Send(
        new AutomationMsg_NavigationStateChanged(0, changed_flags));
  }
}

void ExternalTabContainer::ReplaceContents(TabContents* source, TabContents* new_contents) {
}

void ExternalTabContainer::AddNewContents(TabContents* source,
                            TabContents* new_contents,
                            WindowOpenDisposition disposition,
                            const gfx::Rect& initial_pos,
                            bool user_gesture) {
}

void ExternalTabContainer::ActivateContents(TabContents* contents) {
}

void ExternalTabContainer::LoadingStateChanged(TabContents* source) {
}

void ExternalTabContainer::CloseContents(TabContents* source) {
}

void ExternalTabContainer::MoveContents(TabContents* source,
                                        const gfx::Rect& pos) {
}

bool ExternalTabContainer::IsPopup(TabContents* source) {
  return false;
}

void ExternalTabContainer::URLStarredChanged(TabContents* source,
                                             bool starred) {
}

void ExternalTabContainer::UpdateTargetURL(TabContents* source,
                                           const GURL& url) {
  if (automation_) {
    std::wstring url_string = CA2W(url.spec().c_str());
    automation_->Send(
        new AutomationMsg_UpdateTargetUrl(0, url_string));
  }
}

void ExternalTabContainer::ContentsZoomChange(bool zoom_in) {
}

void ExternalTabContainer::ToolbarSizeChanged(TabContents* source,
                                              bool finished) {
}

void ExternalTabContainer::ForwardMessageToExternalHost(
    const std::string& receiver, const std::string& message) {
  if(automation_) {
    automation_->Send(
        new AutomationMsg_ForwardMessageToExternalHost(0, receiver, message));
  }
}

void ExternalTabContainer::Observe(NotificationType type,
                                   const NotificationSource& source,
                                   const NotificationDetails& details) {
  switch (type.value) {
    case NotificationType::NAV_ENTRY_COMMITTED:
      if (automation_) {
        const NavigationController::LoadCommittedDetails* commit =
            Details<NavigationController::LoadCommittedDetails>(details).ptr();

        // When the previous entry index is invalid, it will be -1, which will
        // still make the computation come out right (navigating to the 0th
        // entry will be +1).
        automation_->Send(new AutomationMsg_DidNavigate(
            0, commit->type,
            commit->previous_entry_index -
                tab_contents_->controller()->GetLastCommittedEntryIndex()));
      }
      break;
    default:
      NOTREACHED();
  }
}

void ExternalTabContainer::GetBounds(gfx::Rect* out,
                                     bool including_frame) const {
  CRect crect;
  GetWindowRect(&crect);
  *out = gfx::Rect(crect);
}

void ExternalTabContainer::MoveToFront(bool should_activate) {
}

HWND ExternalTabContainer::GetHWND() const {
  return m_hWnd;
}

void ExternalTabContainer::PaintNow(const gfx::Rect& update_rect) {
  RECT native_update_rect = update_rect.ToRECT();
  RedrawWindow(&native_update_rect,
               NULL,
               RDW_INVALIDATE | RDW_ALLCHILDREN | RDW_NOERASE);
}

views::RootView* ExternalTabContainer::GetRootView() {
  return const_cast<views::RootView*>(&root_view_);
}

bool ExternalTabContainer::IsVisible() {
  return !!::IsWindowVisible(*this);
}

bool ExternalTabContainer::IsActive() {
  return win_util::IsWindowActive(*this);
}

bool ExternalTabContainer::ProcessKeyDown(HWND window, UINT message,
                                          WPARAM wparam, LPARAM lparam) {
  if (!automation_) {
    return false;
  }
  if ((wparam == VK_TAB) && !win_util::IsCtrlPressed()) {
    // Tabs are handled separately (except if this is Ctrl-Tab or
    // Ctrl-Shift-Tab)
    return false;
  }
  int flags = HIWORD(lparam);
  if ((flags & KF_EXTENDED) || (flags & KF_ALTDOWN) ||
      (wparam >= VK_F1 && wparam <= VK_F24) ||
      win_util::IsShiftPressed() || win_util::IsCtrlPressed()) {
    // If this is an extended key or if one or more of Alt, Shift and Control
    // are pressed, this might be an accelerator that the external host wants
    // to handle. If the host does not handle this accelerator, it will reflect
    // the accelerator back to us via the ProcessUnhandledAccelerator method.
    MSG msg = {0};
    msg.hwnd = window;
    msg.message = message;
    msg.wParam = wparam;
    msg.lParam = lparam;
    automation_->Send(new AutomationMsg_HandleAccelerator(0, msg));
    return true;
  }
  return false;
}

void ExternalTabContainer::SetAccelerators(HACCEL accel_table,
                                           int accel_table_entry_count) {
  external_accel_table_ = accel_table;
  external_accel_entry_count_ = accel_table_entry_count;
}

void ExternalTabContainer::ProcessUnhandledAccelerator(const MSG& msg) {
  // We just received an accelerator key that we had sent to external host
  // back. Since the external host was not interested in handling this, we
  // need to dispatch this message as if we had just peeked this out. (we
  // also need to call TranslateMessage to generate a WM_CHAR if needed).
  TranslateMessage(&msg);
  DispatchMessage(&msg);
}

void ExternalTabContainer::SetInitialFocus(bool reverse) {
  DCHECK(tab_contents_);
  if (tab_contents_) {
    tab_contents_->SetInitialFocus(reverse);
  }
}

// static
bool ExternalTabContainer::IsExternalTabContainer(HWND window) {
  std::wstring class_name = win_util::GetClassName(window);
  return _wcsicmp(class_name.c_str(), chrome::kExternalTabWindowClass) == 0;
}

// static
ExternalTabContainer* ExternalTabContainer::GetContainerForTab(
    HWND tab_window) {
  HWND parent_window = ::GetParent(tab_window);
  if (!::IsWindow(parent_window)) {
    return NULL;
  }
  if (!IsExternalTabContainer(parent_window)) {
    return NULL;
  }
  ExternalTabContainer* container = reinterpret_cast<ExternalTabContainer*>(
      GetProp(parent_window, kWindowObjectKey));
  return container;
}
<|endoftext|>


<commit_before>// Copyright (c) 2009 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 "chrome/browser/gtk/download_shelf_gtk.h"

#include "base/gfx/gtk_util.h"
#include "chrome/browser/download/download_item_model.h"
#include "chrome/browser/gtk/custom_button.h"
#include "chrome/browser/gtk/download_item_gtk.h"
#include "chrome/browser/gtk/link_button_gtk.h"
#include "chrome/browser/gtk/slide_animator_gtk.h"
#include "chrome/browser/tab_contents/tab_contents.h"
#include "chrome/common/l10n_util.h"
#include "chrome/common/resource_bundle.h"
#include "grit/generated_resources.h"
#include "grit/theme_resources.h"

namespace {

// The height of the download items. Should be at least 28, as that is the
// minimum height of their nineboxes.
const int kDownloadItemHeight = 38;

// Padding between the download widgets.
const int kDownloadItemPadding = 10;

// Padding between the top/bottom of the download widgets and the edge of the
// shelf.
const int kTopBottomPadding = 4;

// Padding between the left side of the shelf and the first download item.
const int kLeftPadding = 2;

// Padding between the right side of the shelf and the close button.
const int kRightPadding = 10;

// The background color of the shelf.
static GdkColor kBackgroundColor = GDK_COLOR_RGB(230, 237, 244);

// Border color (the top pixel of the shelf).
static GdkColor kBorderColor = GDK_COLOR_RGB(214, 214, 214);

}  // namespace

// static
DownloadShelf* DownloadShelf::Create(TabContents* tab_contents) {
  return new DownloadShelfGtk(tab_contents);
}

DownloadShelfGtk::DownloadShelfGtk(TabContents* tab_contents)
    : DownloadShelf(tab_contents),
      is_showing_(false) {
  // Logically, the shelf is a vbox that contains two children: a one pixel
  // tall event box, which serves as the top border, and an hbox, which holds
  // the download items and other shelf widgets (close button, show-all-
  // downloads link).
  // To make things pretty, we have to add a few more widgets. To get padding
  // right, we stick the hbox in an alignment. We put that alignment in an
  // event box so we can color the background.

  // Create the top border.
  GtkWidget* top_border = gtk_event_box_new();
  gtk_widget_set_size_request(GTK_WIDGET(top_border), 0, 1);
  gtk_widget_modify_bg(top_border, GTK_STATE_NORMAL, &kBorderColor);

  // Create |hbox_|.
  hbox_ = gtk_hbox_new(FALSE, kDownloadItemPadding);
  gtk_widget_set_size_request(hbox_, -1, kDownloadItemHeight);

  // Get the padding and background color for |hbox_| right.
  GtkWidget* padding = gtk_alignment_new(0, 0, 1, 1);
  // Subtract 1 from top spacing to account for top border.
  gtk_alignment_set_padding(GTK_ALIGNMENT(padding),
      kTopBottomPadding - 1, kTopBottomPadding, kLeftPadding, kRightPadding);
  GtkWidget* padding_bg = gtk_event_box_new();
  gtk_container_add(GTK_CONTAINER(padding_bg), padding);
  gtk_container_add(GTK_CONTAINER(padding), hbox_);
  gtk_widget_modify_bg(padding_bg, GTK_STATE_NORMAL, &kBackgroundColor);

  shelf_.Own(gtk_vbox_new(FALSE, 0));
  gtk_box_pack_start(GTK_BOX(shelf_.get()), top_border, FALSE, FALSE, 0);
  gtk_box_pack_start(GTK_BOX(shelf_.get()), padding_bg, FALSE, FALSE, 0);

  // Create and pack the close button.
  close_button_.reset(CustomDrawButton::AddBarCloseButton(hbox_));
  g_signal_connect(close_button_->widget(), "clicked",
                   G_CALLBACK(OnButtonClick), this);

  // Create the "Show all downloads..." link and connect to the click event.
  std::string link_text =
      l10n_util::GetStringUTF8(IDS_SHOW_ALL_DOWNLOADS);
  link_button_.reset(new LinkButtonGtk(link_text.c_str()));
  g_signal_connect(link_button_->widget(), "clicked",
                   G_CALLBACK(OnButtonClick), this);

  // Make the download arrow icon.
  ResourceBundle& rb = ResourceBundle::GetSharedInstance();
  GdkPixbuf* download_pixbuf = rb.GetPixbufNamed(IDR_DOWNLOADS_FAVICON);
  GtkWidget* download_image = gtk_image_new_from_pixbuf(download_pixbuf);

  // Pack the link and the icon in an hbox.
  link_hbox_ = gtk_hbox_new(FALSE, 5);
  gtk_box_pack_start(GTK_BOX(link_hbox_), download_image, FALSE, FALSE, 0);
  gtk_box_pack_start(GTK_BOX(link_hbox_), link_button_->widget(),
                     FALSE, FALSE, 0);
  gtk_box_pack_end(GTK_BOX(hbox_), link_hbox_, FALSE, FALSE, 0);

  slide_widget_.reset(new SlideAnimatorGtk(shelf_.get(),
                                           SlideAnimatorGtk::UP, NULL));
  // Stick ourselves at the bottom of the parent tab contents.
  GtkWidget* parent_contents = tab_contents->GetNativeView();
  gtk_box_pack_end(GTK_BOX(parent_contents), slide_widget_->widget(),
                   FALSE, FALSE, 0);
  slide_widget_->Open();
}

DownloadShelfGtk::~DownloadShelfGtk() {
  shelf_.Destroy();
}

void DownloadShelfGtk::AddDownload(BaseDownloadItemModel* download_model_) {
  // TODO(estade): we need to delete these at some point. There's no explicit
  // mass delete on windows, figure out where they do it.
  download_items_.push_back(new DownloadItemGtk(download_model_, hbox_,
                                                link_hbox_));
  slide_widget_->Open();
}

bool DownloadShelfGtk::IsShowing() const {
  return slide_widget_->IsShowing();
}

// static
void DownloadShelfGtk::OnButtonClick(GtkWidget* button,
                                     DownloadShelfGtk* shelf) {
  if (button == shelf->close_button_->widget()) {
    shelf->slide_widget_->Close();
  } else {
    // The link button was clicked.
    shelf->ShowAllDownloads();
  }
}
<commit_msg>Fix a memory leak in the download shelf.<commit_after>// Copyright (c) 2009 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 "chrome/browser/gtk/download_shelf_gtk.h"

#include "base/gfx/gtk_util.h"
#include "chrome/browser/download/download_item_model.h"
#include "chrome/browser/gtk/custom_button.h"
#include "chrome/browser/gtk/download_item_gtk.h"
#include "chrome/browser/gtk/link_button_gtk.h"
#include "chrome/browser/gtk/slide_animator_gtk.h"
#include "chrome/browser/tab_contents/tab_contents.h"
#include "chrome/common/l10n_util.h"
#include "chrome/common/resource_bundle.h"
#include "grit/generated_resources.h"
#include "grit/theme_resources.h"

namespace {

// The height of the download items. Should be at least 28, as that is the
// minimum height of their nineboxes.
const int kDownloadItemHeight = 38;

// Padding between the download widgets.
const int kDownloadItemPadding = 10;

// Padding between the top/bottom of the download widgets and the edge of the
// shelf.
const int kTopBottomPadding = 4;

// Padding between the left side of the shelf and the first download item.
const int kLeftPadding = 2;

// Padding between the right side of the shelf and the close button.
const int kRightPadding = 10;

// The background color of the shelf.
static GdkColor kBackgroundColor = GDK_COLOR_RGB(230, 237, 244);

// Border color (the top pixel of the shelf).
static GdkColor kBorderColor = GDK_COLOR_RGB(214, 214, 214);

}  // namespace

// static
DownloadShelf* DownloadShelf::Create(TabContents* tab_contents) {
  return new DownloadShelfGtk(tab_contents);
}

DownloadShelfGtk::DownloadShelfGtk(TabContents* tab_contents)
    : DownloadShelf(tab_contents),
      is_showing_(false) {
  // Logically, the shelf is a vbox that contains two children: a one pixel
  // tall event box, which serves as the top border, and an hbox, which holds
  // the download items and other shelf widgets (close button, show-all-
  // downloads link).
  // To make things pretty, we have to add a few more widgets. To get padding
  // right, we stick the hbox in an alignment. We put that alignment in an
  // event box so we can color the background.

  // Create the top border.
  GtkWidget* top_border = gtk_event_box_new();
  gtk_widget_set_size_request(GTK_WIDGET(top_border), 0, 1);
  gtk_widget_modify_bg(top_border, GTK_STATE_NORMAL, &kBorderColor);

  // Create |hbox_|.
  hbox_ = gtk_hbox_new(FALSE, kDownloadItemPadding);
  gtk_widget_set_size_request(hbox_, -1, kDownloadItemHeight);

  // Get the padding and background color for |hbox_| right.
  GtkWidget* padding = gtk_alignment_new(0, 0, 1, 1);
  // Subtract 1 from top spacing to account for top border.
  gtk_alignment_set_padding(GTK_ALIGNMENT(padding),
      kTopBottomPadding - 1, kTopBottomPadding, kLeftPadding, kRightPadding);
  GtkWidget* padding_bg = gtk_event_box_new();
  gtk_container_add(GTK_CONTAINER(padding_bg), padding);
  gtk_container_add(GTK_CONTAINER(padding), hbox_);
  gtk_widget_modify_bg(padding_bg, GTK_STATE_NORMAL, &kBackgroundColor);

  shelf_.Own(gtk_vbox_new(FALSE, 0));
  gtk_box_pack_start(GTK_BOX(shelf_.get()), top_border, FALSE, FALSE, 0);
  gtk_box_pack_start(GTK_BOX(shelf_.get()), padding_bg, FALSE, FALSE, 0);

  // Create and pack the close button.
  close_button_.reset(CustomDrawButton::AddBarCloseButton(hbox_));
  g_signal_connect(close_button_->widget(), "clicked",
                   G_CALLBACK(OnButtonClick), this);

  // Create the "Show all downloads..." link and connect to the click event.
  std::string link_text =
      l10n_util::GetStringUTF8(IDS_SHOW_ALL_DOWNLOADS);
  link_button_.reset(new LinkButtonGtk(link_text.c_str()));
  g_signal_connect(link_button_->widget(), "clicked",
                   G_CALLBACK(OnButtonClick), this);

  // Make the download arrow icon.
  ResourceBundle& rb = ResourceBundle::GetSharedInstance();
  GdkPixbuf* download_pixbuf = rb.GetPixbufNamed(IDR_DOWNLOADS_FAVICON);
  GtkWidget* download_image = gtk_image_new_from_pixbuf(download_pixbuf);

  // Pack the link and the icon in an hbox.
  link_hbox_ = gtk_hbox_new(FALSE, 5);
  gtk_box_pack_start(GTK_BOX(link_hbox_), download_image, FALSE, FALSE, 0);
  gtk_box_pack_start(GTK_BOX(link_hbox_), link_button_->widget(),
                     FALSE, FALSE, 0);
  gtk_box_pack_end(GTK_BOX(hbox_), link_hbox_, FALSE, FALSE, 0);

  slide_widget_.reset(new SlideAnimatorGtk(shelf_.get(),
                                           SlideAnimatorGtk::UP, NULL));
  // Stick ourselves at the bottom of the parent tab contents.
  GtkWidget* parent_contents = tab_contents->GetNativeView();
  gtk_box_pack_end(GTK_BOX(parent_contents), slide_widget_->widget(),
                   FALSE, FALSE, 0);
  slide_widget_->Open();
}

DownloadShelfGtk::~DownloadShelfGtk() {
  for (std::vector<DownloadItemGtk*>::iterator iter = download_items_.begin();
       iter != download_items_.end(); ++iter) {
      delete *iter;
  }

  shelf_.Destroy();
}

void DownloadShelfGtk::AddDownload(BaseDownloadItemModel* download_model_) {
  download_items_.push_back(new DownloadItemGtk(download_model_, hbox_,
                                                link_hbox_));
  slide_widget_->Open();
}

bool DownloadShelfGtk::IsShowing() const {
  return slide_widget_->IsShowing();
}

// static
void DownloadShelfGtk::OnButtonClick(GtkWidget* button,
                                     DownloadShelfGtk* shelf) {
  if (button == shelf->close_button_->widget()) {
    shelf->slide_widget_->Close();
  } else {
    // The link button was clicked.
    shelf->ShowAllDownloads();
  }
}
<|endoftext|>