diff options
Diffstat (limited to 'src/util/units.cpp')
| -rw-r--r-- | src/util/units.cpp | 344 |
1 files changed, 344 insertions, 0 deletions
diff --git a/src/util/units.cpp b/src/util/units.cpp new file mode 100644 index 000000000..c49b2176d --- /dev/null +++ b/src/util/units.cpp @@ -0,0 +1,344 @@ +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <cmath> +#include <cerrno> +#include <glib.h> + +#include "io/simple-sax.h" +#include "util/units.h" +#include "path-prefix.h" +#include "streq.h" + +namespace Inkscape { +namespace Util { + +class UnitsSAXHandler : public Inkscape::IO::FlatSaxHandler +{ +public: + UnitsSAXHandler(UnitTable *table) : FlatSaxHandler(), tbl(table) {} + virtual ~UnitsSAXHandler() {} + + virtual void _startElement(xmlChar const *name, xmlChar const **attrs); + virtual void _endElement(xmlChar const *name); + + UnitTable *tbl; + bool primary; + bool skip; + Unit unit; +}; + + +#define BUFSIZE (255) + +/** + * Returns the suggested precision to use for displaying numbers + * of this unit. + */ +int Unit::defaultDigits() const { + int factor_digits = int(log10(factor)); + if (factor_digits < 0) { + g_warning("factor = %f, factor_digits = %d", factor, factor_digits); + g_warning("factor_digits < 0 - returning 0"); + return 0; + } else { + return factor_digits; + } +} + +/** + * Initializes the unit tables and identifies the primary unit types. + * + * The primary unit's conversion factor is required to be 1.00 + */ +UnitTable::UnitTable() +{ + // if we swich to the xml file, don't forget to force locale to 'C' + // load("share/ui/units.xml"); // <-- Buggy + gchar *filename = g_build_filename(INKSCAPE_UIDIR, "units.txt", NULL); + loadText(filename); + g_free(filename); +} + +UnitTable::~UnitTable() { + UnitMap::iterator iter = _unit_map.begin(); + while (iter != _unit_map.end()) { + delete (*iter).second; + ++iter; + } +} + +/** Add a new unit to the table */ +void +UnitTable::addUnit(Unit const &u, bool primary) { + _unit_map[u.abbr] = new Unit(u); + if (primary) { + _primary_unit[u.type] = u.abbr; + } +} + +/** Retrieve a given unit based on its string identifier */ +Unit +UnitTable::getUnit(Glib::ustring const &unit_abbr) const { + UnitMap::const_iterator iter = _unit_map.find(unit_abbr); + if (iter != _unit_map.end()) { + return *((*iter).second); + } else { + return Unit(); + } +} + +/** Remove a unit definition from the given unit type table */ +bool +UnitTable::deleteUnit(Unit const &u) { + if (u.abbr == _primary_unit[u.type]) { + // Cannot delete the primary unit type since it's + // used for conversions + return false; + } + UnitMap::iterator iter = _unit_map.find(u.abbr); + if (iter != _unit_map.end()) { + delete (*iter).second; + _unit_map.erase(iter); + return true; + } else { + return false; + } +} + +/** Returns true if the given string 'name' is a valid unit in the table */ +bool +UnitTable::hasUnit(Glib::ustring const &unit) const { + UnitMap::const_iterator iter = _unit_map.find(unit); + return (iter != _unit_map.end()); +} + +/** Provides an iteratable list of items in the given unit table */ +UnitTable::UnitMap +UnitTable::units(UnitType type) const +{ + UnitMap submap; + for (UnitMap::const_iterator iter = _unit_map.begin(); + iter != _unit_map.end(); ++iter) { + if (((*iter).second)->type == type) { + submap.insert(UnitMap::value_type((*iter).first, new Unit(*((*iter).second)))); + } + } + + return submap; +} + +/** Returns the default unit abbr for the given type */ +Glib::ustring +UnitTable::primary(UnitType type) const { + return _primary_unit[type]; +} + +/** Merges the contents of the given file into the UnitTable, + possibly overwriting existing unit definitions. This loads + from a text file */ +bool +UnitTable::loadText(Glib::ustring const &filename) { + char buf[BUFSIZE]; + + // Open file for reading + FILE * f = fopen(filename.c_str(), "r"); + if (f == NULL) { + g_warning("Could not open units file '%s': %s\n", + filename.c_str(), strerror(errno)); + g_warning("* INKSCAPE_DATADIR is: '%s'\n", INKSCAPE_DATADIR); + g_warning("* INKSCAPE_UIDIR is: '%s'\n", INKSCAPE_UIDIR); + return false; + } + + // bypass current locale in order to make + // sscanf read floats with '.' as a separator + // set locate to 'C' and keep old locale + char *old_locale; + old_locale = g_strdup (setlocale (LC_NUMERIC, NULL)); + setlocale (LC_NUMERIC, "C"); + + while (fgets(buf, BUFSIZE, f) != NULL) { + char name[BUFSIZE]; + char plural[BUFSIZE]; + char abbr[BUFSIZE]; + char type[BUFSIZE]; + double factor; + char primary[BUFSIZE]; + + int nchars = 0; + // locate is set to C, scanning %lf should work _everywhere_ + if (sscanf(buf, "%s %s %s %s %lf %s %n", + name, plural, abbr, type, &factor, + primary, &nchars) != 6) { + // Skip the line - doesn't appear to be valid + continue; + } + g_assert(nchars < BUFSIZE); + + char *desc = buf; + desc += nchars; // buf is now only the description + + // insert into _unit_map + Unit u; + u.name = name; + u.name_plural = plural; + u.abbr = abbr; + u.description = desc; + u.factor = factor; + + if (streq(type, "DIMENSIONLESS")) { + u.type = UNIT_TYPE_DIMENSIONLESS; + } else if (streq(type, "LINEAR")) { + u.type = UNIT_TYPE_LINEAR; + } else if (streq(type, "RADIAL")) { + u.type = UNIT_TYPE_RADIAL; + } else if (streq(type, "FONT_HEIGHT")) { + u.type = UNIT_TYPE_FONT_HEIGHT; + } else { + g_warning("Skipping unknown unit type '%s' for %s.\n", + type, name); + continue; + } + + // if primary is 'Y', list this unit as a primary + addUnit(u, (primary[0]=='Y' || primary[0]=='y')); + + } + + // set back the saved locale + setlocale (LC_NUMERIC, old_locale); + g_free (old_locale); + + // close file + if (fclose(f) != 0) { + g_warning("Error closing units file '%s': %s\n", + filename.c_str(), strerror(errno)); + return false; + } + + return true; +} + +bool +UnitTable::load(Glib::ustring const &filename) { + UnitsSAXHandler handler(this); + + int result = handler.parseFile( filename.c_str() ); + if ( result != 0 ) { + // perhaps + g_warning("Problem loading units file '%s': %d\n", + filename.c_str(), result); + return false; + } + + return true; +} + +/** Saves the current UnitTable to the given file. */ +bool +UnitTable::save(Glib::ustring const &filename) { + + // open file for writing + FILE *f = fopen(filename.c_str(), "w"); + if (f == NULL) { + g_warning("Could not open units file '%s': %s\n", + filename.c_str(), strerror(errno)); + return false; + } + + // write out header + // foreach item in _unit_map, sorted alphabetically by type and then unit name + // sprintf a line + // name + // name_plural + // abbr + // type + // factor + // PRI - if listed in primary unit table, 'Y', else 'N' + // description + // write line to the file + + // close file + if (fclose(f) != 0) { + g_warning("Error closing units file '%s': %s\n", + filename.c_str(), strerror(errno)); + return false; + } + + return true; +} + + +void UnitsSAXHandler::_startElement(xmlChar const *name, xmlChar const **attrs) +{ + if (streq("unit", (char const *)name)) { + // reset for next use + unit.name.clear(); + unit.name_plural.clear(); + unit.abbr.clear(); + unit.description.clear(); + unit.type = UNIT_TYPE_DIMENSIONLESS; + unit.factor = 1.0; + primary = false; + skip = false; + + for ( int i = 0; attrs[i]; i += 2 ) { + char const *const key = (char const *)attrs[i]; + if (streq("type", key)) { + char const *type = (char const*)attrs[i+1]; + if (streq(type, "DIMENSIONLESS")) { + unit.type = UNIT_TYPE_DIMENSIONLESS; + } else if (streq(type, "LINEAR")) { + unit.type = UNIT_TYPE_LINEAR; + } else if (streq(type, "RADIAL")) { + unit.type = UNIT_TYPE_RADIAL; + } else if (streq(type, "FONT_HEIGHT")) { + unit.type = UNIT_TYPE_FONT_HEIGHT; + } else { + g_warning("Skipping unknown unit type '%s' for %s.\n", type, name); + skip = true; + } + } else if (streq("pri", key)) { + primary = attrs[i+1][0] == 'y' || attrs[i+1][0] == 'Y'; + } + } + } +} + +void UnitsSAXHandler::_endElement(xmlChar const *xname) +{ + char const *const name = (char const *) xname; + if (streq("name", name)) { + unit.name = data; + } else if (streq("plural", name)) { + unit.name_plural = data; + } else if (streq("abbr", name)) { + unit.abbr = data; + } else if (streq("factor", name)) { + // TODO make sure we use the right conversion + unit.factor = atol(data.c_str()); + } else if (streq("description", name)) { + unit.description = data; + } else if (streq("unit", name)) { + if (!skip) { + tbl->addUnit(unit, primary); + } + } +} + +} // namespace Util +} // namespace Inkscape + + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : |
