UNITS OF MEASUREMENT
FOR ADA

version 3.4
by Dmitry A. Kazakov

(mailbox@dmitry-kazakov.de)
[Home]

This library 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 library 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 library; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.

As a special exception, if other files instantiate generics from this unit, or you link this unit with other files to produce an executable, this unit does not by itself cause the resulting executable to be covered by the GNU General Public License. This exception does not however invalidate any other reasons why the executable file might be covered by the GNU Public License.


[TOC][Next]

Foreword

The need of support for handling measurement units does not require any approval. It is in particular true for the Ada programming language with its stress on safety and maintainability. The proposed set of packages represents a possible solution of the unit problem. Dimension checking is performed at run-time, so performance is sacrificed for safety. However, an attempt was made to reduce performance hit. The type Measure used to represent dimensioned values can be constrained to a specific unit. Operations on constrained subtypes of Measure could be potentially as efficient as floating-point operations. They are declared inlined, so that at least theoretically, a decent compiler could perform discriminant calculation and check at compile time.

The key features of the approach:

  • The type Unit denotes the dimension of a physical entity. The type Measure represents a dimensioned value;
  • Mixed unit arithmetic. Values in SI (Le Système International d'Unités) units can be mixed with irregular units (such as foot and yard) as long as the result is defined;
  • Shifted unit support (a widely used instance of a shifted unit is degree of Celsius);
  • The type Measure is generic, parameterized by a floating-point type. A non-generic version based on the type Float is also provided;
  • String to Measure conversion supports as wide set of irregular units as possible;
  • Currently the powers of the base unit components lie in the range -8..7. This limitation has an implementation reason (absence of 64-bit modular integers in some Ada compilers). For further discussion of this topic see;
  • GTK+ widgets for a comfortable visual measurement unit editing and selection. The rest of the library can be used independently on this part as well as on GTK+.

See also the changes log.

  Measures edit dialog

You also may wish to visit this site devoted to the problem of dimensioned values in Ada.

Download Units of Measurements for Ada Platform:   64- 32bit
Fedora packages Fedora   precompiled and packaged using RPM     [Download page] [Download page]
Debian packages Debian   precompiled and packaged for dpkg   [Download page] [Download page]
Source distribution (any platform)   units_3_4.tgz (tar + gzip, Windows users may use WinZip)   [Download]

The units converter and mapper are small utilities provided as exercises to illustrate use of the software. The unit converter is a small dialog box where you can enter a dimensioned value and then convert it to SI. The unit mapper is a dialog box that converts value from one unit to another as you type it. The source code is discussed in the section 3.2.

Units converter     
Download Units Converter Platform:   64- 32bit
Windows    units_converter.exe.gz built using Windows API (gzip compressed)      [Download]
Fedora Fedora   binary RPM package     [Download page] [Download page]
Debian Debian   binary package   [Download page] [Download page]

Download Units Mapper
     
Windows   units_mapper.exe.gz built using GTK+ API (gzip compressed)     [Download]
Fedora Fedora   binary RPM package   [Download page] [Download page]
Debian Debian   binary package   [Download page] [Download page]

[Back][TOC][Next]

1. Types

Two types are used for dealing with units. The type Unit denotes the dimension of a physical entity. The type Measure represents a dimensioned value it is defined in a generic package Measures which is instantiated with the desired floating-point type as the parameter:

generic
   type Number is digits <>;
package Measures is
   ...

The package Float_Measures is an instance of Measures for the standard Float type: .

[Back][TOC][Next]

1.1. The type Unit

The type Unit is defined in the package Units. A value of the type Unit corresponds to a kind of physical entity, like energy, charge or velocity. The package itself is rather useless, because there are few things one can do with units. They are:

Or in Ada terms the following functions are defined:

function "**" (Left : Unit; Right : Integer) return Unit;
function "*"  (Left, Right : Unit) return Unit;
function "/"  (Left, Right : Unit) return Unit;
function Sqrt (X : Unit) return Unit;

Values of the type Unit build a group. A value of Unit type can be viewed as an unordered set of seven base components (mass, length, time, current etc.). Each component has integer exponent part. The function Sqrt is defined for only units which components have even exponent parts. The exception Constraint_Error is propagated when the result of an operation is illegal.

The package also declares the exception Unit_Error and the type:

type Code_Set is (ASCII_Set, Latin1_Set, UTF8_Set);

and the procedure:

procedure Split
          (  SI           : Unit;
             Current      : out Natural;
             Luminescence : out Natural;
             Temperature  : out Natural;
             Mass         : out Natural;
             Length       : out Natural;
             Quantity     : out Natural;
             Time         : out Natural
          );

The procedure returns base unit powers of the argument SI.

1.1.1. Base units

The child package Units.Base defines constants for the base SI units:

Current       : constant Unit;
Luminescence  : constant Unit;
Temperature   : constant Unit;
Mass          : constant Unit;
Length        : constant Unit;
Quantity      : constant Unit;
Time          : constant Unit;
Unitless      : constant Unit;

1.1.2. Unit constants

The child package Units.Constants defines some of more complex units. Geometric units are:

Area          : constant Unit := Length ** 2;
Volume        : constant Unit := Length ** 3;

Units used in mechanics:

Velocity      : constant Unit := Length / Time;
Acceleration  : constant Unit := Length / Time ** 2;
Force         : constant Unit := Mass * Acceleration;
Pressure      : constant Unit := Force / Area;
Energy        : constant Unit := Force * Length;
Power         : constant Unit := Energy / Time;

Electricity units:

Charge        : constant Unit := Current * Time;
Potential     : constant Unit := Energy / Charge;
Capacitance   : constant Unit := Charge / Potential;
Resistance    : constant Unit := Potential / Current;
Conductance   : constant Unit := Current / Potential;
Inductance    : constant Unit := Potential * Time / Current;

Chemistry units:

Concentration : constant Unit := Quantity / Volume;
Density       : constant Unit := Mass / Volume;

Optic units:

Luminance     : constant Unit := Luminescence / Area;

Other units:

Frequency     : constant Unit := Unitless / Time;

1.1.3. Conversion to ASCII and Latin-1 strings

The package Units.Edit provides the function Image is used to convert Unit to String:

function Image
         (  Value  : Unit;
            Latin1 : Boolean := True
         )  return String;

The syntax of the result string is:

<result>  ::=  <list>/<list> | 1/<list> | <list> | 1
<list> ::= <item> [<*><list>]
<item> ::= <base-unit>[<power>]
<base-unit> ::= A | cd | K | kg | m | mol | s

Here <*> is either * (if Latin1 is false), or · from Latin-1 character set. When Latin1 is false <power> is always ^<number>. With Latin-1 character set enabled, powers 2 and 3 are indicated using the corresponding superscript characters: 2 and 3. The following table lists the Latin-1 characters used for output:

Special characters
Character Latin-1 Meaning
2 B216 Power 2
3 B316 Power 3
· B716 Multiplication operator

1.1.4. Conversion to UTF-8 encoded strings

The package Units.UTF8_Edit provides the function Image used to convert Unit to an UTF-8 encoded string:

function Image (Value : Unit) return String;

The syntax of the result string is:

<result>  ::=  <list>/<list> | <list> | 1
<list> ::= <item> [·<list>]
<item> ::= <base-unit>[<power>]
<base-unit> ::= A | cd | K | kg | m | mol | s
<power> ::= [-]<number>
<number> ::= <digit>[<number>]
<digit> ::= 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9

Powers are output as superscript digits. When all powers are negative, the output uses no fraction but negative powers instead. For example: m-2 instead of 1/m2. The following table summarizes the list of special characters and their encodings in UTF-8:

Special characters
Character UTF-8 encoding Code point Meaning
2 C216 B216 00B216 Power 2
3 C216 B316 00B316 Power 3
4 E216 8116 B416 207416 Power 4
5 E216 8116 B516 207516 Power 5
6 E216 8116 B616 207616 Power 6
7 E216 8116 B716 207716 Power 7
8 E216 8116 B816 207816 Power 8
9 E216 8116 B916 207916 Power 9
- E216 8116 BB16 207B16 Power -
· C216 B716 00B716 Multiplication operator

1.1.5. Design notes

Some words about the design. I didn't make the type Unit private because then it could not be used as a discriminant of the type Measure (see the package Measures). To have Measure discriminated seems to be very important. It allows to define subtypes of Measure with fixed Unit component. For the same reason and for performance sake too, Unit is a modular number. As the result it inherits a number of operations which are disallowed by making them abstract. In the current implementation the type Interfaces.Unsigned_32 is used. This determines the range of the exponent part of a base component as -8..7. With Interfaces.Unsigned_64 the range would be -512..511. The following fragment of the file units.ads can be modified if the compiler supports modular numbers larger than Interfaces.Unsigned_32:

with Interfaces;

package Units is
   --
   -- Here we define the type used to represent units. It should be a
   -- modular type with a defined operation Shift_Right.
   --

   subtype UnitPower is Interfaces.Unsigned_n;
   . . .

Rational powers are not allowed, because the choice of base units in SI ensures whole powers of all physical values. Examples like Schottky-Langmuir equation can be easily written in a correct form with whole powers. Often it is argued that rational powers might be useful for dealing with intermediate results. A simple example x = ln ex shows that in general case it cannot be done anyway.

[Back][TOC][Next]

1.2. The type Measure

A value of the type Measure (defined in the package Measures) represents some quantity of a physical unit:

type Measure (SI : Unit := Units.Base.Unitless) is record
   Gain   : Number;
   Offset : Number'Base := 0.0;
end record;

Measures can be added, subtracted, multiplied, divided and exponentiated. The type Measure is a discriminated record type. The discriminant SI has the type Unit. Its value defines the dimension of a Measure. Unconstrained instances of Measure can be declared as:

Entity : Measure;      -- May hold value of any measurement unit
. . .
Entity := 5.0 * A / s; -- Set value 5 A/s
Entity := 3.0 * km;    -- Set value 3.0 km
Entity := Entity + s;  -- Illegal (km + s), Unit_Error

It is possible however, to create constrained subtypes of Measure capable to hold values of only a specific dimension. For instance:

subtype Speed is Measure (Velocity);
Car_Speed : Speed;          -- Only velocities are allowed
. . .
Car_Speed := 10.0 * km / h; -- OK
Car_Speed := A;             -- Illegal, Constraint_Error

The type Measure has two components: Gain and Offset. To get a SI equivalent of the value one should sum Gain and Offset. This is what the function Get_Value actually does. For instance:

Meter : Measure := (Length, Gain => 1.0,    Offset => 0.0);
Foot  : Measure := (Length, Gain => 0.3048, Offset => 0.0);

The subtype Dimensionless for dimensionless measures.

1.2.1. Shifted and unshifted Measures

The field Offset of the type Measure defines the value shift. Measures with zero offset are called unshifted. They form a field with addition and multiplication operations:

Meter : Measure := (Length, Gain => 1.0,    Offset => 0.0);
Foot  : Measure := (Length, Gain => 0.3048, Offset => 0.0);
. . .
Meter * Foot -- 0.3048 square meters
Meter + Foot -- 1.3048 m
Meter ** 2   -- 1 square meter

Most of physical units and all SI units are unshifted. Shifted measurement units are rare. Degrees of Celsius and Fahrenheit is an example of shifted units. The degree of Celsius can be defined as:

Celsius : Measure := (Temperature, Gain => 1.0, Offset => 273.15);

Actually, the field Offset should also be a discriminant of the type Measure. Unfortunately it is illegal in Ada. Measures of same dimension and shift form a set closed relatively addition, subtraction and multiplication to a number (i.e. a group). Therefore:

5.0 * Celsius     -- Legal, 5ºC = 278.15 K
Celsius + Celsius -- Legal, 2ºC = 275.15 K
Celsius * Celsius -- Illegal, Unit_Error is propagated

In other words, shifted measures can be neither multiplied nor divided, except when a shifted measure is multiplied to or divided by a dimensionless unshifted value as in the first example above. Note that addition and subtraction are only legal when dimension and shift are same for both operands:

Kelvin  : Measure := (Temperature, Gain => 1.0, Offset => 0.0);
Celsius : Measure := (Temperature, Gain => 1.0, Offset => 273.15);
. . .
Celsius + Kelvin -- Illegal, Unit_Error is propagated
Celsius * Kelvin -- Illegal, Unit_Error is propagated

Mixing of differently shifted measures is ambiguous. In the given example Celsius and Kelvin could be added if one would be converted to another:

Convert (Celsius, Kelvin) + Kelvin  -- +275.15 K (unshifted) = 2ºC
Celsius + Convert (Kelvin, Celsius) -- -271.15ºC (shifted)   = 2 K

Note that the numeric equivalent of the result depends on whether Celsius is converted to Kelvin or inversely. Even if two zeros (0ºC and 0 K) are added the results will differ:

Convert (0.0 * Celsius, Kelvin) + 0.0 * Kelvin  -- +273.15 K (0ºC)
0.0 * Celsius + Convert (0.0 * Kelvin, Celsius) -- -273.15ºC (0 K)

1.2.2. Operations defined on Measures

Unary operations:

function "abs" (Right : Measure) return Measure;
function "+"   (Right : Measure) return Measure;
function "-"   (Right : Measure) return Measure;

Exponentiation:

function "**" (Left : Measure; Right : Integer) return Measure;

Multiplication:

function "*" (Left : Number'Base; Right : Measure) return Measure;
function "*" (Left : Measure; Right : Number'Base) return Measure;
function "*" (Left, Right : Measure)               return Measure;

Division:

function "/" (Left : Number'Base; Right : Measure) return Measure;
function "/" (Left : Measure; Right : Number'Base) return Measure;
function "/" (Left, Right : Measure)               return Measure;

Addition and subtraction:

function "+" (Left, Right : Measure) return Measure;
function "-" (Left, Right : Measure) return Measure;

Comparisons:

function ">"  (Left, Right : Measure) return Boolean;
function "<"  (Left, Right : Measure) return Boolean;
function "="  (Left, Right : Measure) return Boolean;
function ">=" (Left, Right : Measure) return Boolean;
function "<=" (Left, Right : Measure) return Boolean;

The equality operator can be applied to any pair of measures. The inequality operation is implicitly defined by the compiler.

Scale shift (value destructive):

function "and" (Left : Measure; Right : Number'Base) return Measure;

The result of scale shift is formed by adding the value of the parameter Right to the field Offset. Note that the numeric SI equivalent of the result will differ from one of the parameter Left. For instance:

Celsius : constant Measure := K and 273.15; -- 1ºC (is not equal to 1 K)

See also the functions Normalize and Shift which perform scale shifts retaining the value.

The exception Constraint_Error is raised by the operations when the corresponding numeric operation does. This behavior depends on whether Number’Machine_Overflows is true, as Ada Reference Manual states in 4.5.5 (22). Also Constraint_Error is propagated out of multiplicative operations "*", "/" and "**" when the dimension of the result cannot be represented as Unit because of a base unit power overflow.

1.2.3. Unit conversions

The function Get_Value returns a SI equivalent of the argument:

function Get_Value (Value : Measure) return Number;

The function Get_Value_As returns a numeric equivalent of the first argument in the measurement units defined by the second argument:

function Get_Value_As (Value, Scale : Measure) return Number;

For instance Get_Value_As (T, Celsius) returns the value of T in degrees of Celsius. The arguments Value and Scale should have same units. Otherwise the exception Unit_Error is propagated.

function Normalize (Value : Measure) return Measure;

The function Normalize returns an unshifted measure with the numeric equivalent same as one of the argument. For example:

Zero : Measure := Normalize (0.0 * Celsius); -- 273.15 K (unshifted)

Here 0ºC is converted to its unshifted equivalent 273.15 K.

function Shift (Value : Measure, Shift : Number'Base) return Measure;

The function Shift returns the argument shifted to Shift items. Unlike the operation and, the numeric equivalent of the result is same as one of the first argument. The value of the parameter Shift is subtracted from the field Gain and added to the field Offset to produce the result:

Zero : Measure := Shift (0.0 * Celsius, -273.15); -- 273.15 K

Here 0ºC is converted to its unshifted equivalent 273.15 K. Note that Normalize (X) is equivalent to Shift (X, X.Offset).

function Convert (Value, Scale : Measure) return Measure;

The function Convert is used for measurement unit conversions. The value of the first argument is converted to the measurement units of the second argument. The arguments Value and Scale should have same dimension. Otherwise the exception Unit_Error is propagated. Convert (X, Y) is an equivalent of Shift (X, Y.Offset - X.Offset). This is null operation if both arguments have same Offsets. Otherwise, the result has the shift of the second argument.

function To_Measure (Value : Number) return Measure;

This function returns a dimensionless unshifted measure corresponding to Value.

1.2.4. Constants

Few constants of the type Measure are defined in the package Measures. They correspond to the base SI measurement units: 

Base SI constants
Constant The base SI unit
A Ampere
cd Candela
K Kelvin
kg Kilogram
m Meter
mol Mole
s Second

and dimensionless SI units:

Dimensionless constants
Constant Meaning
Np Neper*
rad Radian, plane angle
sr Steradian, solid angle
* Neper is not a SI unit. It is put here to have a constant for 1 SI other than rad. Actually Np = rad = sr

The generic package Measures_Derived defines the constants corresponding to the derived SI measurement units:

generic
   with package
Measures is new Standard.Measures (<>);
package
Measures_Derived is ...

The package defines the following constants:

Constants corresponding to the derived SI units
Constant Unit Comment
C Coulomb  
Bq Becquerel  
F Farad  
Gy Gray  
Henry Henry The full name Henry is used instead of H to reserve h for hour, which being a non-SI unit would be still more important for many than Henry
Hz Hertz  
J Joule  
kat katal Catalytic activity
lm Lumen  
lx Lux  
N Newton  
Ohm Ohm The short name (Greek omega Ω) does not belong to the Latin-1 character set
Pa Pascal  
Siemens Siemens The SI name S would conflict with s (seconds) defined in the package Measures. Therefore the full name is used instead
Sv Sievert  
Tesla Tesla The short name t is reserved for metric ton
V Volt  
W Watt  
Wb Weber  

The package Float_Measures_Derived is an instance of Measures_Derived for the standard type Float.

Further constants are defined in the generic package Measures_Irregular:

generic
   with package
Derived_Measures is new Measures_Derived (<>);
package
Measures_Irregular is ...

The package provides constants corresponding to various irregular units: 

Irregular units constants
Constant Unit of measurement Definition
acre Acre 43_560 ft2 [2]
ang Ångström 1 nm [2, 5]
are Are 100 m2 [5]
atm Athmosphere 101_325 Pa
B Bel
1  ln10  [5]
2
BTU British thermal unit, international table 1_055.056 J [6]
bar Bar 105 Pa [5]
barleycorn Barleycorn
1  inch  [3]
3
barn Barn 1 fm2 [3]
barrel Barrel, crude oil or petroleum 42 gal [2]
Ci Curie 3.7·1010 Bq [5]
cal Calorie, international 4.186_8 J [6]
carat Carat 200 mg [2]
Celsius Degree of Celsius 1 K shifted by 273.15K [4, 6]
ch Chain 66 ft [2]
cubit Cubit 18 inch [3]
d Day 60·60·24 s [5]
dB Decibel 5 ln10 [5]
degree Degree (plane angle) 2π/360 [5]
dram Dram, international avoirdupois
1  lb  [2]
256
dyn Dyn 10-5 N [6]
eV Electronvolt 1.602_18·10-19  J [5]
ell Ell 45 inch [3]
erg Erg 10-7 J [6]
Fahrenheit Degree of Fahrenheit
1  K shifted by 459.67K [6]
1.8
fathom Fathom 6 ft [2]
finger Finger 41/2 inch
ft Foot, international 0.304_8 m = 1/3 yd [2]
fpm Feet per minute 1 ft/min
fps Feet per second 1 ft/s
fur Furlong 660 ft [2]
G Gauss 10-4 T [6]
gal Gallon, U.S. liquid gallon 231 inch3 [2]
gi Gill, liquid
1  gal  [2]
32
grain Grain 0.000_064_798_91 kg [1]
gram Gram. The SI unit for mass is kilogram 0.001 kg
h Hour 3_600 s [5]
hand Hand 4 inch [2]
hectare Hectare 102 are [5]
hp Horsepower, metric 735.498_8 W [6]
INM International nautical mile 1852 m [1, 5]
inch Inch 1/12 ft [2]
kcal Kilocalorie 103 cal
kgf Kilogram-force 9.806_65 N [6]
knot Knot 1 INM/h [5]
L Liter 1 dm3 [5]
league League 3 mi [2]
lb Pound, international avoirdupois 0.453_592_37 [1]
line Line
1  inch  [3]
12
link Link 0.66 ft [2]
ly Light year 9.460_73 1015 m [6]
mi Mile, international 5_280 ft [2]
min Minute 60 s [5]
min_of_arc Minute of arc 2π/(360·60) [5]
mpg Miles per gallon, international mile per U.S. liquid gallon 1 mi/gal
mph Miles per hour, international mile per hour 1 mi/h
mps Miles per second, international mile per second 1 mi/s
nail Nail 21/4 inch [3]
Oe Oersted
1_000  A/m  [6]
oz Ounce, international avoirdupois
1  lb  [2]
16
pace Pace 30 inch [3]
pc Parsec 3.085_678 1016 m [6]
percent Percent 10-2
point Point 0.013_837 inch [2]
ppb Parts per billion 10-9
ppm Parts per million 10-6
ppt Parts per trillion 10-12
psi Pounds per square inch 1 lb/inch2
pt Pint, liquid
1  gal  [2]
8
qt Quart, liquid
1  gal  [2]
4
R Roentgen 2.58·10-4 C/kg [5]
rd Rod 16.5 ft [2]
rood Rood, square furlong 1 fur2
rpm Revolutions per minute 2π min-1
rps Revolutions per second 2π s-1
sec_of_arc Second of arc 2π/(360·60·60) [5]
span Span 9 inch [3]
t Metric ton Mg [5]
tablespoon Table spoon
1  gi  [2]
8
teaspoon Tea spoon
1  tablespoon  [2]
3
torr Torr (mmHg)
101_325  Pa  [6]
760
township Township 36 mi2 [2]
u Unified atomic mass 1.660_54·10-27 kg [5]
ua Astronomical unit 1.495_98·1011 [5]
wineglass Wine glass
1  pt = 4 fluid ounces  [3]
4
yd Yard, international 0.914_4 m [1]
year Year, tropical 3.155_693 107 [6]
1 National Bureau of Standards: "Refinement of Values for the Yard and the Pound.", July 1, 1959
2 National Institute of Standards and Technology: "General Tables of Units of Measurement"
3 Webster's Third New International Dictionary
4 National Institute of Standards and Technology: "The NIST Reference on Constants, Units, and Uncertainty. SI Units"
5 National Institute of Standards and Technology: "The NIST Reference on Constants, Units, and Uncertainty. Units outside the SI"
6 National Institute of Standards and Technology: "Guide for the Use of the International System of Units (SI)"

The package Float_Measures_Irregular is an instance of Measures_Irregular for the type Float.

1.2.5. Elementary functions

The generic package Measures_Elementary_Functions

generic
   type
Number is digits <>;
   with package
The_Measures is new Measures (Number);
package
Measures_Elementary_Functions is
   ...

provides the following functions:

function Sqrt (X : Measure) return Measure;

The function Sqrt is defined for the measures which unit has components with even exponent part. The exception Unit_Error is propagated otherwise. For instance:

Area : Measure := 25.0 * m**2;
Side : Measure;
. . .
Side := Sqrt (Area); -- OK, the result is 5 m
Side := Sqrt (Side); -- Error, Unit_Error propagates

Exponent and log functions require a dimensionless argument. The result is also dimensionless. Note that integer exponentiation is defined for all unshifted measures.

function Exp (X : Dimensionless) return Dimensionless;
function Log (X : Dimensionless) return Dimensionless;
function Log (X : Dimensionless; Base : Number'Base)
   return Dimensionless;

function "**" (Left : Dimensionless; Right : Dimensionless)
   return Dimensionless;
function "**" (Left : Dimensionless; Right : Number'Base)
   return Dimensionless;
function "**" (Left : Number'Base; Right : Dimensionless)
   return Dimensionless;

The trigonometric functions:

function Sin (X : Dimensionless) return Dimensionless;
function Cos (X : Dimensionless) return Dimensionless;
function Tan (X : Dimensionless) return Dimensionless;
function Cot (X : Dimensionless) return Dimensionless;

The argument of trigonometric functions is measured in radians (dimensionless). No variants with the parameter Cycle as in Ada.Numerics.Generic_Elementary_Functions are necessary, because arguments in degrees can be naturally expressed using units:

Cos (180.0 * degree); -- degree is declared in Measures_Irregular
Cos (3.1415 * rad);

The inverse trigonometric functions return the result measured in radians:

function Arcsin (X : Dimensionless) return Dimensionless;
function Arccos (X : Dimensionless) return Dimensionless;
function Arctan (X : Dimensionless) return Dimensionless;
function Arccot (X : Dimensionless) return Dimensionless;
function Arctan (Y, X : Measure) return Dimensionless;
function Arccot (X, Y : Measure) return Dimensionless;

Arctan and Arccot are defined for any pair of compatible unshifted measures (Unit_Error is propagated otherwise). For instance:

subtype Height is Measure (Length);
subtype Width is Measure (Length);
X : Width;
Y : Height;
Angle : Dimensionless; -- Radians
. . .
X := 25.0 * m;
Y := 30.1 * ft; -- ft (foot) is declared in Measures_Irregular
Angle := Arctan (Y, X);

Hyperbolical functions are defined on dimensionless argument:

function Sinh (X : Dimensionless) return Dimensionless;
function Cosh (X : Dimensionless) return Dimensionless;
function Tanh (X : Dimensionless) return Dimensionless;
function Coth (X : Dimensionless) return Dimensionless;

function Arcsinh (X : Dimensionless) return Dimensionless;
function Arccosh (X : Dimensionless) return Dimensionless;
function Arctanh (X : Dimensionless) return Dimensionless;
function Arccoth (X : Dimensionless) return Dimensionless;

The package instance for the type Float is named Float_Measures_Elementary_Functions.

1.2.6. Conversion from String

There are two encoding-specific generic packages responsible conversions of Measure to and from strings and one universal generic package for handling any encoding. The package Measures_Edit and the package Measures_UTF8_Edit are encoding-specific. Both packages have same formal parameters:

generic
   with package
Irregular_Measures is new Measures_Irregular (<>);
   with package Float_Edit is
      new
Strings_Edit.Float_Edit (Irregular_Measures.Measures_Of.Number);
package Measures_[UTF8_]Edit is ...

The package Measures_Edit is used for dealing with strings encoded in either ASCII or Latin-1 character sets. The package Measures_UTF8_Edit is used for the strings encoded in UTF-8 (see Unicode Transformation Format). The package Measures_UTF8_Edit instantiates Measures_Universal_Edit in its public part under the name Universal_Edit.

The routines provided by both packages are almost identical and differ only the sets of symbols supported. (The packages Float_Measures_Edit and Float_Measures_UTF8_Edit are instantiations of Measures_Edit and Measures_UTF8_Edit for the standard type Float.)

The following subroutines are used for measures input:

procedure Measures_Edit.Get
          (  Source  : String;
             Pointer : in out Integer;
             Value   : out Measure;
             Latin1  : Boolean := True
          );
procedure Measures_UTF8_Edit.Get
          (  Source  : String;
             Pointer : in out Integer;
             Value   : out Measure
          );

The procedures Get input a measure from the string Source. The process starts from the Source (Pointer) position. After successful completion Pointer is advanced to the position following the measure taken from the string. The parameter Value accepts the measure. The flag Latin1 of Measures_Edit.Get indicates Latin-1 character set support. With Latin1 = true the set of recognized symbols is extended as shown in the table below. The variant Measures_UTF8_Edit.Get is used for UTF-8 encoded strings. It supports an even larger set of symbols:

Special characters
Character Latim-1 UTF-8 Code point Meaning
0 - E216 8116 B016 207016 Power 0
1 B916 C216 B916 00B916 Power 1
2 B216 C216 B216 00B216 Power 2
3 B316 C216 B316 00B316 Power 3
4 - E216 8116 B416 207416 Power 4
5 - E216 8116 B516 207516 Power 5
6 - E216 8116 B616 207616 Power 6
7 - E216 8116 B716 207716 Power 7
8 - E216 8116 B816 207816 Power 8
9 - E216 8116 B916 207916 Power 9
+ - E216 8116 BA16 207A16 Power +
- - E216 8116 BB16 207B16 Power -
· B716 C216 B716 00B716 Multiplication operator
° B016 C216 B016 00B016 Degree, also in °C, °F etc
µ B516 C216 B516 00B516 Micro
CE16 BC16 03BC16
°C - E216 8416 8316 210316 Celsius (one letter sign)
°F - E216 8416 8916 210916 Fahrenheit (one letter sign)
- E216 8416 A516 212516 Ounce sign
K - E216 8416 AA16 212A16 K (Kelvin, one letter sign)
Å - E216 8416 AB16 212B16 Ångström (one letter sign)
Å C516 C316 8516 00C516 Å in Ångström
å E516 C316 A516 00E516 Small Å in Ångström
ö F616 C316 B616 00F616 Small O-umlaut in Ångström
Ω - CE16 A916 03A916 Ohm
E216 8416 A616 212616

The measure syntax is:

<measure>  ::=  ( <measure> )
<measure> ::= <measure> [<dyadic-operation>] <measure>
<measure> ::= <prefix-operation><measure>
<measure> ::= <measure><postfix-operation>
<measure> ::= <number>
<measure> ::= <unit>
<dyadic-operation> ::= ** | ^ | * | · | / | + | - | and
<prefix-operation> ::= + | -
<postfix-operation>  ::= <superscript-integer>

Space and tabs can be used as delimiters. Two consequent lexemes shall be separated by at least one delimiter if the first one ends with a letter while the second starts with a letter. For instance: 

As   Illegal, there is no any delimiter between A and s
A s OK, A*s
A·s OK, A*s
5A OK, 5*A

The exponentiation operation (** or ^) has the highest priority. Then the multiplication (*, · or empty) follows. The division (/) has lower priority than the multiplication, therefore kg/m*s is an equivalent to kg/(m*s). The lowest priority has the shift (and). The right argument of the exponentiation operation should be dimensionless and have zero fraction, i.e. be convertible to an integer without a precision loss. <superscript-integer> is recognized only in UTF-8 variant and when Latin1 = true, but then only the powers 1, 2 and 3 are supported. <unit> is a name denoting a measurement unit, such as foot: 

<unit>  ::=  <short-regular-unit> | <full-regular-unit> | <irregular-unit>
<short-regular-unit> ::= [<short-SI-prefix>] <short-unit-name>
<full-regular-unit> ::= [<full-SI-prefix>] <full-unit-name>

The following table defines <short-unit-name> and <full-unit-name>. Beware, all names are case sensitive:

Regular unit names (used with SI prefixes)
Short
name
Full name(s) Comments
A ampere  
  bar  
B bel  
barn barn  
Bq becquerel  
C coulomb  
°C   In Latin-1 and UTF-8 encoded strings a combination of ring (00B016) and the capital letter C can be used. In UTF-8 the degree Celsius (210316) is also supported
cd candela  
Ci curie  
erg erg  
F farad  
G gauss  
g gram, grams, gramme, grammes  
Gy gray  
H henry  
Hz hertz  
J joule  
K kelvin In UTF-8 encoded strings, Kelvin sign (212A16) is recognized as well
kat katal  
L, l liter, liters, litre, litres  
lm lumen  
lx lux  
m meter, meters, metre, metres  
mol mole  
N newton  
Ω ohm, Ohm In UTF-8 encoded strings both Unicode Greek omega (03A916) and the Ohm (212616) sign are recognized
Pa pascal  
R roentgen  
rad radian  
S siemens  
s second, seconds  
sr steradian  
Sv sievert  
T tesla  
t ton, tons, tonne, tonnes  
V volt  
W watt  
Wb weber  

Any regular unit name can be used with a SI prefix. The following table defines <short-SI-prefix> and <full-SI-name> (all names are case sensitive):

SI prefixes
Short Full Multiplicand Comments
Y yotta 1024  
Z zetta 1021  
E exa 1018  
P petta 1015  
T tera 1012  
G giga 109  
M mega 106  
k kilo 103  
h hecto 102  
da deka 101  
d deci 10-1  
c centi 10-2  
m milli 10-3  
µ micro 10-6 This prefix (00B516) is only recognized in Latin-1 and UTF-8. The latter additionally does Greek mu (03BC16)
n nano 10-9  
p pico 10-12  
f femto 10-15  
a atto 10-18  
z zepto 10-21  
y yocto 10-24  

Note that short prefixes are used with short names, full prefixes are used with full names. For instance, the only legal notations of km are: km, kilometer, kilometre, kilometers, kilometres. The irregular unit names (<irregular-unit>) cannot be used with SI prefixes. They are (all names are case sensitive):

Irregular unit names
Name(s) Meaning Comments
% Percent  
' Minute of arc  
" Second of arc  
°, degree, degrees Degree Ring (00B016) is recognized only In Latin-1 and UTF-8 encoded strings 
°F, Fahrenheit Degree of Fahrenheit In Latin-1 and UTF-8 encoded strings a combination of ring (00B016) and the capital letter F can be used. Additionally, in UTF-8 degree Fahrenheit is recognized (210916)
°K, Kelvin Degree of Kelvin In Latin-1 and UTF-8 encoded strings a combination of ring (00B016) and the capital letter K can be used. Names K and kelvin are regular (can be used with a SI prefix). In UTF-8 Kelvin sign (212A16) is also regular.
Å, Ångström, ångström Ångström Letters with accents are supported only in Latin-1 and UTF-8. Additionally in UTF-8 the angstrom sign (212B16) is recognized
a., acre, acres Acre  
are, ares Are  
atm, atmosphere, atmospheres Atmosphere  
barleycorn, barleycorns Barleycorn  
bbl, barrel, barrels Barrel  
BTU, Btu, btu British thermal unit  
c, carat, carats Carat  
cal, calorie, calories Calorie  
Celsius Degree of Celsius The short form of Celsius degree (°C) is regular and can be used with short SI prefixes. Therefore, the expression m°C is legal and means one thousandth part of °C i.e. (0.001 + 273.15) K.
ch, chain, chains Chain  
cubit, cubits Cubit  
d, day, days Day  
dr, dram, drams Dram  
dyn, dyne Dyne  
ell, ells Ell  
eV Electronvolt  
f, fathom, fathoms Fathom  
foot, feet Foot Note that the commonly used abbreviation ft conflicts with metric ton: ft=femtoton=10-12kg
finger, fingers Finger  
fpm Feet per minute  
fps Feet per second  
fur, furlong, furlongs Furlong  
gal, gallon, gallons Gallon  
gi, gill, gills Gill  
grain, grains Grain  
h, hour, hours Hour  
hand, hands Hand  
hectare, hectares Hectare  
hp, horsepower Horsepower  
in., inch, inches Inch  
INM International Nautical Mile  
Kcal, kcal Kilocalorie  
kgf, kilogram-force Kilogram-force  
knot, knots Knot  
lb, pound, pounds Pound  
league, leagues League  
line, lines Line  
link, links Link  
liqpt, liquidpint Liquid pint  
ly, lightyear, lightyears Lightyear  
mi, mile, miles Mile  
min, minute, minutes Minute  
mmHg Torr  
mpg Miles per gallon  
mph Miles per hour  
mps Miles per second  
nail, nails Nail  
Oe, oersted Oersted  
oz, ounce, ounces Ounce Additionally, in UTF-8 ounce sign is recognized (021D16)
pace, paces Pace  
pc, parsec, parsecs Parsec  
point, points Point  
ppb Parts per billion  
ppm Parts per million  
ppt Parts per trillion  
PSI, psi Pounds per square inch  
pt, pint, pints Pint  
qt, quart, quarts Quart  
rd, rod, rods Rod  
rood, roods Rood  
rpm Revolutions per minute  
rps Revolutions per second  
sec Second Names s, second and seconds are regular (can be used with a SI prefix)
span, spans Span  
tablespoon, tablespoons Tablespoon  
teaspoon, teaspoons Teaspoon  
torr Torr  
township, townships Township  
u Unified atomic mass  
ua Astronomical unit  
wineglass, wineglasses Wineglass  
yd, yard, yards Yard  
year, years Year  

Examples:

34.5 * mm    
65·km/h   65 km/h
65km/h   65 km/h
K and 273.15   degree Celsius
yd^2   one square yard
lb·yd²/s²   use of Latin-1 superscripts
Exceptions
Constraint_Error Numeric error in unit expression
Data_Error Syntax error
End_Error There is no measure in the string
Layout_Error The value of Pointer is not in the range Source'First..Source'Last+1 
Unit_Error Illegal unit expression (like m/°C)

procedure Measures_Edit.Get
          (  Source  : String;
             Pointer : in out Integer;
             Value   : out Scaled;
             Latin1  : Boolean := True
          );
procedure Measures_UTF8_Edit.Get
          (  Source  : String;
             Pointer : in out Integer;
             Value   : out Scaled
          );

These procedures input a measure in the form of a numeral multiplied by dimensioned scale. The syntax of measure and the parameters except Value are identical to corresponding procedures Get. The parameter Value has the type Scaled declared in the package Measures as follows:

type Value_Format is (Scalar, Numeric, Canonic, Jumbled);
type
Scaled (Format : Value_Format := Canonic) is record
   Numeral : Number'Base;
   Scale   : Measure;
end record;

The input may fall into one of the following categories:

An input is Numeric or Canonic when the expression's outermost dyadic operation is either multiplication or division and its left argument is a number. Examples:

Scalar values:

    Numeral
2**2 + 1   5
45   45

Numeric values:

    Numeral        Scale
10*4   10   4
2 / 4 / 2   2   0.125 = (1/4)/2

Canonic values:

    Numeral        Scale
-34.5 * mm   -34.5   mm
3/s   3   Hz
10*4 feet   10   4 feet
1*(4 and 3)   1   4 shifted by 3
(2**2 + 1) m   5   m

Jumbled values:

km/h        There is no numeral given 
(1 m)*s   The numeral is not a number
(1 m/m)*s   Same as above, even though it is dimensionless it is not a number
Exceptions
Constraint_Error Numeric error in unit expression
Data_Error Syntax error
End_Error There is no measure in the string
Layout_Error The value of Pointer is not in the range Source'First..Source'Last+1 
Unit_Error Illegal unit expression (like m/°C)

procedure Measures_Edit.Get_Unit
          (  Source  : String;
             Pointer : in out Integer;
             Value   : out Measure;
             Latin1  : Boolean := True
          );
procedure Measures_UTF8_Edit.Get_Unit
          (  Source  : String;
             Pointer : in out Integer;
             Value   : out Measure
          );

These procedures are restricted variant of the corresponding Gets. They recognize only measure units such as foot or meter. Irregular units and units with SI prefixes are recognized as well. No numbers or unit operations are recognized. Blanks are not skipped. This can be useful in syntax analyzers that may have different rules about operations and spaces. Such analyzer would rather use Get_Unit and then apply unit arithmetic for the operations it recognize. The meaning of the parameters is same as described for Get.

Exceptions
Data_Error Syntax error
End_Error There is no measure in the string
Layout_Error The value of Pointer is not in the range Source'First..Source'Last+1 

function Measures_Edit.Value
         (  Source : String;
            Latin1 : Boolean := True
         )  return Measure;
function
Measures_UTF8_Edit.Value
         (  Source : String
         )  return Measure;

These functions get the measure from the string Source. They are simplified versions of the corresponding Get-procedures. The whole string should be matched. Otherwise the exception Data_Error is propagated. The following exceptions are propagated out of the functions:

Exceptions
Constraint_Error Numeric error in unit expression 
Data_Error Syntax error 
End_Error There is no measure in the string Source
Unit_Error Illegal unit expression (like m/°C)

1.2.7. Conversion to String

The packages Measures_Edit and Measures_UTF8_Edit provide the following subroutines for measures output:

procedure Measures_Edit.Put
          (  Destination : in out String;
             Pointer     : in out Integer;
             Value       : Measure;
             Latin1      : Boolean  := True;
             Derived     : Boolean  := True;
             RelSmall    : Positive := Strings_Edit.MaxSmall;
             AbsSmall    : Integer  :=-Strings_Edit.MaxSmall;
             Field       : Natural  := 0;
             Justify     : Strings_Edit.Alignment := Strings_Edit.Left;
             Fill        : Character := ' '
          );
procedure
Measures_UTF8_Edit.Put
          (  Destination : in out String;
             Pointer     : in out Integer;
             Value       : Measure;
             Derived     : Boolean  := True;
             RelSmall    : Positive := Strings_Edit.MaxSmall;
             AbsSmall    : Integer  :=-Strings_Edit.MaxSmall;
             Field       : Natural  := 0;
             Justify     : Strings_Edit.Alignment := Strings_Edit.Left;
             Fill        : Character := ' '
          );

These procedures place the measure specified by the parameter Value into the output string Destination. The string is written starting from Destination (Pointer). The procedure from the package Measures_Editt has the parameter Latin1, which when true, allows using of Latin-1 characters listed in the table above, otherwise the output is done in ASCII. The procedure from the package Measures_UTF8_Edit uses UTF-8 encoded characters from the table. In all cases only the code positions in the table having white background are used in output. So for example in UTF-8 for Ohm, Greek omega is used rather than the Ohm sign.

The parameter Derived if true, allows derived SI units (such as N, F etc.) to appear in the output, and, additionally to them, °C. These units appear only alone or else with a numeric factor. They are not mixed with other units, like in N/s.

The parameters RelSmall and AbsSmall specify the precision of numeric output (see Strings_Edit.Float_Edit for further information). The procedure from the package Measures_Edit has additional optional parameters Field, Justify, Fill. When the parameter Field is not zero then Justify specifies alignment and Fill is the character used for filling. When Field is greater than Destination'Last - Pointer + 1, the latter is used instead. The UTF-8 variant from the package Measures_UTF8_Edit does not have these parameters because they would be meaningless for a UTF-8 encoded output. After successful completion Pointer is advanced to the first character following the output or to Destination'Last + 1. A measure can be output in one of the following forms:

Format Measure type Example
<gain> Unshifted, dimensionless (a number) 25.7
<unit> Unshifted, Gain = 1 (a SI unit) m/s
<gain>*<unit> Unshifted 25.7·W
<gain> and <offset> Shifted, dimensionless 4.1 and 6.4
<unit> and <offset> Shifted, Gain = 1 A and 100.0
<gain>*<unit> and <offset> Shifted 35.08·A and 100.0

For instance:

Text    : String (1..80);
Pointer : Positive := 1;
. . .
Put (Text, Pointer, 25.0*N, Derived => False, AbsSmall => 0);

will put 25·kg·m/s² into the string Text starting from the position 1. The parameter Derived=false forbids use of N (newton) in the output. Otherwise the result would be 25·N. The parameter AbsSmall=0 tells that the value has the precision ±0.5·10AbsSmall-1, so only the digits before the decimal point are output. For further information about floating-point I/O see the description of the package Strings_Edit.

The procedure factors out SI prefixes of power 10±3n. The prefix is then applied to the first unit, like for example in

25.7·km/s

Note that the parameter AbsSmall is corrected according to the prefix, so the effective small used for the output of <gain> (divided by 10±3n) is AbsSmall - 3n. The choice of the prefix is influenced by AbsSmall to avoid, when possible, representations of gain with an exponent.

Exceptions
Layout_Error Pointer is not in Destination'Range or there is
no room for the output

function Measures_Edit.Image
         (  Value    : Measure;
            Latin1   : Boolean  := True;
            Derived  : Boolean  := True;
            RelSmall : Positive := Strings_Edit.MaxSmall;
            AbsSmall : Integer  :=-Strings_Edit.MaxSmall
         )  return String;
function Measures_UTF8_Edit.Image
         (  Value    : Measure;
            Derived  : Boolean  := True;
            RelSmall : Positive := Strings_Edit.MaxSmall;
            AbsSmall : Integer  :=-Strings_Edit.MaxSmall
         )  return String;

The functions Image convert the parameter Value to string. The parameters Latin1, Derived, RelSmall and AbsSmall have same meaning as in Put (see).

1.2.8. Handling I/O in multiple encodings

The generic package Measures_Universal_Edit provides unified subroutines for string I/O with arbitrary encodings:

generic
   with package
Irregular_Measures is new Measures_Irregular (<>);
   with package
Float_Edit is
      new
Strings_Edit.Float_Edit (Irregular_Measures.Measures_Of.Number);
package
Measures_Universal_Edit is ...

The package provides a set of subroutines similar to ones of Measures_Edit and Measures_UTF8_Edit. They differs from the corresponding subroutines of these package in one additional parameter Mode, which for each subroutine determines the character set to be used. 

procedure Get
          (  Source  : String;
             Pointer : in out Integer;
             Value   : out Measure;
             Mode    : Code_Set
          );
procedure
Get
          (  Source  : String;
             Pointer : in out Integer;
             Value   : out Scaled;
             Mode    : Code_Set
          );
procedure
Get_Unit
          (  Source  : String;
             Pointer : in out Integer;
             Value   : out Measure;
             Mode    : Code_Set
          );
function Image
         (  Value    : Measure;
            Mode     : Code_Set;
            Derived  : Boolean  := True;
            RelSmall : Positive := Strings_Edit.MaxSmall;
            AbsSmall : Integer  := -Strings_Edit.MaxSmall
         )  return String;
function
Value
         (  Source : String;
            Mode   : Code_Set
         )  return Measure;
procedure
Put
          (  Destination : in out String;
             Pointer     : in out Integer;
             Value       : Measure;
             Mode        : Code_Set;
             Derived     : Boolean  := True;
             RelSmall    : Positive := Strings_Edit.MaxSmall;
             AbsSmall    : Integer  := -Strings_Edit.MaxSmall
          );

The parameter Mode has the enumeration type Code_Set. The meaning of the values is as follows:

This package has a non-generic instance Float_Measures_Universal_Edit for the measures based on the standard type Float.

[Back][TOC][Next]

1.3. GTK+ widgets

The described here packages use GtkAda, an Ada bindings to GTK+, a portable platform-independent graphical framework. To make use of this packages you need installed:

1.3.1. Unit selection widget

The generic package Measures_Gtk_Edit provides an interactive unit selection widget. The widget might look like:

Unit selection widget

The widget consists of:

The widget can be composed with other GTK+ widgets using standard widget packing techniques (see). It is also possibly to select a measurement unit in the form of a dialog, see the child package Measures_Gtk_Edit.Dialogs for further information.

The widget emits two signals, which are usually handled by a dialog window containing it. The dialog translates them into a response so that user could select a unit using simple button press or double click:

Selection of a unit in the tree view is intelligent. When the value in the edit field can interpreted as a numeral multiplied by a scale then the selected unit replaces the scale. If additionally the new unit is compatible with the old one, the numeral part is modified to retain the integral value. When the value in the edit field is not interpretable in the described form then the selected unit replaces it completely.

The package is declared as follows:

generic
   Class_Name : String;
   with package
UTF8_Edit is new Measures_UTF8_Edit (<>);
package
Measures_Gtk_Edit is ...

The formal parameters of the package is the name of the GTK+ class the widget will have. Class names are used in GTK+ to associate external resources like styles with the widget. The second parameter is an instance of the package Measures_UTF8_Edit.

There exists an instantiated version of Measures_Gtk_Edit for the standard floating point type: Gtk.Float_Measures_Edit..It is a child package of Gtk to make its naming consistent with the naming scheme used in GtkAda.

The package Measures_Gtk_Edit defines the following types:

type Gtk_Unit_Selection_Record is
   new
Gtk_Box_Record with private;
type Gtk_Unit_Selection is
   access all
Gtk_Unit_Selection_Record'Class;

The type Gtk_Unit_Selection_Record is the type of the widget object. The type Gtk_Unit_Selection is a reference type used to deal with the widget. This is the standard scheme used in GtkAda. One might need Gtk_Unit_Selection_Record only when a new widget type were derived from it. The following operations are defined in the package:

function Filter
         (  Widget : Gtk_Unit_Selection_Record;
            Value  : Measure
         )  return Boolean;

This is a primitive operation of Gtk_Unit_Selection_Record. A derived type might wish to override it. The function is used to filter out the measurement units, which cannot be selected. Such units do not appear in the tree view and are not accepted as direct input. The parameter Widget is the widget object. The parameter Value is a measurement unit to check. The function returns true if Value is acceptable. The default implementation returns false if Value is incompatible with the unit constraint set on Widget. Otherwise, or when no constraint is set, the result is true.

function Get
         (  Widget : not null access Gtk_Unit_Selection_Record
         )  return Measure;
function Get
         (  Widget : not null access Gtk_Unit_Selection_Record
         )  return GLib.UTF8_String;

These functions return the currently selected or otherwise input measure. It is the content of unit entry box. The value is checked using the function Filter and Constraint_Error is propagated when it does not match. The result is either the measure or its textual equivalent.

Exceptions
Constraint_Error Numeric error in unit expression or Filter returned false
Data_Error Syntax error 
End_Error There is no measure selected
Unit_Error Illegal unit expression (like m/°C)
function Get_Entry
         (  Widget : not null access Gtk_Unit_Selection_Record
         )  return Gtk_Entry;

This functions returns the edit field of Widget.

function Get_Tree_View
         (  Widget : not null access Gtk_Unit_Selection_Record
         )  return Gtk_Tree_View;

This functions returns the tree view field of Widget.

function Get_Type return Gtk_Type;

This functions returns the GTK+ type of the widget.

procedure Gtk_New
          (  Widget     : out Gtk_Unit_Selection;
             Constraint : Unit;
             Initial    : UTF8_String := ""
          );
procedure Gtk_New
          (  Widget  : out Gtk_Unit_Selection;
             Initial : UTF8_String := ""
          );

These functions create a new widget. The parameter Initial specifies the initial selection. It can be any string. Note that Initial is not checked to be a valid measurement unit, neither it is passed through Filter. Instead it is accepted as-is. When Initial contains a valid measure that can be found in the tree view box, it is additionally selected there. This search is takes into account SI prefixes when measurement unit is regular. So, Initial "km" would select "m" with the SI prefix k for 103. The parameter Constraint is optional. It specifies the unit to which the widget must be constrained. When specified, the default implementation of Filter will accept only the measures compatible with Constraint. The parameter Widget is the result.

procedure Initialize
          (  Widget     : not null access Gtk_Unit_Selection_Record'Class;
             Constraint : Unit;
             Initial    : UTF8_String := ""
          );
procedure Initialize
          (  Widget  : not null access Gtk_Unit_Selection_Record'Class;
             Initial : UTF8_String := ""
          );

These procedures perform initialization of the widget. In fact Gtk_New call to them internally. Any derived type shall call to them from its Initialize.

Style properties of the selection widget. All texts appearing in the widget can be changed using the style properties of the widget. The following table summarizes them:

Name Type Default Description
acceleration String Acceleration Subsection name of acceleration units
amount String Amount Subsection name of substance units
angle String Angle Subsection name of angular units
area String Area Subsection name of area units
capacitance String Capacitance Subsection name of capacitance units
charge String Charge Subsection name of charge units
chemistry String Chemistry Section name of chemistry units
concentration String Concentration Subsection name of concentration units
conductance String Conductance Subsection name of conductance units
current String Current Subsection name of current units
electricity String Electricity electricity units
energy String Energy Subsection name of energy units
equivalent-column-title String SI equivalent The title of the second column in the unit selection tree view
flux String Flux Subsection name of flux units
force String Force Subsection name of force units
frequency String Frequency Subsection name of frequency units
geometry String Geometry Section name of geometric units
illuminance String Illuminance Subsection name of illuminance units
inductance String Inductance Subsection name of inductance units
intensity String Intensity Subsection name of intensity units
length String Length Subsection name of length units
luminance String Luminance Subsection name of luminance units
mass String Mass Subsection name of mass units
mechanics String Mechanics Section name of mechanics units
optic String Optic Section name of optic units
potential String Potential Subsection name of potential units
power String Power Subsection name of power units
power-label String Power The label of the spin box
pressure String Pressure Subsection name of pressure units
resistance String Resistance Subsection name of resistance units
temperature String Temperature Subsection name of temperature units
time String Time Subsection name of time units
unit-column-title String Unit The title of the first column in the unit selection tree view.
velocity String Velocity Subsection name of velocity units
volume String Volume Subsection name of volume units

The formal parameters of the package is the name of the GTK+ class the widget will have. Class names are using in GTK+ to associate external resources like styles with the widget. The second parameter is an instance of the package Measures_UTF8_Edit.

1.3.2. Dimensioned value entry

The child generic package Measures_Gtk_Edit.GEntry provides a widget having the functionality of a combo box for dimensioned values input. The widget is a descendant of Gtk_Entry. It also implements the GtkCellEditable interface and thus can be used with a tree view cell renderer to provide unit input in a tree view control. When focused a drop down box containing the unit selection list appears. The user can selecting a unit from the list and directly edit the content of the entry box.

generic
   type
Custom_Unit_Selection_Record is
      new
Gtk_Unit_Selection_Record with private;
package
Measures_Gtk_Edit.GEntry is ...

The formal generic parameter is a unit selection widget type derived from Gtk_Unit_Selection_Record. This widget will be used within the entry. The class name of the widget is constructed from the class name specified in the actual generic parameter by adding "Entry" to it.

There exists an instantiated version of Measures_Gtk_Edit.GEntry for the standard floating point type: Gtk.Float_Measures_Entry..It is a child package of Gtk to make its naming consistent with the naming scheme used in GtkAda.

The package defines the type of the unit selection entry widget:

type Gtk_Unit_Entry_Record is
   new
Gtk_Entry_Record with private;
type
Gtk_Unit_Entry is
   access all
Gtk_Unit_Entry_Record'Class;

The widget is a specialized entry. The entry is not directly editable. When user attempts to edit it a pop down window containing Custom_Unit_Selection_Record is shown. When the entry contains a text it is shown there. When the text is a valid measure it is selected in the unit selection tree. The user can change the selection or manually edit the measure. The editing is committed by pressing enter or cancelled by pressing escape keys. It is also cancelled by leaving the focus. When the editing is successful the result of becomes the new content of the entry. The following subprograms are defined in the package:

function Editing_Canceled (Widget : not null access Gtk_Unit_Entry_Record)
   return Measure;

This function return true if the last editing was cancelled by user. It is used in a cell renderer when it handles editing-done signal. At this point it would take the value from the widget when Editing_Canceled returns false, and ignore it otherwise.

function Get
         (  Widget : not null access Gtk_Unit_Entry_Record
         )  return Measure;
function
Get
         (  Widget : not null access Gtk_Unit_Entry_Record;
            Scale  : Measure
         )  return Measure;

This function parses the entry text and returns it as a measure. The result is checked against the Widget's constraint if any. Constraint_Error is propagated if unit does not match. When the parameter Scale is specified is shall have the units compatible with the Widget's constraint. Otherwise Constraint_Error is propagated. When the widget is unconstrained Scale is ignored. When the widget is constrained and Scale is specified then a numeric content of the widget is treated as a valid measure by multiplying to Scale. When the content is dimensioned, Scale is ignored. Note that the Set_Numeric has no effect on this behavior.

Exceptions
Constraint_Error Numeric error in unit expression, incompatible unit of the widget content or Scale
Data_Error Syntax error 
End_Error There is no measure selected
Unit_Error Illegal unit expression (like m/°C)
function Get_Constraint
         (  Widget : not null access Gtk_Unit_Entry_Record
         )  return Unit;

This function returns the current widget constraint. Constraint_Error is propagated when Widget is unconstrained.

function Get_Type return Gtk_Type;

This functions returns the GTK+ type of the widget.

procedure Gtk_New
          (  Widget     : out Gtk_Unit_Entry_Record;
             Constraint : Unit;
             Initial    : GLib.UTF8_String := ""
          );
procedure Gtk_New
          (  Widget  : out Gtk_Unit_Entry_Record;
             Initial : GLib.UTF8_String := ""
          );

These functions create a new widget. The parameter Initial specifies the initial content of the entry. It can be any string. Note that Initial is not checked to be a valid measurement unit, neither it is passed through Filter of the type Custom_Unit_Selection_Record as specified in the generic formal parameter of the package. The parameter Constraint is optional. It specifies the unit to which the widget must be constrained. When specified, the default implementation of Filter will accept only the measures compatible with Constraint. The parameter Widget is the result.

procedure Initialize
          (  Widget     : not null access Gtk_Unit_Entry_Record'Class;
             Constraint : Unit;
             Initial    : GLib.UTF8_String := ""
          );
procedure Initialize
          (  Widget  : not null access Gtk_Unit_Entry_Record'Class;
             Initial : GLib.UTF8_String := ""
          );

These procedures perform initialization of the widget. In fact Gtk_New call to them internally. Any derived type shall call to them from its Initialize.

function Is_Constrained
         (  Widget : not null access Gtk_Unit_Entry_Record
         )  return Boolean;

This function returns true if the widget is constrained to a specific unit.

function Is_Numeric
         (  Widget : not null access Gtk_Unit_Entry_Record
         )  return Boolean;

This function returns true if the widget allows numeric values when editable. This is the default.

procedure Reset_Constraint
          (  Widget : not null access Gtk_Unit_Entry_Record
          );

This procedure removes the unit constraint if any. Note that if the drop-down window is visible, the operation will not have effect on its content.

procedure Set_Constraint
          (  Widget     : not null access Gtk_Unit_Entry_Record;
             Constraint : Unit
          );

This procedure sets/replaces widget's unit constraint. The parameter Constraint is the new constraint to set. Note that if the drop-down window is visible, the operation will not have effect on its content.

procedure Set_Numeric
          (  Widget  : not null access Gtk_Unit_Entry_Record;
             Allowed : Boolean
          );

This procedure controls the Widget's behavior when the input is a plain number and the widget is constrained to non-dimensionless values. In this case specifying a plain number is allowed, otherwise such input causes unit mismatch error.

Style properties of the unit entry widget. The following table summarizes them:

Name Type Default Description
has-header Boolean false True if the unit selection tree view should have column's header

1.3.3. Dimensioned value cell renderer

The child generic package Measures_Gtk_Edit.GEntry.Cell_Renderer provides an editable cell renderer for GTK+ tree view widget. The renderer indicates dimensioned values as fixed-point numbers in using the specified scale. When edited, the renderer drops down a unit selection list. The user can select a unit from the list and edit the value entry. The input when the dimensioned is automatically converted to the scale of the renderer. When the input contains no units there the value is assumed specified in the renderer's scale. The renderer rejects any improperly dimensioned input. The renderer class name of the widget is constructed from the class name specified in the actual generic parameter of its parent by adding "CellRenderer" to it.

generic
package
Measures_Gtk_Edit.GEntry.Cell_Renderer is ...

There exists an instantiated version of Measures_Gtk_Edit.GEntry.Cell_Renderer for the standard floating point type: Gtk.Float_Measures_Cell_Renderer..It is a child package of Gtk to make its naming consistent with the naming scheme used in GtkAda.

The package defines the type of the renderer:

type Gtk_Cell_Renderer_Measure_Record is
   new
Gtk_Abstract_Renderer_Record with private;
type
Gtk_Cell_Renderer_Measure is
   access all
Gtk_Cell_Renderer_Measure_Record'Class;

The following subprograms are defined:

function Get (Cell : not null access Gtk_Cell_Renderer_Measure_Record)
   return Number;
function Get (Cell : not null access Gtk_Cell_Renderer_Measure_Record)
   return Measure;
function Get (Cell : not null access Gtk_Cell_Renderer_Measure_Record)
   return GValue;
function Get (Cell : not null access Gtk_Cell_Renderer_Measure_Record)
   return UTF8_String;

These functions are used to query the rendered value. It can be obtained as:

function Get_Type return Gtk_Type;

This functions returns the GTK+ type of the renderer.

procedure Gtk_New
          (  Cell  : out Gtk_Cell_Renderer_Measure;
             Scale : UTF8_String := "";
             After : Natural     := 0;
          );

This procedure creates a new renderer. The result is returned through the parameter Cell .The parameter Scale determines the dimension and the multiplier of the edited values. It must be a valid measure specification for Measures_UTF8_Edit. Further the gain of the measure has to be non-negative otherwise Constraint_Error is propagated. When Scale is specified empty, it assumed to be 1 SI. The parameter After specifies the number of decimal positions after the decimal point for the rendered values. Observe that the values are rendered in Scale.

Exceptions
Constraint_Error A numeric error in unit expression of Scale. The value of Scale has non-positive gain
Data_Error Syntax error in Scale
Unit_Error Illegal unit expression (like m/°C)
procedure Initialize
          (  Cell  : not null access Gtk_Cell_Renderer_Measure_Record'Class;
             Scale : UTF8_String := "";
             After : Natural     := 0;
          );

This procedure is called by any derived type from its Initialize.

Exceptions
Constraint_Error A numeric error in unit expression of Scale. The value of Scale has non-positive gain
Data_Error Syntax error in Scale
Unit_Error Illegal unit expression (like m/°C)
procedure Put
          (  Cell  : not null access Gtk_Cell_Renderer_Measure_Record;
             Value : Number
          );
procedure Put
          (  Cell  : not null access Gtk_Cell_Renderer_Measure_Record;
             Value : Measure
          );
procedure Put
          (  Cell  : not null access Gtk_Cell_Renderer_Measure_Record;
             Value : UTF8_String
          );
procedure Put
          (  Cell  : not null access Gtk_Cell_Renderer_Measure_Record;
             Value : GValue
          );

These functions are used to set the rendered value. It can be one of the following types:

Exceptions
Constraint_Error A numeric error in unit expression
Data_Error Syntax error
Unit_Error Illegal unit expression (like m/°C), incompatible unit

Properties of the renderer. The following table summarizes them:

Name Type Default Description
after UInt 0 Digits after decimal point
scale string - The text representing the scale of the rendered values. When empty the scale is 1 SI
text string - The text representing the rendered value including the scale
value GDouble - Scaled number, the value rendered

1.3.4. Unit selection dialogs

The child generic package Measures_Gtk_Edit.Dialogs provides a simple way to query for a measurement unit from a dialog box:

Unit selection dialog

generic
   type
Custom_Unit_Selection_Record is
      new
Gtk_Unit_Selection_Record with private;
package
Measures_Gtk_Edit.Dialogs is ...

The formal generic parameter is a unit selection widget type derived from Gtk_Unit_Selection_Record. This widget will be used within the dialog. The package provides the following subprograms:

function Get
         (  Constraint   : Unit;
            Initial      : GLib.UTF8_String := "";
            Title        : GLib.UTF8_String := "Unit selection";
            Confirm      : GLib.UTF8_String := "gtk-ok";
            Cancel       : GLib.UTF8_String := "gtk-cancel";
            Parent       : Gtk_Window       := null;
            Flags        : Gtk_Dialog_Flags := Modal;
            Missing      : GLib.UTF8_String := "No unit specified";
            Erroneous    : GLib.UTF8_String := "Illegal unit";
            Incompatible : GLib.UTF8_String := "Incompatible unit"
         )  return GLib.UTF8_String;
function
Get
         (  ... -- Same parameters as above
         )  return Measure;
function
Get
         (  ... -- Same parameters as above, without Constraint
         )  return GLib.UTF8_String;
function
Get
         (  ... -- Same parameters as above, without Constraint
         )  return Measure;

These function cause a dialog to appear. The dialog has the unit selection widget in it and up to two buttons. A function does not return until the dialog closes. When the user presses the OK button, which label is specified by the parameter Confirm, the selected measurement unit is checked for validity and it is the result of the function. The result is either a string or a measure. On failed checks the dialog does not end and an appropriate message text is shown as the parameters Missing, Erroneous and Incompatible specify. When the parameter Cancel is not an empty string, the Cancel button is shown. When the dialog box is closed using the window manager or the Cancel button, the functions propagate End_Error exception. The button names can refer to GTK+ stock buttons, as the default values do. Otherwise, the underline character within the name refer to the hot-key character. The parameter Constraint limits the measurement unit to a specific dimension. In the example shown on the figure it was Units.Base.Length. When Constraint is omitted, any valid measurement unit is acceptable. An additional or alternative constraint can be imposed by the type Cunstom_Unit_Selection_Record, as Filter describes. Title is the dialog window title. Parent is the parent window of. The parameter Flags is the dialog flags.

The package has an instantiated version for the standard floating-point type: Gtk.Float_Measures_Dialogs.


[Back][TOC][Next]

2. Packages

Two separate sets of packages implement the types Unit and the type Measure. There is also a set of supporting packages used internally.

[Back][TOC][Next]

2.1. The packages related to the type Unit

The packages related to the type Unit:

Package Provides
Units The type Unit, unit arithmetic
      Base The constants corresponding to the base SI units
Constants The constants corresponding to some physical entities
Edit Conversion to ASCII and Latin-1 strings
UTF8_Edit Conversion to UTF-8 encoded strings

[Back][TOC][Next]

2.2. The packages related to the type Measure

The following packages are related to the type Measure:

Package (generic) Provides Non-generic version (Float)
Measures The type Measure, measure arithmetic, constants corresponding to the base SI units. Float_Measures
Measures_Derived The constants corresponding to the derived SI units. Float_Measures_Derived
Measures_Edit Input and output of measures (ASCII and Latin-1 string conversions) Float_Measures_Edit
Measures_Elementary_Functions Elementary functions (sqrt, log, exp etc.) Float_Measures_Elementary_Functions
Measures_Gtk_Edit GTK+ widget for measurement unit selection Gtk.Float_Measures_Edit
    Dialogs GTK+ dialogs for measurement unit selection Gtk.Float_Measures_Dialogs
GEntry GTK+ widget for editing units with the functionality of a combo box Gtk.Float_Measures_Entry
     Cell_Renderer Tree view cell renderer for dimensioned value Gtk.Float_Measures_Cell_Renderer
Measures_Irregular The constants corresponding to some of irregular units Float_Measures_Irregular
Measures_Universal_Edit Input and output of measures using multiple encodings Float_Measures_Universal_Edit,
Float_Measures_UTF8_Edit.Universal_Edit
Measures_UTF8_Edit Input and output of measures with UTF-8 encoded strings Float_Measures_UTF8_Edit

The following example shows how the generic packages are instantiated:


with
Measures;
with Measures_Derived;
with Measures_Irregular;
with Measures_Edit;
with Strings_Edit.Float_Edit;
   . . .
type Real is digits ...;
--
-- Instantiation of Measures with the type Real as the parameter
--

package Real_Measures is new Measures (Real);
--
-- Instantiation of Measures_Derived
--

package Real_Measures_Derived is new Measures_Derived (Real_Measures);
--
-- Instantiation Measures_Irregular
--

package Real_Measures_Irregular is
   new
Measures_Irregular (Real_Measures_Derived);
--
-- Instantiation Measures_Edit
--
package Real_Edit is new Strings_Edit.Float_Edit (Real);
--
-- Instantiation Measures_Edit
--

package Real_Measures_Edit is
   new
Measures_Derived
       (  Real_Measures_Irregular,
          Real_Edit
       );

[Back][TOC][Next]

2.3. Packages defined for internal use

The software uses the packages Strings_edit and Tables. The following table shows the packages defined for internal use:

Package Provides
Measures_Generic_Edit Generic package for I/O of measures for all types of encodings. Measures_Edit, Measures_UTF8_Edit, Measures_Universal_Edit instantiate it.
Measures_Table_Of_Integer The table of SI prefixes (their powers) or unit operations (coded as integers). Instantiates the package Tables with Integer as the parameter.
Measures_Table_Of_Measure The table of measurement units. Instantiates the package Tables with Address as the parameter.


[Back][TOC][Next]

3. Examples

[Back][TOC][Next]

3.1. Self-tests

The subdirectory test_units contains various tests.

The file test_measures.adb contains a test program for the packages Measures and Units. It also estimates the performance hit when the type Measure is used for calculations instead of Float. With GNAT compiler the program can be built using gnatmake:

>gnatmake -I.. test_measures.adb

The file test_gtk_unit_selection.adb is a test program for GTK+. To build it one needs installed:

Provided that GtkAda contributions is placed in the directory gtkada on the same level as the root directory of this software the command line to build test_gtk_unit_selection might look under Linux as:

>gnatmake -I.. -I../../gtkada test_gtk_unit_selection.adb `gtkada-config`

Under Windows instead of automatic configuration of GtkAda paths, you should specify the install directory of GtkAda implicitly:

>gnatmake -I.. -I../../gtkada -IC:/GtkAda/include/gtkada test_gtk_unit_selection.adb

[Back][TOC][Next]

3.2. Unit converters and mappers

The subdirectory units-examples contains a simple sample programs which convert dimensioned values.

3.2.1. Unit converter for Win32

The following is the full source code of the converter. It is located in units-examplesunits-converterwin32. The fragments relevant to dealing with units are highlighted yellow. The rest is dealing with Windows API. The program uses Windows Unicode support. For this measures input and output is performed in UTF-8 format using the package Float_Measures_UTF8_Edit. Note that Windows uses USC-2 encoding therefore all strings are converted from Wide_String to UTF-8 and back using subroutines of the package Strings_Edit.UTF8.Handling.

Implementation, file units_converter.adb:

with Ada.IO_Exceptions; use Ada.IO_Exceptions;
with
Ada.Unchecked_Conversion;
--
-- Things related to Win32-API
--
with Win32;           use Win32;
with
Win32.WinBase;   use Win32.WinBase;
with
Win32.WinDef;    use Win32.WinDef;
with
Win32.WinMain;   use Win32.WinMain;
with
Win32.WinUser;   use Win32.WinUser;
with
Win32.CommCTRL;  use Win32.CommCTRL;
with Float_Measures;              use Float_Measures;
with Float_Measures_UTF8_Edit;    use Float_Measures_UTF8_Edit;
with
Strings_Edit.UTF8.Handling;  use Strings_Edit.UTF8.Handling;
with Units;                       use Units;
with Interfaces.C;
with System;

procedure
Units_Converter is
  
--
   -- The following are the constants defined in the resource script. See
   -- Units_Converter.rc file.
   --
   Dialog_ID  : constant := 101;
   Input_ID   : constant := 1000;
   SI_ID      : constant := 1001;
  
Base_ID    : constant := 1002;
   Message_ID : constant := 1003;
   Go_ID      : constant := 1004;
   --
   -- Useless windows return codes
   --
  
INT_Result  : INT;
   BOOL_Result : BOOL;
   UINT_Result : UINT;
   --
   -- Dialog_Proc -- Process messages to the dialog box
   --
   --    Window  - The window (a handle to)
   --    Message - To process
   --    WPar    - Its short parameter
   --    LPar    - Its long parameter
   --
   -- Returns :
   --
   --    Message processing code (message specific)
   --
  
function Dialog_Proc
            (  Window  : HWND;
               Message : UINT;
               WPar    : WPARAM;
               LPar    : LPARAM
            )  return BOOL;
   pragma Convention (Stdcall, Dialog_Proc);
   --
   -- Dialog_Proc_Access -- Pointer to Dialog_Proc
   --
  
type Dialog_Proc_Access is access function
       
(  Window  : HWND;
           Message : UINT;
           WPar    : WPARAM;
           LPar    : LPARAM
        )  return BOOL;
   pragma Convention (Stdcall, Dialog_Proc_Access);
   --
   -- To_DLGPROC -- Conversion from Dialog_Proc_Access to DLGPROC
   --
   -- This is necessary to work around access type protection system.
   -- DLGPROC is declared so that no nested function pointer may be
   -- converted to it. So we have declared another pointer type and convert
   -- it to DLGPROC.
   --
  
function To_DLGPROC is
      new
Ada.Unchecked_Conversion (Dialog_Proc_Access, DLGPROC);
   --
   -- Get_Text -- Wrapper around windows API function GetDlgItemText
   --
   --    Window - A handle to
   --    ID     - Of the control
   --
   -- Returns :
   --
   --    The text of the control
   --
  
function Get_Text (Window : HWND; ID : UINT) return String is
     
Input : WCHAR_Array (1..256);
   begin
     
UINT_Result :=
         GetDlgItemTextW
         (  Window,
            Input_ID,
            Input (Input'First)'Unchecked_Access,
            Input'Length
         );
      return
      
  To_UTF8
         (  Interfaces.C.To_Ada (Interfaces.C.WCHAR_Array (Input))
         );
   end
Get_Text;
   --
   -- Set_Text -- Wrapper around windows API function SetDlgItemText
   --
   --    Window - A handle to
   --    ID     - Of the control
   --    Text   - To be put there
   --
  
procedure Set_Text
             (  Window : HWND;
                ID     : UINT;
                Text   : String
             )  is
      Output : WCHAR_Array :=
         To_Win (Interfaces.C.To_C (To_Wide_String (Text)));
   begin
      BOOL_Result :=
         SetDlgItemTextW
         (  Window,
            INT (ID),
            Output (Output'First)'Unchecked_Access
         );
   end
Set_Text;
   --
   -- Go -- When the Go button gets pressed
   --
   --    Window - A handle to
   --
  
procedure Go (Window : HWND) is
      Number : Measure;
   begin
      Set_Text (Window, SI_ID, "");
      Set_Text (Window, Base_ID, "");
      Set_Text (window, Message_ID, "");
      Number := Value (Get_Text (Window, Input_ID));
      Set_Text (Window, SI_ID, Image (Number));
      Set_Text (Window, Base_ID, Image (Number, Derived => False));
   exception
      when
Constraint_Error =>
         Set_Text (Window, Message_ID, "Numeric error");
      when Data_Error =>
         Set_Text (Window, Message_ID, "Syntax error");
      when End_Error =>
         Set_Text (Window, Message_ID, "Nothing recognized");
      when Unit_Error =>
         Set_Text (Window, Message_ID, "Unit error");
   end Go;
   --
   -- Dialog_Proc -- Implementation
   --
  
function Dialog_Proc
            (  Window  : HWND;
               Message : UINT;
               WPar    : WPARAM;
               LPar    : LPARAM
            )  return BOOL is
   begin
      case
Message is
         when
WM_CLOSE =>
            BOOL_Result := EndDialog (Window, 0);
         when WM_COMMAND =>
            case HIWORD (DWORD (WPar)) is
               when
BN_CLICKED =>
                  case LOWORD (DWORD (WPar)) is
                     when
Go_ID =>
                        Go (Window);
                     when others =>
                        null;
                  end case;
               when others
=>
                  null;
            end case;
         when
WM_INITDIALOG =>
            Set_Text
            (  Window,
               Message_ID,
               "Source: www.dmitry-kazakov.de/ada/units.htm"
            );
         when others =>
            null;
      end case;
      return
0;
   end Dialog_Proc;
   Ptr    : Dialog_Proc_Access := Dialog_Proc'Access;
   Name   : CHAR_Array := "RichEd20.dll" & CHAR'Val (0);
   Handle : HINSTANCE;
begin
  
InitCommonControls;  -- Needed for Unicode support
   Handle := LoadLibrary (Name (Name'First)'Unchecked_Access); 
   Int_Result :=
      DialogBoxParam
      (  Get_hInstance,
         PCCH (MAKEINTRESOURCE (Dialog_ID)),
         System.Null_Address,
         To_DLGPROC (Ptr),
         0
      );
   if 0 = FreeLibrary (Handle) then null; end if;
end Units_Converter;

3.2.2. Unit converter for GTK+

GTK+ is a platform-independent library for creating graphical applications. It is available for a wide range of platforms which includes Windows and Linux. The following is the full source code of the converter based on GTK+. It is located in units-examplesunits-convertergtk. The fragments relevant to dealing with units are highlighted yellow. The rest is dealing with GTK+. Note that GTK+ natively supports UTF-8, so the package Float_Measures_UTF8_Edit is used without any further translation.

Implementation, file units_converter.adb:

with Ada.Characters.Latin_1;  use Ada.Characters.Latin_1;
with Ada.IO_Exceptions;       use Ada.IO_Exceptions;
--
-- Things related to Gtk-API
--

with Gtk.Enums;   use Gtk.Enums;
with Gtk.Frame;   use Gtk.Frame;
with Gtk.Button;  use Gtk.Button;
with Gtk.GEntry;  use Gtk.GEntry;
with Gtk.Label;   use Gtk.Label;
with Gtk.Table;   use Gtk.Table;
with Gtk.Widget;  use Gtk.Widget;
with Gtk.Window;  use Gtk.Window;

with Ada.Unchecked_Conversion;
with Gtk.Main;
with Gtk.Missed;
with Float_Measures;            use Float_Measures;
with Float_Measures_UTF8_Edit;  use Float_Measures_UTF8_Edit;
with Units;                     use Units;
procedure Units_Converter is
--
-- Read_Only_Text -- Sunken labels
--

   type Read_Only_Text is record
      Text  : Gtk_Label;
      Frame : Gtk_Frame;
   end record;
--
-- Gtk_New -- Initializes the label
--
   procedure Gtk_New
             (  Text  : in out Read_Only_Text;
                Label : String := ""
             )  is
   begin

      Gtk_New (Text.Frame);
      Text.Frame.Set_Shadow_Type (Shadow_In);
      Gtk_New (Text.Text, Label);
      Text.Text.Set_Alignment (0.0, 0.5);
      Text.Frame.Add (Text.Text);
   end Gtk_New;
--
-- Show -- The label
--
   procedure Show (Text : in out Read_Only_Text) is
   begin

      Text.Text.Show;
      Text.Frame.Show;
   end Show;

   Window              : Gtk_Window;
   Grid                : Gtk_Table;
   Label1              : Gtk_Label;
   Label2              : Gtk_Label;
   Label3              : Gtk_Label;
   Value_To_Convert    : Gtk_Entry;
   Value_In_SI         : Gtk_Entry;
   Value_In_Base_Units : Gtk_Entry;
   Button              : Gtk_Button;
   Message             : Read_Only_Text;
--
-- Conversions of callbacks to circumvent accessibility checks
--
   type Local_Handler is access procedure
        (  Widget : not null access Gtk_Widget_Record'Class
        );
   function "+" is
      new
Ada.Unchecked_Conversion (Local_Handler, Cb_Gtk_Button_Void);
   function "+" is
      new
Ada.Unchecked_Conversion (Local_Handler, Cb_Gtk_Entry_Void);
   --
   -- Go -- When the Go button gets pressed
   --
   -- Widget - The button
   --

   procedure Go (Widget : access Gtk_Widget_Record'Class) is
      Number : Measure;
   begin
      Value_In_SI.Set_Text ("");
      Value_In_Base_Units.Set_Text ("");
      Message.Text.Set_Text ("");
      Number := Value (Value_To_Convert.Get_Text);
      Value_In_SI.Set_Text (Image (Number));
      Value_In_Base_Units.Set_Text (Image (Number, Derived => False));
   exception
      when Constraint_Error =>
         Message.Text.Set_Text ("Numeric error");
      when Data_Error =>
         Message.Text.Set_Text ("Syntax error");
      when End_Error =>
         Message.Text.Set_Text ("Nothing recognized");
      when Unit_Error =>
         Message.Text.Set_Text ("Unit error");
   end Go;
begin
   --
   -- Initialization
   --
  
Gtk.Main.Init;
   --
   -- Creating the main window and handle its events
   --
   Gtk.Window.Gtk_New (Window);
   Window.Set_Title ("Unit conversion (Ada95 GTK+)");
   Window.On_Delete_Event (Gtk.Missed.Delete_Event_Handler'Access);
   Window.On_Destroy (Gtk.Missed.Destroy_Handler'Access);
   Window.Set_Border_Width (10);
   --
   -- Creating the grid, a table to align all other widgets
   --
  
Gtk_New (Grid, 3, 4, False);
   Grid.Set_Row_Spacings (3);
   Grid.Set_Col_Spacings (3);
   Window.Add (Grid);
   --
   -- The left column are labels
   --
   
Gtk_New (Label1, "Value to convert" & LF & "For example 23.5 bar");
   Grid.Attach_Defaults (Label1, 0, 1, 0, 1);
   Label1.Set_Justify (Justify_Right);
   Label1.Set_Alignment (1.0, 0.5);
   Gtk_New (Label2, "SI equivalent");
   Grid.Attach_Defaults (Label2, 0, 1, 1, 2);
   Label2.Set_Alignment (1.0, 0.5);
   Gtk_New (Label3, "Base units only");
   Grid.Attach_Defaults (Label3, 0, 1, 3, 4);
   Label3.Set_Alignment (1.0, 0.5);
   --
   -- The central column is the edit fields
   --
  
Gtk_New (Value_To_Convert);
   Value_To_Convert.On_Activate (+Go'Access);
   Grid.Attach_Defaults (Value_To_Convert, 1, 2, 0, 1);
   Gtk_New (Value_In_SI);
   Value_In_SI.Set_Sensitive (False);
   Grid.Attach_Defaults (Value_In_SI, 1, 2, 1, 2);
   Gtk_New (Value_In_Base_Units);
   Grid.Attach_Defaults (Value_In_Base_Units, 1, 2, 3, 4);
   --
   -- The right column is the button Go
   --
   Gtk_New (Button, " Go ");
   Button.On_Clicked (+Go'Access);
   Grid.Attach_Defaults (Button, 3, 4, 0, 4);
   --
   -- Error messages is beneath
   --
  
Gtk_New (Message, "Source: www.dmitry-kazakov.de/ada/units.htm");
   Grid.Attach_Defaults (Message.Frame, 0, 4, 4, 5);
   --
   -- Show everything
   --
   Window.Show_All;
   --
   -- Enter the events processing loop
   --
   Gtk.Main.Main;
end Units_Converter;

To compile this example you will need GTK+, and GtkAda bindings installed.

3.2.3. Unit mapper for GTK+

Unit mapper is a small GUI program that converts values from one unit to another. It is located in units-examplesunits-mapper. The source code:

Implementation, file units_mapper.adb:

with Ada.IO_Exceptions; use Ada.IO_Exceptions;
--
-- Things related to Gtk-API
--

with GLib;              use GLib;
with
Gtk.Editable;      use Gtk.Editable;
with
Gtk.Entry_Buffer;  use Gtk.Entry_Buffer;
with
Gtk.GEntry;        use Gtk.GEntry;
with Gtk.Table;         use Gtk.Table;
with Gtk.Widget;        use Gtk.Widget;
with Gtk.Window;        use Gtk.Window;

with Ada.Unchecked_Conversion;
with
Gtk.Main;
with
Gtk.Missed;
--
-- This is what we need for unit conversions
--

with Gtk.Float_Measures_Entry;  use Gtk.Float_Measures_Entry;
with Float_Measures;            use Float_Measures;
with Strings_Edit.Floats;       use Strings_Edit.Floats;
with Units;                     use Units;

procedure Units_Mapper is

   Window     : Gtk_Window;
   Grid       : Gtk_Table;
   From_Unit  : Gtk_Unit_Entry;
   From_Value : Gtk_Entry;
   To_Unit    : Gtk_Unit_Entry;
   To_Value   : Gtk_Entry;
   Ignore     : Boolean := False;

Here we do standard GTK+ stuff plus the variables for from and to fields of the mapper.

Implementation, file units_mapper.adb (continued):

--
-- Conversions of callbacks to circumvent accessibility checks
--

   type Local_Callback is access procedure (Cell : Gtk_Editable);
   function
"+" is
      new
Ada.Unchecked_Conversion
          (  Local_Callback,
             Cb_Gtk_Editable_Void
          );

This piece is used to circumvent accessibility checks in order to keep the program in one file. Normally if the signal handler is declared at the library level no unchecked conversion is needed.

Implementation, file units_mapper.adb (continued):

   procedure Changed_From (Widget : Gtk_Editable) is
   begin
      if
Ignore then
         return
;
      end if;
      Ignore := True;
      begin
         To_Value.Set_Text
         (  Image
            (  Get_Value_As
               (  Value (From_Value.Get_Text) * From_Unit.Get,
                  To_Unit.Get
         )  )  );
      exception
         when
Data_Error =>
            To_Value.Set_Text ("Not a number");
         when End_Error | Constraint_Error =>
            To_Value.Set_Text ("");
         when Unit_Error =>
            To_Value.Set_Text ("Unit error");
      end;
      Ignore := False;
   end Changed_From;

This is the procedure called when the from field gets changed. It converts it to the units of the to field and changes the corresponding entry box. The variable Ignore is used prevent an infinite recursion of reactions on changes in the entry boxes.

Implementation, file units_mapper.adb (continued):

   procedure Changed_To (Widget : Gtk_Editable) is
   begin
      if
Ignore then
         return
;
      end if;
      Ignore := True;
      begin
         From_Value.Set_Text
         (  Image
            (  Get_Value_As
               (  Value (To_Value.Get_Text) * To_Unit.Get,
                  From_Unit.Get
         )  )  );
      exception
         when
Data_Error =>
            From_Value.Set_Text ("Not a number");
         when End_Error | Constraint_Error =>
            From_Value.Set_Text ("");
         when Unit_Error =>
            From_Value.Set_Text ("Unit error");
      end;
      Ignore := False;
   end Changed_To;

This is the procedure called when the to field gets changed. It converts it to the units of the from field and changes the corresponding entry box.

Implementation, file units_mapper.adb (continued):

   procedure Changed_Unit (Widget : Gtk_Editable)  is
   begin

      To_Unit.Set_Text (From_Unit.Get_Text);
      To_Unit.Set_Constraint (From_Unit.Get.SI);
      Changed_From (Widget);
   exception
      when
End_Error =>
         null;
   end Changed_Unit;

This is the procedure called when a unit field gets changed. It converts the from field. The to field update automatically follows per change notification.

Implementation, file units_mapper.adb (continued):

begin
   --
   -- Initialization
   --

   Gtk.Main.Init;
   --
   -- Creating the main window and handle its events
   --

   Gtk_New (Window);
   Window.Set_Title ("Unit mapper (Ada GTK+)");
   Window.On_Delete_Event (Gtk.Missed.Delete_Event_Handler'Access);
   Window.On_Destroy (Gtk.Missed.Destroy_Handler'Access);
   Window.Set_Border_Width (10);
   --
   -- Creating the grid, a table to align all other widgets
   --

   Gtk_New (Grid, 2, 2, False);
   Grid.Set_Row_Spacings (3);
   Grid.Set_Col_Spacings (3);
   Window.Add (Grid);
   --
   -- The left column are labels
   --

   Gtk_New (From_Value);
   From_Value.Set_Tooltip_Text ("First value to convert");
   Grid.Attach_Defaults (From_Value, 0, 1, 0, 1);
   On_Changed (+From_Value, +Changed_From'Access);

   Gtk_New (From_Unit);
   From_Unit.Set_Tooltip_Text ("First value unit");
   Grid.Attach_Defaults (From_Unit, 1, 2, 0, 1);
   On_Changed (+From_Unit, +Changed_Unit'Access);

   Gtk_New (To_Value);
   To_Value.Set_Tooltip_Text ("Second value to convert");
   Grid.Attach_Defaults (To_Value, 0, 1, 1, 2);
   On_Changed (+To_Value, +Changed_To'Access);

   Gtk_New (To_Unit);
   To_Unit.Set_Tooltip_Text ("Second value unit");
   Grid.Attach_Defaults (To_Unit, 1, 2, 1, 2);
   On_Changed (+To_Unit, +Changed_From'Access);
   --
   -- Show everything
   --

   Window.Show_All;
   --
   -- Enter the events processing loop
   --
   Gtk.Main.Main;
end Units_Mapper;


[Back][TOC][Next]

4. Installation

The software does not require special installation. The archive's content can be put in a directory and used as-is. For users of GNAT compiler the software provides gpr project files, which can be used in the Gnat Programming Studio (GPS).

To ease use of the software with GPS, it can be integrated into the GPS using the GPS Library Installer (gps_installer). Start the gps_installer as root (or with the corresponding administrative rights to the GNAT installation directory) specifying the source directory as the argument. Follow the instructions.

The packages based on GtkAda require it and the GtkAda Contributions installed. When these packages are not planned for use, the corresponding failures during the installation can be safely ignored.

Project files Provides Use in custom project
units Units of measurements for Ada with "units.gpr";
units-gtk Units of measurements for Ada with GTK+ widgets. The project automatically includes the projects of GtkAda and GtkAda Contributions with "units-gtk.gpr";

4.1. Debian and Fedora packages

Under Fedora, Debian Linux and their derivates you can use packages for Fedora and Debian.

4.2. Fedora packages repository

The Fedora packages of this library are located in a yum software package manager repository. They can be searched, installed and updated automatically using yum. In order to do so, the file dmitry-kazakov.repo can be put into the directory /etc/yum.repos.d.

4.3. Debian packages repository

In order to use apt Debian repository for automatic install and update these packages add the following line to /etc/apt/sources.list:

deb http://dmitry-kazakov.de/distributions sid main

[Back][TOC][Next]

5. Changes log

The following versions were tested with the compilers:

and the GtkAda versions:

Changes  (1 June 2014) to the version 3.3:

The following versions were tested with the compilers:

and the GtkAda versions:

Changes to the version 3.2 (27 April 2013):

Changes to the version 3.1:

Changes to the version 3.0:

The following versions were tested with the compilers:

and the GtkAda:

Changes to the version 2.9.

Changes to the version 2.8.

The following versions were tested with the compilers:

and the GtkAda:

Changes to the version 2.7.

Changes to the version 2.6.

The following versions were tested with the compilers:

and the GtkAda:

Changes to the version 2.5.

The following versions were tested with the compilers:

and the GtkAda:

Changes to the version 2.4.

Changes to the version 2.3.

Changes to the version 2.2.

The following versions were tested with the compilers:

Note that GNAT GPL 2006 distribution for Windows has a bug in the installation files that prevents GtkAda programs from being linked. To fix it you should replace all GTK+ DLLs in the directory GNAT/bin with the same named DLLs from the directory GtkAda/bin.

and the GtkAda:

Changes to the version 2.1.:

The following versions were tested with the compilers:

Changes to the version 2.0.:

The following versions were tested with GNAT 3.15p compiler.

Changes to the version 1.8:

Changes to the version 1.7:

Changes to the version 1.6:

Changes to the version 1.5:

Changes to the version 1.4:

Changes to the version 1.3:


[Back][TOC]

6. Table of Contents

Foreword
1 Types
    1.1. The type Unit
        1.1.1. Base units
        1.1.2. Unit constants
        1.1.3. Conversion to ASCII and Latin-1 strings
        1.1.4. Conversion to UTF-8 encoded strings
        1.1.5. Design notes
    1.2. The type Measure
        1.2.1. Shifted and unshifted Measures
        1.2.2. Operations defined on Measures
        1.2.3. Unit conversions
        1.2.4. Constants
        1.2.5. Elementary functions
        1.2.6. Conversion from String
        1.2.7. Conversion to String
        1.2.8. Handling I/O in multiple encodings
    1.3. GTK+ widgets
        1.3.1. Unit selection widget
        1.3.2. Dimensioned value entry
        1.3.3. Dimensioned value cell renderer
        1.3.4. Unit selection dialogs
2 Packages
    2.1. The packages related to the type Unit
    2.2. The packages related to the type Measure
    2.3. Packages defined for internal use
3 Examples
   3.1 Self-tests
   3.2 Unit converter
        3.2.1. Unit converter for Win32
        3.2.2. Unit converter for GTK+
        3.2.3. Unit mapper for GTK+
4 Installation
   4.1. Debian and Fedora packages
   4.2. Fedora packages repository
   4.3. Debian packages repository
5 Changes log
6 Table of contents