Code

Filters. Custom predefined filters update and new ABC filters.
[inkscape.git] / src / util / units.cpp
1 #ifdef HAVE_CONFIG_H
2 # include <config.h>
3 #endif
5 #include <cmath>
6 #include <cerrno>
7 #include <glib.h>
9 #include "io/simple-sax.h"
10 #include "util/units.h"
11 #include "path-prefix.h"
12 #include "streq.h"
14 namespace Inkscape {
15 namespace Util {
17 class UnitsSAXHandler : public Inkscape::IO::FlatSaxHandler
18 {
19 public:
20     UnitsSAXHandler(UnitTable *table) : FlatSaxHandler(), tbl(table) {}
21     virtual ~UnitsSAXHandler() {}
23     virtual void _startElement(xmlChar const *name, xmlChar const **attrs);
24     virtual void _endElement(xmlChar const *name);
26     UnitTable *tbl;
27     bool primary;
28     bool skip;
29     Unit unit;
30 };
33 #define BUFSIZE (255)
35 /**
36  * Returns the suggested precision to use for displaying numbers
37  * of this unit.
38  */
39 int Unit::defaultDigits() const {
40     int factor_digits = int(log10(factor));
41     if (factor_digits < 0) {
42         g_warning("factor = %f, factor_digits = %d", factor, factor_digits);
43         g_warning("factor_digits < 0 - returning 0");
44         return 0;
45     } else {
46         return factor_digits;
47     }
48 }
50 /**
51  * Initializes the unit tables and identifies the primary unit types.
52  *
53  * The primary unit's conversion factor is required to be 1.00
54  */
55 UnitTable::UnitTable() 
56 {
57     // if we swich to the xml file, don't forget to force locale to 'C'
58     //    load("share/ui/units.xml");  // <-- Buggy 
59     gchar *filename = g_build_filename(INKSCAPE_UIDIR, "units.txt", NULL);
60     loadText(filename);
61     g_free(filename);
62 }
64 UnitTable::~UnitTable() {
65     UnitMap::iterator iter = _unit_map.begin();
66     while (iter != _unit_map.end()) {
67         delete (*iter).second;
68         ++iter;
69     }
70 }
72 /** Add a new unit to the table */
73 void
74 UnitTable::addUnit(Unit const &u, bool primary) {
75     _unit_map[u.abbr] = new Unit(u);
76     if (primary) {
77         _primary_unit[u.type] = u.abbr;
78     }
79 }
81 /** Retrieve a given unit based on its string identifier */
82 Unit
83 UnitTable::getUnit(Glib::ustring const &unit_abbr) const {
84     UnitMap::const_iterator iter = _unit_map.find(unit_abbr);
85     if (iter != _unit_map.end()) {
86         return *((*iter).second);
87     } else {
88         return Unit();
89     }
90 }
92 /** Remove a unit definition from the given unit type table */
93 bool 
94 UnitTable::deleteUnit(Unit const &u) {
95     if (u.abbr == _primary_unit[u.type]) {
96         // Cannot delete the primary unit type since it's
97         // used for conversions
98         return false;
99     }
100     UnitMap::iterator iter = _unit_map.find(u.abbr);
101     if (iter != _unit_map.end()) {
102         delete (*iter).second;
103         _unit_map.erase(iter);
104         return true;
105     } else {
106         return false;
107     }
110 /** Returns true if the given string 'name' is a valid unit in the table */
111 bool
112 UnitTable::hasUnit(Glib::ustring const &unit) const {
113     UnitMap::const_iterator iter = _unit_map.find(unit);
114     return (iter != _unit_map.end());
117 /** Provides an iteratable list of items in the given unit table */
118 UnitTable::UnitMap 
119 UnitTable::units(UnitType type) const
121     UnitMap submap;
122     for (UnitMap::const_iterator iter = _unit_map.begin();
123          iter != _unit_map.end(); ++iter) {
124         if (((*iter).second)->type == type) {
125             submap.insert(UnitMap::value_type((*iter).first, new Unit(*((*iter).second))));
126         }
127     }
129     return submap;
132 /** Returns the default unit abbr for the given type */
133 Glib::ustring
134 UnitTable::primary(UnitType type) const {
135     return _primary_unit[type];
138 /** Merges the contents of the given file into the UnitTable,
139     possibly overwriting existing unit definitions.  This loads
140     from a text file */
141 bool
142 UnitTable::loadText(Glib::ustring const &filename) {
143     char buf[BUFSIZE];
145     // Open file for reading
146     FILE * f = fopen(filename.c_str(), "r");
147     if (f == NULL) {
148         g_warning("Could not open units file '%s': %s\n", 
149                 filename.c_str(), strerror(errno));
150         g_warning("* INKSCAPE_DATADIR is:  '%s'\n", INKSCAPE_DATADIR);
151         g_warning("* INKSCAPE_UIDIR is:  '%s'\n", INKSCAPE_UIDIR);
152         return false;
153     }
155     // bypass current locale in order to make
156     // sscanf read floats with '.' as a separator
157     // set locate to 'C' and keep old locale
158     char *old_locale;
159     old_locale = g_strdup (setlocale (LC_NUMERIC, NULL));
160     setlocale (LC_NUMERIC, "C");
162     while (fgets(buf, BUFSIZE, f) != NULL) {
163         char name[BUFSIZE];
164         char plural[BUFSIZE];
165         char abbr[BUFSIZE];
166         char type[BUFSIZE];
167         double factor;
168         char primary[BUFSIZE];
170         int nchars = 0;
171     // locate is set to C, scanning %lf should work _everywhere_
172         if (sscanf(buf, "%s %s %s %s %lf %s %n", 
173                    name, plural, abbr, type, &factor, 
174                    primary, &nchars) != 6) {
175             // Skip the line - doesn't appear to be valid
176             continue;
177         }
178         g_assert(nchars < BUFSIZE);
180         char *desc = buf;
181         desc += nchars;  // buf is now only the description
183         // insert into _unit_map
184         Unit u;
185         u.name = name;
186         u.name_plural = plural;
187         u.abbr = abbr;
188         u.description = desc;
189         u.factor = factor;
191         if (streq(type, "DIMENSIONLESS")) {
192             u.type = UNIT_TYPE_DIMENSIONLESS;
193         } else if (streq(type, "LINEAR")) {
194             u.type = UNIT_TYPE_LINEAR;
195         } else if (streq(type, "RADIAL")) {
196             u.type = UNIT_TYPE_RADIAL;
197         } else if (streq(type, "FONT_HEIGHT")) {
198             u.type = UNIT_TYPE_FONT_HEIGHT;
199         } else {
200             g_warning("Skipping unknown unit type '%s' for %s.\n", 
201                     type, name);
202             continue;
203         }
205         // if primary is 'Y', list this unit as a primary
206         addUnit(u, (primary[0]=='Y' || primary[0]=='y'));
208     }
210     // set back the saved locale
211     setlocale (LC_NUMERIC, old_locale);
212     g_free (old_locale);
214     // close file
215     if (fclose(f) != 0) {
216         g_warning("Error closing units file '%s':  %s\n",
217                 filename.c_str(), strerror(errno));
218         return false;
219     }
221     return true;
224 bool
225 UnitTable::load(Glib::ustring const &filename) {
226     UnitsSAXHandler handler(this);
228     int result = handler.parseFile( filename.c_str() );
229     if ( result != 0 ) {
230         // perhaps
231         g_warning("Problem loading units file '%s':  %d\n", 
232                   filename.c_str(), result);
233         return false;
234     }
236     return true;
239 /** Saves the current UnitTable to the given file. */
240 bool
241 UnitTable::save(Glib::ustring const &filename) {
243     // open file for writing
244     FILE *f = fopen(filename.c_str(), "w");
245     if (f == NULL) {
246         g_warning("Could not open units file '%s': %s\n", 
247                   filename.c_str(), strerror(errno));
248         return false;
249     }
251     // write out header
252     // foreach item in _unit_map, sorted alphabetically by type and then unit name
253     //    sprintf a line
254     //      name
255     //      name_plural
256     //      abbr
257     //      type
258     //      factor
259     //      PRI - if listed in primary unit table, 'Y', else 'N'
260     //      description
261     //    write line to the file
263     // close file
264     if (fclose(f) != 0) {
265         g_warning("Error closing units file '%s':  %s\n",
266                   filename.c_str(), strerror(errno));
267         return false;
268     }
270     return true;
274 void UnitsSAXHandler::_startElement(xmlChar const *name, xmlChar const **attrs)
276     if (streq("unit", (char const *)name)) {
277         // reset for next use
278         unit.name.clear();
279         unit.name_plural.clear();
280         unit.abbr.clear();
281         unit.description.clear();
282         unit.type = UNIT_TYPE_DIMENSIONLESS;
283         unit.factor = 1.0;
284         primary = false;
285         skip = false;
287         for ( int i = 0; attrs[i]; i += 2 ) {
288             char const *const key = (char const *)attrs[i];
289             if (streq("type", key)) {
290                 char const *type = (char const*)attrs[i+1];
291                 if (streq(type, "DIMENSIONLESS")) {
292                     unit.type = UNIT_TYPE_DIMENSIONLESS;
293                 } else if (streq(type, "LINEAR")) {
294                     unit.type = UNIT_TYPE_LINEAR;
295                 } else if (streq(type, "RADIAL")) {
296                     unit.type = UNIT_TYPE_RADIAL;
297                 } else if (streq(type, "FONT_HEIGHT")) {
298                     unit.type = UNIT_TYPE_FONT_HEIGHT;
299                 } else {
300                     g_warning("Skipping unknown unit type '%s' for %s.\n", type, name);
301                     skip = true;
302                 }
303             } else if (streq("pri", key)) {
304                 primary = attrs[i+1][0] == 'y' || attrs[i+1][0] == 'Y';
305             }
306         }
307     }
310 void UnitsSAXHandler::_endElement(xmlChar const *xname)
312     char const *const name = (char const *) xname;
313     if (streq("name", name)) {
314         unit.name = data;
315     } else if (streq("plural", name)) {
316         unit.name_plural = data;
317     } else if (streq("abbr", name)) {
318         unit.abbr = data;
319     } else if (streq("factor", name)) {
320         // TODO make sure we use the right conversion
321         unit.factor = atol(data.c_str());
322     } else if (streq("description", name)) {
323         unit.description = data;
324     } else if (streq("unit", name)) {
325         if (!skip) {
326             tbl->addUnit(unit, primary);
327         }
328     }
331 } // namespace Util
332 } // namespace Inkscape
335 /*
336   Local Variables:
337   mode:c++
338   c-file-style:"stroustrup"
339   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
340   indent-tabs-mode:nil
341   fill-column:99
342   End:
343 */
344 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :