UNITS OF MEASUREMENT

FOR ADA

version 3.9

by Dmitry A. Kazakov

(mailbox@dmitry-kazakov.de)



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.

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 S ystème I nternational d'Unités) units can be mixed with irregular units (such as foot and yard) as long as the result is defined;

ystème nternational 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.

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

ARM Intel Download Units of Measurements for Ada Platform: v7 64- 32bit Fedora packages precompiled and packaged using RPM CentOS packages precompiled and packaged using RPM Debian packages precompiled and packaged for dpkg Ubuntu packages precompiled and packaged for dpkg Source distribution (any platform) units_3_9.tgz (tar + gzip, Windows users may use WinZip)

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.

ARM Intel Download Units Converter Platform: v7 64- 32bit Windows units_converter.exe.gz built using Windows 64-bit API (gzip compressed) Fedora binary package CentOS binary package Debian binary package Ubuntu binary package

Download Units Mapper Windows units_mapper.exe.gz built using GTK+ API (gzip compressed) Fedora binary package CentOS binary package Debian binary package Ubuntu binary package

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: .

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:

Exponentiation, the power is an integer;

Multiplication;

Division;

Square root;

Conversion to String.

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 B2 16 Power 2 3 B3 16 Power 3 · B7 16 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 C2 16 B2 16 00B2 16 Power 2 3 C2 16 B3 16 00B3 16 Power 3 4 E2 16 81 16 B4 16 2074 16 Power 4 5 E2 16 81 16 B5 16 2075 16 Power 5 6 E2 16 81 16 B6 16 2076 16 Power 6 7 E2 16 81 16 B7 16 2077 16 Power 7 8 E2 16 81 16 B8 16 2078 16 Power 8 9 E2 16 81 16 B9 16 2079 16 Power 9 - E2 16 81 16 BB 16 207B 16 Power - · C2 16 B7 16 00B7 16 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.

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 (non-shifted)

Here 0ºC is converted to its non-shifted 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] 4π 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 m [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 s [6] 1 National Bureau of Standards: "Refinement of Values for the Yard and the Pound.",

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 - E2 16 81 16 B0 16 2070 16 Power 0 1 B9 16 C2 16 B9 16 00B9 16 Power 1 2 B2 16 C2 16 B2 16 00B2 16 Power 2 3 B3 16 C2 16 B3 16 00B3 16 Power 3 4 - E2 16 81 16 B4 16 2074 16 Power 4 5 - E2 16 81 16 B5 16 2075 16 Power 5 6 - E2 16 81 16 B6 16 2076 16 Power 6 7 - E2 16 81 16 B7 16 2077 16 Power 7 8 - E2 16 81 16 B8 16 2078 16 Power 8 9 - E2 16 81 16 B9 16 2079 16 Power 9 + - E2 16 81 16 BA 16 207A 16 Power + - - E2 16 81 16 BB 16 207B 16 Power - · B7 16 C2 16 B7 16 00B7 16 Multiplication operator ° B0 16 C2 16 B0 16 00B0 16 Degree, also in °C, °F etc µ B5 16 C2 16 B5 16 00B5 16 Micro CE 16 BC 16 03BC 16 °C - E2 16 84 16 83 16 2103 16 Celsius (one letter sign) °F - E2 16 84 16 89 16 2109 16 Fahrenheit (one letter sign) ℥ - E2 16 84 16 A5 16 2125 16 Ounce sign K - E2 16 84 16 AA 16 212A 16 K (Kelvin, one letter sign) Å - E2 16 84 16 AB 16 212B 16 Ångström (one letter sign) Å C5 16 C3 16 85 16 00C5 16 Å in Ångström å E5 16 C3 16 A5 16 00E5 16 Small Å in Ångström ö F6 16 C3 16 B6 16 00F6 16 Small O-umlaut in Ångström Ω - CE 16 A9 16 03A9 16 Ohm E2 16 84 16 A6 16 2126 16

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 (00B0 16 ) and the capital letter C can be used. In UTF-8 the degree Celsius (2103 16 ) 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 (212A 16 ) 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 (03A9 16 ) and the Ohm (2126 16 ) 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 (00B5 16 ) is only recognized in Latin-1 and UTF-8. The latter additionally does Greek mu (03BC 16 ) 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 (00B0 16 ) 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 (00B0 16 ) and the capital letter F can be used. Additionally, in UTF-8 degree Fahrenheit is recognized (2109 16 ) °K, Kelvin Degree of Kelvin In Latin-1 and UTF-8 encoded strings a combination of ring (00B0 16 ) 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 (212A 16 ) 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 (212B 16 ) 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 (021D 16 ) 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:

Scalar is a numeric value with no scale specified. The field Numeral contains the value. The field Scale is 1 SI.;

is a numeric value with no scale specified. The field Numeral contains the value. The field Scale is 1 SI.; Numeric is a numeric value with numeric scale. The field Numeral is the value, the field Scale is the dimensionless scale of, with zero offset;

is a numeric value with numeric scale. The field Numeral is the value, the field Scale is the dimensionless scale of, with zero offset; Canonic is a numeric value with dimensioned scale. The field Numeral is the value, the field Scale is the scale of;

is a numeric value with dimensioned scale. The field Numeral is the value, the field Scale is the scale of; Jumbled is any other value. The field Numeral is 1.0. The field Scale contains the value.

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:

ASCII_Set is same as the corresponding subroutine of Measures_Edit with Latin1 false .

. Latin1_Set is same as Measures_Edit with Latin1 true .

. UTF8_Set is same as Measures_UTF8_Edit.

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

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:

GTK+ run-time and developing libraries installed;

GtkAda installed. If you get a distribution of GtkAda it typically contains GTK+ run-time and libraries in it;

GtkAda contributions. You can place the files of in the same directory as the source files of the measurements units for Ada.

1.3.1. Unit selection widget

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

The widget consists of:

an edit field, where a measurement unit can be directly input;

an tree view widget that provides commonly used units divided into sections;

a spin box, where the unit factor can be adjusted, if the unit selected is regular. The box disappears for irregular units or when the user starts to type the unit into the edit field.

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:

commit is emitted when an enter key is pressed within the widget or else when upon a double click on a unit row in the tree view;

is emitted when an enter key is pressed within the widget or else when upon a double click on a unit row in the tree view; reject is emitted when an escape key is pressed.

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:

a plain number scaled to the renderer's scale;

measure;

GTK+ value of the type GType_Double scaled to the renderer's scale;

String composed out of the rendered value followed by the scale specification text of the renderer.

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:

a plain number scaled to the renderer's scale;

measure, Unit_Error is propagated when Value has the units incompatible with Cell ;

has the units incompatible with ; GTK+ value of the type GType_Double scaled to the renderer's scale or else GType_String. When the value is of GType_String, the effect is same as if it were passed as string;

String containing a measure in the format of Measures_UTF8_Edit. The value when dimensioned is treated as if it were passed as a measure. When the value is dimensionless the effect is same as if it was passed as a plain number.

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:

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.

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.

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

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

);

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.

3. Examples

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

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-examples→units-converter→win32. 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 UTF-16 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 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 Ada.Unchecked_Conversion;

with Interfaces.C;

with System;



procedure Units_Converter is

--

-- Things related to Win32-API

--

BN_CLICKED : constant := 0 ;



WM_CLOSE : constant := 16#010# ;

WM_INITDIALOG : constant := 16#110# ;

WM_COMMAND : constant := 16#111# ;



type BOOL is new Interfaces.C.int;

type DWORD is new Interfaces.C.unsigned_long;

type UINT is new Interfaces.C.unsigned;

type INT is new Interfaces.C.int;

type WORD is new Interfaces.C.unsigned_short;

type WPARAM is mod 2 ** Standard'Address_Size;

type LPARAM is range -( 2 ** (Standard'Address_Size - 1 ))

.. +( 2 ** (Standard'Address_Size - 1 ) - 1 );



type LPSTR is access all Interfaces.C.char;

type LPWSTR is access all Interfaces.C.wchar_t;



type HWND is new System.Address;

type HINSTANCE is new System.Address;



type DLGPROC is access function

( Window : HWND;

Message : UINT;

W_Param : WPARAM;

L_Param : LPARAM

) return BOOL;

pragma Convention (Stdcall, DLGPROC);



function DialogBoxParamW

( Instance : HINSTANCE;

ID : LPWSTR;

Parent : HWND;

Dialog_Func : DLGPROC;

Init_Param : LPARAM

) return INT;

pragma Import (Stdcall, DialogBoxParamW, " DialogBoxParamW ");



function EndDialog (Dialog : HWND; Result : INT) return BOOL;

pragma Import (Stdcall, EndDialog, " EndDialog ");



function FreeLibrary

( Module : HINSTANCE

) return BOOL;

pragma Import (Stdcall, FreeLibrary, " FreeLibrary ");



function HIWORD (Value : DWORD) return WORD is

begin

return WORD (Value / ( 2 ** 16 ));

end HIWORD;



function LOWORD (Value : DWORD) return WORD is

begin

return WORD (Value mod ( 2 ** 16 ));

end LOWORD;



function GetDlgItemTextW

( Dialog : HWND;

ID : INT;

Text : LPWSTR;

Max_Count : INT

) return UINT;

pragma Import (Stdcall, GetDlgItemTextW, " GetDlgItemTextW ");



function GetLastError return DWORD;

pragma Import (Stdcall, GetLastError, " GetLastError ");



procedure InitCommonControls;

pragma Import (Stdcall, InitCommonControls, " InitCommonControls ");



function LoadLibrary (File_Name : LPSTR) return HINSTANCE;

pragma Import (Stdcall, LoadLibrary, "LoadLibraryA");



function MAKEINTRESOURCEW (Value : WORD) return LPWSTR is

function To_LPWSTR is

new Ada.Unchecked_Conversion (WPARAM, LPWSTR);

begin

return To_LPWSTR (WPARAM (Value));

end MAKEINTRESOURCEW;



function Get_Instance return HINSTANCE;

pragma Import (C, Get_Instance, " rts_get_hInstance ");



function SetDlgItemTextW

( Dialog : HWND;

ID : INT;

Text : LPWSTR

) return BOOL;

pragma Import (Stdcall, SetDlgItemTextW, " SetDlgItemTextW ");

--

-- 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 : Interfaces.C.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 (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 : Interfaces.C.wchar_array :=

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 : Interfaces.C.char_array :=

Interfaces.C.To_C (" RichEd20.dll ");

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-examples→units-converter→gtk. 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-examples→units-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;

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).

For CentOS, Debian, Fedora, Ubuntu Linux distributions there are pre-compiled packages see the links on the top of the page.

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";

5. Changes log

The following versions were tested with the compilers:

GNAT Studio Community 2020 (20200427)

GNAT Community 2018 (20180523-73)

GNAT 9

GNAT 8

and the GtkAda versions:

GtkAda 3.14.15

Changes (31 May 2020) to the version 3.8:

Code cleanup;

Adapted to GNAT Studio Community 2020;

The following versions were tested with the compilers:

GNAT Community 2018 (20180523-73)

GNAT 8

and the GtkAda versions:

GtkAda 3.14.15

Changes (5 Aug 2018) to the version 3.7:

Compatibility with GtkAda 3.14.15;

All Windows examples changed to 64-bit;

The example Units_Converter was changed to be independent on Win32Ada bindings as they are no more available for GNAT Community 2018;

Units_Converter for Windows can be built using gprbuild only.

The following versions were tested with the compilers:

GNAT GPL 2016 (20160515-49)

GNAT 6

and the GtkAda versions:

GtkAda 3.14.2

Changes (25 July 2016) to the version 3.6:

Compatibility with GtkAda 3.14

The following versions were tested with the compilers:

GNAT GPL 2015 (20150428-49)

GNAT 5.3

and the GtkAda versions:

GtkAda 3.8.3

Changes (29 June 2015) to the version 3.5:

Adapted to GNAT GPL 2015 and GtkAda 3.8.3.

The following versions were tested with the compilers:

GNAT GPL 2014 (20140331)

GNAT 4.8.2 (20131212)

GNAT 4.9.0

and the GtkAda versions:

GtkAda 3.8.2

Changes (2 April 2015) to the version 3.4:

ARMv7 (AKA armhf) support.

Changes (1 June 2014) to the version 3.3:

The widgets and renderers are adapted for the GTK 3.x. GTK 2.x is no more supported;

Procedure Split is added to the package Units;

Only Ada 2005 and Ada 2012 are supported when widgets and renderes are used. The non-GUI parts of the software remain Ada 95 conform;

Bug fix in text conversion that led to false output of values with units like square meter;

Compiled with GNAT 4.9.

The following versions were tested with the compilers:

GNAT Pro 6.4.2 (20110614-4)

GNAT 4.6.2 (20111027)

and the GtkAda versions:

GtkAda 2.18, 2.24

Changes to the version 3.2 (27 April 2013):

The value of the constant hp (Horsepower) fixed in the package Measures_Irregular.

Changes to the version 3.1:

The procedure Put in Measures_UTF8_Edit has additional parameters Field, Justify, Fill;

Fedora and Debian packages are provided for both 32- and 64-bit architectures.

Changes to the version 3.0:

Packaged for Debian and Fedora.

The following versions were tested with the compilers:

GNAT Pro 6.2.1 (20090115-43)

GNAT GPL 2009 (20090519)

and the GtkAda:

GtkAda GPL 2.14

Changes to the version 2.9.

Output of exact zero values uses power 1 with any small. E.g. 0W is output as 0 · W rather than 0 ·y W.

Changes to the version 2.8.

SI prefixes are used for dimensioned output;

GNAT project files reworked;

Minor bug fixes;

Installation instructions added.

The following versions were tested with the compilers:

GNAT GPL 2008 (20080521)

and the GtkAda:

GtkAda GPL 2.10.2

Changes to the version 2.7.

Added irregular units dram, gill, league;

Added SI derived unit katal (kat);

Removed irregular unit candle;

Bq was moved to derived SI units;

Unicode ounce sign is now supported;

Bug fix in Get_Value_As;

Conversion factors verified and corrected, references to the sources provided in the documentation.

Changes to the version 2.6.

The implementations of popup windows were modified in order to fit GtkAda 2.10.2.

The following versions were tested with the compilers:

GNAT 4.1.2 20070502;

GNAT GPL 2007 (20070405-41).

and the GtkAda:

GtkAda GPL 2.10.0 (An unofficial binary release for Windows can be found at www.ada-ru.org/win_bin_en, For Linux it is officially available in sources and can be routinely compiled from.)

Changes to the version 2.5.

This version is aimed to work with GtkAda 2.10.0. Note that it is not compatible with earlier GtkAda 2.8.0;

The widget Gtk_Unit_Selection was modified to keep track of the value set in it. When the value is interpretable as a value with the scale, then selection of another unit retains its numeral component.

The following versions were tested with the compilers:

GNAT 4.1.2 20070502;

GNAT GPL 2007 (20070405-41).

and the GtkAda:

GtkAda GPL 2.8.0

Changes to the version 2.4.

The procedure Get was added for parsing measures as a numeral multiplied by dimensioned scale.

Changes to the version 2.3.

A GTK program sample for mapping dimensioned values;

The package Measures_UTF8_Edit publicly instantiates Measures_Universal_Edit;

The package Measures_Gtk_Edit.GEntry provides dimensioned values editing entry;

The package Measures_Gtk_Edit.GEntry.Cell_Renderer provides dimensioned values tree view cell renderer.

Changes to the version 2.2.

For GNAT users GPS project files were added.

The following versions were tested with the compilers:

GNAT 4.1.1 20070105;

GNAT GPL 2006 (20060522-34).

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:

GtkAda GPL 2.8.0

Changes to the version 2.1.:

GTK+ unit selection widget added

The following versions were tested with the compilers:

GNAT 2005, GCC 4.0.2 (20051125);

GNAT 3.15p.

Changes to the version 2.0.:

Conformity with Ada 2005

The following versions were tested with GNAT 3.15p compiler.

Changes to the version 1.8:

The exception Unit_Error was moved to the package Units;

GetValue, GetValueAs, GetUnit functions were renamed to Get_Value, Get_Value_As, Get_Unit to be conform with the established naming tradition in Ada;

Instantiation of generic packages was simplified;

The package Measures_Universal_Edit was added to provide string I/O for multiple character sets.

Changes to the version 1.7:

Get_Unit procedure was added for input pure measurement units (expression terms) rather than measures (arbitrary expression);

To_Measure was added for explicit number to measure conversion;

Documentation bugs were fixed.

Changes to the version 1.6:

Change in multiplication and division of shifted measures. An unshifted dimensionless measure can now be mixed with a shifted one in * and /;

UTF-8 encoding support;

Minor bug fix in units converter examples: Unit_Error is now caught;

The package Units has the new child Units.Edit. The function Image is moved there;

Units converter for GTK is now statically linked for i686 target;

Units converter for Windows supports Unicode.

Changes to the version 1.5:

Platform independent units converter sample for GTK;

Bug fix in unit input, the conflict between SI prefix and unit name in cases P(petto) vs. Pa is now correctly resolved.

Changes to the version 1.4:

Licensing wording was corrected to comply with GMGPL

Changes to the version 1.3:

The Get procedures will raise Constraint_Error on numeric errors even if T’Machine_Overflows is not true

6. Table of Contents