<commit_before>#include "ff_source_syntax.h"

#include <sstream>
#include <stack>

#include "sentence_metadata.h"
#include "array2d.h"
#include "filelib.h"

using namespace std;

// implements the source side syntax features described in Blunsom et al. (EMNLP 2008)
// source trees must be represented in Penn Treebank format, e.g.
//     (S (NP John) (VP (V left)))

// log transform to make long spans cluster together
// but preserve differences
inline int SpanSizeTransform(unsigned span_size) {
  if (!span_size) return 0;
  return static_cast<int>(log(span_size+1) / log(1.39)) - 1;
}

struct SourceSyntaxFeaturesImpl {
  SourceSyntaxFeaturesImpl() {}

  void InitializeGrids(const string& tree, unsigned src_len) {
    assert(tree.size() > 0);
    fids_cat.clear();
    fids_fonly.clear();
    fids_ef.clear();
    src_tree.clear();
    fids_cat.resize(src_len, src_len + 1);
    fids_fonly.resize(src_len, src_len + 1);
    fids_ef.resize(src_len, src_len + 1);
    src_tree.resize(src_len, src_len + 1, TD::Convert("XX"));
    ParseTreeString(tree, src_len);
  }

  void ParseTreeString(const string& tree, unsigned src_len) {
    stack<pair<int, WordID> > stk;  // first = i, second = category
    pair<int, WordID> cur_cat; cur_cat.first = -1;
    unsigned i = 0;
    unsigned p = 0;
    while(p < tree.size()) {
      const char cur = tree[p];
      if (cur == '(') {
        stk.push(cur_cat);
        ++p;
        unsigned k = p + 1;
        while (k < tree.size() && tree[k] != ' ') { ++k; }
        cur_cat.first = i;
        cur_cat.second = TD::Convert(tree.substr(p, k - p));
        // cerr << "NT: '" << tree.substr(p, k-p) << "' (i=" << i << ")\n";
        p = k + 1;
      } else if (cur == ')') {
        unsigned k = p;
        while (k < tree.size() && tree[k] == ')') { ++k; }
        const unsigned num_closes = k - p;
        for (unsigned ci = 0; ci < num_closes; ++ci) {
          // cur_cat.second spans from cur_cat.first to i
          // cerr << TD::Convert(cur_cat.second) << " from " << cur_cat.first << " to " << i << endl;
          // NOTE: unary rule chains end up being labeled with the top-most category
          src_tree(cur_cat.first, i) = cur_cat.second;
          cur_cat = stk.top();
          stk.pop();
        }
        p = k;
        while (p < tree.size() && (tree[p] == ' ' || tree[p] == '\t')) { ++p; }
      } else if (cur == ' ' || cur == '\t') {
        cerr << "Unexpected whitespace in: " << tree << endl;
        abort();
      } else { // terminal symbol
        unsigned k = p + 1;
        do {
          while (k < tree.size() && tree[k] != ')' && tree[k] != ' ') { ++k; }
          // cerr << "TERM: '" << tree.substr(p, k-p) << "' (i=" << i << ")\n";
          ++i;
          assert(i <= src_len);
          while (k < tree.size() && tree[k] == ' ') { ++k; }
          p = k;
        } while (p < tree.size() && tree[p] != ')');
      }
    }
    // cerr << "i=" << i << "  src_len=" << src_len << endl;
    assert(i == src_len);  // make sure tree specified in src_tree is
                           // the same length as the source sentence
  }

  WordID FireFeatures(const TRule& rule, const int i, const int j, const WordID* ants, SparseVector<double>* feats) {
    //cerr << "fire features: " << rule.AsString() << " for " << i << "," << j << endl;
    const WordID lhs = src_tree(i,j);
    int& fid_cat = fids_cat(i,j);
    int& fid_fonly = fids_fonly(i,j)[&rule];
    int& fid_ef = fids_ef(i,j)[&rule];
    if (fid_ef <= 0) {
      ostringstream os;
      ostringstream os2;
      os << "SYN:" << TD::Convert(lhs);
      os2 << "SYN:" << TD::Convert(lhs) << '_' << SpanSizeTransform(j - i);
      fid_cat = FD::Convert(os2.str());
      os << ':';
      unsigned ntc = 0;
      for (unsigned k = 0; k < rule.f_.size(); ++k) {
        if (k > 0) os << '_';
        int fj = rule.f_[k];
        if (fj <= 0) {
          os << '[' << TD::Convert(ants[ntc++]) << ']';
        } else {
          os << TD::Convert(fj);
        }
      }
      fid_fonly = FD::Convert(os.str());
      os << ':';
      for (unsigned k = 0; k < rule.e_.size(); ++k) {
        const int ei = rule.e_[k];
        if (k > 0) os << '_';
        if (ei <= 0)
          os << '[' << (1-ei) << ']';
        else
          os << TD::Convert(ei);
      }
      fid_ef = FD::Convert(os.str());
    }
    if (fid_cat > 0)
      feats->set_value(fid_cat, 1.0);
    if (fid_fonly > 0)
      feats->set_value(fid_fonly, 1.0);
    if (fid_ef > 0)
      feats->set_value(fid_ef, 1.0);
    return lhs;
  }

  Array2D<WordID> src_tree;  // src_tree(i,j) NT = type
  mutable Array2D<int> fids_cat;   // fires for an LHS match
  mutable Array2D<map<const TRule*, int> > fids_fonly; // fires for an f-string
  mutable Array2D<map<const TRule*, int> > fids_ef;    // fires for fully lexicalized
};

SourceSyntaxFeatures::SourceSyntaxFeatures(const string& param) :
    FeatureFunction(sizeof(WordID)) {
  impl = new SourceSyntaxFeaturesImpl;
}

SourceSyntaxFeatures::~SourceSyntaxFeatures() {
  delete impl;
  impl = NULL;
}

void SourceSyntaxFeatures::TraversalFeaturesImpl(const SentenceMetadata& smeta,
                                     const Hypergraph::Edge& edge,
                                     const vector<const void*>& ant_contexts,
                                     SparseVector<double>* features,
                                     SparseVector<double>* estimated_features,
                                     void* context) const {
  WordID ants[8];
  for (unsigned i = 0; i < ant_contexts.size(); ++i)
    ants[i] = *static_cast<const WordID*>(ant_contexts[i]);

  *static_cast<WordID*>(context) =
     impl->FireFeatures(*edge.rule_, edge.i_, edge.j_, ants, features);
}

void SourceSyntaxFeatures::PrepareForInput(const SentenceMetadata& smeta) {
  impl->InitializeGrids(smeta.GetSGMLValue("src_tree"), smeta.GetSourceLength());
}

<commit_msg>remove features that are overfitting<commit_after>#include "ff_source_syntax.h"

#include <sstream>
#include <stack>

#include "sentence_metadata.h"
#include "array2d.h"
#include "filelib.h"

using namespace std;

// implements the source side syntax features described in Blunsom et al. (EMNLP 2008)
// source trees must be represented in Penn Treebank format, e.g.
//     (S (NP John) (VP (V left)))

// log transform to make long spans cluster together
// but preserve differences
inline int SpanSizeTransform(unsigned span_size) {
  if (!span_size) return 0;
  return static_cast<int>(log(span_size+1) / log(1.39)) - 1;
}

struct SourceSyntaxFeaturesImpl {
  SourceSyntaxFeaturesImpl() {}

  void InitializeGrids(const string& tree, unsigned src_len) {
    assert(tree.size() > 0);
    //fids_cat.clear();
    fids_ef.clear();
    src_tree.clear();
    //fids_cat.resize(src_len, src_len + 1);
    fids_ef.resize(src_len, src_len + 1);
    src_tree.resize(src_len, src_len + 1, TD::Convert("XX"));
    ParseTreeString(tree, src_len);
  }

  void ParseTreeString(const string& tree, unsigned src_len) {
    stack<pair<int, WordID> > stk;  // first = i, second = category
    pair<int, WordID> cur_cat; cur_cat.first = -1;
    unsigned i = 0;
    unsigned p = 0;
    while(p < tree.size()) {
      const char cur = tree[p];
      if (cur == '(') {
        stk.push(cur_cat);
        ++p;
        unsigned k = p + 1;
        while (k < tree.size() && tree[k] != ' ') { ++k; }
        cur_cat.first = i;
        cur_cat.second = TD::Convert(tree.substr(p, k - p));
        // cerr << "NT: '" << tree.substr(p, k-p) << "' (i=" << i << ")\n";
        p = k + 1;
      } else if (cur == ')') {
        unsigned k = p;
        while (k < tree.size() && tree[k] == ')') { ++k; }
        const unsigned num_closes = k - p;
        for (unsigned ci = 0; ci < num_closes; ++ci) {
          // cur_cat.second spans from cur_cat.first to i
          // cerr << TD::Convert(cur_cat.second) << " from " << cur_cat.first << " to " << i << endl;
          // NOTE: unary rule chains end up being labeled with the top-most category
          src_tree(cur_cat.first, i) = cur_cat.second;
          cur_cat = stk.top();
          stk.pop();
        }
        p = k;
        while (p < tree.size() && (tree[p] == ' ' || tree[p] == '\t')) { ++p; }
      } else if (cur == ' ' || cur == '\t') {
        cerr << "Unexpected whitespace in: " << tree << endl;
        abort();
      } else { // terminal symbol
        unsigned k = p + 1;
        do {
          while (k < tree.size() && tree[k] != ')' && tree[k] != ' ') { ++k; }
          // cerr << "TERM: '" << tree.substr(p, k-p) << "' (i=" << i << ")\n";
          ++i;
          assert(i <= src_len);
          while (k < tree.size() && tree[k] == ' ') { ++k; }
          p = k;
        } while (p < tree.size() && tree[p] != ')');
      }
    }
    // cerr << "i=" << i << "  src_len=" << src_len << endl;
    assert(i == src_len);  // make sure tree specified in src_tree is
                           // the same length as the source sentence
  }

  WordID FireFeatures(const TRule& rule, const int i, const int j, const WordID* ants, SparseVector<double>* feats) {
    //cerr << "fire features: " << rule.AsString() << " for " << i << "," << j << endl;
    const WordID lhs = src_tree(i,j);
    //int& fid_cat = fids_cat(i,j);
    int& fid_ef = fids_ef(i,j)[&rule];
    if (fid_ef <= 0) {
      ostringstream os;
      //ostringstream os2;
      os << "SYN:" << TD::Convert(lhs);
      //os2 << "SYN:" << TD::Convert(lhs) << '_' << SpanSizeTransform(j - i);
      //fid_cat = FD::Convert(os2.str());
      os << ':';
      unsigned ntc = 0;
      for (unsigned k = 0; k < rule.f_.size(); ++k) {
        if (k > 0) os << '_';
        int fj = rule.f_[k];
        if (fj <= 0) {
          os << '[' << TD::Convert(ants[ntc++]) << ']';
        } else {
          os << TD::Convert(fj);
        }
      }
      os << ':';
      for (unsigned k = 0; k < rule.e_.size(); ++k) {
        const int ei = rule.e_[k];
        if (k > 0) os << '_';
        if (ei <= 0)
          os << '[' << (1-ei) << ']';
        else
          os << TD::Convert(ei);
      }
      fid_ef = FD::Convert(os.str());
    }
    //if (fid_cat > 0)
    //  feats->set_value(fid_cat, 1.0);
    if (fid_ef > 0)
      feats->set_value(fid_ef, 1.0);
    return lhs;
  }

  Array2D<WordID> src_tree;  // src_tree(i,j) NT = type
  // mutable Array2D<int> fids_cat;   // this tends to overfit baddly
  mutable Array2D<map<const TRule*, int> > fids_ef;    // fires for fully lexicalized
};

SourceSyntaxFeatures::SourceSyntaxFeatures(const string& param) :
    FeatureFunction(sizeof(WordID)) {
  impl = new SourceSyntaxFeaturesImpl;
}

SourceSyntaxFeatures::~SourceSyntaxFeatures() {
  delete impl;
  impl = NULL;
}

void SourceSyntaxFeatures::TraversalFeaturesImpl(const SentenceMetadata& smeta,
                                     const Hypergraph::Edge& edge,
                                     const vector<const void*>& ant_contexts,
                                     SparseVector<double>* features,
                                     SparseVector<double>* estimated_features,
                                     void* context) const {
  WordID ants[8];
  for (unsigned i = 0; i < ant_contexts.size(); ++i)
    ants[i] = *static_cast<const WordID*>(ant_contexts[i]);

  *static_cast<WordID*>(context) =
     impl->FireFeatures(*edge.rule_, edge.i_, edge.j_, ants, features);
}

void SourceSyntaxFeatures::PrepareForInput(const SentenceMetadata& smeta) {
  impl->InitializeGrids(smeta.GetSGMLValue("src_tree"), smeta.GetSourceLength());
}

<|endoftext|>


<commit_before>// -*- Mode: C++; tab-width: 2; -*-
// vi: set ts=2:
//
// $Id: SurfaceModel_test.C,v 1.1.2.3 2007/05/10 20:16:16 amoll Exp $
//
// Author:
//   Andreas Moll
//

#include <BALL/CONCEPT/classTest.h>
#include <BALL/FORMAT/PDBFile.h>
#include <BALL/KERNEL/forEach.h>
#include <BALL/KERNEL/bond.h>
#include <BALL/VIEW/PRIMITIVES/mesh.h>

///////////////////////////
#include <BALL/VIEW/MODELS/surfaceModel.h>
///////////////////////////

using namespace BALL;
using namespace BALL::VIEW;

START_TEST(AddSurfaceModel, "$Id: SurfaceModel_test.C,v 1.1.2.3 2007/05/10 20:16:16 amoll Exp $")

CHECK(CSTR)
	AddSurfaceModel();
RESULT

CHECK(AddSurfaceModel::BALL_CREATE(AddSurfaceModel))
  //BAUSTELLE
RESULT

CHECK(AddSurfaceModel::setProbeRadius(float radius))
	AddSurfaceModel bs;
	bs.setProbeRadius(0.12);
RESULT

CHECK(AddSurfaceModel::getProbeRadius() const  throw())
	AddSurfaceModel bs;
	bs.setProbeRadius(0.12);
	TEST_REAL_EQUAL(bs.getProbeRadius(), 0.12)
RESULT

PDBFile pdb("data/1BNA.pdb");
System system;
pdb >> system;

CHECK(AddSurfaceModel::Processor::Result operator() (Composite& composite))
	AddSurfaceModel bs;
	bool result = bs.operator() (system);
	TEST_EQUAL(result, true)
RESULT

CHECK(AddSurfaceModel::createGeometricObjects() throw())
	AddSurfaceModel bs;
	bs.setProbeRadius(1);
	system.apply(bs);
	bs.createGeometricObjects();
	TEST_EQUAL(bs.getGeometricObjects().size(), 1)
	Mesh* m = dynamic_cast<Mesh*>(*bs.getGeometricObjects().begin());
	TEST_NOT_EQUAL(m, 0)
	PRECISION(0.0001)
	TEST_EQUAL(m->vertex.size(), 33673)
	TEST_EQUAL(m->triangle.size(), 67415)
	TEST_EQUAL(m->normal.size(), 33673)
	TEST_REAL_EQUAL(m->vertex[0].x, 3.42099)
	TEST_REAL_EQUAL(m->vertex[0].y, 23.7674)
	TEST_REAL_EQUAL(m->vertex[0].z, 9.45008)
	TEST_REAL_EQUAL(m->normal[0].x, 0.57071)
	TEST_REAL_EQUAL(m->normal[0].y, 0.526747)
	TEST_REAL_EQUAL(m->normal[0].z, -0.629943)
	TEST_EQUAL(m->triangle[0].v1, 1)
	TEST_EQUAL(m->triangle[0].v2, 3247)
	TEST_EQUAL(m->triangle[0].v3, 3251)
RESULT

/////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////
END_TEST
<commit_msg>no message<commit_after>// -*- Mode: C++; tab-width: 2; -*-
// vi: set ts=2:
//
// $Id: SurfaceModel_test.C,v 1.1.2.4 2007/05/14 08:18:07 amoll Exp $
//
// Author:
//   Andreas Moll
//

#include <BALL/CONCEPT/classTest.h>
#include <BALL/FORMAT/PDBFile.h>
#include <BALL/KERNEL/forEach.h>
#include <BALL/KERNEL/bond.h>
#include <BALL/VIEW/PRIMITIVES/mesh.h>

///////////////////////////
#include <BALL/VIEW/MODELS/surfaceModel.h>
///////////////////////////

using namespace BALL;
using namespace BALL::VIEW;

START_TEST(AddSurfaceModel, "$Id: SurfaceModel_test.C,v 1.1.2.4 2007/05/14 08:18:07 amoll Exp $")

CHECK(CSTR)
	AddSurfaceModel();
RESULT

CHECK(AddSurfaceModel::BALL_CREATE(AddSurfaceModel))
  //BAUSTELLE
RESULT

CHECK(AddSurfaceModel::setProbeRadius(float radius))
	AddSurfaceModel bs;
	bs.setProbeRadius(0.12);
RESULT

CHECK(AddSurfaceModel::getProbeRadius() const  throw())
	AddSurfaceModel bs;
	bs.setProbeRadius(0.12);
	TEST_REAL_EQUAL(bs.getProbeRadius(), 0.12)
RESULT

PDBFile pdb("data/1BNA.pdb");
System system;
pdb >> system;

CHECK(AddSurfaceModel::Processor::Result operator() (Composite& composite))
	AddSurfaceModel bs;
	bool result = bs.operator() (system);
	TEST_EQUAL(result, true)
RESULT

CHECK(AddSurfaceModel::createGeometricObjects() throw())
	AddSurfaceModel bs;
	bs.setProbeRadius(1);
	system.apply(bs);
	bs.createGeometricObjects();
	TEST_EQUAL(bs.getGeometricObjects().size(), 1)
	Mesh* m = dynamic_cast<Mesh*>(*bs.getGeometricObjects().begin());
	TEST_NOT_EQUAL(m, 0)
	PRECISION(0.0001)
	TEST_EQUAL(m->vertex.size() > 30000, true)
	TEST_EQUAL(m->triangle.size() > 60000, true)
	TEST_EQUAL(m->normal.size() > 30000, true)
	TEST_EQUAL(m->normal.size(), m->vertex.size())
RESULT

/////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////
END_TEST
<|endoftext|>


<commit_before>// -*- Mode: C++; tab-width: 2; -*-
// vi: set ts=2:
//
#include <BALL/VIEW/DIALOGS/PTEDialog.h>
#include <BALL/KERNEL/PTE.h>
#include <BALL/VIEW/WIDGETS/scene.h>

#include <QtGui/QToolTip>
#include <QtGui/QPushButton>

namespace BALL
{
	namespace VIEW
	{

PTEDialog::PTEDialog(QWidget* parent)
	: QDialog(parent),
		Ui_PTEDialogData()
{
	setupUi(this);

	connectChilds_(this);

	vector<String> elements;
	Element element;

	for (Position nr = 0; ; nr++)
	{
		element = PTE.getElement(nr);
		if (element == Element::UNKNOWN) break;

		elements.push_back(element.getSymbol());
	}

	sort(elements.begin(), elements.end());

	for (Position nr = 0; nr < elements.size(); nr++)
	{
		element_box->addItem(elements[nr].c_str());
	}

	connect(element_box, SIGNAL(activated(int)), this, SLOT(elementChoosen_()));

	Scene* scene = Scene::getInstance(0);
	if (scene == 0)
	{
	  Log.error() << "Expected a Scene, but found none!" << std::endl;
		return;
  }

	int an = scene->getEditElementType();

	String symb = PTE[an].getSymbol();
	element_box->setCurrentIndex(element_box->findText(symb.c_str()));
}

PTEDialog::~PTEDialog()
{
}

void PTEDialog::newElementType(int elementNumber)
{
 Scene* scene = Scene::getInstance(0);

 if (scene == 0)
 {
	 Log.error() << "Expected an Scene, but found none!" << std::endl;
 }
 else
 {
	 scene->setEditElementType(elementNumber);
 }
}

QString PTEDialog::atomProperties_(int number)
{
 Element e = PTE[number];
 String result("Element: ");
 result += e.getName();
 result += "\nSymbol: ";
 result += e.getSymbol();
 result += "\nAtomic Weight: ";
 result += String(e.getAtomicWeight()).c_str();
 result += "\nAtomic Radius: ";
 result += String(e.getAtomicRadius()).c_str();
 result += "\nCovalent Radius: ";
 result += String(e.getCovalentRadius()).c_str();
 result += "\nVan der Waals Radius: ";
 result += String(e.getVanDerWaalsRadius()).c_str();
 result += "\nElectronegativity: ";
 result += String(e.getElectronegativity()).c_str();
 result += "\n";

 QString r(result.c_str());

 return r;
}

void PTEDialog::elementClicked_()
{
	QObject* w = sender();
	if (w == 0) return;
	QPushButton* bt = dynamic_cast<QPushButton*>(w);
	if (bt == 0) return;

	String element = ascii(bt->text());

	Element e = PTE[element];
	newElementType(e.getAtomicNumber());
	accept();
}

void PTEDialog::elementChoosen_()
{
	int an = PTE[(ascii(element_box->currentText()))].getAtomicNumber();
	newElementType(an);
	accept();
}

void PTEDialog::connectChilds_(QObject* o)
{
	QPushButton* w = dynamic_cast<QPushButton*>(o);
	if (w != 0)
	{
		String s = ascii(w->text());
		if (s.size() > 2) return;
		Position an = PTE[s].getAtomicNumber();
		w->setToolTip(atomProperties_(an));
		connect(w, SIGNAL(released()), this, SLOT(elementClicked_()));
		return;
	}

	// iterate over all buttons in the button group
	QObjectList ol = o->children();
	QObjectList::iterator it = ol.begin();
	for (; it != ol.end(); it++)
	{
		connectChilds_(*it);
	}
}

	} // VIEW and BALL namespace:
}
<commit_msg>Indentation fixes in PTEDialog<commit_after>// -*- Mode: C++; tab-width: 2; -*-
// vi: set ts=2:
//
#include <BALL/VIEW/DIALOGS/PTEDialog.h>
#include <BALL/KERNEL/PTE.h>
#include <BALL/VIEW/WIDGETS/scene.h>

#include <QtGui/QToolTip>
#include <QtGui/QPushButton>

namespace BALL
{
	namespace VIEW
	{

		PTEDialog::PTEDialog(QWidget* parent)
			: QDialog(parent),
				Ui_PTEDialogData()
		{
			setupUi(this);

			connectChilds_(this);

			vector<String> elements;
			Element element;

			for (Position nr = 0; ; nr++)
			{
				element = PTE.getElement(nr);
				if (element == Element::UNKNOWN) break;

				elements.push_back(element.getSymbol());
			}

			sort(elements.begin(), elements.end());

			for (Position nr = 0; nr < elements.size(); nr++)
			{
				element_box->addItem(elements[nr].c_str());
			}

			connect(element_box, SIGNAL(activated(int)), this, SLOT(elementChoosen_()));

			Scene* scene = Scene::getInstance(0);
			if (scene == 0)
			{
				Log.error() << "Expected a Scene, but found none!" << std::endl;
				return;
			}

			int an = scene->getEditElementType();

			String symb = PTE[an].getSymbol();
			element_box->setCurrentIndex(element_box->findText(symb.c_str()));
		}

		PTEDialog::~PTEDialog()
		{
		}

		void PTEDialog::newElementType(int elementNumber)
		{
			Scene* scene = Scene::getInstance(0);

			if (scene == 0)
			{
				Log.error() << "Expected an Scene, but found none!" << std::endl;
			}
			else
			{
				scene->setEditElementType(elementNumber);
			}
		}

		QString PTEDialog::atomProperties_(int number)
		{
			Element e = PTE[number];
			String result("Element: ");
			result += e.getName();
			result += "\nSymbol: ";
			result += e.getSymbol();
			result += "\nAtomic Weight: ";
			result += String(e.getAtomicWeight()).c_str();
			result += "\nAtomic Radius: ";
			result += String(e.getAtomicRadius()).c_str();
			result += "\nCovalent Radius: ";
			result += String(e.getCovalentRadius()).c_str();
			result += "\nVan der Waals Radius: ";
			result += String(e.getVanDerWaalsRadius()).c_str();
			result += "\nElectronegativity: ";
			result += String(e.getElectronegativity()).c_str();
			result += "\n";

			QString r(result.c_str());

			return r;
		}

		void PTEDialog::elementClicked_()
		{
			QObject* w = sender();
			if (w == 0) return;
			QPushButton* bt = dynamic_cast<QPushButton*>(w);
			if (bt == 0) return;

			String element = ascii(bt->text());

			Element e = PTE[element];
			newElementType(e.getAtomicNumber());
			accept();
		}

		void PTEDialog::elementChoosen_()
		{
			int an = PTE[(ascii(element_box->currentText()))].getAtomicNumber();
			newElementType(an);
			accept();
		}

		void PTEDialog::connectChilds_(QObject* o)
		{
			QPushButton* w = dynamic_cast<QPushButton*>(o);
			if (w != 0)
			{
				String s = ascii(w->text());
				if (s.size() > 2) return;
				Position an = PTE[s].getAtomicNumber();
				w->setToolTip(atomProperties_(an));
				connect(w, SIGNAL(released()), this, SLOT(elementClicked_()));
				return;
			}

			// iterate over all buttons in the button group
			QObjectList ol = o->children();
			QObjectList::iterator it = ol.begin();
			for (; it != ol.end(); it++)
			{
				connectChilds_(*it);
			}
		}

	} // VIEW and BALL namespace:
}
<|endoftext|>


<commit_before>// -*- Mode: C++; tab-width: 2; -*-
// vi: set ts=2:
//
#include <BALL/VIEW/DIALOGS/setCamera.h>
#include <BALL/VIEW/WIDGETS/scene.h>
#include <BALL/VIEW/KERNEL/mainControl.h>
#include <BALL/VIEW/KERNEL/stage.h>

#include <qpushbutton.h>
#include <qlineedit.h>

namespace BALL
{
	namespace VIEW
	{

SetCamera::SetCamera( QWidget* parent,  const char* name, bool modal, WFlags fl )
    : SetCameraData( parent, name, modal, fl )
{
	const Camera& camera = ((Scene*) parent)->getStage()->getCamera();

	String text(String(camera.getViewPoint().x) + "|" +
							String(camera.getViewPoint().y) + "|" +
							String(camera.getViewPoint().z));
	view_edit->setText(text.c_str());

	text = String(camera.getLookAtPosition().x) + "|" +
				 String(camera.getLookAtPosition().y) + "|" +
				 String(camera.getLookAtPosition().z);

	look_edit->setText(text.c_str());
}

SetCamera::~SetCamera()
{
 // no need to delete child widgets, Qt does it all for us
}

void SetCamera::okPressed()
{
	MainControl *main_control = MainControl::getMainControl(parentWidget());

	hide();
	vector<String> strings1, strings2;
	String(view_edit->text().ascii()).split(strings1, "|");
	String(look_edit->text().ascii()).split(strings2, "|");

	if (strings1.size() != 3 ||
			strings2.size() != 3)
	{
		main_control->setStatusbarText("Invalid Values!");
		return;
	}

	for (Index i = 0; i<3; i++)
	{
		if (!strings1[i].isFloat() ||
				!strings2[i].isFloat())
		{
			main_control->setStatusbarText("Invalid Values!");
			return;
		}
	}

	Vector3 vp(strings1[0].toFloat(), strings1[1].toFloat(), strings1[2].toFloat());
	Vector3 lp(strings2[0].toFloat(), strings2[1].toFloat(), strings2[2].toFloat());
	if (vp == lp)
	{
		Log.error() << "Invalid values for setCamera: viewpoint = look at" << std::endl;
		return;
	}

	Camera& camera = ((Scene*) parentWidget())->getStage()->getCamera();
	camera.setViewPoint(vp);
	camera.setLookAtPosition(lp);
}

// NAMESPACE
} }
<commit_msg>no message<commit_after>// -*- Mode: C++; tab-width: 2; -*-
// vi: set ts=2:
//
#include <BALL/VIEW/DIALOGS/setCamera.h>
#include <BALL/VIEW/WIDGETS/scene.h>
#include <BALL/VIEW/KERNEL/mainControl.h>
#include <BALL/VIEW/KERNEL/stage.h>

#include <qpushbutton.h>
#include <qlineedit.h>

namespace BALL
{
	namespace VIEW
	{

SetCamera::SetCamera( QWidget* parent,  const char* name, bool modal, WFlags fl )
    : SetCameraData( parent, name, modal, fl )
{
	const Camera& camera = ((Scene*) parent)->getStage()->getCamera();

	view_x->setText(String((Index)camera.getViewPoint().x).c_str());
	view_y->setText(String((Index)camera.getViewPoint().y).c_str());
	view_z->setText(String((Index)camera.getViewPoint().z).c_str());

	look_x->setText(String((Index)camera.getLookAtPosition().x).c_str());
	look_y->setText(String((Index)camera.getLookAtPosition().y).c_str());
	look_z->setText(String((Index)camera.getLookAtPosition().z).c_str());
}

SetCamera::~SetCamera()
{
 // no need to delete child widgets, Qt does it all for us
}

void SetCamera::okPressed()
{
	MainControl *main_control = MainControl::getMainControl(parentWidget());
	hide();

	float vx, vy, vz, lx, ly, lz;

	try
	{
		vx = String(view_x->text().ascii()).toFloat();
		vy = String(view_y->text().ascii()).toFloat();
		vz = String(view_z->text().ascii()).toFloat();
		lx = String(look_x->text().ascii()).toFloat();
		ly = String(look_y->text().ascii()).toFloat();
		lz = String(look_z->text().ascii()).toFloat();
	}
	catch(...)
	{
		main_control->setStatusbarText("Invalid Values!");
		return;
	}

	Vector3 vp(vx,vy,vz);
	Vector3 lp(lx,ly,lz);

	if (vp == lp)
	{
		Log.error() << "Invalid values for setCamera: viewpoint = look at" << std::endl;
		return;
	}

	Camera& camera = ((Scene*) parentWidget())->getStage()->getCamera();
	camera.setViewPoint(vp);
	camera.setLookAtPosition(lp);
}

// NAMESPACE
} }
<|endoftext|>


<commit_before>//
// Aspia Project
// Copyright (C) 2019 Dmitry Chapyshev <dmitry@aspia.ru>
//
// 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
// (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 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 <https://www.gnu.org/licenses/>.
//

#include "base/version.h"

#include <gtest/gtest.h>

#include <cstddef>
#include <cstdint>
#include <utility>

namespace {

TEST(VersionTest, DefaultConstructor)
{
    base::Version v;
    EXPECT_FALSE(v.isValid());
}

TEST(VersionTest, ValueSemantics)
{
    base::Version v1("1.2.3.4");
    EXPECT_TRUE(v1.isValid());
    base::Version v3;
    EXPECT_FALSE(v3.isValid());
    {
        base::Version v2(v1);
        v3 = v2;
        EXPECT_TRUE(v2.isValid());
        EXPECT_EQ(v1, v2);
    }
    EXPECT_EQ(v3, v1);
}

TEST(VersionTest, MoveSemantics)
{
    const std::vector<uint32_t> components = { 1, 2, 3, 4 };
    base::Version v1(std::move(components));
    EXPECT_TRUE(v1.isValid());
    base::Version v2("1.2.3.4");
    EXPECT_EQ(v1, v2);
}

TEST(VersionTest, GetVersionFromString)
{
    static const struct version_string
    {
        const char* input;
        size_t parts;
        uint32_t firstpart;
        bool success;
    } cases[] =
    {
        { "", 0, 0, false },
        { " ", 0, 0, false },
        { "\t", 0, 0, false },
        { "\n", 0, 0, false },
        { "  ", 0, 0, false },
        { ".", 0, 0, false },
        { " . ", 0, 0, false },
        { "0", 1, 0, true },
        { "0.", 0, 0, false },
        { "0.0", 2, 0, true },
        { "4294967295.0", 2, 4294967295, true },
        { "4294967296.0", 0, 0, false },
        { "-1.0", 0, 0, false },
        { "1.-1.0", 0, 0, false },
        { "1,--1.0", 0, 0, false },
        { "+1.0", 0, 0, false },
        { "1.+1.0", 0, 0, false },
        { "1+1.0", 0, 0, false },
        { "++1.0", 0, 0, false },
        { "1.0a", 0, 0, false },
        { "1.2.3.4.5.6.7.8.9.0", 10, 1, true },
        { "02.1", 0, 0, false },
        { "0.01", 2, 0, true },
        { "f.1", 0, 0, false },
        { "15.007.20011", 3, 15, true },
        { "15.5.28.130162", 4, 15, true },
    };

    int index = 0;

    for (const auto& i : cases)
    {
        printf("case %d\n", index);
        base::Version version(i.input);
        EXPECT_EQ(i.success, version.isValid());
        if (i.success)
        {
            EXPECT_EQ(i.parts, version.components().size());
            EXPECT_EQ(i.firstpart, version.components()[0]);
        }
        ++index;
    }
}

TEST(VersionTest, Compare)
{
    static const struct version_compare
    {
        const char* lhs;
        const char* rhs;
        int expected;
    } cases[] =
    {
        { "1.0", "1.0", 0 },
        { "1.0", "0.0", 1 },
        { "1.0", "2.0", -1 },
        { "1.0", "1.1", -1 },
        { "1.1", "1.0", 1 },
        { "1.0", "1.0.1", -1 },
        { "1.1", "1.0.1", 1 },
        { "1.1", "1.0.1", 1 },
        { "1.0.0", "1.0", 0 },
        { "1.0.3", "1.0.20", -1 },
        { "11.0.10", "15.007.20011", -1 },
        { "11.0.10", "15.5.28.130162", -1 },
        { "15.5.28.130162", "15.5.28.130162", 0 },
    };

    for (const auto& i : cases)
    {
        base::Version lhs(i.lhs);
        base::Version rhs(i.rhs);
        EXPECT_EQ(lhs.compareTo(rhs), i.expected) << i.lhs << " ? " << i.rhs;
        // CompareToWildcardString() should have same behavior as CompareTo() when
        // no wildcards are present.
        EXPECT_EQ(lhs.compareToWildcardString(i.rhs), i.expected)
            << i.lhs << " ? " << i.rhs;
        EXPECT_EQ(rhs.compareToWildcardString(i.lhs), -i.expected)
            << i.lhs << " ? " << i.rhs;

        // Test comparison operators
        switch (i.expected)
        {
            case -1:
                EXPECT_LT(lhs, rhs);
                EXPECT_LE(lhs, rhs);
                EXPECT_NE(lhs, rhs);
                EXPECT_FALSE(lhs == rhs);
                EXPECT_FALSE(lhs >= rhs);
                EXPECT_FALSE(lhs > rhs);
                break;
            case 0:
                EXPECT_FALSE(lhs < rhs);
                EXPECT_LE(lhs, rhs);
                EXPECT_FALSE(lhs != rhs);
                EXPECT_EQ(lhs, rhs);
                EXPECT_GE(lhs, rhs);
                EXPECT_FALSE(lhs > rhs);
                break;
            case 1:
                EXPECT_FALSE(lhs < rhs);
                EXPECT_FALSE(lhs <= rhs);
                EXPECT_NE(lhs, rhs);
                EXPECT_FALSE(lhs == rhs);
                EXPECT_GE(lhs, rhs);
                EXPECT_GT(lhs, rhs);
                break;
        }
    }
}

TEST(VersionTest, CompareToWildcardString)
{
    static const struct version_compare
    {
        const char* lhs;
        const char* rhs;
        int expected;
    } cases[] =
    {
        { "1.0", "1.*", 0 },
        { "1.0", "0.*", 1 },
        { "1.0", "2.*", -1 },
        { "1.2.3", "1.2.3.*", 0 },
        { "10.0", "1.0.*", 1 },
        { "1.0", "3.0.*", -1 },
        { "1.4", "1.3.0.*", 1 },
        { "1.3.9", "1.3.*", 0 },
        { "1.4.1", "1.3.*", 1 },
        { "1.3", "1.4.5.*", -1 },
        { "1.5", "1.4.5.*", 1 },
        { "1.3.9", "1.3.*", 0 },
        { "1.2.0.0.0.0", "1.2.*", 0 },
    };

    for (const auto& i : cases)
    {
        const base::Version version(i.lhs);
        const int result = version.compareToWildcardString(i.rhs);
        EXPECT_EQ(result, i.expected) << i.lhs << "?" << i.rhs;
    }
}

TEST(VersionTest, IsValidWildcardString)
{
    static const struct version_compare
    {
        const char* version;
        bool expected;
    } cases[] =
    {
        { "1.0", true },
        { "", false },
        { "1.2.3.4.5.6", true },
        { "1.2.3.*", true },
        { "1.2.3.5*", false },
        { "1.2.3.56*", false },
        { "1.*.3", false },
        { "20.*", true },
        { "+2.*", false },
        { "*", false },
        { "*.2", false },
    };
    for (const auto& i : cases)
    {
        EXPECT_EQ(base::Version::isValidWildcardString(i.version), i.expected)
            << i.version << "?" << i.expected;
    }
}

} // namespace
<commit_msg>- Remove unneeded printf.<commit_after>//
// Aspia Project
// Copyright (C) 2019 Dmitry Chapyshev <dmitry@aspia.ru>
//
// 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
// (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 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 <https://www.gnu.org/licenses/>.
//

#include "base/version.h"

#include <gtest/gtest.h>

#include <cstddef>
#include <cstdint>
#include <utility>

namespace {

TEST(VersionTest, DefaultConstructor)
{
    base::Version v;
    EXPECT_FALSE(v.isValid());
}

TEST(VersionTest, ValueSemantics)
{
    base::Version v1("1.2.3.4");
    EXPECT_TRUE(v1.isValid());
    base::Version v3;
    EXPECT_FALSE(v3.isValid());
    {
        base::Version v2(v1);
        v3 = v2;
        EXPECT_TRUE(v2.isValid());
        EXPECT_EQ(v1, v2);
    }
    EXPECT_EQ(v3, v1);
}

TEST(VersionTest, MoveSemantics)
{
    const std::vector<uint32_t> components = { 1, 2, 3, 4 };
    base::Version v1(std::move(components));
    EXPECT_TRUE(v1.isValid());
    base::Version v2("1.2.3.4");
    EXPECT_EQ(v1, v2);
}

TEST(VersionTest, GetVersionFromString)
{
    static const struct version_string
    {
        const char* input;
        size_t parts;
        uint32_t firstpart;
        bool success;
    } cases[] =
    {
        { "", 0, 0, false },
        { " ", 0, 0, false },
        { "\t", 0, 0, false },
        { "\n", 0, 0, false },
        { "  ", 0, 0, false },
        { ".", 0, 0, false },
        { " . ", 0, 0, false },
        { "0", 1, 0, true },
        { "0.", 0, 0, false },
        { "0.0", 2, 0, true },
        { "4294967295.0", 2, 4294967295, true },
        { "4294967296.0", 0, 0, false },
        { "-1.0", 0, 0, false },
        { "1.-1.0", 0, 0, false },
        { "1,--1.0", 0, 0, false },
        { "+1.0", 0, 0, false },
        { "1.+1.0", 0, 0, false },
        { "1+1.0", 0, 0, false },
        { "++1.0", 0, 0, false },
        { "1.0a", 0, 0, false },
        { "1.2.3.4.5.6.7.8.9.0", 10, 1, true },
        { "02.1", 0, 0, false },
        { "0.01", 2, 0, true },
        { "f.1", 0, 0, false },
        { "15.007.20011", 3, 15, true },
        { "15.5.28.130162", 4, 15, true },
    };

    int index = 0;

    for (const auto& i : cases)
    {
        base::Version version(i.input);
        EXPECT_EQ(i.success, version.isValid());
        if (i.success)
        {
            EXPECT_EQ(i.parts, version.components().size());
            EXPECT_EQ(i.firstpart, version.components()[0]);
        }
        ++index;
    }
}

TEST(VersionTest, Compare)
{
    static const struct version_compare
    {
        const char* lhs;
        const char* rhs;
        int expected;
    } cases[] =
    {
        { "1.0", "1.0", 0 },
        { "1.0", "0.0", 1 },
        { "1.0", "2.0", -1 },
        { "1.0", "1.1", -1 },
        { "1.1", "1.0", 1 },
        { "1.0", "1.0.1", -1 },
        { "1.1", "1.0.1", 1 },
        { "1.1", "1.0.1", 1 },
        { "1.0.0", "1.0", 0 },
        { "1.0.3", "1.0.20", -1 },
        { "11.0.10", "15.007.20011", -1 },
        { "11.0.10", "15.5.28.130162", -1 },
        { "15.5.28.130162", "15.5.28.130162", 0 },
    };

    for (const auto& i : cases)
    {
        base::Version lhs(i.lhs);
        base::Version rhs(i.rhs);
        EXPECT_EQ(lhs.compareTo(rhs), i.expected) << i.lhs << " ? " << i.rhs;
        // CompareToWildcardString() should have same behavior as CompareTo() when
        // no wildcards are present.
        EXPECT_EQ(lhs.compareToWildcardString(i.rhs), i.expected)
            << i.lhs << " ? " << i.rhs;
        EXPECT_EQ(rhs.compareToWildcardString(i.lhs), -i.expected)
            << i.lhs << " ? " << i.rhs;

        // Test comparison operators
        switch (i.expected)
        {
            case -1:
                EXPECT_LT(lhs, rhs);
                EXPECT_LE(lhs, rhs);
                EXPECT_NE(lhs, rhs);
                EXPECT_FALSE(lhs == rhs);
                EXPECT_FALSE(lhs >= rhs);
                EXPECT_FALSE(lhs > rhs);
                break;
            case 0:
                EXPECT_FALSE(lhs < rhs);
                EXPECT_LE(lhs, rhs);
                EXPECT_FALSE(lhs != rhs);
                EXPECT_EQ(lhs, rhs);
                EXPECT_GE(lhs, rhs);
                EXPECT_FALSE(lhs > rhs);
                break;
            case 1:
                EXPECT_FALSE(lhs < rhs);
                EXPECT_FALSE(lhs <= rhs);
                EXPECT_NE(lhs, rhs);
                EXPECT_FALSE(lhs == rhs);
                EXPECT_GE(lhs, rhs);
                EXPECT_GT(lhs, rhs);
                break;
        }
    }
}

TEST(VersionTest, CompareToWildcardString)
{
    static const struct version_compare
    {
        const char* lhs;
        const char* rhs;
        int expected;
    } cases[] =
    {
        { "1.0", "1.*", 0 },
        { "1.0", "0.*", 1 },
        { "1.0", "2.*", -1 },
        { "1.2.3", "1.2.3.*", 0 },
        { "10.0", "1.0.*", 1 },
        { "1.0", "3.0.*", -1 },
        { "1.4", "1.3.0.*", 1 },
        { "1.3.9", "1.3.*", 0 },
        { "1.4.1", "1.3.*", 1 },
        { "1.3", "1.4.5.*", -1 },
        { "1.5", "1.4.5.*", 1 },
        { "1.3.9", "1.3.*", 0 },
        { "1.2.0.0.0.0", "1.2.*", 0 },
    };

    for (const auto& i : cases)
    {
        const base::Version version(i.lhs);
        const int result = version.compareToWildcardString(i.rhs);
        EXPECT_EQ(result, i.expected) << i.lhs << "?" << i.rhs;
    }
}

TEST(VersionTest, IsValidWildcardString)
{
    static const struct version_compare
    {
        const char* version;
        bool expected;
    } cases[] =
    {
        { "1.0", true },
        { "", false },
        { "1.2.3.4.5.6", true },
        { "1.2.3.*", true },
        { "1.2.3.5*", false },
        { "1.2.3.56*", false },
        { "1.*.3", false },
        { "20.*", true },
        { "+2.*", false },
        { "*", false },
        { "*.2", false },
    };
    for (const auto& i : cases)
    {
        EXPECT_EQ(base::Version::isValidWildcardString(i.version), i.expected)
            << i.version << "?" << i.expected;
    }
}

} // namespace
<|endoftext|>


<commit_before>#include "bitcoinamountfield.h"
#include "qvaluecombobox.h"
#include "bitcoinunits.h"

#include "guiconstants.h"

#include <QLabel>
#include <QLineEdit>
#include <QHBoxLayout>
#include <QKeyEvent>
#include <QDoubleSpinBox>
#include <QComboBox>
#include <QApplication>
#include <qmath.h>

BitcoinAmountField::BitcoinAmountField(QWidget* parent) : QWidget(parent), amount(nullptr), currentUnit(-1), valid(true)
{
    amount = new QDoubleSpinBox(this);
    amount->setLocale(QLocale::c());
    amount->setDecimals(8);
    amount->installEventFilter(this);
    amount->setMaximumWidth(170);
    amount->setSingleStep(0.001);

    QHBoxLayout *layout = new QHBoxLayout(this);
    layout->addWidget(amount);
    unit = new QValueComboBox(this);
    unit->setModel(new BitcoinUnits(this));
    unit->setMinimumWidth(80);
    layout->addWidget(unit);
    layout->addStretch(1);
    layout->setContentsMargins(0,0,0,0);

    setLayout(layout);

    setFocusPolicy(Qt::TabFocus);
    setFocusProxy(amount);

    // If one of the widgets changes, the combined content changes as well
    connect(amount, static_cast<void (QDoubleSpinBox::*)(const QString&)>(&QDoubleSpinBox::textChanged), this, &BitcoinAmountField::textChanged);
    connect(unit, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, &BitcoinAmountField::unitChanged);

    // Set default based on configuration
    unitChanged(unit->currentIndex());
}

void BitcoinAmountField::setText(const QString &text)
{
    if (text.isEmpty())
        amount->clear();
    else
        amount->setValue(text.toDouble());
}

void BitcoinAmountField::clear()
{
    amount->clear();
    unit->setCurrentIndex(0);
}

bool BitcoinAmountField::validate()
{
    bool valid = true;
    if (amount->value() == 0.0)
        valid = false;
    if (valid && !BitcoinUnits::parse(currentUnit, text(), nullptr))
        valid = false;

    setValid(valid);

    return valid;
}

void BitcoinAmountField::setValid(bool valid)
{
    if(valid == this->valid)
        return;

    if (valid)
        amount->setStyleSheet("");
    else
        amount->setStyleSheet(STYLE_INVALID);

    this->valid = valid;
}

QString BitcoinAmountField::text() const
{
    if (amount->text().isEmpty())
        return QString();
    else
        return amount->text();
}

bool BitcoinAmountField::eventFilter(QObject *object, QEvent *event)
{
    if (event->type() == QEvent::FocusIn)
    {
        // Clear invalid flag on focus
        setValid(true);
    }
    else if (event->type() == QEvent::KeyPress || event->type() == QEvent::KeyRelease)
    {
        QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
        if (keyEvent->key() == Qt::Key_Comma)
        {
            // Translate a comma into a period
            QKeyEvent periodKeyEvent(event->type(), Qt::Key_Period, keyEvent->modifiers(), ".", keyEvent->isAutoRepeat(), keyEvent->count());
            qApp->sendEvent(object, &periodKeyEvent);
            return true;
        }
    }
    return QWidget::eventFilter(object, event);
}

QWidget *BitcoinAmountField::setupTabChain(QWidget *prev)
{
    QWidget::setTabOrder(prev, amount);
    return amount;
}

qint64 BitcoinAmountField::value(bool *valid_out) const
{
    qint64 val_out = 0;
    bool valid = BitcoinUnits::parse(currentUnit, text(), &val_out);
    if(valid_out)
    {
        *valid_out = valid;
    }
    return val_out;
}

void BitcoinAmountField::setValue(qint64 value)
{
    setText(BitcoinUnits::format(currentUnit, value));
}

void BitcoinAmountField::unitChanged(int idx)
{
    // Use description tooltip for current unit for the combobox
    unit->setToolTip(unit->itemData(idx, Qt::ToolTipRole).toString());

    // Determine new unit ID
    int newUnit = unit->itemData(idx, BitcoinUnits::UnitRole).toInt();

    // Parse current value and convert to new unit
    bool valid = false;
    qint64 currentValue = value(&valid);

    currentUnit = newUnit;

    // Set max length after retrieving the value, to prevent truncation
    amount->setDecimals(BitcoinUnits::decimals(currentUnit));
    amount->setMaximum(qPow(10, BitcoinUnits::amountDigits(currentUnit)) - qPow(10, -amount->decimals()));

    if(valid)
    {
        // If value was valid, re-place it in the widget with the new unit
        setValue(currentValue);
    }
    else
    {
        // If current value is invalid, just clear field
        setText("");
    }
    setValid(true);
}

void BitcoinAmountField::setDisplayUnit(int newUnit)
{
    unit->setValue(newUnit);
}
<commit_msg>Correct signal in bitcoinamountfield.cpp<commit_after>#include "bitcoinamountfield.h"
#include "qvaluecombobox.h"
#include "bitcoinunits.h"

#include "guiconstants.h"

#include <QLabel>
#include <QLineEdit>
#include <QHBoxLayout>
#include <QKeyEvent>
#include <QDoubleSpinBox>
#include <QComboBox>
#include <QApplication>
#include <qmath.h>

BitcoinAmountField::BitcoinAmountField(QWidget* parent) : QWidget(parent), amount(nullptr), currentUnit(-1), valid(true)
{
    amount = new QDoubleSpinBox(this);
    amount->setLocale(QLocale::c());
    amount->setDecimals(8);
    amount->installEventFilter(this);
    amount->setMaximumWidth(170);
    amount->setSingleStep(0.001);

    QHBoxLayout *layout = new QHBoxLayout(this);
    layout->addWidget(amount);
    unit = new QValueComboBox(this);
    unit->setModel(new BitcoinUnits(this));
    unit->setMinimumWidth(80);
    layout->addWidget(unit);
    layout->addStretch(1);
    layout->setContentsMargins(0,0,0,0);

    setLayout(layout);

    setFocusPolicy(Qt::TabFocus);
    setFocusProxy(amount);

    // If one of the widgets changes, the combined content changes as well
    connect(amount, static_cast<void (QDoubleSpinBox::*)(const QString&)>(&QDoubleSpinBox::valueChanged),
            this, &BitcoinAmountField::textChanged);
    connect(unit, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, &BitcoinAmountField::unitChanged);

    // Set default based on configuration
    unitChanged(unit->currentIndex());
}

void BitcoinAmountField::setText(const QString &text)
{
    if (text.isEmpty())
        amount->clear();
    else
        amount->setValue(text.toDouble());
}

void BitcoinAmountField::clear()
{
    amount->clear();
    unit->setCurrentIndex(0);
}

bool BitcoinAmountField::validate()
{
    bool valid = true;
    if (amount->value() == 0.0)
        valid = false;
    if (valid && !BitcoinUnits::parse(currentUnit, text(), nullptr))
        valid = false;

    setValid(valid);

    return valid;
}

void BitcoinAmountField::setValid(bool valid)
{
    if(valid == this->valid)
        return;

    if (valid)
        amount->setStyleSheet("");
    else
        amount->setStyleSheet(STYLE_INVALID);

    this->valid = valid;
}

QString BitcoinAmountField::text() const
{
    if (amount->text().isEmpty())
        return QString();
    else
        return amount->text();
}

bool BitcoinAmountField::eventFilter(QObject *object, QEvent *event)
{
    if (event->type() == QEvent::FocusIn)
    {
        // Clear invalid flag on focus
        setValid(true);
    }
    else if (event->type() == QEvent::KeyPress || event->type() == QEvent::KeyRelease)
    {
        QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
        if (keyEvent->key() == Qt::Key_Comma)
        {
            // Translate a comma into a period
            QKeyEvent periodKeyEvent(event->type(), Qt::Key_Period, keyEvent->modifiers(), ".", keyEvent->isAutoRepeat(), keyEvent->count());
            qApp->sendEvent(object, &periodKeyEvent);
            return true;
        }
    }
    return QWidget::eventFilter(object, event);
}

QWidget *BitcoinAmountField::setupTabChain(QWidget *prev)
{
    QWidget::setTabOrder(prev, amount);
    return amount;
}

qint64 BitcoinAmountField::value(bool *valid_out) const
{
    qint64 val_out = 0;
    bool valid = BitcoinUnits::parse(currentUnit, text(), &val_out);
    if(valid_out)
    {
        *valid_out = valid;
    }
    return val_out;
}

void BitcoinAmountField::setValue(qint64 value)
{
    setText(BitcoinUnits::format(currentUnit, value));
}

void BitcoinAmountField::unitChanged(int idx)
{
    // Use description tooltip for current unit for the combobox
    unit->setToolTip(unit->itemData(idx, Qt::ToolTipRole).toString());

    // Determine new unit ID
    int newUnit = unit->itemData(idx, BitcoinUnits::UnitRole).toInt();

    // Parse current value and convert to new unit
    bool valid = false;
    qint64 currentValue = value(&valid);

    currentUnit = newUnit;

    // Set max length after retrieving the value, to prevent truncation
    amount->setDecimals(BitcoinUnits::decimals(currentUnit));
    amount->setMaximum(qPow(10, BitcoinUnits::amountDigits(currentUnit)) - qPow(10, -amount->decimals()));

    if(valid)
    {
        // If value was valid, re-place it in the widget with the new unit
        setValue(currentValue);
    }
    else
    {
        // If current value is invalid, just clear field
        setText("");
    }
    setValid(true);
}

void BitcoinAmountField::setDisplayUnit(int newUnit)
{
    unit->setValue(newUnit);
}
<|endoftext|>


<commit_before>// Copyright (c) 2011-2018 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include <qt/bitcoinamountfield.h>

#include <qt/bitcoinunits.h>
#include <qt/guiconstants.h>
#include <qt/qvaluecombobox.h>
#include "guiutil.h"

#include "assetsdir.h"
#include "chainparams.h"

#include <QApplication>
#include <QAbstractSpinBox>
#include <QHBoxLayout>
#include <QKeyEvent>
#include <QLineEdit>

Q_DECLARE_METATYPE(CAsset)

/** QSpinBox that uses fixed-point numbers internally and uses our own
 * formatting/parsing functions.
 */
class AmountSpinBox: public QAbstractSpinBox
{
    Q_OBJECT

public:
    explicit AmountSpinBox(QWidget *parent):
        QAbstractSpinBox(parent),
        currentUnit(BitcoinUnits::BTC),
        singleStep(0)
    {
        current_asset = Params().GetConsensus().pegged_asset;

        setAlignment(Qt::AlignRight);

        connect(lineEdit(), &QLineEdit::textEdited, this, &AmountSpinBox::valueChanged);
    }

    QValidator::State validate(QString &text, int &pos) const
    {
        if(text.isEmpty())
            return QValidator::Intermediate;
        bool valid = false;
        parse(text, &valid);
        /* Make sure we return Intermediate so that fixup() is called on defocus */
        return valid ? QValidator::Intermediate : QValidator::Invalid;
    }

    void fixup(QString &input) const
    {
        bool valid = false;
        CAmount val = parse(input, &valid);
        if(valid)
        {
            input = GUIUtil::formatAssetAmount(current_asset, val, currentUnit, BitcoinUnits::separatorAlways, false);
            lineEdit()->setText(input);
        }
    }

    int currentPeggedUnit() const
    {
        assert(current_asset == Params().GetConsensus().pegged_asset);
        return currentUnit;
    }

    std::pair<CAsset, CAmount> value(bool *valid_out=0) const
    {
        return std::make_pair(current_asset, parse(text(), valid_out));
    }

    void setValue(const CAsset& asset, CAmount value)
    {
        current_asset = asset;
        lineEdit()->setText(GUIUtil::formatAssetAmount(asset, value, currentUnit, BitcoinUnits::separatorAlways, false));
        Q_EMIT valueChanged();
    }

    inline void setValue(const std::pair<CAsset, CAmount>& value)
    {
        setValue(value.first, value.second);
    }

    void stepBy(int steps)
    {
        bool valid = false;
        auto val = value(&valid);
        CAmount currentSingleStep = singleStep;
        if (!currentSingleStep) {
            if (current_asset == Params().GetConsensus().pegged_asset) {
                currentSingleStep = 100000;  // satoshis
            } else {
                currentSingleStep = 100000000;  // a whole asset
            }
        }
        val.second = val.second + steps * singleStep;
        val.second = qMax(val.second, CAmount(0));
        // FIXME: Add this back in when assets can have > MAX_MONEY
        // if (val.first == Params().GetConsensus().pegged_asset)
        {
            val.second = qMin(val.second, BitcoinUnits::maxMoney());
        }
        setValue(val);
    }

    void setDisplayUnit(const CAsset& asset)
    {
        if (asset == Params().GetConsensus().pegged_asset) {
            setDisplayUnit(currentUnit);
            return;
        }

        // Only used for bitcoins -> other asset
        // Leave the number alone, since the user probably intended it for this asset

        current_asset = asset;
        Q_EMIT valueChanged();
    }

    void setDisplayUnit(int unit)
    {
        bool valid = false;
        std::pair<CAsset, CAmount> val = value(&valid);
        const bool was_pegged = (val.first == Params().GetConsensus().pegged_asset);

        current_asset = Params().GetConsensus().pegged_asset;
        currentUnit = unit;

        if (!was_pegged) {
            // Leave the text as-is, if it's valid
            value(&valid);
            if (valid) {
                Q_EMIT valueChanged();
            } else {
                clear();
            }
        } else
        if(valid)
            setValue(val);
        else
            clear();
    }

    void setSingleStep(const CAmount& step)
    {
        singleStep = step;
    }

    QSize minimumSizeHint() const
    {
        if(cachedMinimumSizeHint.isEmpty())
        {
            ensurePolished();

            const QFontMetrics fm(fontMetrics());
            int h = lineEdit()->minimumSizeHint().height();
            int w = fm.width(BitcoinUnits::format(BitcoinUnits::BTC, BitcoinUnits::maxMoney(), false, BitcoinUnits::separatorAlways));
            w += 2; // cursor blinking space

            QStyleOptionSpinBox opt;
            initStyleOption(&opt);
            QSize hint(w, h);
            QSize extra(35, 6);
            opt.rect.setSize(hint + extra);
            extra += hint - style()->subControlRect(QStyle::CC_SpinBox, &opt,
                                                    QStyle::SC_SpinBoxEditField, this).size();
            // get closer to final result by repeating the calculation
            opt.rect.setSize(hint + extra);
            extra += hint - style()->subControlRect(QStyle::CC_SpinBox, &opt,
                                                    QStyle::SC_SpinBoxEditField, this).size();
            hint += extra;
            hint.setHeight(h);

            opt.rect = rect();

            cachedMinimumSizeHint = style()->sizeFromContents(QStyle::CT_SpinBox, &opt, hint, this)
                                    .expandedTo(QApplication::globalStrut());
        }
        return cachedMinimumSizeHint;
    }

private:
    CAsset current_asset;
    int currentUnit;
    CAmount singleStep;
    mutable QSize cachedMinimumSizeHint;

    /**
     * Parse a string into a number of base monetary units and
     * return validity.
     * @note Must return 0 if !valid.
     */
    CAmount parse(const QString &text, bool *valid_out=0) const
    {
        CAmount val = 0;
        bool valid = GUIUtil::parseAssetAmount(current_asset, text, currentUnit, &val);
        if(valid)
        {
            // FIXME: Add this back in when assets can have > MAX_MONEY
            if (val < 0 || (val > BitcoinUnits::maxMoney() /*&& current_asset == Params().GetConsensus().pegged_asset*/)) {
                valid = false;
            }
        }
        if(valid_out)
            *valid_out = valid;
        return valid ? val : 0;
    }

protected:
    bool event(QEvent *event)
    {
        if (event->type() == QEvent::KeyPress || event->type() == QEvent::KeyRelease)
        {
            QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
            if (keyEvent->key() == Qt::Key_Comma)
            {
                // Translate a comma into a period
                QKeyEvent periodKeyEvent(event->type(), Qt::Key_Period, keyEvent->modifiers(), ".", keyEvent->isAutoRepeat(), keyEvent->count());
                return QAbstractSpinBox::event(&periodKeyEvent);
            }
        }
        return QAbstractSpinBox::event(event);
    }

    StepEnabled stepEnabled() const
    {
        if (isReadOnly()) // Disable steps when AmountSpinBox is read-only
            return StepNone;
        if (text().isEmpty()) // Allow step-up with empty field
            return StepUpEnabled;

        StepEnabled rv = 0;
        bool valid = false;
        const std::pair<CAsset, CAmount> val = value(&valid);
        if(valid)
        {
            if (val.second > 0) {
                rv |= StepDownEnabled;
            }
            if (val.second < BitcoinUnits::maxMoney() || val.first != Params().GetConsensus().pegged_asset) {
                rv |= StepUpEnabled;
            }
        }
        return rv;
    }

Q_SIGNALS:
    void valueChanged();
};

#include <qt/bitcoinamountfield.moc>

BitcoinAmountField::BitcoinAmountField(std::set<CAsset> allowed_assets, QWidget *parent) :
    QWidget(parent),
    m_allowed_assets(allowed_assets),
    amount(0)
{
    amount = new AmountSpinBox(this);
    amount->setLocale(QLocale::c());
    amount->installEventFilter(this);
    amount->setMaximumWidth(240);

    QHBoxLayout *layout = new QHBoxLayout(this);
    layout->addWidget(amount);
    unit = new QComboBox(this);
    m_allowed_assets = allowed_assets;
    for (const auto& asset : allowed_assets) {
        addAssetChoice(asset);
    }
    layout->addWidget(unit);
    layout->addStretch(1);
    layout->setContentsMargins(0,0,0,0);

    setLayout(layout);

    setFocusPolicy(Qt::TabFocus);
    setFocusProxy(amount);

    // If one if the widgets changes, the combined content changes as well
    connect(amount, &AmountSpinBox::valueChanged, this, &BitcoinAmountField::valueChanged);
    connect(unit, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, &BitcoinAmountField::unitChanged);

    // Set default based on configuration
    unitChanged(unit->currentIndex());
}

BitcoinAmountField::BitcoinAmountField(QWidget *parent) :
    BitcoinAmountField(std::set<CAsset>({Params().GetConsensus().pegged_asset}), parent)
{
}

void BitcoinAmountField::clear()
{
    amount->clear();
    unit->setCurrentIndex(0);
}

void BitcoinAmountField::setEnabled(bool fEnabled)
{
    amount->setEnabled(fEnabled);
    unit->setEnabled(fEnabled);
}

bool BitcoinAmountField::validate()
{
    bool valid = false;
    fullValue(&valid);
    setValid(valid);
    return valid;
}

void BitcoinAmountField::setValid(bool valid)
{
    if (valid)
        amount->setStyleSheet("");
    else
        amount->setStyleSheet(STYLE_INVALID);
}

bool BitcoinAmountField::eventFilter(QObject *object, QEvent *event)
{
    if (event->type() == QEvent::FocusIn)
    {
        // Clear invalid flag on focus
        setValid(true);
    }
    return QWidget::eventFilter(object, event);
}

QWidget *BitcoinAmountField::setupTabChain(QWidget *prev)
{
    QWidget::setTabOrder(prev, amount);
    QWidget::setTabOrder(amount, unit);
    return unit;
}

std::pair<CAsset, CAmount> BitcoinAmountField::fullValue(bool *valid_out) const
{
    return amount->value(valid_out);
}

void BitcoinAmountField::setFullValue(const CAsset& asset, const CAmount& value)
{
    amount->setValue(asset, value);
    setDisplayUnit(asset);
}

CAmount BitcoinAmountField::value(bool *valid_out) const
{
    std::pair<CAsset, CAmount> val = amount->value(valid_out);
    assert(val.first == Params().GetConsensus().pegged_asset);
    return val.second;
}

void BitcoinAmountField::setValue(const CAmount& value)
{
    amount->setValue(Params().GetConsensus().pegged_asset, value);
    setDisplayUnit(amount->currentPeggedUnit());
}

void BitcoinAmountField::setReadOnly(bool fReadOnly)
{
    amount->setReadOnly(fReadOnly);
}

bool BitcoinAmountField::hasAssetChoice(const CAsset& asset) const
{
    if (asset == Params().GetConsensus().pegged_asset) {
        return -1 != unit->findData(0, Qt::UserRole);
    }
    return -1 != unit->findData(QVariant::fromValue(asset), Qt::UserRole);
}

void BitcoinAmountField::addAssetChoice(const CAsset& asset)
{
    if (asset == Params().GetConsensus().pegged_asset) {
        // Special handling
        for (const auto& pegged_unit : BitcoinUnits::availableUnits()) {
            unit->addItem(BitcoinUnits::shortName(pegged_unit), int(pegged_unit));
        }
        return;
    }
    unit->addItem(QString::fromStdString(gAssetsDir.GetIdentifier(asset)), QVariant::fromValue(asset));
}

void BitcoinAmountField::removeAssetChoice(const CAsset& asset)
{
    if (asset == Params().GetConsensus().pegged_asset) {
        // Special handling
        for (const auto& pegged_unit : BitcoinUnits::availableUnits()) {
            unit->removeItem(unit->findData(int(pegged_unit), Qt::UserRole));
        }
        return;
    }
    unit->removeItem(unit->findData(QVariant::fromValue(asset), Qt::UserRole));
}

void BitcoinAmountField::setAllowedAssets(const std::set<CAsset>& allowed_assets)
{
    std::set<CAsset> assets_to_remove;
    for (const auto& asset : m_allowed_assets) {
        if (!allowed_assets.count(asset)) {
            assets_to_remove.insert(asset);
        }
    }
    m_allowed_assets = allowed_assets;
    const QVariant& sel_userdata = unit->itemData(unit->currentIndex(), Qt::UserRole);
    const CAsset sel_asset = (sel_userdata.type() == QVariant::UserType) ? sel_userdata.value<CAsset>() : Params().GetConsensus().pegged_asset;
    for (const auto& asset : assets_to_remove) {
        // Leave it in place for now if it's selected
        if (sel_asset == asset) continue;

        removeAssetChoice(asset);
    }
    for (const auto& asset : allowed_assets) {
        if (!hasAssetChoice(asset)) {
            addAssetChoice(asset);
        }
    }
}

void BitcoinAmountField::unitChanged(int idx)
{
    const CAsset previous_asset = amount->value().first;

    // Use description tooltip for current unit for the combobox
    const QVariant& userdata = unit->itemData(idx, Qt::UserRole);
    if (userdata.type() == QVariant::UserType) {
        const CAsset asset = userdata.value<CAsset>();
        unit->setToolTip(tr("Custom asset (%1)").arg(QString::fromStdString(asset.GetHex())));

        amount->setDisplayUnit(asset);
    } else {
        // Determine new unit ID
        int newUnit = userdata.toInt();

        unit->setToolTip(BitcoinUnits::description(newUnit));

        amount->setDisplayUnit(newUnit);
    }

    if (!(m_allowed_assets.count(previous_asset) || amount->value().first == previous_asset)) {
        removeAssetChoice(previous_asset);
    }
}

void BitcoinAmountField::setDisplayUnit(const CAsset& asset)
{
    if (asset == Params().GetConsensus().pegged_asset) {
        setDisplayUnit(amount->currentPeggedUnit());
        return;
    }
    if (!hasAssetChoice(asset)) {
        addAssetChoice(asset);
    }
    unit->setCurrentIndex(unit->findData(QVariant::fromValue(asset), Qt::UserRole));
}

void BitcoinAmountField::setDisplayUnit(int newUnit)
{
    if (!hasAssetChoice(Params().GetConsensus().pegged_asset)) {
        addAssetChoice(Params().GetConsensus().pegged_asset);
    }
    unit->setCurrentIndex(unit->findData(newUnit, Qt::UserRole));
}

void BitcoinAmountField::setSingleStep(const CAmount& step)
{
    amount->setSingleStep(step);
}
<commit_msg>GUI: Emit BitcoinAmountField::valueChanged when unit changes even if the current value is invalid<commit_after>// Copyright (c) 2011-2018 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include <qt/bitcoinamountfield.h>

#include <qt/bitcoinunits.h>
#include <qt/guiconstants.h>
#include <qt/qvaluecombobox.h>
#include "guiutil.h"

#include "assetsdir.h"
#include "chainparams.h"

#include <QApplication>
#include <QAbstractSpinBox>
#include <QHBoxLayout>
#include <QKeyEvent>
#include <QLineEdit>

Q_DECLARE_METATYPE(CAsset)

/** QSpinBox that uses fixed-point numbers internally and uses our own
 * formatting/parsing functions.
 */
class AmountSpinBox: public QAbstractSpinBox
{
    Q_OBJECT

public:
    explicit AmountSpinBox(QWidget *parent):
        QAbstractSpinBox(parent),
        currentUnit(BitcoinUnits::BTC),
        singleStep(0)
    {
        current_asset = Params().GetConsensus().pegged_asset;

        setAlignment(Qt::AlignRight);

        connect(lineEdit(), &QLineEdit::textEdited, this, &AmountSpinBox::valueChanged);
    }

    QValidator::State validate(QString &text, int &pos) const
    {
        if(text.isEmpty())
            return QValidator::Intermediate;
        bool valid = false;
        parse(text, &valid);
        /* Make sure we return Intermediate so that fixup() is called on defocus */
        return valid ? QValidator::Intermediate : QValidator::Invalid;
    }

    void fixup(QString &input) const
    {
        bool valid = false;
        CAmount val = parse(input, &valid);
        if(valid)
        {
            input = GUIUtil::formatAssetAmount(current_asset, val, currentUnit, BitcoinUnits::separatorAlways, false);
            lineEdit()->setText(input);
        }
    }

    int currentPeggedUnit() const
    {
        assert(current_asset == Params().GetConsensus().pegged_asset);
        return currentUnit;
    }

    std::pair<CAsset, CAmount> value(bool *valid_out=0) const
    {
        return std::make_pair(current_asset, parse(text(), valid_out));
    }

    void setValue(const CAsset& asset, CAmount value)
    {
        current_asset = asset;
        lineEdit()->setText(GUIUtil::formatAssetAmount(asset, value, currentUnit, BitcoinUnits::separatorAlways, false));
        Q_EMIT valueChanged();
    }

    inline void setValue(const std::pair<CAsset, CAmount>& value)
    {
        setValue(value.first, value.second);
    }

    void stepBy(int steps)
    {
        bool valid = false;
        auto val = value(&valid);
        CAmount currentSingleStep = singleStep;
        if (!currentSingleStep) {
            if (current_asset == Params().GetConsensus().pegged_asset) {
                currentSingleStep = 100000;  // satoshis
            } else {
                currentSingleStep = 100000000;  // a whole asset
            }
        }
        val.second = val.second + steps * singleStep;
        val.second = qMax(val.second, CAmount(0));
        // FIXME: Add this back in when assets can have > MAX_MONEY
        // if (val.first == Params().GetConsensus().pegged_asset)
        {
            val.second = qMin(val.second, BitcoinUnits::maxMoney());
        }
        setValue(val);
    }

    void setDisplayUnit(const CAsset& asset)
    {
        if (asset == Params().GetConsensus().pegged_asset) {
            setDisplayUnit(currentUnit);
            return;
        }

        // Only used for bitcoins -> other asset
        // Leave the number alone, since the user probably intended it for this asset

        current_asset = asset;
        Q_EMIT valueChanged();
    }

    void setDisplayUnit(int unit)
    {
        bool valid = false;
        std::pair<CAsset, CAmount> val = value(&valid);
        const bool was_pegged = (val.first == Params().GetConsensus().pegged_asset);

        current_asset = Params().GetConsensus().pegged_asset;
        currentUnit = unit;

        if (!was_pegged) {
            // Leave the text as-is, if it's valid
            value(&valid);
            if (!valid) {
                clear();
            }
        } else
        if(valid)
            setValue(val);
        else
            clear();
        Q_EMIT valueChanged();
    }

    void setSingleStep(const CAmount& step)
    {
        singleStep = step;
    }

    QSize minimumSizeHint() const
    {
        if(cachedMinimumSizeHint.isEmpty())
        {
            ensurePolished();

            const QFontMetrics fm(fontMetrics());
            int h = lineEdit()->minimumSizeHint().height();
            int w = fm.width(BitcoinUnits::format(BitcoinUnits::BTC, BitcoinUnits::maxMoney(), false, BitcoinUnits::separatorAlways));
            w += 2; // cursor blinking space

            QStyleOptionSpinBox opt;
            initStyleOption(&opt);
            QSize hint(w, h);
            QSize extra(35, 6);
            opt.rect.setSize(hint + extra);
            extra += hint - style()->subControlRect(QStyle::CC_SpinBox, &opt,
                                                    QStyle::SC_SpinBoxEditField, this).size();
            // get closer to final result by repeating the calculation
            opt.rect.setSize(hint + extra);
            extra += hint - style()->subControlRect(QStyle::CC_SpinBox, &opt,
                                                    QStyle::SC_SpinBoxEditField, this).size();
            hint += extra;
            hint.setHeight(h);

            opt.rect = rect();

            cachedMinimumSizeHint = style()->sizeFromContents(QStyle::CT_SpinBox, &opt, hint, this)
                                    .expandedTo(QApplication::globalStrut());
        }
        return cachedMinimumSizeHint;
    }

private:
    CAsset current_asset;
    int currentUnit;
    CAmount singleStep;
    mutable QSize cachedMinimumSizeHint;

    /**
     * Parse a string into a number of base monetary units and
     * return validity.
     * @note Must return 0 if !valid.
     */
    CAmount parse(const QString &text, bool *valid_out=0) const
    {
        CAmount val = 0;
        bool valid = GUIUtil::parseAssetAmount(current_asset, text, currentUnit, &val);
        if(valid)
        {
            // FIXME: Add this back in when assets can have > MAX_MONEY
            if (val < 0 || (val > BitcoinUnits::maxMoney() /*&& current_asset == Params().GetConsensus().pegged_asset*/)) {
                valid = false;
            }
        }
        if(valid_out)
            *valid_out = valid;
        return valid ? val : 0;
    }

protected:
    bool event(QEvent *event)
    {
        if (event->type() == QEvent::KeyPress || event->type() == QEvent::KeyRelease)
        {
            QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
            if (keyEvent->key() == Qt::Key_Comma)
            {
                // Translate a comma into a period
                QKeyEvent periodKeyEvent(event->type(), Qt::Key_Period, keyEvent->modifiers(), ".", keyEvent->isAutoRepeat(), keyEvent->count());
                return QAbstractSpinBox::event(&periodKeyEvent);
            }
        }
        return QAbstractSpinBox::event(event);
    }

    StepEnabled stepEnabled() const
    {
        if (isReadOnly()) // Disable steps when AmountSpinBox is read-only
            return StepNone;
        if (text().isEmpty()) // Allow step-up with empty field
            return StepUpEnabled;

        StepEnabled rv = 0;
        bool valid = false;
        const std::pair<CAsset, CAmount> val = value(&valid);
        if(valid)
        {
            if (val.second > 0) {
                rv |= StepDownEnabled;
            }
            if (val.second < BitcoinUnits::maxMoney() || val.first != Params().GetConsensus().pegged_asset) {
                rv |= StepUpEnabled;
            }
        }
        return rv;
    }

Q_SIGNALS:
    void valueChanged();
};

#include <qt/bitcoinamountfield.moc>

BitcoinAmountField::BitcoinAmountField(std::set<CAsset> allowed_assets, QWidget *parent) :
    QWidget(parent),
    m_allowed_assets(allowed_assets),
    amount(0)
{
    amount = new AmountSpinBox(this);
    amount->setLocale(QLocale::c());
    amount->installEventFilter(this);
    amount->setMaximumWidth(240);

    QHBoxLayout *layout = new QHBoxLayout(this);
    layout->addWidget(amount);
    unit = new QComboBox(this);
    m_allowed_assets = allowed_assets;
    for (const auto& asset : allowed_assets) {
        addAssetChoice(asset);
    }
    layout->addWidget(unit);
    layout->addStretch(1);
    layout->setContentsMargins(0,0,0,0);

    setLayout(layout);

    setFocusPolicy(Qt::TabFocus);
    setFocusProxy(amount);

    // If one if the widgets changes, the combined content changes as well
    connect(amount, &AmountSpinBox::valueChanged, this, &BitcoinAmountField::valueChanged);
    connect(unit, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, &BitcoinAmountField::unitChanged);

    // Set default based on configuration
    unitChanged(unit->currentIndex());
}

BitcoinAmountField::BitcoinAmountField(QWidget *parent) :
    BitcoinAmountField(std::set<CAsset>({Params().GetConsensus().pegged_asset}), parent)
{
}

void BitcoinAmountField::clear()
{
    amount->clear();
    unit->setCurrentIndex(0);
}

void BitcoinAmountField::setEnabled(bool fEnabled)
{
    amount->setEnabled(fEnabled);
    unit->setEnabled(fEnabled);
}

bool BitcoinAmountField::validate()
{
    bool valid = false;
    fullValue(&valid);
    setValid(valid);
    return valid;
}

void BitcoinAmountField::setValid(bool valid)
{
    if (valid)
        amount->setStyleSheet("");
    else
        amount->setStyleSheet(STYLE_INVALID);
}

bool BitcoinAmountField::eventFilter(QObject *object, QEvent *event)
{
    if (event->type() == QEvent::FocusIn)
    {
        // Clear invalid flag on focus
        setValid(true);
    }
    return QWidget::eventFilter(object, event);
}

QWidget *BitcoinAmountField::setupTabChain(QWidget *prev)
{
    QWidget::setTabOrder(prev, amount);
    QWidget::setTabOrder(amount, unit);
    return unit;
}

std::pair<CAsset, CAmount> BitcoinAmountField::fullValue(bool *valid_out) const
{
    return amount->value(valid_out);
}

void BitcoinAmountField::setFullValue(const CAsset& asset, const CAmount& value)
{
    amount->setValue(asset, value);
    setDisplayUnit(asset);
}

CAmount BitcoinAmountField::value(bool *valid_out) const
{
    std::pair<CAsset, CAmount> val = amount->value(valid_out);
    assert(val.first == Params().GetConsensus().pegged_asset);
    return val.second;
}

void BitcoinAmountField::setValue(const CAmount& value)
{
    amount->setValue(Params().GetConsensus().pegged_asset, value);
    setDisplayUnit(amount->currentPeggedUnit());
}

void BitcoinAmountField::setReadOnly(bool fReadOnly)
{
    amount->setReadOnly(fReadOnly);
}

bool BitcoinAmountField::hasAssetChoice(const CAsset& asset) const
{
    if (asset == Params().GetConsensus().pegged_asset) {
        return -1 != unit->findData(0, Qt::UserRole);
    }
    return -1 != unit->findData(QVariant::fromValue(asset), Qt::UserRole);
}

void BitcoinAmountField::addAssetChoice(const CAsset& asset)
{
    if (asset == Params().GetConsensus().pegged_asset) {
        // Special handling
        for (const auto& pegged_unit : BitcoinUnits::availableUnits()) {
            unit->addItem(BitcoinUnits::shortName(pegged_unit), int(pegged_unit));
        }
        return;
    }
    unit->addItem(QString::fromStdString(gAssetsDir.GetIdentifier(asset)), QVariant::fromValue(asset));
}

void BitcoinAmountField::removeAssetChoice(const CAsset& asset)
{
    if (asset == Params().GetConsensus().pegged_asset) {
        // Special handling
        for (const auto& pegged_unit : BitcoinUnits::availableUnits()) {
            unit->removeItem(unit->findData(int(pegged_unit), Qt::UserRole));
        }
        return;
    }
    unit->removeItem(unit->findData(QVariant::fromValue(asset), Qt::UserRole));
}

void BitcoinAmountField::setAllowedAssets(const std::set<CAsset>& allowed_assets)
{
    std::set<CAsset> assets_to_remove;
    for (const auto& asset : m_allowed_assets) {
        if (!allowed_assets.count(asset)) {
            assets_to_remove.insert(asset);
        }
    }
    m_allowed_assets = allowed_assets;
    const QVariant& sel_userdata = unit->itemData(unit->currentIndex(), Qt::UserRole);
    const CAsset sel_asset = (sel_userdata.type() == QVariant::UserType) ? sel_userdata.value<CAsset>() : Params().GetConsensus().pegged_asset;
    for (const auto& asset : assets_to_remove) {
        // Leave it in place for now if it's selected
        if (sel_asset == asset) continue;

        removeAssetChoice(asset);
    }
    for (const auto& asset : allowed_assets) {
        if (!hasAssetChoice(asset)) {
            addAssetChoice(asset);
        }
    }
}

void BitcoinAmountField::unitChanged(int idx)
{
    const CAsset previous_asset = amount->value().first;

    // Use description tooltip for current unit for the combobox
    const QVariant& userdata = unit->itemData(idx, Qt::UserRole);
    if (userdata.type() == QVariant::UserType) {
        const CAsset asset = userdata.value<CAsset>();
        unit->setToolTip(tr("Custom asset (%1)").arg(QString::fromStdString(asset.GetHex())));

        amount->setDisplayUnit(asset);
    } else {
        // Determine new unit ID
        int newUnit = userdata.toInt();

        unit->setToolTip(BitcoinUnits::description(newUnit));

        amount->setDisplayUnit(newUnit);
    }

    if (!(m_allowed_assets.count(previous_asset) || amount->value().first == previous_asset)) {
        removeAssetChoice(previous_asset);
    }
}

void BitcoinAmountField::setDisplayUnit(const CAsset& asset)
{
    if (asset == Params().GetConsensus().pegged_asset) {
        setDisplayUnit(amount->currentPeggedUnit());
        return;
    }
    if (!hasAssetChoice(asset)) {
        addAssetChoice(asset);
    }
    unit->setCurrentIndex(unit->findData(QVariant::fromValue(asset), Qt::UserRole));
}

void BitcoinAmountField::setDisplayUnit(int newUnit)
{
    if (!hasAssetChoice(Params().GetConsensus().pegged_asset)) {
        addAssetChoice(Params().GetConsensus().pegged_asset);
    }
    unit->setCurrentIndex(unit->findData(newUnit, Qt::UserRole));
}

void BitcoinAmountField::setSingleStep(const CAmount& step)
{
    amount->setSingleStep(step);
}
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2011, Paul Tagliamonte <tag@pault.ag>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

#include <ansiescape.hh>
#include <ansicsi.hh>
#include <iostream>
#include <sstream>

#include "ANSITerminal.hh"
#include "Terminal.hh"
#include "Shibuya.hh"

ANSITerminal::ANSITerminal() {
	this->_init_ANSITerminal();
	this->_init_Terminal( 80, 25 );
}

ANSITerminal::ANSITerminal( int width, int height ) {
	this->_init_ANSITerminal();
	this->_init_Terminal( width, height );
}

ANSITerminal::~ANSITerminal() {
}

void ANSITerminal::_init_ANSITerminal() {
	ansi_escape_parser_reset();
}

void ANSITerminal::_handle_private_escape( ansi_sequence * last ) {
	this->log( "Ignoring a private-mode ANSI sequence." );
}

void ANSITerminal::_handle_escape( ansi_sequence * last ) {
	char mode               = last->mode;
	std::vector<int> * seqs = last->values;

	int move_steps =  1;
	int nRow       = -1;
	int nCol       = -1;
	int nTop       = -1;
	int nBottom    = -1; /* Sorry about this hack, friend */
	int cTemp1     = -1;
	int cTemp2     = -1;

	if ( last->priv ) {
		/* We have a private-mode ANSI CSI Sequence. */
		this->_handle_private_escape( last );
		return; /* Don't drop to normal handlers */
	}

	std::ostringstream oss;
	oss << "Incoming mode: " << mode << " -- seqs follow: ";
	for ( unsigned int i = 0; i < seqs->size(); ++i )
		oss << seqs->at(i) << ", ";
	this->log(oss.str());

	switch ( mode ) {
		case CSI_CUU:
		case CSI_CUD:
		case CSI_CUF:
		case CSI_CUB:
			this->log( "CU[U|D|F|B Command issued." );
			/* Moves the cursor n (default 1) cells in the given direction. If
			 * the cursor is already at the edge of the screen, this has no
			 * effect. */
			move_steps = ( seqs->at(0) > 0 ) ? seqs->at(0) : 1;
			for ( int i = 0; i < move_steps; ++i ) {
				switch ( mode ) {
					case CSI_CUU: this->cY--; break;
					case CSI_CUD: this->cY++; break;
					case CSI_CUF: this->cX++; break;
					case CSI_CUB: this->cX--; break;
				}
			}
			this->cX = ( this->cX < this->width  )
				? this->cX : this->width;
			this->cY = ( this->cY < this->height )
				? this->cY : this->height;
			break;
		case CSI_CHA:
			this->log( "CHA Command issued" );
			/* Moves the cursor to column n. */
			this->cX = ( seqs->at(0) < 1 ) ? 0 : seqs->at(0) - 1;
			break;
		case 'P': // XXX: Fixme
			move_steps = ( seqs->at(0) > 0 ) ? seqs->at(0) : 1;
			for ( int j = 0; j < move_steps; ++j ) {
				for ( int i = this->cX; i < this->width - 1; ++i ) {
					int rootChar = GET_OFFSET( i, this->cY );
					int remoChar = GET_OFFSET( i + 1, this->cY );
					this->chars[rootChar].ch   = this->chars[remoChar].ch;
					this->chars[rootChar].attr = this->chars[remoChar].attr;
				}
				this->chars[GET_OFFSET( this->width - 1, this->cY )].ch = ' ';
				this->chars[GET_OFFSET( this->width - 1, this->cY )].attr =
					SHIBUYA_DEFAULT_CMODE;
			}
			break;
		case 'd': // XXX: Fixme
			this->log( "'d' command issued" );
			/* moves the cursor to row n. */
			this->cY = ( seqs->at(0) < 1 ) ? 0 : seqs->at(0) - 1;
			break;
		case 'M': // XXX: Fixme (CSI_DL)
			this->log( "'M' command issued" );
			/* DL | Delete the indicated # of lines. */
			move_steps = ( seqs->at(0) > 0 ) ? seqs->at(0) : 0;
			for ( int i = 0; i < move_steps; ++i )
				this->delete_line( this->cY );
			break;
		case 'L': // XXX: FIXME
			this->log( "'L' command issued" );
			move_steps = ( seqs->at(0) > 0 ) ? seqs->at(0) : 0;
			for ( int i = 0; i < move_steps; ++i )
				this->insert_line( this->cY );
			break;
		case 'm': // XXX: FIXME
			this->log( "Color command issued" );
			/*
			 * 22    Normal color or intensity neither bright, bold nor faint
			 * 25    Blink: off
			 * 27    Image: Positive
			 * 28    Reveal conceal off
			 * 39    Default text color	implementation defined
			 *       (according to standard)
			 * 49    Default background color implementation defined
			 *       (according to standard) */
			for ( unsigned int i = 0; i < seqs->size(); ++i ) {
				switch ( seqs->at(i) ) {
					case 0:
						/* Reset global attr */
						this->cMode = SHIBUYA_DEFAULT_CMODE;
						break;
					case 1:
						/* bold global attr */
						if ( SHIBUYA_ATTR_HAS_BOLD(this->cMode) == 0 )
							this->cMode += SHIBUYA_ATTR_BOLD;
						break;
					case 5:
					case 6: /* Blink */
						if ( SHIBUYA_ATTR_HAS_BLINK(this->cMode) == 0 )
							this->cMode += SHIBUYA_ATTR_BLINK;
						break;
					case 7: /* Invert */
						cTemp1 = (this->cMode & SHIBUYA_ATTR_FG_MASK);
						cTemp2 = (this->cMode & SHIBUYA_ATTR_BG_MASK);
						this->cMode -= ( cTemp1 + cTemp2 );
						cTemp1 = ( cTemp1 >> SHIBUYA_ATTR_FG_OFFSET )
							<< SHIBUYA_ATTR_BG_OFFSET;
						cTemp2 = ( cTemp2 >> SHIBUYA_ATTR_BG_OFFSET )
							<< SHIBUYA_ATTR_FG_OFFSET;
						this->cMode += ( cTemp1 + cTemp2 );
						break;
					case 30: case 31: case 32:
					case 33: case 34: case 35:
					case 36: case 37: /* Foreground */
						cTemp1  = seqs->at(i);
						cTemp1 -= 30;
						this->cMode -= (this->cMode & SHIBUYA_ATTR_FG_MASK);
						this->cMode += ( cTemp1 << SHIBUYA_ATTR_FG_OFFSET );
						this->log("Set the foreground.");
					break;
					case 40: case 41: case 42:
					case 43: case 44: case 45:
					case 46: case 47: /* Background */
						cTemp1  = seqs->at(i);
						cTemp1 -= 40;
						this->cMode -= (this->cMode & SHIBUYA_ATTR_BG_MASK);
						this->cMode += (cTemp1 << SHIBUYA_ATTR_BG_OFFSET);
						this->log("Set the background.");
					break;
					default: /* Unknown m sequence id */
						this->cMode = 0x70; //XXX: fixme
						this->log("Unknown color things.");
						break;
				}
			}
			break;
		case 'r':
			this->log( "'r' command issued" );
			nTop = seqs->at(0);

			if ( nTop < 0 )
				nTop = 1;

			if ( seqs->size() >= 2 )
				nBottom = ( seqs->at(1) > 0 ) ? seqs->at(1) : this->height;
			else
				nBottom = this->height;

			nBottom = ( nBottom < 1 )         ? 0            : nBottom;
			nTop    = ( nTop > this->height ) ? this->height : nTop - 1;

			this->scroll_frame_bottom = nBottom;
			this->scroll_frame_top    = nTop;

			this->cX = 0;
			this->cY = nTop;

			break;
		case CSI_CUP:
			this->log( "CUP command issued" );
			/* Moves the cursor to row n, column m. The values are 1-based, and
			 * default to 1 (top left corner) if omitted. A sequence such as CSI
			 * ;5H is a synonym for CSI 1;5H as well as CSI 17;H is the same as
			 * CSI 17H and CSI 17;1H */
			nRow = seqs->at(0);

			if ( nRow < 0 )
				nRow = 1;

			if ( seqs->size() >= 2 )
				nCol = seqs->at(1);

			nRow = ( nRow < 1 ) ? 1 : nRow;
			nCol = ( nCol < 1 ) ? 1 : nCol;
			nRow = ( nRow > this->height ) ? this->height : nRow;
			nCol = ( nCol > this->width  ) ? this->width  : nCol;

			this->cX = (nCol - 1);
			this->cY = (nRow - 1);

			break;
		case CSI_EL:
			this->log( "EL command issued" );
			/* Erases part of the line. If n is zero (or missing), clear from
			 * cursor to the end of the line. If n is one, clear from cursor to
			 * beginning of the line. If n is two, clear entire line. Cursor
			 * position does not change. */
			switch ( seqs->at(0) ) {
				case -1:
				case 0:
					this->erase_to_from( this->cX, this->cY,
						this->width - 1, this->cY );
					break;
				case 1:
					this->erase_to_from( 0, this->cY, this->cX, this->cY );
					break;
				case 2:
					this->erase_to_from( 0, this->cY,
						this->width - 1, this->cY );
					break;
			}
			break;
		case CSI_ED:
			this->log( "ED command issued" );
			/* Clears part of the screen. If n is zero (or missing),
			 * clear from cursor to end of screen. If n is one,
			 * clear from cursor to beginning of the screen. If n is two, clear
			 * entire screen (and moves cursor to upper left on MS-DOS
			 * ANSI.SYS).*/
			switch ( seqs->at(0) ) {
				case -1:
				case  0:
					this->erase_to_from( this->cX, this->cY,
						this->width, this->height );
					break;
				case 1:
					this->erase_to_from( 0, 0, this->cX, this->cY );
					break;
				case 2:
					this->erase_to_from( 0, 0,
						this->width, this->height - 1 );
					break;
			}
			break;
		default:
			this->log( "Hit an unhandled command bit" );
			break;
	}

}

void ANSITerminal::insert( unsigned char c ) {
	ANSI_ESCAPE_PARSE_T res = ansi_escape_parser_feed( c );
	ansi_sequence * last = NULL;
	switch ( res ) {
		case ANSI_ESCAPE_PARSE_OK:
			last = ansi_escape_get_last_sequence();
			this->_handle_escape( last );
			break;
		case ANSI_ESCAPE_PARSE_BAD:
			ansi_escape_parser_reset();
			Terminal::insert(c);
			break;
		case ANSI_ESCAPE_PARSE_INCOMPLETE:
			break;
	}
}
<commit_msg>adding in color voodoo<commit_after>/*
 * Copyright (C) 2011, Paul Tagliamonte <tag@pault.ag>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

#include <ansiescape.hh>
#include <ansicsi.hh>
#include <iostream>
#include <sstream>

#include "ANSITerminal.hh"
#include "Terminal.hh"
#include "Shibuya.hh"

ANSITerminal::ANSITerminal() {
	this->_init_ANSITerminal();
	this->_init_Terminal( 80, 25 );
}

ANSITerminal::ANSITerminal( int width, int height ) {
	this->_init_ANSITerminal();
	this->_init_Terminal( width, height );
}

ANSITerminal::~ANSITerminal() {
}

void ANSITerminal::_init_ANSITerminal() {
	ansi_escape_parser_reset();
}

void ANSITerminal::_handle_private_escape( ansi_sequence * last ) {
	this->log( "Ignoring a private-mode ANSI sequence." );
}

void ANSITerminal::_handle_escape( ansi_sequence * last ) {
	char mode               = last->mode;
	std::vector<int> * seqs = last->values;

	int move_steps =  1;
	int nRow       = -1;
	int nCol       = -1;
	int nTop       = -1;
	int nBottom    = -1; /* Sorry about this hack, friend */
	int cTemp1     = -1;
	int cTemp2     = -1;

	if ( last->priv ) {
		/* We have a private-mode ANSI CSI Sequence. */
		this->_handle_private_escape( last );
		return; /* Don't drop to normal handlers */
	}

	std::ostringstream oss;
	oss << "Incoming mode: " << mode << " -- seqs follow: ";
	for ( unsigned int i = 0; i < seqs->size(); ++i )
		oss << seqs->at(i) << ", ";
	this->log(oss.str());

	switch ( mode ) {
		case CSI_CUU:
		case CSI_CUD:
		case CSI_CUF:
		case CSI_CUB:
			this->log( "CU[U|D|F|B Command issued." );
			/* Moves the cursor n (default 1) cells in the given direction. If
			 * the cursor is already at the edge of the screen, this has no
			 * effect. */
			move_steps = ( seqs->at(0) > 0 ) ? seqs->at(0) : 1;
			for ( int i = 0; i < move_steps; ++i ) {
				switch ( mode ) {
					case CSI_CUU: this->cY--; break;
					case CSI_CUD: this->cY++; break;
					case CSI_CUF: this->cX++; break;
					case CSI_CUB: this->cX--; break;
				}
			}
			this->cX = ( this->cX < this->width  )
				? this->cX : this->width;
			this->cY = ( this->cY < this->height )
				? this->cY : this->height;
			break;
		case CSI_CHA:
			this->log( "CHA Command issued" );
			/* Moves the cursor to column n. */
			this->cX = ( seqs->at(0) < 1 ) ? 0 : seqs->at(0) - 1;
			break;
		case 'P': // XXX: Fixme
			move_steps = ( seqs->at(0) > 0 ) ? seqs->at(0) : 1;
			for ( int j = 0; j < move_steps; ++j ) {
				for ( int i = this->cX; i < this->width - 1; ++i ) {
					int rootChar = GET_OFFSET( i, this->cY );
					int remoChar = GET_OFFSET( i + 1, this->cY );
					this->chars[rootChar].ch   = this->chars[remoChar].ch;
					this->chars[rootChar].attr = this->chars[remoChar].attr;
				}
				this->chars[GET_OFFSET( this->width - 1, this->cY )].ch = ' ';
				this->chars[GET_OFFSET( this->width - 1, this->cY )].attr =
					SHIBUYA_DEFAULT_CMODE;
			}
			break;
		case 'd': // XXX: Fixme
			this->log( "'d' command issued" );
			/* moves the cursor to row n. */
			this->cY = ( seqs->at(0) < 1 ) ? 0 : seqs->at(0) - 1;
			break;
		case 'M': // XXX: Fixme (CSI_DL)
			this->log( "'M' command issued" );
			/* DL | Delete the indicated # of lines. */
			move_steps = ( seqs->at(0) > 0 ) ? seqs->at(0) : 0;
			for ( int i = 0; i < move_steps; ++i )
				this->delete_line( this->cY );
			break;
		case 'L': // XXX: FIXME
			this->log( "'L' command issued" );
			move_steps = ( seqs->at(0) > 0 ) ? seqs->at(0) : 0;
			for ( int i = 0; i < move_steps; ++i )
				this->insert_line( this->cY );
			break;
		case 'm': // XXX: FIXME
			this->log( "Color command issued" );
			for ( unsigned int i = 0; i < seqs->size(); ++i ) {
				switch ( seqs->at(i) ) {
					case 0: /* Reset global attr */
						this->cMode = SHIBUYA_DEFAULT_CMODE;
						break;
					case 1: /* bold global attr */
						if ( SHIBUYA_ATTR_HAS_BOLD(this->cMode) == 0 )
							this->cMode += SHIBUYA_ATTR_BOLD;
						break;
					case 5:
					case 6: /* Blink */
						if ( SHIBUYA_ATTR_HAS_BLINK(this->cMode) == 0 )
							this->cMode += SHIBUYA_ATTR_BLINK;
						break;
					case 7: /* Invert */
						cTemp1 = (this->cMode & SHIBUYA_ATTR_FG_MASK);
						cTemp2 = (this->cMode & SHIBUYA_ATTR_BG_MASK);
						this->cMode -= ( cTemp1 + cTemp2 );
						cTemp1 = ( cTemp1 >> SHIBUYA_ATTR_FG_OFFSET )
							<< SHIBUYA_ATTR_BG_OFFSET;
						cTemp2 = ( cTemp2 >> SHIBUYA_ATTR_BG_OFFSET )
							<< SHIBUYA_ATTR_FG_OFFSET;
						this->cMode += ( cTemp1 + cTemp2 );
						break;
					case 22: /* Normal color or intensity neither bright, bold nor faint */
						if ( SHIBUYA_ATTR_HAS_BOLD(this->cMode) )
							this->cMode -= SHIBUYA_ATTR_BOLD;
						break;
					case 25: /* 25    Blink: off */
						if ( SHIBUYA_ATTR_HAS_BLINK(this->cMode) )
							this->cMode -= SHIBUYA_ATTR_BLINK;
						break;
					case 27: /* 27    Image: Positive */
					case 28: /* 28    Reveal conceal off */
						// XXX: FIXME
						this->cMode = SHIBUYA_DEFAULT_CMODE; // XXX: FIXME
						break;
					case 39: /* Default text color */
						this->cMode -= (this->cMode & SHIBUYA_ATTR_FG_MASK);
						this->cMode += (SHIBUYA_DEFAULT_CMODE & SHIBUYA_ATTR_FG_MASK);
						break;
					case 30: case 31: case 32:
					case 33: case 34: case 35:
					case 36: case 37: /* Foreground */
						cTemp1  = seqs->at(i);
						cTemp1 -= 30;
						this->cMode -= (this->cMode & SHIBUYA_ATTR_FG_MASK);
						this->cMode += ( cTemp1 << SHIBUYA_ATTR_FG_OFFSET );
						this->log("Set the foreground.");
						break;
					case 40: case 41: case 42:
					case 43: case 44: case 45:
					case 46: case 47: /* Background */
						cTemp1  = seqs->at(i);
						cTemp1 -= 40;
						this->cMode -= (this->cMode & SHIBUYA_ATTR_BG_MASK);
						this->cMode += (cTemp1 << SHIBUYA_ATTR_BG_OFFSET);
						this->log("Set the background.");
						break;
					case 49: /* Default background */
						this->cMode -= (this->cMode & SHIBUYA_ATTR_BG_MASK);
						this->cMode += (SHIBUYA_DEFAULT_CMODE & SHIBUYA_ATTR_BG_MASK);
						break;
					default: /* Unknown m sequence id */
						this->log("Unknown color things.");
						break;
				}
			}
			break;
		case 'r':
			this->log( "'r' command issued" );
			nTop = seqs->at(0);

			if ( nTop < 0 )
				nTop = 1;

			if ( seqs->size() >= 2 )
				nBottom = ( seqs->at(1) > 0 ) ? seqs->at(1) : this->height;
			else
				nBottom = this->height;

			nBottom = ( nBottom < 1 )         ? 0            : nBottom;
			nTop    = ( nTop > this->height ) ? this->height : nTop - 1;

			this->scroll_frame_bottom = nBottom;
			this->scroll_frame_top    = nTop;

			this->cX = 0;
			this->cY = nTop;

			break;
		case CSI_CUP:
			this->log( "CUP command issued" );
			/* Moves the cursor to row n, column m. The values are 1-based, and
			 * default to 1 (top left corner) if omitted. A sequence such as CSI
			 * ;5H is a synonym for CSI 1;5H as well as CSI 17;H is the same as
			 * CSI 17H and CSI 17;1H */
			nRow = seqs->at(0);

			if ( nRow < 0 )
				nRow = 1;

			if ( seqs->size() >= 2 )
				nCol = seqs->at(1);

			nRow = ( nRow < 1 ) ? 1 : nRow;
			nCol = ( nCol < 1 ) ? 1 : nCol;
			nRow = ( nRow > this->height ) ? this->height : nRow;
			nCol = ( nCol > this->width  ) ? this->width  : nCol;

			this->cX = (nCol - 1);
			this->cY = (nRow - 1);

			break;
		case CSI_EL:
			this->log( "EL command issued" );
			/* Erases part of the line. If n is zero (or missing), clear from
			 * cursor to the end of the line. If n is one, clear from cursor to
			 * beginning of the line. If n is two, clear entire line. Cursor
			 * position does not change. */
			switch ( seqs->at(0) ) {
				case -1:
				case 0:
					this->erase_to_from( this->cX, this->cY,
						this->width - 1, this->cY );
					break;
				case 1:
					this->erase_to_from( 0, this->cY, this->cX, this->cY );
					break;
				case 2:
					this->erase_to_from( 0, this->cY,
						this->width - 1, this->cY );
					break;
			}
			break;
		case CSI_ED:
			this->log( "ED command issued" );
			/* Clears part of the screen. If n is zero (or missing),
			 * clear from cursor to end of screen. If n is one,
			 * clear from cursor to beginning of the screen. If n is two, clear
			 * entire screen (and moves cursor to upper left on MS-DOS
			 * ANSI.SYS).*/
			switch ( seqs->at(0) ) {
				case -1:
				case  0:
					this->erase_to_from( this->cX, this->cY,
						this->width, this->height );
					break;
				case 1:
					this->erase_to_from( 0, 0, this->cX, this->cY );
					break;
				case 2:
					this->erase_to_from( 0, 0,
						this->width, this->height - 1 );
					break;
			}
			break;
		default:
			this->log( "Hit an unhandled command bit" );
			break;
	}

}

void ANSITerminal::insert( unsigned char c ) {
	ANSI_ESCAPE_PARSE_T res = ansi_escape_parser_feed( c );
	ansi_sequence * last = NULL;
	switch ( res ) {
		case ANSI_ESCAPE_PARSE_OK:
			last = ansi_escape_get_last_sequence();
			this->_handle_escape( last );
			break;
		case ANSI_ESCAPE_PARSE_BAD:
			ansi_escape_parser_reset();
			Terminal::insert(c);
			break;
		case ANSI_ESCAPE_PARSE_INCOMPLETE:
			break;
	}
}
<|endoftext|>