lumiera_/src/lib/time/digxel.hpp
Ichthyostega 670c670d55 style-adjustment: GUI indentation, naming and braces
over time, a specific Lumiera code writing style has emerged.
The GUI, as it stood, used somewhat different conventions,
which now have been aligned to the common standard.

Basically we use GNU style, with some adjustments for OO-programming,
we prefer CamelCase, and write TypeNames uppercase, variableNames lowercase
2015-05-29 04:44:58 +02:00

352 lines
11 KiB
C++

/*
DIGXEL.hpp - grid aligned and fixed format time specifications
Copyright (C) Lumiera.org
2010, Hermann Vosseler <Ichthyostega@web.de>
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 2 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, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/** @file digxel.hpp
** A self-contained numeric element for building structured numeric displays.
** Within the GUI of an editing application, we got to build several display widgets
** to show numeric values in a structured fashion, like Colours or Timecode. While the
** actual formats of such display elements vary largely, the common property is that
** they use an underlying \em format to build the numbers out of individual numeric elements.
** For a timecode display these are for example the sexagesimal (base 60) "digits" of the
** common human readable time representation. A "Digxel" is an abstract element with specific
** properties to support building such display widgets. It doesn't contain any GUI code, but
** can be wrapped up to build a custom widget.
**
** \par properties of a "Digxel"
**
** Semantically, it's a number or number component. It holds an internal numeric representation
** and is implicitly convertible back to the underlying numeric type (usually int or double).
**
** But at the same time, a Digxel has a definite textual format and the ability to present
** its numeric value formatted accordingly. To this end, the contract \em requires that
** numeric data pushed to the Digxel be kept within such limits to prevent exceeding the
** embedded formatting buffer. There is an assertion in debug mode, and a range check,
** but the result will be just truncated, so passing only sane values is clearly the
** caller's responsibility. Digxel might be considered an implementation support class,
** and performance is important to some limited degree;
** especially, formatted values will be \em cached.
**
** To support in-place modification, the digxel stores a mutation signal (functor). This
** functor will be invoked, whenever a new value gets assigned. The actual functor is free
** to cause side effects; the value returned from this functor will be the new value to set.
** If not configured, the default implementation just accepts the given value unaltered. Usually
** this mutation functor should invoke some internal recalculations, maybe resulting in a new
** value being pushed to the Digxel for display.
**
** \par configuration
** the Digxel template can be configured to some degree to adjust the stored numeric data
** and the actual format to be applied
**
** @see timecode.hpp typical usage example
**
*/
#ifndef LIB_TIME_DIGXEL_H
#define LIB_TIME_DIGXEL_H
#include "lib/error.hpp"
#include "lib/symbol.hpp"
#include "lib/util.hpp"
#include <boost/operators.hpp>
#include <boost/lexical_cast.hpp>
#include <functional>
#include <string>
#include <cstdio>
#include <cmath>
using std::string;
namespace lib {
namespace time {
namespace digxel {
using util::cStr;
using lib::Literal;
using boost::lexical_cast;
typedef const char* CBuf;
/**
* Default / base implementation for Digxel formatting.
* This formatter holds an inline buffer of limited size,
* receiving and caching the textual representation
*/
template<typename NUM, size_t len>
class PrintfFormatter
{
enum{ bufsiz = len+1 };
char printbuffer_[bufsiz];
Literal formatSpec_;
public:
PrintfFormatter (Literal fmt)
: printbuffer_()
, formatSpec_(fmt)
{
clear();
}
void clear() { printbuffer_[0] = '\0'; }
bool empty() { return '\0' == *printbuffer_; }
size_t
maxlen() const
{
return len;
}
CBuf
show (NUM val)
{
if (empty())
{
size_t space = std::snprintf (printbuffer_, bufsiz, formatSpec_, val);
REQUIRE (space < bufsiz, "Digxel value exceeded available buffer size. "
"For showing %s, %zu+1 chars instead of just %zu+1 would be required."
, cStr(lexical_cast<string>(val)), space, len); ///////////TICKET #197
}
ENSURE (!empty());
return printbuffer_;
}
};
/**
* default configured Formatter implementations
* for some of the basic numeric types
*/
template<typename NUM>
struct Formatter;
template<>
struct Formatter<int>
: PrintfFormatter<int, 9>
{
Formatter() : PrintfFormatter<int,9>("%3d") { }
};
template<>
struct Formatter<double>
: PrintfFormatter<double, 7>
{
Formatter() : PrintfFormatter<double,7>("%06.3f") { }
};
/* == other specialised Formatters == */
struct SexaFormatter
: PrintfFormatter<int, 4>
{
SexaFormatter() : PrintfFormatter<int,4>("%02d") { }
};
struct HexaFormatter
: PrintfFormatter<uint, 2>
{
HexaFormatter() : PrintfFormatter<uint,2>("%02X") { }
};
struct CountFormatter
: PrintfFormatter<long, 20>
{
CountFormatter() : PrintfFormatter<long,20>("%04ld") { }
};
struct HourFormatter
: PrintfFormatter<int, 9>
{
HourFormatter() : PrintfFormatter<int,9>("%2d") { }
};
struct SignFormatter
{
void clear() { }
size_t maxlen() const { return 1; }
CBuf show (int val) { return val<0? "-":" "; }
};
} //(End) digxel configuration namespace
using std::bind;
using std::function;
using std::placeholders::_1;
/**
* A number element for building structured numeric displays.
* The purpose is to represent parts of a numeric format, like
* e.g. the sexagesimal "digits" of a timecode display. A Digxel
* - is customised by template parameters to a specific number format
* - requires that any given number must not overflow the format buffer
* - can receive new numbers by assignment
* - stores and these given value numerically
* - will then format these numbers and cache the formatted representation
* - can store and invoke a mutation functor to pre-process values on setting
*
* @note comparisons are assumed to be not performance relevant
* @param NUM numeric type to be used for the value
* @param FMT a formatter and buffer holder type
* @see digxel::Formatter default printf based formatter
* @see lib::time::TCode
* @see Digxel_test
*
*/
template< typename NUM
, class FMT = digxel::Formatter<NUM>
>
class Digxel
: public boost::totally_ordered<Digxel<NUM,FMT> >
{
mutable
FMT buffer_;
NUM value_;
typedef Digxel<NUM,FMT> _Digxel;
typedef function<void(NUM)> _Mutator;
_Mutator mutator; ///< Functor for setting a new digxel value
public:
/** install an external functor to be applied on any new digxel value.
* This allows individual instances to limit the possible digxel values,
* or to update an compound value (e.g. a time comprised of hour, minute
* and second digxel elements). The installed functor needs to accept
* a "this" pointer and actually perform any desired state change
* as sideeffect. The default is to accept any value as-is.
* @warning using a mutator creates significant overhead;
* measurements indicate a factor of 4
* @see Digxel_test
*/
template<typename FUN, class THIS>
void
installMutator (FUN mutate, THIS& self)
{
mutator = bind (mutate, &self, _1 );
}
Digxel ()
: buffer_()
, value_ ()
, mutator()
{ }
// using the standard copy operations
operator NUM() const { return value_; }
operator string() const { return show(); }
size_t maxlen() const { return buffer_.maxlen(); }
digxel::CBuf
show() const
{
return buffer_.show (value_);
}
void
operator= (NUM n)
{
if (n == value_) return;
if (mutator)
mutator (n);
else
setValueRaw (n);
}
void
setValueRaw (NUM newVal)
{
if (newVal != value_)
{
value_ = newVal;
buffer_.clear();
}
}
//---Supporting-increments--------------
Digxel& operator+= (NUM inc) { *this = value_ + inc; return *this; }
Digxel& operator-= (NUM dec) { *this = value_ - dec; return *this; }
Digxel& operator++ () { *this = value_ + 1; return *this; }
Digxel& operator-- () { *this = value_ - 1; return *this; }
NUM operator++ (int) { NUM p(value_); *this =p+1; return p;}
NUM operator-- (int) { NUM p(value_); *this =p-1; return p;}
//---Supporting-totally_ordered---------
bool operator< (Digxel const& o) const { return value_ < NUM(o); }
bool operator== (Digxel const& o) const { return value_ == NUM(o); }
};
/* == predefined Digxel configurations == */
typedef Digxel< int, digxel::SexaFormatter> SexaDigit; ///< for displaying time components (sexagesimal)
typedef Digxel<uint, digxel::HexaFormatter> HexaDigit; ///< for displaying a hex byte
typedef Digxel< int, digxel::HourFormatter> HourDigit; ///< for displaying hours in H:M.S
typedef int64_t FrameCnt;
typedef Digxel<FrameCnt, digxel::CountFormatter> CountVal; ///< for displaying a counter
/** special Digxel to show a sign.
* @note values limited to +1 and -1 */
class Signum
: public Digxel<int,digxel::SignFormatter>
{
typedef Digxel<int, digxel::SignFormatter> _Par;
void
storeSign (int val)
{
setValueRaw (val<0? -1:+1);
}
public:
Signum()
{
setValueRaw(1);
installMutator (&Signum::storeSign, *this);
}
using _Par::operator=;
friend int operator*= (Signum& s, int c) { s = c*s; return s; }
};
}} // lib::time
#endif