xaizek / pms (License: GPLv3+) (since 2018-12-07)
Older version of Practical Music Search written in C++.
<root> / src / topbar.cpp (76b977e2f0582a7fb6092afe12f22416b7639917) (8,355B) (mode 100644) [raw]
/* vi:set ts=8 sts=8 sw=8 noet:
 *
 * Practical Music Search
 * Copyright (c) 2006-2011  Kim Tore Jensen
 *
 * 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 <http://www.gnu.org/licenses/>.
 *
 */

#include <string>
#include <vector>
#include "topbar.h"
#include "field.h"
#include "console.h"
#include "song.h"
#include "config.h"
#include "mpd.h"
using namespace std;

extern Fieldtypes * fieldtypes;
extern Config * config;
extern MPD * mpd;

Topbarchunk::Topbarchunk(string s, Color * c)
{
	str = s;
	color = c;
}

Topbarsegment::Topbarsegment()
{
	condition.t = CONDITION_NONE;
	condition.f = CONDITION_NONE;
	condition.ctl = CONDITION_NONE;
	format = "";
}

Topbarsegment::~Topbarsegment()
{
	vector<Topbarchunk *>::iterator i;
	for (i = chunks.begin(); i != chunks.end(); ++i)
		delete *i;
}

unsigned int Topbarsegment::compile(Song * song)
{
	string str;
	unsigned int current_condition = CONDITION_NONE;
	size_t s = 0, e = 0;
	unsigned int strl = 0;
	vector<Topbarchunk *>::iterator i;
	vector<Field *>::iterator field;

	for (i = chunks.begin(); i != chunks.end(); ++i)
		delete *i;

	chunks.clear();
	if (!format.size())
		return 0;

	/* What is our current condition? */
	if (mpd->status.state == MPD_STATE_PLAY)
		current_condition |= CONDITION_PLAYING;
	if (mpd->currentsong)
		current_condition |= CONDITION_SONG;
	if (mpd->is_connected())
		current_condition |= CONDITION_CONNECTED;

	/* Evaluate wanted conditions against current conditions */
	if ((condition.t & current_condition) != condition.t)
		return 0;
	if ((condition.f & ~current_condition) != condition.f)
		return 0;

	/* Iterate through fields and place them into chunks */
	field = fields.begin();
	do
	{
		if ((e = format.find('$', s)) != string::npos)
		{
			/* our string */
			chunks.push_back(new Topbarchunk(format.substr(s, e - s), config->colors.topbar));
			chunks.push_back(new Topbarchunk((*field)->format(song), config->colors.field[(*field)->type]));
			++field;
			s = e + 1;
			continue;
		}
	}
	while(field != fields.end());

	if (s != string::npos)
		chunks.push_back(new Topbarchunk(format.substr(s), config->colors.topbar));
	
	for (i = chunks.begin(); i != chunks.end(); ++i)
		strl += (*i)->str.size();

	return strl;
}

Topbarline::~Topbarline()
{
	vector<Topbarsegment *>::iterator i;
	for (i = segments.begin(); i != segments.end(); ++i)
		delete *i;
}

void Topbar::clear()
{
	vector<Topbarline *>::iterator i;
	unsigned int t;

	for (t = 0; t < 3; ++t)
	{
		for (i = lines[t].begin(); i != lines[t].end(); ++i)
			delete *i;
		lines[t].clear();
	}
};

int Topbar::set(string format)
{
	string::iterator it;
	string working = "";
	string varname = "";
	bool bracket = false;
	bool var = false;
	bool conditional = false;
	bool invcondition;
	unsigned int condition = CONDITION_NONE;
	condition_t lastcondition = { CONDITION_NONE, CONDITION_NONE, CONDITION_NONE };
	vector<condition_t> conditions;
	unsigned int pos = 0; /* left/center/right */
	Field * field;
	Topbarline * line;
	Topbarsegment * segment;

	cached_format = format;

	clear();
	if (!format.size())
		return true;

	line = new Topbarline;
	segment = new Topbarsegment;
	lines[pos].push_back(line);

	for (it = format.begin(); it != format.end(); ++it)
	{
		/* Variable handling */
		if (var)
		{
			if (*it < 'a' || *it > 'z')
			{
				/* End of variable, store this field into segment */
				if ((field = fieldtypes->find(varname)) != NULL)
				{
					--it;
					segment->fields.push_back(field);
					segment->format += '$';
					var = false;
					varname.clear();
					continue;
				}

				/* Open conditional */
				else if ((*it == '(' && varname == "if") ||
						(*it == '{' && varname == "else"))
				{
					/* Store segment */
					segment->src = working;
					line->segments.push_back(segment);

					/* Create new segment */
					segment = new Topbarsegment;
					if (conditions.size())
						segment->condition = conditions.back();

					/* Reverse conditional "else" */
					if (*it == '{')
					{
						--it;

						if (invcondition)
							segment->condition.t |= lastcondition.ctl;
						else
							segment->condition.f |= lastcondition.ctl;

						segment->condition.ctl = condition;
						conditions.push_back(segment->condition);
					}

					/* Open a new segment based on conditional */
					var = false;
					conditional = true;
					working.clear();
					varname.clear();

					continue;
				}

				sterr("Topbar: unknown variable `%s' near %s", varname.c_str(), working.c_str());
				delete segment;
				return false;
			}

			varname += *it;
			working += *it;
			continue;
		}

		/* Conditional name handling */
		if (conditional && *it != '{')
		{
			if ((*it < 'a' || *it > 'z') && *it != '!')
			{
				/* Detect conditional name */
				if (*it != ')')
				{
					sterr("Topbar: unexpected `%c', expected `)' near %s", *it, working.c_str());
					delete segment;
					return false;
				}

				if ((invcondition = (varname.size() > 1 && varname[0] == '!')))
					varname = varname.substr(1);

				if (varname == "playing")
					condition = CONDITION_PLAYING;
				else if (varname == "song")
					condition = CONDITION_SONG;
				else if (varname == "connected")
					condition = CONDITION_CONNECTED;
				else
				{
					sterr("Topbar: unknown condition `%s' near %s", varname.c_str(), working.c_str());
					delete segment;
					return false;
				}

				/* Valid condition name, assign to current segment */
				if (!invcondition)
					segment->condition.t |= condition;
				else
					segment->condition.f |= condition;

				segment->condition.ctl = condition;
				conditions.push_back(segment->condition);
				varname.clear();
				working.clear();
				continue;
			}

			varname += *it;
			working += *it;
			continue;
		}

		working += *it;

		/*
		 * Closing and opening of brackets
		 */
		if (*it == '{')
		{
			if (!bracket)
			{
				bracket = true;
				continue;
			}
			if (conditional)
			{
				conditional = false;
				continue;
			}
			sterr("Topbar: unexpected `%c', expected identifier near %s", *it, working.c_str());
			delete segment;
			return false;
		}
		if (*it == '}')
		{
			if (conditional)
			{
				sterr("Topbar: unexpected `%c', expected `)' near %s", *it, working.c_str());
				delete segment;
				return false;
			}

			/* End conditional bracket */
			if (conditions.size())
			{
				lastcondition = conditions.back();
				conditions.pop_back();

				/* Store this segment */
				segment->src = working;
				line->segments.push_back(segment);

				/* Open a new segment based on previous conditional */
				segment = new Topbarsegment;
				if (conditions.size())
					segment->condition = conditions.back();

				working.clear();
				continue;
			}

			if (bracket)
			{
				bracket = false;
				segment->src = working;
				line->segments.push_back(segment);
				segment = new Topbarsegment;
				if (conditions.size())
					segment->condition = conditions.back();

				line = new Topbarline;
				if (++pos > 2)
					pos = 0;
				lines[pos].push_back(line);
				working.clear();
				continue;
			}
		}
		if (!bracket)
		{
			sterr("Topbar: unexpected `%c', expected `{' near %s", *it, working.c_str());
			delete segment;
			return false;
		}

		if (*it == '$')
		{
			var = true;
			continue;
		}

		/* If all else fails, append to text */
		segment->format += *it;
	}

	if (conditions.size())
	{
		sterr("Topbar: unexpected end of line, expected end of conditional `}' near %s", working.c_str());
		delete segment;
		return false;
	}

	if (conditional)
	{
		sterr("Topbar: unexpected end of line, expected end of expression `)' near %s", working.c_str());
		delete segment;
		return false;
	}

	if (bracket)
	{
		sterr("Topbar: unexpected end of line, expected end of segment `}' near %s", working.c_str());
		delete segment;
		return false;
	}

	return true;
}
Hints

Before first commit, do not forget to setup your git environment:
git config --global user.name "your_name_here"
git config --global user.email "your@email_here"

Clone this repository using HTTP(S):
git clone https://code.reversed.top/user/xaizek/pms

Clone this repository using ssh (do not forget to upload a key first):
git clone ssh://rocketgit@code.reversed.top/user/xaizek/pms

You are allowed to anonymously push to this repository.
This means that your pushed commits will automatically be transformed into a pull request:
... clone the repository ...
... make some changes and some commits ...
git push origin master