2011-01-04 03:50:33 +01:00
/*
TIMECODE . 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 0213 9 , 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
2011-01-06 03:45:29 +01:00
* * and is implicitly convertible back to the underlying numeric type ( usually int or double ) .
2011-01-04 03:50:33 +01:00
* *
* * 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 ,
2011-01-06 03:45:29 +01:00
* * 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 .
2011-01-04 03:50:33 +01:00
* *
2011-01-06 03:45:29 +01:00
* * 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 .
2011-01-04 03:50:33 +01:00
* *
* * \ 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"
2011-01-05 01:05:13 +01:00
# include "lib/symbol.hpp"
2011-01-05 05:14:28 +01:00
# include "lib/util.hpp"
2011-01-04 03:50:33 +01:00
2011-01-05 01:05:13 +01:00
# include <boost/operators.hpp>
# include <boost/lexical_cast.hpp>
2011-01-05 05:14:28 +01:00
# include <tr1/functional>
2011-01-04 03:50:33 +01:00
# include <string>
2011-03-31 18:43:50 +02:00
# include <cstdio>
2011-01-06 03:45:29 +01:00
# include <cmath>
using std : : string ;
2011-01-04 03:50:33 +01:00
namespace lib {
namespace time {
2011-01-04 20:56:47 +01:00
namespace digxel {
2011-01-05 05:14:28 +01:00
using util : : cStr ;
2011-01-05 01:05:13 +01:00
using lib : : Literal ;
using boost : : lexical_cast ;
2011-01-05 03:07:42 +01:00
typedef const char * CBuf ;
2011-01-05 01:05:13 +01:00
2011-01-06 03:45:29 +01:00
/**
* Default / base implementation for Digxel formatting .
* This formatter holds an inline buffer of limited size ,
* receiving and caching the textual representation
*/
2011-01-05 01:05:13 +01:00
template < typename NUM , size_t len >
class PrintfFormatter
{
enum { bufsiz = len + 1 } ;
2011-01-04 20:56:47 +01:00
char printbuffer_ [ bufsiz ] ;
2011-01-05 01:05:13 +01:00
Literal formatSpec_ ;
2011-01-04 20:56:47 +01:00
2011-01-05 01:05:13 +01:00
public :
PrintfFormatter ( Literal fmt )
: printbuffer_ ( )
, formatSpec_ ( fmt )
2011-01-05 05:14:28 +01:00
{
clear ( ) ;
2011-01-05 01:05:13 +01:00
}
2011-01-05 05:14:28 +01:00
void clear ( ) { printbuffer_ [ 0 ] = ' \0 ' ; }
2011-01-06 03:45:29 +01:00
bool empty ( ) { return ' \0 ' = = * printbuffer_ ; }
2011-01-05 05:14:28 +01:00
size_t
maxlen ( ) const
{
return len ;
}
CBuf
2011-01-04 20:56:47 +01:00
show ( NUM val )
{
2011-01-05 05:14:28 +01:00
if ( empty ( ) )
{
size_t space = std : : snprintf ( printbuffer_ , bufsiz , formatSpec_ , val ) ;
REQUIRE ( space < bufsiz , " Digxel value exceeded available buffer size. "
2011-03-31 18:43:50 +02:00
" For showing %s, %zu+1 chars instead of just %zu+1 would be required. "
2011-01-06 01:35:22 +01:00
, cStr ( lexical_cast < string > ( val ) ) , space , len ) ; ///////////TICKET #197
2011-01-05 05:14:28 +01:00
}
ENSURE ( ! empty ( ) ) ;
return printbuffer_ ;
2011-01-04 20:56:47 +01:00
}
} ;
2011-01-06 03:45:29 +01:00
/**
* default configured Formatter implementations
* for some of the basic numeric types
*/
2011-01-04 20:56:47 +01:00
template < typename NUM >
struct Formatter ;
template < >
struct Formatter < int >
2011-01-18 05:01:25 +01:00
: PrintfFormatter < int , 9 >
2011-01-04 20:56:47 +01:00
{
2011-01-18 05:01:25 +01:00
Formatter ( ) : PrintfFormatter < int , 9 > ( " %3d " ) { }
2011-01-05 01:05:13 +01:00
} ;
template < >
struct Formatter < double >
: PrintfFormatter < double , 7 >
{
Formatter ( ) : PrintfFormatter < double , 7 > ( " %06.3f " ) { }
2011-01-04 20:56:47 +01:00
} ;
2011-01-06 03:45:29 +01:00
/* == other specialised Formatters == */
struct SexaFormatter
: PrintfFormatter < int , 4 >
{
SexaFormatter ( ) : PrintfFormatter < int , 4 > ( " %02d " ) { }
} ;
struct HexaFormatter
: PrintfFormatter < uint , 2 >
{
HexaFormatter ( ) : PrintfFormatter < uint , 2 > ( " %02X " ) { }
} ;
2011-01-13 23:56:52 +01:00
struct CountFormatter
: PrintfFormatter < long , 20 >
{
2011-01-17 07:25:22 +01:00
CountFormatter ( ) : PrintfFormatter < long , 20 > ( " %04ld " ) { }
2011-01-13 23:56:52 +01:00
} ;
2011-01-06 03:45:29 +01:00
2011-01-21 16:22:01 +01:00
struct HourFormatter
: PrintfFormatter < int , 9 >
{
HourFormatter ( ) : PrintfFormatter < int , 9 > ( " %2d " ) { }
} ;
2011-01-18 04:59:40 +01:00
struct SignFormatter
{
void clear ( ) { }
size_t maxlen ( ) const { return 1 ; }
CBuf show ( int val ) { return val < 0 ? " - " : " " ; }
} ;
2011-01-05 03:07:42 +01:00
} //(End) digxel configuration namespace
2011-01-05 05:14:28 +01:00
2011-01-06 03:45:29 +01:00
2011-01-22 14:04:43 +01:00
using std : : tr1 : : bind ;
using std : : tr1 : : function ;
using std : : tr1 : : placeholders : : _1 ;
2011-01-05 03:07:42 +01:00
/**
* A number element for building structured numeric displays .
* The purpose is to represent parts of a numeric format , like
2011-01-06 03:45:29 +01:00
* e . g . the sexagesimal " digits " of a timecode display . A Digxel
2011-01-05 03:07:42 +01:00
* - is customised by template parameters to a specific number format
2011-01-06 03:45:29 +01:00
* - requires that any given number must not overflow the format buffer
2011-01-05 03:07:42 +01:00
* - can receive new numbers by assignment
2011-01-06 03:45:29 +01:00
* - stores and these given value numerically
2011-01-05 03:07:42 +01:00
* - will then format these numbers and cache the formatted representation
2011-01-06 03:45:29 +01:00
* - can store and invoke a mutation functor to pre - process values on setting
*
2011-01-05 03:07:42 +01:00
* @ note comparisons are assumed to be not performance relevant
2011-01-06 03:45:29 +01:00
* @ 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
2011-01-05 03:07:42 +01:00
* @ see lib : : time : : TCode
2011-01-06 03:45:29 +01:00
* @ see Digxel_test
*
2011-01-05 03:07:42 +01:00
*/
template < typename NUM
, class FMT = digxel : : Formatter < NUM >
>
class Digxel
2011-01-05 05:14:28 +01:00
: public boost : : totally_ordered < Digxel < NUM , FMT > >
2011-01-05 03:07:42 +01:00
{
2011-01-05 05:14:28 +01:00
mutable
FMT buffer_ ;
NUM value_ ;
2011-01-05 03:07:42 +01:00
2011-01-22 14:04:43 +01:00
typedef Digxel < NUM , FMT > _Digxel ;
typedef function < void ( NUM ) > _Mutator ;
2011-01-05 03:07:42 +01:00
2011-01-22 14:04:43 +01:00
_Mutator mutator ; ///< Functor for setting a new digxel value
2011-01-05 03:07:42 +01:00
public :
2011-01-22 14:04:43 +01:00
/** install an external functor to be applied on any new digxel value.
2011-01-05 05:14:28 +01:00
* This allows individual instances to limit the possible digxel values ,
* or to update an compound value ( e . g . a time comprised of hour , minute
2011-01-22 14:04:43 +01:00
* 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
2011-01-05 05:14:28 +01:00
*/
2011-01-22 14:04:43 +01:00
template < typename FUN , class THIS >
void
installMutator ( FUN mutate , THIS & self )
{
mutator = bind ( mutate , & self , _1 ) ;
}
2011-01-05 05:14:28 +01:00
Digxel ( )
: buffer_ ( )
2011-01-06 03:45:29 +01:00
, value_ ( )
2011-01-22 14:04:43 +01:00
, mutator ( )
2011-01-05 05:14:28 +01:00
{ }
// using the standard copy operations
operator NUM ( ) const { return value_ ; }
operator string ( ) const { return show ( ) ; }
size_t maxlen ( ) const { return buffer_ . maxlen ( ) ; }
2011-01-06 03:45:29 +01:00
2011-01-05 05:14:28 +01:00
digxel : : CBuf
show ( ) const
{
return buffer_ . show ( value_ ) ;
}
void
operator = ( NUM n )
{
2011-01-20 13:21:14 +01:00
if ( n = = value_ ) return ;
2011-01-22 14:04:43 +01:00
if ( mutator )
mutator ( n ) ;
else
setValueRaw ( n ) ;
2011-01-05 05:14:28 +01:00
}
2011-01-05 03:07:42 +01:00
2011-01-05 05:14:28 +01:00
void
setValueRaw ( NUM newVal )
{
if ( newVal ! = value_ )
{
value_ = newVal ;
buffer_ . clear ( ) ;
}
}
2011-01-05 03:07:42 +01:00
2011-01-18 05:01:25 +01:00
//---Supporting-increments--------------
2011-01-19 10:52:18 +01:00
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 ; }
2011-01-18 05:01:25 +01:00
2011-01-05 03:07:42 +01:00
//---Supporting-totally_ordered---------
bool operator < ( Digxel const & o ) const { return value_ < NUM ( o ) ; }
bool operator = = ( Digxel const & o ) const { return value_ = = NUM ( o ) ; }
} ;
2011-01-04 20:56:47 +01:00
2011-01-04 03:50:33 +01:00
2011-01-06 03:45:29 +01:00
/* == 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
2011-01-21 16:22:01 +01:00
typedef Digxel < int , digxel : : HourFormatter > HourDigit ; ///< for displaying hours in H:M.S
2013-11-09 02:35:18 +01:00
typedef Digxel < int64_t , digxel : : CountFormatter > CountVal ; ///< for displaying a counter ///////////TICKET #882 : should better use a typedef for frame counts
2011-01-18 04:59:40 +01:00
/** special Digxel to show a sign.
* @ note values limited to + 1 and - 1 */
2011-01-21 20:24:41 +01:00
class Signum
: public Digxel < int , digxel : : SignFormatter >
2011-01-18 04:59:40 +01:00
{
2011-01-22 14:04:43 +01:00
typedef Digxel < int , digxel : : SignFormatter > _Par ;
void
storeSign ( int val )
2011-01-21 20:24:41 +01:00
{
2011-01-22 14:04:43 +01:00
setValueRaw ( val < 0 ? - 1 : + 1 ) ;
2011-01-21 20:24:41 +01:00
}
public :
Signum ( )
{
setValueRaw ( 1 ) ;
2011-01-22 14:04:43 +01:00
installMutator ( & Signum : : storeSign , * this ) ;
2011-01-21 20:24:41 +01:00
}
2011-01-18 04:59:40 +01:00
2011-01-22 14:04:43 +01:00
using _Par : : operator = ;
2011-01-18 04:59:40 +01:00
2011-01-21 20:24:41 +01:00
friend int operator * = ( Signum & s , int c ) { s = c * s ; return s ; }
2011-01-18 04:59:40 +01:00
} ;
2011-01-06 03:45:29 +01:00
2011-01-04 03:50:33 +01:00
} } // lib::time
# endif