<commit_before>/**
 * The Seeks proxy and plugin framework are part of the SEEKS project.
 * Copyright (C) 2010 Emmanuel Benazera, juban@free.fr
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "se_handler_img.h"
#include "websearch.h"
#include "curl_mget.h"
#include "miscutil.h"
#include "encode.h"
#include "errlog.h"

#include "se_parser_bing_img.h"
#include "se_parser_ggle_img.h"

using namespace sp;

namespace seeks_plugins
{
   se_bing_img::se_bing_img()
     :search_engine()
       {
       }

   se_bing_img::~se_bing_img()
     {
     }

   void se_bing_img::query_to_se(const hash_map<const char*, const char*, hash<const char*>, eqstr> *parameters,
				 std::string &url, const query_context *qc)
     {
	std::string q_bing = se_handler_img::_se_strings[BING_IMG]; // query to bing.
	const char *query = miscutil::lookup(parameters,"q");

	// query.
	int p = 36;
	char *qenc = encode::url_encode(se_handler::no_command_query(std::string(query)).c_str());
	std::string qenc_str = std::string(qenc);
	free(qenc);
	q_bing.replace(p,6,qenc_str);

	// page.
	const char *expansion = miscutil::lookup(parameters,"expansion");
	int pp = (strcmp(expansion,"")!=0) ? (atoi(expansion)-1) * websearch::_wconfig->_N : 0;
	std::string pp_str = miscutil::to_string(pp);
	miscutil::replace_in_string(q_bing,"%start",pp_str);

	// language.
	miscutil::replace_in_string(q_bing,"%lang",qc->_auto_lang_reg);

	// log the query.
	errlog::log_error(LOG_LEVEL_INFO, "Querying bing: %s", q_bing.c_str());

	url = q_bing;
     }

   se_ggle_img::se_ggle_img()
     :search_engine()
       {
       }

   se_ggle_img::~se_ggle_img()
     {
     }

   void se_ggle_img::query_to_se(const hash_map<const char*, const char*, hash<const char*>, eqstr> *parameters,
				 std::string &url, const query_context *qc)
     {
	std::string q_ggle = se_handler_img::_se_strings[GOOGLE_IMG]; // query to ggle.
	const char *query = miscutil::lookup(parameters,"q");

	// query.
	int p = 31;
	char *qenc = encode::url_encode(se_handler::no_command_query(std::string(query)).c_str());
	std::string qenc_str = std::string(qenc);
	free(qenc);
	q_ggle.replace(p,6,qenc_str);

	// expansion = result page called...
	const char *expansion = miscutil::lookup(parameters,"expansion");
	int pp = (strcmp(expansion,"")!=0) ? (atoi(expansion)-1) * websearch::_wconfig->_N : 0;
	std::string pp_str = miscutil::to_string(pp);
	miscutil::replace_in_string(q_ggle,"%start",pp_str);

	// number of results.
	int num = websearch::_wconfig->_N; // by default.
	std::string num_str = miscutil::to_string(num);
	miscutil::replace_in_string(q_ggle,"%num",num_str);

	// encoding.
	miscutil::replace_in_string(q_ggle,"%encoding","utf-8");

	// language.
	if (websearch::_wconfig->_lang == "auto")
	  miscutil::replace_in_string(q_ggle,"%lang",qc->_auto_lang);
	else miscutil::replace_in_string(q_ggle,"%lang",websearch::_wconfig->_lang);
	// log the query.
	errlog::log_error(LOG_LEVEL_INFO, "Querying ggle: %s", q_ggle.c_str());

	url = q_ggle;
     }

   /*- se_handler_img -*/
   std::string se_handler_img::_se_strings[IMG_NSEs] =  // in alphabetical order.
     {
	// bing: www.bing.com/images/search?q=markov+chain&go=&form=QBIR
	"http://www.bing.com/images/search?q=%query&first=%start&mkt=%lang",
	// ggle: www.google.com/images?q=markov+chain&hl=en&safe=off&prmd=mi&source=lnms&tbs=isch:1&sa=X&oi=mode_link&ct=mode
     	"http://www.google.com/images?q=%query&start=%start&num=%num&hl=%lang&ie=%encoding&oe=%encoding"
     };

   se_bing_img se_handler_img::_img_bing = se_bing_img();
   se_ggle_img se_handler_img::_img_ggle = se_ggle_img();

   /*-- queries to the image search engines. --*/
   std::string** se_handler_img::query_to_ses(const hash_map<const char*, const char*, hash<const char*>, eqstr> *parameters,
					      int &nresults, const query_context *qc, const std::bitset<IMG_NSEs> &se_enabled)
     {
	std::vector<std::string> urls;
	urls.reserve(IMG_NSEs);
	std::vector<std::list<const char*>*> headers;
	headers.reserve(IMG_NSEs);

	// enabling of SEs.
	for (int i=0;i<IMG_NSEs;i++)
	  {
	     if (se_enabled[i])
	       {
		  std::string url;
		  std::list<const char*> *lheaders = NULL;
		  se_handler_img::query_to_se(parameters,(IMG_SE)i,url,qc,lheaders);
		  urls.push_back(url);
		  headers.push_back(lheaders);
	       }
	  }

	if (urls.empty())
	  {
	     nresults = 0;
	     return NULL; // beware.
	  }
	else nresults = urls.size();

	// get content.
	curl_mget cmg(urls.size(),websearch::_wconfig->_se_transfer_timeout,0,
		      websearch::_wconfig->_se_connect_timeout,0);
	cmg.www_mget(urls,urls.size(),&headers,false);  // don't go through the proxy, or will loop til death!

	std::string **outputs = new std::string*[urls.size()];
	bool have_outputs = false;
	for (size_t i=0;i<urls.size();i++)
	  {
	     outputs[i] = NULL;
	     if (cmg._outputs[i])
	       {
		  outputs[i] = cmg._outputs[i];
		  have_outputs = true;
	       }

	     // delete headers, if any.
	     if (headers.at(i))
	       {
		  miscutil::list_remove_all(headers.at(i));
		  delete headers.at(i);
	       }
	  }
	//
	if (!have_outputs)
	  {
	     delete[] outputs;
	     outputs = NULL;
	  }

	return outputs;
     }

   void se_handler_img::query_to_se(const hash_map<const char*, const char*, hash<const char*>, eqstr> *parameters,
				    const IMG_SE &se, std::string &url, const query_context *qc,
				    std::list<const char*> *&lheaders)
     {
	lheaders = new std::list<const char*>();

	/* pass the user-agent header. */
	std::list<const char*>::const_iterator sit = qc->_useful_http_headers.begin();
	while(sit!=qc->_useful_http_headers.end())
	  {
	     lheaders->push_back(strdup((*sit)));
	     ++sit;
	  }

	switch(se)
	  {
	   case GOOGLE_IMG:
	     _img_ggle.query_to_se(parameters,url,qc);
	     break;
	   case BING_IMG:
	     _img_bing.query_to_se(parameters,url,qc);
	     break;
	  }
     }

   sp_err se_handler_img::parse_ses_output(std::string **outputs, const int &nresults,
					   std::vector<search_snippet*> &snippets,
					   const int &count_offset,
					   query_context *qr,
					   const std::bitset<IMG_NSEs> &se_enabled)
     {
	int j = 0;
	size_t active_ses = se_enabled.count();
	pthread_t parser_threads[active_ses];
	ps_thread_arg* parser_args[active_ses];
	for (size_t i=0;i<active_ses;i++)
	  parser_args[i] = NULL;

	// threads, one per parser.
	int k = 0;
	for (int i=0;i<IMG_NSEs;i++)
	  {
	     if (se_enabled[i])
	       {
		  if (outputs[j])
		    {
		       ps_thread_arg *args = new ps_thread_arg();
		       args->_se = (IMG_SE)i;
		       args->_output = (char*) outputs[j]->c_str();  // XXX: sad cast.
		       args->_snippets = new std::vector<search_snippet*>();
		       args->_offset = count_offset;
		       args->_qr = qr;
		       parser_args[k] = args;

		       pthread_t ps_thread;
		       int err = pthread_create(&ps_thread, NULL,  // default attribute is PTHREAD_CREATE_JOINABLE
						(void * (*)(void *))se_handler_img::parse_output, args);
		       if (err != 0)
			 {
			    errlog::log_error(LOG_LEVEL_ERROR, "Error creating parser thread.");
			    parser_threads[k++] = 0;
			    delete args;
			    parser_args[k] = NULL;
			    continue;
			 }
		       parser_threads[k++] = ps_thread;
		    }
		  else parser_threads[k++] = 0;
		  j++;
	       }
	  }
	// join and merge results.
	for (size_t i=0;i<active_ses;i++)
	  {
	     if (parser_threads[i]!=0)
	       pthread_join(parser_threads[i],NULL);
	  }

	for (size_t i=0;i<active_ses;i++)
	  {
	     if (parser_args[i])
	       {
		  std::copy(parser_args[i]->_snippets->begin(),parser_args[i]->_snippets->end(),
			    std::back_inserter(snippets));
		  parser_args[i]->_snippets->clear();
		  delete parser_args[i]->_snippets;
		  delete parser_args[i];
	       }
	  }
	return SP_ERR_OK;
     }

   void se_handler_img::parse_output(const ps_thread_arg &args)
     {
	se_parser *se = se_handler_img::create_se_parser((IMG_SE)args._se);
	if ((IMG_SE)args._se == BING_IMG)
	  se->parse_output_xml(args._output,args._snippets,args._offset);
	else se->parse_output(args._output,args._snippets,args._offset);

	// link the snippets to the query context
	// and post-process them.
	for (size_t i=0;i<args._snippets->size();i++)
	  {
	     args._snippets->at(i)->_qc = args._qr;
	     args._snippets->at(i)->tag();
	  }
	delete se;
     }

   se_parser* se_handler_img::create_se_parser(const IMG_SE &se)
     {
	se_parser *sep = NULL;
	switch(se)
	  {
	   case GOOGLE_IMG:
	     sep = new se_parser_ggle_img();
	     break;
	   case BING_IMG:
	     sep = new se_parser_bing_img();
	     break;
	  }
	return sep;
     }

} /* end of namespace. */
<commit_msg>prevent ggle from using the user-agent header to render the image search output<commit_after>/**
 * The Seeks proxy and plugin framework are part of the SEEKS project.
 * Copyright (C) 2010 Emmanuel Benazera, juban@free.fr
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "se_handler_img.h"
#include "websearch.h"
#include "curl_mget.h"
#include "miscutil.h"
#include "encode.h"
#include "errlog.h"

#include "se_parser_bing_img.h"
#include "se_parser_ggle_img.h"

using namespace sp;

namespace seeks_plugins
{
   se_bing_img::se_bing_img()
     :search_engine()
       {
       }

   se_bing_img::~se_bing_img()
     {
     }

   void se_bing_img::query_to_se(const hash_map<const char*, const char*, hash<const char*>, eqstr> *parameters,
				 std::string &url, const query_context *qc)
     {
	std::string q_bing = se_handler_img::_se_strings[BING_IMG]; // query to bing.
	const char *query = miscutil::lookup(parameters,"q");

	// query.
	int p = 36;
	char *qenc = encode::url_encode(se_handler::no_command_query(std::string(query)).c_str());
	std::string qenc_str = std::string(qenc);
	free(qenc);
	q_bing.replace(p,6,qenc_str);

	// page.
	const char *expansion = miscutil::lookup(parameters,"expansion");
	int pp = (strcmp(expansion,"")!=0) ? (atoi(expansion)-1) * websearch::_wconfig->_N : 0;
	std::string pp_str = miscutil::to_string(pp);
	miscutil::replace_in_string(q_bing,"%start",pp_str);

	// language.
	miscutil::replace_in_string(q_bing,"%lang",qc->_auto_lang_reg);

	// log the query.
	errlog::log_error(LOG_LEVEL_INFO, "Querying bing: %s", q_bing.c_str());

	url = q_bing;
     }

   se_ggle_img::se_ggle_img()
     :search_engine()
       {
       }

   se_ggle_img::~se_ggle_img()
     {
     }

   void se_ggle_img::query_to_se(const hash_map<const char*, const char*, hash<const char*>, eqstr> *parameters,
				 std::string &url, const query_context *qc)
     {
	std::string q_ggle = se_handler_img::_se_strings[GOOGLE_IMG]; // query to ggle.
	const char *query = miscutil::lookup(parameters,"q");

	// query.
	int p = 31;
	char *qenc = encode::url_encode(se_handler::no_command_query(std::string(query)).c_str());
	std::string qenc_str = std::string(qenc);
	free(qenc);
	q_ggle.replace(p,6,qenc_str);

	// expansion = result page called...
	const char *expansion = miscutil::lookup(parameters,"expansion");
	int pp = (strcmp(expansion,"")!=0) ? (atoi(expansion)-1) * websearch::_wconfig->_N : 0;
	std::string pp_str = miscutil::to_string(pp);
	miscutil::replace_in_string(q_ggle,"%start",pp_str);

	// number of results.
	int num = websearch::_wconfig->_N; // by default.
	std::string num_str = miscutil::to_string(num);
	miscutil::replace_in_string(q_ggle,"%num",num_str);

	// encoding.
	miscutil::replace_in_string(q_ggle,"%encoding","utf-8");

	// language.
	if (websearch::_wconfig->_lang == "auto")
	  miscutil::replace_in_string(q_ggle,"%lang",qc->_auto_lang);
	else miscutil::replace_in_string(q_ggle,"%lang",websearch::_wconfig->_lang);
	// log the query.
	errlog::log_error(LOG_LEVEL_INFO, "Querying ggle: %s", q_ggle.c_str());

	url = q_ggle;
     }

   /*- se_handler_img -*/
   std::string se_handler_img::_se_strings[IMG_NSEs] =  // in alphabetical order.
     {
	// bing: www.bing.com/images/search?q=markov+chain&go=&form=QBIR
	"http://www.bing.com/images/search?q=%query&first=%start&mkt=%lang",
	// ggle: www.google.com/images?q=markov+chain&hl=en&safe=off&prmd=mi&source=lnms&tbs=isch:1&sa=X&oi=mode_link&ct=mode
     	"http://www.google.com/images?q=%query&start=%start&num=%num&hl=%lang&ie=%encoding&oe=%encoding"
     };

   se_bing_img se_handler_img::_img_bing = se_bing_img();
   se_ggle_img se_handler_img::_img_ggle = se_ggle_img();

   /*-- queries to the image search engines. --*/
   std::string** se_handler_img::query_to_ses(const hash_map<const char*, const char*, hash<const char*>, eqstr> *parameters,
					      int &nresults, const query_context *qc, const std::bitset<IMG_NSEs> &se_enabled)
     {
	std::vector<std::string> urls;
	urls.reserve(IMG_NSEs);
	std::vector<std::list<const char*>*> headers;
	headers.reserve(IMG_NSEs);

	// enabling of SEs.
	for (int i=0;i<IMG_NSEs;i++)
	  {
	     if (se_enabled[i])
	       {
		  std::string url;
		  std::list<const char*> *lheaders = NULL;
		  se_handler_img::query_to_se(parameters,(IMG_SE)i,url,qc,lheaders);
		  urls.push_back(url);
		  headers.push_back(lheaders);
	       }
	  }

	if (urls.empty())
	  {
	     nresults = 0;
	     return NULL; // beware.
	  }
	else nresults = urls.size();

	// get content.
	curl_mget cmg(urls.size(),websearch::_wconfig->_se_transfer_timeout,0,
		      websearch::_wconfig->_se_connect_timeout,0);
	cmg.www_mget(urls,urls.size(),&headers,false);  // don't go through the proxy, or will loop til death!

	std::string **outputs = new std::string*[urls.size()];
	bool have_outputs = false;
	for (size_t i=0;i<urls.size();i++)
	  {
	     outputs[i] = NULL;
	     if (cmg._outputs[i])
	       {
		  outputs[i] = cmg._outputs[i];
		  have_outputs = true;
	       }

	     // delete headers, if any.
	     if (headers.at(i))
	       {
		  miscutil::list_remove_all(headers.at(i));
		  delete headers.at(i);
	       }
	  }
	//
	if (!have_outputs)
	  {
	     delete[] outputs;
	     outputs = NULL;
	  }

	return outputs;
     }

   void se_handler_img::query_to_se(const hash_map<const char*, const char*, hash<const char*>, eqstr> *parameters,
				    const IMG_SE &se, std::string &url, const query_context *qc,
				    std::list<const char*> *&lheaders)
     {
	lheaders = new std::list<const char*>();

	/* pass the user-agent header. */
	std::list<const char*>::const_iterator sit = qc->_useful_http_headers.begin();
	while(sit!=qc->_useful_http_headers.end())
	  {
	     if (se == GOOGLE_IMG && miscutil::strncmpic((*sit),"user-agent:",11) == 0)
	       {
		  // XXX: ggle seems to render queries based on the user agent.
	       }
	     else lheaders->push_back(strdup((*sit)));
	     ++sit;
	  }

	switch(se)
	  {
	   case GOOGLE_IMG:
	     _img_ggle.query_to_se(parameters,url,qc);
	     break;
	   case BING_IMG:
	     _img_bing.query_to_se(parameters,url,qc);
	     break;
	  }
     }

   sp_err se_handler_img::parse_ses_output(std::string **outputs, const int &nresults,
					   std::vector<search_snippet*> &snippets,
					   const int &count_offset,
					   query_context *qr,
					   const std::bitset<IMG_NSEs> &se_enabled)
     {
	int j = 0;
	size_t active_ses = se_enabled.count();
	pthread_t parser_threads[active_ses];
	ps_thread_arg* parser_args[active_ses];
	for (size_t i=0;i<active_ses;i++)
	  parser_args[i] = NULL;

	// threads, one per parser.
	int k = 0;
	for (int i=0;i<IMG_NSEs;i++)
	  {
	     if (se_enabled[i])
	       {
		  if (outputs[j])
		    {
		       ps_thread_arg *args = new ps_thread_arg();
		       args->_se = (IMG_SE)i;
		       args->_output = (char*) outputs[j]->c_str();  // XXX: sad cast.
		       args->_snippets = new std::vector<search_snippet*>();
		       args->_offset = count_offset;
		       args->_qr = qr;
		       parser_args[k] = args;

		       pthread_t ps_thread;
		       int err = pthread_create(&ps_thread, NULL,  // default attribute is PTHREAD_CREATE_JOINABLE
						(void * (*)(void *))se_handler_img::parse_output, args);
		       if (err != 0)
			 {
			    errlog::log_error(LOG_LEVEL_ERROR, "Error creating parser thread.");
			    parser_threads[k++] = 0;
			    delete args;
			    parser_args[k] = NULL;
			    continue;
			 }
		       parser_threads[k++] = ps_thread;
		    }
		  else parser_threads[k++] = 0;
		  j++;
	       }
	  }
	// join and merge results.
	for (size_t i=0;i<active_ses;i++)
	  {
	     if (parser_threads[i]!=0)
	       pthread_join(parser_threads[i],NULL);
	  }

	for (size_t i=0;i<active_ses;i++)
	  {
	     if (parser_args[i])
	       {
		  std::copy(parser_args[i]->_snippets->begin(),parser_args[i]->_snippets->end(),
			    std::back_inserter(snippets));
		  parser_args[i]->_snippets->clear();
		  delete parser_args[i]->_snippets;
		  delete parser_args[i];
	       }
	  }
	return SP_ERR_OK;
     }

   void se_handler_img::parse_output(const ps_thread_arg &args)
     {
	se_parser *se = se_handler_img::create_se_parser((IMG_SE)args._se);
	if ((IMG_SE)args._se == BING_IMG)
	  se->parse_output_xml(args._output,args._snippets,args._offset);
	else se->parse_output(args._output,args._snippets,args._offset);

	// link the snippets to the query context
	// and post-process them.
	for (size_t i=0;i<args._snippets->size();i++)
	  {
	     args._snippets->at(i)->_qc = args._qr;
	     args._snippets->at(i)->tag();
	  }
	delete se;
     }

   se_parser* se_handler_img::create_se_parser(const IMG_SE &se)
     {
	se_parser *sep = NULL;
	switch(se)
	  {
	   case GOOGLE_IMG:
	     sep = new se_parser_ggle_img();
	     break;
	   case BING_IMG:
	     sep = new se_parser_bing_img();
	     break;
	  }
	return sep;
     }

} /* end of namespace. */
<|endoftext|>


<commit_before>#include <tbb/blocked_range2d.h>
#include <tbb/enumerable_thread_specific.h>
#include <tbb/parallel_for.h>
#include <tbb/task_group.h>

#include <possumwood_sdk/node_implementation.h>

#include <actions/traits.h>

#include "sequence.h"

namespace {

dependency_graph::InAttr<possumwood::opencv::Sequence> a_inSequence;
dependency_graph::InAttr<unsigned> a_kernelSize;
dependency_graph::OutAttr<possumwood::opencv::Sequence> a_outSequence;

template <typename T>
class MedianFilter {
  public:
	MedianFilter(int size) : m_size(size / 2), m_buffer(std::vector<T>(size * size)) {
		assert(size % 2 == 1);
	}

	T operator()(const cv::Mat& m, int x, int y, int channel) {
		auto& buffer = m_buffer.local();

		buffer.clear();
		for(int yi = std::max(0, y - m_size); yi <= std::min(y + m_size, m.cols - 1); ++yi)
			for(int xi = std::max(0, x - m_size); xi <= std::min(x + m_size, m.rows - 1); ++xi) {
				buffer.push_back(m.ptr<T>(yi, xi)[channel]);
			}

		std::sort(buffer.begin(), buffer.end());

		return buffer[buffer.size() / 2];
	}

  private:
	int m_size;
	tbb::enumerable_thread_specific<std::vector<T>> m_buffer;
};

template <typename T>
void doTheThing(const possumwood::opencv::Sequence& input, possumwood::opencv::Sequence& result, int kernelSize) {
	tbb::task_group group;

	for(auto it = input.begin(); it != input.end(); ++it) {
		group.run([it, &result, kernelSize]() {
			const cv::Mat& in = it->second;
			cv::Mat out = cv::Mat::zeros(in.rows, in.cols, in.type());

			MedianFilter<T> filter(kernelSize);

			tbb::parallel_for(tbb::blocked_range2d<int>(0, in.rows, 0, in.cols),
			                  [&](const tbb::blocked_range2d<int>& range) {
				                  for(int y = range.rows().begin(); y != range.rows().end(); ++y)
					                  for(int x = range.cols().begin(); x != range.cols().end(); ++x)
						                  for(int c = 0; c < in.channels(); ++c)
							                  out.ptr<T>(y, x)[c] = filter(in, x, y, c);
			                  });

			result[it->first] = std::move(out);
		});
	}

	group.wait();
}

dependency_graph::State compute(dependency_graph::Values& data) {
	if(data.get(a_kernelSize) % 2 != 1)
		throw std::runtime_error("Median filter size should be an odd number.");

	const possumwood::opencv::Sequence& input = data.get(a_inSequence);
	possumwood::opencv::Sequence result;

	if(!input.empty()) {
		switch(input.begin()->second.depth()) {
			case CV_32F:
				doTheThing<float>(input, result, data.get(a_kernelSize));
				break;
			case CV_8U:
				doTheThing<uint8_t>(input, result, data.get(a_kernelSize));
				break;
			case CV_16U:
				doTheThing<uint16_t>(input, result, data.get(a_kernelSize));
				break;
			default:
				throw std::runtime_error("Unsupported sequence type on input");
		}
	}

	data.set(a_outSequence, result);

	return dependency_graph::State();
}

void init(possumwood::Metadata& meta) {
	meta.addAttribute(a_inSequence, "in_seq", possumwood::opencv::Sequence());
	meta.addAttribute(a_kernelSize, "kernel_size", 5u);
	meta.addAttribute(a_outSequence, "out_seq", possumwood::opencv::Sequence());

	meta.addInfluence(a_inSequence, a_outSequence);
	meta.addInfluence(a_kernelSize, a_outSequence);

	meta.setCompute(compute);
}

possumwood::NodeImplementation s_impl("opencv/sequence/median", init);

}  // namespace
<commit_msg>Marking MedianFilter constructor explicit<commit_after>#include <tbb/blocked_range2d.h>
#include <tbb/enumerable_thread_specific.h>
#include <tbb/parallel_for.h>
#include <tbb/task_group.h>

#include <possumwood_sdk/node_implementation.h>

#include <actions/traits.h>

#include "sequence.h"

namespace {

dependency_graph::InAttr<possumwood::opencv::Sequence> a_inSequence;
dependency_graph::InAttr<unsigned> a_kernelSize;
dependency_graph::OutAttr<possumwood::opencv::Sequence> a_outSequence;

template <typename T>
class MedianFilter {
  public:
	explicit MedianFilter(int size) : m_size(size / 2), m_buffer(std::vector<T>(size * size)) {
		assert(size % 2 == 1);
	}

	T operator()(const cv::Mat& m, int x, int y, int channel) {
		auto& buffer = m_buffer.local();

		buffer.clear();
		for(int yi = std::max(0, y - m_size); yi <= std::min(y + m_size, m.cols - 1); ++yi)
			for(int xi = std::max(0, x - m_size); xi <= std::min(x + m_size, m.rows - 1); ++xi) {
				buffer.push_back(m.ptr<T>(yi, xi)[channel]);
			}

		std::sort(buffer.begin(), buffer.end());

		return buffer[buffer.size() / 2];
	}

  private:
	int m_size;
	tbb::enumerable_thread_specific<std::vector<T>> m_buffer;
};

template <typename T>
void doTheThing(const possumwood::opencv::Sequence& input, possumwood::opencv::Sequence& result, int kernelSize) {
	tbb::task_group group;

	for(auto it = input.begin(); it != input.end(); ++it) {
		group.run([it, &result, kernelSize]() {
			const cv::Mat& in = it->second;
			cv::Mat out = cv::Mat::zeros(in.rows, in.cols, in.type());

			MedianFilter<T> filter(kernelSize);

			tbb::parallel_for(tbb::blocked_range2d<int>(0, in.rows, 0, in.cols),
			                  [&](const tbb::blocked_range2d<int>& range) {
				                  for(int y = range.rows().begin(); y != range.rows().end(); ++y)
					                  for(int x = range.cols().begin(); x != range.cols().end(); ++x)
						                  for(int c = 0; c < in.channels(); ++c)
							                  out.ptr<T>(y, x)[c] = filter(in, x, y, c);
			                  });

			result[it->first] = std::move(out);
		});
	}

	group.wait();
}

dependency_graph::State compute(dependency_graph::Values& data) {
	if(data.get(a_kernelSize) % 2 != 1)
		throw std::runtime_error("Median filter size should be an odd number.");

	const possumwood::opencv::Sequence& input = data.get(a_inSequence);
	possumwood::opencv::Sequence result;

	if(!input.empty()) {
		switch(input.begin()->second.depth()) {
			case CV_32F:
				doTheThing<float>(input, result, data.get(a_kernelSize));
				break;
			case CV_8U:
				doTheThing<uint8_t>(input, result, data.get(a_kernelSize));
				break;
			case CV_16U:
				doTheThing<uint16_t>(input, result, data.get(a_kernelSize));
				break;
			default:
				throw std::runtime_error("Unsupported sequence type on input");
		}
	}

	data.set(a_outSequence, result);

	return dependency_graph::State();
}

void init(possumwood::Metadata& meta) {
	meta.addAttribute(a_inSequence, "in_seq", possumwood::opencv::Sequence());
	meta.addAttribute(a_kernelSize, "kernel_size", 5u);
	meta.addAttribute(a_outSequence, "out_seq", possumwood::opencv::Sequence());

	meta.addInfluence(a_inSequence, a_outSequence);
	meta.addInfluence(a_kernelSize, a_outSequence);

	meta.setCompute(compute);
}

possumwood::NodeImplementation s_impl("opencv/sequence/median", init);

}  // namespace
<|endoftext|>


<commit_before>/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** Commercial Usage
**
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
**
** GNU Lesser General Public License Usage
**
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** If you are unsure which license is appropriate for your use, please
** contact the sales department at http://qt.nokia.com/contact.
**
**************************************************************************/

#include "outputwindow.h"
#include "projectexplorerconstants.h"
#include "runconfiguration.h"

#include <coreplugin/actionmanager/actionmanager.h>
#include <coreplugin/coreconstants.h>
#include <coreplugin/icore.h>
#include <coreplugin/uniqueidmanager.h>
#include <find/basetextfind.h>
#include <aggregation/aggregate.h>

#include <QtGui/QIcon>
#include <QtGui/QScrollBar>
#include <QtGui/QTextLayout>
#include <QtGui/QPainter>
#include <QtGui/QApplication>
#include <QtGui/QClipboard>
#include <QtGui/QMenu>
#include <QtGui/QMessageBox>
#include <QtGui/QVBoxLayout>
#include <QtGui/QTabWidget>

#include <QtGui/QApplication>

using namespace ProjectExplorer::Internal;
using namespace ProjectExplorer;

static const int MaxBlockCount = 100000;

OutputPane::OutputPane()
    : m_mainWidget(new QWidget)
{
//     m_insertLineButton = new QToolButton;
//     m_insertLineButton->setIcon(QIcon(ProjectExplorer::Constants::ICON_INSERT_LINE));
//     m_insertLineButton->setText(tr("Insert line"));
//     m_insertLineButton->setToolTip(tr("Insert line"));
//     m_insertLineButton->setAutoRaise(true);
//     connect(m_insertLineButton, SIGNAL(clicked()), this, SLOT(insertLine()));

    QIcon runIcon(Constants::ICON_RUN);
    runIcon.addFile(Constants::ICON_RUN_SMALL);

    // Rerun
    m_reRunButton = new QToolButton;
    m_reRunButton->setIcon(runIcon);
    m_reRunButton->setToolTip(tr("Re-run this run-configuration"));
    m_reRunButton->setAutoRaise(true);
    m_reRunButton->setEnabled(false);
    connect(m_reRunButton, SIGNAL(clicked()),
            this, SLOT(reRunRunControl()));

    // Stop
    Core::ActionManager *am = Core::ICore::instance()->actionManager();
    QList<int> globalcontext;
    globalcontext.append(Core::Constants::C_GLOBAL_ID);

    m_stopAction = new QAction(QIcon(Constants::ICON_STOP), tr("Stop"), this);
    m_stopAction->setToolTip(tr("Stop"));
    m_stopAction->setEnabled(false);

    Core::Command *cmd = am->registerAction(m_stopAction, Constants::STOP, globalcontext);
    cmd->setDefaultKeySequence(QKeySequence(tr("Ctrl+Shift+R")));

    m_stopButton = new QToolButton;
    m_stopButton->setDefaultAction(cmd->action());
    m_stopButton->setAutoRaise(true);

    connect(m_stopAction, SIGNAL(triggered()),
            this, SLOT(stopRunControl()));

    // Spacer (?)

    QVBoxLayout *layout = new QVBoxLayout;
    layout->setMargin(0);
    m_tabWidget = new QTabWidget;
    m_tabWidget->setDocumentMode(true);
    m_tabWidget->setTabsClosable(true);
    m_tabWidget->setMovable(true);
    connect(m_tabWidget, SIGNAL(tabCloseRequested(int)), this, SLOT(closeTab(int)));
    layout->addWidget(m_tabWidget);

    connect(m_tabWidget, SIGNAL(currentChanged(int)), this, SLOT(tabChanged(int)));

    m_mainWidget->setLayout(layout);

    connect(Core::ICore::instance(), SIGNAL(coreAboutToClose()),
            this, SLOT(coreAboutToClose()));
}

void OutputPane::coreAboutToClose()
{
    while (m_tabWidget->count()) {
        RunControl *rc = runControlForTab(0);
        if (rc->isRunning())
            rc->stop();
        closeTab(0);
    }
}

OutputPane::~OutputPane()
{
    delete m_mainWidget;
}

QWidget *OutputPane::outputWidget(QWidget *)
{
    return m_mainWidget;
}

QList<QWidget*> OutputPane::toolBarWidgets() const
{
    return QList<QWidget*>() << m_reRunButton << m_stopButton
            ; // << m_insertLineButton;
}

QString OutputPane::name() const
{
    return tr("Application Output");
}

int OutputPane::priorityInStatusBar() const
{
    return 60;
}

void OutputPane::clearContents()
{
    OutputWindow *currentWindow = qobject_cast<OutputWindow *>(m_tabWidget->currentWidget());
    if (currentWindow)
        currentWindow->clear();
}

void OutputPane::visibilityChanged(bool /* b */)
{
}

bool OutputPane::hasFocus()
{
    return m_tabWidget->currentWidget() && m_tabWidget->currentWidget()->hasFocus();
}

bool OutputPane::canFocus()
{
    return m_tabWidget->currentWidget();
}

void OutputPane::setFocus()
{
    if (m_tabWidget->currentWidget())
        m_tabWidget->currentWidget()->setFocus();
}

void OutputPane::appendOutput(const QString &/*out*/)
{
    // This function is in the interface, since we can't do anything sensible here, we don't do anything here.
}

void OutputPane::createNewOutputWindow(RunControl *rc)
{
    connect(rc, SIGNAL(started()),
            this, SLOT(runControlStarted()));
    connect(rc, SIGNAL(finished()),
            this, SLOT(runControlFinished()));

    // First look if we can reuse a tab
    bool found = false;
    for (int i = 0; i < m_tabWidget->count(); ++i) {
        RunControl *old = runControlForTab(i);
        if (old->runConfiguration() == rc->runConfiguration() && !old->isRunning()) {
            // Reuse this tab
            delete old;
            m_outputWindows.remove(old);
            OutputWindow *ow = static_cast<OutputWindow *>(m_tabWidget->widget(i));
            ow->appendOutput("");//New line
            m_outputWindows.insert(rc, ow);
            found = true;
            break;
        }
    }
    if (!found) {
        OutputWindow *ow = new OutputWindow(m_tabWidget);
        Aggregation::Aggregate *agg = new Aggregation::Aggregate;
        agg->add(ow);
        agg->add(new Find::BaseTextFind(ow));
        m_outputWindows.insert(rc, ow);
        m_tabWidget->addTab(ow, rc->runConfiguration()->name());
    }
}

void OutputPane::appendOutput(RunControl *rc, const QString &out)
{
    OutputWindow *ow = m_outputWindows.value(rc);
    ow->appendOutput(out);
}

void OutputPane::appendOutputInline(RunControl *rc, const QString &out)
{
    OutputWindow *ow = m_outputWindows.value(rc);
    ow->appendOutputInline(out);
}

void OutputPane::showTabFor(RunControl *rc)
{
    OutputWindow *ow = m_outputWindows.value(rc);
    m_tabWidget->setCurrentWidget(ow);
}

void OutputPane::insertLine()
{
    OutputWindow *currentWindow = qobject_cast<OutputWindow *>(m_tabWidget->currentWidget());
    if (currentWindow)
        currentWindow->clear();
}

void OutputPane::reRunRunControl()
{
    RunControl *rc = runControlForTab(m_tabWidget->currentIndex());
    if (rc->runConfiguration()->project() != 0)
        rc->start();
}

void OutputPane::stopRunControl()
{
    RunControl *rc = runControlForTab(m_tabWidget->currentIndex());
    rc->stop();
}

void OutputPane::closeTab(int index)
{
    OutputWindow *ow = static_cast<OutputWindow *>(m_tabWidget->widget(index));
    RunControl *rc = m_outputWindows.key(ow);

    if (rc->isRunning()) {
        QString msg = tr("The application is still running. Close it first.");
        QMessageBox::critical(0, tr("Unable to close"), msg);
        return;
    }

    m_tabWidget->removeTab(index);
    delete ow;
    delete rc;
}

void OutputPane::projectRemoved()
{
    tabChanged(m_tabWidget->currentIndex());
}

void OutputPane::tabChanged(int i)
{
    if (i == -1) {
        m_stopAction->setEnabled(false);
        m_reRunButton->setEnabled(false);
    } else {
        RunControl *rc = runControlForTab(i);
        m_stopAction->setEnabled(rc->isRunning());
        m_reRunButton->setEnabled(!rc->isRunning() && rc->runConfiguration()->project());
    }
}

void OutputPane::runControlStarted()
{
    RunControl *rc = runControlForTab(m_tabWidget->currentIndex());
    if (rc == qobject_cast<RunControl *>(sender())) {
        m_reRunButton->setEnabled(false);
        m_stopAction->setEnabled(true);
    }
}

void OutputPane::runControlFinished()
{
    RunControl *rc = runControlForTab(m_tabWidget->currentIndex());
    if (rc == qobject_cast<RunControl *>(sender())) {
        m_reRunButton->setEnabled(rc->runConfiguration()->project());
        m_stopAction->setEnabled(false);
    }
}

RunControl* OutputPane::runControlForTab(int index) const
{
    return m_outputWindows.key(qobject_cast<OutputWindow *>(m_tabWidget->widget(index)));
}

bool OutputPane::canNext()
{
    return false;
}

bool OutputPane::canPrevious()
{
    return false;
}

void OutputPane::goToNext()
{

}

void OutputPane::goToPrev()
{

}

bool OutputPane::canNavigate()
{
    return false;
}

/*******************/

OutputWindow::OutputWindow(QWidget *parent)
    : QPlainTextEdit(parent)
{
    m_enforceNewline = false;
    m_scrollToBottom = false;

    setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
    //setCenterOnScroll(false);
    setWindowTitle(tr("Application Output Window"));
    setWindowIcon(QIcon(":/qt4projectmanager/images/window.png"));
    setFrameShape(QFrame::NoFrame);

    static uint usedIds = 0;
    Core::ICore *core = Core::ICore::instance();
    QList<int> context;
    context << core->uniqueIDManager()->uniqueIdentifier(QString(Constants::C_APP_OUTPUT) + QString().setNum(usedIds++));
    m_outputWindowContext = new Core::BaseContext(this, context);
    core->addContextObject(m_outputWindowContext);

    QAction *undoAction = new QAction(this);
    QAction *redoAction = new QAction(this);
    QAction *cutAction = new QAction(this);
    QAction *copyAction = new QAction(this);
    QAction *pasteAction = new QAction(this);
    QAction *selectAllAction = new QAction(this);

    core->actionManager()->registerAction(undoAction, Core::Constants::UNDO, context);
    core->actionManager()->registerAction(redoAction, Core::Constants::REDO, context);
    core->actionManager()->registerAction(cutAction, Core::Constants::CUT, context);
    core->actionManager()->registerAction(copyAction, Core::Constants::COPY, context);
    core->actionManager()->registerAction(pasteAction, Core::Constants::PASTE, context);
    core->actionManager()->registerAction(selectAllAction, Core::Constants::SELECTALL, context);

    connect(undoAction, SIGNAL(triggered()), this, SLOT(undo()));
    connect(redoAction, SIGNAL(triggered()), this, SLOT(redo()));
    connect(cutAction, SIGNAL(triggered()), this, SLOT(cut()));
    connect(copyAction, SIGNAL(triggered()), this, SLOT(copy()));
    connect(pasteAction, SIGNAL(triggered()), this, SLOT(paste()));
    connect(selectAllAction, SIGNAL(triggered()), this, SLOT(selectAll()));

    connect(this, SIGNAL(undoAvailable(bool)), undoAction, SLOT(setEnabled(bool)));
    connect(this, SIGNAL(redoAvailable(bool)), redoAction, SLOT(setEnabled(bool)));
    connect(this, SIGNAL(copyAvailable(bool)), cutAction, SLOT(setEnabled(bool)));  // OutputWindow never read-only
    connect(this, SIGNAL(copyAvailable(bool)), copyAction, SLOT(setEnabled(bool)));

    undoAction->setEnabled(false);
    redoAction->setEnabled(false);
    cutAction->setEnabled(false);
    copyAction->setEnabled(false);
}

OutputWindow::~OutputWindow()
{
    Core::ICore::instance()->removeContextObject(m_outputWindowContext);
}

void OutputWindow::showEvent(QShowEvent *e)
{
    QPlainTextEdit::showEvent(e);
    if (m_scrollToBottom) {
        verticalScrollBar()->setValue(verticalScrollBar()->maximum());
    }
    m_scrollToBottom = false;
}

void OutputWindow::appendOutput(const QString &out)
{
    m_scrollToBottom = true;
    QString s = out;
    m_enforceNewline = true; // make appendOutputInline put in a newline next time
    if (s.endsWith(QLatin1Char('\n'))) {
        s.chop(1);
    }
    setMaximumBlockCount(MaxBlockCount);
    appendPlainText(out);
    enableUndoRedo();
}

void OutputWindow::appendOutputInline(const QString &out)
{
    m_scrollToBottom = true;
    setMaximumBlockCount(MaxBlockCount);

    int newline = -1;
    bool enforceNewline = m_enforceNewline;
    m_enforceNewline = false;

    if (!enforceNewline) {
        newline = out.indexOf(QLatin1Char('\n'));
        moveCursor(QTextCursor::End);
        bool atBottom = (blockBoundingRect(document()->lastBlock()).bottom() + contentOffset().y()
                         <= viewport()->rect().bottom());
        insertPlainText(newline < 0 ? out : out.left(newline)); // doesn't enforce new paragraph like appendPlainText
        if (atBottom)
            verticalScrollBar()->setValue(verticalScrollBar()->maximum());
    }

    QString s = out.mid(newline+1);
    if (s.isEmpty()) {
        m_enforceNewline = true;
    } else {
        if (s.endsWith(QLatin1Char('\n'))) {
            m_enforceNewline = true;
            s.chop(1);
        }
        appendPlainText(s);
    }

    enableUndoRedo();
}

void OutputWindow::insertLine()
{
    m_scrollToBottom = true;
    setMaximumBlockCount(MaxBlockCount);
    appendPlainText(QString());
    enableUndoRedo();
}

void OutputWindow::enableUndoRedo()
{
    setMaximumBlockCount(0);
    setUndoRedoEnabled(true);
}
<commit_msg>Fix memory leaking a context per output window<commit_after>/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** Commercial Usage
**
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
**
** GNU Lesser General Public License Usage
**
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** If you are unsure which license is appropriate for your use, please
** contact the sales department at http://qt.nokia.com/contact.
**
**************************************************************************/

#include "outputwindow.h"
#include "projectexplorerconstants.h"
#include "runconfiguration.h"

#include <coreplugin/actionmanager/actionmanager.h>
#include <coreplugin/coreconstants.h>
#include <coreplugin/icore.h>
#include <coreplugin/uniqueidmanager.h>
#include <find/basetextfind.h>
#include <aggregation/aggregate.h>

#include <QtGui/QIcon>
#include <QtGui/QScrollBar>
#include <QtGui/QTextLayout>
#include <QtGui/QPainter>
#include <QtGui/QApplication>
#include <QtGui/QClipboard>
#include <QtGui/QMenu>
#include <QtGui/QMessageBox>
#include <QtGui/QVBoxLayout>
#include <QtGui/QTabWidget>

#include <QtGui/QApplication>

using namespace ProjectExplorer::Internal;
using namespace ProjectExplorer;

static const int MaxBlockCount = 100000;

OutputPane::OutputPane()
    : m_mainWidget(new QWidget)
{
//     m_insertLineButton = new QToolButton;
//     m_insertLineButton->setIcon(QIcon(ProjectExplorer::Constants::ICON_INSERT_LINE));
//     m_insertLineButton->setText(tr("Insert line"));
//     m_insertLineButton->setToolTip(tr("Insert line"));
//     m_insertLineButton->setAutoRaise(true);
//     connect(m_insertLineButton, SIGNAL(clicked()), this, SLOT(insertLine()));

    QIcon runIcon(Constants::ICON_RUN);
    runIcon.addFile(Constants::ICON_RUN_SMALL);

    // Rerun
    m_reRunButton = new QToolButton;
    m_reRunButton->setIcon(runIcon);
    m_reRunButton->setToolTip(tr("Re-run this run-configuration"));
    m_reRunButton->setAutoRaise(true);
    m_reRunButton->setEnabled(false);
    connect(m_reRunButton, SIGNAL(clicked()),
            this, SLOT(reRunRunControl()));

    // Stop
    Core::ActionManager *am = Core::ICore::instance()->actionManager();
    QList<int> globalcontext;
    globalcontext.append(Core::Constants::C_GLOBAL_ID);

    m_stopAction = new QAction(QIcon(Constants::ICON_STOP), tr("Stop"), this);
    m_stopAction->setToolTip(tr("Stop"));
    m_stopAction->setEnabled(false);

    Core::Command *cmd = am->registerAction(m_stopAction, Constants::STOP, globalcontext);
    cmd->setDefaultKeySequence(QKeySequence(tr("Ctrl+Shift+R")));

    m_stopButton = new QToolButton;
    m_stopButton->setDefaultAction(cmd->action());
    m_stopButton->setAutoRaise(true);

    connect(m_stopAction, SIGNAL(triggered()),
            this, SLOT(stopRunControl()));

    // Spacer (?)

    QVBoxLayout *layout = new QVBoxLayout;
    layout->setMargin(0);
    m_tabWidget = new QTabWidget;
    m_tabWidget->setDocumentMode(true);
    m_tabWidget->setTabsClosable(true);
    m_tabWidget->setMovable(true);
    connect(m_tabWidget, SIGNAL(tabCloseRequested(int)), this, SLOT(closeTab(int)));
    layout->addWidget(m_tabWidget);

    connect(m_tabWidget, SIGNAL(currentChanged(int)), this, SLOT(tabChanged(int)));

    m_mainWidget->setLayout(layout);

    connect(Core::ICore::instance(), SIGNAL(coreAboutToClose()),
            this, SLOT(coreAboutToClose()));
}

void OutputPane::coreAboutToClose()
{
    while (m_tabWidget->count()) {
        RunControl *rc = runControlForTab(0);
        if (rc->isRunning())
            rc->stop();
        closeTab(0);
    }
}

OutputPane::~OutputPane()
{
    delete m_mainWidget;
}

QWidget *OutputPane::outputWidget(QWidget *)
{
    return m_mainWidget;
}

QList<QWidget*> OutputPane::toolBarWidgets() const
{
    return QList<QWidget*>() << m_reRunButton << m_stopButton
            ; // << m_insertLineButton;
}

QString OutputPane::name() const
{
    return tr("Application Output");
}

int OutputPane::priorityInStatusBar() const
{
    return 60;
}

void OutputPane::clearContents()
{
    OutputWindow *currentWindow = qobject_cast<OutputWindow *>(m_tabWidget->currentWidget());
    if (currentWindow)
        currentWindow->clear();
}

void OutputPane::visibilityChanged(bool /* b */)
{
}

bool OutputPane::hasFocus()
{
    return m_tabWidget->currentWidget() && m_tabWidget->currentWidget()->hasFocus();
}

bool OutputPane::canFocus()
{
    return m_tabWidget->currentWidget();
}

void OutputPane::setFocus()
{
    if (m_tabWidget->currentWidget())
        m_tabWidget->currentWidget()->setFocus();
}

void OutputPane::appendOutput(const QString &/*out*/)
{
    // This function is in the interface, since we can't do anything sensible here, we don't do anything here.
}

void OutputPane::createNewOutputWindow(RunControl *rc)
{
    connect(rc, SIGNAL(started()),
            this, SLOT(runControlStarted()));
    connect(rc, SIGNAL(finished()),
            this, SLOT(runControlFinished()));

    // First look if we can reuse a tab
    bool found = false;
    for (int i = 0; i < m_tabWidget->count(); ++i) {
        RunControl *old = runControlForTab(i);
        if (old->runConfiguration() == rc->runConfiguration() && !old->isRunning()) {
            // Reuse this tab
            delete old;
            m_outputWindows.remove(old);
            OutputWindow *ow = static_cast<OutputWindow *>(m_tabWidget->widget(i));
            ow->appendOutput("");//New line
            m_outputWindows.insert(rc, ow);
            found = true;
            break;
        }
    }
    if (!found) {
        OutputWindow *ow = new OutputWindow(m_tabWidget);
        Aggregation::Aggregate *agg = new Aggregation::Aggregate;
        agg->add(ow);
        agg->add(new Find::BaseTextFind(ow));
        m_outputWindows.insert(rc, ow);
        m_tabWidget->addTab(ow, rc->runConfiguration()->name());
    }
}

void OutputPane::appendOutput(RunControl *rc, const QString &out)
{
    OutputWindow *ow = m_outputWindows.value(rc);
    ow->appendOutput(out);
}

void OutputPane::appendOutputInline(RunControl *rc, const QString &out)
{
    OutputWindow *ow = m_outputWindows.value(rc);
    ow->appendOutputInline(out);
}

void OutputPane::showTabFor(RunControl *rc)
{
    OutputWindow *ow = m_outputWindows.value(rc);
    m_tabWidget->setCurrentWidget(ow);
}

void OutputPane::insertLine()
{
    OutputWindow *currentWindow = qobject_cast<OutputWindow *>(m_tabWidget->currentWidget());
    if (currentWindow)
        currentWindow->clear();
}

void OutputPane::reRunRunControl()
{
    RunControl *rc = runControlForTab(m_tabWidget->currentIndex());
    if (rc->runConfiguration()->project() != 0)
        rc->start();
}

void OutputPane::stopRunControl()
{
    RunControl *rc = runControlForTab(m_tabWidget->currentIndex());
    rc->stop();
}

void OutputPane::closeTab(int index)
{
    OutputWindow *ow = static_cast<OutputWindow *>(m_tabWidget->widget(index));
    RunControl *rc = m_outputWindows.key(ow);

    if (rc->isRunning()) {
        QString msg = tr("The application is still running. Close it first.");
        QMessageBox::critical(0, tr("Unable to close"), msg);
        return;
    }

    m_tabWidget->removeTab(index);
    delete ow;
    delete rc;
}

void OutputPane::projectRemoved()
{
    tabChanged(m_tabWidget->currentIndex());
}

void OutputPane::tabChanged(int i)
{
    if (i == -1) {
        m_stopAction->setEnabled(false);
        m_reRunButton->setEnabled(false);
    } else {
        RunControl *rc = runControlForTab(i);
        m_stopAction->setEnabled(rc->isRunning());
        m_reRunButton->setEnabled(!rc->isRunning() && rc->runConfiguration()->project());
    }
}

void OutputPane::runControlStarted()
{
    RunControl *rc = runControlForTab(m_tabWidget->currentIndex());
    if (rc == qobject_cast<RunControl *>(sender())) {
        m_reRunButton->setEnabled(false);
        m_stopAction->setEnabled(true);
    }
}

void OutputPane::runControlFinished()
{
    RunControl *rc = runControlForTab(m_tabWidget->currentIndex());
    if (rc == qobject_cast<RunControl *>(sender())) {
        m_reRunButton->setEnabled(rc->runConfiguration()->project());
        m_stopAction->setEnabled(false);
    }
}

RunControl* OutputPane::runControlForTab(int index) const
{
    return m_outputWindows.key(qobject_cast<OutputWindow *>(m_tabWidget->widget(index)));
}

bool OutputPane::canNext()
{
    return false;
}

bool OutputPane::canPrevious()
{
    return false;
}

void OutputPane::goToNext()
{

}

void OutputPane::goToPrev()
{

}

bool OutputPane::canNavigate()
{
    return false;
}

/*******************/

OutputWindow::OutputWindow(QWidget *parent)
    : QPlainTextEdit(parent)
{
    m_enforceNewline = false;
    m_scrollToBottom = false;

    setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
    //setCenterOnScroll(false);
    setWindowTitle(tr("Application Output Window"));
    setWindowIcon(QIcon(":/qt4projectmanager/images/window.png"));
    setFrameShape(QFrame::NoFrame);

    static uint usedIds = 0;
    Core::ICore *core = Core::ICore::instance();
    QList<int> context;
    context << core->uniqueIDManager()->uniqueIdentifier(QString(Constants::C_APP_OUTPUT) + QString().setNum(usedIds++));
    m_outputWindowContext = new Core::BaseContext(this, context);
    core->addContextObject(m_outputWindowContext);

    QAction *undoAction = new QAction(this);
    QAction *redoAction = new QAction(this);
    QAction *cutAction = new QAction(this);
    QAction *copyAction = new QAction(this);
    QAction *pasteAction = new QAction(this);
    QAction *selectAllAction = new QAction(this);

    core->actionManager()->registerAction(undoAction, Core::Constants::UNDO, context);
    core->actionManager()->registerAction(redoAction, Core::Constants::REDO, context);
    core->actionManager()->registerAction(cutAction, Core::Constants::CUT, context);
    core->actionManager()->registerAction(copyAction, Core::Constants::COPY, context);
    core->actionManager()->registerAction(pasteAction, Core::Constants::PASTE, context);
    core->actionManager()->registerAction(selectAllAction, Core::Constants::SELECTALL, context);

    connect(undoAction, SIGNAL(triggered()), this, SLOT(undo()));
    connect(redoAction, SIGNAL(triggered()), this, SLOT(redo()));
    connect(cutAction, SIGNAL(triggered()), this, SLOT(cut()));
    connect(copyAction, SIGNAL(triggered()), this, SLOT(copy()));
    connect(pasteAction, SIGNAL(triggered()), this, SLOT(paste()));
    connect(selectAllAction, SIGNAL(triggered()), this, SLOT(selectAll()));

    connect(this, SIGNAL(undoAvailable(bool)), undoAction, SLOT(setEnabled(bool)));
    connect(this, SIGNAL(redoAvailable(bool)), redoAction, SLOT(setEnabled(bool)));
    connect(this, SIGNAL(copyAvailable(bool)), cutAction, SLOT(setEnabled(bool)));  // OutputWindow never read-only
    connect(this, SIGNAL(copyAvailable(bool)), copyAction, SLOT(setEnabled(bool)));

    undoAction->setEnabled(false);
    redoAction->setEnabled(false);
    cutAction->setEnabled(false);
    copyAction->setEnabled(false);
}

OutputWindow::~OutputWindow()
{
    Core::ICore::instance()->removeContextObject(m_outputWindowContext);
    delete m_outputWindowContext;
}

void OutputWindow::showEvent(QShowEvent *e)
{
    QPlainTextEdit::showEvent(e);
    if (m_scrollToBottom) {
        verticalScrollBar()->setValue(verticalScrollBar()->maximum());
    }
    m_scrollToBottom = false;
}

void OutputWindow::appendOutput(const QString &out)
{
    m_scrollToBottom = true;
    QString s = out;
    m_enforceNewline = true; // make appendOutputInline put in a newline next time
    if (s.endsWith(QLatin1Char('\n'))) {
        s.chop(1);
    }
    setMaximumBlockCount(MaxBlockCount);
    appendPlainText(out);
    enableUndoRedo();
}

void OutputWindow::appendOutputInline(const QString &out)
{
    m_scrollToBottom = true;
    setMaximumBlockCount(MaxBlockCount);

    int newline = -1;
    bool enforceNewline = m_enforceNewline;
    m_enforceNewline = false;

    if (!enforceNewline) {
        newline = out.indexOf(QLatin1Char('\n'));
        moveCursor(QTextCursor::End);
        bool atBottom = (blockBoundingRect(document()->lastBlock()).bottom() + contentOffset().y()
                         <= viewport()->rect().bottom());
        insertPlainText(newline < 0 ? out : out.left(newline)); // doesn't enforce new paragraph like appendPlainText
        if (atBottom)
            verticalScrollBar()->setValue(verticalScrollBar()->maximum());
    }

    QString s = out.mid(newline+1);
    if (s.isEmpty()) {
        m_enforceNewline = true;
    } else {
        if (s.endsWith(QLatin1Char('\n'))) {
            m_enforceNewline = true;
            s.chop(1);
        }
        appendPlainText(s);
    }

    enableUndoRedo();
}

void OutputWindow::insertLine()
{
    m_scrollToBottom = true;
    setMaximumBlockCount(MaxBlockCount);
    appendPlainText(QString());
    enableUndoRedo();
}

void OutputWindow::enableUndoRedo()
{
    setMaximumBlockCount(0);
    setUndoRedoEnabled(true);
}
<|endoftext|>


<commit_before>/*
 * ArcScripts for ArcEmu MMORPG Server
 * Copyright (C) 2009 ArcEmu Team <http://www.arcemu.org/>
 * Copyright (C) 2008-2009 Sun++ Team <http://www.sunscripting.com/>
 * Copyright (C) 2008 WEmu Team
 * Copyright (C) 2005-2008 Ascent Team
 * Copyright (C) 2007-2008 Moon++ Team
 *
 * This program 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
 * any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
/**********************
Edits by : FenixGman
**********************/
#include "Setup.h"

class Lunaclaw : public CreatureAIScript
{
	public:
		ADD_CREATURE_FACTORY_FUNCTION(Lunaclaw);

		Lunaclaw(Creature* pCreature) : CreatureAIScript(pCreature) {}

		void OnDied(Unit* mKiller)
		{
			if(!mKiller->IsPlayer())
				return;

			Player* plr = TO_PLAYER(mKiller);

			sEAS.SpawnCreature(plr, 12144, _unit->GetPositionX(), _unit->GetPositionY(), _unit->GetPositionZ(), 0, 1 * 60 * 1000);
		}
};

// Lunaclaw ghost gossip
#define GOSSIP_GHOST_MOONKIN    "You have fought well, spirit. I ask you to grand me the strenght of your body and the strenght of your heart."

class SCRIPT_DECL MoonkinGhost_Gossip : public GossipScript
{
	public:
		void GossipHello(Object* pObject, Player* plr)
		{
			GossipMenu* Menu;
			objmgr.CreateGossipMenuForPlayer(&Menu, pObject->GetGUID(), 4714, plr);

			if(plr->GetQuestLogForEntry(6002) != NULL)
			{
				Menu->AddItem(0, GOSSIP_GHOST_MOONKIN, 1);  //Horde
			}
			else if(plr->GetQuestLogForEntry(6001) != NULL)
			{
				Menu->AddItem(0, GOSSIP_GHOST_MOONKIN, 2);  //Ally
			}

			Menu->SendTo(plr);
		}

		void GossipSelectOption(Object* pObject, Player* plr, uint32 Id, uint32 IntId, const char* Code)
		{
			if(!pObject->IsCreature())
				return;
			Creature* pCreature = TO_CREATURE(pObject);

			GossipMenu* Menu;
			switch(IntId)
			{
				case 0: // Return to start
					GossipHello(pCreature, plr);
					break;

				case 1: //Horde
					{
						objmgr.CreateGossipMenuForPlayer(&Menu, pObject->GetGUID(), 4715, plr);
						Menu->SendTo(plr);

						QuestLogEntry* qle = plr->GetQuestLogForEntry(6002);
						if(qle == NULL)
							return;

						if(qle->GetMobCount(0) != 0)
							return;

						qle->SetMobCount(0, 1);
						qle->SendUpdateAddKill(0);
						qle->UpdatePlayerFields();

						pCreature->Emote(EMOTE_ONESHOT_WAVE);
						pCreature->Despawn(240000, 0);
					}
					break;

				case 2: //Ally
					{
						objmgr.CreateGossipMenuForPlayer(&Menu, pObject->GetGUID(), 4715, plr);
						Menu->SendTo(plr);

						QuestLogEntry* qle = plr->GetQuestLogForEntry(6001);
						if(qle == NULL)
							return;

						if(qle->GetMobCount(0) != 0)
							return;

						qle->SetMobCount(0, 1);
						qle->SendUpdateAddKill(0);
						qle->UpdatePlayerFields();

						pCreature->Emote(EMOTE_ONESHOT_WAVE);
						pCreature->Despawn(240000, 0);
					}
					break;

			}
		}
};

// bear ghost gossip
#define GOSSIP_GHOST_BEAR_A    "What do you represent, spirit?"
#define GOSSIP_GHOST_BEAR_B    "I seek to understand the importance of strength of the body."
#define GOSSIP_GHOST_BEAR_C    "I seek to understand the importance of strength of the heart."
#define GOSSIP_GHOST_BEAR_D    "I have heard your words, Great Bear Spirit, and I understand. I now seek your blessings to fully learn the way of the Claw."

class SCRIPT_DECL BearGhost_Gossip : public GossipScript
{
	public:
		void GossipHello(Object* pObject, Player* plr)
		{
			GossipMenu* Menu;
			objmgr.CreateGossipMenuForPlayer(&Menu, pObject->GetGUID(), 4719, plr);

			if(plr->GetQuestLogForEntry(5930) != NULL) // horde
			{
				Menu->AddItem(0, GOSSIP_GHOST_BEAR_A, 1);
			}
			else if(plr->GetQuestLogForEntry(5929) != NULL) // ally
			{
				Menu->AddItem(0, GOSSIP_GHOST_BEAR_A, 5);
			}

			Menu->SendTo(plr);
		}

		void GossipSelectOption(Object* pObject, Player* plr, uint32 Id, uint32 IntId, const char* Code)
		{
			Creature*  pCreature = (pObject->IsCreature()) ? (TO_CREATURE(pObject)) : NULL;
			if(!pObject->IsCreature())
				return;

			GossipMenu* Menu;
			switch(IntId)
			{
				case 0: // Return to start
					GossipHello(pCreature, plr);
					break;
				case 1:
					{
						objmgr.CreateGossipMenuForPlayer(&Menu, pObject->GetGUID(), 4721, plr);
						Menu->AddItem(0, GOSSIP_GHOST_BEAR_B, 2);
						Menu->SendTo(plr);
						break;
					}
				case 2:
					{
						objmgr.CreateGossipMenuForPlayer(&Menu, pObject->GetGUID(), 4733, plr);
						Menu->AddItem(0, GOSSIP_GHOST_BEAR_C, 3);
						Menu->SendTo(plr);
						break;
					}
				case 3:
					{
						objmgr.CreateGossipMenuForPlayer(&Menu, pObject->GetGUID(), 4734, plr);
						Menu->AddItem(0, GOSSIP_GHOST_BEAR_D, 4);
						Menu->SendTo(plr);
						break;
					}
				case 4:
					{
						objmgr.CreateGossipMenuForPlayer(&Menu, pObject->GetGUID(), 4735, plr);

						QuestLogEntry* qle = plr->GetQuestLogForEntry(5930);
						if(qle == NULL)
							return;

						if(qle->GetMobCount(0) != 0)
							return;

						qle->SetMobCount(0, 1);
						qle->SendUpdateAddKill(0);
						qle->UpdatePlayerFields();

						Menu->SendTo(plr);
						break;
					}
				case 5:
					{
						objmgr.CreateGossipMenuForPlayer(&Menu, pObject->GetGUID(), 4721, plr);
						Menu->AddItem(0, GOSSIP_GHOST_BEAR_B, 6);
						Menu->SendTo(plr);
						break;
					}
				case 6:
					{
						objmgr.CreateGossipMenuForPlayer(&Menu, pObject->GetGUID(), 4733, plr);
						Menu->AddItem(0, GOSSIP_GHOST_BEAR_C, 7);
						Menu->SendTo(plr);
						break;
					}
				case 7:
					{
						objmgr.CreateGossipMenuForPlayer(&Menu, pObject->GetGUID(), 4734, plr);
						Menu->AddItem(0, GOSSIP_GHOST_BEAR_D, 8);
						Menu->SendTo(plr);
						break;
					}
				case 8:
					{
						objmgr.CreateGossipMenuForPlayer(&Menu, pObject->GetGUID(), 4735, plr);

						QuestLogEntry* qle = plr->GetQuestLogForEntry(5929);
						if(qle == NULL)
							return;

						if(qle->GetMobCount(0) != 0)
							return;

						qle->SetMobCount(0, 1);
						qle->SendUpdateAddKill(0);
						qle->UpdatePlayerFields();

						Menu->SendTo(plr);
						break;
					}
			}
		}
};

class MoongladeQuest : public QuestScript
{
	public:
		void OnQuestStart(Player* mTarget, QuestLogEntry* qLogEntry)
		{
			if(!mTarget->HasSpell(19027))
				mTarget->CastSpell(mTarget, dbcSpell.LookupEntry(19027), true);
		}
};

void SetupDruid(ScriptMgr* mgr)
{

	GossipScript* MoonkinGhostGossip = new MoonkinGhost_Gossip;
	GossipScript* BearGhostGossip = new BearGhost_Gossip;
	QuestScript* Moonglade = new MoongladeQuest();
	mgr->register_quest_script(5921, Moonglade);
	mgr->register_quest_script(5922, Moonglade);
	mgr->register_creature_script(12138, &Lunaclaw::Create);

	//Register gossip scripts
	mgr->register_gossip_script(12144, MoonkinGhostGossip); // Ghost of Lunaclaw
	mgr->register_gossip_script(11956, BearGhostGossip); // Great Bear Spirit

}

<commit_msg>Fix DruidQuest "Great Bear Spirit" for Horde and Alliance<commit_after>/*
 * ArcScripts for ArcEmu MMORPG Server
 * Copyright (C) 2009 ArcEmu Team <http://www.arcemu.org/>
 * Copyright (C) 2008-2009 Sun++ Team <http://www.sunscripting.com/>
 * Copyright (C) 2008 WEmu Team
 * Copyright (C) 2005-2008 Ascent Team
 * Copyright (C) 2007-2008 Moon++ Team
 *
 * This program 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
 * any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
/**********************
Edits by : FenixGman
**********************/
#include "Setup.h"

class Lunaclaw : public CreatureAIScript
{
	public:
		ADD_CREATURE_FACTORY_FUNCTION(Lunaclaw);

		Lunaclaw(Creature* pCreature) : CreatureAIScript(pCreature) {}

		void OnDied(Unit* mKiller)
		{
			if(!mKiller->IsPlayer())
				return;

			Player* plr = TO_PLAYER(mKiller);

			sEAS.SpawnCreature(plr, 12144, _unit->GetPositionX(), _unit->GetPositionY(), _unit->GetPositionZ(), 0, 1 * 60 * 1000);
		}
};

// Lunaclaw ghost gossip
#define GOSSIP_GHOST_MOONKIN    "You have fought well, spirit. I ask you to grand me the strenght of your body and the strenght of your heart."

class SCRIPT_DECL MoonkinGhost_Gossip : public GossipScript
{
	public:
		void GossipHello(Object* pObject, Player* plr)
		{
			GossipMenu* Menu;
			objmgr.CreateGossipMenuForPlayer(&Menu, pObject->GetGUID(), 4714, plr);

			if(plr->GetQuestLogForEntry(6002) != NULL)
			{
				Menu->AddItem(0, GOSSIP_GHOST_MOONKIN, 1);  //Horde
			}
			else if(plr->GetQuestLogForEntry(6001) != NULL)
			{
				Menu->AddItem(0, GOSSIP_GHOST_MOONKIN, 2);  //Ally
			}

			Menu->SendTo(plr);
		}

		void GossipSelectOption(Object* pObject, Player* plr, uint32 Id, uint32 IntId, const char* Code)
		{
			if(!pObject->IsCreature())
				return;
			Creature* pCreature = TO_CREATURE(pObject);

			GossipMenu* Menu;
			switch(IntId)
			{
				case 0: // Return to start
					GossipHello(pCreature, plr);
					break;

				case 1: //Horde
					{
						objmgr.CreateGossipMenuForPlayer(&Menu, pObject->GetGUID(), 4715, plr);
						Menu->SendTo(plr);

						QuestLogEntry* qle = plr->GetQuestLogForEntry(6002);
						if(qle == NULL)
							return;

						if(qle->GetMobCount(0) != 0)
							return;

						qle->SetMobCount(0, 1);
						qle->SendUpdateAddKill(0);
						qle->UpdatePlayerFields();

						pCreature->Emote(EMOTE_ONESHOT_WAVE);
						pCreature->Despawn(240000, 0);
					}
					break;

				case 2: //Ally
					{
						objmgr.CreateGossipMenuForPlayer(&Menu, pObject->GetGUID(), 4715, plr);
						Menu->SendTo(plr);

						QuestLogEntry* qle = plr->GetQuestLogForEntry(6001);
						if(qle == NULL)
							return;

						if(qle->GetMobCount(0) != 0)
							return;

						qle->SetMobCount(0, 1);
						qle->SendUpdateAddKill(0);
						qle->UpdatePlayerFields();

						pCreature->Emote(EMOTE_ONESHOT_WAVE);
						pCreature->Despawn(240000, 0);
					}
					break;

			}
		}
};

// bear ghost gossip
#define GOSSIP_GHOST_BEAR_A    "What do you represent, spirit?"
#define GOSSIP_GHOST_BEAR_B    "I seek to understand the importance of strength of the body."
#define GOSSIP_GHOST_BEAR_C    "I seek to understand the importance of strength of the heart."
#define GOSSIP_GHOST_BEAR_D    "I have heard your words, Great Bear Spirit, and I understand. I now seek your blessings to fully learn the way of the Claw."

class SCRIPT_DECL BearGhost_Gossip : public GossipScript
{
	public:
		void GossipHello(Object* pObject, Player* plr)
		{
			GossipMenu* Menu;
			objmgr.CreateGossipMenuForPlayer(&Menu, pObject->GetGUID(), 4719, plr);

			if(plr->HasQuest(5930)) // horde
			{
				Menu->AddItem(0, GOSSIP_GHOST_BEAR_A, 1);
			}
			else if(plr->HasQuest(5929)) // ally
			{
				Menu->AddItem(0, GOSSIP_GHOST_BEAR_A, 5);
			}

			Menu->SendTo(plr);
		}

		void GossipSelectOption(Object* pObject, Player* plr, uint32 Id, uint32 IntId, const char* Code)
		{
			Creature*  pCreature = (pObject->IsCreature()) ? (TO_CREATURE(pObject)) : NULL;
			if(!pObject->IsCreature())
				return;

			GossipMenu* Menu;
			switch(IntId)
			{
				case 0: // Return to start
					GossipHello(pCreature, plr);
					break;
				case 1:
					{
						objmgr.CreateGossipMenuForPlayer(&Menu, pObject->GetGUID(), 4721, plr);
						Menu->AddItem(0, GOSSIP_GHOST_BEAR_B, 2);
						Menu->SendTo(plr);
						break;
					}
				case 2:
					{
						objmgr.CreateGossipMenuForPlayer(&Menu, pObject->GetGUID(), 4733, plr);
						Menu->AddItem(0, GOSSIP_GHOST_BEAR_C, 3);
						Menu->SendTo(plr);
						break;
					}
				case 3:
					{
						objmgr.CreateGossipMenuForPlayer(&Menu, pObject->GetGUID(), 4734, plr);
						Menu->AddItem(0, GOSSIP_GHOST_BEAR_D, 4);
						Menu->SendTo(plr);
						break;
					}
				case 4:
					{
						objmgr.CreateGossipMenuForPlayer(&Menu, pObject->GetGUID(), 4735, plr);
						Menu->SendTo(plr);

						QuestLogEntry* qle = plr->GetQuestLogForEntry(5930);
						if(qle == NULL)
							return;

						if(qle->CanBeFinished())
							return;

						qle->Complete();
						qle->SendQuestComplete();
						qle->UpdatePlayerFields();
						break;
					}
				case 5:
					{
						objmgr.CreateGossipMenuForPlayer(&Menu, pObject->GetGUID(), 4721, plr);
						Menu->AddItem(0, GOSSIP_GHOST_BEAR_B, 6);
						Menu->SendTo(plr);
						break;
					}
				case 6:
					{
						objmgr.CreateGossipMenuForPlayer(&Menu, pObject->GetGUID(), 4733, plr);
						Menu->AddItem(0, GOSSIP_GHOST_BEAR_C, 7);
						Menu->SendTo(plr);
						break;
					}
				case 7:
					{
						objmgr.CreateGossipMenuForPlayer(&Menu, pObject->GetGUID(), 4734, plr);
						Menu->AddItem(0, GOSSIP_GHOST_BEAR_D, 8);
						Menu->SendTo(plr);
						break;
					}
				case 8:
					{
						objmgr.CreateGossipMenuForPlayer(&Menu, pObject->GetGUID(), 4735, plr);
						Menu->SendTo(plr);

						QuestLogEntry* qle = plr->GetQuestLogForEntry(5929);
						if(qle == NULL)
							return;

						if(qle->CanBeFinished())
							return;

						qle->Complete();
						qle->SendQuestComplete();
						qle->UpdatePlayerFields();
						break;
					}
			}
		}
};

class MoongladeQuest : public QuestScript
{
	public:
		void OnQuestStart(Player* mTarget, QuestLogEntry* qLogEntry)
		{
			if(!mTarget->HasSpell(19027))
				mTarget->CastSpell(mTarget, dbcSpell.LookupEntry(19027), true);
		}
};

void SetupDruid(ScriptMgr* mgr)
{

	GossipScript* MoonkinGhostGossip = new MoonkinGhost_Gossip;
	GossipScript* BearGhostGossip = new BearGhost_Gossip;
	QuestScript* Moonglade = new MoongladeQuest();
	mgr->register_quest_script(5921, Moonglade);
	mgr->register_quest_script(5922, Moonglade);
	mgr->register_creature_script(12138, &Lunaclaw::Create);

	//Register gossip scripts
	mgr->register_gossip_script(12144, MoonkinGhostGossip); // Ghost of Lunaclaw
	mgr->register_gossip_script(11956, BearGhostGossip); // Great Bear Spirit

}

<|endoftext|>


<commit_before>/******************************************************************************
 *                       ____    _    _____                                   *
 *                      / ___|  / \  |  ___|    C++                           *
 *                     | |     / _ \ | |_       Actor                         *
 *                     | |___ / ___ \|  _|      Framework                     *
 *                      \____/_/   \_|_|                                      *
 *                                                                            *
 * Copyright (C) 2011 - 2017                                                  *
 * Dominik Charousset <dominik.charousset (at) haw-hamburg.de>                *
 *                                                                            *
 * Distributed under the terms and conditions of the BSD 3-Clause License or  *
 * (at your option) under the terms and conditions of the Boost Software      *
 * License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE.       *
 *                                                                            *
 * If you did not receive a copy of the license files, see                    *
 * http://opensource.org/licenses/BSD-3-Clause and                            *
 * http://www.boost.org/LICENSE_1_0.txt.                                      *
 ******************************************************************************/

#include "caf/config.hpp"

#define CAF_SUITE atom
#include "caf/test/unit_test.hpp"

#include <string>

#include "caf/all.hpp"

using namespace caf;

namespace {

constexpr auto s_foo = atom("FooBar");

using a_atom = atom_constant<atom("a")>;
using b_atom = atom_constant<atom("b")>;
using c_atom = atom_constant<atom("c")>;
using abc_atom = atom_constant<atom("abc")>;
using def_atom = atom_constant<atom("def")>;
using foo_atom = atom_constant<atom("foo")>;

struct fixture {
  fixture() : system(cfg) {
    // nop
  }

  actor_system_config cfg;
  actor_system system;
};

} // namespace <anonymous>

CAF_TEST_FIXTURE_SCOPE(atom_tests, fixture)

CAF_TEST(basics) {
  // check if there are leading bits that distinguish "zzz" and "000 "
  CAF_CHECK_NOT_EQUAL(atom("zzz"), atom("000 "));
  // check if there are leading bits that distinguish "abc" and " abc"
  CAF_CHECK_NOT_EQUAL(atom("abc"), atom(" abc"));
  // 'illegal' characters are mapped to whitespaces
  CAF_CHECK_EQUAL(atom("   "), atom("@!?"));
  // check to_string impl
  CAF_CHECK_EQUAL(to_string(s_foo), "FooBar");
}

struct send_to_self {
  explicit send_to_self(blocking_actor* self) : self_(self) {
    // nop
  }
  template <class... Ts>
  void operator()(Ts&&... xs) {
    self_->send(self_, std::forward<Ts>(xs)...);
  }
  blocking_actor* self_;
};

CAF_TEST(receive_atoms) {
  scoped_actor self{system};
  send_to_self f{self.ptr()};
  f(foo_atom::value, static_cast<uint32_t>(42));
  f(abc_atom::value, def_atom::value, "cstring");
  f(1.f);
  f(a_atom::value, b_atom::value, c_atom::value, 23.f);
  bool matched_pattern[3] = {false, false, false};
  int i = 0;
  CAF_MESSAGE("start receive loop");
  for (i = 0; i < 3; ++i) {
    self->receive(
      [&](foo_atom, uint32_t value) {
        matched_pattern[0] = true;
        CAF_CHECK_EQUAL(value, 42u);
      },
      [&](abc_atom, def_atom, const std::string& str) {
        matched_pattern[1] = true;
        CAF_CHECK_EQUAL(str, "cstring");
      },
      [&](a_atom, b_atom, c_atom, float value) {
        matched_pattern[2] = true;
        CAF_CHECK_EQUAL(value, 23.f);
      }
    );
  }
  CAF_CHECK(matched_pattern[0] && matched_pattern[1] && matched_pattern[2]);
  self->receive(
    [](float) {
      // erase float message
    }
  );
  atom_value x = atom("abc");
  atom_value y = abc_atom::value;
  CAF_CHECK_EQUAL(x, y);
  auto msg = make_message(atom("abc"));
  self->send(self, msg);
  self->receive(
    [](abc_atom) {
      CAF_MESSAGE("received 'abc'");
    }
  );
}

using testee = typed_actor<replies_to<abc_atom>::with<int>>;

testee::behavior_type testee_impl(testee::pointer self) {
  return {
    [=](abc_atom) {
      self->quit();
      return 42;
    }
  };
}

CAF_TEST(request_atom_constants) {
  scoped_actor self{system};
  auto tst = system.spawn(testee_impl);
  self->request(tst, infinite, abc_atom::value).receive(
    [](int i) {
      CAF_CHECK_EQUAL(i, 42);
    },
    [&](error& err) {
      CAF_FAIL("err: " << system.render(err));
    }
  );
}

CAF_TEST_FIXTURE_SCOPE_END()
<commit_msg>Add unit test for atom_from_string<commit_after>/******************************************************************************
 *                       ____    _    _____                                   *
 *                      / ___|  / \  |  ___|    C++                           *
 *                     | |     / _ \ | |_       Actor                         *
 *                     | |___ / ___ \|  _|      Framework                     *
 *                      \____/_/   \_|_|                                      *
 *                                                                            *
 * Copyright (C) 2011 - 2017                                                  *
 * Dominik Charousset <dominik.charousset (at) haw-hamburg.de>                *
 *                                                                            *
 * Distributed under the terms and conditions of the BSD 3-Clause License or  *
 * (at your option) under the terms and conditions of the Boost Software      *
 * License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE.       *
 *                                                                            *
 * If you did not receive a copy of the license files, see                    *
 * http://opensource.org/licenses/BSD-3-Clause and                            *
 * http://www.boost.org/LICENSE_1_0.txt.                                      *
 ******************************************************************************/

#include "caf/config.hpp"

#define CAF_SUITE atom
#include "caf/test/unit_test.hpp"

#include <string>

#include "caf/all.hpp"

using namespace caf;

namespace {

constexpr auto s_foo = atom("FooBar");

using a_atom = atom_constant<atom("a")>;
using b_atom = atom_constant<atom("b")>;
using c_atom = atom_constant<atom("c")>;
using abc_atom = atom_constant<atom("abc")>;
using def_atom = atom_constant<atom("def")>;
using foo_atom = atom_constant<atom("foo")>;

struct fixture {
  fixture() : system(cfg) {
    // nop
  }

  actor_system_config cfg;
  actor_system system;
};

} // namespace <anonymous>

CAF_TEST_FIXTURE_SCOPE(atom_tests, fixture)

CAF_TEST(basics) {
  // check if there are leading bits that distinguish "zzz" and "000 "
  CAF_CHECK_NOT_EQUAL(atom("zzz"), atom("000 "));
  // check if there are leading bits that distinguish "abc" and " abc"
  CAF_CHECK_NOT_EQUAL(atom("abc"), atom(" abc"));
  // 'illegal' characters are mapped to whitespaces
  CAF_CHECK_EQUAL(atom("   "), atom("@!?"));
  // check to_string impl
  CAF_CHECK_EQUAL(to_string(s_foo), "FooBar");
}

struct send_to_self {
  explicit send_to_self(blocking_actor* self) : self_(self) {
    // nop
  }
  template <class... Ts>
  void operator()(Ts&&... xs) {
    self_->send(self_, std::forward<Ts>(xs)...);
  }
  blocking_actor* self_;
};

CAF_TEST(receive_atoms) {
  scoped_actor self{system};
  send_to_self f{self.ptr()};
  f(foo_atom::value, static_cast<uint32_t>(42));
  f(abc_atom::value, def_atom::value, "cstring");
  f(1.f);
  f(a_atom::value, b_atom::value, c_atom::value, 23.f);
  bool matched_pattern[3] = {false, false, false};
  int i = 0;
  CAF_MESSAGE("start receive loop");
  for (i = 0; i < 3; ++i) {
    self->receive(
      [&](foo_atom, uint32_t value) {
        matched_pattern[0] = true;
        CAF_CHECK_EQUAL(value, 42u);
      },
      [&](abc_atom, def_atom, const std::string& str) {
        matched_pattern[1] = true;
        CAF_CHECK_EQUAL(str, "cstring");
      },
      [&](a_atom, b_atom, c_atom, float value) {
        matched_pattern[2] = true;
        CAF_CHECK_EQUAL(value, 23.f);
      }
    );
  }
  CAF_CHECK(matched_pattern[0] && matched_pattern[1] && matched_pattern[2]);
  self->receive(
    [](float) {
      // erase float message
    }
  );
  atom_value x = atom("abc");
  atom_value y = abc_atom::value;
  CAF_CHECK_EQUAL(x, y);
  auto msg = make_message(atom("abc"));
  self->send(self, msg);
  self->receive(
    [](abc_atom) {
      CAF_MESSAGE("received 'abc'");
    }
  );
}

using testee = typed_actor<replies_to<abc_atom>::with<int>>;

testee::behavior_type testee_impl(testee::pointer self) {
  return {
    [=](abc_atom) {
      self->quit();
      return 42;
    }
  };
}

CAF_TEST(request_atom_constants) {
  scoped_actor self{system};
  auto tst = system.spawn(testee_impl);
  self->request(tst, infinite, abc_atom::value).receive(
    [](int i) {
      CAF_CHECK_EQUAL(i, 42);
    },
    [&](error& err) {
      CAF_FAIL("err: " << system.render(err));
    }
  );
}

CAF_TEST(runtime_conversion) {
  CAF_CHECK_EQUAL(atom("foo"), atom_from_string("foo"));
  CAF_CHECK_EQUAL(atom(""), atom_from_string("tooManyCharacters"));
}

CAF_TEST_FIXTURE_SCOPE_END()
<|endoftext|>


<commit_before>#pragma once
#ifndef AMFVECTOR_HPP
#define AMFVECTOR_HPP

#include <vector>

#include "deserializationcontext.hpp"
#include "deserializer.hpp"

#include "types/amfitem.hpp"
#include "types/amfinteger.hpp"
#include "types/amfstring.hpp"

#include "utils/amfitemptr.hpp"

namespace amf {

template<typename T>
struct VectorProperties;

template<>
struct VectorProperties<int> {
	static const u8 marker = AMF_VECTOR_INT;
	static const unsigned int size = 4;
	typedef void type;
};

template<>
struct VectorProperties<unsigned int> {
	static const u8 marker = AMF_VECTOR_UINT;
	static const unsigned int size = 4;
	typedef void type;
};

template<>
struct VectorProperties<double> {
	static const u8 marker = AMF_VECTOR_DOUBLE;
	static const unsigned int size = 8;
	typedef void type;
};

template<typename T, class Enable = void>
class AmfVector;

template<typename T>
class AmfVector<T, typename VectorProperties<T>::type> : public AmfItem {
public:
	AmfVector() : values({}), fixed(false) { };
	AmfVector(std::vector<T> vector, bool fixed = false) :
		values(vector), fixed(fixed) { };

	bool operator==(const AmfItem& other) const {
		const AmfVector<T>* p = dynamic_cast<const AmfVector<T>*>(&other);
		return p != nullptr && fixed == p->fixed && values == p->values;
	}

	void push_back(T item) {
		values.push_back(item);
	}

	T& at(int index) {
		return values.at(index);
	}

	std::vector<u8> serialize() const {
		// U29V value
		std::vector<u8> buf = AmfInteger::asLength(values.size(),
			VectorProperties<T>::marker);

		// fixed-vector marker
		buf.push_back(fixed ? 0x01 : 0x00);

		for (const T& it : values) {
			// values are encoded in network byte order
			// ints are encoded as U32, not U29
			T netvalue = hton(it);
			const u8* bytes = reinterpret_cast<const u8*>(&netvalue);
			buf.insert(buf.end(), bytes, bytes + VectorProperties<T>::size);
		}

		return buf;
	}

	static AmfVector<T> deserialize(v8::const_iterator& it, v8::const_iterator end, DeserializationContext& ctx) {
		if (it == end || *it++ != VectorProperties<T>::marker)
			throw std::invalid_argument("AmfVector: Invalid type marker");

		int type = AmfInteger::deserializeValue(it, end);
		if ((type & 0x01) == 0)
			return ctx.getObject<AmfVector<T>>(type >> 1);

		unsigned int stride = VectorProperties<T>::size;
		size_t count = type >> 1;

		if (it == end)
			throw std::out_of_range("Not enough bytes for AmfVector");

		bool fixed = (*it++ == 0x01);

		if (end - it < count * stride)
			throw std::out_of_range("Not enough bytes for AmfVector");

		std::vector<T> values(count);
		for (size_t i = 0; i < count; ++i) {
			// Convert value bytes to requested type.
			T val;
			std::copy(it, it + stride, reinterpret_cast<u8 *>(&val));
			it += stride;

			// Values are stored in network order.
			values[i] = ntoh(val);
		}

		AmfVector<T> ret(values, fixed);
		ctx.addObject<AmfVector<T>>(ret);

		return ret;
	}

	std::vector<T> values;
	bool fixed;
};

template<>
class AmfVector<AmfItem> : public AmfItem {
public:
	AmfVector(std::string type, bool fixed = false) : type(type), fixed(fixed) { }

	bool operator==(const AmfItem& other) const {
		const AmfVector<AmfItem>* p = dynamic_cast<const AmfVector<AmfItem>*>(&other);
		return p != nullptr && fixed == p->fixed && type == p->type && values == p->values;
	}

	std::vector<u8> serialize() const {
		// U29V value, encoding the length
		std::vector<u8> buf = AmfInteger::asLength(values.size(), AMF_VECTOR_OBJECT);

		// fixed-vector marker
		buf.push_back(fixed ? 0x01 : 0x00);

		// object type name
		std::vector<u8> typeName = AmfString(type).serialize();
		// skip the AMF_STRING marker
		buf.insert(buf.end(), typeName.begin() + 1, typeName.end());

		for (const auto& it : values) {
			auto s = it->serialize();
			buf.insert(buf.end(), s.begin(), s.end());
		}

		return buf;
	}

	static AmfVector<AmfItem> deserialize(v8::const_iterator& it, v8::const_iterator end, DeserializationContext& ctx) {
		if (it == end || *it++ != AMF_VECTOR_OBJECT)
			throw std::invalid_argument("AmfVector<Object>: Invalid type marker");

		int type = AmfInteger::deserializeValue(it, end);
		if ((type & 0x01) == 0)
			return ctx.getObject<AmfVector<AmfItem>>(type >> 1);

		if (it == end)
			throw std::out_of_range("Not enough bytes for AmfVector");
		bool fixed = (*it++ == 0x01);

		std::string name = AmfString::deserializeValue(it, end, ctx);
		int count = type >> 1;

		AmfVector<AmfItem> ret(name, fixed);
		size_t contextIndex = ctx.addObject<AmfVector<AmfItem>>(ret);

		ret.values.reserve(count);
		for (int i = 0; i < count; ++i) {
			ret.values.push_back(Deserializer::deserialize(it, end, ctx));
		}

		ctx.setObject<AmfVector<AmfItem>>(contextIndex, ret);
		return ret;
	}

	template<typename V, typename std::enable_if<std::is_base_of<AmfItem, V>::value, int>::type = 0>
	AmfVector<V> as() {
		AmfVector<V> ret({}, type, fixed);
		ret.values = values;
		return ret;
	}

	std::vector<AmfItemPtr> values;
	std::string type;
	bool fixed;
};

template<typename T>
class AmfVector<T, typename std::enable_if<
	std::is_base_of<AmfItem, T>::value>::type> : public AmfVector<AmfItem> {
public:
	AmfVector(std::vector<T> vector, std::string type, bool fixed = false) :
		AmfVector<AmfItem>(type, fixed) {
		for (const auto& it : vector)
			push_back(it);
	};

	bool operator==(const AmfItem& other) const {
		const AmfVector<T>* p = dynamic_cast<const AmfVector<T>*>(&other);
		return p != nullptr && fixed == p->fixed && type == p->type && values == p->values;
	}

	void push_back(const T& item) {
		values.emplace_back(new T(item));
	}

	T& at(int index) {
		return values.at(index).template as<T>();
	}

	static AmfVector<T> deserialize(v8::const_iterator& it, v8::const_iterator end, DeserializationContext& ctx) {
		return AmfVector<AmfItem>::deserialize(it, end, ctx).as<T>();
	}
};

} // namespace amf

#endif
<commit_msg>Make unsigned comparison signed.<commit_after>#pragma once
#ifndef AMFVECTOR_HPP
#define AMFVECTOR_HPP

#include <vector>

#include "deserializationcontext.hpp"
#include "deserializer.hpp"

#include "types/amfitem.hpp"
#include "types/amfinteger.hpp"
#include "types/amfstring.hpp"

#include "utils/amfitemptr.hpp"

namespace amf {

template<typename T>
struct VectorProperties;

template<>
struct VectorProperties<int> {
	static const u8 marker = AMF_VECTOR_INT;
	static const unsigned int size = 4;
	typedef void type;
};

template<>
struct VectorProperties<unsigned int> {
	static const u8 marker = AMF_VECTOR_UINT;
	static const unsigned int size = 4;
	typedef void type;
};

template<>
struct VectorProperties<double> {
	static const u8 marker = AMF_VECTOR_DOUBLE;
	static const unsigned int size = 8;
	typedef void type;
};

template<typename T, class Enable = void>
class AmfVector;

template<typename T>
class AmfVector<T, typename VectorProperties<T>::type> : public AmfItem {
public:
	AmfVector() : values({}), fixed(false) { };
	AmfVector(std::vector<T> vector, bool fixed = false) :
		values(vector), fixed(fixed) { };

	bool operator==(const AmfItem& other) const {
		const AmfVector<T>* p = dynamic_cast<const AmfVector<T>*>(&other);
		return p != nullptr && fixed == p->fixed && values == p->values;
	}

	void push_back(T item) {
		values.push_back(item);
	}

	T& at(int index) {
		return values.at(index);
	}

	std::vector<u8> serialize() const {
		// U29V value
		std::vector<u8> buf = AmfInteger::asLength(values.size(),
			VectorProperties<T>::marker);

		// fixed-vector marker
		buf.push_back(fixed ? 0x01 : 0x00);

		for (const T& it : values) {
			// values are encoded in network byte order
			// ints are encoded as U32, not U29
			T netvalue = hton(it);
			const u8* bytes = reinterpret_cast<const u8*>(&netvalue);
			buf.insert(buf.end(), bytes, bytes + VectorProperties<T>::size);
		}

		return buf;
	}

	static AmfVector<T> deserialize(v8::const_iterator& it, v8::const_iterator end, DeserializationContext& ctx) {
		if (it == end || *it++ != VectorProperties<T>::marker)
			throw std::invalid_argument("AmfVector: Invalid type marker");

		int type = AmfInteger::deserializeValue(it, end);
		if ((type & 0x01) == 0)
			return ctx.getObject<AmfVector<T>>(type >> 1);

		unsigned int stride = VectorProperties<T>::size;
		size_t count = type >> 1;

		if (it == end)
			throw std::out_of_range("Not enough bytes for AmfVector");

		bool fixed = (*it++ == 0x01);

		if (end - it < static_cast<std::ptrdiff_t>(count * stride))
			throw std::out_of_range("Not enough bytes for AmfVector");

		std::vector<T> values(count);
		for (size_t i = 0; i < count; ++i) {
			// Convert value bytes to requested type.
			T val;
			std::copy(it, it + stride, reinterpret_cast<u8 *>(&val));
			it += stride;

			// Values are stored in network order.
			values[i] = ntoh(val);
		}

		AmfVector<T> ret(values, fixed);
		ctx.addObject<AmfVector<T>>(ret);

		return ret;
	}

	std::vector<T> values;
	bool fixed;
};

template<>
class AmfVector<AmfItem> : public AmfItem {
public:
	AmfVector(std::string type, bool fixed = false) : type(type), fixed(fixed) { }

	bool operator==(const AmfItem& other) const {
		const AmfVector<AmfItem>* p = dynamic_cast<const AmfVector<AmfItem>*>(&other);
		return p != nullptr && fixed == p->fixed && type == p->type && values == p->values;
	}

	std::vector<u8> serialize() const {
		// U29V value, encoding the length
		std::vector<u8> buf = AmfInteger::asLength(values.size(), AMF_VECTOR_OBJECT);

		// fixed-vector marker
		buf.push_back(fixed ? 0x01 : 0x00);

		// object type name
		std::vector<u8> typeName = AmfString(type).serialize();
		// skip the AMF_STRING marker
		buf.insert(buf.end(), typeName.begin() + 1, typeName.end());

		for (const auto& it : values) {
			auto s = it->serialize();
			buf.insert(buf.end(), s.begin(), s.end());
		}

		return buf;
	}

	static AmfVector<AmfItem> deserialize(v8::const_iterator& it, v8::const_iterator end, DeserializationContext& ctx) {
		if (it == end || *it++ != AMF_VECTOR_OBJECT)
			throw std::invalid_argument("AmfVector<Object>: Invalid type marker");

		int type = AmfInteger::deserializeValue(it, end);
		if ((type & 0x01) == 0)
			return ctx.getObject<AmfVector<AmfItem>>(type >> 1);

		if (it == end)
			throw std::out_of_range("Not enough bytes for AmfVector");
		bool fixed = (*it++ == 0x01);

		std::string name = AmfString::deserializeValue(it, end, ctx);
		int count = type >> 1;

		AmfVector<AmfItem> ret(name, fixed);
		size_t contextIndex = ctx.addObject<AmfVector<AmfItem>>(ret);

		ret.values.reserve(count);
		for (int i = 0; i < count; ++i) {
			ret.values.push_back(Deserializer::deserialize(it, end, ctx));
		}

		ctx.setObject<AmfVector<AmfItem>>(contextIndex, ret);
		return ret;
	}

	template<typename V, typename std::enable_if<std::is_base_of<AmfItem, V>::value, int>::type = 0>
	AmfVector<V> as() {
		AmfVector<V> ret({}, type, fixed);
		ret.values = values;
		return ret;
	}

	std::vector<AmfItemPtr> values;
	std::string type;
	bool fixed;
};

template<typename T>
class AmfVector<T, typename std::enable_if<
	std::is_base_of<AmfItem, T>::value>::type> : public AmfVector<AmfItem> {
public:
	AmfVector(std::vector<T> vector, std::string type, bool fixed = false) :
		AmfVector<AmfItem>(type, fixed) {
		for (const auto& it : vector)
			push_back(it);
	};

	bool operator==(const AmfItem& other) const {
		const AmfVector<T>* p = dynamic_cast<const AmfVector<T>*>(&other);
		return p != nullptr && fixed == p->fixed && type == p->type && values == p->values;
	}

	void push_back(const T& item) {
		values.emplace_back(new T(item));
	}

	T& at(int index) {
		return values.at(index).template as<T>();
	}

	static AmfVector<T> deserialize(v8::const_iterator& it, v8::const_iterator end, DeserializationContext& ctx) {
		return AmfVector<AmfItem>::deserialize(it, end, ctx).as<T>();
	}
};

} // namespace amf

#endif
<|endoftext|>