<commit_before>#include <cassert>
#include <iostream>
#include <map>

#include <pqxx/connection>
#include <pqxx/transaction>
#include <pqxx/transactor>
#include <pqxx/result>

using namespace PGSTD;
using namespace pqxx;

// Example program for libpqxx.  Modify the database, retaining transactional
// integrity using the transactor framework.
//
// Usage: test007 [connect-string]
//
// Where connect-string is a set of connection options in Postgresql's
// PQconnectdb() format, eg. "dbname=template1" to select from a database
// called template1, or "host=foo.bar.net user=smith" to connect to a
// backend running on host foo.bar.net, logging in as user smith.
//
// This assumes the existence of a database table "pqxxevents" containing a
// 2-digit "year" field, which is extended to a 4-digit format by assuming all
// year numbers of 70 or higher are in the 20th century, and all others in the
// 21st, and that no years before 1970 are possible.

namespace
{

// Global list of converted year numbers and what they've been converted to
map<int,int> theConversions;

// Convert year to 4-digit format.
int To4Digits(int Y)
{
  int Result = Y;

  if (Y < 0)          throw runtime_error("Negative year: " + to_string(Y));
  else if (Y  < 70)   Result += 2000;
  else if (Y  < 100)  Result += 1900;
  else if (Y  < 1970) throw runtime_error("Unexpected year: " + to_string(Y));

  return Result;
}

// Transaction definition for year-field update
class UpdateYears : public transactor<>
{
public:
  UpdateYears() : transactor<>("YearUpdate") {}

  // Transaction definition
  void operator()(argument_type &T)
  {
    // First select all different years occurring in the table.
    result R( T.exec("SELECT year FROM pqxxevents") );

    // SELECT affects no rows.
    assert(!R.affected_rows());

    // See if we get reasonable type identifier for this column
    const oid rctype = R.column_type(0);
    const string rct = to_string(rctype);
    if (rctype <= 0)
      throw logic_error("Got strange type ID for column: " + rct);
    const string rcol = R.column_name(0);
    if (rcol.empty())
      throw logic_error("Didn't get a name for column!");
    const oid rcctype = R.column_type(rcol);
    if (rcctype != rctype)
      throw logic_error("Column has type " + rct + ", "
	  		"but by name, it's " + to_string(rcctype));
    const oid rawrcctype = R.column_type(rcol.c_str());
    if (rawrcctype != rctype)
      throw logic_error("Column has type " + rct + ", "
	                "but by C-style name it's " + to_string(rawrcctype));

    // Note all different years currently occurring in the table, writing them
    // and their correct mappings to m_Conversions
    for (result::const_iterator r = R.begin(); r != R.end(); ++r)
    {
      int Y;

      // Read year, and if it is non-null, note its converted value
      if (r[0] >> Y) m_Conversions[Y] = To4Digits(Y);

      // See if type identifiers are consistent
      const oid tctype = r->column_type(0);
      if (tctype != rctype)
	throw logic_error("Column has type " + rct + ", "
	                  "but tuple says it's " + to_string(tctype));
      const oid ctctype = r->column_type(rcol);
      if (ctctype != rctype)
	throw logic_error("Column has type " + rct + ", "
	    		  "but by name, tuple says it's " + to_string(ctctype));
      const oid rawctctype = r->column_type(rcol.c_str());
      if (rawctctype != rctype)
	throw logic_error("Column has type " + rct + ", "
	    		  "but by C-style name, tuple says it's " +
			  to_string(rawctctype));
      const oid fctype = r[0].type();
      if (fctype != rctype)
	throw logic_error("Column has type " + rct + ", "
	    		  "but field says it's " + to_string(fctype));
    }

    result::size_type AffectedRows = 0;

    // For each occurring year, write converted date back to whereever it may
    // occur in the table.  Since we're in a transaction, any changes made by
    // others at the same time will not affect us.
    for (map<int,int>::const_iterator c = m_Conversions.begin();
	 c != m_Conversions.end();
	 ++c)
    {
      R = T.exec(("UPDATE pqxxevents "
	          "SET year=" + to_string(c->second) + " "
	          "WHERE year=" + to_string(c->first)).c_str());
      AffectedRows += R.affected_rows();
    }
    cout << AffectedRows << " rows updated." << endl;
  }

  // Postprocessing code for successful execution
  void OnCommit()
  {
    // Report the conversions performed once the transaction has completed
    // successfully.  Do not report conversions occurring in unsuccessful
    // attempts, as some of those may have been removed from the table by
    // somebody else between our attempts.
    // Even if this fails (eg. because we run out of memory), the actual
    // database transaction will still have been performed.
    theConversions = m_Conversions;
  }

  // Postprocessing code for aborted execution attempt
  void OnAbort(const char Reason[]) throw ()
  {
    try
    {
      // Notify user that the transaction attempt went wrong; we may retry.
      cerr << "Transaction interrupted: " << Reason << endl;
    }
    catch (const exception &)
    {
      // Ignore any exceptions on cerr.
    }
  }

private:
  // Local store of date conversions to be performed
  map<int,int> m_Conversions;
};

} // namespace

int main(int, char *argv[])
{
  try
  {
    connection C(argv[1]);
    C.set_client_encoding("SQL_ASCII");

    // Perform (an instantiation of) the UpdateYears transactor we've defined
    // in the code above.  This is where the work gets done.
    C.perform(UpdateYears());

    // Just for fun, report the exact conversions performed.  Note that this
    // list will be accurate even if other people were modifying the database
    // at the same time; this property was established through use of the
    // transactor framework.
    for (map<int,int>::const_iterator i = theConversions.begin();
	 i != theConversions.end();
	 ++i)
      cout << '\t' << i->first << "\t-> " << i->second << endl;
  }
  catch (const sql_error &e)
  {
    // If we're interested in the text of a failed query, we can write separate
    // exception handling code for this type of exception
    cerr << "SQL error: " << e.what() << endl
         << "Query was: '" << e.query() << "'" << endl;
    return 1;
  }
  catch (const exception &e)
  {
    // All exceptions thrown by libpqxx are derived from std::exception
    cerr << "Exception: " << e.what() << endl;
    return 2;
  }
  catch (...)
  {
    // This is really unexpected (see above)
    cerr << "Unhandled exception" << endl;
    return 100;
  }

  return 0;
}

<commit_msg>Test result::tuple::column_type() for both int and size_type<commit_after>#include <cassert>
#include <iostream>
#include <map>

#include <pqxx/connection>
#include <pqxx/transaction>
#include <pqxx/transactor>
#include <pqxx/result>

using namespace PGSTD;
using namespace pqxx;

// Example program for libpqxx.  Modify the database, retaining transactional
// integrity using the transactor framework.
//
// Usage: test007 [connect-string]
//
// Where connect-string is a set of connection options in Postgresql's
// PQconnectdb() format, eg. "dbname=template1" to select from a database
// called template1, or "host=foo.bar.net user=smith" to connect to a
// backend running on host foo.bar.net, logging in as user smith.
//
// This assumes the existence of a database table "pqxxevents" containing a
// 2-digit "year" field, which is extended to a 4-digit format by assuming all
// year numbers of 70 or higher are in the 20th century, and all others in the
// 21st, and that no years before 1970 are possible.

namespace
{

// Global list of converted year numbers and what they've been converted to
map<int,int> theConversions;

// Convert year to 4-digit format.
int To4Digits(int Y)
{
  int Result = Y;

  if (Y < 0)          throw runtime_error("Negative year: " + to_string(Y));
  else if (Y  < 70)   Result += 2000;
  else if (Y  < 100)  Result += 1900;
  else if (Y  < 1970) throw runtime_error("Unexpected year: " + to_string(Y));

  return Result;
}

// Transaction definition for year-field update
class UpdateYears : public transactor<>
{
public:
  UpdateYears() : transactor<>("YearUpdate") {}

  // Transaction definition
  void operator()(argument_type &T)
  {
    // First select all different years occurring in the table.
    result R( T.exec("SELECT year FROM pqxxevents") );

    // SELECT affects no rows.
    assert(!R.affected_rows());

    // See if we get reasonable type identifier for this column
    const oid rctype = R.column_type(0);
    if (rctype != R.column_type(result::tuple::size_type(0)))
      throw logic_error("Inconsistent result::column_type()");
    const string rct = to_string(rctype);
    if (rctype <= 0)
      throw logic_error("Got strange type ID for column: " + rct);
    const string rcol = R.column_name(0);
    if (rcol.empty())
      throw logic_error("Didn't get a name for column!");
    const oid rcctype = R.column_type(rcol);
    if (rcctype != rctype)
      throw logic_error("Column has type " + rct + ", "
	  		"but by name, it's " + to_string(rcctype));
    const oid rawrcctype = R.column_type(rcol.c_str());
    if (rawrcctype != rctype)
      throw logic_error("Column has type " + rct + ", "
	                "but by C-style name it's " + to_string(rawrcctype));

    // Note all different years currently occurring in the table, writing them
    // and their correct mappings to m_Conversions
    for (result::const_iterator r = R.begin(); r != R.end(); ++r)
    {
      int Y;

      // Read year, and if it is non-null, note its converted value
      if (r[0] >> Y) m_Conversions[Y] = To4Digits(Y);

      // See if type identifiers are consistent
      const oid tctype = r->column_type(0);
      if (tctype != r->column_type(result::tuple::size_type(0)))
	throw logic_error("Inconsistent result::tuple::column_type()");
      if (tctype != rctype)
	throw logic_error("Column has type " + rct + ", "
	                  "but tuple says it's " + to_string(tctype));
      const oid ctctype = r->column_type(rcol);
      if (ctctype != rctype)
	throw logic_error("Column has type " + rct + ", "
	    		  "but by name, tuple says it's " + to_string(ctctype));
      const oid rawctctype = r->column_type(rcol.c_str());
      if (rawctctype != rctype)
	throw logic_error("Column has type " + rct + ", "
	    		  "but by C-style name, tuple says it's " +
			  to_string(rawctctype));
      const oid fctype = r[0].type();
      if (fctype != rctype)
	throw logic_error("Column has type " + rct + ", "
	    		  "but field says it's " + to_string(fctype));
    }

    result::size_type AffectedRows = 0;

    // For each occurring year, write converted date back to whereever it may
    // occur in the table.  Since we're in a transaction, any changes made by
    // others at the same time will not affect us.
    for (map<int,int>::const_iterator c = m_Conversions.begin();
	 c != m_Conversions.end();
	 ++c)
    {
      R = T.exec(("UPDATE pqxxevents "
	          "SET year=" + to_string(c->second) + " "
	          "WHERE year=" + to_string(c->first)).c_str());
      AffectedRows += R.affected_rows();
    }
    cout << AffectedRows << " rows updated." << endl;
  }

  // Postprocessing code for successful execution
  void OnCommit()
  {
    // Report the conversions performed once the transaction has completed
    // successfully.  Do not report conversions occurring in unsuccessful
    // attempts, as some of those may have been removed from the table by
    // somebody else between our attempts.
    // Even if this fails (eg. because we run out of memory), the actual
    // database transaction will still have been performed.
    theConversions = m_Conversions;
  }

  // Postprocessing code for aborted execution attempt
  void OnAbort(const char Reason[]) throw ()
  {
    try
    {
      // Notify user that the transaction attempt went wrong; we may retry.
      cerr << "Transaction interrupted: " << Reason << endl;
    }
    catch (const exception &)
    {
      // Ignore any exceptions on cerr.
    }
  }

private:
  // Local store of date conversions to be performed
  map<int,int> m_Conversions;
};

} // namespace

int main(int, char *argv[])
{
  try
  {
    connection C(argv[1]);
    C.set_client_encoding("SQL_ASCII");

    // Perform (an instantiation of) the UpdateYears transactor we've defined
    // in the code above.  This is where the work gets done.
    C.perform(UpdateYears());

    // Just for fun, report the exact conversions performed.  Note that this
    // list will be accurate even if other people were modifying the database
    // at the same time; this property was established through use of the
    // transactor framework.
    for (map<int,int>::const_iterator i = theConversions.begin();
	 i != theConversions.end();
	 ++i)
      cout << '\t' << i->first << "\t-> " << i->second << endl;
  }
  catch (const sql_error &e)
  {
    // If we're interested in the text of a failed query, we can write separate
    // exception handling code for this type of exception
    cerr << "SQL error: " << e.what() << endl
         << "Query was: '" << e.query() << "'" << endl;
    return 1;
  }
  catch (const exception &e)
  {
    // All exceptions thrown by libpqxx are derived from std::exception
    cerr << "Exception: " << e.what() << endl;
    return 2;
  }
  catch (...)
  {
    // This is really unexpected (see above)
    cerr << "Unhandled exception" << endl;
    return 100;
  }

  return 0;
}

<|endoftext|>


<commit_before>#include <iostream>
#include <sstream>

#include <pqxx/all.h>
#include <pqxx/largeobject.h>

using namespace PGSTD;
using namespace pqxx;

namespace
{

/* Read contents of Stream into a single string.  The data will go through
 * input formatting, so whitespace will be taken as separators between chunks
 * of data.
 */
template<typename T> string UnStream(T &Stream)
{
  string Result, X;
  while (Stream >> X) Result += X;
  return Result;
}

class WriteLargeObject : public Transactor
{
public:
  WriteLargeObject(const string &Contents, LargeObject &O) :
    Transactor("WriteLargeObject"),
    m_Contents(Contents),
    m_Object(),
    m_ObjectOutput(O)
  {
  }

  void operator()(argument_type &T)
  {
    m_Object = LargeObject(T);
    cout << "Created large object #" << m_Object.id() << endl;

    largeobject_streambuf<> O(T, m_Object, ios::out);
    ostream OS(&O);
    OS << m_Contents << endl;
  }

  void OnCommit()
  {
    m_ObjectOutput = m_Object;
  }

private:
  string m_Contents;
  LargeObject m_Object;
  LargeObject &m_ObjectOutput;
};

class ReadLargeObject : public Transactor
{
public:
  ReadLargeObject(string &Contents, LargeObject O) :
    Transactor("ReadLargeObject"),
    m_Contents(),
    m_ContentsOutput(Contents),
    m_Object(O)
  {
  }

  void operator()(argument_type &T)
  {
    largeobject_streambuf<> I(T, m_Object.id(), ios::in);
    istream S(&I);
    m_Contents = UnStream(S);
  }

  void OnCommit()
  {
    m_ContentsOutput = m_Contents;
  }

private:
  string m_Contents;
  string &m_ContentsOutput;
  LargeObject m_Object;
};

class DeleteLargeObject : public Transactor
{
public:
  DeleteLargeObject(LargeObject O) : m_Object(O) {}

  void operator()(argument_type &T)
  {
    m_Object.remove(T);
  }

private:
  LargeObject m_Object;
};

}

// Simple test program for libpqxx's Large Objects interface.
//
// Usage: test48 [connect-string]
//
// Where connect-string is a set of connection options in Postgresql's
// PQconnectdb() format, eg. "dbname=template1" to select from a database
// called template1, or "host=foo.bar.net user=smith" to connect to a
// backend running on host foo.bar.net, logging in as user smith.
int main(int, char *argv[])
{
  try
  {
    Connection C(argv[1]);

    LargeObject Obj(InvalidOid);
    const string Contents = "Testing, testing, 1-2-3";

    C.Perform(WriteLargeObject(Contents, Obj));

    string Readback;		// Contents as read back from large object
    C.Perform(ReadLargeObject(Readback, Obj));

    C.Perform(DeleteLargeObject(Obj));

    /* Reconstruct what will happen to our contents string if we put it into a
     * stream and then read it back.  We can compare this with what comes back
     * from our large object stream.
     */
    stringstream TestStream;
    TestStream << Contents;
    const string StreamedContents = UnStream(TestStream);

    cout << StreamedContents << endl << Readback << endl;

    if (Readback != StreamedContents)
      throw logic_error("Large objects: expected to read "
	                "'" + StreamedContents + "', "
			"got '" + Readback + "'");
  }
  catch (const exception &e)
  {
    cerr << "Exception: " << e.what() << endl;
    return 2;
  }
  catch (...)
  {
    cerr << "Unhandled exception" << endl;
    return 100;
  }

  return 0;
}

<commit_msg>Uses lostreams<commit_after>#include <iostream>
#include <sstream>

#include <pqxx/all.h>
#include <pqxx/largeobject.h>

using namespace PGSTD;
using namespace pqxx;

namespace
{

/* Read contents of Stream into a single string.  The data will go through
 * input formatting, so whitespace will be taken as separators between chunks
 * of data.
 */
template<typename T> string UnStream(T &Stream)
{
  string Result, X;
  while (Stream >> X) Result += X;
  return Result;
}

class WriteLargeObject : public Transactor
{
public:
  WriteLargeObject(const string &Contents, LargeObject &O) :
    Transactor("WriteLargeObject"),
    m_Contents(Contents),
    m_Object(),
    m_ObjectOutput(O)
  {
  }

  void operator()(argument_type &T)
  {
    m_Object = LargeObject(T);
    cout << "Created large object #" << m_Object.id() << endl;

    olostream S(T, m_Object);
    S << m_Contents;
  }

  void OnCommit()
  {
    m_ObjectOutput = m_Object;
  }

private:
  string m_Contents;
  LargeObject m_Object;
  LargeObject &m_ObjectOutput;
};

class ReadLargeObject : public Transactor
{
public:
  ReadLargeObject(string &Contents, LargeObject O) :
    Transactor("ReadLargeObject"),
    m_Contents(),
    m_ContentsOutput(Contents),
    m_Object(O)
  {
  }

  void operator()(argument_type &T)
  {
    ilostream S(T, m_Object.id());
    m_Contents = UnStream(S);
  }

  void OnCommit()
  {
    m_ContentsOutput = m_Contents;
  }

private:
  string m_Contents;
  string &m_ContentsOutput;
  LargeObject m_Object;
};

class DeleteLargeObject : public Transactor
{
public:
  DeleteLargeObject(LargeObject O) : m_Object(O) {}

  void operator()(argument_type &T)
  {
    m_Object.remove(T);
  }

private:
  LargeObject m_Object;
};

}

// Simple test program for libpqxx's Large Objects interface.
//
// Usage: test48 [connect-string]
//
// Where connect-string is a set of connection options in Postgresql's
// PQconnectdb() format, eg. "dbname=template1" to select from a database
// called template1, or "host=foo.bar.net user=smith" to connect to a
// backend running on host foo.bar.net, logging in as user smith.
int main(int, char *argv[])
{
  try
  {
    Connection C(argv[1]);

    LargeObject Obj(InvalidOid);
    const string Contents = "Testing, testing, 1-2-3";

    C.Perform(WriteLargeObject(Contents, Obj));

    string Readback;		// Contents as read back from large object
    C.Perform(ReadLargeObject(Readback, Obj));

    C.Perform(DeleteLargeObject(Obj));

    /* Reconstruct what will happen to our contents string if we put it into a
     * stream and then read it back.  We can compare this with what comes back
     * from our large object stream.
     */
    stringstream TestStream;
    TestStream << Contents;
    const string StreamedContents = UnStream(TestStream);

    cout << StreamedContents << endl << Readback << endl;

    if (Readback != StreamedContents)
      throw logic_error("Large objects: expected to read "
	                "'" + StreamedContents + "', "
			"got '" + Readback + "'");
  }
  catch (const exception &e)
  {
    cerr << "Exception: " << e.what() << endl;
    return 2;
  }
  catch (...)
  {
    cerr << "Unhandled exception" << endl;
    return 100;
  }

  return 0;
}

<|endoftext|>


<commit_before>#include "NodeSearch.h"
#include "NodeTrack.h"
#include "NodeAlbum.h"
#include "NodeArtist.h"
#include "NodePlaylist.h"
#include "../../Application.h"
#include "../../common_macros.h"

extern Application* application;

NodeSearch::NodeSearch(const char* _searchQuery) : searchQuery(_searchQuery), trackOffset(0), albumOffset(0), artistOffset(0), playlistOffset(0),
  trackLimit(10), albumLimit(10), artistLimit(10), playlistLimit(10) {

}

NodeSearch::NodeSearch(const char* _searchQuery, int offset) : searchQuery(_searchQuery), trackOffset(offset), albumOffset(offset), artistOffset(offset), playlistOffset(offset),
  trackLimit(10), albumLimit(10), artistLimit(10), playlistLimit(10) {

}

NodeSearch::NodeSearch(const char* _searchQuery, int offset, int limit) : searchQuery(_searchQuery), trackOffset(offset), albumOffset(offset), artistOffset(offset), playlistOffset(offset),
  trackLimit(limit), albumLimit(limit), artistLimit(limit), playlistLimit(limit) {

}

NAN_METHOD(NodeSearch::execute) {
  NanScope();
  if(args.Length() < 1) {//TODO: how to check if it is a function? ->IsFunction() does not work, it does not recoginze functions.
    NanThrowError("execute needs a callback function as its argument.");
  }
  NodeSearch* nodeSearch = node::ObjectWrap::Unwrap<NodeSearch>(args.This());
  nodeSearch->makePersistent();
  nodeSearch->browseCompleteCallback = std::unique_ptr<NanCallback>(new NanCallback(args[0].As<Function>()));
  nodeSearch->search = std::unique_ptr<Search>(new Search());
  nodeSearch->search->nodeObject = nodeSearch;
  nodeSearch->search->execute(nodeSearch->searchQuery, nodeSearch->trackOffset, nodeSearch->trackLimit,
    nodeSearch->albumOffset, nodeSearch->albumLimit,
    nodeSearch->artistOffset, nodeSearch->artistLimit,
    nodeSearch->playlistLimit, nodeSearch->playlistLimit);
  nodeSearch->setupAdditionalMethods();
  NanReturnUndefined();
}

/**
 * Adds adiitional properties to the V8 object.
 * These will call libspotify functions and should first be available when the search has been executed.
 **/
void NodeSearch::setupAdditionalMethods() {
  Handle<Object> nodeObject = this->getV8Object();
  nodeObject->SetAccessor(NanNew<String>("didYouMean"), didYouMean);
  nodeObject->SetAccessor(NanNew<String>("link"), getLink);
  nodeObject->Set(NanNew<String>("getTrack"), NanNew<FunctionTemplate>(getTrack)->GetFunction());
  nodeObject->Set(NanNew<String>("getAlbum"), NanNew<FunctionTemplate>(getAlbum)->GetFunction());
  nodeObject->Set(NanNew<String>("getArtist"), NanNew<FunctionTemplate>(getArtist)->GetFunction());
  nodeObject->Set(NanNew<String>("getPlaylist"), NanNew<FunctionTemplate>(getPlaylist)->GetFunction());
  nodeObject->SetAccessor(NanNew<String>("totalTracks"), getTotalTracks);
  nodeObject->SetAccessor(NanNew<String>("numTracks"), getNumTracks);
  nodeObject->SetAccessor(NanNew<String>("totalAlbums"), getTotalAlbums);
  nodeObject->SetAccessor(NanNew<String>("numAlbums"), getNumAlbums);
  nodeObject->SetAccessor(NanNew<String>("totalArtists"), getTotalArtists);
  nodeObject->SetAccessor(NanNew<String>("numArtists"), getNumArtists);
  nodeObject->SetAccessor(NanNew<String>("totalPlaylists"), getTotalPlaylists);
  nodeObject->SetAccessor(NanNew<String>("numPlaylists"), getNumPlaylists);
}

NAN_GETTER(NodeSearch::getTrackOffset) {
  NanScope();
  NodeSearch* nodeSearch = node::ObjectWrap::Unwrap<NodeSearch>(args.This());
  NanReturnValue(NanNew<Integer>(nodeSearch->trackOffset));
}

NAN_SETTER(NodeSearch::setTrackOffset) {
  NanScope();
  NodeSearch* nodeSearch = node::ObjectWrap::Unwrap<NodeSearch>(args.This());
  nodeSearch->trackOffset = value->ToInteger()->Value();
  NanReturnUndefined();
}

NAN_GETTER(NodeSearch::getAlbumOffset) {
  NanScope();
  NodeSearch* nodeSearch = node::ObjectWrap::Unwrap<NodeSearch>(args.This());
  NanReturnValue(NanNew<Integer>(nodeSearch->albumOffset));
}

NAN_SETTER(NodeSearch::setAlbumOffset) {
  NanScope();
  NodeSearch* nodeSearch = node::ObjectWrap::Unwrap<NodeSearch>(args.This());
  nodeSearch->albumOffset = value->ToInteger()->Value();
  NanReturnUndefined();
}

NAN_GETTER(NodeSearch::getArtistOffset) {
  NanScope();
  NodeSearch* nodeSearch = node::ObjectWrap::Unwrap<NodeSearch>(args.This());
  NanReturnValue(NanNew<Integer>(nodeSearch->artistOffset));
}

NAN_SETTER(NodeSearch::setArtistOffset) {
  NanScope();
  NodeSearch* nodeSearch = node::ObjectWrap::Unwrap<NodeSearch>(args.This());
  nodeSearch->artistOffset = value->ToInteger()->Value();
  NanReturnUndefined();
}

NAN_GETTER(NodeSearch::getPlaylistOffset) {
  NanScope();
  NodeSearch* nodeSearch = node::ObjectWrap::Unwrap<NodeSearch>(args.This());
  NanReturnValue(NanNew<Integer>(nodeSearch->playlistOffset));
}

NAN_SETTER(NodeSearch::setPlaylistOffset) {
  NanScope();
  NodeSearch* nodeSearch = node::ObjectWrap::Unwrap<NodeSearch>(args.This());
  nodeSearch->playlistOffset = value->ToInteger()->Value();
  NanReturnUndefined();
}

NAN_GETTER(NodeSearch::getTrackLimit) {
  NanScope();
  NodeSearch* nodeSearch = node::ObjectWrap::Unwrap<NodeSearch>(args.This());
  NanReturnValue(NanNew<Integer>(nodeSearch->trackLimit));
}

NAN_SETTER(NodeSearch::setTrackLimit) {
  NanScope();
  NodeSearch* nodeSearch = node::ObjectWrap::Unwrap<NodeSearch>(args.This());
  nodeSearch->trackLimit = value->ToInteger()->Value();
  NanReturnUndefined();
}

NAN_GETTER(NodeSearch::getAlbumLimit) {
  NanScope();
  NodeSearch* nodeSearch = node::ObjectWrap::Unwrap<NodeSearch>(args.This());
  NanReturnValue(NanNew<Integer>(nodeSearch->albumLimit));
}

NAN_SETTER(NodeSearch::setAlbumLimit) {
  NanScope();
  NodeSearch* nodeSearch = node::ObjectWrap::Unwrap<NodeSearch>(args.This());
  nodeSearch->albumLimit = value->ToInteger()->Value();
  NanReturnUndefined();
}

NAN_GETTER(NodeSearch::getArtistLimit) {
  NanScope();
  NodeSearch* nodeSearch = node::ObjectWrap::Unwrap<NodeSearch>(args.This());
  NanReturnValue(NanNew<Integer>(nodeSearch->artistLimit));
}

NAN_SETTER(NodeSearch::setArtistLimit) {
  NanScope();
  NodeSearch* nodeSearch = node::ObjectWrap::Unwrap<NodeSearch>(args.This());
  nodeSearch->artistLimit = value->ToInteger()->Value();
  NanReturnUndefined();
}

NAN_GETTER(NodeSearch::getPlaylistLimit) {
  NanScope();
  NodeSearch* nodeSearch = node::ObjectWrap::Unwrap<NodeSearch>(args.This());
  NanReturnValue(NanNew<Integer>(nodeSearch->playlistLimit));
}

NAN_SETTER(NodeSearch::setPlaylistLimit) {
  NanScope();
  NodeSearch* nodeSearch = node::ObjectWrap::Unwrap<NodeSearch>(args.This());
  nodeSearch->playlistLimit = value->ToInteger()->Value();
  NanReturnUndefined();
}

NAN_METHOD(NodeSearch::New) {
  NanScope();
  NodeSearch* search;
  NanUtf8String searchQuery(args[0]->ToString());
  if(args.Length() == 1) {
    search = new NodeSearch(*searchQuery);
  } else if(args.Length() == 2) {
    int offset = args[1]->ToInteger()->Value();
    search = new NodeSearch(*searchQuery, offset);
  } else if(args.Length() == 3) {
    int offset = args[1]->ToInteger()->Value();
    int limit = args[2]->ToInteger()->Value();
    search = new NodeSearch(*searchQuery, offset, limit);
  } else {
    NanThrowError("Please provide an object to the node-spotify initializer function");
    return; //add a custom return so the compiler does not complain about search being unitialized.
  }
  search->Wrap(args.This());
  NanReturnThis();
}

NAN_GETTER(NodeSearch::didYouMean) {
  NanScope();
  NodeSearch* nodeSearch = node::ObjectWrap::Unwrap<NodeSearch>(args.This());
  NanReturnValue(NanNew<String>(nodeSearch->search->didYouMeanText().c_str()));
}

NAN_GETTER(NodeSearch::getLink) {
  NanScope();
  NodeSearch* nodeSearch = node::ObjectWrap::Unwrap<NodeSearch>(args.This());
  NanReturnValue(NanNew<String>(nodeSearch->search->link().c_str()));
}

NAN_METHOD(NodeSearch::getTrack) {
  NanScope();
  NodeSearch* nodeSearch = node::ObjectWrap::Unwrap<NodeSearch>(args.This());
  if(args.Length() < 1 || !args[0]->IsNumber()) {
    NanThrowError("getTrack needs a number as its first argument.");
  }

  int position = args[0]->ToNumber()->IntegerValue();
  if(position >= nodeSearch->search->numTracks() || position < 0) {
    NanThrowError("Track index out of bounds");
  }

  NodeTrack* nodeTrack = new NodeTrack(nodeSearch->search->getTrack(position));
  NanReturnValue(nodeTrack->createInstance());
}

NAN_METHOD(NodeSearch::getAlbum) {
  NanScope();
  NodeSearch* nodeSearch = node::ObjectWrap::Unwrap<NodeSearch>(args.This());
  if(args.Length() < 1 || !args[0]->IsNumber()) {
    NanThrowError("getAlbum needs a number as its first argument.");
  }

  int position = args[0]->ToNumber()->IntegerValue();
  if(position >= nodeSearch->search->numAlbums() || position < 0) {
    NanThrowError("Album index out of bounds");
  }

  NodeAlbum* nodeAlbum = new NodeAlbum(nodeSearch->search->getAlbum(position));
  NanReturnValue(nodeAlbum->createInstance());
}

NAN_METHOD(NodeSearch::getArtist) {
  NanScope();
  NodeSearch* nodeSearch = node::ObjectWrap::Unwrap<NodeSearch>(args.This());
  if(args.Length() < 1 || !args[0]->IsNumber()) {
    NanThrowError("getArtist needs a number as its first argument.");
  }

  int position = args[0]->ToNumber()->IntegerValue();
  if(position >= nodeSearch->search->numArtists() || position < 0) {
    NanThrowError("Artist index out of bounds");
  }

  NodeArtist* nodeArtist = new NodeArtist(nodeSearch->search->getArtist(position));
  NanReturnValue(nodeArtist->createInstance());
}

NAN_METHOD(NodeSearch::getPlaylist) {
  NanScope();
  NodeSearch* nodeSearch = node::ObjectWrap::Unwrap<NodeSearch>(args.This());
  if(args.Length() < 1 || !args[0]->IsNumber()) {
    NanThrowError("getPlaylist needs a number as its first argument.");
  }

  int position = args[0]->ToNumber()->IntegerValue();
  if(position >= nodeSearch->search->numPlaylists() || position < 0) {
    NanThrowError("Playlist index out of bounds");
  }

  NodePlaylist* nodePlaylist = new NodePlaylist(nodeSearch->search->getPlaylist(position));
  NanReturnValue(nodePlaylist->createInstance());
}

NAN_GETTER(NodeSearch::getTotalTracks) {
  NanScope();
  NodeSearch* nodeSearch = node::ObjectWrap::Unwrap<NodeSearch>(args.This());
  NanReturnValue(NanNew<Integer>(nodeSearch->search->totalTracks()));
}

NAN_GETTER(NodeSearch::getNumTracks) {
  NanScope();
  NodeSearch* nodeSearch = node::ObjectWrap::Unwrap<NodeSearch>(args.This());
  NanReturnValue(NanNew<Integer>(nodeSearch->search->numTracks()));
}

NAN_GETTER(NodeSearch::getTotalAlbums) {
  NanScope();
  NodeSearch* nodeSearch = node::ObjectWrap::Unwrap<NodeSearch>(args.This());
  NanReturnValue(NanNew<Integer>(nodeSearch->search->totalAlbums()));
}

NAN_GETTER(NodeSearch::getNumAlbums) {
  NanScope();
  NodeSearch* nodeSearch = node::ObjectWrap::Unwrap<NodeSearch>(args.This());
  NanReturnValue(NanNew<Integer>(nodeSearch->search->numAlbums()));
}

NAN_GETTER(NodeSearch::getTotalArtists) {
  NanScope();
  NodeSearch* nodeSearch = node::ObjectWrap::Unwrap<NodeSearch>(args.This());
  NanReturnValue(NanNew<Integer>(nodeSearch->search->totalArtists()));
}

NAN_GETTER(NodeSearch::getNumArtists) {
  NanScope();
  NodeSearch* nodeSearch = node::ObjectWrap::Unwrap<NodeSearch>(args.This());
  NanReturnValue(NanNew<Integer>(nodeSearch->search->numArtists()));
}

NAN_GETTER(NodeSearch::getTotalPlaylists) {
  NanScope();
  NodeSearch* nodeSearch = node::ObjectWrap::Unwrap<NodeSearch>(args.This());
  NanReturnValue(NanNew<Integer>(nodeSearch->search->totalPlaylists()));
}

NAN_GETTER(NodeSearch::getNumPlaylists) {
  NanScope();
  NodeSearch* nodeSearch = node::ObjectWrap::Unwrap<NodeSearch>(args.This());
  NanReturnValue(NanNew<Integer>(nodeSearch->search->numPlaylists()));
}

void NodeSearch::init() {
  NanScope();
  Local<FunctionTemplate> constructorTemplate = NanNew<FunctionTemplate>(New);
  constructorTemplate->SetClassName(NanNew<String>("Search"));
  constructorTemplate->InstanceTemplate()->SetInternalFieldCount(1);
  NODE_SET_PROTOTYPE_METHOD(constructorTemplate, "execute", execute);
  constructorTemplate->InstanceTemplate()->SetAccessor(NanNew<String>("trackOffset"), getTrackOffset, setTrackOffset);
  constructorTemplate->InstanceTemplate()->SetAccessor(NanNew<String>("trackLimit"), getTrackLimit, setTrackLimit);
  constructorTemplate->InstanceTemplate()->SetAccessor(NanNew<String>("albumOffset"), getAlbumOffset, setAlbumOffset);
  constructorTemplate->InstanceTemplate()->SetAccessor(NanNew<String>("albumLimit"), getAlbumLimit, setAlbumLimit);
  constructorTemplate->InstanceTemplate()->SetAccessor(NanNew<String>("artistOffset"), getArtistOffset, setArtistOffset);
  constructorTemplate->InstanceTemplate()->SetAccessor(NanNew<String>("artistLimit"), getArtistLimit, setArtistLimit);
  constructorTemplate->InstanceTemplate()->SetAccessor(NanNew<String>("playlistOffset"), getPlaylistOffset, setPlaylistOffset);
  constructorTemplate->InstanceTemplate()->SetAccessor(NanNew<String>("playlistLimit"), getPlaylistLimit, setPlaylistLimit);
  NanAssignPersistent(NodeSearch::constructorTemplate, constructorTemplate);
}
<commit_msg>Fixing #81<commit_after>#include "NodeSearch.h"
#include "NodeTrack.h"
#include "NodeAlbum.h"
#include "NodeArtist.h"
#include "NodePlaylist.h"
#include "../../Application.h"
#include "../../common_macros.h"

extern Application* application;

NodeSearch::NodeSearch(const char* _searchQuery) : searchQuery(_searchQuery), trackOffset(0), albumOffset(0), artistOffset(0), playlistOffset(0),
  trackLimit(10), albumLimit(10), artistLimit(10), playlistLimit(10) {

}

NodeSearch::NodeSearch(const char* _searchQuery, int offset) : searchQuery(_searchQuery), trackOffset(offset), albumOffset(offset), artistOffset(offset), playlistOffset(offset),
  trackLimit(10), albumLimit(10), artistLimit(10), playlistLimit(10) {

}

NodeSearch::NodeSearch(const char* _searchQuery, int offset, int limit) : searchQuery(_searchQuery), trackOffset(offset), albumOffset(offset), artistOffset(offset), playlistOffset(offset),
  trackLimit(limit), albumLimit(limit), artistLimit(limit), playlistLimit(limit) {

}

NAN_METHOD(NodeSearch::execute) {
  NanScope();
  if(args.Length() < 1) {//TODO: how to check if it is a function? ->IsFunction() does not work, it does not recoginze functions.
    NanThrowError("execute needs a callback function as its argument.");
  }
  NodeSearch* nodeSearch = node::ObjectWrap::Unwrap<NodeSearch>(args.This());
  nodeSearch->makePersistent();
  nodeSearch->browseCompleteCallback = std::unique_ptr<NanCallback>(new NanCallback(args[0].As<Function>()));
  nodeSearch->search = std::unique_ptr<Search>(new Search());
  nodeSearch->search->nodeObject = nodeSearch;
  nodeSearch->search->execute(nodeSearch->searchQuery, nodeSearch->trackOffset, nodeSearch->trackLimit,
    nodeSearch->albumOffset, nodeSearch->albumLimit,
    nodeSearch->artistOffset, nodeSearch->artistLimit,
    nodeSearch->playlistLimit, nodeSearch->playlistLimit);
  nodeSearch->setupAdditionalMethods();
  NanReturnUndefined();
}

/**
 * Adds adiitional properties to the V8 object.
 * These will call libspotify functions and should first be available when the search has been executed.
 **/
void NodeSearch::setupAdditionalMethods() {
  Handle<Object> nodeObject = this->getV8Object();
  nodeObject->SetAccessor(NanNew<String>("didYouMean"), didYouMean);
  nodeObject->SetAccessor(NanNew<String>("link"), getLink);
  nodeObject->Set(NanNew<String>("getTrack"), NanNew<FunctionTemplate>(getTrack)->GetFunction());
  nodeObject->Set(NanNew<String>("getAlbum"), NanNew<FunctionTemplate>(getAlbum)->GetFunction());
  nodeObject->Set(NanNew<String>("getArtist"), NanNew<FunctionTemplate>(getArtist)->GetFunction());
  nodeObject->Set(NanNew<String>("getPlaylist"), NanNew<FunctionTemplate>(getPlaylist)->GetFunction());
  nodeObject->SetAccessor(NanNew<String>("totalTracks"), getTotalTracks);
  nodeObject->SetAccessor(NanNew<String>("numTracks"), getNumTracks);
  nodeObject->SetAccessor(NanNew<String>("totalAlbums"), getTotalAlbums);
  nodeObject->SetAccessor(NanNew<String>("numAlbums"), getNumAlbums);
  nodeObject->SetAccessor(NanNew<String>("totalArtists"), getTotalArtists);
  nodeObject->SetAccessor(NanNew<String>("numArtists"), getNumArtists);
  nodeObject->SetAccessor(NanNew<String>("totalPlaylists"), getTotalPlaylists);
  nodeObject->SetAccessor(NanNew<String>("numPlaylists"), getNumPlaylists);
}

NAN_GETTER(NodeSearch::getTrackOffset) {
  NanScope();
  NodeSearch* nodeSearch = node::ObjectWrap::Unwrap<NodeSearch>(args.This());
  NanReturnValue(NanNew<Integer>(nodeSearch->trackOffset));
}

NAN_SETTER(NodeSearch::setTrackOffset) {
  NanScope();
  NodeSearch* nodeSearch = node::ObjectWrap::Unwrap<NodeSearch>(args.This());
  nodeSearch->trackOffset = value->ToInteger()->Value();
}

NAN_GETTER(NodeSearch::getAlbumOffset) {
  NanScope();
  NodeSearch* nodeSearch = node::ObjectWrap::Unwrap<NodeSearch>(args.This());
  NanReturnValue(NanNew<Integer>(nodeSearch->albumOffset));
}

NAN_SETTER(NodeSearch::setAlbumOffset) {
  NanScope();
  NodeSearch* nodeSearch = node::ObjectWrap::Unwrap<NodeSearch>(args.This());
  nodeSearch->albumOffset = value->ToInteger()->Value();
}

NAN_GETTER(NodeSearch::getArtistOffset) {
  NanScope();
  NodeSearch* nodeSearch = node::ObjectWrap::Unwrap<NodeSearch>(args.This());
  NanReturnValue(NanNew<Integer>(nodeSearch->artistOffset));
}

NAN_SETTER(NodeSearch::setArtistOffset) {
  NanScope();
  NodeSearch* nodeSearch = node::ObjectWrap::Unwrap<NodeSearch>(args.This());
  nodeSearch->artistOffset = value->ToInteger()->Value();
}

NAN_GETTER(NodeSearch::getPlaylistOffset) {
  NanScope();
  NodeSearch* nodeSearch = node::ObjectWrap::Unwrap<NodeSearch>(args.This());
  NanReturnValue(NanNew<Integer>(nodeSearch->playlistOffset));
}

NAN_SETTER(NodeSearch::setPlaylistOffset) {
  NanScope();
  NodeSearch* nodeSearch = node::ObjectWrap::Unwrap<NodeSearch>(args.This());
  nodeSearch->playlistOffset = value->ToInteger()->Value();
}

NAN_GETTER(NodeSearch::getTrackLimit) {
  NanScope();
  NodeSearch* nodeSearch = node::ObjectWrap::Unwrap<NodeSearch>(args.This());
  NanReturnValue(NanNew<Integer>(nodeSearch->trackLimit));
}

NAN_SETTER(NodeSearch::setTrackLimit) {
  NanScope();
  NodeSearch* nodeSearch = node::ObjectWrap::Unwrap<NodeSearch>(args.This());
  nodeSearch->trackLimit = value->ToInteger()->Value();
}

NAN_GETTER(NodeSearch::getAlbumLimit) {
  NanScope();
  NodeSearch* nodeSearch = node::ObjectWrap::Unwrap<NodeSearch>(args.This());
  NanReturnValue(NanNew<Integer>(nodeSearch->albumLimit));
}

NAN_SETTER(NodeSearch::setAlbumLimit) {
  NanScope();
  NodeSearch* nodeSearch = node::ObjectWrap::Unwrap<NodeSearch>(args.This());
  nodeSearch->albumLimit = value->ToInteger()->Value();
}

NAN_GETTER(NodeSearch::getArtistLimit) {
  NanScope();
  NodeSearch* nodeSearch = node::ObjectWrap::Unwrap<NodeSearch>(args.This());
  NanReturnValue(NanNew<Integer>(nodeSearch->artistLimit));
}

NAN_SETTER(NodeSearch::setArtistLimit) {
  NanScope();
  NodeSearch* nodeSearch = node::ObjectWrap::Unwrap<NodeSearch>(args.This());
  nodeSearch->artistLimit = value->ToInteger()->Value();
}

NAN_GETTER(NodeSearch::getPlaylistLimit) {
  NanScope();
  NodeSearch* nodeSearch = node::ObjectWrap::Unwrap<NodeSearch>(args.This());
  NanReturnValue(NanNew<Integer>(nodeSearch->playlistLimit));
}

NAN_SETTER(NodeSearch::setPlaylistLimit) {
  NanScope();
  NodeSearch* nodeSearch = node::ObjectWrap::Unwrap<NodeSearch>(args.This());
  nodeSearch->playlistLimit = value->ToInteger()->Value();
}

NAN_METHOD(NodeSearch::New) {
  NanScope();
  NodeSearch* search;
  NanUtf8String searchQuery(args[0]->ToString());
  if(args.Length() == 1) {
    search = new NodeSearch(*searchQuery);
  } else if(args.Length() == 2) {
    int offset = args[1]->ToInteger()->Value();
    search = new NodeSearch(*searchQuery, offset);
  } else if(args.Length() == 3) {
    int offset = args[1]->ToInteger()->Value();
    int limit = args[2]->ToInteger()->Value();
    search = new NodeSearch(*searchQuery, offset, limit);
  } else {
    NanThrowError("Please provide an object to the node-spotify initializer function");
    return; //add a custom return so the compiler does not complain about search being unitialized.
  }
  search->Wrap(args.This());
  NanReturnThis();
}

NAN_GETTER(NodeSearch::didYouMean) {
  NanScope();
  NodeSearch* nodeSearch = node::ObjectWrap::Unwrap<NodeSearch>(args.This());
  NanReturnValue(NanNew<String>(nodeSearch->search->didYouMeanText().c_str()));
}

NAN_GETTER(NodeSearch::getLink) {
  NanScope();
  NodeSearch* nodeSearch = node::ObjectWrap::Unwrap<NodeSearch>(args.This());
  NanReturnValue(NanNew<String>(nodeSearch->search->link().c_str()));
}

NAN_METHOD(NodeSearch::getTrack) {
  NanScope();
  NodeSearch* nodeSearch = node::ObjectWrap::Unwrap<NodeSearch>(args.This());
  if(args.Length() < 1 || !args[0]->IsNumber()) {
    NanThrowError("getTrack needs a number as its first argument.");
  }

  int position = args[0]->ToNumber()->IntegerValue();
  if(position >= nodeSearch->search->numTracks() || position < 0) {
    NanThrowError("Track index out of bounds");
  }

  NodeTrack* nodeTrack = new NodeTrack(nodeSearch->search->getTrack(position));
  NanReturnValue(nodeTrack->createInstance());
}

NAN_METHOD(NodeSearch::getAlbum) {
  NanScope();
  NodeSearch* nodeSearch = node::ObjectWrap::Unwrap<NodeSearch>(args.This());
  if(args.Length() < 1 || !args[0]->IsNumber()) {
    NanThrowError("getAlbum needs a number as its first argument.");
  }

  int position = args[0]->ToNumber()->IntegerValue();
  if(position >= nodeSearch->search->numAlbums() || position < 0) {
    NanThrowError("Album index out of bounds");
  }

  NodeAlbum* nodeAlbum = new NodeAlbum(nodeSearch->search->getAlbum(position));
  NanReturnValue(nodeAlbum->createInstance());
}

NAN_METHOD(NodeSearch::getArtist) {
  NanScope();
  NodeSearch* nodeSearch = node::ObjectWrap::Unwrap<NodeSearch>(args.This());
  if(args.Length() < 1 || !args[0]->IsNumber()) {
    NanThrowError("getArtist needs a number as its first argument.");
  }

  int position = args[0]->ToNumber()->IntegerValue();
  if(position >= nodeSearch->search->numArtists() || position < 0) {
    NanThrowError("Artist index out of bounds");
  }

  NodeArtist* nodeArtist = new NodeArtist(nodeSearch->search->getArtist(position));
  NanReturnValue(nodeArtist->createInstance());
}

NAN_METHOD(NodeSearch::getPlaylist) {
  NanScope();
  NodeSearch* nodeSearch = node::ObjectWrap::Unwrap<NodeSearch>(args.This());
  if(args.Length() < 1 || !args[0]->IsNumber()) {
    NanThrowError("getPlaylist needs a number as its first argument.");
  }

  int position = args[0]->ToNumber()->IntegerValue();
  if(position >= nodeSearch->search->numPlaylists() || position < 0) {
    NanThrowError("Playlist index out of bounds");
  }

  NodePlaylist* nodePlaylist = new NodePlaylist(nodeSearch->search->getPlaylist(position));
  NanReturnValue(nodePlaylist->createInstance());
}

NAN_GETTER(NodeSearch::getTotalTracks) {
  NanScope();
  NodeSearch* nodeSearch = node::ObjectWrap::Unwrap<NodeSearch>(args.This());
  NanReturnValue(NanNew<Integer>(nodeSearch->search->totalTracks()));
}

NAN_GETTER(NodeSearch::getNumTracks) {
  NanScope();
  NodeSearch* nodeSearch = node::ObjectWrap::Unwrap<NodeSearch>(args.This());
  NanReturnValue(NanNew<Integer>(nodeSearch->search->numTracks()));
}

NAN_GETTER(NodeSearch::getTotalAlbums) {
  NanScope();
  NodeSearch* nodeSearch = node::ObjectWrap::Unwrap<NodeSearch>(args.This());
  NanReturnValue(NanNew<Integer>(nodeSearch->search->totalAlbums()));
}

NAN_GETTER(NodeSearch::getNumAlbums) {
  NanScope();
  NodeSearch* nodeSearch = node::ObjectWrap::Unwrap<NodeSearch>(args.This());
  NanReturnValue(NanNew<Integer>(nodeSearch->search->numAlbums()));
}

NAN_GETTER(NodeSearch::getTotalArtists) {
  NanScope();
  NodeSearch* nodeSearch = node::ObjectWrap::Unwrap<NodeSearch>(args.This());
  NanReturnValue(NanNew<Integer>(nodeSearch->search->totalArtists()));
}

NAN_GETTER(NodeSearch::getNumArtists) {
  NanScope();
  NodeSearch* nodeSearch = node::ObjectWrap::Unwrap<NodeSearch>(args.This());
  NanReturnValue(NanNew<Integer>(nodeSearch->search->numArtists()));
}

NAN_GETTER(NodeSearch::getTotalPlaylists) {
  NanScope();
  NodeSearch* nodeSearch = node::ObjectWrap::Unwrap<NodeSearch>(args.This());
  NanReturnValue(NanNew<Integer>(nodeSearch->search->totalPlaylists()));
}

NAN_GETTER(NodeSearch::getNumPlaylists) {
  NanScope();
  NodeSearch* nodeSearch = node::ObjectWrap::Unwrap<NodeSearch>(args.This());
  NanReturnValue(NanNew<Integer>(nodeSearch->search->numPlaylists()));
}

void NodeSearch::init() {
  NanScope();
  Local<FunctionTemplate> constructorTemplate = NanNew<FunctionTemplate>(New);
  constructorTemplate->SetClassName(NanNew<String>("Search"));
  constructorTemplate->InstanceTemplate()->SetInternalFieldCount(1);
  NODE_SET_PROTOTYPE_METHOD(constructorTemplate, "execute", execute);
  constructorTemplate->InstanceTemplate()->SetAccessor(NanNew<String>("trackOffset"), getTrackOffset, setTrackOffset);
  constructorTemplate->InstanceTemplate()->SetAccessor(NanNew<String>("trackLimit"), getTrackLimit, setTrackLimit);
  constructorTemplate->InstanceTemplate()->SetAccessor(NanNew<String>("albumOffset"), getAlbumOffset, setAlbumOffset);
  constructorTemplate->InstanceTemplate()->SetAccessor(NanNew<String>("albumLimit"), getAlbumLimit, setAlbumLimit);
  constructorTemplate->InstanceTemplate()->SetAccessor(NanNew<String>("artistOffset"), getArtistOffset, setArtistOffset);
  constructorTemplate->InstanceTemplate()->SetAccessor(NanNew<String>("artistLimit"), getArtistLimit, setArtistLimit);
  constructorTemplate->InstanceTemplate()->SetAccessor(NanNew<String>("playlistOffset"), getPlaylistOffset, setPlaylistOffset);
  constructorTemplate->InstanceTemplate()->SetAccessor(NanNew<String>("playlistLimit"), getPlaylistLimit, setPlaylistLimit);
  NanAssignPersistent(NodeSearch::constructorTemplate, constructorTemplate);
}
<|endoftext|>


<commit_before>/**
 * Open[Flex] Board View
 *
 * Copyright chloridite 2016
 * Copyright inflex 2016 (Paul Daniels)
 *
 * Git Fork: https://github.com/inflex/OpenBoardView
 *
 */
#include "BoardView.h"

#include "imgui_impl_dx9.h"
#include <d3d9.h>
#define DIRECTINPUT_VERSION 0x0800
#include "platform.h"
#include "confparse.h"
#include "crtdbg.h"
#include "resource.h"
#include <dinput.h>
#include <direct.h>
#include <shlwapi.h>
#include <tchar.h>

// Data
static LPDIRECT3DDEVICE9 g_pd3dDevice = NULL;
static D3DPRESENT_PARAMETERS g_d3dpp;

// local functions
#ifndef S_ISDIR
#define S_ISDIR(mode) (((mode)&S_IFMT) == S_IFDIR)
#endif
uint32_t byte4swap(uint32_t x) {
	/*
	* used to convert RGBA -> ABGR etc
	*/
	return (((x & 0x000000ff) << 24) | ((x & 0x0000ff00) << 8) | ((x & 0x00ff0000) >> 8) | ((x & 0xff000000) >> 24));
}

extern LRESULT ImGui_ImplDX9_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) {
	if (ImGui_ImplDX9_WndProcHandler(hWnd, msg, wParam, lParam)) return true;

	switch (msg) {
		case WM_SIZE:
			if (g_pd3dDevice != NULL && wParam != SIZE_MINIMIZED) {
				ImGui_ImplDX9_InvalidateDeviceObjects();
				g_d3dpp.BackBufferWidth  = LOWORD(lParam);
				g_d3dpp.BackBufferHeight = HIWORD(lParam);
				HRESULT hr               = g_pd3dDevice->Reset(&g_d3dpp);
				if (hr == D3DERR_INVALIDCALL) IM_ASSERT(0);
				ImGui_ImplDX9_CreateDeviceObjects();
			}
			return 0;
		case WM_SYSCOMMAND:
			if ((wParam & 0xfff0) == SC_KEYMENU) // Disable ALT application menu
				return 0;
			break;
		case WM_DESTROY: PostQuitMessage(0); return 0;
	}
	return DefWindowProc(hWnd, msg, wParam, lParam);
}

int CALLBACK WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {

	// Initialize comctl
	CoInitializeEx(NULL, COINIT_MULTITHREADED);

	char ss[1025];
	char *homepath;
	int err;
	size_t hpsz;
	Confparse obvconfig;
	int sizex, sizey;
	bool use_exepath  = false;
	bool use_homepath = true;
	CHAR history_file[MAX_PATH];
	CHAR conf_file[MAX_PATH];

	static const wchar_t *class_name = L"Openflex Board View";

	HMODULE hModule = GetModuleHandleA(NULL);
	CHAR exepath[MAX_PATH];
	GetModuleFileNameA(hModule, exepath, MAX_PATH);

	/*
	 * Trim off the filename at the end of the path
	*/
	int l = strlen(exepath);
	while (--l) {
		if (exepath[l] == '\\') {
			exepath[l] = '\0';
			break;
		}
	}
	snprintf(history_file, sizeof(history_file), "%s\\obv.history", exepath);
	snprintf(conf_file, sizeof(conf_file), "%s\\obv.conf", exepath);

	err = _dupenv_s(&homepath, &hpsz, "APPDATA");
	if (homepath) {
		struct stat st;
		int sr;
		snprintf(ss, sizeof(ss), "%s/openboardview", homepath);
		sr = stat(ss, &st);
		if (sr == -1) {
			//_mkdir(ss);
			// sr = stat(ss, &st);
		} else {
			snprintf(history_file, sizeof(history_file), "%s\\obv.history", homepath);
			snprintf(conf_file, sizeof(conf_file), "%s\\obv.conf", homepath);
		}
	}

	// Create application window
	HINSTANCE instance = GetModuleHandle(NULL);
	HICON icon         = LoadIcon(instance, MAKEINTRESOURCE(IDI_ICON1));
	WNDCLASSEX wc      = {sizeof(WNDCLASSEX), CS_CLASSDC, WndProc, 0L, 0L, instance, icon, NULL, NULL, NULL, class_name, NULL};
	RegisterClassEx(&wc);

	obvconfig.Load(conf_file);
	sizex = obvconfig.ParseInt("windowX", 1280);
	sizey = obvconfig.ParseInt("windowY", 900);

	HWND hwnd = CreateWindow(class_name,
	                         _T("Openflex Board Viewer"),
	                         WS_OVERLAPPEDWINDOW,
	                         CW_USEDEFAULT,
	                         CW_USEDEFAULT,
	                         1280,
	                         800,
	                         NULL,
	                         NULL,
	                         wc.hInstance,
	                         NULL);

	// Initialize Direct3D
	LPDIRECT3D9 pD3D;
	if ((pD3D = Direct3DCreate9(D3D_SDK_VERSION)) == NULL) {
		UnregisterClass(class_name, wc.hInstance);
		return 0;
	}
	ZeroMemory(&g_d3dpp, sizeof(g_d3dpp));
	g_d3dpp.Windowed               = TRUE;
	g_d3dpp.SwapEffect             = D3DSWAPEFFECT_DISCARD;
	g_d3dpp.BackBufferFormat       = D3DFMT_UNKNOWN;
	g_d3dpp.EnableAutoDepthStencil = TRUE;
	g_d3dpp.AutoDepthStencilFormat = D3DFMT_D16;
	g_d3dpp.PresentationInterval   = D3DPRESENT_INTERVAL_ONE;

	// Create the D3DDevice
	if (pD3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hwnd, D3DCREATE_HARDWARE_VERTEXPROCESSING, &g_d3dpp, &g_pd3dDevice) <
	    0) {
		pD3D->Release();
		UnregisterClass(class_name, wc.hInstance);
		return 0;
	}

	// Setup ImGui binding
	ImGui_ImplDX9_Init(hwnd, g_pd3dDevice);

	// Load Fonts
	// (there is a default font, this is only if you want to change it. see
	// extra_fonts/README.txt
	// for more details)
	ImGuiIO &io = ImGui::GetIO();
	int ttf_size;
	unsigned char *ttf_data = LoadAsset(&ttf_size, ASSET_FIRA_SANS);
	ImFontConfig font_cfg{};
	font_cfg.FontDataOwnedByAtlas = false;
	io.Fonts->AddFontFromMemoryTTF(ttf_data, ttf_size, 20.0f, &font_cfg);
// io.Fonts->AddFontDefault();
// io.Fonts->AddFontFromFileTTF("../../extra_fonts/Cousine-Regular.ttf", 15.0f);
// io.Fonts->AddFontFromFileTTF("../../extra_fonts/DroidSans.ttf", 16.0f);
// io.Fonts->AddFontFromFileTTF("../../extra_fonts/ProggyClean.ttf", 13.0f);
// io.Fonts->AddFontFromFileTTF("../../extra_fonts/ProggyTiny.ttf", 10.0f);
// io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\ArialUni.ttf", 18.0f, NULL,
// io.Fonts->GetGlyphRangesJapanese());
#if 0
	// Get current flag
	int tmpFlag = _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG);

	// Turn on leak-checking bit.
	tmpFlag |= _CRTDBG_CHECK_ALWAYS_DF;

	// Set flag to the new value.
	_CrtSetDbgFlag(tmpFlag);
#endif

	BoardView app{};
	if (homepath) {
		app.fhistory.Set_filename(history_file);
		app.fhistory.Load();
	}

	/*
	* Some machines (Atom etc) don't have enough CPU/GPU
	* grunt to cope with the large number of AA'd circles
	* generated on a large dense board like a Macbook Pro
	* so we have the lowCPU option which will let people
	* trade good-looks for better FPS
	*/
	app.slowCPU = obvconfig.ParseBool("slowCPU", false);
	if (app.slowCPU == true) {
		ImGuiStyle &style       = ImGui::GetStyle();
		style.AntiAliasedShapes = false;
	}

	app.showFPS = obvconfig.ParseBool("showFPS", false);
	app.showFPS = obvconfig.ParseBool("pinHalo", true);

	/*
	* Colours in ImGui can be represented as a 4-byte packed uint32_t as ABGR
	* but most humans are more accustomed to RBGA, so for the sake of readability
	* we use the human-readable version but swap the ordering around when
	* it comes to assigning the actual colour to ImGui.
	*/
	app.m_colors.backgroundColor     = byte4swap(obvconfig.ParseHex("backgroundColor", 0x000000a0));
	app.m_colors.partTextColor       = byte4swap(obvconfig.ParseHex("partTextColor", 0x008080ff));
	app.m_colors.boardOutline        = byte4swap(obvconfig.ParseHex("boardOutline", 0xffff00ff));
	app.m_colors.boxColor            = byte4swap(obvconfig.ParseHex("boxColor", 0xccccccff));
	app.m_colors.pinDefault          = byte4swap(obvconfig.ParseHex("pinDefault", 0xff0000ff));
	app.m_colors.pinGround           = byte4swap(obvconfig.ParseHex("pinGround", 0xbb0000ff));
	app.m_colors.pinNotConnected     = byte4swap(obvconfig.ParseHex("pinNotConnected", 0x0000ffff));
	app.m_colors.pinTestPad          = byte4swap(obvconfig.ParseHex("pinTestPad", 0x888888ff));
	app.m_colors.pinSelected         = byte4swap(obvconfig.ParseHex("pinSelected", 0xeeeeeeff));
	app.m_colors.pinHalo             = byte4swap(obvconfig.ParseHex("pinHaloColor", 0x00ff006f));
	app.m_colors.pinHighlighted      = byte4swap(obvconfig.ParseHex("pinHighlighted", 0xffffffff));
	app.m_colors.pinHighlightSameNet = byte4swap(obvconfig.ParseHex("pinHighlightSameNet", 0xfff888ff));
	app.m_colors.annotationPartAlias = byte4swap(obvconfig.ParseHex("annotationPartAlias", 0xffff00ff));
	app.m_colors.partHullColor       = byte4swap(obvconfig.ParseHex("partHullColor", 0x80808080));
	app.m_colors.selectedMaskPins    = byte4swap(obvconfig.ParseHex("selectedMaskPins", 0xffffff4f));
	app.m_colors.selectedMaskParts   = byte4swap(obvconfig.ParseHex("selectedMaskParts", 0xffffff8f));
	app.m_colors.selectedMaskOutline = byte4swap(obvconfig.ParseHex("selectedMaskOutline", 0xffffff8f));

	bool show_test_window    = true;
	bool show_another_window = false;
	ImVec4 clear_col         = ImColor(20, 20, 30);

	// Main loop
	MSG msg;
	ZeroMemory(&msg, sizeof(msg));
	ShowWindow(hwnd, SW_SHOWDEFAULT);
	UpdateWindow(hwnd);
	while (msg.message != WM_QUIT) {
		if (PeekMessage(&msg, NULL, 0U, 0U, PM_REMOVE)) {
			TranslateMessage(&msg);
			DispatchMessage(&msg);
			continue;
		}
		ImGui_ImplDX9_NewFrame();
		app.Update();
		if (app.m_wantsQuit) {
			PostMessage(hwnd, WM_QUIT, 0, 0);
		}

		// Rendering
		g_pd3dDevice->SetRenderState(D3DRS_ZENABLE, false);
		g_pd3dDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, false);
		g_pd3dDevice->SetRenderState(D3DRS_SCISSORTESTENABLE, false);
		D3DCOLOR clear_col_dx = D3DCOLOR_RGBA(
		    (int)(clear_col.x * 255.0f), (int)(clear_col.y * 255.0f), (int)(clear_col.z * 255.0f), (int)(clear_col.w * 255.0f));
		g_pd3dDevice->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, clear_col_dx, 1.0f, 0);
		if (g_pd3dDevice->BeginScene() >= 0) {
			ImGui::Render();
			g_pd3dDevice->EndScene();
		}
		g_pd3dDevice->Present(NULL, NULL, NULL, NULL);
	}

	ImGui_ImplDX9_Shutdown();
	if (g_pd3dDevice) g_pd3dDevice->Release();
	if (pD3D) pD3D->Release();
	UnregisterClass(class_name, wc.hInstance);

	return 0;
}
<commit_msg>Fixed missing parsing of width, height and font-size for windows in config<commit_after>/**
 * Open[Flex] Board View
 *
 * Copyright chloridite 2016
 * Copyright inflex 2016 (Paul Daniels)
 *
 * Git Fork: https://github.com/inflex/OpenBoardView
 *
 */
#include "BoardView.h"

#include "imgui_impl_dx9.h"
#include <d3d9.h>
#define DIRECTINPUT_VERSION 0x0800
#include "platform.h"
#include "confparse.h"
#include "crtdbg.h"
#include "resource.h"
#include <dinput.h>
#include <direct.h>
#include <shlwapi.h>
#include <tchar.h>

// Data
static LPDIRECT3DDEVICE9 g_pd3dDevice = NULL;
static D3DPRESENT_PARAMETERS g_d3dpp;

// local functions
#ifndef S_ISDIR
#define S_ISDIR(mode) (((mode)&S_IFMT) == S_IFDIR)
#endif
uint32_t byte4swap(uint32_t x) {
	/*
	* used to convert RGBA -> ABGR etc
	*/
	return (((x & 0x000000ff) << 24) | ((x & 0x0000ff00) << 8) | ((x & 0x00ff0000) >> 8) | ((x & 0xff000000) >> 24));
}

extern LRESULT ImGui_ImplDX9_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) {
	if (ImGui_ImplDX9_WndProcHandler(hWnd, msg, wParam, lParam)) return true;

	switch (msg) {
		case WM_SIZE:
			if (g_pd3dDevice != NULL && wParam != SIZE_MINIMIZED) {
				ImGui_ImplDX9_InvalidateDeviceObjects();
				g_d3dpp.BackBufferWidth  = LOWORD(lParam);
				g_d3dpp.BackBufferHeight = HIWORD(lParam);
				HRESULT hr               = g_pd3dDevice->Reset(&g_d3dpp);
				if (hr == D3DERR_INVALIDCALL) IM_ASSERT(0);
				ImGui_ImplDX9_CreateDeviceObjects();
			}
			return 0;
		case WM_SYSCOMMAND:
			if ((wParam & 0xfff0) == SC_KEYMENU) // Disable ALT application menu
				return 0;
			break;
		case WM_DESTROY: PostQuitMessage(0); return 0;
	}
	return DefWindowProc(hWnd, msg, wParam, lParam);
}

int CALLBACK WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {

	// Initialize comctl
	CoInitializeEx(NULL, COINIT_MULTITHREADED);

	char ss[1025];
	char *homepath;
	int err;
	size_t hpsz;
	Confparse obvconfig;
	int sizex, sizey;
	bool use_exepath  = false;
	bool use_homepath = true;
	CHAR history_file[MAX_PATH];
	CHAR conf_file[MAX_PATH];

	static const wchar_t *class_name = L"Openflex Board View";

	HMODULE hModule = GetModuleHandleA(NULL);
	CHAR exepath[MAX_PATH];
	GetModuleFileNameA(hModule, exepath, MAX_PATH);

	/*
	 * Trim off the filename at the end of the path
	*/
	int l = strlen(exepath);
	while (--l) {
		if (exepath[l] == '\\') {
			exepath[l] = '\0';
			break;
		}
	}
	snprintf(history_file, sizeof(history_file), "%s\\obv.history", exepath);
	snprintf(conf_file, sizeof(conf_file), "%s\\obv.conf", exepath);

	err = _dupenv_s(&homepath, &hpsz, "APPDATA");
	if (homepath) {
		struct stat st;
		int sr;
		snprintf(ss, sizeof(ss), "%s/openboardview", homepath);
		sr = stat(ss, &st);
		if (sr == -1) {
			//_mkdir(ss);
			// sr = stat(ss, &st);
		} else {
			snprintf(history_file, sizeof(history_file), "%s\\obv.history", homepath);
			snprintf(conf_file, sizeof(conf_file), "%s\\obv.conf", homepath);
		}
	}

	// Create application window
	HINSTANCE instance = GetModuleHandle(NULL);
	HICON icon         = LoadIcon(instance, MAKEINTRESOURCE(IDI_ICON1));
	WNDCLASSEX wc      = {sizeof(WNDCLASSEX), CS_CLASSDC, WndProc, 0L, 0L, instance, icon, NULL, NULL, NULL, class_name, NULL};
	RegisterClassEx(&wc);

	obvconfig.Load(conf_file);
	sizex = obvconfig.ParseInt("windowX", 900);
	sizey = obvconfig.ParseInt("windowY", 600);

	HWND hwnd = CreateWindow(class_name,
	                         _T("Openflex Board Viewer"),
	                         WS_OVERLAPPEDWINDOW,
	                         CW_USEDEFAULT,
	                         CW_USEDEFAULT,
	                         sizex,
	                         sizey,
	                         NULL,
	                         NULL,
	                         wc.hInstance,
	                         NULL);

	// Initialize Direct3D
	LPDIRECT3D9 pD3D;
	if ((pD3D = Direct3DCreate9(D3D_SDK_VERSION)) == NULL) {
		UnregisterClass(class_name, wc.hInstance);
		return 0;
	}
	ZeroMemory(&g_d3dpp, sizeof(g_d3dpp));
	g_d3dpp.Windowed               = TRUE;
	g_d3dpp.SwapEffect             = D3DSWAPEFFECT_DISCARD;
	g_d3dpp.BackBufferFormat       = D3DFMT_UNKNOWN;
	g_d3dpp.EnableAutoDepthStencil = TRUE;
	g_d3dpp.AutoDepthStencilFormat = D3DFMT_D16;
	g_d3dpp.PresentationInterval   = D3DPRESENT_INTERVAL_ONE;

	// Create the D3DDevice
	if (pD3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hwnd, D3DCREATE_HARDWARE_VERTEXPROCESSING, &g_d3dpp, &g_pd3dDevice) <
	    0) {
		pD3D->Release();
		UnregisterClass(class_name, wc.hInstance);
		return 0;
	}

	// Setup ImGui binding
	ImGui_ImplDX9_Init(hwnd, g_pd3dDevice);

	// Load Fonts
	// (there is a default font, this is only if you want to change it. see
	// extra_fonts/README.txt
	// for more details)
	ImGuiIO &io = ImGui::GetIO();
	int ttf_size;
	unsigned char *ttf_data = LoadAsset(&ttf_size, ASSET_FIRA_SANS);
	ImFontConfig font_cfg{};
	font_cfg.FontDataOwnedByAtlas = false;
	io.Fonts->AddFontFromMemoryTTF(ttf_data, ttf_size, obvconfig.ParseDouble("fontSize", 20.0f), &font_cfg);
// io.Fonts->AddFontDefault();
// io.Fonts->AddFontFromFileTTF("../../extra_fonts/Cousine-Regular.ttf", 15.0f);
// io.Fonts->AddFontFromFileTTF("../../extra_fonts/DroidSans.ttf", 16.0f);
// io.Fonts->AddFontFromFileTTF("../../extra_fonts/ProggyClean.ttf", 13.0f);
// io.Fonts->AddFontFromFileTTF("../../extra_fonts/ProggyTiny.ttf", 10.0f);
// io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\ArialUni.ttf", 18.0f, NULL,
// io.Fonts->GetGlyphRangesJapanese());
#if 0
	// Get current flag
	int tmpFlag = _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG);

	// Turn on leak-checking bit.
	tmpFlag |= _CRTDBG_CHECK_ALWAYS_DF;

	// Set flag to the new value.
	_CrtSetDbgFlag(tmpFlag);
#endif

	BoardView app{};
	if (homepath) {
		app.fhistory.Set_filename(history_file);
		app.fhistory.Load();
	}

	/*
	* Some machines (Atom etc) don't have enough CPU/GPU
	* grunt to cope with the large number of AA'd circles
	* generated on a large dense board like a Macbook Pro
	* so we have the lowCPU option which will let people
	* trade good-looks for better FPS
	*/
	app.slowCPU = obvconfig.ParseBool("slowCPU", false);
	if (app.slowCPU == true) {
		ImGuiStyle &style       = ImGui::GetStyle();
		style.AntiAliasedShapes = false;
	}

	app.showFPS = obvconfig.ParseBool("showFPS", false);
	app.showFPS = obvconfig.ParseBool("pinHalo", true);

	/*
	* Colours in ImGui can be represented as a 4-byte packed uint32_t as ABGR
	* but most humans are more accustomed to RBGA, so for the sake of readability
	* we use the human-readable version but swap the ordering around when
	* it comes to assigning the actual colour to ImGui.
	*/
	app.m_colors.backgroundColor     = byte4swap(obvconfig.ParseHex("backgroundColor", 0x000000a0));
	app.m_colors.partTextColor       = byte4swap(obvconfig.ParseHex("partTextColor", 0x008080ff));
	app.m_colors.boardOutline        = byte4swap(obvconfig.ParseHex("boardOutline", 0xffff00ff));
	app.m_colors.boxColor            = byte4swap(obvconfig.ParseHex("boxColor", 0xccccccff));
	app.m_colors.pinDefault          = byte4swap(obvconfig.ParseHex("pinDefault", 0xff0000ff));
	app.m_colors.pinGround           = byte4swap(obvconfig.ParseHex("pinGround", 0xbb0000ff));
	app.m_colors.pinNotConnected     = byte4swap(obvconfig.ParseHex("pinNotConnected", 0x0000ffff));
	app.m_colors.pinTestPad          = byte4swap(obvconfig.ParseHex("pinTestPad", 0x888888ff));
	app.m_colors.pinSelected         = byte4swap(obvconfig.ParseHex("pinSelected", 0xeeeeeeff));
	app.m_colors.pinHalo             = byte4swap(obvconfig.ParseHex("pinHaloColor", 0x00ff006f));
	app.m_colors.pinHighlighted      = byte4swap(obvconfig.ParseHex("pinHighlighted", 0xffffffff));
	app.m_colors.pinHighlightSameNet = byte4swap(obvconfig.ParseHex("pinHighlightSameNet", 0xfff888ff));
	app.m_colors.annotationPartAlias = byte4swap(obvconfig.ParseHex("annotationPartAlias", 0xffff00ff));
	app.m_colors.partHullColor       = byte4swap(obvconfig.ParseHex("partHullColor", 0x80808080));
	app.m_colors.selectedMaskPins    = byte4swap(obvconfig.ParseHex("selectedMaskPins", 0xffffff4f));
	app.m_colors.selectedMaskParts   = byte4swap(obvconfig.ParseHex("selectedMaskParts", 0xffffff8f));
	app.m_colors.selectedMaskOutline = byte4swap(obvconfig.ParseHex("selectedMaskOutline", 0xffffff8f));

	app.SetFZKey(obvconfig.ParseStr("FZKey", ""));

	bool show_test_window    = true;
	bool show_another_window = false;
	ImVec4 clear_col         = ImColor(20, 20, 30);

	// Main loop
	MSG msg;
	ZeroMemory(&msg, sizeof(msg));
	ShowWindow(hwnd, SW_SHOWDEFAULT);
	UpdateWindow(hwnd);
	while (msg.message != WM_QUIT) {
		if (PeekMessage(&msg, NULL, 0U, 0U, PM_REMOVE)) {
			TranslateMessage(&msg);
			DispatchMessage(&msg);
			continue;
		}
		ImGui_ImplDX9_NewFrame();
		app.Update();
		if (app.m_wantsQuit) {
			PostMessage(hwnd, WM_QUIT, 0, 0);
		}

		// Rendering
		g_pd3dDevice->SetRenderState(D3DRS_ZENABLE, false);
		g_pd3dDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, false);
		g_pd3dDevice->SetRenderState(D3DRS_SCISSORTESTENABLE, false);
		D3DCOLOR clear_col_dx = D3DCOLOR_RGBA(
		    (int)(clear_col.x * 255.0f), (int)(clear_col.y * 255.0f), (int)(clear_col.z * 255.0f), (int)(clear_col.w * 255.0f));
		g_pd3dDevice->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, clear_col_dx, 1.0f, 0);
		if (g_pd3dDevice->BeginScene() >= 0) {
			ImGui::Render();
			g_pd3dDevice->EndScene();
		}
		g_pd3dDevice->Present(NULL, NULL, NULL, NULL);
	}

	ImGui_ImplDX9_Shutdown();
	if (g_pd3dDevice) g_pd3dDevice->Release();
	if (pD3D) pD3D->Release();
	UnregisterClass(class_name, wc.hInstance);

	return 0;
}
<|endoftext|>


<commit_before>#include "subcarrier.h"

#ifdef HAVE_LIQUID

#include <complex>
#include <deque>
#include <iostream>

#include "liquid_wrappers.h"

namespace redsea {

namespace {

const float kFs_Hz               = 171000.0f;
const float kFc_0_Hz             = 57000.0f;
const float kBitsPerSecond       = 1187.5f;
const int   kInputBufferSize     = 4096;
const int   kSamplesPerSymbol    = 3;
const float kAGCBandwidth_Hz     = 500.0f;
const float kAGCInitialGain      = 0.0077f;
const float kLowpassCutoff_Hz    = 2600.0f;
const float kSymsyncBandwidth_Hz = 2400.0f;
const int   kSymsyncDelay        = 2;
const float kSymsyncBeta         = 0.8f;
const float kPLLBandwidth_Hz     = 0.3f;

float hertz2step(float Hz) {
  return Hz * 2.0f * M_PI / kFs_Hz;
}

#ifdef DEBUG
float step2hertz(float step) {
  return step * kFs_Hz / (2.0f * M_PI);
}
#endif

}

BiphaseDecoder::BiphaseDecoder() : prev_psk_symbol_(0.0f),
  clock_history_(48), clock_(0), clock_polarity_(0) {

}

BiphaseDecoder::~BiphaseDecoder() {

}

// ret: is_clock, symbol
std::pair<bool, std::complex<float>> BiphaseDecoder::push(
    std::complex<float> psk_symbol) {

  std::complex<float> biphase = (psk_symbol - prev_psk_symbol_);
  bool is_clock = (clock_ % 2 == clock_polarity_);

  clock_history_[clock_] =
    biphase.real() < 0.f ? -biphase.real() : biphase.real();

  if (++clock_ == clock_history_.size()) {

    float a=0;
    float b=0;

    for (int i=0; i<(int)clock_history_.size();i++) {
      if (i%2==0)
        a += clock_history_[i];
      else
        b += clock_history_[i];
      clock_history_[i] = 0;
    }

    if      (a>b) clock_polarity_ = 0;
    else if (b>a) clock_polarity_ = 1;

    clock_ = 0;

  }

  prev_psk_symbol_ = psk_symbol;

  return {is_clock, biphase};
}

DeltaDecoder::DeltaDecoder() : prev_(0) {

}

DeltaDecoder::~DeltaDecoder() {

}

unsigned DeltaDecoder::decode(unsigned d) {
  unsigned bit = (d != prev_);
  prev_ = d;
  return bit;
}

Subcarrier::Subcarrier() : numsamples_(0), bit_buffer_(),
  fir_lpf_(256, kLowpassCutoff_Hz / kFs_Hz),
  agc_(kAGCBandwidth_Hz / kFs_Hz, kAGCInitialGain),
  nco_approx_(hertz2step(kFc_0_Hz)),
  nco_exact_(hertz2step(kFc_0_Hz)),
  symsync_(LIQUID_FIRFILT_RRC, kSamplesPerSymbol, kSymsyncDelay,
           kSymsyncBeta, 32),
  modem_(LIQUID_MODEM_PSK2), is_eof_(false),
  delta_decoder_() {

    symsync_.setBandwidth(kSymsyncBandwidth_Hz / kFs_Hz);
    symsync_.setOutputRate(1);
    nco_exact_.setPLLBandwidth(kPLLBandwidth_Hz / kFs_Hz);

}

Subcarrier::~Subcarrier() {

}

void Subcarrier::demodulateMoreBits() {

  int16_t inbuffer[kInputBufferSize];
  int samplesread = fread(inbuffer, sizeof(inbuffer[0]), kInputBufferSize,
      stdin);
  if (samplesread < kInputBufferSize) {
    is_eof_ = true;
    return;
  }

  const int decimate = kFs_Hz / kBitsPerSecond / 2 / kSamplesPerSymbol;

  for (int16_t sample : inbuffer) {

    std::complex<float> sample_baseband = nco_approx_.mixDown(sample);

    fir_lpf_.push(sample_baseband);

    if (numsamples_ % decimate == 0) {

      std::complex<float> sample_lopass = agc_.execute(fir_lpf_.execute());

      // PLL-controlled 57 kHz mixdown, aliasing is intentional
      sample_lopass = nco_exact_.mixDown(sample_lopass);

      std::vector<std::complex<float>> symbols =
        symsync_.execute(sample_lopass);

      for (std::complex<float> symbol : symbols) {
#ifdef DEBUG
        printf("sy:%f,%f,%f\n",
            numsamples_ / kFs_Hz,
            symbol.real(),
            symbol.imag());
#endif

        // Modem is only used for phase tracking
        modem_.demodulate(symbol);
        nco_exact_.stepPLL(modem_.getPhaseError() * 9.f);// factor is a quickfix

        bool is_clock;
        std::complex<float> biphase;
        std::tie(is_clock,biphase) = biphase_decoder_.push(symbol);

        if (is_clock) {
          bit_buffer_.push_back(delta_decoder_.decode(
                biphase.real() >= 0));
#ifdef DEBUG
          printf("bi:%f,%f,%f\n",
              numsamples_ / kFs_Hz,
              biphase.real(),
              biphase.imag());
#endif
        }

      }
#ifdef DEBUG
      printf("f:%f,%f,%f,%f,%f,%f,%f\n",
          numsamples_ / kFs_Hz,
          (float)sample,
          step2hertz(nco_exact_.getFrequency()),
          modem_.getPhaseError(),
          agc_.getGain(),
          sample_lopass.real(),
          sample_lopass.imag());
#endif
    }

    nco_exact_.step();
    nco_approx_.step();

    numsamples_ ++;

  }
}

int Subcarrier::getNextBit() {
  while (bit_buffer_.size() < 1 && !isEOF())
    demodulateMoreBits();

  int bit = 0;

  if (bit_buffer_.size() > 0) {
    bit = bit_buffer_.front();
    bit_buffer_.pop_front();
  }

  return bit;
}

bool Subcarrier::isEOF() const {
  return is_eof_;
}

#ifdef DEBUG
float Subcarrier::getT() const {
  return numsamples_ / kFs_Hz;
}
#endif

} // namespace redsea

#endif // HAVE_LIQUID
<commit_msg>parameterize<commit_after>#include "subcarrier.h"

#ifdef HAVE_LIQUID

#include <complex>
#include <deque>
#include <iostream>

#include "liquid_wrappers.h"

namespace redsea {

namespace {

const float kFs_Hz               = 171000.0f;
const float kFc_0_Hz             = 57000.0f;
const float kBitsPerSecond       = 1187.5f;
const int   kInputBufferSize     = 4096;
const int   kSamplesPerSymbol    = 3;
const float kAGCBandwidth_Hz     = 500.0f;
const float kAGCInitialGain      = 0.0077f;
const float kLowpassCutoff_Hz    = 2600.0f;
const float kSymsyncBandwidth_Hz = 2400.0f;
const int   kSymsyncDelay        = 2;
const float kSymsyncBeta         = 0.8f;
const float kPLLBandwidth_Hz     = 0.3f;
const float kPLLMultiplier       = 9.0f;

float hertz2step(float Hz) {
  return Hz * 2.0f * M_PI / kFs_Hz;
}

#ifdef DEBUG
float step2hertz(float step) {
  return step * kFs_Hz / (2.0f * M_PI);
}
#endif

}

BiphaseDecoder::BiphaseDecoder() : prev_psk_symbol_(0.0f),
  clock_history_(48), clock_(0), clock_polarity_(0) {

}

BiphaseDecoder::~BiphaseDecoder() {

}

// ret: is_clock, symbol
std::pair<bool, std::complex<float>> BiphaseDecoder::push(
    std::complex<float> psk_symbol) {

  std::complex<float> biphase = (psk_symbol - prev_psk_symbol_);
  bool is_clock = (clock_ % 2 == clock_polarity_);

  clock_history_[clock_] =
    biphase.real() < 0.f ? -biphase.real() : biphase.real();

  if (++clock_ == clock_history_.size()) {

    float a=0;
    float b=0;

    for (int i=0; i<(int)clock_history_.size();i++) {
      if (i%2==0)
        a += clock_history_[i];
      else
        b += clock_history_[i];
      clock_history_[i] = 0;
    }

    if      (a>b) clock_polarity_ = 0;
    else if (b>a) clock_polarity_ = 1;

    clock_ = 0;

  }

  prev_psk_symbol_ = psk_symbol;

  return {is_clock, biphase};
}

DeltaDecoder::DeltaDecoder() : prev_(0) {

}

DeltaDecoder::~DeltaDecoder() {

}

unsigned DeltaDecoder::decode(unsigned d) {
  unsigned bit = (d != prev_);
  prev_ = d;
  return bit;
}

Subcarrier::Subcarrier() : numsamples_(0), bit_buffer_(),
  fir_lpf_(256, kLowpassCutoff_Hz / kFs_Hz),
  agc_(kAGCBandwidth_Hz / kFs_Hz, kAGCInitialGain),
  nco_approx_(hertz2step(kFc_0_Hz)),
  nco_exact_(hertz2step(kFc_0_Hz)),
  symsync_(LIQUID_FIRFILT_RRC, kSamplesPerSymbol, kSymsyncDelay,
           kSymsyncBeta, 32),
  modem_(LIQUID_MODEM_PSK2), is_eof_(false),
  delta_decoder_() {

    symsync_.setBandwidth(kSymsyncBandwidth_Hz / kFs_Hz);
    symsync_.setOutputRate(1);
    nco_exact_.setPLLBandwidth(kPLLBandwidth_Hz / kFs_Hz);

}

Subcarrier::~Subcarrier() {

}

void Subcarrier::demodulateMoreBits() {

  int16_t inbuffer[kInputBufferSize];
  int samplesread = fread(inbuffer, sizeof(inbuffer[0]), kInputBufferSize,
      stdin);
  if (samplesread < kInputBufferSize) {
    is_eof_ = true;
    return;
  }

  const int decimate = kFs_Hz / kBitsPerSecond / 2 / kSamplesPerSymbol;

  for (int16_t sample : inbuffer) {

    std::complex<float> sample_baseband = nco_approx_.mixDown(sample);

    fir_lpf_.push(sample_baseband);

    if (numsamples_ % decimate == 0) {

      std::complex<float> sample_lopass = agc_.execute(fir_lpf_.execute());

      // PLL-controlled 57 kHz mixdown, aliasing is intentional
      sample_lopass = nco_exact_.mixDown(sample_lopass);

      std::vector<std::complex<float>> symbols =
        symsync_.execute(sample_lopass);

      for (std::complex<float> symbol : symbols) {
#ifdef DEBUG
        printf("sy:%f,%f,%f\n",
            numsamples_ / kFs_Hz,
            symbol.real(),
            symbol.imag());
#endif

        // Modem is only used for phase tracking
        modem_.demodulate(symbol);
        nco_exact_.stepPLL(modem_.getPhaseError() * kPLLMultiplier);

        bool is_clock;
        std::complex<float> biphase;
        std::tie(is_clock,biphase) = biphase_decoder_.push(symbol);

        if (is_clock) {
          bit_buffer_.push_back(delta_decoder_.decode(
                biphase.real() >= 0));
#ifdef DEBUG
          printf("bi:%f,%f,%f\n",
              numsamples_ / kFs_Hz,
              biphase.real(),
              biphase.imag());
#endif
        }

      }
#ifdef DEBUG
      printf("f:%f,%f,%f,%f,%f,%f,%f\n",
          numsamples_ / kFs_Hz,
          (float)sample,
          step2hertz(nco_exact_.getFrequency()),
          modem_.getPhaseError(),
          agc_.getGain(),
          sample_lopass.real(),
          sample_lopass.imag());
#endif
    }

    nco_exact_.step();
    nco_approx_.step();

    numsamples_ ++;

  }
}

int Subcarrier::getNextBit() {
  while (bit_buffer_.size() < 1 && !isEOF())
    demodulateMoreBits();

  int bit = 0;

  if (bit_buffer_.size() > 0) {
    bit = bit_buffer_.front();
    bit_buffer_.pop_front();
  }

  return bit;
}

bool Subcarrier::isEOF() const {
  return is_eof_;
}

#ifdef DEBUG
float Subcarrier::getT() const {
  return numsamples_ / kFs_Hz;
}
#endif

} // namespace redsea

#endif // HAVE_LIQUID
<|endoftext|>


<commit_before>#include "library/common/engine.h"

#include "common/common/lock_guard.h"

namespace Envoy {

// As a server, Envoy's static factory registration happens when main is run. However, when compiled
// as a library, there is no guarantee that such registration will happen before the names are
// needed. The following calls ensure that registration happens before the entities are needed. Note
// that as more registrations are needed, explicit initialization calls will need to be added here.
static void registerFactories() {
  Envoy::Extensions::Clusters::DynamicForwardProxy::forceRegisterClusterFactory();
  Envoy::Extensions::HttpFilters::DynamicForwardProxy::
      forceRegisterDynamicForwardProxyFilterFactory();
  Envoy::Extensions::HttpFilters::RouterFilter::forceRegisterRouterFilterConfig();
  Envoy::Extensions::NetworkFilters::HttpConnectionManager::
      forceRegisterHttpConnectionManagerFilterConfigFactory();
  Envoy::Extensions::TransportSockets::RawBuffer::forceRegisterDownstreamRawBufferSocketFactory();
  Envoy::Extensions::TransportSockets::RawBuffer::forceRegisterUpstreamRawBufferSocketFactory();
  Envoy::Extensions::TransportSockets::Tls::forceRegisterUpstreamSslSocketFactory();
  Envoy::Upstream::forceRegisterLogicalDnsClusterFactory();
}

Engine::Engine(envoy_engine_callbacks callbacks, const char* config, const char* log_level,
               std::atomic<envoy_network_t>& preferred_network)
    : callbacks_(callbacks) {
  // Ensure static factory registration occurs on time.
  // TODO: ensure this is only called one time once multiple Engine objects can be allocated.
  registerFactories();

  // Create the Http::Dispatcher first since it contains initial queueing logic.
  // TODO: consider centralizing initial queueing in this class.
  http_dispatcher_ = std::make_unique<Http::Dispatcher>(preferred_network);

  // Start the Envoy on a dedicated thread.
  main_thread_ = std::thread(&Engine::run, this, std::string(config), std::string(log_level));
}

envoy_status_t Engine::run(std::string config, std::string log_level) {
  {
    Thread::LockGuard lock(mutex_);
    try {
      char* envoy_argv[] = {strdup("envoy"), strdup("--config-yaml"),   strdup(config.c_str()),
                            strdup("-l"),    strdup(log_level.c_str()), nullptr};

      main_common_ = std::make_unique<Envoy::MainCommon>(5, envoy_argv);
      event_dispatcher_ = &main_common_->server()->dispatcher();
      cv_.notifyOne();
    } catch (const Envoy::NoServingException& e) {
      return ENVOY_FAILURE;
    } catch (const Envoy::MalformedArgvException& e) {
      std::cerr << e.what() << std::endl;
      return ENVOY_FAILURE;
    } catch (const Envoy::EnvoyException& e) {
      std::cerr << e.what() << std::endl;
      return ENVOY_FAILURE;
    }

    // Note: We're waiting longer than we might otherwise to drain to the main thread's dispatcher.
    // This is because we're not simply waiting for its availability and for it to have started, but
    // also because we're waiting for clusters to have done their first attempt at DNS resolution.
    // When we improve synchronous failure handling and/or move to dynamic forwarding, we only need
    // to wait until the dispatcher is running (and can drain by enqueueing a drain callback on it,
    // as we did previously).
    auto server = main_common_->server();
    postinit_callback_handler_ = main_common_->server()->lifecycleNotifier().registerCallback(
        Envoy::Server::ServerLifecycleNotifier::Stage::PostInit, [this, server]() -> void {
          http_dispatcher_->ready(server->dispatcher(), server->clusterManager());
        });
  } // mutex_

  // The main run loop must run without holding the mutex, so that the destructor can acquire it.
  return TS_UNCHECKED_READ(main_common_)->run() ? ENVOY_SUCCESS : ENVOY_FAILURE;
}

Engine::~Engine() {
  // If we're already on the main thread, it should be safe to simply destruct.
  if (!main_thread_.joinable()) {
    return;
  }

  // If we're not on the main thread, we need to be sure that MainCommon is finished being
  // constructed so we can dispatch shutdown.
  {
    Thread::LockGuard lock(mutex_);
    if (!main_common_) {
      cv_.wait(mutex_);
    }
    ASSERT(main_common_);
    // Gracefully shutdown the running envoy instance by resetting the main_common_ unique_ptr.
    // Destroying MainCommon's member variables shutsdown things in the correct order and
    // gracefully.
    event_dispatcher_->post([this]() -> void {
      callbacks_.on_exit();
      TS_UNCHECKED_READ(main_common_).reset();
    });
  } // _mutex

  // Now we wait for the main thread to wrap things up.
  main_thread_.join();
}

Http::Dispatcher& Engine::httpDispatcher() { return *http_dispatcher_; }

} // namespace Envoy
<commit_msg>core: fix server early capture (#526)<commit_after>#include "library/common/engine.h"

#include "common/common/lock_guard.h"

namespace Envoy {

// As a server, Envoy's static factory registration happens when main is run. However, when compiled
// as a library, there is no guarantee that such registration will happen before the names are
// needed. The following calls ensure that registration happens before the entities are needed. Note
// that as more registrations are needed, explicit initialization calls will need to be added here.
static void registerFactories() {
  Envoy::Extensions::Clusters::DynamicForwardProxy::forceRegisterClusterFactory();
  Envoy::Extensions::HttpFilters::DynamicForwardProxy::
      forceRegisterDynamicForwardProxyFilterFactory();
  Envoy::Extensions::HttpFilters::RouterFilter::forceRegisterRouterFilterConfig();
  Envoy::Extensions::NetworkFilters::HttpConnectionManager::
      forceRegisterHttpConnectionManagerFilterConfigFactory();
  Envoy::Extensions::TransportSockets::RawBuffer::forceRegisterDownstreamRawBufferSocketFactory();
  Envoy::Extensions::TransportSockets::RawBuffer::forceRegisterUpstreamRawBufferSocketFactory();
  Envoy::Extensions::TransportSockets::Tls::forceRegisterUpstreamSslSocketFactory();
  Envoy::Upstream::forceRegisterLogicalDnsClusterFactory();
}

Engine::Engine(envoy_engine_callbacks callbacks, const char* config, const char* log_level,
               std::atomic<envoy_network_t>& preferred_network)
    : callbacks_(callbacks) {
  // Ensure static factory registration occurs on time.
  // TODO: ensure this is only called one time once multiple Engine objects can be allocated.
  registerFactories();

  // Create the Http::Dispatcher first since it contains initial queueing logic.
  // TODO: consider centralizing initial queueing in this class.
  http_dispatcher_ = std::make_unique<Http::Dispatcher>(preferred_network);

  // Start the Envoy on a dedicated thread.
  main_thread_ = std::thread(&Engine::run, this, std::string(config), std::string(log_level));
}

envoy_status_t Engine::run(std::string config, std::string log_level) {
  {
    Thread::LockGuard lock(mutex_);
    try {
      char* envoy_argv[] = {strdup("envoy"), strdup("--config-yaml"),   strdup(config.c_str()),
                            strdup("-l"),    strdup(log_level.c_str()), nullptr};

      main_common_ = std::make_unique<Envoy::MainCommon>(5, envoy_argv);
      event_dispatcher_ = &main_common_->server()->dispatcher();
      cv_.notifyOne();
    } catch (const Envoy::NoServingException& e) {
      return ENVOY_FAILURE;
    } catch (const Envoy::MalformedArgvException& e) {
      std::cerr << e.what() << std::endl;
      return ENVOY_FAILURE;
    } catch (const Envoy::EnvoyException& e) {
      std::cerr << e.what() << std::endl;
      return ENVOY_FAILURE;
    }

    // Note: We're waiting longer than we might otherwise to drain to the main thread's dispatcher.
    // This is because we're not simply waiting for its availability and for it to have started, but
    // also because we're waiting for clusters to have done their first attempt at DNS resolution.
    // When we improve synchronous failure handling and/or move to dynamic forwarding, we only need
    // to wait until the dispatcher is running (and can drain by enqueueing a drain callback on it,
    // as we did previously).
    postinit_callback_handler_ = main_common_->server()->lifecycleNotifier().registerCallback(
        Envoy::Server::ServerLifecycleNotifier::Stage::PostInit, [this]() -> void {
          Server::Instance* server = TS_UNCHECKED_READ(main_common_)->server();
          http_dispatcher_->ready(server->dispatcher(), server->clusterManager());
        });
  } // mutex_

  // The main run loop must run without holding the mutex, so that the destructor can acquire it.
  return TS_UNCHECKED_READ(main_common_)->run() ? ENVOY_SUCCESS : ENVOY_FAILURE;
}

Engine::~Engine() {
  // If we're already on the main thread, it should be safe to simply destruct.
  if (!main_thread_.joinable()) {
    return;
  }

  // If we're not on the main thread, we need to be sure that MainCommon is finished being
  // constructed so we can dispatch shutdown.
  {
    Thread::LockGuard lock(mutex_);
    if (!main_common_) {
      cv_.wait(mutex_);
    }
    ASSERT(main_common_);
    // Gracefully shutdown the running envoy instance by resetting the main_common_ unique_ptr.
    // Destroying MainCommon's member variables shutsdown things in the correct order and
    // gracefully.
    event_dispatcher_->post([this]() -> void {
      callbacks_.on_exit();
      TS_UNCHECKED_READ(main_common_).reset();
    });
  } // _mutex

  // Now we wait for the main thread to wrap things up.
  main_thread_.join();
}

Http::Dispatcher& Engine::httpDispatcher() { return *http_dispatcher_; }

} // namespace Envoy
<|endoftext|>